From d829df99aa2fea5ef3502029d17b89419792c5a5 Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Sat, 5 Nov 2022 11:33:21 +0800 Subject: [PATCH 01/14] Revise interface of port forwarder This eases the usage of port forwarder as the caller code doesn't need to store previous used port and now can rely on port forwarder doing all the hard work. PR #17967. --- src/base/bittorrent/portforwarderimpl.cpp | 78 +++++++++++++++-------- src/base/bittorrent/portforwarderimpl.h | 10 ++- src/base/net/portforwarder.h | 7 +- src/webui/webui.cpp | 28 ++++---- src/webui/webui.h | 1 - 5 files changed, 75 insertions(+), 49 deletions(-) diff --git a/src/base/bittorrent/portforwarderimpl.cpp b/src/base/bittorrent/portforwarderimpl.cpp index cff7dedac..a6b9e3275 100644 --- a/src/base/bittorrent/portforwarderimpl.cpp +++ b/src/base/bittorrent/portforwarderimpl.cpp @@ -30,6 +30,7 @@ #include +#include "base/algorithm.h" #include "base/logger.h" PortForwarderImpl::PortForwarderImpl(lt::session *provider, QObject *parent) @@ -63,30 +64,45 @@ void PortForwarderImpl::setEnabled(const bool enabled) m_storeActive = enabled; } -void PortForwarderImpl::addPort(const quint16 port) +void PortForwarderImpl::setPorts(const QString &profile, QSet ports) { - if (m_mappedPorts.contains(port)) - return; - - if (isEnabled()) - m_mappedPorts.insert(port, m_provider->add_port_mapping(lt::session::tcp, port, port)); - else - m_mappedPorts.insert(port, {}); -} - -void PortForwarderImpl::deletePort(const quint16 port) -{ - const auto iter = m_mappedPorts.find(port); - if (iter == m_mappedPorts.end()) - return; - - if (isEnabled()) + PortMapping &portMapping = m_portProfiles[profile]; + Algorithm::removeIf(portMapping, [this, &ports](const quint16 port, const std::vector &handles) { - for (const lt::port_mapping_t &portMapping : *iter) - m_provider->delete_port_mapping(portMapping); + // keep existing forwardings + const bool isAlreadyMapped = ports.remove(port); + if (isAlreadyMapped) + return false; + + // remove outdated forwardings + for (const lt::port_mapping_t &handle : handles) + m_provider->delete_port_mapping(handle); + m_forwardedPorts.remove(port); + + return true; + }); + + // add new forwardings + for (const quint16 port : ports) + { + // port already forwarded/taken by other profile, don't do anything + if (m_forwardedPorts.contains(port)) + continue; + + if (isEnabled()) + portMapping.insert(port, m_provider->add_port_mapping(lt::session::tcp, port, port)); + else + portMapping.insert(port, {}); + m_forwardedPorts.insert(port); } - m_mappedPorts.erase(iter); + if (portMapping.isEmpty()) + m_portProfiles.remove(profile); +} + +void PortForwarderImpl::removePorts(const QString &profile) +{ + setPorts(profile, {}); } void PortForwarderImpl::start() @@ -96,12 +112,16 @@ void PortForwarderImpl::start() settingsPack.set_bool(lt::settings_pack::enable_natpmp, true); m_provider->apply_settings(settingsPack); - for (auto iter = m_mappedPorts.begin(); iter != m_mappedPorts.end(); ++iter) + for (auto profileIter = m_portProfiles.begin(); profileIter != m_portProfiles.end(); ++profileIter) { - Q_ASSERT(iter.value().empty()); + PortMapping &portMapping = profileIter.value(); + for (auto iter = portMapping.begin(); iter != portMapping.end(); ++iter) + { + Q_ASSERT(iter.value().empty()); - const quint16 port = iter.key(); - iter.value() = m_provider->add_port_mapping(lt::session::tcp, port, port); + const quint16 port = iter.key(); + iter.value() = m_provider->add_port_mapping(lt::session::tcp, port, port); + } } LogMsg(tr("UPnP/NAT-PMP support: ON"), Log::INFO); @@ -114,9 +134,13 @@ void PortForwarderImpl::stop() settingsPack.set_bool(lt::settings_pack::enable_natpmp, false); m_provider->apply_settings(settingsPack); - // don't clear m_mappedPorts so a later `start()` call can restore the port forwarding - for (auto iter = m_mappedPorts.begin(); iter != m_mappedPorts.end(); ++iter) - iter.value().clear(); + // don't clear m_portProfiles so a later `start()` call can restore the port forwardings + for (auto profileIter = m_portProfiles.begin(); profileIter != m_portProfiles.end(); ++profileIter) + { + PortMapping &portMapping = profileIter.value(); + for (auto iter = portMapping.begin(); iter != portMapping.end(); ++iter) + iter.value().clear(); + } LogMsg(tr("UPnP/NAT-PMP support: OFF"), Log::INFO); } diff --git a/src/base/bittorrent/portforwarderimpl.h b/src/base/bittorrent/portforwarderimpl.h index dee74ed1c..902b1002c 100644 --- a/src/base/bittorrent/portforwarderimpl.h +++ b/src/base/bittorrent/portforwarderimpl.h @@ -34,6 +34,7 @@ #include #include +#include #include "base/net/portforwarder.h" #include "base/settingvalue.h" @@ -50,8 +51,8 @@ public: bool isEnabled() const override; void setEnabled(bool enabled) override; - void addPort(quint16 port) override; - void deletePort(quint16 port) override; + void setPorts(const QString &profile, QSet ports) override; + void removePorts(const QString &profile) override; private: void start(); @@ -59,5 +60,8 @@ private: CachedSettingValue m_storeActive; lt::session *const m_provider = nullptr; - QHash> m_mappedPorts; + + using PortMapping = QHash>; // + QHash m_portProfiles; + QSet m_forwardedPorts; }; diff --git a/src/base/net/portforwarder.h b/src/base/net/portforwarder.h index f23ca2197..6f835d223 100644 --- a/src/base/net/portforwarder.h +++ b/src/base/net/portforwarder.h @@ -29,6 +29,9 @@ #pragma once #include +#include + +class QString; namespace Net { @@ -45,8 +48,8 @@ namespace Net virtual bool isEnabled() const = 0; virtual void setEnabled(bool enabled) = 0; - virtual void addPort(quint16 port) = 0; - virtual void deletePort(quint16 port) = 0; + virtual void setPorts(const QString &profile, QSet ports) = 0; + virtual void removePorts(const QString &profile) = 0; private: static PortForwarder *m_instance; diff --git a/src/webui/webui.cpp b/src/webui/webui.cpp index 5698e65e1..dc1b43596 100644 --- a/src/webui/webui.cpp +++ b/src/webui/webui.cpp @@ -50,25 +50,21 @@ void WebUI::configure() { m_isErrored = false; // clear previous error state - Preferences *const pref = Preferences::instance(); - - const quint16 oldPort = m_port; - m_port = pref->getWebUiPort(); + const QString portForwardingProfile = u"webui"_qs; + const Preferences *pref = Preferences::instance(); + const quint16 port = pref->getWebUiPort(); if (pref->isWebUiEnabled()) { - // UPnP/NAT-PMP + // Port forwarding + auto *portForwarder = Net::PortForwarder::instance(); if (pref->useUPnPForWebUIPort()) { - if (m_port != oldPort) - { - Net::PortForwarder::instance()->deletePort(oldPort); - Net::PortForwarder::instance()->addPort(m_port); - } + portForwarder->setPorts(portForwardingProfile, {port}); } else { - Net::PortForwarder::instance()->deletePort(oldPort); + portForwarder->removePorts(portForwardingProfile); } // http server @@ -81,7 +77,7 @@ void WebUI::configure() else { if ((m_httpServer->serverAddress().toString() != serverAddressString) - || (m_httpServer->serverPort() != m_port)) + || (m_httpServer->serverPort() != port)) m_httpServer->close(); } @@ -112,15 +108,15 @@ void WebUI::configure() { const auto address = ((serverAddressString == u"*") || serverAddressString.isEmpty()) ? QHostAddress::Any : QHostAddress(serverAddressString); - bool success = m_httpServer->listen(address, m_port); + bool success = m_httpServer->listen(address, port); if (success) { - LogMsg(tr("Web UI: Now listening on IP: %1, port: %2").arg(serverAddressString).arg(m_port)); + LogMsg(tr("Web UI: Now listening on IP: %1, port: %2").arg(serverAddressString).arg(port)); } else { const QString errorMsg = tr("Web UI: Unable to bind to IP: %1, port: %2. Reason: %3") - .arg(serverAddressString).arg(m_port).arg(m_httpServer->errorString()); + .arg(serverAddressString).arg(port).arg(m_httpServer->errorString()); LogMsg(errorMsg, Log::CRITICAL); qCritical() << errorMsg; @@ -144,7 +140,7 @@ void WebUI::configure() } else { - Net::PortForwarder::instance()->deletePort(oldPort); + Net::PortForwarder::instance()->removePorts(portForwardingProfile); delete m_httpServer; delete m_webapp; diff --git a/src/webui/webui.h b/src/webui/webui.h index f3fd37422..e2c63b828 100644 --- a/src/webui/webui.h +++ b/src/webui/webui.h @@ -66,5 +66,4 @@ private: QPointer m_httpServer; QPointer m_dnsUpdater; QPointer m_webapp; - quint16 m_port = 0; }; From 2bdc91c53f13b9e858f311236b9477cb4ab5228f Mon Sep 17 00:00:00 2001 From: Hanabishi <13597663+HanabishiRecca@users.noreply.github.com> Date: Sun, 6 Nov 2022 09:21:31 +0500 Subject: [PATCH 02/14] Implement `Peer ID Client` column for `Peers` tab PR #17940. --- src/base/bittorrent/peerinfo.cpp | 25 +++++++++++++++++++ src/base/bittorrent/peerinfo.h | 1 + src/gui/properties/peerlistwidget.cpp | 17 ++++++++++--- src/gui/properties/peerlistwidget.h | 3 ++- src/webui/api/synccontroller.cpp | 2 ++ src/webui/webapplication.h | 2 +- src/webui/www/private/scripts/dynamicTable.js | 1 + 7 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/base/bittorrent/peerinfo.cpp b/src/base/bittorrent/peerinfo.cpp index c3e66d7c8..45b8a158a 100644 --- a/src/base/bittorrent/peerinfo.cpp +++ b/src/base/bittorrent/peerinfo.cpp @@ -181,6 +181,31 @@ QString PeerInfo::client() const return QString::fromStdString(m_nativeInfo.client); } +QString PeerInfo::peerIdClient() const +{ + // when peer ID is not known yet it contains only zero bytes, + // do not create string in such case, return empty string instead + if (m_nativeInfo.pid.is_all_zeros()) + return {}; + + QString result; + + // interesting part of a typical peer ID is first 8 chars + for (int i = 0; i < 8; ++i) + { + const std::uint8_t c = m_nativeInfo.pid[i]; + + // ensure that the peer ID slice consists only of printable ASCII characters, + // this should filter out most of the improper IDs + if ((c < 32) || (c > 126)) + return tr("Unknown"); + + result += QChar::fromLatin1(c); + } + + return result; +} + qreal PeerInfo::progress() const { return m_nativeInfo.progress; diff --git a/src/base/bittorrent/peerinfo.h b/src/base/bittorrent/peerinfo.h index f7f51d177..6c7cb7b1c 100644 --- a/src/base/bittorrent/peerinfo.h +++ b/src/base/bittorrent/peerinfo.h @@ -78,6 +78,7 @@ namespace BitTorrent PeerAddress address() const; QString client() const; + QString peerIdClient() const; qreal progress() const; int payloadUpSpeed() const; int payloadDownSpeed() const; diff --git a/src/gui/properties/peerlistwidget.cpp b/src/gui/properties/peerlistwidget.cpp index e4ed8a75a..5b490df9e 100644 --- a/src/gui/properties/peerlistwidget.cpp +++ b/src/gui/properties/peerlistwidget.cpp @@ -89,7 +89,7 @@ PeerListWidget::PeerListWidget(PropertiesWidget *parent) , m_properties(parent) { // Load settings - loadSettings(); + const bool columnLoaded = loadSettings(); // Visual settings setUniformRowHeights(true); setRootIsDecorated(false); @@ -109,6 +109,7 @@ PeerListWidget::PeerListWidget(PropertiesWidget *parent) m_listModel->setHeaderData(PeerListColumns::FLAGS, Qt::Horizontal, tr("Flags")); m_listModel->setHeaderData(PeerListColumns::CONNECTION, Qt::Horizontal, tr("Connection")); m_listModel->setHeaderData(PeerListColumns::CLIENT, Qt::Horizontal, tr("Client", "i.e.: Client application")); + m_listModel->setHeaderData(PeerListColumns::PEERID_CLIENT, Qt::Horizontal, tr("Peer ID Client", "i.e.: Client resolved from Peer ID")); m_listModel->setHeaderData(PeerListColumns::PROGRESS, Qt::Horizontal, tr("Progress", "i.e: % downloaded")); m_listModel->setHeaderData(PeerListColumns::DOWN_SPEED, Qt::Horizontal, tr("Down Speed", "i.e: Download speed")); m_listModel->setHeaderData(PeerListColumns::UP_SPEED, Qt::Horizontal, tr("Up Speed", "i.e: Upload speed")); @@ -130,8 +131,16 @@ PeerListWidget::PeerListWidget(PropertiesWidget *parent) m_proxyModel->setSourceModel(m_listModel); m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); setModel(m_proxyModel); + hideColumn(PeerListColumns::IP_HIDDEN); hideColumn(PeerListColumns::COL_COUNT); + + // Default hidden columns + if (!columnLoaded) + { + hideColumn(PeerListColumns::PEERID_CLIENT); + } + m_resolveCountries = Preferences::instance()->resolvePeerCountries(); if (!m_resolveCountries) hideColumn(PeerListColumns::COUNTRY); @@ -371,9 +380,9 @@ void PeerListWidget::clear() m_listModel->removeRows(0, nbrows); } -void PeerListWidget::loadSettings() +bool PeerListWidget::loadSettings() { - header()->restoreState(Preferences::instance()->getPeerListState()); + return header()->restoreState(Preferences::instance()->getPeerListState()); } void PeerListWidget::saveSettings() const @@ -461,6 +470,8 @@ void PeerListWidget::updatePeer(const BitTorrent::Torrent *torrent, const BitTor setModelData(row, PeerListColumns::FLAGS, peer.flags(), peer.flags(), {}, peer.flagsDescription()); const QString client = peer.client().toHtmlEscaped(); setModelData(row, PeerListColumns::CLIENT, client, client, {}, client); + const QString peerIdClient = peer.peerIdClient().toHtmlEscaped(); + setModelData(row, PeerListColumns::PEERID_CLIENT, peerIdClient, peerIdClient); setModelData(row, PeerListColumns::PROGRESS, (Utils::String::fromDouble(peer.progress() * 100, 1) + u'%'), peer.progress(), intDataTextAlignment); const QString downSpeed = (hideValues && (peer.payloadDownSpeed() <= 0)) ? QString {} : Utils::Misc::friendlyUnit(peer.payloadDownSpeed(), true); setModelData(row, PeerListColumns::DOWN_SPEED, downSpeed, peer.payloadDownSpeed(), intDataTextAlignment); diff --git a/src/gui/properties/peerlistwidget.h b/src/gui/properties/peerlistwidget.h index a3e9e0d94..2276437c1 100644 --- a/src/gui/properties/peerlistwidget.h +++ b/src/gui/properties/peerlistwidget.h @@ -66,6 +66,7 @@ public: CONNECTION, FLAGS, CLIENT, + PEERID_CLIENT, PROGRESS, DOWN_SPEED, UP_SPEED, @@ -87,7 +88,7 @@ public: void clear(); private slots: - void loadSettings(); + bool loadSettings(); void saveSettings() const; void displayColumnHeaderMenu(); void showPeerListMenu(); diff --git a/src/webui/api/synccontroller.cpp b/src/webui/api/synccontroller.cpp index 173280c6e..f7770aeb5 100644 --- a/src/webui/api/synccontroller.cpp +++ b/src/webui/api/synccontroller.cpp @@ -66,6 +66,7 @@ namespace // Peer keys const QString KEY_PEER_CLIENT = u"client"_qs; + const QString KEY_PEER_ID_CLIENT = u"peer_id_client"_qs; const QString KEY_PEER_CONNECTION_TYPE = u"connection"_qs; const QString KEY_PEER_COUNTRY = u"country"_qs; const QString KEY_PEER_COUNTRY_CODE = u"country_code"_qs; @@ -561,6 +562,7 @@ void SyncController::torrentPeersAction() {KEY_PEER_IP, pi.address().ip.toString()}, {KEY_PEER_PORT, pi.address().port}, {KEY_PEER_CLIENT, pi.client()}, + {KEY_PEER_ID_CLIENT, pi.peerIdClient()}, {KEY_PEER_PROGRESS, pi.progress()}, {KEY_PEER_DOWN_SPEED, pi.payloadDownSpeed()}, {KEY_PEER_UP_SPEED, pi.payloadUpSpeed()}, diff --git a/src/webui/webapplication.h b/src/webui/webapplication.h index 79b5f9c69..df58992ba 100644 --- a/src/webui/webapplication.h +++ b/src/webui/webapplication.h @@ -52,7 +52,7 @@ #include "base/utils/version.h" #include "api/isessionmanager.h" -inline const Utils::Version<3, 2> API_VERSION {2, 8, 16}; +inline const Utils::Version<3, 2> API_VERSION {2, 8, 17}; class APIController; class AuthController; diff --git a/src/webui/www/private/scripts/dynamicTable.js b/src/webui/www/private/scripts/dynamicTable.js index 6a35ae5ec..6f72009ab 100644 --- a/src/webui/www/private/scripts/dynamicTable.js +++ b/src/webui/www/private/scripts/dynamicTable.js @@ -1498,6 +1498,7 @@ window.qBittorrent.DynamicTable = (function() { this.newColumn('connection', '', 'QBT_TR(Connection)QBT_TR[CONTEXT=PeerListWidget]', 50, true); this.newColumn('flags', '', 'QBT_TR(Flags)QBT_TR[CONTEXT=PeerListWidget]', 50, true); this.newColumn('client', '', 'QBT_TR(Client)QBT_TR[CONTEXT=PeerListWidget]', 140, true); + this.newColumn('peer_id_client', '', 'QBT_TR(Peer ID Client)QBT_TR[CONTEXT=PeerListWidget]', 60, false); this.newColumn('progress', '', 'QBT_TR(Progress)QBT_TR[CONTEXT=PeerListWidget]', 50, true); this.newColumn('dl_speed', '', 'QBT_TR(Down Speed)QBT_TR[CONTEXT=PeerListWidget]', 50, true); this.newColumn('up_speed', '', 'QBT_TR(Up Speed)QBT_TR[CONTEXT=PeerListWidget]', 50, true); From 62b2959cb438155dd221adb2954a3e9c6961f07e Mon Sep 17 00:00:00 2001 From: Vladimir Golovnev Date: Sun, 6 Nov 2022 14:24:49 +0300 Subject: [PATCH 03/14] Don't use extra variable to distinguish restored torrents PR #17984. --- src/base/bittorrent/bencoderesumedatastorage.cpp | 1 - src/base/bittorrent/dbresumedatastorage.cpp | 1 - src/base/bittorrent/loadtorrentparams.h | 2 -- src/base/bittorrent/sessionimpl.cpp | 4 ++-- 4 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/base/bittorrent/bencoderesumedatastorage.cpp b/src/base/bittorrent/bencoderesumedatastorage.cpp index f500c5e2d..5c13d0926 100644 --- a/src/base/bittorrent/bencoderesumedatastorage.cpp +++ b/src/base/bittorrent/bencoderesumedatastorage.cpp @@ -210,7 +210,6 @@ BitTorrent::LoadResumeDataResult BitTorrent::BencodeResumeDataStorage::loadTorre return nonstd::make_unexpected(tr("Cannot parse resume data: invalid format")); LoadTorrentParams torrentParams; - torrentParams.restored = true; torrentParams.category = fromLTString(resumeDataRoot.dict_find_string_value("qBt-category")); torrentParams.name = fromLTString(resumeDataRoot.dict_find_string_value("qBt-name")); torrentParams.hasSeedStatus = resumeDataRoot.dict_find_int_value("qBt-seedStatus"); diff --git a/src/base/bittorrent/dbresumedatastorage.cpp b/src/base/bittorrent/dbresumedatastorage.cpp index 2a3d9e0a8..dfaece90e 100644 --- a/src/base/bittorrent/dbresumedatastorage.cpp +++ b/src/base/bittorrent/dbresumedatastorage.cpp @@ -196,7 +196,6 @@ namespace BitTorrent LoadTorrentParams parseQueryResultRow(const QSqlQuery &query) { LoadTorrentParams resumeData; - resumeData.restored = true; resumeData.name = query.value(DB_COLUMN_NAME.name).toString(); resumeData.category = query.value(DB_COLUMN_CATEGORY.name).toString(); const QString tagsData = query.value(DB_COLUMN_TAGS.name).toString(); diff --git a/src/base/bittorrent/loadtorrentparams.h b/src/base/bittorrent/loadtorrentparams.h index f49870c89..ca3d914fb 100644 --- a/src/base/bittorrent/loadtorrentparams.h +++ b/src/base/bittorrent/loadtorrentparams.h @@ -58,7 +58,5 @@ namespace BitTorrent qreal ratioLimit = Torrent::USE_GLOBAL_RATIO; int seedingTimeLimit = Torrent::USE_GLOBAL_SEEDING_TIME; - - bool restored = false; // is existing torrent job? }; } diff --git a/src/base/bittorrent/sessionimpl.cpp b/src/base/bittorrent/sessionimpl.cpp index e8757d375..391b9e6e7 100644 --- a/src/base/bittorrent/sessionimpl.cpp +++ b/src/base/bittorrent/sessionimpl.cpp @@ -5212,7 +5212,7 @@ TorrentImpl *SessionImpl::createTorrent(const lt::torrent_handle &nativeHandle, if (const InfoHash infoHash = torrent->infoHash(); infoHash.isHybrid()) m_hybridTorrentsByAltID.insert(TorrentID::fromSHA1Hash(infoHash.v1()), torrent); - if (!params.restored) + if (isRestored()) { m_resumeDataStorage->store(torrent->id(), params); @@ -5230,7 +5230,7 @@ TorrentImpl *SessionImpl::createTorrent(const lt::torrent_handle &nativeHandle, m_seedingLimitTimer->start(); } - if (params.restored) + if (!isRestored()) { LogMsg(tr("Restored torrent. Torrent: \"%1\"").arg(torrent->name())); } From d884ec173192427696abf9c625a58cfa716a18b7 Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Mon, 7 Nov 2022 11:32:11 +0800 Subject: [PATCH 04/14] Add port forwarding option for embedded tracker Closes #17781. PR #17981. --- src/base/bittorrent/sessionimpl.cpp | 21 ++++++++++++++++---- src/base/bittorrent/tracker.cpp | 8 ++++---- src/base/preferences.cpp | 10 ++++++++++ src/base/preferences.h | 2 ++ src/gui/advancedsettings.cpp | 6 ++++++ src/gui/advancedsettings.h | 2 +- src/webui/api/appcontroller.cpp | 3 +++ src/webui/webapplication.h | 2 +- src/webui/www/private/views/preferences.html | 10 ++++++++++ 9 files changed, 54 insertions(+), 10 deletions(-) diff --git a/src/base/bittorrent/sessionimpl.cpp b/src/base/bittorrent/sessionimpl.cpp index 391b9e6e7..f872ed0ad 100644 --- a/src/base/bittorrent/sessionimpl.cpp +++ b/src/base/bittorrent/sessionimpl.cpp @@ -80,6 +80,7 @@ #include "base/logger.h" #include "base/net/downloadmanager.h" #include "base/net/proxyconfigurationmanager.h" +#include "base/preferences.h" #include "base/profile.h" #include "base/torrentfileguard.h" #include "base/torrentfilter.h" @@ -548,8 +549,6 @@ SessionImpl::SessionImpl(QObject *parent) if (isExcludedFileNamesEnabled()) populateExcludedFileNamesRegExpList(); - enableTracker(isTrackerEnabled()); - connect(Net::ProxyConfigurationManager::instance() , &Net::ProxyConfigurationManager::proxyConfigurationChanged , this, &SessionImpl::configureDeferred); @@ -569,11 +568,14 @@ SessionImpl::SessionImpl(QObject *parent) m_ioThread->start(); + initMetrics(); + loadStatistics(); + // initialize PortForwarder instance new PortForwarderImpl(m_nativeSession); - initMetrics(); - loadStatistics(); + // start embedded tracker + enableTracker(isTrackerEnabled()); prepareStartup(); } @@ -1985,16 +1987,27 @@ void SessionImpl::configurePeerClasses() void SessionImpl::enableTracker(const bool enable) { + const QString profile = u"embeddedTracker"_qs; + auto *portForwarder = Net::PortForwarder::instance(); + if (enable) { if (!m_tracker) m_tracker = new Tracker(this); m_tracker->start(); + + const auto *pref = Preferences::instance(); + if (pref->isTrackerPortForwardingEnabled()) + portForwarder->setPorts(profile, {static_cast(pref->getTrackerPort())}); + else + portForwarder->removePorts(profile); } else { delete m_tracker; + + portForwarder->removePorts(profile); } } diff --git a/src/base/bittorrent/tracker.cpp b/src/base/bittorrent/tracker.cpp index a811502ec..583c01743 100644 --- a/src/base/bittorrent/tracker.cpp +++ b/src/base/bittorrent/tracker.cpp @@ -203,12 +203,12 @@ Tracker::Tracker(QObject *parent) bool Tracker::start() { - const QHostAddress ip = QHostAddress::Any; const int port = Preferences::instance()->getTrackerPort(); if (m_server->isListening()) { - if (m_server->serverPort() == port) + if (const int oldPort = m_server->serverPort() + ; oldPort == port) { // Already listening on the right port, just return return true; @@ -218,9 +218,9 @@ bool Tracker::start() m_server->close(); } - // Listen on the predefined port + // Listen on port + const QHostAddress ip = QHostAddress::Any; const bool listenSuccess = m_server->listen(ip, port); - if (listenSuccess) { LogMsg(tr("Embedded Tracker: Now listening on IP: %1, port: %2") diff --git a/src/base/preferences.cpp b/src/base/preferences.cpp index 59a601118..4cc99227c 100644 --- a/src/base/preferences.cpp +++ b/src/base/preferences.cpp @@ -1164,6 +1164,16 @@ void Preferences::setTrackerPort(const int port) setValue(u"Preferences/Advanced/trackerPort"_qs, port); } +bool Preferences::isTrackerPortForwardingEnabled() const +{ + return value(u"Preferences/Advanced/trackerPortForwarding"_qs, false); +} + +void Preferences::setTrackerPortForwardingEnabled(const bool enabled) +{ + setValue(u"Preferences/Advanced/trackerPortForwarding"_qs, enabled); +} + #if defined(Q_OS_WIN) || defined(Q_OS_MACOS) bool Preferences::isUpdateCheckEnabled() const { diff --git a/src/base/preferences.h b/src/base/preferences.h index 534755b77..d22db8df5 100644 --- a/src/base/preferences.h +++ b/src/base/preferences.h @@ -299,6 +299,8 @@ public: #endif int getTrackerPort() const; void setTrackerPort(int port); + bool isTrackerPortForwardingEnabled() const; + void setTrackerPortForwardingEnabled(bool enabled); #if defined(Q_OS_WIN) || defined(Q_OS_MACOS) bool isUpdateCheckEnabled() const; void setUpdateCheckEnabled(bool enabled); diff --git a/src/gui/advancedsettings.cpp b/src/gui/advancedsettings.cpp index 1f2dda5f5..fb8618f6d 100644 --- a/src/gui/advancedsettings.cpp +++ b/src/gui/advancedsettings.cpp @@ -97,6 +97,7 @@ namespace // embedded tracker TRACKER_STATUS, TRACKER_PORT, + TRACKER_PORT_FORWARDING, // libtorrent section LIBTORRENT_HEADER, ASYNC_IO_THREADS, @@ -292,7 +293,9 @@ void AdvancedSettings::saveAdvancedSettings() const // Tracker pref->setTrackerPort(m_spinBoxTrackerPort.value()); + pref->setTrackerPortForwardingEnabled(m_checkBoxTrackerPortForwarding.isChecked()); session->setTrackerEnabled(m_checkBoxTrackerStatus.isChecked()); + // Choking algorithm session->setChokingAlgorithm(m_comboBoxChokingAlgorithm.currentData().value()); // Seed choking algorithm @@ -732,6 +735,9 @@ void AdvancedSettings::loadAdvancedSettings() m_spinBoxTrackerPort.setMaximum(65535); m_spinBoxTrackerPort.setValue(pref->getTrackerPort()); addRow(TRACKER_PORT, tr("Embedded tracker port"), &m_spinBoxTrackerPort); + // Tracker port forwarding + m_checkBoxTrackerPortForwarding.setChecked(pref->isTrackerPortForwardingEnabled()); + addRow(TRACKER_PORT_FORWARDING, tr("Enable port forwarding for embedded tracker"), &m_checkBoxTrackerPortForwarding); // Choking algorithm m_comboBoxChokingAlgorithm.addItem(tr("Fixed slots"), QVariant::fromValue(BitTorrent::ChokingAlgorithm::FixedSlots)); m_comboBoxChokingAlgorithm.addItem(tr("Upload rate based"), QVariant::fromValue(BitTorrent::ChokingAlgorithm::RateBased)); diff --git a/src/gui/advancedsettings.h b/src/gui/advancedsettings.h index 7846ca5c6..79e3146b2 100644 --- a/src/gui/advancedsettings.h +++ b/src/gui/advancedsettings.h @@ -68,7 +68,7 @@ private: m_spinBoxSavePathHistoryLength, m_spinBoxPeerTurnover, m_spinBoxPeerTurnoverCutoff, m_spinBoxPeerTurnoverInterval, m_spinBoxRequestQueueSize; QCheckBox m_checkBoxOsCache, m_checkBoxRecheckCompleted, m_checkBoxResolveCountries, m_checkBoxResolveHosts, m_checkBoxProgramNotifications, m_checkBoxTorrentAddedNotifications, m_checkBoxReannounceWhenAddressChanged, m_checkBoxTrackerFavicon, m_checkBoxTrackerStatus, - m_checkBoxConfirmTorrentRecheck, m_checkBoxConfirmRemoveAllTags, m_checkBoxAnnounceAllTrackers, m_checkBoxAnnounceAllTiers, + m_checkBoxTrackerPortForwarding, m_checkBoxConfirmTorrentRecheck, m_checkBoxConfirmRemoveAllTags, m_checkBoxAnnounceAllTrackers, m_checkBoxAnnounceAllTiers, m_checkBoxMultiConnectionsPerIp, m_checkBoxValidateHTTPSTrackerCertificate, m_checkBoxSSRFMitigation, m_checkBoxBlockPeersOnPrivilegedPorts, m_checkBoxPieceExtentAffinity, m_checkBoxSuggestMode, m_checkBoxSpeedWidgetEnabled, m_checkBoxIDNSupport; QComboBox m_comboBoxInterface, m_comboBoxInterfaceAddress, m_comboBoxDiskIOReadMode, m_comboBoxDiskIOWriteMode, m_comboBoxUtpMixedMode, m_comboBoxChokingAlgorithm, diff --git a/src/webui/api/appcontroller.cpp b/src/webui/api/appcontroller.cpp index c8d66ea64..0b82613c7 100644 --- a/src/webui/api/appcontroller.cpp +++ b/src/webui/api/appcontroller.cpp @@ -372,6 +372,7 @@ void AppController::preferencesAction() // Embedded tracker data[u"enable_embedded_tracker"_qs] = session->isTrackerEnabled(); data[u"embedded_tracker_port"_qs] = pref->getTrackerPort(); + data[u"embedded_tracker_port_forwarding"_qs] = pref->isTrackerPortForwardingEnabled(); // Choking algorithm data[u"upload_slots_behavior"_qs] = static_cast(session->chokingAlgorithm()); // Seed choking algorithm @@ -899,6 +900,8 @@ void AppController::setPreferencesAction() // Embedded tracker if (hasKey(u"embedded_tracker_port"_qs)) pref->setTrackerPort(it.value().toInt()); + if (hasKey(u"embedded_tracker_port_forwarding"_qs)) + pref->setTrackerPortForwardingEnabled(it.value().toBool()); if (hasKey(u"enable_embedded_tracker"_qs)) session->setTrackerEnabled(it.value().toBool()); // Choking algorithm diff --git a/src/webui/webapplication.h b/src/webui/webapplication.h index df58992ba..54a3f3c4e 100644 --- a/src/webui/webapplication.h +++ b/src/webui/webapplication.h @@ -52,7 +52,7 @@ #include "base/utils/version.h" #include "api/isessionmanager.h" -inline const Utils::Version<3, 2> API_VERSION {2, 8, 17}; +inline const Utils::Version<3, 2> API_VERSION {2, 8, 18}; class APIController; class AuthController; diff --git a/src/webui/www/private/views/preferences.html b/src/webui/www/private/views/preferences.html index fbc237828..78f1b42c5 100644 --- a/src/webui/www/private/views/preferences.html +++ b/src/webui/www/private/views/preferences.html @@ -1016,6 +1016,14 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD + + + + + + + +
@@ -2099,6 +2107,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD $('blockPeersOnPrivilegedPorts').setProperty('checked', pref.block_peers_on_privileged_ports); $('enableEmbeddedTracker').setProperty('checked', pref.enable_embedded_tracker); $('embeddedTrackerPort').setProperty('value', pref.embedded_tracker_port); + $('embeddedTrackerPortForwarding').setProperty('checked', pref.embedded_tracker_port_forwarding); $('uploadSlotsBehavior').setProperty('value', pref.upload_slots_behavior); $('uploadChokingAlgorithm').setProperty('value', pref.upload_choking_algorithm); $('announceAllTrackers').setProperty('checked', pref.announce_to_all_trackers); @@ -2526,6 +2535,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD settings.set('block_peers_on_privileged_ports', $('blockPeersOnPrivilegedPorts').getProperty('checked')); settings.set('enable_embedded_tracker', $('enableEmbeddedTracker').getProperty('checked')); settings.set('embedded_tracker_port', $('embeddedTrackerPort').getProperty('value')); + settings.set('embedded_tracker_port_forwarding', $('embeddedTrackerPortForwarding').getProperty('checked')); settings.set('upload_slots_behavior', $('uploadSlotsBehavior').getProperty('value')); settings.set('upload_choking_algorithm', $('uploadChokingAlgorithm').getProperty('value')); settings.set('announce_to_all_trackers', $('announceAllTrackers').getProperty('checked')); From 27a69d9cca1003e8099395a1aaa2f4ab91b017be Mon Sep 17 00:00:00 2001 From: sledgehammer999 Date: Sat, 5 Nov 2022 01:33:17 +0200 Subject: [PATCH 05/14] Fine tune translations loading for Chinese locales Closes #17506 --- .tx/config | 4 ++-- src/lang/lang.qrc | 2 +- src/lang/{qbittorrent_zh.ts => qbittorrent_zh_CN.ts} | 0 src/webui/www/translations/webui_translations.qrc | 2 +- src/webui/www/translations/{webui_zh.ts => webui_zh_CN.ts} | 0 5 files changed, 4 insertions(+), 4 deletions(-) rename src/lang/{qbittorrent_zh.ts => qbittorrent_zh_CN.ts} (100%) rename src/webui/www/translations/{webui_zh.ts => webui_zh_CN.ts} (100%) diff --git a/.tx/config b/.tx/config index 2b8d3296e..28fa38bf9 100644 --- a/.tx/config +++ b/.tx/config @@ -3,7 +3,7 @@ host = https://www.transifex.com [qbittorrent.qbittorrent_master] file_filter = src/lang/qbittorrent_.ts -lang_map = pt: pt_PT +lang_map = pt: pt_PT, zh: zh_CN source_file = src/lang/qbittorrent_en.ts source_lang = en type = QT @@ -19,7 +19,7 @@ mode = developer [qbittorrent.qbittorrent_webui] file_filter = src/webui/www/translations/webui_.ts -lang_map = pt: pt_PT +lang_map = pt: pt_PT, zh: zh_CN source_file = src/webui/www/translations/webui_en.ts source_lang = en type = QT diff --git a/src/lang/lang.qrc b/src/lang/lang.qrc index 2657f5374..62908ac87 100644 --- a/src/lang/lang.qrc +++ b/src/lang/lang.qrc @@ -53,7 +53,7 @@ qbittorrent_uk.qm qbittorrent_uz@Latn.qm qbittorrent_vi.qm - qbittorrent_zh.qm + qbittorrent_zh_CN.qm qbittorrent_zh_HK.qm qbittorrent_zh_TW.qm diff --git a/src/lang/qbittorrent_zh.ts b/src/lang/qbittorrent_zh_CN.ts similarity index 100% rename from src/lang/qbittorrent_zh.ts rename to src/lang/qbittorrent_zh_CN.ts diff --git a/src/webui/www/translations/webui_translations.qrc b/src/webui/www/translations/webui_translations.qrc index c4287cc02..cbb6a9a19 100644 --- a/src/webui/www/translations/webui_translations.qrc +++ b/src/webui/www/translations/webui_translations.qrc @@ -53,7 +53,7 @@ webui_uk.qm webui_uz@Latn.qm webui_vi.qm - webui_zh.qm + webui_zh_CN.qm webui_zh_HK.qm webui_zh_TW.qm diff --git a/src/webui/www/translations/webui_zh.ts b/src/webui/www/translations/webui_zh_CN.ts similarity index 100% rename from src/webui/www/translations/webui_zh.ts rename to src/webui/www/translations/webui_zh_CN.ts From 4e98b7f0cf6d4565cc7cdbc76f286a9588da9af9 Mon Sep 17 00:00:00 2001 From: BallsOfSpaghetti <115404710+OpenSpaghettiSauce@users.noreply.github.com> Date: Tue, 8 Nov 2022 04:31:18 +0100 Subject: [PATCH 06/14] Add confirmation to resume/pause all This adds a confirmation dialog to Pause All and Resume All. First I wanted to only add it in Tray, but honestly, clicking around in the menu, using hotkeys might trigger it just as easy. Closes #17683. PR #17945. --- src/gui/transferlistwidget.cpp | 18 ++++++++++++++ src/webui/www/private/scripts/mocha-init.js | 26 +++++++++++++++++---- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/gui/transferlistwidget.cpp b/src/gui/transferlistwidget.cpp index 93e8906b7..196334702 100644 --- a/src/gui/transferlistwidget.cpp +++ b/src/gui/transferlistwidget.cpp @@ -363,12 +363,30 @@ void TransferListWidget::setSelectedTorrentsLocation() void TransferListWidget::pauseAllTorrents() { + // Show confirmation if user would really like to Pause All + const QMessageBox::StandardButton ret = + QMessageBox::question(this, tr("Confirm pause") + , tr("Would you like to pause all torrents?") + , (QMessageBox::Yes | QMessageBox::No)); + + if (ret != QMessageBox::Yes) + return; + for (BitTorrent::Torrent *const torrent : asConst(BitTorrent::Session::instance()->torrents())) torrent->pause(); } void TransferListWidget::resumeAllTorrents() { + // Show confirmation if user would really like to Resume All + const QMessageBox::StandardButton ret = + QMessageBox::question(this, tr("Confirm resume") + , tr("Would you like to resume all torrents?") + , (QMessageBox::Yes | QMessageBox::No)); + + if (ret != QMessageBox::Yes) + return; + for (BitTorrent::Torrent *const torrent : asConst(BitTorrent::Session::instance()->torrents())) torrent->resume(); } diff --git a/src/webui/www/private/scripts/mocha-init.js b/src/webui/www/private/scripts/mocha-init.js index dd93e4d4c..87748bf85 100644 --- a/src/webui/www/private/scripts/mocha-init.js +++ b/src/webui/www/private/scripts/mocha-init.js @@ -979,18 +979,34 @@ const initializeWindows = function() { } }; - ['pause', 'resume'].each(function(item) { - addClickEvent(item + 'All', function(e) { - new Event(e).stop(); + addClickEvent('pauseAll', (e) => { + new Event(e).stop(); + + if (confirm('QBT_TR(Would you like to pause all torrents?)QBT_TR[CONTEXT=MainWindow]')) { new Request({ - url: 'api/v2/torrents/' + item, + url: 'api/v2/torrents/pause', method: 'post', data: { hashes: "all" } }).send(); updateMainData(); - }); + } + }); + + addClickEvent('resumeAll', (e) => { + new Event(e).stop(); + + if (confirm('QBT_TR(Would you like to resume all torrents?)QBT_TR[CONTEXT=MainWindow]')) { + new Request({ + url: 'api/v2/torrents/resume', + method: 'post', + data: { + hashes: "all" + } + }).send(); + updateMainData(); + } }); ['pause', 'resume', 'recheck'].each(function(item) { From 3970d91d191d05d5b8c234b04618ba8324a770be Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Wed, 2 Nov 2022 12:21:05 +0800 Subject: [PATCH 07/14] Fix typos --- src/app/application.cpp | 2 +- src/base/net/downloadmanager.cpp | 2 +- src/gui/statusbar.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/application.cpp b/src/app/application.cpp index 4af731821..1b1bcfb85 100644 --- a/src/app/application.cpp +++ b/src/app/application.cpp @@ -882,7 +882,7 @@ void Application::createStartupProgressDialog() m_startupProgressDialog = new QProgressDialog(tr("Loading torrents..."), tr("Exit"), 0, 100); m_startupProgressDialog->setAttribute(Qt::WA_DeleteOnClose); m_startupProgressDialog->setWindowFlag(Qt::WindowMinimizeButtonHint); - m_startupProgressDialog->setMinimumDuration(0); // Show dialog immediatelly by default + m_startupProgressDialog->setMinimumDuration(0); // Show dialog immediately by default m_startupProgressDialog->setAutoReset(false); m_startupProgressDialog->setAutoClose(false); diff --git a/src/base/net/downloadmanager.cpp b/src/base/net/downloadmanager.cpp index ac3022c6d..da5c80c43 100644 --- a/src/base/net/downloadmanager.cpp +++ b/src/base/net/downloadmanager.cpp @@ -124,7 +124,7 @@ namespace // Spoof HTTP Referer to allow adding torrent link from Torcache/KickAssTorrents request.setRawHeader("Referer", request.url().toEncoded().data()); #ifdef QT_NO_COMPRESS - // The macro "QT_NO_COMPRESS" defined in QT will disable the zlib releated features + // The macro "QT_NO_COMPRESS" defined in QT will disable the zlib related features // and reply data auto-decompression in QT will also be disabled. But we can support // gzip encoding and manually decompress the reply data. request.setRawHeader("Accept-Encoding", "gzip"); diff --git a/src/gui/statusbar.cpp b/src/gui/statusbar.cpp index 09a6f290a..0f4bda38e 100644 --- a/src/gui/statusbar.cpp +++ b/src/gui/statusbar.cpp @@ -48,7 +48,7 @@ StatusBar::StatusBar(QWidget *parent) { #ifndef Q_OS_MACOS // Redefining global stylesheet breaks certain elements on mac like tabs. - // Qt checks whether the stylesheet class inherts("QMacStyle") and this becomes false. + // Qt checks whether the stylesheet class inherits("QMacStyle") and this becomes false. setStyleSheet(u"QStatusBar::item { border-width: 0; }"_qs); #endif From 0758109d15dc16aababb9db3d12ff23eaea1ee02 Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Wed, 2 Nov 2022 16:35:40 +0800 Subject: [PATCH 08/14] Reserve space before appending elements --- src/gui/transferlistmodel.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/gui/transferlistmodel.cpp b/src/gui/transferlistmodel.cpp index a1a372e30..14c8c4cb8 100644 --- a/src/gui/transferlistmodel.cpp +++ b/src/gui/transferlistmodel.cpp @@ -632,9 +632,12 @@ bool TransferListModel::setData(const QModelIndex &index, const QVariant &value, void TransferListModel::addTorrents(const QVector &torrents) { - int row = m_torrentList.size(); - beginInsertRows({}, row, (row + torrents.size())); + qsizetype row = m_torrentList.size(); + const qsizetype total = row + torrents.size(); + beginInsertRows({}, row, total); + + m_torrentList.reserve(total); for (BitTorrent::Torrent *torrent : torrents) { Q_ASSERT(!m_torrentMap.contains(torrent)); From a5a242377b07f223c2669b4a6559191785093e24 Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Sat, 5 Nov 2022 02:44:44 +0800 Subject: [PATCH 09/14] Clean up code --- src/gui/torrentcontenttreeview.cpp | 18 ++++++------------ src/gui/torrentcontenttreeview.h | 2 +- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/gui/torrentcontenttreeview.cpp b/src/gui/torrentcontenttreeview.cpp index 984baca52..81a90e2ee 100644 --- a/src/gui/torrentcontenttreeview.cpp +++ b/src/gui/torrentcontenttreeview.cpp @@ -79,25 +79,19 @@ void TorrentContentTreeView::keyPressEvent(QKeyEvent *event) event->accept(); - QModelIndex current = currentNameCell(); - - QVariant value = current.data(Qt::CheckStateRole); + const QVariant value = currentNameCell().data(Qt::CheckStateRole); if (!value.isValid()) { Q_ASSERT(false); return; } - Qt::CheckState state = (static_cast(value.toInt()) == Qt::Checked - ? Qt::Unchecked : Qt::Checked); - + const Qt::CheckState state = (static_cast(value.toInt()) == Qt::Checked) + ? Qt::Unchecked : Qt::Checked; const QModelIndexList selection = selectionModel()->selectedRows(TorrentContentModelItem::COL_NAME); for (const QModelIndex &index : selection) - { - Q_ASSERT(index.column() == TorrentContentModelItem::COL_NAME); model()->setData(index, state, Qt::CheckStateRole); - } } void TorrentContentTreeView::renameSelectedFile(BitTorrent::AbstractFileStorage &fileStorage) @@ -142,16 +136,16 @@ void TorrentContentTreeView::renameSelectedFile(BitTorrent::AbstractFileStorage } } -QModelIndex TorrentContentTreeView::currentNameCell() +QModelIndex TorrentContentTreeView::currentNameCell() const { - QModelIndex current = currentIndex(); + const QModelIndex current = currentIndex(); if (!current.isValid()) { Q_ASSERT(false); return {}; } - return model()->index(current.row(), TorrentContentModelItem::COL_NAME, current.parent()); + return current.siblingAtColumn(TorrentContentModelItem::COL_NAME); } void TorrentContentTreeView::wheelEvent(QWheelEvent *event) diff --git a/src/gui/torrentcontenttreeview.h b/src/gui/torrentcontenttreeview.h index 1e8a5cfa7..f03be6a2f 100644 --- a/src/gui/torrentcontenttreeview.h +++ b/src/gui/torrentcontenttreeview.h @@ -49,6 +49,6 @@ public: void renameSelectedFile(BitTorrent::AbstractFileStorage &fileStorage); private: - QModelIndex currentNameCell(); + QModelIndex currentNameCell() const; void wheelEvent(QWheelEvent *event) override; }; From e62f9ef56a55d5b3db68e322f631dd2b8b0c156b Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Sun, 6 Nov 2022 15:10:23 +0800 Subject: [PATCH 10/14] Move increment out of loop --- src/base/bittorrent/sessionimpl.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/base/bittorrent/sessionimpl.cpp b/src/base/bittorrent/sessionimpl.cpp index f872ed0ad..53feac5da 100644 --- a/src/base/bittorrent/sessionimpl.cpp +++ b/src/base/bittorrent/sessionimpl.cpp @@ -2916,10 +2916,8 @@ void SessionImpl::saveResumeData() saveTorrentsQueue(); for (const TorrentImpl *torrent : asConst(m_torrents)) - { torrent->nativeHandle().save_resume_data(lt::torrent_handle::only_if_modified); - ++m_numResumeData; - } + m_numResumeData += m_torrents.size(); QElapsedTimer timer; timer.start(); From 507ced2fa27ae98c429b25b45af0ad3daf739713 Mon Sep 17 00:00:00 2001 From: Vladimir Golovnev Date: Wed, 9 Nov 2022 08:02:34 +0300 Subject: [PATCH 11/14] Avoid blocking call when changing libtorrent session settings We don't really need to get currently used settings pack in order to apply changes to session settings. It is enough to apply settings pack that contains only updated settings. PR #17989. --- src/base/bittorrent/portforwarderimpl.cpp | 8 +-- src/base/bittorrent/sessionimpl.cpp | 74 +++++++++++------------ src/base/bittorrent/sessionimpl.h | 9 ++- 3 files changed, 42 insertions(+), 49 deletions(-) diff --git a/src/base/bittorrent/portforwarderimpl.cpp b/src/base/bittorrent/portforwarderimpl.cpp index a6b9e3275..6ed58329e 100644 --- a/src/base/bittorrent/portforwarderimpl.cpp +++ b/src/base/bittorrent/portforwarderimpl.cpp @@ -107,10 +107,10 @@ void PortForwarderImpl::removePorts(const QString &profile) void PortForwarderImpl::start() { - lt::settings_pack settingsPack = m_provider->get_settings(); + lt::settings_pack settingsPack; settingsPack.set_bool(lt::settings_pack::enable_upnp, true); settingsPack.set_bool(lt::settings_pack::enable_natpmp, true); - m_provider->apply_settings(settingsPack); + m_provider->apply_settings(std::move(settingsPack)); for (auto profileIter = m_portProfiles.begin(); profileIter != m_portProfiles.end(); ++profileIter) { @@ -129,10 +129,10 @@ void PortForwarderImpl::start() void PortForwarderImpl::stop() { - lt::settings_pack settingsPack = m_provider->get_settings(); + lt::settings_pack settingsPack; settingsPack.set_bool(lt::settings_pack::enable_upnp, false); settingsPack.set_bool(lt::settings_pack::enable_natpmp, false); - m_provider->apply_settings(settingsPack); + m_provider->apply_settings(std::move(settingsPack)); // don't clear m_portProfiles so a later `start()` call can restore the port forwardings for (auto profileIter = m_portProfiles.begin(); profileIter != m_portProfiles.end(); ++profileIter) diff --git a/src/base/bittorrent/sessionimpl.cpp b/src/base/bittorrent/sessionimpl.cpp index 53feac5da..2b3e2cc0c 100644 --- a/src/base/bittorrent/sessionimpl.cpp +++ b/src/base/bittorrent/sessionimpl.cpp @@ -1057,25 +1057,25 @@ void SessionImpl::adjustLimits() { if (isQueueingSystemEnabled()) { - lt::settings_pack settingsPack = m_nativeSession->get_settings(); - adjustLimits(settingsPack); - m_nativeSession->apply_settings(settingsPack); + lt::settings_pack settingsPack; + // Internally increase the queue limits to ensure that the magnet is started + settingsPack.set_int(lt::settings_pack::active_downloads, adjustLimit(maxActiveDownloads())); + settingsPack.set_int(lt::settings_pack::active_limit, adjustLimit(maxActiveTorrents())); + m_nativeSession->apply_settings(std::move(settingsPack)); } } void SessionImpl::applyBandwidthLimits() { - lt::settings_pack settingsPack = m_nativeSession->get_settings(); - applyBandwidthLimits(settingsPack); - m_nativeSession->apply_settings(settingsPack); + lt::settings_pack settingsPack; + settingsPack.set_int(lt::settings_pack::download_rate_limit, downloadSpeedLimit()); + settingsPack.set_int(lt::settings_pack::upload_rate_limit, uploadSpeedLimit()); + m_nativeSession->apply_settings(std::move(settingsPack)); } void SessionImpl::configure() { - lt::settings_pack settingsPack = m_nativeSession->get_settings(); - loadLTSettings(settingsPack); - m_nativeSession->apply_settings(settingsPack); - + m_nativeSession->apply_settings(loadLTSettings()); configureComponents(); m_deferredConfigureScheduled = false; @@ -1426,10 +1426,11 @@ void SessionImpl::endStartup(ResumeSessionContext *context) void SessionImpl::initializeNativeSession() { - const std::string peerId = lt::generate_fingerprint(PEER_ID, QBT_VERSION_MAJOR, QBT_VERSION_MINOR, QBT_VERSION_BUGFIX, QBT_VERSION_BUILD); + lt::settings_pack pack = loadLTSettings(); - lt::settings_pack pack; + const std::string peerId = lt::generate_fingerprint(PEER_ID, QBT_VERSION_MAJOR, QBT_VERSION_MINOR, QBT_VERSION_BUGFIX, QBT_VERSION_BUILD); pack.set_str(lt::settings_pack::peer_fingerprint, peerId); + pack.set_bool(lt::settings_pack::listen_system_port_fallback, false); pack.set_str(lt::settings_pack::user_agent, USER_AGENT.toStdString()); pack.set_bool(lt::settings_pack::use_dht_as_fallback, false); @@ -1446,8 +1447,7 @@ void SessionImpl::initializeNativeSession() pack.set_bool(lt::settings_pack::enable_set_file_valid_data, true); #endif - loadLTSettings(pack); - lt::session_params sessionParams {pack, {}}; + lt::session_params sessionParams {std::move(pack), {}}; #ifdef QBT_USES_LIBTORRENT2 switch (diskIOType()) { @@ -1505,28 +1505,14 @@ void SessionImpl::processBannedIPs(lt::ip_filter &filter) } } -void SessionImpl::adjustLimits(lt::settings_pack &settingsPack) const +int SessionImpl::adjustLimit(const int limit) const { - // Internally increase the queue limits to ensure that the magnet is started - const auto adjustLimit = [this](const int limit) -> int - { - if (limit <= -1) - return limit; - // check for overflow: (limit + m_extraLimit) < std::numeric_limits::max() - return (m_extraLimit < (std::numeric_limits::max() - limit)) - ? (limit + m_extraLimit) - : std::numeric_limits::max(); - }; - - settingsPack.set_int(lt::settings_pack::active_downloads, adjustLimit(maxActiveDownloads())); - settingsPack.set_int(lt::settings_pack::active_limit, adjustLimit(maxActiveTorrents())); -} - -void SessionImpl::applyBandwidthLimits(lt::settings_pack &settingsPack) const -{ - const bool altSpeedLimitEnabled = isAltGlobalSpeedLimitEnabled(); - settingsPack.set_int(lt::settings_pack::download_rate_limit, altSpeedLimitEnabled ? altGlobalDownloadSpeedLimit() : globalDownloadSpeedLimit()); - settingsPack.set_int(lt::settings_pack::upload_rate_limit, altSpeedLimitEnabled ? altGlobalUploadSpeedLimit() : globalUploadSpeedLimit()); + if (limit <= -1) + return limit; + // check for overflow: (limit + m_extraLimit) < std::numeric_limits::max() + return (m_extraLimit < (std::numeric_limits::max() - limit)) + ? (limit + m_extraLimit) + : std::numeric_limits::max(); } void SessionImpl::initMetrics() @@ -1571,8 +1557,10 @@ void SessionImpl::initMetrics() m_metricIndices.disk.diskJobTime = findMetricIndex("disk.disk_job_time"); } -void SessionImpl::loadLTSettings(lt::settings_pack &settingsPack) +lt::settings_pack SessionImpl::loadLTSettings() const { + lt::settings_pack settingsPack; + const lt::alert_category_t alertMask = lt::alert::error_notification | lt::alert::file_progress_notification | lt::alert::ip_block_notification @@ -1590,8 +1578,10 @@ void SessionImpl::loadLTSettings(lt::settings_pack &settingsPack) // It will not take affect until the listen_interfaces settings is updated settingsPack.set_int(lt::settings_pack::listen_queue_size, socketBacklogSize()); - configureNetworkInterfaces(settingsPack); - applyBandwidthLimits(settingsPack); + applyNetworkInterfacesSettings(settingsPack); + + settingsPack.set_int(lt::settings_pack::download_rate_limit, downloadSpeedLimit()); + settingsPack.set_int(lt::settings_pack::upload_rate_limit, uploadSpeedLimit()); // The most secure, rc4 only so that all streams are encrypted settingsPack.set_int(lt::settings_pack::allowed_enc_level, lt::settings_pack::pe_rc4); @@ -1726,7 +1716,9 @@ void SessionImpl::loadLTSettings(lt::settings_pack &settingsPack) // Queueing System if (isQueueingSystemEnabled()) { - adjustLimits(settingsPack); + // Internally increase the queue limits to ensure that the magnet is started + settingsPack.set_int(lt::settings_pack::active_downloads, adjustLimit(maxActiveDownloads())); + settingsPack.set_int(lt::settings_pack::active_limit, adjustLimit(maxActiveTorrents())); settingsPack.set_int(lt::settings_pack::active_seeds, maxActiveUploads()); settingsPack.set_bool(lt::settings_pack::dont_count_slow_torrents, ignoreSlowTorrentsForQueueing()); @@ -1842,9 +1834,11 @@ void SessionImpl::loadLTSettings(lt::settings_pack &settingsPack) settingsPack.set_int(lt::settings_pack::seed_choking_algorithm, lt::settings_pack::anti_leech); break; } + + return settingsPack; } -void SessionImpl::configureNetworkInterfaces(lt::settings_pack &settingsPack) +void SessionImpl::applyNetworkInterfacesSettings(lt::settings_pack &settingsPack) const { if (m_listenInterfaceConfigured) return; diff --git a/src/base/bittorrent/sessionimpl.h b/src/base/bittorrent/sessionimpl.h index d7a747f61..c788f9c53 100644 --- a/src/base/bittorrent/sessionimpl.h +++ b/src/base/bittorrent/sessionimpl.h @@ -474,11 +474,10 @@ namespace BitTorrent Q_INVOKABLE void configure(); void configureComponents(); void initializeNativeSession(); - void loadLTSettings(lt::settings_pack &settingsPack); - void configureNetworkInterfaces(lt::settings_pack &settingsPack); + lt::settings_pack loadLTSettings() const; + void applyNetworkInterfacesSettings(lt::settings_pack &settingsPack) const; void configurePeerClasses(); - void adjustLimits(lt::settings_pack &settingsPack) const; - void applyBandwidthLimits(lt::settings_pack &settingsPack) const; + int adjustLimit(int limit) const; void initMetrics(); void adjustLimits(); void applyBandwidthLimits(); @@ -553,7 +552,7 @@ namespace BitTorrent bool m_deferredConfigureScheduled = false; bool m_IPFilteringConfigured = false; - bool m_listenInterfaceConfigured = false; + mutable bool m_listenInterfaceConfigured = false; CachedSettingValue m_isDHTEnabled; CachedSettingValue m_isLSDEnabled; From 964bf3177595040a53ab430692df62687cf2d045 Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Thu, 10 Nov 2022 13:23:54 +0300 Subject: [PATCH 12/14] Use another workaround to update files tree view --- src/gui/torrentcontentmodel.cpp | 87 +++------------------------------ src/gui/torrentcontentmodel.h | 4 -- 2 files changed, 7 insertions(+), 84 deletions(-) diff --git a/src/gui/torrentcontentmodel.cpp b/src/gui/torrentcontentmodel.cpp index de1042259..58d8c7b16 100644 --- a/src/gui/torrentcontentmodel.cpp +++ b/src/gui/torrentcontentmodel.cpp @@ -221,11 +221,7 @@ void TorrentContentModel::updateFilesProgress(const QVector &fp) m_rootItem->recalculateProgress(); m_rootItem->recalculateAvailability(); - const QVector columns = - { - {TorrentContentModelItem::COL_PROGRESS, TorrentContentModelItem::COL_PROGRESS} - }; - notifySubtreeUpdated(index(0, 0), columns); + emit layoutChanged(); } void TorrentContentModel::updateFilesPriorities(const QVector &fprio) @@ -239,12 +235,7 @@ void TorrentContentModel::updateFilesPriorities(const QVectorsetPriority(static_cast(fprio[i])); - const QVector columns = - { - {TorrentContentModelItem::COL_NAME, TorrentContentModelItem::COL_NAME}, - {TorrentContentModelItem::COL_PRIO, TorrentContentModelItem::COL_PRIO} - }; - notifySubtreeUpdated(index(0, 0), columns); + emit layoutChanged(); } void TorrentContentModel::updateFilesAvailability(const QVector &fa) @@ -259,11 +250,7 @@ void TorrentContentModel::updateFilesAvailability(const QVector &fa) // Update folders progress in the tree m_rootItem->recalculateProgress(); - const QVector columns = - { - {TorrentContentModelItem::COL_AVAILABILITY, TorrentContentModelItem::COL_AVAILABILITY} - }; - notifySubtreeUpdated(index(0, 0), columns); + emit layoutChanged(); } QVector TorrentContentModel::getFilePriorities() const @@ -308,17 +295,13 @@ bool TorrentContentModel::setData(const QModelIndex &index, const QVariant &valu if (currentPrio != newPrio) { + emit layoutAboutToBeChanged(); item->setPriority(newPrio); // Update folders progress in the tree m_rootItem->recalculateProgress(); m_rootItem->recalculateAvailability(); - const QVector columns = - { - {TorrentContentModelItem::COL_NAME, TorrentContentModelItem::COL_NAME}, - {TorrentContentModelItem::COL_PRIO, TorrentContentModelItem::COL_PRIO} - }; - notifySubtreeUpdated(index, columns); + emit layoutChanged(); emit filteredFilesChanged(); return true; @@ -350,14 +333,9 @@ bool TorrentContentModel::setData(const QModelIndex &index, const QVariant &valu const auto newPrio = static_cast(value.toInt()); if (currentPrio != newPrio) { + emit layoutAboutToBeChanged(); item->setPriority(newPrio); - - const QVector columns = - { - {TorrentContentModelItem::COL_NAME, TorrentContentModelItem::COL_NAME}, - {TorrentContentModelItem::COL_PRIO, TorrentContentModelItem::COL_PRIO} - }; - notifySubtreeUpdated(index, columns); + emit layoutChanged(); if ((newPrio == BitTorrent::DownloadPriority::Ignored) || (currentPrio == BitTorrent::DownloadPriority::Ignored)) @@ -590,54 +568,3 @@ void TorrentContentModel::setupModelData(const BitTorrent::AbstractFileStorage & } emit layoutChanged(); } - -void TorrentContentModel::notifySubtreeUpdated(const QModelIndex &index, const QVector &columns) -{ - // For best performance, `columns` entries should be arranged from left to right - - Q_ASSERT(index.isValid()); - - // emit itself - for (const ColumnInterval &column : columns) - emit dataChanged(index.siblingAtColumn(column.first()), index.siblingAtColumn(column.last())); - - // propagate up the model - QModelIndex parentIndex = parent(index); - while (parentIndex.isValid()) - { - for (const ColumnInterval &column : columns) - emit dataChanged(parentIndex.siblingAtColumn(column.first()), parentIndex.siblingAtColumn(column.last())); - parentIndex = parent(parentIndex); - } - - // propagate down the model - QVector parentIndexes; - - if (hasChildren(index)) - parentIndexes.push_back(index); - - while (!parentIndexes.isEmpty()) - { - const QModelIndex parent = parentIndexes.takeLast(); - - const int childCount = rowCount(parent); - const QModelIndex child = this->index(0, 0, parent); - - // emit this generation - for (const ColumnInterval &column : columns) - { - const QModelIndex childTopLeft = child.siblingAtColumn(column.first()); - const QModelIndex childBottomRight = child.sibling((childCount - 1), column.last()); - emit dataChanged(childTopLeft, childBottomRight); - } - - // check generations further down - parentIndexes.reserve(childCount); - for (int i = 0; i < childCount; ++i) - { - const QModelIndex sibling = child.siblingAtRow(i); - if (hasChildren(sibling)) - parentIndexes.push_back(sibling); - } - } -} diff --git a/src/gui/torrentcontentmodel.h b/src/gui/torrentcontentmodel.h index 481cf2ead..84f00976a 100644 --- a/src/gui/torrentcontentmodel.h +++ b/src/gui/torrentcontentmodel.h @@ -81,10 +81,6 @@ signals: void filteredFilesChanged(); private: - using ColumnInterval = IndexInterval; - - void notifySubtreeUpdated(const QModelIndex &index, const QVector &columns); - TorrentContentModelFolder *m_rootItem = nullptr; QVector m_filesIndex; QFileIconProvider *m_fileIconProvider = nullptr; From 75ccce705e8192058af1881382b81cf60c8b8ead Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Thu, 10 Nov 2022 13:32:48 +0300 Subject: [PATCH 13/14] Correctly handle model resetting --- src/gui/torrentcontentmodel.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/gui/torrentcontentmodel.cpp b/src/gui/torrentcontentmodel.cpp index 58d8c7b16..9357fdc8e 100644 --- a/src/gui/torrentcontentmodel.cpp +++ b/src/gui/torrentcontentmodel.cpp @@ -519,7 +519,8 @@ void TorrentContentModel::setupModelData(const BitTorrent::AbstractFileStorage & if (filesCount <= 0) return; - emit layoutAboutToBeChanged(); + beginResetModel(); + // Initialize files_index array qDebug("Torrent contains %d files", filesCount); m_filesIndex.reserve(filesCount); @@ -566,5 +567,6 @@ void TorrentContentModel::setupModelData(const BitTorrent::AbstractFileStorage & lastParent->appendChild(fileItem); m_filesIndex.push_back(fileItem); } - emit layoutChanged(); + + endResetModel(); } From 076b3628b13ac60e27124740f130e146748d8794 Mon Sep 17 00:00:00 2001 From: Vladimir Golovnev Date: Sun, 13 Nov 2022 08:26:25 +0300 Subject: [PATCH 14/14] Save correct resume data when added new torrent PR #18003. --- src/base/bittorrent/sessionimpl.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/base/bittorrent/sessionimpl.cpp b/src/base/bittorrent/sessionimpl.cpp index 2b3e2cc0c..7176d0223 100644 --- a/src/base/bittorrent/sessionimpl.cpp +++ b/src/base/bittorrent/sessionimpl.cpp @@ -2765,9 +2765,6 @@ bool SessionImpl::addTorrent_impl(const std::variant &so p.flags |= lt::torrent_flags::duplicate_is_error; - // Prevent torrent from saving initial resume data twice - p.flags &= ~lt::torrent_flags::need_save_resume; - p.added_time = std::time(nullptr); // Limits @@ -5219,8 +5216,6 @@ TorrentImpl *SessionImpl::createTorrent(const lt::torrent_handle &nativeHandle, if (isRestored()) { - m_resumeDataStorage->store(torrent->id(), params); - // The following is useless for newly added magnet if (torrent->hasMetadata()) {