From 1daa42e4fedabdb05eb0b19a95884c0543bd0c1f Mon Sep 17 00:00:00 2001 From: Vladimir Golovnev Date: Fri, 20 Jun 2025 10:20:13 +0300 Subject: [PATCH 1/7] Find CorePrivate package with Qt >= 6.10 PR #22890. Closes #22887. --- cmake/Modules/CheckPackages.cmake | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmake/Modules/CheckPackages.cmake b/cmake/Modules/CheckPackages.cmake index 6c85b8d11..6b082ea11 100644 --- a/cmake/Modules/CheckPackages.cmake +++ b/cmake/Modules/CheckPackages.cmake @@ -47,6 +47,9 @@ find_package(Boost ${minBoostVersion} REQUIRED) find_package(OpenSSL ${minOpenSSLVersion} REQUIRED) find_package(ZLIB ${minZlibVersion} REQUIRED) find_package(Qt6 ${minQt6Version} REQUIRED COMPONENTS Core Network Sql Xml LinguistTools) +if (Qt6_FOUND AND (Qt6_VERSION VERSION_GREATER_EQUAL 6.10)) + find_package(Qt6 ${minQt6Version} REQUIRED COMPONENTS CorePrivate) +endif() if (DBUS) find_package(Qt6 ${minQt6Version} REQUIRED COMPONENTS DBus) set_package_properties(Qt6DBus PROPERTIES From 13282d94ef642deccd0637d045e1e8feb497def3 Mon Sep 17 00:00:00 2001 From: Vladimir Golovnev Date: Fri, 20 Jun 2025 10:19:16 +0300 Subject: [PATCH 2/7] Don't ignore QFile::open() result PR #22889. Closes #22888. --- src/base/search/searchpluginmanager.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/base/search/searchpluginmanager.cpp b/src/base/search/searchpluginmanager.cpp index 09b7a67cc..ab53c5d5e 100644 --- a/src/base/search/searchpluginmanager.cpp +++ b/src/base/search/searchpluginmanager.cpp @@ -487,14 +487,14 @@ void SearchPluginManager::updateNova() const Path enginePath = engineLocation(); QFile packageFile {(enginePath / Path(u"__init__.py"_s)).data()}; - packageFile.open(QIODevice::WriteOnly); - packageFile.close(); + if (packageFile.open(QIODevice::WriteOnly)) + packageFile.close(); Utils::Fs::mkdir(enginePath / Path(u"engines"_s)); QFile packageFile2 {(enginePath / Path(u"engines/__init__.py"_s)).data()}; - packageFile2.open(QIODevice::WriteOnly); - packageFile2.close(); + if (packageFile2.open(QIODevice::WriteOnly)) + packageFile2.close(); // Copy search plugin files (if necessary) const auto updateFile = [&enginePath](const Path &filename, const bool compareVersion) From 206d5abf84fb8d1fc5b64c3a197f2640623c7a86 Mon Sep 17 00:00:00 2001 From: Vladimir Golovnev Date: Fri, 27 Jun 2025 13:44:10 +0300 Subject: [PATCH 3/7] Don't expose palette colors in UI theme editor PR #22923. Fixes regression introduced by #22330. --- src/gui/uithemecommon.h | 8 +++++++- src/gui/uithemedialog.cpp | 2 -- src/gui/uithemesource.cpp | 2 ++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/gui/uithemecommon.h b/src/gui/uithemecommon.h index 9f1545fe6..ba7a5c2c5 100644 --- a/src/gui/uithemecommon.h +++ b/src/gui/uithemecommon.h @@ -80,8 +80,14 @@ inline QHash defaultUIThemeColors() {u"TransferList.StoppedUploading"_s, {Color::Primer::Light::doneFg, Color::Primer::Dark::doneFg}}, {u"TransferList.Moving"_s, {Color::Primer::Light::successFg, Color::Primer::Dark::successFg}}, {u"TransferList.MissingFiles"_s, {Color::Primer::Light::dangerFg, Color::Primer::Dark::dangerFg}}, - {u"TransferList.Error"_s, {Color::Primer::Light::dangerFg, Color::Primer::Dark::dangerFg}}, + {u"TransferList.Error"_s, {Color::Primer::Light::dangerFg, Color::Primer::Dark::dangerFg}} + }; +} +// Palette isn't customizable in default theme +inline QHash defaultPaletteColors() +{ + return { {u"Palette.Window"_s, {{}, {}}}, {u"Palette.WindowText"_s, {{}, {}}}, {u"Palette.Base"_s, {{}, {}}}, diff --git a/src/gui/uithemedialog.cpp b/src/gui/uithemedialog.cpp index 24ee381da..2d06ceeba 100644 --- a/src/gui/uithemedialog.cpp +++ b/src/gui/uithemedialog.cpp @@ -273,8 +273,6 @@ void UIThemeDialog::loadColors() int row = 2; for (const QString &id : colorIDs) { - if (id == u"Log.Normal") - qDebug() << "!!!!!!!"; m_ui->colorsLayout->addWidget(new QLabel(id), row, 0); const UIThemeColor &defaultColor = defaultColors.value(id); diff --git a/src/gui/uithemesource.cpp b/src/gui/uithemesource.cpp index be0138ee0..b854986ea 100644 --- a/src/gui/uithemesource.cpp +++ b/src/gui/uithemesource.cpp @@ -105,6 +105,8 @@ DefaultThemeSource::DefaultThemeSource() , m_colors {defaultUIThemeColors()} { loadColors(); + // Palette isn't customizable in default theme + m_colors.insert(defaultPaletteColors()); } QByteArray DefaultThemeSource::readStyleSheet() From 9b29d37d210f28f46eb4758611d7b06f4603b9d1 Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Mon, 30 Jun 2025 01:39:03 +0800 Subject: [PATCH 4/7] WebAPI: Trim leading whitespaces on Run External Program fields Hacked qbt instances may contain malicious script placed in Run External Program and the script will attempt to hide itself by adding a lot whitespaces at the start of the command string. Users may mistake the field of being empty but is actually not. So trim the leading whitespaces to easily expose the malicious script. Note that GUI already trim the fields and only WebAPI doesn't trim them. This patch will unify the behavior. Related: https://github.com/qbittorrent/docker-qbittorrent-nox/issues/71#issuecomment-2993567440 PR #22939. --- src/webui/api/appcontroller.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/webui/api/appcontroller.cpp b/src/webui/api/appcontroller.cpp index 95bb6e0d0..a1fc0e1da 100644 --- a/src/webui/api/appcontroller.cpp +++ b/src/webui/api/appcontroller.cpp @@ -673,12 +673,12 @@ void AppController::setPreferencesAction() if (hasKey(u"autorun_on_torrent_added_enabled"_s)) pref->setAutoRunOnTorrentAddedEnabled(it.value().toBool()); if (hasKey(u"autorun_on_torrent_added_program"_s)) - pref->setAutoRunOnTorrentAddedProgram(it.value().toString()); + pref->setAutoRunOnTorrentAddedProgram(it.value().toString().trimmed()); // Run an external program on torrent finished if (hasKey(u"autorun_enabled"_s)) pref->setAutoRunOnTorrentFinishedEnabled(it.value().toBool()); if (hasKey(u"autorun_program"_s)) - pref->setAutoRunOnTorrentFinishedProgram(it.value().toString()); + pref->setAutoRunOnTorrentFinishedProgram(it.value().toString().trimmed()); // Connection // Listening Port From 3fca180e9882e7d790092b897be37a6118bc6366 Mon Sep 17 00:00:00 2001 From: Ryu481 <142620516+Ryu481@users.noreply.github.com> Date: Sun, 29 Jun 2025 19:56:39 +0200 Subject: [PATCH 5/7] Make qBittorrent quit on MacOS with main window closed Fixes the reported bug that you couldn't quit qBittorrent when the main window was closed on MacOS. Closes #22849. PR #22931. --- src/gui/mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index 1dc459fdb..92c8bc848 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -1161,7 +1161,7 @@ void MainWindow::closeEvent(QCloseEvent *e) if (!m_forceExit) { hide(); - e->accept(); + e->ignore(); return; } #else From dd5c9341030a9a2010f1f924eca69bb755391973 Mon Sep 17 00:00:00 2001 From: sledgehammer999 Date: Sun, 29 Jun 2025 22:25:34 +0300 Subject: [PATCH 6/7] 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 bb34444ddca39bb76c7319fcdec8913c35ebdbbe Mon Sep 17 00:00:00 2001 From: sledgehammer999 Date: Mon, 30 Jun 2025 10:08:22 +0300 Subject: [PATCH 7/7] 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 92c8bc848..dd07ea2ef 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -1660,11 +1660,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; };