2 * Bittorrent Client using Qt and libtorrent.
3 * Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
4 * Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * In addition, as a special exception, the copyright holders give permission to
21 * link this program with the OpenSSL project's "OpenSSL" library (or with
22 * modified versions of it that use the same license as the "OpenSSL" library),
23 * and distribute the linked executables. You must obey the GNU General Public
24 * License in all respects for all of the code used other than "OpenSSL". If you
25 * modify file(s), you may extend this exception to your version of the file(s),
26 * but you are not obligated to do so. If you do not wish to do so, delete this
27 * exception statement from your version.
44 #include <libtorrent/alert_types.hpp>
45 #include <libtorrent/error_code.hpp>
46 #include <libtorrent/extensions/smart_ban.hpp>
47 #include <libtorrent/extensions/ut_metadata.hpp>
48 #include <libtorrent/extensions/ut_pex.hpp>
49 #include <libtorrent/ip_filter.hpp>
50 #include <libtorrent/magnet_uri.hpp>
51 #include <libtorrent/session.hpp>
52 #include <libtorrent/session_stats.hpp>
53 #include <libtorrent/session_status.hpp>
54 #include <libtorrent/torrent_info.hpp>
59 #include <QHostAddress>
60 #include <QNetworkAddressEntry>
61 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
62 #include <QNetworkConfigurationManager>
64 #include <QNetworkInterface>
65 #include <QRegularExpression>
71 #include "base/algorithm.h"
72 #include "base/exceptions.h"
73 #include "base/global.h"
74 #include "base/logger.h"
75 #include "base/net/downloadmanager.h"
76 #include "base/net/proxyconfigurationmanager.h"
77 #include "base/profile.h"
78 #include "base/torrentfileguard.h"
79 #include "base/torrentfilter.h"
80 #include "base/unicodestrings.h"
81 #include "base/utils/bytearray.h"
82 #include "base/utils/fs.h"
83 #include "base/utils/misc.h"
84 #include "base/utils/net.h"
85 #include "base/utils/random.h"
86 #include "base/version.h"
87 #include "bandwidthscheduler.h"
88 #include "bencoderesumedatastorage.h"
90 #include "customstorage.h"
91 #include "dbresumedatastorage.h"
92 #include "filesearcher.h"
93 #include "filterparserthread.h"
94 #include "loadtorrentparams.h"
95 #include "ltunderlyingtype.h"
96 #include "magneturi.h"
97 #include "nativesessionextension.h"
98 #include "portforwarderimpl.h"
99 #include "statistics.h"
100 #include "torrentimpl.h"
103 using namespace BitTorrent
;
107 const char PEER_ID
[] = "qB";
108 const char USER_AGENT
[] = "qBittorrent/" QBT_VERSION_2
;
110 void torrentQueuePositionUp(const lt::torrent_handle
&handle
)
114 handle
.queue_position_up();
116 catch (const std::exception
&exc
)
118 qDebug() << Q_FUNC_INFO
<< " fails: " << exc
.what();
122 void torrentQueuePositionDown(const lt::torrent_handle
&handle
)
126 handle
.queue_position_down();
128 catch (const std::exception
&exc
)
130 qDebug() << Q_FUNC_INFO
<< " fails: " << exc
.what();
134 void torrentQueuePositionTop(const lt::torrent_handle
&handle
)
138 handle
.queue_position_top();
140 catch (const std::exception
&exc
)
142 qDebug() << Q_FUNC_INFO
<< " fails: " << exc
.what();
146 void torrentQueuePositionBottom(const lt::torrent_handle
&handle
)
150 handle
.queue_position_bottom();
152 catch (const std::exception
&exc
)
154 qDebug() << Q_FUNC_INFO
<< " fails: " << exc
.what();
158 QStringMap
map_cast(const QVariantMap
&map
)
161 for (auto i
= map
.cbegin(); i
!= map
.cend(); ++i
)
162 result
[i
.key()] = i
.value().toString();
166 QVariantMap
map_cast(const QStringMap
&map
)
169 for (auto i
= map
.cbegin(); i
!= map
.cend(); ++i
)
170 result
[i
.key()] = i
.value();
174 QString
normalizePath(const QString
&path
)
176 QString tmp
= Utils::Fs::toUniformPath(path
.trimmed());
177 if (!tmp
.isEmpty() && !tmp
.endsWith('/'))
182 QString
normalizeSavePath(QString path
, const QString
&defaultPath
= specialFolderLocation(SpecialFolder::Downloads
))
184 path
= path
.trimmed();
186 path
= Utils::Fs::toUniformPath(defaultPath
.trimmed());
188 return normalizePath(path
);
191 QStringMap
expandCategories(const QStringMap
&categories
)
193 QStringMap expanded
= categories
;
195 for (auto i
= categories
.cbegin(); i
!= categories
.cend(); ++i
)
197 const QString
&category
= i
.key();
198 for (const QString
&subcat
: asConst(Session::expandCategory(category
)))
200 if (!expanded
.contains(subcat
))
201 expanded
[subcat
] = "";
208 QString
toString(const lt::socket_type_t socketType
)
212 #if (LIBTORRENT_VERSION_NUM >= 20000)
213 case lt::socket_type_t::http
:
214 return QLatin1String("HTTP");
215 case lt::socket_type_t::http_ssl
:
216 return QLatin1String("HTTP_SSL");
218 case lt::socket_type_t::i2p
:
219 return QLatin1String("I2P");
220 case lt::socket_type_t::socks5
:
221 return QLatin1String("SOCKS5");
222 #if (LIBTORRENT_VERSION_NUM >= 20000)
223 case lt::socket_type_t::socks5_ssl
:
224 return QLatin1String("SOCKS5_SSL");
226 case lt::socket_type_t::tcp
:
227 return QLatin1String("TCP");
228 case lt::socket_type_t::tcp_ssl
:
229 return QLatin1String("TCP_SSL");
230 #if (LIBTORRENT_VERSION_NUM >= 20000)
231 case lt::socket_type_t::utp
:
232 return QLatin1String("UTP");
234 case lt::socket_type_t::udp
:
235 return QLatin1String("UDP");
237 case lt::socket_type_t::utp_ssl
:
238 return QLatin1String("UTP_SSL");
240 return QLatin1String("INVALID");
243 QString
toString(const lt::address
&address
)
247 return QString::fromLatin1(address
.to_string().c_str());
249 catch (const std::exception
&)
251 // suppress conversion error
256 template <typename T
>
259 LowerLimited(T limit
, T ret
)
265 explicit LowerLimited(T limit
)
266 : LowerLimited(limit
, limit
)
270 T
operator()(T val
) const
272 return val
<= m_limit
? m_ret
: val
;
280 template <typename T
>
281 LowerLimited
<T
> lowerLimited(T limit
) { return LowerLimited
<T
>(limit
); }
283 template <typename T
>
284 LowerLimited
<T
> lowerLimited(T limit
, T ret
) { return LowerLimited
<T
>(limit
, ret
); }
286 template <typename T
>
287 auto clampValue(const T lower
, const T upper
)
289 return [lower
, upper
](const T value
) -> T
300 QString
convertIfaceNameToGuid(const QString
&name
)
302 // Under Windows XP or on Qt version <= 5.5 'name' will be a GUID already.
303 const QUuid
uuid(name
);
305 return uuid
.toString().toUpper(); // Libtorrent expects the GUID in uppercase
308 const LONG res
= ::ConvertInterfaceNameToLuidW(name
.toStdWString().c_str(), &luid
);
312 if (::ConvertInterfaceLuidToGuid(&luid
, &guid
) == 0)
313 return QUuid(guid
).toString().toUpper();
321 const int addTorrentParamsId
= qRegisterMetaType
<AddTorrentParams
>();
325 Session
*Session::m_instance
= nullptr;
327 #define BITTORRENT_KEY(name) "BitTorrent/" name
328 #define BITTORRENT_SESSION_KEY(name) BITTORRENT_KEY("Session/") name
330 Session::Session(QObject
*parent
)
332 , m_isDHTEnabled(BITTORRENT_SESSION_KEY("DHTEnabled"), true)
333 , m_isLSDEnabled(BITTORRENT_SESSION_KEY("LSDEnabled"), true)
334 , m_isPeXEnabled(BITTORRENT_SESSION_KEY("PeXEnabled"), true)
335 , m_isIPFilteringEnabled(BITTORRENT_SESSION_KEY("IPFilteringEnabled"), false)
336 , m_isTrackerFilteringEnabled(BITTORRENT_SESSION_KEY("TrackerFilteringEnabled"), false)
337 , m_IPFilterFile(BITTORRENT_SESSION_KEY("IPFilter"))
338 , m_announceToAllTrackers(BITTORRENT_SESSION_KEY("AnnounceToAllTrackers"), false)
339 , m_announceToAllTiers(BITTORRENT_SESSION_KEY("AnnounceToAllTiers"), true)
340 , m_asyncIOThreads(BITTORRENT_SESSION_KEY("AsyncIOThreadsCount"), 10)
341 , m_hashingThreads(BITTORRENT_SESSION_KEY("HashingThreadsCount"), 2)
342 , m_filePoolSize(BITTORRENT_SESSION_KEY("FilePoolSize"), 5000)
343 , m_checkingMemUsage(BITTORRENT_SESSION_KEY("CheckingMemUsageSize"), 32)
344 , m_diskCacheSize(BITTORRENT_SESSION_KEY("DiskCacheSize"), -1)
345 , m_diskCacheTTL(BITTORRENT_SESSION_KEY("DiskCacheTTL"), 60)
346 , m_useOSCache(BITTORRENT_SESSION_KEY("UseOSCache"), true)
348 , m_coalesceReadWriteEnabled(BITTORRENT_SESSION_KEY("CoalesceReadWrite"), true)
350 , m_coalesceReadWriteEnabled(BITTORRENT_SESSION_KEY("CoalesceReadWrite"), false)
352 , m_usePieceExtentAffinity(BITTORRENT_SESSION_KEY("PieceExtentAffinity"), false)
353 , m_isSuggestMode(BITTORRENT_SESSION_KEY("SuggestMode"), false)
354 , m_sendBufferWatermark(BITTORRENT_SESSION_KEY("SendBufferWatermark"), 500)
355 , m_sendBufferLowWatermark(BITTORRENT_SESSION_KEY("SendBufferLowWatermark"), 10)
356 , m_sendBufferWatermarkFactor(BITTORRENT_SESSION_KEY("SendBufferWatermarkFactor"), 50)
357 , m_connectionSpeed(BITTORRENT_SESSION_KEY("ConnectionSpeed"), 30)
358 , m_socketBacklogSize(BITTORRENT_SESSION_KEY("SocketBacklogSize"), 30)
359 , m_isAnonymousModeEnabled(BITTORRENT_SESSION_KEY("AnonymousModeEnabled"), false)
360 , m_isQueueingEnabled(BITTORRENT_SESSION_KEY("QueueingSystemEnabled"), false)
361 , m_maxActiveDownloads(BITTORRENT_SESSION_KEY("MaxActiveDownloads"), 3, lowerLimited(-1))
362 , m_maxActiveUploads(BITTORRENT_SESSION_KEY("MaxActiveUploads"), 3, lowerLimited(-1))
363 , m_maxActiveTorrents(BITTORRENT_SESSION_KEY("MaxActiveTorrents"), 5, lowerLimited(-1))
364 , m_ignoreSlowTorrentsForQueueing(BITTORRENT_SESSION_KEY("IgnoreSlowTorrentsForQueueing"), false)
365 , m_downloadRateForSlowTorrents(BITTORRENT_SESSION_KEY("SlowTorrentsDownloadRate"), 2)
366 , m_uploadRateForSlowTorrents(BITTORRENT_SESSION_KEY("SlowTorrentsUploadRate"), 2)
367 , m_slowTorrentsInactivityTimer(BITTORRENT_SESSION_KEY("SlowTorrentsInactivityTimer"), 60)
368 , m_outgoingPortsMin(BITTORRENT_SESSION_KEY("OutgoingPortsMin"), 0)
369 , m_outgoingPortsMax(BITTORRENT_SESSION_KEY("OutgoingPortsMax"), 0)
370 , m_UPnPLeaseDuration(BITTORRENT_SESSION_KEY("UPnPLeaseDuration"), 0)
371 , m_peerToS(BITTORRENT_SESSION_KEY("PeerToS"), 0x20)
372 , m_ignoreLimitsOnLAN(BITTORRENT_SESSION_KEY("IgnoreLimitsOnLAN"), false)
373 , m_includeOverheadInLimits(BITTORRENT_SESSION_KEY("IncludeOverheadInLimits"), false)
374 , m_announceIP(BITTORRENT_SESSION_KEY("AnnounceIP"))
375 , m_maxConcurrentHTTPAnnounces(BITTORRENT_SESSION_KEY("MaxConcurrentHTTPAnnounces"), 50)
376 , m_isReannounceWhenAddressChangedEnabled(BITTORRENT_SESSION_KEY("ReannounceWhenAddressChanged"), false)
377 , m_stopTrackerTimeout(BITTORRENT_SESSION_KEY("StopTrackerTimeout"), 5)
378 , m_maxConnections(BITTORRENT_SESSION_KEY("MaxConnections"), 500, lowerLimited(0, -1))
379 , m_maxUploads(BITTORRENT_SESSION_KEY("MaxUploads"), 20, lowerLimited(0, -1))
380 , m_maxConnectionsPerTorrent(BITTORRENT_SESSION_KEY("MaxConnectionsPerTorrent"), 100, lowerLimited(0, -1))
381 , m_maxUploadsPerTorrent(BITTORRENT_SESSION_KEY("MaxUploadsPerTorrent"), 4, lowerLimited(0, -1))
382 , m_btProtocol(BITTORRENT_SESSION_KEY("BTProtocol"), BTProtocol::Both
383 , clampValue(BTProtocol::Both
, BTProtocol::UTP
))
384 , m_isUTPRateLimited(BITTORRENT_SESSION_KEY("uTPRateLimited"), true)
385 , m_utpMixedMode(BITTORRENT_SESSION_KEY("uTPMixedMode"), MixedModeAlgorithm::TCP
386 , clampValue(MixedModeAlgorithm::TCP
, MixedModeAlgorithm::Proportional
))
387 , m_IDNSupportEnabled(BITTORRENT_SESSION_KEY("IDNSupportEnabled"), false)
388 , m_multiConnectionsPerIpEnabled(BITTORRENT_SESSION_KEY("MultiConnectionsPerIp"), false)
389 , m_validateHTTPSTrackerCertificate(BITTORRENT_SESSION_KEY("ValidateHTTPSTrackerCertificate"), true)
390 , m_blockPeersOnPrivilegedPorts(BITTORRENT_SESSION_KEY("BlockPeersOnPrivilegedPorts"), false)
391 , m_isAddTrackersEnabled(BITTORRENT_SESSION_KEY("AddTrackersEnabled"), false)
392 , m_additionalTrackers(BITTORRENT_SESSION_KEY("AdditionalTrackers"))
393 , m_globalMaxRatio(BITTORRENT_SESSION_KEY("GlobalMaxRatio"), -1, [](qreal r
) { return r
< 0 ? -1. : r
;})
394 , m_globalMaxSeedingMinutes(BITTORRENT_SESSION_KEY("GlobalMaxSeedingMinutes"), -1, lowerLimited(-1))
395 , m_isAddTorrentPaused(BITTORRENT_SESSION_KEY("AddTorrentPaused"), false)
396 , m_torrentContentLayout(BITTORRENT_SESSION_KEY("TorrentContentLayout"), TorrentContentLayout::Original
)
397 , m_isAppendExtensionEnabled(BITTORRENT_SESSION_KEY("AddExtensionToIncompleteFiles"), false)
398 , m_refreshInterval(BITTORRENT_SESSION_KEY("RefreshInterval"), 1500)
399 , m_isPreallocationEnabled(BITTORRENT_SESSION_KEY("Preallocation"), false)
400 , m_torrentExportDirectory(BITTORRENT_SESSION_KEY("TorrentExportDirectory"))
401 , m_finishedTorrentExportDirectory(BITTORRENT_SESSION_KEY("FinishedTorrentExportDirectory"))
402 , m_globalDownloadSpeedLimit(BITTORRENT_SESSION_KEY("GlobalDLSpeedLimit"), 0, lowerLimited(0))
403 , m_globalUploadSpeedLimit(BITTORRENT_SESSION_KEY("GlobalUPSpeedLimit"), 0, lowerLimited(0))
404 , m_altGlobalDownloadSpeedLimit(BITTORRENT_SESSION_KEY("AlternativeGlobalDLSpeedLimit"), 10, lowerLimited(0))
405 , m_altGlobalUploadSpeedLimit(BITTORRENT_SESSION_KEY("AlternativeGlobalUPSpeedLimit"), 10, lowerLimited(0))
406 , m_isAltGlobalSpeedLimitEnabled(BITTORRENT_SESSION_KEY("UseAlternativeGlobalSpeedLimit"), false)
407 , m_isBandwidthSchedulerEnabled(BITTORRENT_SESSION_KEY("BandwidthSchedulerEnabled"), false)
408 , m_saveResumeDataInterval(BITTORRENT_SESSION_KEY("SaveResumeDataInterval"), 60)
409 , m_port(BITTORRENT_SESSION_KEY("Port"), -1)
410 , m_useRandomPort(BITTORRENT_SESSION_KEY("UseRandomPort"), false)
411 , m_networkInterface(BITTORRENT_SESSION_KEY("Interface"))
412 , m_networkInterfaceName(BITTORRENT_SESSION_KEY("InterfaceName"))
413 , m_networkInterfaceAddress(BITTORRENT_SESSION_KEY("InterfaceAddress"))
414 , m_encryption(BITTORRENT_SESSION_KEY("Encryption"), 0)
415 , m_isProxyPeerConnectionsEnabled(BITTORRENT_SESSION_KEY("ProxyPeerConnections"), false)
416 , m_chokingAlgorithm(BITTORRENT_SESSION_KEY("ChokingAlgorithm"), ChokingAlgorithm::FixedSlots
417 , clampValue(ChokingAlgorithm::FixedSlots
, ChokingAlgorithm::RateBased
))
418 , m_seedChokingAlgorithm(BITTORRENT_SESSION_KEY("SeedChokingAlgorithm"), SeedChokingAlgorithm::FastestUpload
419 , clampValue(SeedChokingAlgorithm::RoundRobin
, SeedChokingAlgorithm::AntiLeech
))
420 , m_storedCategories(BITTORRENT_SESSION_KEY("Categories"))
421 , m_storedTags(BITTORRENT_SESSION_KEY("Tags"))
422 , m_maxRatioAction(BITTORRENT_SESSION_KEY("MaxRatioAction"), Pause
)
423 , m_defaultSavePath(BITTORRENT_SESSION_KEY("DefaultSavePath"), specialFolderLocation(SpecialFolder::Downloads
), normalizePath
)
424 , m_tempPath(BITTORRENT_SESSION_KEY("TempPath"), defaultSavePath() + "temp/", normalizePath
)
425 , m_isSubcategoriesEnabled(BITTORRENT_SESSION_KEY("SubcategoriesEnabled"), false)
426 , m_isTempPathEnabled(BITTORRENT_SESSION_KEY("TempPathEnabled"), false)
427 , m_isAutoTMMDisabledByDefault(BITTORRENT_SESSION_KEY("DisableAutoTMMByDefault"), true)
428 , m_isDisableAutoTMMWhenCategoryChanged(BITTORRENT_SESSION_KEY("DisableAutoTMMTriggers/CategoryChanged"), false)
429 , m_isDisableAutoTMMWhenDefaultSavePathChanged(BITTORRENT_SESSION_KEY("DisableAutoTMMTriggers/DefaultSavePathChanged"), true)
430 , m_isDisableAutoTMMWhenCategorySavePathChanged(BITTORRENT_SESSION_KEY("DisableAutoTMMTriggers/CategorySavePathChanged"), true)
431 , m_isTrackerEnabled(BITTORRENT_KEY("TrackerEnabled"), false)
432 , m_peerTurnover(BITTORRENT_SESSION_KEY("PeerTurnover"), 4)
433 , m_peerTurnoverCutoff(BITTORRENT_SESSION_KEY("PeerTurnoverCutOff"), 90)
434 , m_peerTurnoverInterval(BITTORRENT_SESSION_KEY("PeerTurnoverInterval"), 300)
435 , m_bannedIPs("State/BannedIPs"
437 , [](const QStringList
&value
)
439 QStringList tmp
= value
;
444 , m_resumeDataStorageType(BITTORRENT_SESSION_KEY("ResumeDataStorageType"), ResumeDataStorageType::Legacy
)
445 #if defined(Q_OS_WIN)
446 , m_OSMemoryPriority(BITTORRENT_KEY("OSMemoryPriority"), OSMemoryPriority::BelowNormal
)
448 , m_seedingLimitTimer
{new QTimer
{this}}
449 , m_resumeDataTimer
{new QTimer
{this}}
450 , m_statistics
{new Statistics
{this}}
451 , m_ioThread
{new QThread
{this}}
452 , m_recentErroredTorrentsTimer
{new QTimer
{this}}
453 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
454 , m_networkManager
{new QNetworkConfigurationManager
{this}}
458 m_port
= Utils::Random::rand(1024, 65535);
460 m_recentErroredTorrentsTimer
->setSingleShot(true);
461 m_recentErroredTorrentsTimer
->setInterval(1000);
462 connect(m_recentErroredTorrentsTimer
, &QTimer::timeout
463 , this, [this]() { m_recentErroredTorrents
.clear(); });
465 m_seedingLimitTimer
->setInterval(10000);
466 connect(m_seedingLimitTimer
, &QTimer::timeout
, this, &Session::processShareLimits
);
468 initializeNativeSession();
469 configureComponents();
471 if (isBandwidthSchedulerEnabled())
472 enableBandwidthScheduler();
474 m_categories
= map_cast(m_storedCategories
);
475 if (isSubcategoriesEnabled())
477 // if subcategories support changed manually
478 m_categories
= expandCategories(m_categories
);
479 m_storedCategories
= map_cast(m_categories
);
482 const QStringList storedTags
= m_storedTags
.get();
483 m_tags
= {storedTags
.cbegin(), storedTags
.cend()};
486 updateSeedingLimitTimer();
487 populateAdditionalTrackers();
489 enableTracker(isTrackerEnabled());
491 connect(Net::ProxyConfigurationManager::instance()
492 , &Net::ProxyConfigurationManager::proxyConfigurationChanged
493 , this, &Session::configureDeferred
);
495 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
496 // Network configuration monitor
497 connect(m_networkManager
, &QNetworkConfigurationManager::onlineStateChanged
, this, &Session::networkOnlineStateChanged
);
498 connect(m_networkManager
, &QNetworkConfigurationManager::configurationAdded
, this, &Session::networkConfigurationChange
);
499 connect(m_networkManager
, &QNetworkConfigurationManager::configurationRemoved
, this, &Session::networkConfigurationChange
);
500 connect(m_networkManager
, &QNetworkConfigurationManager::configurationChanged
, this, &Session::networkConfigurationChange
);
503 m_fileSearcher
= new FileSearcher
;
504 m_fileSearcher
->moveToThread(m_ioThread
);
505 connect(m_ioThread
, &QThread::finished
, m_fileSearcher
, &QObject::deleteLater
);
506 connect(m_fileSearcher
, &FileSearcher::searchFinished
, this, &Session::fileSearchFinished
);
510 // Regular saving of fastresume data
511 connect(m_resumeDataTimer
, &QTimer::timeout
, this, [this]() { generateResumeData(); });
512 const int saveInterval
= saveResumeDataInterval();
513 if (saveInterval
> 0)
515 m_resumeDataTimer
->setInterval(saveInterval
* 60 * 1000);
516 m_resumeDataTimer
->start();
519 // initialize PortForwarder instance
520 new PortForwarderImpl
{m_nativeSession
};
525 bool Session::isDHTEnabled() const
527 return m_isDHTEnabled
;
530 void Session::setDHTEnabled(bool enabled
)
532 if (enabled
!= m_isDHTEnabled
)
534 m_isDHTEnabled
= enabled
;
536 LogMsg(tr("DHT support [%1]").arg(enabled
? tr("ON") : tr("OFF")), Log::INFO
);
540 bool Session::isLSDEnabled() const
542 return m_isLSDEnabled
;
545 void Session::setLSDEnabled(const bool enabled
)
547 if (enabled
!= m_isLSDEnabled
)
549 m_isLSDEnabled
= enabled
;
551 LogMsg(tr("Local Peer Discovery support [%1]").arg(enabled
? tr("ON") : tr("OFF"))
556 bool Session::isPeXEnabled() const
558 return m_isPeXEnabled
;
561 void Session::setPeXEnabled(const bool enabled
)
563 m_isPeXEnabled
= enabled
;
564 if (m_wasPexEnabled
!= enabled
)
565 LogMsg(tr("Restart is required to toggle PeX support"), Log::WARNING
);
568 bool Session::isTempPathEnabled() const
570 return m_isTempPathEnabled
;
573 void Session::setTempPathEnabled(const bool enabled
)
575 if (enabled
!= isTempPathEnabled())
577 m_isTempPathEnabled
= enabled
;
578 for (TorrentImpl
*const torrent
: asConst(m_torrents
))
579 torrent
->handleTempPathChanged();
583 bool Session::isAppendExtensionEnabled() const
585 return m_isAppendExtensionEnabled
;
588 void Session::setAppendExtensionEnabled(const bool enabled
)
590 if (isAppendExtensionEnabled() != enabled
)
592 m_isAppendExtensionEnabled
= enabled
;
594 // append or remove .!qB extension for incomplete files
595 for (TorrentImpl
*const torrent
: asConst(m_torrents
))
596 torrent
->handleAppendExtensionToggled();
600 int Session::refreshInterval() const
602 return m_refreshInterval
;
605 void Session::setRefreshInterval(const int value
)
607 if (value
!= refreshInterval())
609 m_refreshInterval
= value
;
613 bool Session::isPreallocationEnabled() const
615 return m_isPreallocationEnabled
;
618 void Session::setPreallocationEnabled(const bool enabled
)
620 m_isPreallocationEnabled
= enabled
;
623 QString
Session::torrentExportDirectory() const
625 return Utils::Fs::toUniformPath(m_torrentExportDirectory
);
628 void Session::setTorrentExportDirectory(QString path
)
630 path
= Utils::Fs::toUniformPath(path
);
631 if (path
!= torrentExportDirectory())
632 m_torrentExportDirectory
= path
;
635 QString
Session::finishedTorrentExportDirectory() const
637 return Utils::Fs::toUniformPath(m_finishedTorrentExportDirectory
);
640 void Session::setFinishedTorrentExportDirectory(QString path
)
642 path
= Utils::Fs::toUniformPath(path
);
643 if (path
!= finishedTorrentExportDirectory())
644 m_finishedTorrentExportDirectory
= path
;
647 QString
Session::defaultSavePath() const
649 return Utils::Fs::toUniformPath(m_defaultSavePath
);
652 QString
Session::tempPath() const
654 return Utils::Fs::toUniformPath(m_tempPath
);
657 QString
Session::torrentTempPath(const TorrentInfo
&torrentInfo
) const
659 if ((torrentInfo
.filesCount() > 1) && !torrentInfo
.hasRootFolder())
660 return tempPath() + torrentInfo
.name() + '/';
665 bool Session::isValidCategoryName(const QString
&name
)
667 static const QRegularExpression
re(R
"(^([^\\\/]|[^\\\/]([^\\\/]|\/(?=[^\/]))*[^\\\/])$)");
668 if (!name
.isEmpty() && (name
.indexOf(re
) != 0))
670 qDebug() << "Incorrect category name:" << name
;
677 QStringList
Session::expandCategory(const QString
&category
)
680 if (!isValidCategoryName(category
))
684 while ((index
= category
.indexOf('/', index
)) >= 0)
686 result
<< category
.left(index
);
694 QStringMap
Session::categories() const
699 QString
Session::categorySavePath(const QString
&categoryName
) const
701 const QString basePath
= m_defaultSavePath
;
702 if (categoryName
.isEmpty()) return basePath
;
704 QString path
= m_categories
.value(categoryName
);
705 if (path
.isEmpty()) // use implicit save path
706 path
= Utils::Fs::toValidFileSystemName(categoryName
, true);
708 if (!QDir::isAbsolutePath(path
))
709 path
.prepend(basePath
);
711 return normalizeSavePath(path
);
714 bool Session::addCategory(const QString
&name
, const QString
&savePath
)
716 if (name
.isEmpty()) return false;
717 if (!isValidCategoryName(name
) || m_categories
.contains(name
))
720 if (isSubcategoriesEnabled())
722 for (const QString
&parent
: asConst(expandCategory(name
)))
724 if ((parent
!= name
) && !m_categories
.contains(parent
))
726 m_categories
[parent
] = "";
727 emit
categoryAdded(parent
);
732 m_categories
[name
] = savePath
;
733 m_storedCategories
= map_cast(m_categories
);
734 emit
categoryAdded(name
);
739 bool Session::editCategory(const QString
&name
, const QString
&savePath
)
741 if (!m_categories
.contains(name
)) return false;
742 if (categorySavePath(name
) == savePath
) return false;
744 m_categories
[name
] = savePath
;
745 m_storedCategories
= map_cast(m_categories
);
746 if (isDisableAutoTMMWhenCategorySavePathChanged())
748 for (TorrentImpl
*const torrent
: asConst(m_torrents
))
749 if (torrent
->category() == name
)
750 torrent
->setAutoTMMEnabled(false);
754 for (TorrentImpl
*const torrent
: asConst(m_torrents
))
755 if (torrent
->category() == name
)
756 torrent
->handleCategorySavePathChanged();
762 bool Session::removeCategory(const QString
&name
)
764 for (TorrentImpl
*const torrent
: asConst(m_torrents
))
765 if (torrent
->belongsToCategory(name
))
766 torrent
->setCategory("");
768 // remove stored category and its subcategories if exist
770 if (isSubcategoriesEnabled())
772 // remove subcategories
773 const QString test
= name
+ '/';
774 Algorithm::removeIf(m_categories
, [this, &test
, &result
](const QString
&category
, const QString
&)
776 if (category
.startsWith(test
))
779 emit
categoryRemoved(category
);
786 result
= (m_categories
.remove(name
) > 0) || result
;
790 // update stored categories
791 m_storedCategories
= map_cast(m_categories
);
792 emit
categoryRemoved(name
);
798 bool Session::isSubcategoriesEnabled() const
800 return m_isSubcategoriesEnabled
;
803 void Session::setSubcategoriesEnabled(const bool value
)
805 if (isSubcategoriesEnabled() == value
) return;
809 // expand categories to include all parent categories
810 m_categories
= expandCategories(m_categories
);
811 // update stored categories
812 m_storedCategories
= map_cast(m_categories
);
817 m_categories
= map_cast(m_storedCategories
);
820 m_isSubcategoriesEnabled
= value
;
821 emit
subcategoriesSupportChanged();
824 QSet
<QString
> Session::tags() const
829 bool Session::isValidTag(const QString
&tag
)
831 return (!tag
.trimmed().isEmpty() && !tag
.contains(','));
834 bool Session::hasTag(const QString
&tag
) const
836 return m_tags
.contains(tag
);
839 bool Session::addTag(const QString
&tag
)
841 if (!isValidTag(tag
) || hasTag(tag
))
845 m_storedTags
= m_tags
.values();
850 bool Session::removeTag(const QString
&tag
)
852 if (m_tags
.remove(tag
))
854 for (TorrentImpl
*const torrent
: asConst(m_torrents
))
855 torrent
->removeTag(tag
);
856 m_storedTags
= m_tags
.values();
857 emit
tagRemoved(tag
);
863 bool Session::isAutoTMMDisabledByDefault() const
865 return m_isAutoTMMDisabledByDefault
;
868 void Session::setAutoTMMDisabledByDefault(const bool value
)
870 m_isAutoTMMDisabledByDefault
= value
;
873 bool Session::isDisableAutoTMMWhenCategoryChanged() const
875 return m_isDisableAutoTMMWhenCategoryChanged
;
878 void Session::setDisableAutoTMMWhenCategoryChanged(const bool value
)
880 m_isDisableAutoTMMWhenCategoryChanged
= value
;
883 bool Session::isDisableAutoTMMWhenDefaultSavePathChanged() const
885 return m_isDisableAutoTMMWhenDefaultSavePathChanged
;
888 void Session::setDisableAutoTMMWhenDefaultSavePathChanged(const bool value
)
890 m_isDisableAutoTMMWhenDefaultSavePathChanged
= value
;
893 bool Session::isDisableAutoTMMWhenCategorySavePathChanged() const
895 return m_isDisableAutoTMMWhenCategorySavePathChanged
;
898 void Session::setDisableAutoTMMWhenCategorySavePathChanged(const bool value
)
900 m_isDisableAutoTMMWhenCategorySavePathChanged
= value
;
903 bool Session::isAddTorrentPaused() const
905 return m_isAddTorrentPaused
;
908 void Session::setAddTorrentPaused(const bool value
)
910 m_isAddTorrentPaused
= value
;
913 bool Session::isTrackerEnabled() const
915 return m_isTrackerEnabled
;
918 void Session::setTrackerEnabled(const bool enabled
)
920 if (m_isTrackerEnabled
!= enabled
)
921 m_isTrackerEnabled
= enabled
;
923 // call enableTracker() unconditionally, otherwise port change won't trigger
925 enableTracker(enabled
);
928 qreal
Session::globalMaxRatio() const
930 return m_globalMaxRatio
;
933 // Torrents with a ratio superior to the given value will
934 // be automatically deleted
935 void Session::setGlobalMaxRatio(qreal ratio
)
940 if (ratio
!= globalMaxRatio())
942 m_globalMaxRatio
= ratio
;
943 updateSeedingLimitTimer();
947 int Session::globalMaxSeedingMinutes() const
949 return m_globalMaxSeedingMinutes
;
952 void Session::setGlobalMaxSeedingMinutes(int minutes
)
957 if (minutes
!= globalMaxSeedingMinutes())
959 m_globalMaxSeedingMinutes
= minutes
;
960 updateSeedingLimitTimer();
967 // Do some BT related saving
970 // We must delete FilterParserThread
971 // before we delete lt::session
972 delete m_filterParser
;
974 // We must delete PortForwarderImpl before
975 // we delete lt::session
976 delete Net::PortForwarder::instance();
978 qDebug("Deleting the session");
979 delete m_nativeSession
;
985 void Session::initInstance()
988 m_instance
= new Session
;
991 void Session::freeInstance()
994 m_instance
= nullptr;
997 Session
*Session::instance()
1002 void Session::adjustLimits()
1004 if (isQueueingSystemEnabled())
1006 lt::settings_pack settingsPack
= m_nativeSession
->get_settings();
1007 adjustLimits(settingsPack
);
1008 m_nativeSession
->apply_settings(settingsPack
);
1012 void Session::applyBandwidthLimits()
1014 lt::settings_pack settingsPack
= m_nativeSession
->get_settings();
1015 applyBandwidthLimits(settingsPack
);
1016 m_nativeSession
->apply_settings(settingsPack
);
1019 void Session::configure()
1021 lt::settings_pack settingsPack
= m_nativeSession
->get_settings();
1022 loadLTSettings(settingsPack
);
1023 m_nativeSession
->apply_settings(settingsPack
);
1025 configureComponents();
1027 m_deferredConfigureScheduled
= false;
1030 void Session::configureComponents()
1032 // This function contains components/actions that:
1033 // 1. Need to be setup at start up
1034 // 2. When deferred configure is called
1036 configurePeerClasses();
1038 if (!m_IPFilteringConfigured
)
1040 if (isIPFilteringEnabled())
1044 m_IPFilteringConfigured
= true;
1047 #if defined(Q_OS_WIN)
1048 applyOSMemoryPriority();
1052 void Session::initializeNativeSession()
1054 const lt::alert_category_t alertMask
= lt::alert::error_notification
1055 | lt::alert::file_progress_notification
1056 | lt::alert::ip_block_notification
1057 | lt::alert::peer_notification
1058 | lt::alert::performance_warning
1059 | lt::alert::port_mapping_notification
1060 | lt::alert::status_notification
1061 | lt::alert::storage_notification
1062 | lt::alert::tracker_notification
;
1063 const std::string peerId
= lt::generate_fingerprint(PEER_ID
, QBT_VERSION_MAJOR
, QBT_VERSION_MINOR
, QBT_VERSION_BUGFIX
, QBT_VERSION_BUILD
);
1065 lt::settings_pack pack
;
1066 pack
.set_int(lt::settings_pack::alert_mask
, alertMask
);
1067 pack
.set_str(lt::settings_pack::peer_fingerprint
, peerId
);
1068 pack
.set_bool(lt::settings_pack::listen_system_port_fallback
, false);
1069 pack
.set_str(lt::settings_pack::user_agent
, USER_AGENT
);
1070 pack
.set_bool(lt::settings_pack::use_dht_as_fallback
, false);
1072 pack
.set_int(lt::settings_pack::auto_scrape_interval
, 1200); // 20 minutes
1073 pack
.set_int(lt::settings_pack::auto_scrape_min_interval
, 900); // 15 minutes
1074 // libtorrent 1.1 enables UPnP & NAT-PMP by default
1075 // turn them off before `lt::session` ctor to avoid split second effects
1076 pack
.set_bool(lt::settings_pack::enable_upnp
, false);
1077 pack
.set_bool(lt::settings_pack::enable_natpmp
, false);
1079 #if (LIBTORRENT_VERSION_NUM > 20000)
1080 // preserve the same behavior as in earlier libtorrent versions
1081 pack
.set_bool(lt::settings_pack::enable_set_file_valid_data
, true);
1084 loadLTSettings(pack
);
1085 lt::session_params sessionParams
{pack
, {}};
1086 #if (LIBTORRENT_VERSION_NUM >= 20000)
1087 sessionParams
.disk_io_constructor
= customDiskIOConstructor
;
1089 m_nativeSession
= new lt::session
{sessionParams
};
1091 LogMsg(tr("Peer ID: ") + QString::fromStdString(peerId
));
1092 LogMsg(tr("HTTP User-Agent is '%1'").arg(USER_AGENT
));
1093 LogMsg(tr("DHT support [%1]").arg(isDHTEnabled() ? tr("ON") : tr("OFF")), Log::INFO
);
1094 LogMsg(tr("Local Peer Discovery support [%1]").arg(isLSDEnabled() ? tr("ON") : tr("OFF")), Log::INFO
);
1095 LogMsg(tr("PeX support [%1]").arg(isPeXEnabled() ? tr("ON") : tr("OFF")), Log::INFO
);
1096 LogMsg(tr("Anonymous mode [%1]").arg(isAnonymousModeEnabled() ? tr("ON") : tr("OFF")), Log::INFO
);
1097 LogMsg(tr("Encryption support [%1]").arg((encryption() == 0) ? tr("ON") :
1098 ((encryption() == 1) ? tr("FORCED") : tr("OFF"))), Log::INFO
);
1100 m_nativeSession
->set_alert_notify([this]()
1102 QMetaObject::invokeMethod(this, &Session::readAlerts
, Qt::QueuedConnection
);
1106 m_nativeSession
->add_extension(<::create_smart_ban_plugin
);
1107 m_nativeSession
->add_extension(<::create_ut_metadata_plugin
);
1109 m_nativeSession
->add_extension(<::create_ut_pex_plugin
);
1111 m_nativeSession
->add_extension(std::make_shared
<NativeSessionExtension
>());
1114 void Session::processBannedIPs(lt::ip_filter
&filter
)
1116 // First, import current filter
1117 for (const QString
&ip
: asConst(m_bannedIPs
.get()))
1120 const lt::address addr
= lt::make_address(ip
.toLatin1().constData(), ec
);
1123 filter
.add_rule(addr
, addr
, lt::ip_filter::blocked
);
1127 void Session::adjustLimits(lt::settings_pack
&settingsPack
) const
1129 // Internally increase the queue limits to ensure that the magnet is started
1130 const int maxDownloads
= maxActiveDownloads();
1131 const int maxActive
= maxActiveTorrents();
1133 settingsPack
.set_int(lt::settings_pack::active_downloads
1134 , maxDownloads
> -1 ? maxDownloads
+ m_extraLimit
: maxDownloads
);
1135 settingsPack
.set_int(lt::settings_pack::active_limit
1136 , maxActive
> -1 ? maxActive
+ m_extraLimit
: maxActive
);
1139 void Session::applyBandwidthLimits(lt::settings_pack
&settingsPack
) const
1141 const bool altSpeedLimitEnabled
= isAltGlobalSpeedLimitEnabled();
1142 settingsPack
.set_int(lt::settings_pack::download_rate_limit
, altSpeedLimitEnabled
? altGlobalDownloadSpeedLimit() : globalDownloadSpeedLimit());
1143 settingsPack
.set_int(lt::settings_pack::upload_rate_limit
, altSpeedLimitEnabled
? altGlobalUploadSpeedLimit() : globalUploadSpeedLimit());
1146 void Session::initMetrics()
1148 const auto findMetricIndex
= [](const char *name
) -> int
1150 const int index
= lt::find_metric_idx(name
);
1151 Q_ASSERT(index
>= 0);
1155 // TODO: switch to "designated initializers" in C++20
1156 m_metricIndices
.net
.hasIncomingConnections
= findMetricIndex("net.has_incoming_connections");
1157 m_metricIndices
.net
.sentPayloadBytes
= findMetricIndex("net.sent_payload_bytes");
1158 m_metricIndices
.net
.recvPayloadBytes
= findMetricIndex("net.recv_payload_bytes");
1159 m_metricIndices
.net
.sentBytes
= findMetricIndex("net.sent_bytes");
1160 m_metricIndices
.net
.recvBytes
= findMetricIndex("net.recv_bytes");
1161 m_metricIndices
.net
.sentIPOverheadBytes
= findMetricIndex("net.sent_ip_overhead_bytes");
1162 m_metricIndices
.net
.recvIPOverheadBytes
= findMetricIndex("net.recv_ip_overhead_bytes");
1163 m_metricIndices
.net
.sentTrackerBytes
= findMetricIndex("net.sent_tracker_bytes");
1164 m_metricIndices
.net
.recvTrackerBytes
= findMetricIndex("net.recv_tracker_bytes");
1165 m_metricIndices
.net
.recvRedundantBytes
= findMetricIndex("net.recv_redundant_bytes");
1166 m_metricIndices
.net
.recvFailedBytes
= findMetricIndex("net.recv_failed_bytes");
1168 m_metricIndices
.peer
.numPeersConnected
= findMetricIndex("peer.num_peers_connected");
1169 m_metricIndices
.peer
.numPeersDownDisk
= findMetricIndex("peer.num_peers_down_disk");
1170 m_metricIndices
.peer
.numPeersUpDisk
= findMetricIndex("peer.num_peers_up_disk");
1172 m_metricIndices
.dht
.dhtBytesIn
= findMetricIndex("dht.dht_bytes_in");
1173 m_metricIndices
.dht
.dhtBytesOut
= findMetricIndex("dht.dht_bytes_out");
1174 m_metricIndices
.dht
.dhtNodes
= findMetricIndex("dht.dht_nodes");
1176 m_metricIndices
.disk
.diskBlocksInUse
= findMetricIndex("disk.disk_blocks_in_use");
1177 m_metricIndices
.disk
.numBlocksRead
= findMetricIndex("disk.num_blocks_read");
1178 #if (LIBTORRENT_VERSION_NUM < 20000)
1179 m_metricIndices
.disk
.numBlocksCacheHits
= findMetricIndex("disk.num_blocks_cache_hits");
1181 m_metricIndices
.disk
.writeJobs
= findMetricIndex("disk.num_write_ops");
1182 m_metricIndices
.disk
.readJobs
= findMetricIndex("disk.num_read_ops");
1183 m_metricIndices
.disk
.hashJobs
= findMetricIndex("disk.num_blocks_hashed");
1184 m_metricIndices
.disk
.queuedDiskJobs
= findMetricIndex("disk.queued_disk_jobs");
1185 m_metricIndices
.disk
.diskJobTime
= findMetricIndex("disk.disk_job_time");
1188 void Session::loadLTSettings(lt::settings_pack
&settingsPack
)
1190 settingsPack
.set_int(lt::settings_pack::connection_speed
, connectionSpeed());
1192 // from libtorrent doc:
1193 // It will not take affect until the listen_interfaces settings is updated
1194 settingsPack
.set_int(lt::settings_pack::listen_queue_size
, socketBacklogSize());
1196 configureNetworkInterfaces(settingsPack
);
1197 applyBandwidthLimits(settingsPack
);
1199 // The most secure, rc4 only so that all streams are encrypted
1200 settingsPack
.set_int(lt::settings_pack::allowed_enc_level
, lt::settings_pack::pe_rc4
);
1201 settingsPack
.set_bool(lt::settings_pack::prefer_rc4
, true);
1202 switch (encryption())
1205 settingsPack
.set_int(lt::settings_pack::out_enc_policy
, lt::settings_pack::pe_enabled
);
1206 settingsPack
.set_int(lt::settings_pack::in_enc_policy
, lt::settings_pack::pe_enabled
);
1209 settingsPack
.set_int(lt::settings_pack::out_enc_policy
, lt::settings_pack::pe_forced
);
1210 settingsPack
.set_int(lt::settings_pack::in_enc_policy
, lt::settings_pack::pe_forced
);
1212 default: // Disabled
1213 settingsPack
.set_int(lt::settings_pack::out_enc_policy
, lt::settings_pack::pe_disabled
);
1214 settingsPack
.set_int(lt::settings_pack::in_enc_policy
, lt::settings_pack::pe_disabled
);
1218 const auto proxyManager
= Net::ProxyConfigurationManager::instance();
1219 const Net::ProxyConfiguration proxyConfig
= proxyManager
->proxyConfiguration();
1221 switch (proxyConfig
.type
)
1223 case Net::ProxyType::HTTP
:
1224 settingsPack
.set_int(lt::settings_pack::proxy_type
, lt::settings_pack::http
);
1226 case Net::ProxyType::HTTP_PW
:
1227 settingsPack
.set_int(lt::settings_pack::proxy_type
, lt::settings_pack::http_pw
);
1229 case Net::ProxyType::SOCKS4
:
1230 settingsPack
.set_int(lt::settings_pack::proxy_type
, lt::settings_pack::socks4
);
1232 case Net::ProxyType::SOCKS5
:
1233 settingsPack
.set_int(lt::settings_pack::proxy_type
, lt::settings_pack::socks5
);
1235 case Net::ProxyType::SOCKS5_PW
:
1236 settingsPack
.set_int(lt::settings_pack::proxy_type
, lt::settings_pack::socks5_pw
);
1238 case Net::ProxyType::None
:
1240 settingsPack
.set_int(lt::settings_pack::proxy_type
, lt::settings_pack::none
);
1243 if (proxyConfig
.type
!= Net::ProxyType::None
)
1245 settingsPack
.set_str(lt::settings_pack::proxy_hostname
, proxyConfig
.ip
.toStdString());
1246 settingsPack
.set_int(lt::settings_pack::proxy_port
, proxyConfig
.port
);
1248 if (proxyManager
->isAuthenticationRequired())
1250 settingsPack
.set_str(lt::settings_pack::proxy_username
, proxyConfig
.username
.toStdString());
1251 settingsPack
.set_str(lt::settings_pack::proxy_password
, proxyConfig
.password
.toStdString());
1254 settingsPack
.set_bool(lt::settings_pack::proxy_peer_connections
, isProxyPeerConnectionsEnabled());
1257 settingsPack
.set_bool(lt::settings_pack::announce_to_all_trackers
, announceToAllTrackers());
1258 settingsPack
.set_bool(lt::settings_pack::announce_to_all_tiers
, announceToAllTiers());
1260 settingsPack
.set_int(lt::settings_pack::peer_turnover
, peerTurnover());
1261 settingsPack
.set_int(lt::settings_pack::peer_turnover_cutoff
, peerTurnoverCutoff());
1262 settingsPack
.set_int(lt::settings_pack::peer_turnover_interval
, peerTurnoverInterval());
1264 settingsPack
.set_int(lt::settings_pack::aio_threads
, asyncIOThreads());
1265 #if (LIBTORRENT_VERSION_NUM >= 20000)
1266 settingsPack
.set_int(lt::settings_pack::hashing_threads
, hashingThreads());
1268 settingsPack
.set_int(lt::settings_pack::file_pool_size
, filePoolSize());
1270 const int checkingMemUsageSize
= checkingMemUsage() * 64;
1271 settingsPack
.set_int(lt::settings_pack::checking_mem_usage
, checkingMemUsageSize
);
1273 #if (LIBTORRENT_VERSION_NUM < 20000)
1274 const int cacheSize
= (diskCacheSize() > -1) ? (diskCacheSize() * 64) : -1;
1275 settingsPack
.set_int(lt::settings_pack::cache_size
, cacheSize
);
1276 settingsPack
.set_int(lt::settings_pack::cache_expiry
, diskCacheTTL());
1279 lt::settings_pack::io_buffer_mode_t mode
= useOSCache() ? lt::settings_pack::enable_os_cache
1280 : lt::settings_pack::disable_os_cache
;
1281 settingsPack
.set_int(lt::settings_pack::disk_io_read_mode
, mode
);
1282 settingsPack
.set_int(lt::settings_pack::disk_io_write_mode
, mode
);
1284 #if (LIBTORRENT_VERSION_NUM < 20000)
1285 settingsPack
.set_bool(lt::settings_pack::coalesce_reads
, isCoalesceReadWriteEnabled());
1286 settingsPack
.set_bool(lt::settings_pack::coalesce_writes
, isCoalesceReadWriteEnabled());
1289 settingsPack
.set_bool(lt::settings_pack::piece_extent_affinity
, usePieceExtentAffinity());
1291 settingsPack
.set_int(lt::settings_pack::suggest_mode
, isSuggestModeEnabled()
1292 ? lt::settings_pack::suggest_read_cache
: lt::settings_pack::no_piece_suggestions
);
1294 settingsPack
.set_int(lt::settings_pack::send_buffer_watermark
, sendBufferWatermark() * 1024);
1295 settingsPack
.set_int(lt::settings_pack::send_buffer_low_watermark
, sendBufferLowWatermark() * 1024);
1296 settingsPack
.set_int(lt::settings_pack::send_buffer_watermark_factor
, sendBufferWatermarkFactor());
1298 settingsPack
.set_bool(lt::settings_pack::anonymous_mode
, isAnonymousModeEnabled());
1301 if (isQueueingSystemEnabled())
1303 adjustLimits(settingsPack
);
1305 settingsPack
.set_int(lt::settings_pack::active_seeds
, maxActiveUploads());
1306 settingsPack
.set_bool(lt::settings_pack::dont_count_slow_torrents
, ignoreSlowTorrentsForQueueing());
1307 settingsPack
.set_int(lt::settings_pack::inactive_down_rate
, downloadRateForSlowTorrents() * 1024); // KiB to Bytes
1308 settingsPack
.set_int(lt::settings_pack::inactive_up_rate
, uploadRateForSlowTorrents() * 1024); // KiB to Bytes
1309 settingsPack
.set_int(lt::settings_pack::auto_manage_startup
, slowTorrentsInactivityTimer());
1313 settingsPack
.set_int(lt::settings_pack::active_downloads
, -1);
1314 settingsPack
.set_int(lt::settings_pack::active_seeds
, -1);
1315 settingsPack
.set_int(lt::settings_pack::active_limit
, -1);
1317 settingsPack
.set_int(lt::settings_pack::active_tracker_limit
, -1);
1318 settingsPack
.set_int(lt::settings_pack::active_dht_limit
, -1);
1319 settingsPack
.set_int(lt::settings_pack::active_lsd_limit
, -1);
1320 settingsPack
.set_int(lt::settings_pack::alert_queue_size
, std::numeric_limits
<int>::max() / 2);
1323 settingsPack
.set_int(lt::settings_pack::outgoing_port
, outgoingPortsMin());
1324 settingsPack
.set_int(lt::settings_pack::num_outgoing_ports
, outgoingPortsMax() - outgoingPortsMin() + 1);
1325 // UPnP lease duration
1326 settingsPack
.set_int(lt::settings_pack::upnp_lease_duration
, UPnPLeaseDuration());
1328 settingsPack
.set_int(lt::settings_pack::peer_tos
, peerToS());
1329 // Include overhead in transfer limits
1330 settingsPack
.set_bool(lt::settings_pack::rate_limit_ip_overhead
, includeOverheadInLimits());
1331 // IP address to announce to trackers
1332 settingsPack
.set_str(lt::settings_pack::announce_ip
, announceIP().toStdString());
1333 // Max concurrent HTTP announces
1334 settingsPack
.set_int(lt::settings_pack::max_concurrent_http_announces
, maxConcurrentHTTPAnnounces());
1335 // Stop tracker timeout
1336 settingsPack
.set_int(lt::settings_pack::stop_tracker_timeout
, stopTrackerTimeout());
1337 // * Max connections limit
1338 settingsPack
.set_int(lt::settings_pack::connections_limit
, maxConnections());
1339 // * Global max upload slots
1340 settingsPack
.set_int(lt::settings_pack::unchoke_slots_limit
, maxUploads());
1342 switch (btProtocol())
1344 case BTProtocol::Both
:
1346 settingsPack
.set_bool(lt::settings_pack::enable_incoming_tcp
, true);
1347 settingsPack
.set_bool(lt::settings_pack::enable_outgoing_tcp
, true);
1348 settingsPack
.set_bool(lt::settings_pack::enable_incoming_utp
, true);
1349 settingsPack
.set_bool(lt::settings_pack::enable_outgoing_utp
, true);
1352 case BTProtocol::TCP
:
1353 settingsPack
.set_bool(lt::settings_pack::enable_incoming_tcp
, true);
1354 settingsPack
.set_bool(lt::settings_pack::enable_outgoing_tcp
, true);
1355 settingsPack
.set_bool(lt::settings_pack::enable_incoming_utp
, false);
1356 settingsPack
.set_bool(lt::settings_pack::enable_outgoing_utp
, false);
1359 case BTProtocol::UTP
:
1360 settingsPack
.set_bool(lt::settings_pack::enable_incoming_tcp
, false);
1361 settingsPack
.set_bool(lt::settings_pack::enable_outgoing_tcp
, false);
1362 settingsPack
.set_bool(lt::settings_pack::enable_incoming_utp
, true);
1363 settingsPack
.set_bool(lt::settings_pack::enable_outgoing_utp
, true);
1367 switch (utpMixedMode())
1369 case MixedModeAlgorithm::TCP
:
1371 settingsPack
.set_int(lt::settings_pack::mixed_mode_algorithm
, lt::settings_pack::prefer_tcp
);
1373 case MixedModeAlgorithm::Proportional
:
1374 settingsPack
.set_int(lt::settings_pack::mixed_mode_algorithm
, lt::settings_pack::peer_proportional
);
1378 settingsPack
.set_bool(lt::settings_pack::allow_idna
, isIDNSupportEnabled());
1380 settingsPack
.set_bool(lt::settings_pack::allow_multiple_connections_per_ip
, multiConnectionsPerIpEnabled());
1382 settingsPack
.set_bool(lt::settings_pack::validate_https_trackers
, validateHTTPSTrackerCertificate());
1384 settingsPack
.set_bool(lt::settings_pack::no_connect_privileged_ports
, blockPeersOnPrivilegedPorts());
1386 settingsPack
.set_bool(lt::settings_pack::apply_ip_filter_to_trackers
, isTrackerFilteringEnabled());
1388 settingsPack
.set_bool(lt::settings_pack::enable_dht
, isDHTEnabled());
1390 settingsPack
.set_str(lt::settings_pack::dht_bootstrap_nodes
, "dht.libtorrent.org:25401,router.bittorrent.com:6881,router.utorrent.com:6881,dht.transmissionbt.com:6881,dht.aelitis.com:6881");
1391 settingsPack
.set_bool(lt::settings_pack::enable_lsd
, isLSDEnabled());
1393 switch (chokingAlgorithm())
1395 case ChokingAlgorithm::FixedSlots
:
1397 settingsPack
.set_int(lt::settings_pack::choking_algorithm
, lt::settings_pack::fixed_slots_choker
);
1399 case ChokingAlgorithm::RateBased
:
1400 settingsPack
.set_int(lt::settings_pack::choking_algorithm
, lt::settings_pack::rate_based_choker
);
1404 switch (seedChokingAlgorithm())
1406 case SeedChokingAlgorithm::RoundRobin
:
1407 settingsPack
.set_int(lt::settings_pack::seed_choking_algorithm
, lt::settings_pack::round_robin
);
1409 case SeedChokingAlgorithm::FastestUpload
:
1411 settingsPack
.set_int(lt::settings_pack::seed_choking_algorithm
, lt::settings_pack::fastest_upload
);
1413 case SeedChokingAlgorithm::AntiLeech
:
1414 settingsPack
.set_int(lt::settings_pack::seed_choking_algorithm
, lt::settings_pack::anti_leech
);
1419 void Session::configureNetworkInterfaces(lt::settings_pack
&settingsPack
)
1421 if (m_listenInterfaceConfigured
)
1424 const int port
= useRandomPort() ? 0 : this->port();
1425 if (port
> 0) // user specified port
1426 settingsPack
.set_int(lt::settings_pack::max_retry_port_bind
, 0);
1428 QStringList endpoints
;
1429 QStringList outgoingInterfaces
;
1430 const QString portString
= ':' + QString::number(port
);
1432 for (const QString
&ip
: asConst(getListeningIPs()))
1434 const QHostAddress addr
{ip
};
1437 const bool isIPv6
= (addr
.protocol() == QAbstractSocket::IPv6Protocol
);
1438 const QString ip
= isIPv6
1439 ? Utils::Net::canonicalIPv6Addr(addr
).toString()
1442 endpoints
<< ((isIPv6
? ('[' + ip
+ ']') : ip
) + portString
);
1444 if ((ip
!= QLatin1String("0.0.0.0")) && (ip
!= QLatin1String("::")))
1445 outgoingInterfaces
<< ip
;
1449 // ip holds an interface name
1451 // On Vista+ versions and after Qt 5.5 QNetworkInterface::name() returns
1452 // the interface's LUID and not the GUID.
1453 // Libtorrent expects GUIDs for the 'listen_interfaces' setting.
1454 const QString guid
= convertIfaceNameToGuid(ip
);
1455 if (!guid
.isEmpty())
1457 endpoints
<< (guid
+ portString
);
1458 outgoingInterfaces
<< guid
;
1462 LogMsg(tr("Could not get GUID of network interface: %1").arg(ip
) , Log::WARNING
);
1463 // Since we can't get the GUID, we'll pass the interface name instead.
1464 // Otherwise an empty string will be passed to outgoing_interface which will cause IP leak.
1465 endpoints
<< (ip
+ portString
);
1466 outgoingInterfaces
<< ip
;
1469 endpoints
<< (ip
+ portString
);
1470 outgoingInterfaces
<< ip
;
1475 const QString finalEndpoints
= endpoints
.join(',');
1476 settingsPack
.set_str(lt::settings_pack::listen_interfaces
, finalEndpoints
.toStdString());
1477 LogMsg(tr("Trying to listen on: %1", "e.g: Trying to listen on: 192.168.0.1:6881")
1478 .arg(finalEndpoints
), Log::INFO
);
1480 settingsPack
.set_str(lt::settings_pack::outgoing_interfaces
, outgoingInterfaces
.join(',').toStdString());
1481 m_listenInterfaceConfigured
= true;
1484 void Session::configurePeerClasses()
1487 // lt::make_address("255.255.255.255") crashes on some people's systems
1488 // so instead we use address_v4::broadcast()
1489 // Proactively do the same for 0.0.0.0 and address_v4::any()
1490 f
.add_rule(lt::address_v4::any()
1491 , lt::address_v4::broadcast()
1492 , 1 << static_cast<LTUnderlyingType
<lt::peer_class_t
>>(lt::session::global_peer_class_id
));
1494 // IPv6 may not be available on OS and the parsing
1495 // would result in an exception -> abnormal program termination
1496 // Affects Windows XP
1499 f
.add_rule(lt::address_v6::any()
1500 , lt::make_address("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
1501 , 1 << static_cast<LTUnderlyingType
<lt::peer_class_t
>>(lt::session::global_peer_class_id
));
1503 catch (const std::exception
&) {}
1505 if (ignoreLimitsOnLAN())
1508 f
.add_rule(lt::make_address("10.0.0.0")
1509 , lt::make_address("10.255.255.255")
1510 , 1 << static_cast<LTUnderlyingType
<lt::peer_class_t
>>(lt::session::local_peer_class_id
));
1511 f
.add_rule(lt::make_address("172.16.0.0")
1512 , lt::make_address("172.31.255.255")
1513 , 1 << static_cast<LTUnderlyingType
<lt::peer_class_t
>>(lt::session::local_peer_class_id
));
1514 f
.add_rule(lt::make_address("192.168.0.0")
1515 , lt::make_address("192.168.255.255")
1516 , 1 << static_cast<LTUnderlyingType
<lt::peer_class_t
>>(lt::session::local_peer_class_id
));
1518 f
.add_rule(lt::make_address("169.254.0.0")
1519 , lt::make_address("169.254.255.255")
1520 , 1 << static_cast<LTUnderlyingType
<lt::peer_class_t
>>(lt::session::local_peer_class_id
));
1522 f
.add_rule(lt::make_address("127.0.0.0")
1523 , lt::make_address("127.255.255.255")
1524 , 1 << static_cast<LTUnderlyingType
<lt::peer_class_t
>>(lt::session::local_peer_class_id
));
1526 // IPv6 may not be available on OS and the parsing
1527 // would result in an exception -> abnormal program termination
1528 // Affects Windows XP
1532 f
.add_rule(lt::make_address("fe80::")
1533 , lt::make_address("febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
1534 , 1 << static_cast<LTUnderlyingType
<lt::peer_class_t
>>(lt::session::local_peer_class_id
));
1535 // unique local addresses
1536 f
.add_rule(lt::make_address("fc00::")
1537 , lt::make_address("fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
1538 , 1 << static_cast<LTUnderlyingType
<lt::peer_class_t
>>(lt::session::local_peer_class_id
));
1540 f
.add_rule(lt::address_v6::loopback()
1541 , lt::address_v6::loopback()
1542 , 1 << static_cast<LTUnderlyingType
<lt::peer_class_t
>>(lt::session::local_peer_class_id
));
1544 catch (const std::exception
&) {}
1546 m_nativeSession
->set_peer_class_filter(f
);
1548 lt::peer_class_type_filter peerClassTypeFilter
;
1549 peerClassTypeFilter
.add(lt::peer_class_type_filter::tcp_socket
, lt::session::tcp_peer_class_id
);
1550 peerClassTypeFilter
.add(lt::peer_class_type_filter::ssl_tcp_socket
, lt::session::tcp_peer_class_id
);
1551 peerClassTypeFilter
.add(lt::peer_class_type_filter::i2p_socket
, lt::session::tcp_peer_class_id
);
1552 if (!isUTPRateLimited())
1554 peerClassTypeFilter
.disallow(lt::peer_class_type_filter::utp_socket
1555 , lt::session::global_peer_class_id
);
1556 peerClassTypeFilter
.disallow(lt::peer_class_type_filter::ssl_utp_socket
1557 , lt::session::global_peer_class_id
);
1559 m_nativeSession
->set_peer_class_type_filter(peerClassTypeFilter
);
1562 void Session::enableTracker(const bool enable
)
1567 m_tracker
= new Tracker(this);
1577 void Session::enableBandwidthScheduler()
1581 m_bwScheduler
= new BandwidthScheduler(this);
1582 connect(m_bwScheduler
.data(), &BandwidthScheduler::bandwidthLimitRequested
1583 , this, &Session::setAltGlobalSpeedLimitEnabled
);
1585 m_bwScheduler
->start();
1588 void Session::populateAdditionalTrackers()
1590 m_additionalTrackerList
.clear();
1592 const QString trackers
= additionalTrackers();
1593 for (QStringRef tracker
: asConst(trackers
.splitRef('\n')))
1595 tracker
= tracker
.trimmed();
1596 if (!tracker
.isEmpty())
1597 m_additionalTrackerList
.append({tracker
.toString()});
1601 void Session::processShareLimits()
1603 qDebug("Processing share limits...");
1605 // We shouldn't iterate over `m_torrents` in the loop below
1606 // since `deleteTorrent()` modifies it indirectly
1607 const QHash
<TorrentID
, TorrentImpl
*> torrents
{m_torrents
};
1608 for (TorrentImpl
*const torrent
: torrents
)
1610 if (torrent
->isSeed() && !torrent
->isForced())
1612 if (torrent
->ratioLimit() != Torrent::NO_RATIO_LIMIT
)
1614 const qreal ratio
= torrent
->realRatio();
1615 qreal ratioLimit
= torrent
->ratioLimit();
1616 if (ratioLimit
== Torrent::USE_GLOBAL_RATIO
)
1617 // If Global Max Ratio is really set...
1618 ratioLimit
= globalMaxRatio();
1620 if (ratioLimit
>= 0)
1622 qDebug("Ratio: %f (limit: %f)", ratio
, ratioLimit
);
1624 if ((ratio
<= Torrent::MAX_RATIO
) && (ratio
>= ratioLimit
))
1626 if (m_maxRatioAction
== Remove
)
1628 LogMsg(tr("'%1' reached the maximum ratio you set. Removed.").arg(torrent
->name()));
1629 deleteTorrent(torrent
->id());
1631 else if (m_maxRatioAction
== DeleteFiles
)
1633 LogMsg(tr("'%1' reached the maximum ratio you set. Removed torrent and its files.").arg(torrent
->name()));
1634 deleteTorrent(torrent
->id(), DeleteTorrentAndFiles
);
1636 else if ((m_maxRatioAction
== Pause
) && !torrent
->isPaused())
1639 LogMsg(tr("'%1' reached the maximum ratio you set. Paused.").arg(torrent
->name()));
1641 else if ((m_maxRatioAction
== EnableSuperSeeding
) && !torrent
->isPaused() && !torrent
->superSeeding())
1643 torrent
->setSuperSeeding(true);
1644 LogMsg(tr("'%1' reached the maximum ratio you set. Enabled super seeding for it.").arg(torrent
->name()));
1651 if (torrent
->seedingTimeLimit() != Torrent::NO_SEEDING_TIME_LIMIT
)
1653 const qlonglong seedingTimeInMinutes
= torrent
->seedingTime() / 60;
1654 int seedingTimeLimit
= torrent
->seedingTimeLimit();
1655 if (seedingTimeLimit
== Torrent::USE_GLOBAL_SEEDING_TIME
)
1657 // If Global Seeding Time Limit is really set...
1658 seedingTimeLimit
= globalMaxSeedingMinutes();
1661 if (seedingTimeLimit
>= 0)
1663 if ((seedingTimeInMinutes
<= Torrent::MAX_SEEDING_TIME
) && (seedingTimeInMinutes
>= seedingTimeLimit
))
1665 if (m_maxRatioAction
== Remove
)
1667 LogMsg(tr("'%1' reached the maximum seeding time you set. Removed.").arg(torrent
->name()));
1668 deleteTorrent(torrent
->id());
1670 else if (m_maxRatioAction
== DeleteFiles
)
1672 LogMsg(tr("'%1' reached the maximum seeding time you set. Removed torrent and its files.").arg(torrent
->name()));
1673 deleteTorrent(torrent
->id(), DeleteTorrentAndFiles
);
1675 else if ((m_maxRatioAction
== Pause
) && !torrent
->isPaused())
1678 LogMsg(tr("'%1' reached the maximum seeding time you set. Paused.").arg(torrent
->name()));
1680 else if ((m_maxRatioAction
== EnableSuperSeeding
) && !torrent
->isPaused() && !torrent
->superSeeding())
1682 torrent
->setSuperSeeding(true);
1683 LogMsg(tr("'%1' reached the maximum seeding time you set. Enabled super seeding for it.").arg(torrent
->name()));
1692 // Add to BitTorrent session the downloaded torrent file
1693 void Session::handleDownloadFinished(const Net::DownloadResult
&result
)
1695 switch (result
.status
)
1697 case Net::DownloadStatus::Success
:
1698 emit
downloadFromUrlFinished(result
.url
);
1699 addTorrent(TorrentInfo::load(result
.data
), m_downloadedTorrents
.take(result
.url
));
1701 case Net::DownloadStatus::RedirectedToMagnet
:
1702 emit
downloadFromUrlFinished(result
.url
);
1703 addTorrent(MagnetUri
{result
.magnet
}, m_downloadedTorrents
.take(result
.url
));
1706 emit
downloadFromUrlFailed(result
.url
, result
.errorString
);
1710 void Session::fileSearchFinished(const TorrentID
&id
, const QString
&savePath
, const QStringList
&fileNames
)
1712 TorrentImpl
*torrent
= m_torrents
.value(id
);
1715 torrent
->fileSearchFinished(savePath
, fileNames
);
1719 const auto loadingTorrentsIter
= m_loadingTorrents
.find(id
);
1720 if (loadingTorrentsIter
!= m_loadingTorrents
.end())
1722 LoadTorrentParams params
= loadingTorrentsIter
.value();
1723 m_loadingTorrents
.erase(loadingTorrentsIter
);
1725 lt::add_torrent_params
&p
= params
.ltAddTorrentParams
;
1727 p
.save_path
= Utils::Fs::toNativePath(savePath
).toStdString();
1728 for (int i
= 0; i
< fileNames
.size(); ++i
)
1729 p
.renamed_files
[lt::file_index_t
{i
}] = fileNames
[i
].toStdString();
1731 loadTorrent(params
);
1735 // Return the torrent handle, given its hash
1736 Torrent
*Session::findTorrent(const TorrentID
&id
) const
1738 return m_torrents
.value(id
);
1741 bool Session::hasActiveTorrents() const
1743 return std::any_of(m_torrents
.begin(), m_torrents
.end(), [](TorrentImpl
*torrent
)
1745 return TorrentFilter::ActiveTorrent
.match(torrent
);
1749 bool Session::hasUnfinishedTorrents() const
1751 return std::any_of(m_torrents
.begin(), m_torrents
.end(), [](const TorrentImpl
*torrent
)
1753 return (!torrent
->isSeed() && !torrent
->isPaused() && !torrent
->isErrored());
1757 bool Session::hasRunningSeed() const
1759 return std::any_of(m_torrents
.begin(), m_torrents
.end(), [](const TorrentImpl
*torrent
)
1761 return (torrent
->isSeed() && !torrent
->isPaused());
1765 void Session::banIP(const QString
&ip
)
1767 QStringList bannedIPs
= m_bannedIPs
;
1768 if (!bannedIPs
.contains(ip
))
1770 lt::ip_filter filter
= m_nativeSession
->get_ip_filter();
1772 const lt::address addr
= lt::make_address(ip
.toLatin1().constData(), ec
);
1775 filter
.add_rule(addr
, addr
, lt::ip_filter::blocked
);
1776 m_nativeSession
->set_ip_filter(filter
);
1780 m_bannedIPs
= bannedIPs
;
1784 // Delete a torrent from the session, given its hash
1785 // and from the disk, if the corresponding deleteOption is chosen
1786 bool Session::deleteTorrent(const TorrentID
&id
, const DeleteOption deleteOption
)
1788 TorrentImpl
*const torrent
= m_torrents
.take(id
);
1789 if (!torrent
) return false;
1791 qDebug("Deleting torrent with ID: %s", qUtf8Printable(torrent
->id().toString()));
1792 emit
torrentAboutToBeRemoved(torrent
);
1794 // Remove it from session
1795 if (deleteOption
== DeleteTorrent
)
1797 m_removingTorrents
[torrent
->id()] = {torrent
->name(), "", deleteOption
};
1799 const lt::torrent_handle nativeHandle
{torrent
->nativeHandle()};
1800 const auto iter
= std::find_if(m_moveStorageQueue
.begin(), m_moveStorageQueue
.end()
1801 , [&nativeHandle
](const MoveStorageJob
&job
)
1803 return job
.torrentHandle
== nativeHandle
;
1805 if (iter
!= m_moveStorageQueue
.end())
1807 // We shouldn't actually remove torrent until existing "move storage jobs" are done
1808 torrentQueuePositionBottom(nativeHandle
);
1809 nativeHandle
.unset_flags(lt::torrent_flags::auto_managed
);
1810 nativeHandle
.pause();
1814 m_nativeSession
->remove_torrent(nativeHandle
, lt::session::delete_partfile
);
1819 QString rootPath
= torrent
->rootPath(true);
1820 if (!rootPath
.isEmpty() && torrent
->useTempPath())
1822 // torrent without root folder still has it in its temporary save path
1823 rootPath
= torrent
->actualStorageLocation();
1826 m_removingTorrents
[torrent
->id()] = {torrent
->name(), rootPath
, deleteOption
};
1828 if (m_moveStorageQueue
.size() > 1)
1830 // Delete "move storage job" for the deleted torrent
1831 // (note: we shouldn't delete active job)
1832 const auto iter
= std::find_if(m_moveStorageQueue
.begin() + 1, m_moveStorageQueue
.end()
1833 , [torrent
](const MoveStorageJob
&job
)
1835 return job
.torrentHandle
== torrent
->nativeHandle();
1837 if (iter
!= m_moveStorageQueue
.end())
1838 m_moveStorageQueue
.erase(iter
);
1841 m_nativeSession
->remove_torrent(torrent
->nativeHandle(), lt::session::delete_files
);
1844 // Remove it from torrent resume directory
1845 m_resumeDataStorage
->remove(torrent
->id());
1851 bool Session::cancelDownloadMetadata(const TorrentID
&id
)
1853 const auto downloadedMetadataIter
= m_downloadedMetadata
.find(id
);
1854 if (downloadedMetadataIter
== m_downloadedMetadata
.end()) return false;
1856 m_downloadedMetadata
.erase(downloadedMetadataIter
);
1859 m_nativeSession
->remove_torrent(m_nativeSession
->find_torrent(id
), lt::session::delete_files
);
1863 void Session::increaseTorrentsQueuePos(const QVector
<TorrentID
> &ids
)
1865 using ElementType
= std::pair
<int, const TorrentImpl
*>;
1866 std::priority_queue
<ElementType
1867 , std::vector
<ElementType
>
1868 , std::greater
<ElementType
>> torrentQueue
;
1870 // Sort torrents by queue position
1871 for (const TorrentID
&id
: ids
)
1873 const TorrentImpl
*torrent
= m_torrents
.value(id
);
1874 if (!torrent
) continue;
1875 if (const int position
= torrent
->queuePosition(); position
>= 0)
1876 torrentQueue
.emplace(position
, torrent
);
1879 // Increase torrents queue position (starting with the one in the highest queue position)
1880 while (!torrentQueue
.empty())
1882 const TorrentImpl
*torrent
= torrentQueue
.top().second
;
1883 torrentQueuePositionUp(torrent
->nativeHandle());
1887 saveTorrentsQueue();
1890 void Session::decreaseTorrentsQueuePos(const QVector
<TorrentID
> &ids
)
1892 using ElementType
= std::pair
<int, const TorrentImpl
*>;
1893 std::priority_queue
<ElementType
> torrentQueue
;
1895 // Sort torrents by queue position
1896 for (const TorrentID
&id
: ids
)
1898 const TorrentImpl
*torrent
= m_torrents
.value(id
);
1899 if (!torrent
) continue;
1900 if (const int position
= torrent
->queuePosition(); position
>= 0)
1901 torrentQueue
.emplace(position
, torrent
);
1904 // Decrease torrents queue position (starting with the one in the lowest queue position)
1905 while (!torrentQueue
.empty())
1907 const TorrentImpl
*torrent
= torrentQueue
.top().second
;
1908 torrentQueuePositionDown(torrent
->nativeHandle());
1912 for (auto i
= m_downloadedMetadata
.cbegin(); i
!= m_downloadedMetadata
.cend(); ++i
)
1913 torrentQueuePositionBottom(m_nativeSession
->find_torrent(*i
));
1915 saveTorrentsQueue();
1918 void Session::topTorrentsQueuePos(const QVector
<TorrentID
> &ids
)
1920 using ElementType
= std::pair
<int, const TorrentImpl
*>;
1921 std::priority_queue
<ElementType
> torrentQueue
;
1923 // Sort torrents by queue position
1924 for (const TorrentID
&id
: ids
)
1926 const TorrentImpl
*torrent
= m_torrents
.value(id
);
1927 if (!torrent
) continue;
1928 if (const int position
= torrent
->queuePosition(); position
>= 0)
1929 torrentQueue
.emplace(position
, torrent
);
1932 // Top torrents queue position (starting with the one in the lowest queue position)
1933 while (!torrentQueue
.empty())
1935 const TorrentImpl
*torrent
= torrentQueue
.top().second
;
1936 torrentQueuePositionTop(torrent
->nativeHandle());
1940 saveTorrentsQueue();
1943 void Session::bottomTorrentsQueuePos(const QVector
<TorrentID
> &ids
)
1945 using ElementType
= std::pair
<int, const TorrentImpl
*>;
1946 std::priority_queue
<ElementType
1947 , std::vector
<ElementType
>
1948 , std::greater
<ElementType
>> torrentQueue
;
1950 // Sort torrents by queue position
1951 for (const TorrentID
&id
: ids
)
1953 const TorrentImpl
*torrent
= m_torrents
.value(id
);
1954 if (!torrent
) continue;
1955 if (const int position
= torrent
->queuePosition(); position
>= 0)
1956 torrentQueue
.emplace(position
, torrent
);
1959 // Bottom torrents queue position (starting with the one in the highest queue position)
1960 while (!torrentQueue
.empty())
1962 const TorrentImpl
*torrent
= torrentQueue
.top().second
;
1963 torrentQueuePositionBottom(torrent
->nativeHandle());
1967 for (auto i
= m_downloadedMetadata
.cbegin(); i
!= m_downloadedMetadata
.cend(); ++i
)
1968 torrentQueuePositionBottom(m_nativeSession
->find_torrent(*i
));
1970 saveTorrentsQueue();
1973 void Session::handleTorrentNeedSaveResumeData(const TorrentImpl
*torrent
)
1975 if (m_needSaveResumeDataTorrents
.empty())
1977 QMetaObject::invokeMethod(this, [this]()
1979 for (const TorrentID
&torrentID
: asConst(m_needSaveResumeDataTorrents
))
1981 TorrentImpl
*torrent
= m_torrents
.value(torrentID
);
1983 torrent
->saveResumeData();
1985 m_needSaveResumeDataTorrents
.clear();
1986 }, Qt::QueuedConnection
);
1989 m_needSaveResumeDataTorrents
.insert(torrent
->id());
1992 void Session::handleTorrentSaveResumeDataRequested(const TorrentImpl
*torrent
)
1994 qDebug("Saving resume data is requested for torrent '%s'...", qUtf8Printable(torrent
->name()));
1998 QVector
<Torrent
*> Session::torrents() const
2000 QVector
<Torrent
*> result
;
2001 result
.reserve(m_torrents
.size());
2002 for (TorrentImpl
*torrent
: asConst(m_torrents
))
2008 bool Session::addTorrent(const QString
&source
, const AddTorrentParams
¶ms
)
2010 // `source`: .torrent file path/url or magnet uri
2012 if (Net::DownloadManager::hasSupportedScheme(source
))
2014 LogMsg(tr("Downloading '%1', please wait...", "e.g: Downloading 'xxx.torrent', please wait...").arg(source
));
2015 // Launch downloader
2016 Net::DownloadManager::instance()->download(Net::DownloadRequest(source
).limit(MAX_TORRENT_SIZE
)
2017 , this, &Session::handleDownloadFinished
);
2018 m_downloadedTorrents
[source
] = params
;
2022 const MagnetUri magnetUri
{source
};
2023 if (magnetUri
.isValid())
2024 return addTorrent(magnetUri
, params
);
2026 TorrentFileGuard guard
{source
};
2027 if (addTorrent(TorrentInfo::loadFromFile(source
), params
))
2029 guard
.markAsAddedToSession();
2036 bool Session::addTorrent(const MagnetUri
&magnetUri
, const AddTorrentParams
¶ms
)
2038 if (!magnetUri
.isValid()) return false;
2040 return addTorrent_impl(magnetUri
, params
);
2043 bool Session::addTorrent(const TorrentInfo
&torrentInfo
, const AddTorrentParams
¶ms
)
2045 if (!torrentInfo
.isValid()) return false;
2047 return addTorrent_impl(torrentInfo
, params
);
2050 LoadTorrentParams
Session::initLoadTorrentParams(const AddTorrentParams
&addTorrentParams
)
2052 LoadTorrentParams loadTorrentParams
;
2054 loadTorrentParams
.name
= addTorrentParams
.name
;
2055 loadTorrentParams
.firstLastPiecePriority
= addTorrentParams
.firstLastPiecePriority
;
2056 loadTorrentParams
.hasSeedStatus
= addTorrentParams
.skipChecking
; // do not react on 'torrent_finished_alert' when skipping
2057 loadTorrentParams
.contentLayout
= addTorrentParams
.contentLayout
.value_or(torrentContentLayout());
2058 loadTorrentParams
.operatingMode
= (addTorrentParams
.addForced
? TorrentOperatingMode::Forced
: TorrentOperatingMode::AutoManaged
);
2059 loadTorrentParams
.stopped
= addTorrentParams
.addPaused
.value_or(isAddTorrentPaused());
2060 loadTorrentParams
.ratioLimit
= addTorrentParams
.ratioLimit
;
2061 loadTorrentParams
.seedingTimeLimit
= addTorrentParams
.seedingTimeLimit
;
2063 const bool useAutoTMM
= addTorrentParams
.useAutoTMM
.value_or(!isAutoTMMDisabledByDefault());
2065 loadTorrentParams
.savePath
= "";
2066 else if (addTorrentParams
.savePath
.isEmpty())
2067 loadTorrentParams
.savePath
= defaultSavePath();
2068 else if (QDir(addTorrentParams
.savePath
).isRelative())
2069 loadTorrentParams
.savePath
= QDir(defaultSavePath()).absoluteFilePath(addTorrentParams
.savePath
);
2071 loadTorrentParams
.savePath
= normalizePath(addTorrentParams
.savePath
);
2073 const QString category
= addTorrentParams
.category
;
2074 if (!category
.isEmpty() && !m_categories
.contains(category
) && !addCategory(category
))
2075 loadTorrentParams
.category
= "";
2077 loadTorrentParams
.category
= addTorrentParams
.category
;
2079 for (const QString
&tag
: addTorrentParams
.tags
)
2081 if (hasTag(tag
) || addTag(tag
))
2082 loadTorrentParams
.tags
.insert(tag
);
2085 return loadTorrentParams
;
2088 // Add a torrent to the BitTorrent session
2089 bool Session::addTorrent_impl(const std::variant
<MagnetUri
, TorrentInfo
> &source
, const AddTorrentParams
&addTorrentParams
)
2091 const bool hasMetadata
= std::holds_alternative
<TorrentInfo
>(source
);
2092 TorrentInfo metadata
= (hasMetadata
? std::get
<TorrentInfo
>(source
) : TorrentInfo
{});
2093 const MagnetUri
&magnetUri
= (hasMetadata
? MagnetUri
{} : std::get
<MagnetUri
>(source
));
2094 const auto id
= TorrentID::fromInfoHash(hasMetadata
? metadata
.infoHash() : magnetUri
.infoHash());
2096 // It looks illogical that we don't just use an existing handle,
2097 // but as previous experience has shown, it actually creates unnecessary
2098 // problems and unwanted behavior due to the fact that it was originally
2099 // added with parameters other than those provided by the user.
2100 cancelDownloadMetadata(id
);
2102 // We should not add the torrent if it is already
2103 // processed or is pending to add to session
2104 if (m_loadingTorrents
.contains(id
))
2107 TorrentImpl
*const torrent
= m_torrents
.value(id
);
2109 { // a duplicate torrent is added
2110 if (torrent
->isPrivate() || (hasMetadata
&& metadata
.isPrivate()))
2113 // merge trackers and web seeds
2114 torrent
->addTrackers(hasMetadata
? metadata
.trackers() : magnetUri
.trackers());
2115 torrent
->addUrlSeeds(hasMetadata
? metadata
.urlSeeds() : magnetUri
.urlSeeds());
2119 LoadTorrentParams loadTorrentParams
= initLoadTorrentParams(addTorrentParams
);
2120 lt::add_torrent_params
&p
= loadTorrentParams
.ltAddTorrentParams
;
2122 bool isFindingIncompleteFiles
= false;
2124 // If empty then Automatic mode, otherwise Manual mode
2125 const QString actualSavePath
= loadTorrentParams
.savePath
.isEmpty() ? categorySavePath(loadTorrentParams
.category
) : loadTorrentParams
.savePath
;
2128 metadata
.setContentLayout(loadTorrentParams
.contentLayout
);
2130 if (!loadTorrentParams
.hasSeedStatus
)
2132 findIncompleteFiles(metadata
, actualSavePath
);
2133 isFindingIncompleteFiles
= true;
2136 // if torrent name wasn't explicitly set we handle the case of
2137 // initial renaming of torrent content and rename torrent accordingly
2138 if (loadTorrentParams
.name
.isEmpty())
2140 QString contentName
= metadata
.rootFolder();
2141 if (contentName
.isEmpty() && (metadata
.filesCount() == 1))
2142 contentName
= metadata
.fileName(0);
2144 if (!contentName
.isEmpty() && (contentName
!= metadata
.name()))
2145 loadTorrentParams
.name
= contentName
;
2148 Q_ASSERT(p
.file_priorities
.empty());
2149 std::transform(addTorrentParams
.filePriorities
.cbegin(), addTorrentParams
.filePriorities
.cend()
2150 , std::back_inserter(p
.file_priorities
), [](const DownloadPriority priority
)
2152 return static_cast<lt::download_priority_t
>(
2153 static_cast<lt::download_priority_t::underlying_type
>(priority
));
2156 p
.ti
= metadata
.nativeInfo();
2160 p
= magnetUri
.addTorrentParams();
2162 if (loadTorrentParams
.name
.isEmpty() && !p
.name
.empty())
2163 loadTorrentParams
.name
= QString::fromStdString(p
.name
);
2166 p
.save_path
= Utils::Fs::toNativePath(actualSavePath
).toStdString();
2168 p
.upload_limit
= addTorrentParams
.uploadLimit
;
2169 p
.download_limit
= addTorrentParams
.downloadLimit
;
2171 // Preallocation mode
2172 p
.storage_mode
= isPreallocationEnabled() ? lt::storage_mode_allocate
: lt::storage_mode_sparse
;
2174 if (addTorrentParams
.sequential
)
2175 p
.flags
|= lt::torrent_flags::sequential_download
;
2177 p
.flags
&= ~lt::torrent_flags::sequential_download
;
2180 // Skip checking and directly start seeding
2181 if (addTorrentParams
.skipChecking
)
2182 p
.flags
|= lt::torrent_flags::seed_mode
;
2184 p
.flags
&= ~lt::torrent_flags::seed_mode
;
2186 if (loadTorrentParams
.stopped
|| (loadTorrentParams
.operatingMode
== TorrentOperatingMode::AutoManaged
))
2187 p
.flags
|= lt::torrent_flags::paused
;
2189 p
.flags
&= ~lt::torrent_flags::paused
;
2190 if (loadTorrentParams
.stopped
|| (loadTorrentParams
.operatingMode
== TorrentOperatingMode::Forced
))
2191 p
.flags
&= ~lt::torrent_flags::auto_managed
;
2193 p
.flags
|= lt::torrent_flags::auto_managed
;
2195 if (!isFindingIncompleteFiles
)
2196 return loadTorrent(loadTorrentParams
);
2198 m_loadingTorrents
.insert(id
, loadTorrentParams
);
2202 // Add a torrent to the BitTorrent session
2203 bool Session::loadTorrent(LoadTorrentParams params
)
2205 lt::add_torrent_params
&p
= params
.ltAddTorrentParams
;
2207 #if (LIBTORRENT_VERSION_NUM < 20000)
2208 p
.storage
= customStorageConstructor
;
2211 p
.max_connections
= maxConnectionsPerTorrent();
2212 p
.max_uploads
= maxUploadsPerTorrent();
2214 const bool hasMetadata
= (p
.ti
&& p
.ti
->is_valid());
2215 #if (LIBTORRENT_VERSION_NUM >= 20000)
2216 const auto id
= TorrentID::fromInfoHash(hasMetadata
? p
.ti
->info_hashes() : p
.info_hashes
);
2218 const auto id
= TorrentID::fromInfoHash(hasMetadata
? p
.ti
->info_hash() : p
.info_hash
);
2220 m_loadingTorrents
.insert(id
, params
);
2222 // Adding torrent to BitTorrent session
2223 m_nativeSession
->async_add_torrent(p
);
2228 void Session::findIncompleteFiles(const TorrentInfo
&torrentInfo
, const QString
&savePath
) const
2230 const auto searchId
= TorrentID::fromInfoHash(torrentInfo
.infoHash());
2231 const QStringList originalFileNames
= torrentInfo
.filePaths();
2232 const QString completeSavePath
= savePath
;
2233 const QString incompleteSavePath
= (isTempPathEnabled() ? torrentTempPath(torrentInfo
) : QString
{});
2234 QMetaObject::invokeMethod(m_fileSearcher
, [=]()
2236 m_fileSearcher
->search(searchId
, originalFileNames
, completeSavePath
, incompleteSavePath
);
2240 // Add a torrent to libtorrent session in hidden mode
2241 // and force it to download its metadata
2242 bool Session::downloadMetadata(const MagnetUri
&magnetUri
)
2244 if (!magnetUri
.isValid()) return false;
2246 const auto id
= TorrentID::fromInfoHash(magnetUri
.infoHash());
2247 const QString name
= magnetUri
.name();
2249 // We should not add torrent if it's already
2250 // processed or adding to session
2251 if (m_torrents
.contains(id
)) return false;
2252 if (m_loadingTorrents
.contains(id
)) return false;
2253 if (m_downloadedMetadata
.contains(id
)) return false;
2255 qDebug("Adding torrent to preload metadata...");
2256 qDebug(" -> Torrent ID: %s", qUtf8Printable(id
.toString()));
2257 qDebug(" -> Name: %s", qUtf8Printable(name
));
2259 lt::add_torrent_params p
= magnetUri
.addTorrentParams();
2262 // Preallocation mode
2263 if (isPreallocationEnabled())
2264 p
.storage_mode
= lt::storage_mode_allocate
;
2266 p
.storage_mode
= lt::storage_mode_sparse
;
2269 p
.max_connections
= maxConnectionsPerTorrent();
2270 p
.max_uploads
= maxUploadsPerTorrent();
2272 const QString savePath
= Utils::Fs::tempPath() + id
.toString();
2273 p
.save_path
= Utils::Fs::toNativePath(savePath
).toStdString();
2276 p
.flags
&= ~lt::torrent_flags::paused
;
2277 p
.flags
&= ~lt::torrent_flags::auto_managed
;
2279 // Solution to avoid accidental file writes
2280 p
.flags
|= lt::torrent_flags::upload_mode
;
2282 #if (LIBTORRENT_VERSION_NUM < 20000)
2283 p
.storage
= customStorageConstructor
;
2286 // Adding torrent to libtorrent session
2288 lt::torrent_handle h
= m_nativeSession
->add_torrent(p
, ec
);
2289 if (ec
) return false;
2291 // waiting for metadata...
2292 m_downloadedMetadata
.insert(h
.info_hash());
2299 void Session::exportTorrentFile(const TorrentInfo
&torrentInfo
, const QString
&folderPath
, const QString
&baseName
)
2301 const QString validName
= Utils::Fs::toValidFileSystemName(baseName
);
2302 QString torrentExportFilename
= QString::fromLatin1("%1.torrent").arg(validName
);
2303 const QDir exportDir
{folderPath
};
2304 if (exportDir
.exists() || exportDir
.mkpath(exportDir
.absolutePath()))
2306 QString newTorrentPath
= exportDir
.absoluteFilePath(torrentExportFilename
);
2308 while (QFile::exists(newTorrentPath
))
2310 // Append number to torrent name to make it unique
2311 torrentExportFilename
= QString::fromLatin1("%1 %2.torrent").arg(validName
).arg(++counter
);
2312 newTorrentPath
= exportDir
.absoluteFilePath(torrentExportFilename
);
2317 torrentInfo
.saveToFile(newTorrentPath
);
2319 catch (const RuntimeError
&err
)
2321 LogMsg(tr("Couldn't export torrent metadata file '%1'. Reason: %2.")
2322 .arg(newTorrentPath
, err
.message()), Log::WARNING
);
2327 void Session::generateResumeData()
2329 for (TorrentImpl
*const torrent
: asConst(m_torrents
))
2331 if (!torrent
->isValid()) continue;
2333 if (torrent
->needSaveResumeData())
2335 torrent
->saveResumeData();
2336 m_needSaveResumeDataTorrents
.remove(torrent
->id());
2342 void Session::saveResumeData()
2345 m_nativeSession
->pause();
2347 if (isQueueingSystemEnabled())
2348 saveTorrentsQueue();
2349 generateResumeData();
2351 while (m_numResumeData
> 0)
2353 const std::vector
<lt::alert
*> alerts
= getPendingAlerts(lt::seconds
{30});
2356 LogMsg(tr("Error: Aborted saving resume data for %1 outstanding torrents.").arg(QString::number(m_numResumeData
))
2361 for (const lt::alert
*a
: alerts
)
2365 case lt::save_resume_data_failed_alert::alert_type
:
2366 case lt::save_resume_data_alert::alert_type
:
2367 dispatchTorrentAlert(a
);
2374 void Session::saveTorrentsQueue() const
2376 QVector
<TorrentID
> queue
;
2377 for (const TorrentImpl
*torrent
: asConst(m_torrents
))
2379 // We require actual (non-cached) queue position here!
2380 const int queuePos
= static_cast<LTUnderlyingType
<lt::queue_position_t
>>(torrent
->nativeHandle().queue_position());
2383 if (queuePos
>= queue
.size())
2384 queue
.resize(queuePos
+ 1);
2385 queue
[queuePos
] = torrent
->id();
2389 m_resumeDataStorage
->storeQueue(queue
);
2392 void Session::removeTorrentsQueue() const
2394 m_resumeDataStorage
->storeQueue({});
2397 void Session::setDefaultSavePath(QString path
)
2399 path
= normalizeSavePath(path
);
2400 if (path
== m_defaultSavePath
) return;
2402 m_defaultSavePath
= path
;
2404 if (isDisableAutoTMMWhenDefaultSavePathChanged())
2405 for (TorrentImpl
*const torrent
: asConst(m_torrents
))
2406 torrent
->setAutoTMMEnabled(false);
2408 for (TorrentImpl
*const torrent
: asConst(m_torrents
))
2409 torrent
->handleCategorySavePathChanged();
2412 void Session::setTempPath(QString path
)
2414 path
= normalizeSavePath(path
, defaultSavePath() + "temp/");
2415 if (path
== m_tempPath
) return;
2419 for (TorrentImpl
*const torrent
: asConst(m_torrents
))
2420 torrent
->handleTempPathChanged();
2423 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
2424 void Session::networkOnlineStateChanged(const bool online
)
2426 LogMsg(tr("System network status changed to %1", "e.g: System network status changed to ONLINE").arg(online
? tr("ONLINE") : tr("OFFLINE")), Log::INFO
);
2429 void Session::networkConfigurationChange(const QNetworkConfiguration
&cfg
)
2431 const QString configuredInterfaceName
= networkInterface();
2432 // Empty means "Any Interface". In this case libtorrent has binded to 0.0.0.0 so any change to any interface will
2433 // be automatically picked up. Otherwise we would rebinding here to 0.0.0.0 again.
2434 if (configuredInterfaceName
.isEmpty()) return;
2436 const QString changedInterface
= cfg
.name();
2438 if (configuredInterfaceName
== changedInterface
)
2440 LogMsg(tr("Network configuration of %1 has changed, refreshing session binding", "e.g: Network configuration of tun0 has changed, refreshing session binding").arg(changedInterface
), Log::INFO
);
2441 configureListeningInterface();
2446 QStringList
Session::getListeningIPs() const
2450 const QString ifaceName
= networkInterface();
2451 const QString ifaceAddr
= networkInterfaceAddress();
2452 const QHostAddress
configuredAddr(ifaceAddr
);
2453 const bool allIPv4
= (ifaceAddr
== QLatin1String("0.0.0.0")); // Means All IPv4 addresses
2454 const bool allIPv6
= (ifaceAddr
== QLatin1String("::")); // Means All IPv6 addresses
2456 if (!ifaceAddr
.isEmpty() && !allIPv4
&& !allIPv6
&& configuredAddr
.isNull())
2458 LogMsg(tr("Configured network interface address %1 isn't valid.", "Configured network interface address 124.5.158.1 isn't valid.").arg(ifaceAddr
), Log::CRITICAL
);
2459 // Pass the invalid user configured interface name/address to libtorrent
2460 // in hopes that it will come online later.
2461 // This will not cause IP leak but allow user to reconnect the interface
2462 // and re-establish connection without restarting the client.
2463 IPs
.append(ifaceAddr
);
2467 if (ifaceName
.isEmpty())
2469 if (ifaceAddr
.isEmpty())
2470 return {QLatin1String("0.0.0.0"), QLatin1String("::")}; // Indicates all interfaces + all addresses (aka default)
2473 return {QLatin1String("0.0.0.0")};
2476 return {QLatin1String("::")};
2479 const auto checkAndAddIP
= [allIPv4
, allIPv6
, &IPs
](const QHostAddress
&addr
, const QHostAddress
&match
)
2481 if ((allIPv4
&& (addr
.protocol() != QAbstractSocket::IPv4Protocol
))
2482 || (allIPv6
&& (addr
.protocol() != QAbstractSocket::IPv6Protocol
)))
2485 if ((match
== addr
) || allIPv4
|| allIPv6
)
2486 IPs
.append(addr
.toString());
2489 if (ifaceName
.isEmpty())
2491 const QList
<QHostAddress
> addresses
= QNetworkInterface::allAddresses();
2492 for (const auto &addr
: addresses
)
2493 checkAndAddIP(addr
, configuredAddr
);
2495 // At this point ifaceAddr was non-empty
2496 // If IPs.isEmpty() it means the configured Address was not found
2499 LogMsg(tr("Can't find the configured address '%1' to listen on"
2500 , "Can't find the configured address '192.168.1.3' to listen on")
2501 .arg(ifaceAddr
), Log::CRITICAL
);
2502 IPs
.append(ifaceAddr
);
2508 // Attempt to listen on provided interface
2509 const QNetworkInterface networkIFace
= QNetworkInterface::interfaceFromName(ifaceName
);
2510 if (!networkIFace
.isValid())
2512 qDebug("Invalid network interface: %s", qUtf8Printable(ifaceName
));
2513 LogMsg(tr("The network interface defined is invalid: %1").arg(ifaceName
), Log::CRITICAL
);
2514 IPs
.append(ifaceName
);
2518 if (ifaceAddr
.isEmpty())
2520 IPs
.append(ifaceName
);
2521 return IPs
; // On Windows calling code converts it to GUID
2524 const QList
<QNetworkAddressEntry
> addresses
= networkIFace
.addressEntries();
2525 qDebug("This network interface has %d IP addresses", addresses
.size());
2526 for (const QNetworkAddressEntry
&entry
: addresses
)
2527 checkAndAddIP(entry
.ip(), configuredAddr
);
2529 // Make sure there is at least one IP
2530 // At this point there was an explicit interface and an explicit address set
2531 // and the address should have been found
2534 LogMsg(tr("Can't find the configured address '%1' to listen on"
2535 , "Can't find the configured address '192.168.1.3' to listen on")
2536 .arg(ifaceAddr
), Log::CRITICAL
);
2537 IPs
.append(ifaceAddr
);
2543 // Set the ports range in which is chosen the port
2544 // the BitTorrent session will listen to
2545 void Session::configureListeningInterface()
2547 m_listenInterfaceConfigured
= false;
2548 configureDeferred();
2551 int Session::globalDownloadSpeedLimit() const
2553 // Unfortunately the value was saved as KiB instead of B.
2554 // But it is better to pass it around internally(+ webui) as Bytes.
2555 return m_globalDownloadSpeedLimit
* 1024;
2558 void Session::setGlobalDownloadSpeedLimit(const int limit
)
2560 // Unfortunately the value was saved as KiB instead of B.
2561 // But it is better to pass it around internally(+ webui) as Bytes.
2562 if (limit
== globalDownloadSpeedLimit())
2566 m_globalDownloadSpeedLimit
= 0;
2567 else if (limit
<= 1024)
2568 m_globalDownloadSpeedLimit
= 1;
2570 m_globalDownloadSpeedLimit
= (limit
/ 1024);
2572 if (!isAltGlobalSpeedLimitEnabled())
2573 configureDeferred();
2576 int Session::globalUploadSpeedLimit() const
2578 // Unfortunately the value was saved as KiB instead of B.
2579 // But it is better to pass it around internally(+ webui) as Bytes.
2580 return m_globalUploadSpeedLimit
* 1024;
2583 void Session::setGlobalUploadSpeedLimit(const int limit
)
2585 // Unfortunately the value was saved as KiB instead of B.
2586 // But it is better to pass it around internally(+ webui) as Bytes.
2587 if (limit
== globalUploadSpeedLimit())
2591 m_globalUploadSpeedLimit
= 0;
2592 else if (limit
<= 1024)
2593 m_globalUploadSpeedLimit
= 1;
2595 m_globalUploadSpeedLimit
= (limit
/ 1024);
2597 if (!isAltGlobalSpeedLimitEnabled())
2598 configureDeferred();
2601 int Session::altGlobalDownloadSpeedLimit() const
2603 // Unfortunately the value was saved as KiB instead of B.
2604 // But it is better to pass it around internally(+ webui) as Bytes.
2605 return m_altGlobalDownloadSpeedLimit
* 1024;
2608 void Session::setAltGlobalDownloadSpeedLimit(const int limit
)
2610 // Unfortunately the value was saved as KiB instead of B.
2611 // But it is better to pass it around internally(+ webui) as Bytes.
2612 if (limit
== altGlobalDownloadSpeedLimit())
2616 m_altGlobalDownloadSpeedLimit
= 0;
2617 else if (limit
<= 1024)
2618 m_altGlobalDownloadSpeedLimit
= 1;
2620 m_altGlobalDownloadSpeedLimit
= (limit
/ 1024);
2622 if (isAltGlobalSpeedLimitEnabled())
2623 configureDeferred();
2626 int Session::altGlobalUploadSpeedLimit() const
2628 // Unfortunately the value was saved as KiB instead of B.
2629 // But it is better to pass it around internally(+ webui) as Bytes.
2630 return m_altGlobalUploadSpeedLimit
* 1024;
2633 void Session::setAltGlobalUploadSpeedLimit(const int limit
)
2635 // Unfortunately the value was saved as KiB instead of B.
2636 // But it is better to pass it around internally(+ webui) as Bytes.
2637 if (limit
== altGlobalUploadSpeedLimit())
2641 m_altGlobalUploadSpeedLimit
= 0;
2642 else if (limit
<= 1024)
2643 m_altGlobalUploadSpeedLimit
= 1;
2645 m_altGlobalUploadSpeedLimit
= (limit
/ 1024);
2647 if (isAltGlobalSpeedLimitEnabled())
2648 configureDeferred();
2651 int Session::downloadSpeedLimit() const
2653 return isAltGlobalSpeedLimitEnabled()
2654 ? altGlobalDownloadSpeedLimit()
2655 : globalDownloadSpeedLimit();
2658 void Session::setDownloadSpeedLimit(const int limit
)
2660 if (isAltGlobalSpeedLimitEnabled())
2661 setAltGlobalDownloadSpeedLimit(limit
);
2663 setGlobalDownloadSpeedLimit(limit
);
2666 int Session::uploadSpeedLimit() const
2668 return isAltGlobalSpeedLimitEnabled()
2669 ? altGlobalUploadSpeedLimit()
2670 : globalUploadSpeedLimit();
2673 void Session::setUploadSpeedLimit(const int limit
)
2675 if (isAltGlobalSpeedLimitEnabled())
2676 setAltGlobalUploadSpeedLimit(limit
);
2678 setGlobalUploadSpeedLimit(limit
);
2681 bool Session::isAltGlobalSpeedLimitEnabled() const
2683 return m_isAltGlobalSpeedLimitEnabled
;
2686 void Session::setAltGlobalSpeedLimitEnabled(const bool enabled
)
2688 if (enabled
== isAltGlobalSpeedLimitEnabled()) return;
2690 // Save new state to remember it on startup
2691 m_isAltGlobalSpeedLimitEnabled
= enabled
;
2692 applyBandwidthLimits();
2694 emit
speedLimitModeChanged(m_isAltGlobalSpeedLimitEnabled
);
2697 bool Session::isBandwidthSchedulerEnabled() const
2699 return m_isBandwidthSchedulerEnabled
;
2702 void Session::setBandwidthSchedulerEnabled(const bool enabled
)
2704 if (enabled
!= isBandwidthSchedulerEnabled())
2706 m_isBandwidthSchedulerEnabled
= enabled
;
2708 enableBandwidthScheduler();
2710 delete m_bwScheduler
;
2714 int Session::saveResumeDataInterval() const
2716 return m_saveResumeDataInterval
;
2719 void Session::setSaveResumeDataInterval(const int value
)
2721 if (value
== m_saveResumeDataInterval
)
2724 m_saveResumeDataInterval
= value
;
2728 m_resumeDataTimer
->setInterval(value
* 60 * 1000);
2729 m_resumeDataTimer
->start();
2733 m_resumeDataTimer
->stop();
2737 int Session::port() const
2742 void Session::setPort(const int port
)
2747 configureListeningInterface();
2749 if (isReannounceWhenAddressChangedEnabled())
2750 reannounceToAllTrackers();
2754 bool Session::useRandomPort() const
2756 return m_useRandomPort
;
2759 void Session::setUseRandomPort(const bool value
)
2761 m_useRandomPort
= value
;
2764 QString
Session::networkInterface() const
2766 return m_networkInterface
;
2769 void Session::setNetworkInterface(const QString
&iface
)
2771 if (iface
!= networkInterface())
2773 m_networkInterface
= iface
;
2774 configureListeningInterface();
2778 QString
Session::networkInterfaceName() const
2780 return m_networkInterfaceName
;
2783 void Session::setNetworkInterfaceName(const QString
&name
)
2785 m_networkInterfaceName
= name
;
2788 QString
Session::networkInterfaceAddress() const
2790 return m_networkInterfaceAddress
;
2793 void Session::setNetworkInterfaceAddress(const QString
&address
)
2795 if (address
!= networkInterfaceAddress())
2797 m_networkInterfaceAddress
= address
;
2798 configureListeningInterface();
2802 int Session::encryption() const
2804 return m_encryption
;
2807 void Session::setEncryption(const int state
)
2809 if (state
!= encryption())
2811 m_encryption
= state
;
2812 configureDeferred();
2813 LogMsg(tr("Encryption support [%1]").arg(
2814 state
== 0 ? tr("ON") : ((state
== 1) ? tr("FORCED") : tr("OFF")))
2819 bool Session::isProxyPeerConnectionsEnabled() const
2821 return m_isProxyPeerConnectionsEnabled
;
2824 void Session::setProxyPeerConnectionsEnabled(const bool enabled
)
2826 if (enabled
!= isProxyPeerConnectionsEnabled())
2828 m_isProxyPeerConnectionsEnabled
= enabled
;
2829 configureDeferred();
2833 ChokingAlgorithm
Session::chokingAlgorithm() const
2835 return m_chokingAlgorithm
;
2838 void Session::setChokingAlgorithm(const ChokingAlgorithm mode
)
2840 if (mode
== m_chokingAlgorithm
) return;
2842 m_chokingAlgorithm
= mode
;
2843 configureDeferred();
2846 SeedChokingAlgorithm
Session::seedChokingAlgorithm() const
2848 return m_seedChokingAlgorithm
;
2851 void Session::setSeedChokingAlgorithm(const SeedChokingAlgorithm mode
)
2853 if (mode
== m_seedChokingAlgorithm
) return;
2855 m_seedChokingAlgorithm
= mode
;
2856 configureDeferred();
2859 bool Session::isAddTrackersEnabled() const
2861 return m_isAddTrackersEnabled
;
2864 void Session::setAddTrackersEnabled(const bool enabled
)
2866 m_isAddTrackersEnabled
= enabled
;
2869 QString
Session::additionalTrackers() const
2871 return m_additionalTrackers
;
2874 void Session::setAdditionalTrackers(const QString
&trackers
)
2876 if (trackers
!= additionalTrackers())
2878 m_additionalTrackers
= trackers
;
2879 populateAdditionalTrackers();
2883 bool Session::isIPFilteringEnabled() const
2885 return m_isIPFilteringEnabled
;
2888 void Session::setIPFilteringEnabled(const bool enabled
)
2890 if (enabled
!= m_isIPFilteringEnabled
)
2892 m_isIPFilteringEnabled
= enabled
;
2893 m_IPFilteringConfigured
= false;
2894 configureDeferred();
2898 QString
Session::IPFilterFile() const
2900 return Utils::Fs::toUniformPath(m_IPFilterFile
);
2903 void Session::setIPFilterFile(QString path
)
2905 path
= Utils::Fs::toUniformPath(path
);
2906 if (path
!= IPFilterFile())
2908 m_IPFilterFile
= path
;
2909 m_IPFilteringConfigured
= false;
2910 configureDeferred();
2914 void Session::setBannedIPs(const QStringList
&newList
)
2916 if (newList
== m_bannedIPs
)
2917 return; // do nothing
2918 // here filter out incorrect IP
2919 QStringList filteredList
;
2920 for (const QString
&ip
: newList
)
2922 if (Utils::Net::isValidIP(ip
))
2924 // the same IPv6 addresses could be written in different forms;
2925 // QHostAddress::toString() result format follows RFC5952;
2926 // thus we avoid duplicate entries pointing to the same address
2927 filteredList
<< QHostAddress(ip
).toString();
2931 LogMsg(tr("%1 is not a valid IP address and was rejected while applying the list of banned addresses.")
2936 // now we have to sort IPs and make them unique
2937 filteredList
.sort();
2938 filteredList
.removeDuplicates();
2939 // Again ensure that the new list is different from the stored one.
2940 if (filteredList
== m_bannedIPs
)
2941 return; // do nothing
2942 // store to session settings
2943 // also here we have to recreate filter list including 3rd party ban file
2944 // and install it again into m_session
2945 m_bannedIPs
= filteredList
;
2946 m_IPFilteringConfigured
= false;
2947 configureDeferred();
2950 ResumeDataStorageType
Session::resumeDataStorageType() const
2952 return m_resumeDataStorageType
;
2955 void Session::setResumeDataStorageType(const ResumeDataStorageType type
)
2957 m_resumeDataStorageType
= type
;
2960 QStringList
Session::bannedIPs() const
2965 #if defined(Q_OS_WIN)
2966 OSMemoryPriority
Session::getOSMemoryPriority() const
2968 return m_OSMemoryPriority
;
2971 void Session::setOSMemoryPriority(const OSMemoryPriority priority
)
2973 if (m_OSMemoryPriority
== priority
)
2976 m_OSMemoryPriority
= priority
;
2977 configureDeferred();
2980 void Session::applyOSMemoryPriority() const
2982 using SETPROCESSINFORMATION
= BOOL (WINAPI
*)(HANDLE
, PROCESS_INFORMATION_CLASS
, LPVOID
, DWORD
);
2983 const auto setProcessInformation
= Utils::Misc::loadWinAPI
<SETPROCESSINFORMATION
>("Kernel32.dll", "SetProcessInformation");
2984 if (!setProcessInformation
) // only available on Windows >= 8
2987 #if (_WIN32_WINNT < _WIN32_WINNT_WIN8)
2988 // this dummy struct is required to compile successfully when targeting older Windows version
2989 struct MEMORY_PRIORITY_INFORMATION
2991 ULONG MemoryPriority
;
2994 #define MEMORY_PRIORITY_LOWEST 0
2995 #define MEMORY_PRIORITY_VERY_LOW 1
2996 #define MEMORY_PRIORITY_LOW 2
2997 #define MEMORY_PRIORITY_MEDIUM 3
2998 #define MEMORY_PRIORITY_BELOW_NORMAL 4
2999 #define MEMORY_PRIORITY_NORMAL 5
3002 MEMORY_PRIORITY_INFORMATION prioInfo
{};
3003 switch (getOSMemoryPriority())
3005 case OSMemoryPriority::Normal
:
3007 prioInfo
.MemoryPriority
= MEMORY_PRIORITY_NORMAL
;
3009 case OSMemoryPriority::BelowNormal
:
3010 prioInfo
.MemoryPriority
= MEMORY_PRIORITY_BELOW_NORMAL
;
3012 case OSMemoryPriority::Medium
:
3013 prioInfo
.MemoryPriority
= MEMORY_PRIORITY_MEDIUM
;
3015 case OSMemoryPriority::Low
:
3016 prioInfo
.MemoryPriority
= MEMORY_PRIORITY_LOW
;
3018 case OSMemoryPriority::VeryLow
:
3019 prioInfo
.MemoryPriority
= MEMORY_PRIORITY_VERY_LOW
;
3022 setProcessInformation(::GetCurrentProcess(), ProcessMemoryPriority
, &prioInfo
, sizeof(prioInfo
));
3026 int Session::maxConnectionsPerTorrent() const
3028 return m_maxConnectionsPerTorrent
;
3031 void Session::setMaxConnectionsPerTorrent(int max
)
3033 max
= (max
> 0) ? max
: -1;
3034 if (max
!= maxConnectionsPerTorrent())
3036 m_maxConnectionsPerTorrent
= max
;
3038 // Apply this to all session torrents
3039 for (const lt::torrent_handle
&handle
: m_nativeSession
->get_torrents())
3043 handle
.set_max_connections(max
);
3045 catch (const std::exception
&) {}
3050 int Session::maxUploadsPerTorrent() const
3052 return m_maxUploadsPerTorrent
;
3055 void Session::setMaxUploadsPerTorrent(int max
)
3057 max
= (max
> 0) ? max
: -1;
3058 if (max
!= maxUploadsPerTorrent())
3060 m_maxUploadsPerTorrent
= max
;
3062 // Apply this to all session torrents
3063 for (const lt::torrent_handle
&handle
: m_nativeSession
->get_torrents())
3067 handle
.set_max_uploads(max
);
3069 catch (const std::exception
&) {}
3074 bool Session::announceToAllTrackers() const
3076 return m_announceToAllTrackers
;
3079 void Session::setAnnounceToAllTrackers(const bool val
)
3081 if (val
!= m_announceToAllTrackers
)
3083 m_announceToAllTrackers
= val
;
3084 configureDeferred();
3088 bool Session::announceToAllTiers() const
3090 return m_announceToAllTiers
;
3093 void Session::setAnnounceToAllTiers(const bool val
)
3095 if (val
!= m_announceToAllTiers
)
3097 m_announceToAllTiers
= val
;
3098 configureDeferred();
3102 int Session::peerTurnover() const
3104 return m_peerTurnover
;
3107 void Session::setPeerTurnover(const int val
)
3109 if (val
== m_peerTurnover
)
3112 m_peerTurnover
= val
;
3113 configureDeferred();
3116 int Session::peerTurnoverCutoff() const
3118 return m_peerTurnoverCutoff
;
3121 void Session::setPeerTurnoverCutoff(const int val
)
3123 if (val
== m_peerTurnoverCutoff
)
3126 m_peerTurnoverCutoff
= val
;
3127 configureDeferred();
3130 int Session::peerTurnoverInterval() const
3132 return m_peerTurnoverInterval
;
3135 void Session::setPeerTurnoverInterval(const int val
)
3137 if (val
== m_peerTurnoverInterval
)
3140 m_peerTurnoverInterval
= val
;
3141 configureDeferred();
3144 int Session::asyncIOThreads() const
3146 return qBound(1, m_asyncIOThreads
.get(), 1024);
3149 void Session::setAsyncIOThreads(const int num
)
3151 if (num
== m_asyncIOThreads
)
3154 m_asyncIOThreads
= num
;
3155 configureDeferred();
3158 int Session::hashingThreads() const
3160 return qBound(1, m_hashingThreads
.get(), 1024);
3163 void Session::setHashingThreads(const int num
)
3165 if (num
== m_hashingThreads
)
3168 m_hashingThreads
= num
;
3169 configureDeferred();
3172 int Session::filePoolSize() const
3174 return m_filePoolSize
;
3177 void Session::setFilePoolSize(const int size
)
3179 if (size
== m_filePoolSize
)
3182 m_filePoolSize
= size
;
3183 configureDeferred();
3186 int Session::checkingMemUsage() const
3188 return qMax(1, m_checkingMemUsage
.get());
3191 void Session::setCheckingMemUsage(int size
)
3193 size
= qMax(size
, 1);
3195 if (size
== m_checkingMemUsage
)
3198 m_checkingMemUsage
= size
;
3199 configureDeferred();
3202 int Session::diskCacheSize() const
3204 #ifdef QBT_APP_64BIT
3205 return qMin(m_diskCacheSize
.get(), 33554431); // 32768GiB
3207 // When build as 32bit binary, set the maximum at less than 2GB to prevent crashes
3208 // allocate 1536MiB and leave 512MiB to the rest of program data in RAM
3209 return qMin(m_diskCacheSize
.get(), 1536);
3213 void Session::setDiskCacheSize(int size
)
3215 #ifdef QBT_APP_64BIT
3216 size
= qMin(size
, 33554431); // 32768GiB
3218 // allocate 1536MiB and leave 512MiB to the rest of program data in RAM
3219 size
= qMin(size
, 1536);
3221 if (size
!= m_diskCacheSize
)
3223 m_diskCacheSize
= size
;
3224 configureDeferred();
3228 int Session::diskCacheTTL() const
3230 return m_diskCacheTTL
;
3233 void Session::setDiskCacheTTL(const int ttl
)
3235 if (ttl
!= m_diskCacheTTL
)
3237 m_diskCacheTTL
= ttl
;
3238 configureDeferred();
3242 bool Session::useOSCache() const
3244 return m_useOSCache
;
3247 void Session::setUseOSCache(const bool use
)
3249 if (use
!= m_useOSCache
)
3252 configureDeferred();
3256 bool Session::isCoalesceReadWriteEnabled() const
3258 return m_coalesceReadWriteEnabled
;
3261 void Session::setCoalesceReadWriteEnabled(const bool enabled
)
3263 if (enabled
== m_coalesceReadWriteEnabled
) return;
3265 m_coalesceReadWriteEnabled
= enabled
;
3266 configureDeferred();
3269 bool Session::isSuggestModeEnabled() const
3271 return m_isSuggestMode
;
3274 bool Session::usePieceExtentAffinity() const
3276 return m_usePieceExtentAffinity
;
3279 void Session::setPieceExtentAffinity(const bool enabled
)
3281 if (enabled
== m_usePieceExtentAffinity
) return;
3283 m_usePieceExtentAffinity
= enabled
;
3284 configureDeferred();
3287 void Session::setSuggestMode(const bool mode
)
3289 if (mode
== m_isSuggestMode
) return;
3291 m_isSuggestMode
= mode
;
3292 configureDeferred();
3295 int Session::sendBufferWatermark() const
3297 return m_sendBufferWatermark
;
3300 void Session::setSendBufferWatermark(const int value
)
3302 if (value
== m_sendBufferWatermark
) return;
3304 m_sendBufferWatermark
= value
;
3305 configureDeferred();
3308 int Session::sendBufferLowWatermark() const
3310 return m_sendBufferLowWatermark
;
3313 void Session::setSendBufferLowWatermark(const int value
)
3315 if (value
== m_sendBufferLowWatermark
) return;
3317 m_sendBufferLowWatermark
= value
;
3318 configureDeferred();
3321 int Session::sendBufferWatermarkFactor() const
3323 return m_sendBufferWatermarkFactor
;
3326 void Session::setSendBufferWatermarkFactor(const int value
)
3328 if (value
== m_sendBufferWatermarkFactor
) return;
3330 m_sendBufferWatermarkFactor
= value
;
3331 configureDeferred();
3334 int Session::connectionSpeed() const
3336 return m_connectionSpeed
;
3339 void Session::setConnectionSpeed(const int value
)
3341 if (value
== m_connectionSpeed
) return;
3343 m_connectionSpeed
= value
;
3344 configureDeferred();
3347 int Session::socketBacklogSize() const
3349 return m_socketBacklogSize
;
3352 void Session::setSocketBacklogSize(const int value
)
3354 if (value
== m_socketBacklogSize
) return;
3356 m_socketBacklogSize
= value
;
3357 configureDeferred();
3360 bool Session::isAnonymousModeEnabled() const
3362 return m_isAnonymousModeEnabled
;
3365 void Session::setAnonymousModeEnabled(const bool enabled
)
3367 if (enabled
!= m_isAnonymousModeEnabled
)
3369 m_isAnonymousModeEnabled
= enabled
;
3370 configureDeferred();
3371 LogMsg(tr("Anonymous mode [%1]").arg(isAnonymousModeEnabled() ? tr("ON") : tr("OFF"))
3376 bool Session::isQueueingSystemEnabled() const
3378 return m_isQueueingEnabled
;
3381 void Session::setQueueingSystemEnabled(const bool enabled
)
3383 if (enabled
!= m_isQueueingEnabled
)
3385 m_isQueueingEnabled
= enabled
;
3386 configureDeferred();
3389 saveTorrentsQueue();
3391 removeTorrentsQueue();
3395 int Session::maxActiveDownloads() const
3397 return m_maxActiveDownloads
;
3400 void Session::setMaxActiveDownloads(int max
)
3402 max
= std::max(max
, -1);
3403 if (max
!= m_maxActiveDownloads
)
3405 m_maxActiveDownloads
= max
;
3406 configureDeferred();
3410 int Session::maxActiveUploads() const
3412 return m_maxActiveUploads
;
3415 void Session::setMaxActiveUploads(int max
)
3417 max
= std::max(max
, -1);
3418 if (max
!= m_maxActiveUploads
)
3420 m_maxActiveUploads
= max
;
3421 configureDeferred();
3425 int Session::maxActiveTorrents() const
3427 return m_maxActiveTorrents
;
3430 void Session::setMaxActiveTorrents(int max
)
3432 max
= std::max(max
, -1);
3433 if (max
!= m_maxActiveTorrents
)
3435 m_maxActiveTorrents
= max
;
3436 configureDeferred();
3440 bool Session::ignoreSlowTorrentsForQueueing() const
3442 return m_ignoreSlowTorrentsForQueueing
;
3445 void Session::setIgnoreSlowTorrentsForQueueing(const bool ignore
)
3447 if (ignore
!= m_ignoreSlowTorrentsForQueueing
)
3449 m_ignoreSlowTorrentsForQueueing
= ignore
;
3450 configureDeferred();
3454 int Session::downloadRateForSlowTorrents() const
3456 return m_downloadRateForSlowTorrents
;
3459 void Session::setDownloadRateForSlowTorrents(const int rateInKibiBytes
)
3461 if (rateInKibiBytes
== m_downloadRateForSlowTorrents
)
3464 m_downloadRateForSlowTorrents
= rateInKibiBytes
;
3465 configureDeferred();
3468 int Session::uploadRateForSlowTorrents() const
3470 return m_uploadRateForSlowTorrents
;
3473 void Session::setUploadRateForSlowTorrents(const int rateInKibiBytes
)
3475 if (rateInKibiBytes
== m_uploadRateForSlowTorrents
)
3478 m_uploadRateForSlowTorrents
= rateInKibiBytes
;
3479 configureDeferred();
3482 int Session::slowTorrentsInactivityTimer() const
3484 return m_slowTorrentsInactivityTimer
;
3487 void Session::setSlowTorrentsInactivityTimer(const int timeInSeconds
)
3489 if (timeInSeconds
== m_slowTorrentsInactivityTimer
)
3492 m_slowTorrentsInactivityTimer
= timeInSeconds
;
3493 configureDeferred();
3496 int Session::outgoingPortsMin() const
3498 return m_outgoingPortsMin
;
3501 void Session::setOutgoingPortsMin(const int min
)
3503 if (min
!= m_outgoingPortsMin
)
3505 m_outgoingPortsMin
= min
;
3506 configureDeferred();
3510 int Session::outgoingPortsMax() const
3512 return m_outgoingPortsMax
;
3515 void Session::setOutgoingPortsMax(const int max
)
3517 if (max
!= m_outgoingPortsMax
)
3519 m_outgoingPortsMax
= max
;
3520 configureDeferred();
3524 int Session::UPnPLeaseDuration() const
3526 return m_UPnPLeaseDuration
;
3529 void Session::setUPnPLeaseDuration(const int duration
)
3531 if (duration
!= m_UPnPLeaseDuration
)
3533 m_UPnPLeaseDuration
= duration
;
3534 configureDeferred();
3538 int Session::peerToS() const
3543 void Session::setPeerToS(const int value
)
3545 if (value
== m_peerToS
)
3549 configureDeferred();
3552 bool Session::ignoreLimitsOnLAN() const
3554 return m_ignoreLimitsOnLAN
;
3557 void Session::setIgnoreLimitsOnLAN(const bool ignore
)
3559 if (ignore
!= m_ignoreLimitsOnLAN
)
3561 m_ignoreLimitsOnLAN
= ignore
;
3562 configureDeferred();
3566 bool Session::includeOverheadInLimits() const
3568 return m_includeOverheadInLimits
;
3571 void Session::setIncludeOverheadInLimits(const bool include
)
3573 if (include
!= m_includeOverheadInLimits
)
3575 m_includeOverheadInLimits
= include
;
3576 configureDeferred();
3580 QString
Session::announceIP() const
3582 return m_announceIP
;
3585 void Session::setAnnounceIP(const QString
&ip
)
3587 if (ip
!= m_announceIP
)
3590 configureDeferred();
3594 int Session::maxConcurrentHTTPAnnounces() const
3596 return m_maxConcurrentHTTPAnnounces
;
3599 void Session::setMaxConcurrentHTTPAnnounces(const int value
)
3601 if (value
== m_maxConcurrentHTTPAnnounces
)
3604 m_maxConcurrentHTTPAnnounces
= value
;
3605 configureDeferred();
3608 bool Session::isReannounceWhenAddressChangedEnabled() const
3610 return m_isReannounceWhenAddressChangedEnabled
;
3613 void Session::setReannounceWhenAddressChangedEnabled(const bool enabled
)
3615 if (enabled
== m_isReannounceWhenAddressChangedEnabled
)
3618 m_isReannounceWhenAddressChangedEnabled
= enabled
;
3621 void Session::reannounceToAllTrackers() const
3623 for (const lt::torrent_handle
&torrent
: m_nativeSession
->get_torrents())
3624 torrent
.force_reannounce(0, -1, lt::torrent_handle::ignore_min_interval
);
3627 int Session::stopTrackerTimeout() const
3629 return m_stopTrackerTimeout
;
3632 void Session::setStopTrackerTimeout(const int value
)
3634 if (value
== m_stopTrackerTimeout
)
3637 m_stopTrackerTimeout
= value
;
3638 configureDeferred();
3641 int Session::maxConnections() const
3643 return m_maxConnections
;
3646 void Session::setMaxConnections(int max
)
3648 max
= (max
> 0) ? max
: -1;
3649 if (max
!= m_maxConnections
)
3651 m_maxConnections
= max
;
3652 configureDeferred();
3656 int Session::maxUploads() const
3658 return m_maxUploads
;
3661 void Session::setMaxUploads(int max
)
3663 max
= (max
> 0) ? max
: -1;
3664 if (max
!= m_maxUploads
)
3667 configureDeferred();
3671 BTProtocol
Session::btProtocol() const
3673 return m_btProtocol
;
3676 void Session::setBTProtocol(const BTProtocol protocol
)
3678 if ((protocol
< BTProtocol::Both
) || (BTProtocol::UTP
< protocol
))
3681 if (protocol
== m_btProtocol
) return;
3683 m_btProtocol
= protocol
;
3684 configureDeferred();
3687 bool Session::isUTPRateLimited() const
3689 return m_isUTPRateLimited
;
3692 void Session::setUTPRateLimited(const bool limited
)
3694 if (limited
!= m_isUTPRateLimited
)
3696 m_isUTPRateLimited
= limited
;
3697 configureDeferred();
3701 MixedModeAlgorithm
Session::utpMixedMode() const
3703 return m_utpMixedMode
;
3706 void Session::setUtpMixedMode(const MixedModeAlgorithm mode
)
3708 if (mode
== m_utpMixedMode
) return;
3710 m_utpMixedMode
= mode
;
3711 configureDeferred();
3714 bool Session::isIDNSupportEnabled() const
3716 return m_IDNSupportEnabled
;
3719 void Session::setIDNSupportEnabled(const bool enabled
)
3721 if (enabled
== m_IDNSupportEnabled
) return;
3723 m_IDNSupportEnabled
= enabled
;
3724 configureDeferred();
3727 bool Session::multiConnectionsPerIpEnabled() const
3729 return m_multiConnectionsPerIpEnabled
;
3732 void Session::setMultiConnectionsPerIpEnabled(const bool enabled
)
3734 if (enabled
== m_multiConnectionsPerIpEnabled
) return;
3736 m_multiConnectionsPerIpEnabled
= enabled
;
3737 configureDeferred();
3740 bool Session::validateHTTPSTrackerCertificate() const
3742 return m_validateHTTPSTrackerCertificate
;
3745 void Session::setValidateHTTPSTrackerCertificate(const bool enabled
)
3747 if (enabled
== m_validateHTTPSTrackerCertificate
) return;
3749 m_validateHTTPSTrackerCertificate
= enabled
;
3750 configureDeferred();
3753 bool Session::blockPeersOnPrivilegedPorts() const
3755 return m_blockPeersOnPrivilegedPorts
;
3758 void Session::setBlockPeersOnPrivilegedPorts(const bool enabled
)
3760 if (enabled
== m_blockPeersOnPrivilegedPorts
) return;
3762 m_blockPeersOnPrivilegedPorts
= enabled
;
3763 configureDeferred();
3766 bool Session::isTrackerFilteringEnabled() const
3768 return m_isTrackerFilteringEnabled
;
3771 void Session::setTrackerFilteringEnabled(const bool enabled
)
3773 if (enabled
!= m_isTrackerFilteringEnabled
)
3775 m_isTrackerFilteringEnabled
= enabled
;
3776 configureDeferred();
3780 bool Session::isListening() const
3782 return m_nativeSession
->is_listening();
3785 MaxRatioAction
Session::maxRatioAction() const
3787 return static_cast<MaxRatioAction
>(m_maxRatioAction
.get());
3790 void Session::setMaxRatioAction(const MaxRatioAction act
)
3792 m_maxRatioAction
= static_cast<int>(act
);
3795 // If this functions returns true, we cannot add torrent to session,
3796 // but it is still possible to merge trackers in some cases
3797 bool Session::isKnownTorrent(const TorrentID
&id
) const
3799 return (m_torrents
.contains(id
)
3800 || m_loadingTorrents
.contains(id
)
3801 || m_downloadedMetadata
.contains(id
));
3804 void Session::updateSeedingLimitTimer()
3806 if ((globalMaxRatio() == Torrent::NO_RATIO_LIMIT
) && !hasPerTorrentRatioLimit()
3807 && (globalMaxSeedingMinutes() == Torrent::NO_SEEDING_TIME_LIMIT
) && !hasPerTorrentSeedingTimeLimit())
3809 if (m_seedingLimitTimer
->isActive())
3810 m_seedingLimitTimer
->stop();
3812 else if (!m_seedingLimitTimer
->isActive())
3814 m_seedingLimitTimer
->start();
3818 void Session::handleTorrentShareLimitChanged(TorrentImpl
*const)
3820 updateSeedingLimitTimer();
3823 void Session::handleTorrentNameChanged(TorrentImpl
*const)
3827 void Session::handleTorrentSavePathChanged(TorrentImpl
*const torrent
)
3829 emit
torrentSavePathChanged(torrent
);
3832 void Session::handleTorrentCategoryChanged(TorrentImpl
*const torrent
, const QString
&oldCategory
)
3834 emit
torrentCategoryChanged(torrent
, oldCategory
);
3837 void Session::handleTorrentTagAdded(TorrentImpl
*const torrent
, const QString
&tag
)
3839 emit
torrentTagAdded(torrent
, tag
);
3842 void Session::handleTorrentTagRemoved(TorrentImpl
*const torrent
, const QString
&tag
)
3844 emit
torrentTagRemoved(torrent
, tag
);
3847 void Session::handleTorrentSavingModeChanged(TorrentImpl
*const torrent
)
3849 emit
torrentSavingModeChanged(torrent
);
3852 void Session::handleTorrentTrackersAdded(TorrentImpl
*const torrent
, const QVector
<TrackerEntry
> &newTrackers
)
3854 for (const TrackerEntry
&newTracker
: newTrackers
)
3855 LogMsg(tr("Tracker '%1' was added to torrent '%2'").arg(newTracker
.url
, torrent
->name()));
3856 emit
trackersAdded(torrent
, newTrackers
);
3857 if (torrent
->trackers().size() == newTrackers
.size())
3858 emit
trackerlessStateChanged(torrent
, false);
3859 emit
trackersChanged(torrent
);
3862 void Session::handleTorrentTrackersRemoved(TorrentImpl
*const torrent
, const QVector
<TrackerEntry
> &deletedTrackers
)
3864 for (const TrackerEntry
&deletedTracker
: deletedTrackers
)
3865 LogMsg(tr("Tracker '%1' was deleted from torrent '%2'").arg(deletedTracker
.url
, torrent
->name()));
3866 emit
trackersRemoved(torrent
, deletedTrackers
);
3867 if (torrent
->trackers().empty())
3868 emit
trackerlessStateChanged(torrent
, true);
3869 emit
trackersChanged(torrent
);
3872 void Session::handleTorrentTrackersChanged(TorrentImpl
*const torrent
)
3874 emit
trackersChanged(torrent
);
3877 void Session::handleTorrentUrlSeedsAdded(TorrentImpl
*const torrent
, const QVector
<QUrl
> &newUrlSeeds
)
3879 for (const QUrl
&newUrlSeed
: newUrlSeeds
)
3880 LogMsg(tr("URL seed '%1' was added to torrent '%2'").arg(newUrlSeed
.toString(), torrent
->name()));
3883 void Session::handleTorrentUrlSeedsRemoved(TorrentImpl
*const torrent
, const QVector
<QUrl
> &urlSeeds
)
3885 for (const QUrl
&urlSeed
: urlSeeds
)
3886 LogMsg(tr("URL seed '%1' was removed from torrent '%2'").arg(urlSeed
.toString(), torrent
->name()));
3889 void Session::handleTorrentMetadataReceived(TorrentImpl
*const torrent
)
3891 // Copy the torrent file to the export folder
3892 if (!torrentExportDirectory().isEmpty())
3894 #if (LIBTORRENT_VERSION_NUM >= 20000)
3895 const TorrentInfo torrentInfo
{torrent
->nativeHandle().torrent_file_with_hashes()};
3897 const TorrentInfo torrentInfo
{torrent
->nativeHandle().torrent_file()};
3899 exportTorrentFile(torrentInfo
, torrentExportDirectory(), torrent
->name());
3902 emit
torrentMetadataReceived(torrent
);
3905 void Session::handleTorrentPaused(TorrentImpl
*const torrent
)
3907 emit
torrentPaused(torrent
);
3910 void Session::handleTorrentResumed(TorrentImpl
*const torrent
)
3912 emit
torrentResumed(torrent
);
3915 void Session::handleTorrentChecked(TorrentImpl
*const torrent
)
3917 emit
torrentFinishedChecking(torrent
);
3920 void Session::handleTorrentFinished(TorrentImpl
*const torrent
)
3922 emit
torrentFinished(torrent
);
3924 qDebug("Checking if the torrent contains torrent files to download");
3925 // Check if there are torrent files inside
3926 for (int i
= 0; i
< torrent
->filesCount(); ++i
)
3928 const QString torrentRelpath
= torrent
->filePath(i
);
3929 if (torrentRelpath
.endsWith(".torrent", Qt::CaseInsensitive
))
3931 qDebug("Found possible recursive torrent download.");
3932 const QString torrentFullpath
= torrent
->savePath(true) + '/' + torrentRelpath
;
3933 qDebug("Full subtorrent path is %s", qUtf8Printable(torrentFullpath
));
3934 TorrentInfo torrentInfo
= TorrentInfo::loadFromFile(torrentFullpath
);
3935 if (torrentInfo
.isValid())
3937 qDebug("emitting recursiveTorrentDownloadPossible()");
3938 emit
recursiveTorrentDownloadPossible(torrent
);
3943 qDebug("Caught error loading torrent");
3944 LogMsg(tr("Unable to decode '%1' torrent file.").arg(Utils::Fs::toNativePath(torrentFullpath
)), Log::CRITICAL
);
3949 // Move .torrent file to another folder
3950 if (!finishedTorrentExportDirectory().isEmpty())
3952 #if (LIBTORRENT_VERSION_NUM >= 20000)
3953 const TorrentInfo torrentInfo
{torrent
->nativeHandle().torrent_file_with_hashes()};
3955 const TorrentInfo torrentInfo
{torrent
->nativeHandle().torrent_file()};
3957 exportTorrentFile(torrentInfo
, finishedTorrentExportDirectory(), torrent
->name());
3960 if (!hasUnfinishedTorrents())
3961 emit
allTorrentsFinished();
3964 void Session::handleTorrentResumeDataReady(TorrentImpl
*const torrent
, const LoadTorrentParams
&data
)
3968 m_resumeDataStorage
->store(torrent
->id(), data
);
3971 void Session::handleTorrentTrackerReply(TorrentImpl
*const torrent
, const QString
&trackerUrl
)
3973 emit
trackerSuccess(torrent
, trackerUrl
);
3976 void Session::handleTorrentTrackerError(TorrentImpl
*const torrent
, const QString
&trackerUrl
)
3978 emit
trackerError(torrent
, trackerUrl
);
3981 bool Session::addMoveTorrentStorageJob(TorrentImpl
*torrent
, const QString
&newPath
, const MoveStorageMode mode
)
3985 const lt::torrent_handle torrentHandle
= torrent
->nativeHandle();
3986 const QString currentLocation
= torrent
->actualStorageLocation();
3988 if (m_moveStorageQueue
.size() > 1)
3990 const auto iter
= std::find_if(m_moveStorageQueue
.begin() + 1, m_moveStorageQueue
.end()
3991 , [&torrentHandle
](const MoveStorageJob
&job
)
3993 return job
.torrentHandle
== torrentHandle
;
3996 if (iter
!= m_moveStorageQueue
.end())
3998 // remove existing inactive job
3999 m_moveStorageQueue
.erase(iter
);
4000 LogMsg(tr("Cancelled moving \"%1\" from \"%2\" to \"%3\".").arg(torrent
->name(), currentLocation
, iter
->path
));
4004 if (!m_moveStorageQueue
.isEmpty() && (m_moveStorageQueue
.first().torrentHandle
== torrentHandle
))
4006 // if there is active job for this torrent prevent creating meaningless
4007 // job that will move torrent to the same location as current one
4008 if (QDir
{m_moveStorageQueue
.first().path
} == QDir
{newPath
})
4010 LogMsg(tr("Couldn't enqueue move of \"%1\" to \"%2\". Torrent is currently moving to the same destination location.")
4011 .arg(torrent
->name(), newPath
));
4017 if (QDir
{currentLocation
} == QDir
{newPath
})
4019 LogMsg(tr("Couldn't enqueue move of \"%1\" from \"%2\" to \"%3\". Both paths point to the same location.")
4020 .arg(torrent
->name(), currentLocation
, newPath
));
4025 const MoveStorageJob moveStorageJob
{torrentHandle
, newPath
, mode
};
4026 m_moveStorageQueue
<< moveStorageJob
;
4027 LogMsg(tr("Enqueued to move \"%1\" from \"%2\" to \"%3\".").arg(torrent
->name(), currentLocation
, newPath
));
4029 if (m_moveStorageQueue
.size() == 1)
4030 moveTorrentStorage(moveStorageJob
);
4035 void Session::moveTorrentStorage(const MoveStorageJob
&job
) const
4037 #if (LIBTORRENT_VERSION_NUM >= 20000)
4038 const auto id
= TorrentID::fromInfoHash(job
.torrentHandle
.info_hashes());
4040 const auto id
= TorrentID::fromInfoHash(job
.torrentHandle
.info_hash());
4042 const TorrentImpl
*torrent
= m_torrents
.value(id
);
4043 const QString torrentName
= (torrent
? torrent
->name() : id
.toString());
4044 LogMsg(tr("Moving \"%1\" to \"%2\"...").arg(torrentName
, job
.path
));
4046 job
.torrentHandle
.move_storage(job
.path
.toUtf8().constData()
4047 , ((job
.mode
== MoveStorageMode::Overwrite
)
4048 ? lt::move_flags_t::always_replace_files
: lt::move_flags_t::dont_replace
));
4051 void Session::handleMoveTorrentStorageJobFinished()
4053 const MoveStorageJob finishedJob
= m_moveStorageQueue
.takeFirst();
4054 if (!m_moveStorageQueue
.isEmpty())
4055 moveTorrentStorage(m_moveStorageQueue
.first());
4057 const auto iter
= std::find_if(m_moveStorageQueue
.cbegin(), m_moveStorageQueue
.cend()
4058 , [&finishedJob
](const MoveStorageJob
&job
)
4060 return job
.torrentHandle
== finishedJob
.torrentHandle
;
4063 const bool torrentHasOutstandingJob
= (iter
!= m_moveStorageQueue
.cend());
4065 TorrentImpl
*torrent
= m_torrents
.value(finishedJob
.torrentHandle
.info_hash());
4068 torrent
->handleMoveStorageJobFinished(torrentHasOutstandingJob
);
4070 else if (!torrentHasOutstandingJob
)
4072 // Last job is completed for torrent that being removing, so actually remove it
4073 const lt::torrent_handle nativeHandle
{finishedJob
.torrentHandle
};
4074 const RemovingTorrentData
&removingTorrentData
= m_removingTorrents
[nativeHandle
.info_hash()];
4075 if (removingTorrentData
.deleteOption
== DeleteTorrent
)
4076 m_nativeSession
->remove_torrent(nativeHandle
, lt::session::delete_partfile
);
4080 void Session::handleTorrentTrackerWarning(TorrentImpl
*const torrent
, const QString
&trackerUrl
)
4082 emit
trackerWarning(torrent
, trackerUrl
);
4085 bool Session::hasPerTorrentRatioLimit() const
4087 return std::any_of(m_torrents
.cbegin(), m_torrents
.cend(), [](const TorrentImpl
*torrent
)
4089 return (torrent
->ratioLimit() >= 0);
4093 bool Session::hasPerTorrentSeedingTimeLimit() const
4095 return std::any_of(m_torrents
.cbegin(), m_torrents
.cend(), [](const TorrentImpl
*torrent
)
4097 return (torrent
->seedingTimeLimit() >= 0);
4101 void Session::configureDeferred()
4103 if (m_deferredConfigureScheduled
)
4106 m_deferredConfigureScheduled
= true;
4107 QMetaObject::invokeMethod(this, qOverload
<>(&Session::configure
), Qt::QueuedConnection
);
4110 // Enable IP Filtering
4111 // this method creates ban list from scratch combining user ban list and 3rd party ban list file
4112 void Session::enableIPFilter()
4114 qDebug("Enabling IPFilter");
4115 // 1. Parse the IP filter
4116 // 2. In the slot add the manually banned IPs to the provided lt::ip_filter
4117 // 3. Set the ip_filter in one go so there isn't a time window where there isn't an ip_filter
4118 // set between clearing the old one and setting the new one.
4119 if (!m_filterParser
)
4121 m_filterParser
= new FilterParserThread(this);
4122 connect(m_filterParser
.data(), &FilterParserThread::IPFilterParsed
, this, &Session::handleIPFilterParsed
);
4123 connect(m_filterParser
.data(), &FilterParserThread::IPFilterError
, this, &Session::handleIPFilterError
);
4125 m_filterParser
->processFilterFile(IPFilterFile());
4128 // Disable IP Filtering
4129 void Session::disableIPFilter()
4131 qDebug("Disabling IPFilter");
4134 disconnect(m_filterParser
.data(), nullptr, this, nullptr);
4135 delete m_filterParser
;
4138 // Add the banned IPs after the IPFilter disabling
4139 // which creates an empty filter and overrides all previously
4141 lt::ip_filter filter
;
4142 processBannedIPs(filter
);
4143 m_nativeSession
->set_ip_filter(filter
);
4146 void Session::recursiveTorrentDownload(const TorrentID
&id
)
4148 TorrentImpl
*const torrent
= m_torrents
.value(id
);
4149 if (!torrent
) return;
4151 for (int i
= 0; i
< torrent
->filesCount(); ++i
)
4153 const QString torrentRelpath
= torrent
->filePath(i
);
4154 if (torrentRelpath
.endsWith(".torrent"))
4156 LogMsg(tr("Recursive download of file '%1' embedded in torrent '%2'"
4157 , "Recursive download of 'test.torrent' embedded in torrent 'test2'")
4158 .arg(Utils::Fs::toNativePath(torrentRelpath
), torrent
->name()));
4159 const QString torrentFullpath
= torrent
->savePath() + '/' + torrentRelpath
;
4161 AddTorrentParams params
;
4162 // Passing the save path along to the sub torrent file
4163 params
.savePath
= torrent
->savePath();
4164 addTorrent(TorrentInfo::loadFromFile(torrentFullpath
), params
);
4169 const SessionStatus
&Session::status() const
4174 const CacheStatus
&Session::cacheStatus() const
4176 return m_cacheStatus
;
4179 void Session::startUpTorrents()
4181 qDebug("Initializing torrents resume data storage...");
4183 const QString dbPath
= Utils::Fs::expandPathAbs(
4184 specialFolderLocation(SpecialFolder::Data
) + QLatin1String("torrents.db"));
4185 const bool dbStorageExists
= QFile::exists(dbPath
);
4187 ResumeDataStorage
*startupStorage
= nullptr;
4188 if (resumeDataStorageType() == ResumeDataStorageType::SQLite
)
4190 m_resumeDataStorage
= new DBResumeDataStorage(dbPath
, this);
4192 if (!dbStorageExists
)
4194 const QString dataPath
= Utils::Fs::expandPathAbs(
4195 specialFolderLocation(SpecialFolder::Data
) + QLatin1String("BT_backup"));
4196 startupStorage
= new BencodeResumeDataStorage(dataPath
, this);
4201 const QString dataPath
= Utils::Fs::expandPathAbs(
4202 specialFolderLocation(SpecialFolder::Data
) + QLatin1String("BT_backup"));
4203 m_resumeDataStorage
= new BencodeResumeDataStorage(dataPath
, this);
4205 if (dbStorageExists
)
4206 startupStorage
= new DBResumeDataStorage(dbPath
, this);
4209 if (!startupStorage
)
4210 startupStorage
= m_resumeDataStorage
;
4212 qDebug("Starting up torrents...");
4214 const QVector
<TorrentID
> torrents
= startupStorage
->registeredTorrents();
4215 int resumedTorrentsCount
= 0;
4216 QVector
<TorrentID
> queue
;
4217 for (const TorrentID
&torrentID
: torrents
)
4219 const std::optional
<LoadTorrentParams
> resumeData
= startupStorage
->load(torrentID
);
4222 if (m_resumeDataStorage
!= startupStorage
)
4224 m_resumeDataStorage
->store(torrentID
, *resumeData
);
4225 if (isQueueingSystemEnabled() && !resumeData
->hasSeedStatus
)
4226 queue
.append(torrentID
);
4229 qDebug() << "Starting up torrent" << torrentID
.toString() << "...";
4230 if (!loadTorrent(*resumeData
))
4231 LogMsg(tr("Unable to resume torrent '%1'.", "e.g: Unable to resume torrent 'hash'.")
4232 .arg(torrentID
.toString()), Log::CRITICAL
);
4234 // process add torrent messages before message queue overflow
4235 if ((resumedTorrentsCount
% 100) == 0) readAlerts();
4237 ++resumedTorrentsCount
;
4241 LogMsg(tr("Unable to resume torrent '%1'.", "e.g: Unable to resume torrent 'hash'.")
4242 .arg(torrentID
.toString()), Log::CRITICAL
);
4246 if (m_resumeDataStorage
!= startupStorage
)
4248 delete startupStorage
;
4249 if (resumeDataStorageType() == ResumeDataStorageType::Legacy
)
4250 Utils::Fs::forceRemove(dbPath
);
4252 if (isQueueingSystemEnabled())
4253 m_resumeDataStorage
->storeQueue(queue
);
4257 quint64
Session::getAlltimeDL() const
4259 return m_statistics
->getAlltimeDL();
4262 quint64
Session::getAlltimeUL() const
4264 return m_statistics
->getAlltimeUL();
4267 void Session::enqueueRefresh()
4269 Q_ASSERT(!m_refreshEnqueued
);
4271 QTimer::singleShot(refreshInterval(), this, [this] ()
4273 m_nativeSession
->post_torrent_updates();
4274 m_nativeSession
->post_session_stats();
4277 m_refreshEnqueued
= true;
4280 void Session::handleIPFilterParsed(const int ruleCount
)
4284 lt::ip_filter filter
= m_filterParser
->IPfilter();
4285 processBannedIPs(filter
);
4286 m_nativeSession
->set_ip_filter(filter
);
4288 LogMsg(tr("Successfully parsed the provided IP filter: %1 rules were applied.", "%1 is a number").arg(ruleCount
));
4289 emit
IPFilterParsed(false, ruleCount
);
4292 void Session::handleIPFilterError()
4294 lt::ip_filter filter
;
4295 processBannedIPs(filter
);
4296 m_nativeSession
->set_ip_filter(filter
);
4298 LogMsg(tr("Error: Failed to parse the provided IP filter."), Log::CRITICAL
);
4299 emit
IPFilterParsed(true, 0);
4302 std::vector
<lt::alert
*> Session::getPendingAlerts(const lt::time_duration time
) const
4304 if (time
> lt::time_duration::zero())
4305 m_nativeSession
->wait_for_alert(time
);
4307 std::vector
<lt::alert
*> alerts
;
4308 m_nativeSession
->pop_alerts(&alerts
);
4312 TorrentContentLayout
Session::torrentContentLayout() const
4314 return m_torrentContentLayout
;
4317 void Session::setTorrentContentLayout(const TorrentContentLayout value
)
4319 m_torrentContentLayout
= value
;
4322 // Read alerts sent by the BitTorrent session
4323 void Session::readAlerts()
4325 const std::vector
<lt::alert
*> alerts
= getPendingAlerts();
4326 for (const lt::alert
*a
: alerts
)
4330 void Session::handleAlert(const lt::alert
*a
)
4336 #if (LIBTORRENT_VERSION_NUM >= 20003)
4337 case lt::file_prio_alert::alert_type
:
4339 case lt::file_renamed_alert::alert_type
:
4340 case lt::file_completed_alert::alert_type
:
4341 case lt::torrent_finished_alert::alert_type
:
4342 case lt::save_resume_data_alert::alert_type
:
4343 case lt::save_resume_data_failed_alert::alert_type
:
4344 case lt::torrent_paused_alert::alert_type
:
4345 case lt::torrent_resumed_alert::alert_type
:
4346 case lt::tracker_error_alert::alert_type
:
4347 case lt::tracker_reply_alert::alert_type
:
4348 case lt::tracker_warning_alert::alert_type
:
4349 case lt::fastresume_rejected_alert::alert_type
:
4350 case lt::torrent_checked_alert::alert_type
:
4351 case lt::metadata_received_alert::alert_type
:
4352 dispatchTorrentAlert(a
);
4354 case lt::state_update_alert::alert_type
:
4355 handleStateUpdateAlert(static_cast<const lt::state_update_alert
*>(a
));
4357 case lt::session_stats_alert::alert_type
:
4358 handleSessionStatsAlert(static_cast<const lt::session_stats_alert
*>(a
));
4360 case lt::file_error_alert::alert_type
:
4361 handleFileErrorAlert(static_cast<const lt::file_error_alert
*>(a
));
4363 case lt::add_torrent_alert::alert_type
:
4364 handleAddTorrentAlert(static_cast<const lt::add_torrent_alert
*>(a
));
4366 case lt::torrent_removed_alert::alert_type
:
4367 handleTorrentRemovedAlert(static_cast<const lt::torrent_removed_alert
*>(a
));
4369 case lt::torrent_deleted_alert::alert_type
:
4370 handleTorrentDeletedAlert(static_cast<const lt::torrent_deleted_alert
*>(a
));
4372 case lt::torrent_delete_failed_alert::alert_type
:
4373 handleTorrentDeleteFailedAlert(static_cast<const lt::torrent_delete_failed_alert
*>(a
));
4375 case lt::portmap_error_alert::alert_type
:
4376 handlePortmapWarningAlert(static_cast<const lt::portmap_error_alert
*>(a
));
4378 case lt::portmap_alert::alert_type
:
4379 handlePortmapAlert(static_cast<const lt::portmap_alert
*>(a
));
4381 case lt::peer_blocked_alert::alert_type
:
4382 handlePeerBlockedAlert(static_cast<const lt::peer_blocked_alert
*>(a
));
4384 case lt::peer_ban_alert::alert_type
:
4385 handlePeerBanAlert(static_cast<const lt::peer_ban_alert
*>(a
));
4387 case lt::url_seed_alert::alert_type
:
4388 handleUrlSeedAlert(static_cast<const lt::url_seed_alert
*>(a
));
4390 case lt::listen_succeeded_alert::alert_type
:
4391 handleListenSucceededAlert(static_cast<const lt::listen_succeeded_alert
*>(a
));
4393 case lt::listen_failed_alert::alert_type
:
4394 handleListenFailedAlert(static_cast<const lt::listen_failed_alert
*>(a
));
4396 case lt::external_ip_alert::alert_type
:
4397 handleExternalIPAlert(static_cast<const lt::external_ip_alert
*>(a
));
4399 case lt::alerts_dropped_alert::alert_type
:
4400 handleAlertsDroppedAlert(static_cast<const lt::alerts_dropped_alert
*>(a
));
4402 case lt::storage_moved_alert::alert_type
:
4403 handleStorageMovedAlert(static_cast<const lt::storage_moved_alert
*>(a
));
4405 case lt::storage_moved_failed_alert::alert_type
:
4406 handleStorageMovedFailedAlert(static_cast<const lt::storage_moved_failed_alert
*>(a
));
4408 case lt::socks5_alert::alert_type
:
4409 handleSocks5Alert(static_cast<const lt::socks5_alert
*>(a
));
4413 catch (const std::exception
&exc
)
4415 qWarning() << "Caught exception in " << Q_FUNC_INFO
<< ": " << QString::fromStdString(exc
.what());
4419 void Session::dispatchTorrentAlert(const lt::alert
*a
)
4421 TorrentImpl
*const torrent
= m_torrents
.value(static_cast<const lt::torrent_alert
*>(a
)->handle
.info_hash());
4424 torrent
->handleAlert(a
);
4430 case lt::metadata_received_alert::alert_type
:
4431 handleMetadataReceivedAlert(static_cast<const lt::metadata_received_alert
*>(a
));
4436 void Session::createTorrent(const lt::torrent_handle
&nativeHandle
)
4438 #if (LIBTORRENT_VERSION_NUM >= 20000)
4439 const auto torrentID
= TorrentID::fromInfoHash(nativeHandle
.info_hashes());
4441 const auto torrentID
= TorrentID::fromInfoHash(nativeHandle
.info_hash());
4444 Q_ASSERT(m_loadingTorrents
.contains(torrentID
));
4446 const LoadTorrentParams params
= m_loadingTorrents
.take(torrentID
);
4448 auto *const torrent
= new TorrentImpl
{this, m_nativeSession
, nativeHandle
, params
};
4449 m_torrents
.insert(torrent
->id(), torrent
);
4451 const bool hasMetadata
= torrent
->hasMetadata();
4453 if (params
.restored
)
4455 LogMsg(tr("'%1' restored.", "'torrent name' restored.").arg(torrent
->name()));
4459 m_resumeDataStorage
->store(torrent
->id(), params
);
4461 // The following is useless for newly added magnet
4464 // Copy the torrent file to the export folder
4465 if (!torrentExportDirectory().isEmpty())
4467 const TorrentInfo torrentInfo
{params
.ltAddTorrentParams
.ti
};
4468 exportTorrentFile(torrentInfo
, torrentExportDirectory(), torrent
->name());
4472 if (isAddTrackersEnabled() && !torrent
->isPrivate())
4473 torrent
->addTrackers(m_additionalTrackerList
);
4475 LogMsg(tr("'%1' added to download list.", "'torrent name' was added to download list.")
4476 .arg(torrent
->name()));
4479 if (((torrent
->ratioLimit() >= 0) || (torrent
->seedingTimeLimit() >= 0))
4480 && !m_seedingLimitTimer
->isActive())
4481 m_seedingLimitTimer
->start();
4483 // Send torrent addition signal
4484 emit
torrentLoaded(torrent
);
4485 // Send new torrent signal
4486 if (!params
.restored
)
4487 emit
torrentAdded(torrent
);
4489 // Torrent could have error just after adding to libtorrent
4490 if (torrent
->hasError())
4491 LogMsg(tr("Torrent errored. Torrent: \"%1\". Error: %2.").arg(torrent
->name(), torrent
->error()), Log::WARNING
);
4494 void Session::handleAddTorrentAlert(const lt::add_torrent_alert
*p
)
4498 const QString msg
= QString::fromStdString(p
->message());
4499 LogMsg(tr("Couldn't load torrent. Reason: %1.").arg(msg
), Log::WARNING
);
4500 emit
loadTorrentFailed(msg
);
4502 const lt::add_torrent_params
¶ms
= p
->params
;
4503 const bool hasMetadata
= (params
.ti
&& params
.ti
->is_valid());
4504 #if (LIBTORRENT_VERSION_NUM >= 20000)
4505 const auto id
= TorrentID::fromInfoHash(hasMetadata
? params
.ti
->info_hashes() : params
.info_hashes
);
4507 const auto id
= TorrentID::fromInfoHash(hasMetadata
? params
.ti
->info_hash() : params
.info_hash
);
4509 m_loadingTorrents
.remove(id
);
4511 else if (m_loadingTorrents
.contains(p
->handle
.info_hash()))
4513 createTorrent(p
->handle
);
4517 void Session::handleTorrentRemovedAlert(const lt::torrent_removed_alert
*p
)
4519 #if (LIBTORRENT_VERSION_NUM >= 20000)
4520 const auto id
= TorrentID::fromInfoHash(p
->info_hashes
);
4522 const auto id
= TorrentID::fromInfoHash(p
->info_hash
);
4525 const auto removingTorrentDataIter
= m_removingTorrents
.find(id
);
4526 if (removingTorrentDataIter
!= m_removingTorrents
.end())
4528 if (removingTorrentDataIter
->deleteOption
== DeleteTorrent
)
4530 LogMsg(tr("'%1' was removed from the transfer list.", "'xxx.avi' was removed...").arg(removingTorrentDataIter
->name
));
4531 m_removingTorrents
.erase(removingTorrentDataIter
);
4536 void Session::handleTorrentDeletedAlert(const lt::torrent_deleted_alert
*p
)
4538 #if (LIBTORRENT_VERSION_NUM >= 20000)
4539 const auto id
= TorrentID::fromInfoHash(p
->info_hashes
);
4541 const auto id
= TorrentID::fromInfoHash(p
->info_hash
);
4544 const auto removingTorrentDataIter
= m_removingTorrents
.find(id
);
4546 if (removingTorrentDataIter
== m_removingTorrents
.end())
4549 Utils::Fs::smartRemoveEmptyFolderTree(removingTorrentDataIter
->pathToRemove
);
4550 LogMsg(tr("'%1' was removed from the transfer list and hard disk.", "'xxx.avi' was removed...").arg(removingTorrentDataIter
->name
));
4551 m_removingTorrents
.erase(removingTorrentDataIter
);
4554 void Session::handleTorrentDeleteFailedAlert(const lt::torrent_delete_failed_alert
*p
)
4556 #if (LIBTORRENT_VERSION_NUM >= 20000)
4557 const auto id
= TorrentID::fromInfoHash(p
->info_hashes
);
4559 const auto id
= TorrentID::fromInfoHash(p
->info_hash
);
4562 const auto removingTorrentDataIter
= m_removingTorrents
.find(id
);
4564 if (removingTorrentDataIter
== m_removingTorrents
.end())
4569 // libtorrent won't delete the directory if it contains files not listed in the torrent,
4570 // so we remove the directory ourselves
4571 Utils::Fs::smartRemoveEmptyFolderTree(removingTorrentDataIter
->pathToRemove
);
4573 LogMsg(tr("'%1' was removed from the transfer list but the files couldn't be deleted. Error: %2", "'xxx.avi' was removed...")
4574 .arg(removingTorrentDataIter
->name
, QString::fromLocal8Bit(p
->error
.message().c_str()))
4577 else // torrent without metadata, hence no files on disk
4579 LogMsg(tr("'%1' was removed from the transfer list.", "'xxx.avi' was removed...").arg(removingTorrentDataIter
->name
));
4581 m_removingTorrents
.erase(removingTorrentDataIter
);
4584 void Session::handleMetadataReceivedAlert(const lt::metadata_received_alert
*p
)
4586 #if (LIBTORRENT_VERSION_NUM >= 20000)
4587 const auto id
= TorrentID::fromInfoHash(p
->handle
.info_hashes());
4589 const auto id
= TorrentID::fromInfoHash(p
->handle
.info_hash());
4592 const auto downloadedMetadataIter
= m_downloadedMetadata
.find(id
);
4594 if (downloadedMetadataIter
!= m_downloadedMetadata
.end())
4596 const TorrentInfo metadata
{p
->handle
.torrent_file()};
4598 m_downloadedMetadata
.erase(downloadedMetadataIter
);
4601 m_nativeSession
->remove_torrent(p
->handle
, lt::session::delete_files
);
4603 emit
metadataDownloaded(metadata
);
4607 void Session::handleFileErrorAlert(const lt::file_error_alert
*p
)
4609 TorrentImpl
*const torrent
= m_torrents
.value(p
->handle
.info_hash());
4613 torrent
->handleAlert(p
);
4615 const TorrentID id
= torrent
->id();
4616 if (!m_recentErroredTorrents
.contains(id
))
4618 m_recentErroredTorrents
.insert(id
);
4620 const QString msg
= QString::fromStdString(p
->message());
4621 LogMsg(tr("File error alert. Torrent: \"%1\". File: \"%2\". Reason: %3")
4622 .arg(torrent
->name(), p
->filename(), msg
)
4624 emit
fullDiskError(torrent
, msg
);
4627 m_recentErroredTorrentsTimer
->start();
4630 void Session::handlePortmapWarningAlert(const lt::portmap_error_alert
*p
)
4632 LogMsg(tr("UPnP/NAT-PMP: Port mapping failure, message: %1").arg(QString::fromStdString(p
->message())), Log::CRITICAL
);
4635 void Session::handlePortmapAlert(const lt::portmap_alert
*p
)
4637 qDebug("UPnP Success, msg: %s", p
->message().c_str());
4638 LogMsg(tr("UPnP/NAT-PMP: Port mapping successful, message: %1").arg(QString::fromStdString(p
->message())), Log::INFO
);
4641 void Session::handlePeerBlockedAlert(const lt::peer_blocked_alert
*p
)
4646 case lt::peer_blocked_alert::ip_filter
:
4647 reason
= tr("IP filter", "this peer was blocked. Reason: IP filter.");
4649 case lt::peer_blocked_alert::port_filter
:
4650 reason
= tr("port filter", "this peer was blocked. Reason: port filter.");
4652 case lt::peer_blocked_alert::i2p_mixed
:
4653 reason
= tr("%1 mixed mode restrictions", "this peer was blocked. Reason: I2P mixed mode restrictions.").arg("I2P"); // don't translate I2P
4655 case lt::peer_blocked_alert::privileged_ports
:
4656 reason
= tr("use of privileged port", "this peer was blocked. Reason: use of privileged port.");
4658 case lt::peer_blocked_alert::utp_disabled
:
4659 reason
= tr("%1 is disabled", "this peer was blocked. Reason: uTP is disabled.").arg(QString::fromUtf8(C_UTP
)); // don't translate μTP
4661 case lt::peer_blocked_alert::tcp_disabled
:
4662 reason
= tr("%1 is disabled", "this peer was blocked. Reason: TCP is disabled.").arg("TCP"); // don't translate TCP
4666 const QString ip
{toString(p
->endpoint
.address())};
4668 Logger::instance()->addPeer(ip
, true, reason
);
4671 void Session::handlePeerBanAlert(const lt::peer_ban_alert
*p
)
4673 const QString ip
{toString(p
->endpoint
.address())};
4675 Logger::instance()->addPeer(ip
, false);
4678 void Session::handleUrlSeedAlert(const lt::url_seed_alert
*p
)
4680 const TorrentImpl
*torrent
= m_torrents
.value(p
->handle
.info_hash());
4686 LogMsg(tr("URL seed name lookup failed. Torrent: \"%1\". URL: \"%2\". Error: \"%3\"")
4687 .arg(torrent
->name(), p
->server_url(), QString::fromStdString(p
->message()))
4692 LogMsg(tr("Received error message from a URL seed. Torrent: \"%1\". URL: \"%2\". Message: \"%3\"")
4693 .arg(torrent
->name(), p
->server_url(), p
->error_message())
4698 void Session::handleListenSucceededAlert(const lt::listen_succeeded_alert
*p
)
4700 const QString proto
{toString(p
->socket_type
)};
4701 LogMsg(tr("Successfully listening on IP: %1, port: %2/%3"
4702 , "e.g: Successfully listening on IP: 192.168.0.1, port: TCP/6881")
4703 .arg(toString(p
->address
), proto
, QString::number(p
->port
)), Log::INFO
);
4705 // Force reannounce on all torrents because some trackers blacklist some ports
4706 reannounceToAllTrackers();
4709 void Session::handleListenFailedAlert(const lt::listen_failed_alert
*p
)
4711 const QString proto
{toString(p
->socket_type
)};
4712 LogMsg(tr("Failed to listen on IP: %1, port: %2/%3. Reason: %4"
4713 , "e.g: Failed to listen on IP: 192.168.0.1, port: TCP/6881. Reason: already in use")
4714 .arg(toString(p
->address
), proto
, QString::number(p
->port
)
4715 , QString::fromLocal8Bit(p
->error
.message().c_str())), Log::CRITICAL
);
4718 void Session::handleExternalIPAlert(const lt::external_ip_alert
*p
)
4720 const QString externalIP
{toString(p
->external_address
)};
4721 LogMsg(tr("Detected external IP: %1", "e.g. Detected external IP: 1.1.1.1")
4722 .arg(externalIP
), Log::INFO
);
4724 if (m_lastExternalIP
!= externalIP
)
4726 if (isReannounceWhenAddressChangedEnabled() && !m_lastExternalIP
.isEmpty())
4727 reannounceToAllTrackers();
4728 m_lastExternalIP
= externalIP
;
4732 void Session::handleSessionStatsAlert(const lt::session_stats_alert
*p
)
4734 const qreal interval
= lt::total_milliseconds(p
->timestamp() - m_statsLastTimestamp
) / 1000.;
4735 m_statsLastTimestamp
= p
->timestamp();
4737 const auto stats
= p
->counters();
4739 m_status
.hasIncomingConnections
= static_cast<bool>(stats
[m_metricIndices
.net
.hasIncomingConnections
]);
4741 const int64_t ipOverheadDownload
= stats
[m_metricIndices
.net
.recvIPOverheadBytes
];
4742 const int64_t ipOverheadUpload
= stats
[m_metricIndices
.net
.sentIPOverheadBytes
];
4743 const int64_t totalDownload
= stats
[m_metricIndices
.net
.recvBytes
] + ipOverheadDownload
;
4744 const int64_t totalUpload
= stats
[m_metricIndices
.net
.sentBytes
] + ipOverheadUpload
;
4745 const int64_t totalPayloadDownload
= stats
[m_metricIndices
.net
.recvPayloadBytes
];
4746 const int64_t totalPayloadUpload
= stats
[m_metricIndices
.net
.sentPayloadBytes
];
4747 const int64_t trackerDownload
= stats
[m_metricIndices
.net
.recvTrackerBytes
];
4748 const int64_t trackerUpload
= stats
[m_metricIndices
.net
.sentTrackerBytes
];
4749 const int64_t dhtDownload
= stats
[m_metricIndices
.dht
.dhtBytesIn
];
4750 const int64_t dhtUpload
= stats
[m_metricIndices
.dht
.dhtBytesOut
];
4752 auto calcRate
= [interval
](const quint64 previous
, const quint64 current
)
4754 Q_ASSERT(current
>= previous
);
4755 return static_cast<quint64
>((current
- previous
) / interval
);
4758 m_status
.payloadDownloadRate
= calcRate(m_status
.totalPayloadDownload
, totalPayloadDownload
);
4759 m_status
.payloadUploadRate
= calcRate(m_status
.totalPayloadUpload
, totalPayloadUpload
);
4760 m_status
.downloadRate
= calcRate(m_status
.totalDownload
, totalDownload
);
4761 m_status
.uploadRate
= calcRate(m_status
.totalUpload
, totalUpload
);
4762 m_status
.ipOverheadDownloadRate
= calcRate(m_status
.ipOverheadDownload
, ipOverheadDownload
);
4763 m_status
.ipOverheadUploadRate
= calcRate(m_status
.ipOverheadUpload
, ipOverheadUpload
);
4764 m_status
.dhtDownloadRate
= calcRate(m_status
.dhtDownload
, dhtDownload
);
4765 m_status
.dhtUploadRate
= calcRate(m_status
.dhtUpload
, dhtUpload
);
4766 m_status
.trackerDownloadRate
= calcRate(m_status
.trackerDownload
, trackerDownload
);
4767 m_status
.trackerUploadRate
= calcRate(m_status
.trackerUpload
, trackerUpload
);
4769 m_status
.totalDownload
= totalDownload
;
4770 m_status
.totalUpload
= totalUpload
;
4771 m_status
.totalPayloadDownload
= totalPayloadDownload
;
4772 m_status
.totalPayloadUpload
= totalPayloadUpload
;
4773 m_status
.ipOverheadDownload
= ipOverheadDownload
;
4774 m_status
.ipOverheadUpload
= ipOverheadUpload
;
4775 m_status
.trackerDownload
= trackerDownload
;
4776 m_status
.trackerUpload
= trackerUpload
;
4777 m_status
.dhtDownload
= dhtDownload
;
4778 m_status
.dhtUpload
= dhtUpload
;
4779 m_status
.totalWasted
= stats
[m_metricIndices
.net
.recvRedundantBytes
]
4780 + stats
[m_metricIndices
.net
.recvFailedBytes
];
4781 m_status
.dhtNodes
= stats
[m_metricIndices
.dht
.dhtNodes
];
4782 m_status
.diskReadQueue
= stats
[m_metricIndices
.peer
.numPeersUpDisk
];
4783 m_status
.diskWriteQueue
= stats
[m_metricIndices
.peer
.numPeersDownDisk
];
4784 m_status
.peersCount
= stats
[m_metricIndices
.peer
.numPeersConnected
];
4786 m_cacheStatus
.totalUsedBuffers
= stats
[m_metricIndices
.disk
.diskBlocksInUse
];
4787 m_cacheStatus
.jobQueueLength
= stats
[m_metricIndices
.disk
.queuedDiskJobs
];
4789 #if (LIBTORRENT_VERSION_NUM < 20000)
4790 const int64_t numBlocksRead
= stats
[m_metricIndices
.disk
.numBlocksRead
];
4791 const int64_t numBlocksCacheHits
= stats
[m_metricIndices
.disk
.numBlocksCacheHits
];
4792 m_cacheStatus
.readRatio
= static_cast<qreal
>(numBlocksCacheHits
) / std::max
<int64_t>((numBlocksCacheHits
+ numBlocksRead
), 1);
4795 const int64_t totalJobs
= stats
[m_metricIndices
.disk
.writeJobs
] + stats
[m_metricIndices
.disk
.readJobs
]
4796 + stats
[m_metricIndices
.disk
.hashJobs
];
4797 m_cacheStatus
.averageJobTime
= (totalJobs
> 0)
4798 ? (stats
[m_metricIndices
.disk
.diskJobTime
] / totalJobs
) : 0;
4800 emit
statsUpdated();
4802 if (m_refreshEnqueued
)
4803 m_refreshEnqueued
= false;
4808 void Session::handleAlertsDroppedAlert(const lt::alerts_dropped_alert
*p
) const
4810 LogMsg(tr("Error: Internal alert queue full and alerts were dropped, you might see degraded performance. Dropped alert types: %1. Message: %2")
4811 .arg(QString::fromStdString(p
->dropped_alerts
.to_string()), QString::fromStdString(p
->message())), Log::CRITICAL
);
4814 void Session::handleStorageMovedAlert(const lt::storage_moved_alert
*p
)
4816 Q_ASSERT(!m_moveStorageQueue
.isEmpty());
4818 const MoveStorageJob
¤tJob
= m_moveStorageQueue
.first();
4819 Q_ASSERT(currentJob
.torrentHandle
== p
->handle
);
4821 const QString newPath
{p
->storage_path()};
4822 Q_ASSERT(newPath
== currentJob
.path
);
4824 #if (LIBTORRENT_VERSION_NUM >= 20000)
4825 const auto id
= TorrentID::fromInfoHash(currentJob
.torrentHandle
.info_hashes());
4827 const auto id
= TorrentID::fromInfoHash(currentJob
.torrentHandle
.info_hash());
4830 TorrentImpl
*torrent
= m_torrents
.value(id
);
4831 const QString torrentName
= (torrent
? torrent
->name() : id
.toString());
4832 LogMsg(tr("\"%1\" is successfully moved to \"%2\".").arg(torrentName
, newPath
));
4834 handleMoveTorrentStorageJobFinished();
4837 void Session::handleStorageMovedFailedAlert(const lt::storage_moved_failed_alert
*p
)
4839 Q_ASSERT(!m_moveStorageQueue
.isEmpty());
4841 const MoveStorageJob
¤tJob
= m_moveStorageQueue
.first();
4842 Q_ASSERT(currentJob
.torrentHandle
== p
->handle
);
4844 #if (LIBTORRENT_VERSION_NUM >= 20000)
4845 const auto id
= TorrentID::fromInfoHash(currentJob
.torrentHandle
.info_hashes());
4847 const auto id
= TorrentID::fromInfoHash(currentJob
.torrentHandle
.info_hash());
4850 TorrentImpl
*torrent
= m_torrents
.value(id
);
4851 const QString torrentName
= (torrent
? torrent
->name() : id
.toString());
4852 const QString currentLocation
= QString::fromStdString(p
->handle
.status(lt::torrent_handle::query_save_path
).save_path
);
4853 const QString errorMessage
= QString::fromStdString(p
->message());
4854 LogMsg(tr("Failed to move \"%1\" from \"%2\" to \"%3\". Reason: %4.")
4855 .arg(torrentName
, currentLocation
, currentJob
.path
, errorMessage
), Log::CRITICAL
);
4857 handleMoveTorrentStorageJobFinished();
4860 void Session::handleStateUpdateAlert(const lt::state_update_alert
*p
)
4862 QVector
<Torrent
*> updatedTorrents
;
4863 updatedTorrents
.reserve(static_cast<decltype(updatedTorrents
)::size_type
>(p
->status
.size()));
4865 for (const lt::torrent_status
&status
: p
->status
)
4867 #if (LIBTORRENT_VERSION_NUM >= 20000)
4868 const auto id
= TorrentID::fromInfoHash(status
.info_hashes
);
4870 const auto id
= TorrentID::fromInfoHash(status
.info_hash
);
4872 TorrentImpl
*const torrent
= m_torrents
.value(id
);
4876 torrent
->handleStateUpdate(status
);
4877 updatedTorrents
.push_back(torrent
);
4880 if (!updatedTorrents
.isEmpty())
4881 emit
torrentsUpdated(updatedTorrents
);
4883 if (m_refreshEnqueued
)
4884 m_refreshEnqueued
= false;
4889 void Session::handleSocks5Alert(const lt::socks5_alert
*p
) const
4893 LogMsg(tr("SOCKS5 proxy error. Message: %1").arg(QString::fromStdString(p
->message()))