-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Replace qhttp client with curl for favicon downloading #1460
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -199,13 +199,10 @@ add_feature_info(KeePassHTTP WITH_XC_HTTP "Browser integration compatible with C | |
add_feature_info(SSHAgent WITH_XC_SSHAGENT "SSH agent integration compatible with KeeAgent") | ||
add_feature_info(YubiKey WITH_XC_YUBIKEY "YubiKey HMAC-SHA1 challenge-response") | ||
|
||
if(WITH_XC_HTTP) | ||
add_subdirectory(http) | ||
set(keepasshttp_LIB keepasshttp) | ||
endif() | ||
add_subdirectory(http) | ||
if(WITH_XC_NETWORKING) | ||
add_subdirectory(http/qhttp) | ||
set(keepassxcnetwork_LIB qhttp Qt5::Network) | ||
find_package(CURL REQUIRED) | ||
set(keepassxcnetwork_LIB curl) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use ${CURL_LIBRARIES} instead of hardcoding the library name. I think you should also remove this line altogether and use ${CURL_LIBRARIES} directly in target_link_libraries. Then we can get rid of ${keepassxchttp_LIB} when we drop KeepassHTTP. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, duh good point |
||
endif() | ||
|
||
set(BROWSER_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/browser) | ||
|
@@ -251,23 +248,21 @@ endif() | |
add_library(autotype STATIC ${autotype_SOURCES}) | ||
target_link_libraries(autotype Qt5::Core Qt5::Widgets) | ||
|
||
set(autotype_LIB autotype) | ||
|
||
add_library(keepassx_core STATIC ${keepassx_SOURCES}) | ||
|
||
set_target_properties(keepassx_core PROPERTIES COMPILE_DEFINITIONS KEEPASSX_BUILDING_CORE) | ||
target_link_libraries(keepassx_core | ||
${keepassxcbrowser_LIB} | ||
${keepasshttp_LIB} | ||
autotype | ||
${keepassxchttp_LIB} | ||
${keepassxcnetwork_LIB} | ||
${autotype_LIB} | ||
${keepassxcbrowser_LIB} | ||
${sshagent_LIB} | ||
${YUBIKEY_LIBRARIES} | ||
${ZXCVBN_LIBRARIES} | ||
Qt5::Core | ||
Qt5::Network | ||
Qt5::Concurrent | ||
Qt5::Widgets | ||
${YUBIKEY_LIBRARIES} | ||
${ZXCVBN_LIBRARIES} | ||
${ARGON2_LIBRARIES} | ||
${GCRYPT_LIBRARIES} | ||
${GPGERROR_LIBRARIES} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,10 +31,10 @@ | |
#include "gui/MessageBox.h" | ||
|
||
#ifdef WITH_XC_NETWORKING | ||
#include "http/qhttp/qhttpclient.hpp" | ||
#include "http/qhttp/qhttpclientresponse.hpp" | ||
|
||
using namespace qhttp::client; | ||
#include <QFuture> | ||
#include <QtConcurrent> | ||
#include <curl/curl.h> | ||
#undef MessageBox | ||
#endif | ||
|
||
IconStruct::IconStruct() | ||
|
@@ -49,10 +49,6 @@ EditWidgetIcons::EditWidgetIcons(QWidget* parent) | |
, m_database(nullptr) | ||
, m_defaultIconModel(new DefaultIconModel(this)) | ||
, m_customIconModel(new CustomIconModel(this)) | ||
#ifdef WITH_XC_NETWORKING | ||
, m_fallbackToGoogle(true) | ||
, m_redirectCount(0) | ||
#endif | ||
{ | ||
m_ui->setupUi(this); | ||
|
||
|
@@ -148,7 +144,6 @@ void EditWidgetIcons::setUrl(const QString& url) | |
#ifdef WITH_XC_NETWORKING | ||
m_url = url; | ||
m_ui->faviconButton->setVisible(!url.isEmpty()); | ||
resetFaviconDownload(); | ||
#else | ||
Q_UNUSED(url); | ||
m_ui->faviconButton->setVisible(false); | ||
|
@@ -158,107 +153,79 @@ void EditWidgetIcons::setUrl(const QString& url) | |
void EditWidgetIcons::downloadFavicon() | ||
{ | ||
#ifdef WITH_XC_NETWORKING | ||
m_ui->faviconButton->setDisabled(true); | ||
|
||
QUrl url = QUrl(m_url); | ||
url.setPath("/favicon.ico"); | ||
fetchFavicon(url); | ||
#endif | ||
} | ||
|
||
#ifdef WITH_XC_NETWORKING | ||
void EditWidgetIcons::fetchFavicon(const QUrl& url) | ||
{ | ||
if (nullptr == m_httpClient) { | ||
m_httpClient = new QHttpClient(this); | ||
} | ||
|
||
bool requestMade = m_httpClient->request(qhttp::EHTTP_GET, url, [this, url](QHttpResponse* response) { | ||
if (m_database == nullptr) { | ||
return; | ||
} | ||
|
||
response->collectData(); | ||
response->onEnd([this, response, &url]() { | ||
int status = response->status(); | ||
if (200 == status) { | ||
QImage image; | ||
image.loadFromData(response->collectedData()); | ||
|
||
if (!image.isNull()) { | ||
addCustomIcon(image); | ||
resetFaviconDownload(); | ||
} else { | ||
fetchFaviconFromGoogle(url.host()); | ||
} | ||
} else if (301 == status || 302 == status) { | ||
// Check if server has sent a redirect | ||
QUrl possibleRedirectUrl(response->headers().value("location", "")); | ||
if (!possibleRedirectUrl.isEmpty() && possibleRedirectUrl != m_redirectUrl && m_redirectCount < 3) { | ||
resetFaviconDownload(false); | ||
m_redirectUrl = possibleRedirectUrl; | ||
++m_redirectCount; | ||
fetchFavicon(m_redirectUrl); | ||
} else { | ||
// website is trying to redirect to itself or | ||
// maximum number of redirects has been reached, fall back to Google | ||
fetchFaviconFromGoogle(url.host()); | ||
} | ||
} else { | ||
fetchFaviconFromGoogle(url.host()); | ||
} | ||
}); | ||
}); | ||
|
||
if (!requestMade) { | ||
resetFaviconDownload(); | ||
return; | ||
} | ||
|
||
m_httpClient->setConnectingTimeOut(5000, [this]() { | ||
QUrl tempurl = QUrl(m_url); | ||
if (tempurl.scheme() == "http") { | ||
resetFaviconDownload(); | ||
emit messageEditEntry(tr("Unable to fetch favicon.") + "\n" + | ||
tr("Hint: You can enable Google as a fallback under Tools>Settings>Security"), | ||
MessageWidget::Error); | ||
// Attempt to simply load the favicon.ico file | ||
QImage image = fetchFavicon(url); | ||
if (!image.isNull()) { | ||
addCustomIcon(image); | ||
} else if (config()->get("security/IconDownloadFallbackToGoogle", false).toBool()) { | ||
QUrl faviconUrl = QUrl("https://www.google.com/s2/favicons"); | ||
faviconUrl.setQuery("domain=" + QUrl::toPercentEncoding(url.host())); | ||
// Attempt to load favicon from Google | ||
image = fetchFavicon(faviconUrl); | ||
if (!image.isNull()) { | ||
addCustomIcon(image); | ||
} else { | ||
tempurl.setScheme("http"); | ||
m_url = tempurl.url(); | ||
tempurl.setPath("/favicon.ico"); | ||
fetchFavicon(tempurl); | ||
emit messageEditEntry(tr("Unable to fetch favicon."), MessageWidget::Error); | ||
} | ||
}); | ||
} else { | ||
emit messageEditEntry(tr("Unable to fetch favicon.") + "\n" + | ||
tr("Hint: You can enable Google as a fallback under Tools>Settings>Security"), | ||
MessageWidget::Error); | ||
} | ||
|
||
m_ui->faviconButton->setDisabled(true); | ||
m_ui->faviconButton->setDisabled(false); | ||
#endif | ||
} | ||
|
||
void EditWidgetIcons::fetchFaviconFromGoogle(const QString& domain) | ||
#ifdef WITH_XC_NETWORKING | ||
size_t writeCurlResponse(char* ptr, size_t size, size_t nmemb, void* data) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. std::size_t |
||
{ | ||
if (config()->get("security/IconDownloadFallbackToGoogle", false).toBool() && m_fallbackToGoogle) { | ||
resetFaviconDownload(); | ||
m_fallbackToGoogle = false; | ||
QUrl faviconUrl = QUrl("https://www.google.com/s2/favicons"); | ||
faviconUrl.setQuery("domain=" + QUrl::toPercentEncoding(domain)); | ||
fetchFavicon(faviconUrl); | ||
} else { | ||
resetFaviconDownload(); | ||
emit messageEditEntry(tr("Unable to fetch favicon."), MessageWidget::Error); | ||
} | ||
QByteArray* response = static_cast<QByteArray*>(data); | ||
size_t realsize = size * nmemb; | ||
response->append(ptr, realsize); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since we know the real size, you can use response->reserve() to pre-allocate memory. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually reserve doesn't make sense in this context because we are adding the new data received to an existing buffer. The append function is accurate. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. True. I somehow added a loop in my head, which isn't actually there. |
||
return realsize; | ||
} | ||
|
||
void EditWidgetIcons::resetFaviconDownload(bool clearRedirect) | ||
QImage EditWidgetIcons::fetchFavicon(const QUrl& url) | ||
{ | ||
if (clearRedirect) { | ||
m_redirectUrl.clear(); | ||
m_redirectCount = 0; | ||
} | ||
QImage image; | ||
CURL* curl = curl_easy_init(); | ||
if (curl != nullptr) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if (curl) |
||
QByteArray imagedata; | ||
QByteArray baUrl = url.url().toLatin1(); | ||
|
||
curl_easy_setopt(curl, CURLOPT_URL, baUrl.data()); | ||
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5L); | ||
curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); | ||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); | ||
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5L); | ||
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L); | ||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &imagedata); | ||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeCurlResponse); | ||
|
||
// Perform the request in another thread | ||
QFuture<CURLcode> future = QtConcurrent::run([curl]() { | ||
return curl_easy_perform(curl); | ||
}); | ||
|
||
QEventLoop loop; | ||
QFutureWatcher<CURLcode> watcher; | ||
connect(&watcher, SIGNAL(finished()), &loop, SLOT(quit())); | ||
watcher.setFuture(future); | ||
loop.exec(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have helper functions for this now. |
||
|
||
curl_easy_cleanup(curl); | ||
|
||
if (nullptr != m_httpClient) { | ||
m_httpClient->deleteLater(); | ||
m_httpClient = nullptr; | ||
if (future.result() == CURLE_OK) { | ||
image.loadFromData(imagedata); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe show an error message otherwise? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a worker function, the error messages are handled in the parent caller (downloadFavicon()) |
||
} | ||
|
||
m_fallbackToGoogle = true; | ||
m_ui->faviconButton->setDisabled(false); | ||
return image; | ||
} | ||
#endif | ||
|
||
|
@@ -281,7 +248,7 @@ void EditWidgetIcons::addCustomIconFromFile() | |
} | ||
} | ||
|
||
void EditWidgetIcons::addCustomIcon(const QImage &icon) | ||
void EditWidgetIcons::addCustomIcon(const QImage& icon) | ||
{ | ||
if (m_database) { | ||
Uuid uuid = m_database->metadata()->findCustomIcon(icon); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any specific reason to keep this file at all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, now is a good time to trash it!