From c47b981a56414379a6964ae3dac609eaae88f33d Mon Sep 17 00:00:00 2001 From: sledgehammer999 Date: Sun, 29 Jun 2025 22:25:34 +0300 Subject: [PATCH 1/2] Add fallback to update mechanism This brings a fallback version check to the update mechanism, which should be as stable as it can be. It will allow migrating to another primary mechanism without having to have updated the older primary mechanism too. --- src/gui/programupdater.cpp | 72 ++++++++++++++++++++++++++++++++------ src/gui/programupdater.h | 8 ++++- 2 files changed, 69 insertions(+), 11 deletions(-) diff --git a/src/gui/programupdater.cpp b/src/gui/programupdater.cpp index 063bc5473..58b1d47b0 100644 --- a/src/gui/programupdater.cpp +++ b/src/gui/programupdater.cpp @@ -35,10 +35,13 @@ #include #include #include +#include +#include #include #include #include "base/global.h" +#include "base/logger.h" #include "base/net/downloadmanager.h" #include "base/preferences.h" #include "base/utils/version.h" @@ -82,30 +85,34 @@ namespace void ProgramUpdater::checkForUpdates() const { + 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(QStringLiteral("qBittorrent/" QBT_VERSION_2 " ProgramUpdater (www.qbittorrent.org)")) + 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); + + m_hasCompletedOneReq = false; } QString ProgramUpdater::getNewVersion() const { - return m_newVersion; + return shouldUseFallback() ? m_fallbackRemoteVersion : m_remoteVersion; } void ProgramUpdater::rssDownloadFinished(const Net::DownloadResult &result) { if (result.status != Net::DownloadStatus::Success) { - qDebug() << "Downloading the new qBittorrent updates RSS failed:" << result.errorString; - emit updateCheckFinished(); + LogMsg(tr("Failed to download the update info. URL: %1. Error: %2").arg(result.url, result.errorString) , Log::WARNING); + handleFinishedRequest(); return; } - qDebug("Finished downloading the new qBittorrent updates RSS"); - const auto getStringValue = [](QXmlStreamReader &xml) -> QString { xml.readNext(); @@ -148,7 +155,7 @@ void ProgramUpdater::rssDownloadFinished(const Net::DownloadResult &result) qDebug("Detected version is %s", qUtf8Printable(version)); if (isVersionMoreRecent(version)) { - m_newVersion = version; + m_remoteVersion = version; m_updateURL = updateLink; } } @@ -163,10 +170,55 @@ void ProgramUpdater::rssDownloadFinished(const Net::DownloadResult &result) } } - emit updateCheckFinished(); + handleFinishedRequest(); +} + +void ProgramUpdater::fallbackDownloadFinished(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); + handleFinishedRequest(); + return; + } + + const auto json = QJsonDocument::fromJson(result.data); + +#if defined(Q_OS_MACOS) + const QString platformKey = u"macos"_s; +#elif defined(Q_OS_WIN) + const QString platformKey = u"win"_s; +#endif + + if (const QJsonValue verJSON = json[platformKey][u"version"_s]; verJSON.isString()) + { + const auto ver = verJSON.toString(); + if (isVersionMoreRecent(ver)) + m_fallbackRemoteVersion = ver; + } + + handleFinishedRequest(); } bool ProgramUpdater::updateProgram() const { - return QDesktopServices::openUrl(m_updateURL); + return QDesktopServices::openUrl(shouldUseFallback() ? u"https://www.qbittorrent.org/download"_s : m_updateURL); +} + +void ProgramUpdater::handleFinishedRequest() +{ + if (m_hasCompletedOneReq) + emit updateCheckFinished(); + else + m_hasCompletedOneReq = true; +} + +bool ProgramUpdater::shouldUseFallback() const +{ + using Version = Utils::Version<4, 3>; + + const auto remote = Version::fromString(m_remoteVersion); + const auto fallback = Version::fromString(m_fallbackRemoteVersion); + + return fallback > remote; } diff --git a/src/gui/programupdater.h b/src/gui/programupdater.h index 88d41985f..3d046f853 100644 --- a/src/gui/programupdater.h +++ b/src/gui/programupdater.h @@ -55,8 +55,14 @@ signals: private slots: void rssDownloadFinished(const Net::DownloadResult &result); + void fallbackDownloadFinished(const Net::DownloadResult &result); private: - QString m_newVersion; + void handleFinishedRequest(); + bool shouldUseFallback() const; + + mutable bool m_hasCompletedOneReq = false; + QString m_remoteVersion; + QString m_fallbackRemoteVersion; QUrl m_updateURL; }; From 9ad4a9494072b54124f1c412efe1626aa5cedb65 Mon Sep 17 00:00:00 2001 From: sledgehammer999 Date: Mon, 30 Jun 2025 10:08:22 +0300 Subject: [PATCH 2/2] Store version numbers in the appropriate type --- src/gui/mainwindow.cpp | 6 +++--- src/gui/programupdater.cpp | 33 +++++++++++++-------------------- src/gui/programupdater.h | 10 ++++++---- 3 files changed, 22 insertions(+), 27 deletions(-) diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index e30da68d1..271932fbc 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -1662,11 +1662,11 @@ void MainWindow::handleUpdateCheckFinished(ProgramUpdater *updater, const bool i updater->deleteLater(); }; - const QString newVersion = updater->getNewVersion(); - if (!newVersion.isEmpty()) + const auto newVersion = updater->getNewVersion(); + if (newVersion.isValid()) { const QString msg {tr("A new version is available.") + u"
" - + tr("Do you want to download %1?").arg(newVersion) + u"

" + + tr("Do you want to download %1?").arg(newVersion.toString()) + u"

" + u"%1"_s.arg(tr("Open changelog..."))}; auto *msgBox = new QMessageBox {QMessageBox::Question, tr("qBittorrent Update Available"), msg , (QMessageBox::Yes | QMessageBox::No), this}; diff --git a/src/gui/programupdater.cpp b/src/gui/programupdater.cpp index 58b1d47b0..dc6965f29 100644 --- a/src/gui/programupdater.cpp +++ b/src/gui/programupdater.cpp @@ -49,23 +49,20 @@ namespace { - bool isVersionMoreRecent(const QString &remoteVersion) + bool isVersionMoreRecent(const ProgramUpdater::Version &remoteVersion) { - using Version = Utils::Version<4, 3>; - - const auto newVersion = Version::fromString(remoteVersion); - if (!newVersion.isValid()) + if (!remoteVersion.isValid()) return false; - const Version currentVersion {QBT_VERSION_MAJOR, QBT_VERSION_MINOR, QBT_VERSION_BUGFIX, QBT_VERSION_BUILD}; - if (newVersion == currentVersion) + const ProgramUpdater::Version currentVersion {QBT_VERSION_MAJOR, QBT_VERSION_MINOR, QBT_VERSION_BUGFIX, QBT_VERSION_BUILD}; + if (remoteVersion == currentVersion) { const bool isDevVersion = QStringLiteral(QBT_VERSION_STATUS).contains( QRegularExpression(u"(alpha|beta|rc)"_s)); if (isDevVersion) return true; } - return (newVersion > currentVersion); + return (remoteVersion > currentVersion); } QString buildVariant() @@ -99,7 +96,7 @@ void ProgramUpdater::checkForUpdates() const m_hasCompletedOneReq = false; } -QString ProgramUpdater::getNewVersion() const +ProgramUpdater::Version ProgramUpdater::getNewVersion() const { return shouldUseFallback() ? m_fallbackRemoteVersion : m_remoteVersion; } @@ -153,9 +150,10 @@ void ProgramUpdater::rssDownloadFinished(const Net::DownloadResult &result) if (!version.isEmpty()) { qDebug("Detected version is %s", qUtf8Printable(version)); - if (isVersionMoreRecent(version)) + const ProgramUpdater::Version tmpVer {version}; + if (isVersionMoreRecent(tmpVer)) { - m_remoteVersion = version; + m_remoteVersion = tmpVer; m_updateURL = updateLink; } } @@ -192,9 +190,9 @@ void ProgramUpdater::fallbackDownloadFinished(const Net::DownloadResult &result) if (const QJsonValue verJSON = json[platformKey][u"version"_s]; verJSON.isString()) { - const auto ver = verJSON.toString(); - if (isVersionMoreRecent(ver)) - m_fallbackRemoteVersion = ver; + const ProgramUpdater::Version tmpVer {verJSON.toString()}; + if (isVersionMoreRecent(tmpVer)) + m_fallbackRemoteVersion = tmpVer; } handleFinishedRequest(); @@ -215,10 +213,5 @@ void ProgramUpdater::handleFinishedRequest() bool ProgramUpdater::shouldUseFallback() const { - using Version = Utils::Version<4, 3>; - - const auto remote = Version::fromString(m_remoteVersion); - const auto fallback = Version::fromString(m_fallbackRemoteVersion); - - return fallback > remote; + return m_fallbackRemoteVersion > m_remoteVersion; } diff --git a/src/gui/programupdater.h b/src/gui/programupdater.h index 3d046f853..531d12118 100644 --- a/src/gui/programupdater.h +++ b/src/gui/programupdater.h @@ -30,9 +30,10 @@ #pragma once #include -#include #include +#include "base/utils/version.h" + namespace Net { struct DownloadResult; @@ -45,9 +46,10 @@ class ProgramUpdater final : public QObject public: using QObject::QObject; + using Version = Utils::Version<4, 3>; void checkForUpdates() const; - QString getNewVersion() const; + Version getNewVersion() const; bool updateProgram() const; signals: @@ -62,7 +64,7 @@ private: bool shouldUseFallback() const; mutable bool m_hasCompletedOneReq = false; - QString m_remoteVersion; - QString m_fallbackRemoteVersion; + Version m_remoteVersion; + Version m_fallbackRemoteVersion; QUrl m_updateURL; };