From ec0d8f8313d01fa83e7ea7b1acd1b22f9564034d Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Sun, 20 Jul 2025 00:05:28 +0800 Subject: [PATCH] Add alternative URL for program update checking The alternative URL is hosted on GitHub and users are able to access it: https://github.com/qbittorrent/qBittorrent/issues/23000#issuecomment-3092538814 https://github.com/qbittorrent/qBittorrent/issues/23009#issuecomment-3093201180 Also, disguise the user agent as a normal browser to avoid standing out from the crowd and avoid whatever issues from CDN. This only applies to non-fosshub URLs. Closes #23000. Closes #23009. --- src/gui/mainwindow.cpp | 2 +- src/gui/programupdater.cpp | 86 +++++++++++++++++++++++++++----------- src/gui/programupdater.h | 25 +++++++---- 3 files changed, 79 insertions(+), 34 deletions(-) diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index 271932fbc..68221c981 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -1662,7 +1662,7 @@ void MainWindow::handleUpdateCheckFinished(ProgramUpdater *updater, const bool i updater->deleteLater(); }; - const auto newVersion = updater->getNewVersion(); + const ProgramUpdater::Version newVersion = updater->getNewVersion(); if (newVersion.isValid()) { const QString msg {tr("A new version is available.") + u"
" diff --git a/src/gui/programupdater.cpp b/src/gui/programupdater.cpp index dc6965f29..f0fcc5e97 100644 --- a/src/gui/programupdater.cpp +++ b/src/gui/programupdater.cpp @@ -29,6 +29,8 @@ #include "programupdater.h" +#include + #include #include @@ -44,7 +46,6 @@ #include "base/logger.h" #include "base/net/downloadmanager.h" #include "base/preferences.h" -#include "base/utils/version.h" #include "base/version.h" namespace @@ -80,32 +81,50 @@ namespace } } -void ProgramUpdater::checkForUpdates() const +void ProgramUpdater::checkForUpdates() { - const auto USER_AGENT = QStringLiteral("qBittorrent/" QBT_VERSION_2 " ProgramUpdater (www.qbittorrent.org)"); - const auto RSS_URL = u"https://www.fosshub.com/feed/5b8793a7f9ee5a5c3e97a3b2.xml"_s; - const auto FALLBACK_URL = u"https://www.qbittorrent.org/versions.json"_s; - // Don't change this User-Agent. In case our updater goes haywire, // the filehost can identify it and contact us. - Net::DownloadManager::instance()->download(Net::DownloadRequest(RSS_URL).userAgent(USER_AGENT) - , Preferences::instance()->useProxyForGeneralPurposes(), this, &ProgramUpdater::rssDownloadFinished); - Net::DownloadManager::instance()->download(Net::DownloadRequest(FALLBACK_URL).userAgent(USER_AGENT) - , Preferences::instance()->useProxyForGeneralPurposes(), this, &ProgramUpdater::fallbackDownloadFinished); + const auto USER_AGENT = QStringLiteral("qBittorrent/" QBT_VERSION_2 " ProgramUpdater (www.qbittorrent.org)"); + const auto FOSSHUB_URL = u"https://www.fosshub.com/feed/5b8793a7f9ee5a5c3e97a3b2.xml"_s; + const auto QBT_MAIN_URL = u"https://www.qbittorrent.org/versions.json"_s; + const auto QBT_BACKUP_URL = u"https://qbittorrent.github.io/qBittorrent-website/versions.json"_s; - m_hasCompletedOneReq = false; + Net::DownloadManager *netManager = Net::DownloadManager::instance(); + const bool useProxy = Preferences::instance()->useProxyForGeneralPurposes(); + + m_pendingRequestCount = 3; + netManager->download(Net::DownloadRequest(FOSSHUB_URL).userAgent(USER_AGENT), useProxy, this, &ProgramUpdater::rssDownloadFinished); + // don't use the custom user agent for the following requests, disguise as a normal browser instead + netManager->download(Net::DownloadRequest(QBT_MAIN_URL), useProxy, this, [this](const Net::DownloadResult &result) + { + fallbackDownloadFinished(result, m_qbtMainVersion); + }); + netManager->download(Net::DownloadRequest(QBT_BACKUP_URL), useProxy, this, [this](const Net::DownloadResult &result) + { + fallbackDownloadFinished(result, m_qbtBackupVersion); + }); } ProgramUpdater::Version ProgramUpdater::getNewVersion() const { - return shouldUseFallback() ? m_fallbackRemoteVersion : m_remoteVersion; + switch (getLatestRemoteSource()) + { + case RemoteSource::Fosshub: + return m_fosshubVersion; + case RemoteSource::QbtMain: + return m_qbtMainVersion; + case RemoteSource::QbtBackup: + return m_qbtBackupVersion; + } + Q_UNREACHABLE(); } void ProgramUpdater::rssDownloadFinished(const Net::DownloadResult &result) { if (result.status != Net::DownloadStatus::Success) { - LogMsg(tr("Failed to download the update info. URL: %1. Error: %2").arg(result.url, result.errorString) , Log::WARNING); + LogMsg(tr("Failed to download the program update info. URL: \"%1\". Error: \"%2\"").arg(result.url, result.errorString) , Log::WARNING); handleFinishedRequest(); return; } @@ -150,10 +169,10 @@ void ProgramUpdater::rssDownloadFinished(const Net::DownloadResult &result) if (!version.isEmpty()) { qDebug("Detected version is %s", qUtf8Printable(version)); - const ProgramUpdater::Version tmpVer {version}; + const Version tmpVer {version}; if (isVersionMoreRecent(tmpVer)) { - m_remoteVersion = tmpVer; + m_fosshubVersion = tmpVer; m_updateURL = updateLink; } } @@ -171,11 +190,13 @@ void ProgramUpdater::rssDownloadFinished(const Net::DownloadResult &result) handleFinishedRequest(); } -void ProgramUpdater::fallbackDownloadFinished(const Net::DownloadResult &result) +void ProgramUpdater::fallbackDownloadFinished(const Net::DownloadResult &result, Version &version) { + version = {}; + if (result.status != Net::DownloadStatus::Success) { - LogMsg(tr("Failed to download the update info. URL: %1. Error: %2").arg(result.url, result.errorString) , Log::WARNING); + LogMsg(tr("Failed to download the program update info. URL: \"%1\". Error: \"%2\"").arg(result.url, result.errorString) , Log::WARNING); handleFinishedRequest(); return; } @@ -190,9 +211,9 @@ void ProgramUpdater::fallbackDownloadFinished(const Net::DownloadResult &result) if (const QJsonValue verJSON = json[platformKey][u"version"_s]; verJSON.isString()) { - const ProgramUpdater::Version tmpVer {verJSON.toString()}; + const Version tmpVer {verJSON.toString()}; if (isVersionMoreRecent(tmpVer)) - m_fallbackRemoteVersion = tmpVer; + version = tmpVer; } handleFinishedRequest(); @@ -200,18 +221,33 @@ void ProgramUpdater::fallbackDownloadFinished(const Net::DownloadResult &result) bool ProgramUpdater::updateProgram() const { - return QDesktopServices::openUrl(shouldUseFallback() ? u"https://www.qbittorrent.org/download"_s : m_updateURL); + switch (getLatestRemoteSource()) + { + case RemoteSource::Fosshub: + return QDesktopServices::openUrl(m_updateURL); + case RemoteSource::QbtMain: + return QDesktopServices::openUrl(u"https://www.qbittorrent.org/download"_s); + case RemoteSource::QbtBackup: + return QDesktopServices::openUrl(u"https://qbittorrent.github.io/qBittorrent-website/download"_s); + } + Q_UNREACHABLE(); } void ProgramUpdater::handleFinishedRequest() { - if (m_hasCompletedOneReq) + --m_pendingRequestCount; + if (m_pendingRequestCount == 0) emit updateCheckFinished(); - else - m_hasCompletedOneReq = true; } -bool ProgramUpdater::shouldUseFallback() const +ProgramUpdater::RemoteSource ProgramUpdater::getLatestRemoteSource() const { - return m_fallbackRemoteVersion > m_remoteVersion; + const Version max = std::max({m_fosshubVersion, m_qbtMainVersion, m_qbtBackupVersion}); + if (max == m_fosshubVersion) + return RemoteSource::Fosshub; + if (max == m_qbtMainVersion) + return RemoteSource::QbtMain; + if (max == m_qbtBackupVersion) + return RemoteSource::QbtBackup; + Q_UNREACHABLE(); } diff --git a/src/gui/programupdater.h b/src/gui/programupdater.h index 531d12118..528dbdf8b 100644 --- a/src/gui/programupdater.h +++ b/src/gui/programupdater.h @@ -45,10 +45,11 @@ class ProgramUpdater final : public QObject Q_DISABLE_COPY_MOVE(ProgramUpdater) public: - using QObject::QObject; using Version = Utils::Version<4, 3>; - void checkForUpdates() const; + using QObject::QObject; + + void checkForUpdates(); Version getNewVersion() const; bool updateProgram() const; @@ -57,14 +58,22 @@ signals: private slots: void rssDownloadFinished(const Net::DownloadResult &result); - void fallbackDownloadFinished(const Net::DownloadResult &result); + void fallbackDownloadFinished(const Net::DownloadResult &result, Version &version); private: - void handleFinishedRequest(); - bool shouldUseFallback() const; + enum class RemoteSource + { + Fosshub, + QbtMain, + QbtBackup + }; - mutable bool m_hasCompletedOneReq = false; - Version m_remoteVersion; - Version m_fallbackRemoteVersion; + void handleFinishedRequest(); + RemoteSource getLatestRemoteSource() const; + + int m_pendingRequestCount = 0; + Version m_fosshubVersion; + Version m_qbtMainVersion; + Version m_qbtBackupVersion; QUrl m_updateURL; };