1 /*
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>
5  *
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.
10  *
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.
15  *
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.
19  *
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.
28  */
29 
30 #include "session.h"
31 
32 #include <algorithm>
33 #include <cstdint>
34 #include <queue>
35 #include <string>
36 #include <utility>
37 
38 #ifdef Q_OS_WIN
39 #include <Windows.h>
40 #include <wincrypt.h>
41 #include <iphlpapi.h>
42 #endif
43 
44 #include <libtorrent/alert_types.hpp>
45 #include <libtorrent/bdecode.hpp>
46 #include <libtorrent/bencode.hpp>
47 #include <libtorrent/entry.hpp>
48 #include <libtorrent/error_code.hpp>
49 #include <libtorrent/extensions/smart_ban.hpp>
50 #include <libtorrent/extensions/ut_metadata.hpp>
51 #include <libtorrent/extensions/ut_pex.hpp>
52 #include <libtorrent/ip_filter.hpp>
53 #include <libtorrent/magnet_uri.hpp>
54 #include <libtorrent/read_resume_data.hpp>
55 #include <libtorrent/session.hpp>
56 #include <libtorrent/session_stats.hpp>
57 #include <libtorrent/session_status.hpp>
58 #include <libtorrent/torrent_info.hpp>
59 #include <libtorrent/write_resume_data.hpp>
60 
61 #include <QDebug>
62 #include <QDir>
63 #include <QFile>
64 #include <QHostAddress>
65 #include <QNetworkAddressEntry>
66 #include <QNetworkConfigurationManager>
67 #include <QNetworkInterface>
68 #include <QRegularExpression>
69 #include <QString>
70 #include <QThread>
71 #include <QTimer>
72 #include <QUuid>
73 
74 #include "base/algorithm.h"
75 #include "base/exceptions.h"
76 #include "base/global.h"
77 #include "base/logger.h"
78 #include "base/net/downloadmanager.h"
79 #include "base/net/proxyconfigurationmanager.h"
80 #include "base/profile.h"
81 #include "base/torrentfileguard.h"
82 #include "base/torrentfilter.h"
83 #include "base/unicodestrings.h"
84 #include "base/utils/bytearray.h"
85 #include "base/utils/fs.h"
86 #include "base/utils/misc.h"
87 #include "base/utils/net.h"
88 #include "base/utils/random.h"
89 #include "base/version.h"
90 #include "bandwidthscheduler.h"
91 #include "common.h"
92 #include "customstorage.h"
93 #include "filesearcher.h"
94 #include "filterparserthread.h"
95 #include "ltunderlyingtype.h"
96 #include "magneturi.h"
97 #include "nativesessionextension.h"
98 #include "portforwarderimpl.h"
99 #include "resumedatasavingmanager.h"
100 #include "statistics.h"
101 #include "torrentimpl.h"
102 #include "tracker.h"
103 #include "trackerentry.h"
104 
105 static const char PEER_ID[] = "qB";
106 static const char RESUME_FOLDER[] = "BT_backup";
107 static const char USER_AGENT[] = "qBittorrent/" QBT_VERSION_2;
108 
109 using namespace BitTorrent;
110 
111 namespace
112 {
113     template <typename LTStr>
fromLTString(const LTStr & str)114     QString fromLTString(const LTStr &str)
115     {
116         return QString::fromUtf8(str.data(), static_cast<int>(str.size()));
117     }
118 
torrentQueuePositionUp(const lt::torrent_handle & handle)119     void torrentQueuePositionUp(const lt::torrent_handle &handle)
120     {
121         try
122         {
123             handle.queue_position_up();
124         }
125         catch (const std::exception &exc)
126         {
127             qDebug() << Q_FUNC_INFO << " fails: " << exc.what();
128         }
129     }
130 
torrentQueuePositionDown(const lt::torrent_handle & handle)131     void torrentQueuePositionDown(const lt::torrent_handle &handle)
132     {
133         try
134         {
135             handle.queue_position_down();
136         }
137         catch (const std::exception &exc)
138         {
139             qDebug() << Q_FUNC_INFO << " fails: " << exc.what();
140         }
141     }
142 
torrentQueuePositionTop(const lt::torrent_handle & handle)143     void torrentQueuePositionTop(const lt::torrent_handle &handle)
144     {
145         try
146         {
147             handle.queue_position_top();
148         }
149         catch (const std::exception &exc)
150         {
151             qDebug() << Q_FUNC_INFO << " fails: " << exc.what();
152         }
153     }
154 
torrentQueuePositionBottom(const lt::torrent_handle & handle)155     void torrentQueuePositionBottom(const lt::torrent_handle &handle)
156     {
157         try
158         {
159             handle.queue_position_bottom();
160         }
161         catch (const std::exception &exc)
162         {
163             qDebug() << Q_FUNC_INFO << " fails: " << exc.what();
164         }
165     }
166 
map_cast(const QVariantMap & map)167     QStringMap map_cast(const QVariantMap &map)
168     {
169         QStringMap result;
170         for (auto i = map.cbegin(); i != map.cend(); ++i)
171             result[i.key()] = i.value().toString();
172         return result;
173     }
174 
map_cast(const QStringMap & map)175     QVariantMap map_cast(const QStringMap &map)
176     {
177         QVariantMap result;
178         for (auto i = map.cbegin(); i != map.cend(); ++i)
179             result[i.key()] = i.value();
180         return result;
181     }
182 
normalizePath(const QString & path)183     QString normalizePath(const QString &path)
184     {
185         QString tmp = Utils::Fs::toUniformPath(path.trimmed());
186         if (!tmp.isEmpty() && !tmp.endsWith('/'))
187             return tmp + '/';
188         return tmp;
189     }
190 
normalizeSavePath(QString path,const QString & defaultPath=specialFolderLocation (SpecialFolder::Downloads))191     QString normalizeSavePath(QString path, const QString &defaultPath = specialFolderLocation(SpecialFolder::Downloads))
192     {
193         path = path.trimmed();
194         if (path.isEmpty())
195             path = Utils::Fs::toUniformPath(defaultPath.trimmed());
196 
197         return normalizePath(path);
198     }
199 
expandCategories(const QStringMap & categories)200     QStringMap expandCategories(const QStringMap &categories)
201     {
202         QStringMap expanded = categories;
203 
204         for (auto i = categories.cbegin(); i != categories.cend(); ++i)
205         {
206             const QString &category = i.key();
207             for (const QString &subcat : asConst(Session::expandCategory(category)))
208             {
209                 if (!expanded.contains(subcat))
210                     expanded[subcat] = "";
211             }
212         }
213 
214         return expanded;
215     }
216 
toString(const lt::socket_type_t socketType)217     QString toString(const lt::socket_type_t socketType)
218     {
219         switch (socketType)
220         {
221 #if (LIBTORRENT_VERSION_NUM >= 20000)
222         case lt::socket_type_t::http:
223             return QLatin1String("HTTP");
224         case lt::socket_type_t::http_ssl:
225             return QLatin1String("HTTP_SSL");
226 #endif
227         case lt::socket_type_t::i2p:
228             return QLatin1String("I2P");
229         case lt::socket_type_t::socks5:
230             return QLatin1String("SOCKS5");
231 #if (LIBTORRENT_VERSION_NUM >= 20000)
232         case lt::socket_type_t::socks5_ssl:
233             return QLatin1String("SOCKS5_SSL");
234 #endif
235         case lt::socket_type_t::tcp:
236             return QLatin1String("TCP");
237         case lt::socket_type_t::tcp_ssl:
238             return QLatin1String("TCP_SSL");
239 #if (LIBTORRENT_VERSION_NUM >= 20000)
240         case lt::socket_type_t::utp:
241             return QLatin1String("UTP");
242 #else
243         case lt::socket_type_t::udp:
244             return QLatin1String("UDP");
245 #endif
246         case lt::socket_type_t::utp_ssl:
247             return QLatin1String("UTP_SSL");
248         }
249         return QLatin1String("INVALID");
250     }
251 
toString(const lt::address & address)252     QString toString(const lt::address &address)
253     {
254         try
255         {
256             return QString::fromLatin1(address.to_string().c_str());
257         }
258         catch (const std::exception &)
259         {
260             // suppress conversion error
261         }
262         return {};
263     }
264 
265     template <typename T>
266     struct LowerLimited
267     {
LowerLimited__anon73c796b40111::LowerLimited268         LowerLimited(T limit, T ret)
269             : m_limit(limit)
270             , m_ret(ret)
271         {
272         }
273 
LowerLimited__anon73c796b40111::LowerLimited274         explicit LowerLimited(T limit)
275             : LowerLimited(limit, limit)
276         {
277         }
278 
operator ()__anon73c796b40111::LowerLimited279         T operator()(T val) const
280         {
281             return val <= m_limit ? m_ret : val;
282         }
283 
284     private:
285         const T m_limit;
286         const T m_ret;
287     };
288 
289     template <typename T>
lowerLimited(T limit)290     LowerLimited<T> lowerLimited(T limit) { return LowerLimited<T>(limit); }
291 
292     template <typename T>
lowerLimited(T limit,T ret)293     LowerLimited<T> lowerLimited(T limit, T ret) { return LowerLimited<T>(limit, ret); }
294 
295     template <typename T>
clampValue(const T lower,const T upper)296     auto clampValue(const T lower, const T upper)
297     {
298         return [lower, upper](const T value) -> T
299         {
300             if (value < lower)
301                 return lower;
302             if (value > upper)
303                 return upper;
304             return value;
305         };
306     }
307 
308     using ListType = lt::entry::list_type;
309 
setToEntryList(const QSet<QString> & input)310     ListType setToEntryList(const QSet<QString> &input)
311     {
312         ListType entryList;
313         entryList.reserve(input.size());
314         for (const QString &setValue : input)
315             entryList.emplace_back(setValue.toStdString());
316         return entryList;
317     }
318 
319 #ifdef Q_OS_WIN
convertIfaceNameToGuid(const QString & name)320     QString convertIfaceNameToGuid(const QString &name)
321     {
322         // Under Windows XP or on Qt version <= 5.5 'name' will be a GUID already.
323         const QUuid uuid(name);
324         if (!uuid.isNull())
325             return uuid.toString().toUpper(); // Libtorrent expects the GUID in uppercase
326 
327         NET_LUID luid {};
328         const LONG res = ::ConvertInterfaceNameToLuidW(name.toStdWString().c_str(), &luid);
329         if (res == 0)
330         {
331             GUID guid;
332             if (::ConvertInterfaceLuidToGuid(&luid, &guid) == 0)
333                 return QUuid(guid).toString().toUpper();
334         }
335 
336         return {};
337     }
338 #endif
339 }
340 
341 const int addTorrentParamsId = qRegisterMetaType<AddTorrentParams>();
342 
343 // Session
344 
345 Session *Session::m_instance = nullptr;
346 
347 #define BITTORRENT_KEY(name) "BitTorrent/" name
348 #define BITTORRENT_SESSION_KEY(name) BITTORRENT_KEY("Session/") name
349 
Session(QObject * parent)350 Session::Session(QObject *parent)
351     : QObject(parent)
352     , m_isDHTEnabled(BITTORRENT_SESSION_KEY("DHTEnabled"), true)
353     , m_isLSDEnabled(BITTORRENT_SESSION_KEY("LSDEnabled"), true)
354     , m_isPeXEnabled(BITTORRENT_SESSION_KEY("PeXEnabled"), true)
355     , m_isIPFilteringEnabled(BITTORRENT_SESSION_KEY("IPFilteringEnabled"), false)
356     , m_isTrackerFilteringEnabled(BITTORRENT_SESSION_KEY("TrackerFilteringEnabled"), false)
357     , m_IPFilterFile(BITTORRENT_SESSION_KEY("IPFilter"))
358     , m_announceToAllTrackers(BITTORRENT_SESSION_KEY("AnnounceToAllTrackers"), false)
359     , m_announceToAllTiers(BITTORRENT_SESSION_KEY("AnnounceToAllTiers"), true)
360     , m_asyncIOThreads(BITTORRENT_SESSION_KEY("AsyncIOThreadsCount"), 10)
361     , m_hashingThreads(BITTORRENT_SESSION_KEY("HashingThreadsCount"), 2)
362     , m_filePoolSize(BITTORRENT_SESSION_KEY("FilePoolSize"), 5000)
363     , m_checkingMemUsage(BITTORRENT_SESSION_KEY("CheckingMemUsageSize"), 32)
364     , m_diskCacheSize(BITTORRENT_SESSION_KEY("DiskCacheSize"), -1)
365     , m_diskCacheTTL(BITTORRENT_SESSION_KEY("DiskCacheTTL"), 60)
366     , m_useOSCache(BITTORRENT_SESSION_KEY("UseOSCache"), true)
367 #ifdef Q_OS_WIN
368     , m_coalesceReadWriteEnabled(BITTORRENT_SESSION_KEY("CoalesceReadWrite"), true)
369 #else
370     , m_coalesceReadWriteEnabled(BITTORRENT_SESSION_KEY("CoalesceReadWrite"), false)
371 #endif
372     , m_usePieceExtentAffinity(BITTORRENT_SESSION_KEY("PieceExtentAffinity"), false)
373     , m_isSuggestMode(BITTORRENT_SESSION_KEY("SuggestMode"), false)
374     , m_sendBufferWatermark(BITTORRENT_SESSION_KEY("SendBufferWatermark"), 500)
375     , m_sendBufferLowWatermark(BITTORRENT_SESSION_KEY("SendBufferLowWatermark"), 10)
376     , m_sendBufferWatermarkFactor(BITTORRENT_SESSION_KEY("SendBufferWatermarkFactor"), 50)
377     , m_socketBacklogSize(BITTORRENT_SESSION_KEY("SocketBacklogSize"), 30)
378     , m_isAnonymousModeEnabled(BITTORRENT_SESSION_KEY("AnonymousModeEnabled"), false)
379     , m_isQueueingEnabled(BITTORRENT_SESSION_KEY("QueueingSystemEnabled"), false)
380     , m_maxActiveDownloads(BITTORRENT_SESSION_KEY("MaxActiveDownloads"), 3, lowerLimited(-1))
381     , m_maxActiveUploads(BITTORRENT_SESSION_KEY("MaxActiveUploads"), 3, lowerLimited(-1))
382     , m_maxActiveTorrents(BITTORRENT_SESSION_KEY("MaxActiveTorrents"), 5, lowerLimited(-1))
383     , m_ignoreSlowTorrentsForQueueing(BITTORRENT_SESSION_KEY("IgnoreSlowTorrentsForQueueing"), false)
384     , m_downloadRateForSlowTorrents(BITTORRENT_SESSION_KEY("SlowTorrentsDownloadRate"), 2)
385     , m_uploadRateForSlowTorrents(BITTORRENT_SESSION_KEY("SlowTorrentsUploadRate"), 2)
386     , m_slowTorrentsInactivityTimer(BITTORRENT_SESSION_KEY("SlowTorrentsInactivityTimer"), 60)
387     , m_outgoingPortsMin(BITTORRENT_SESSION_KEY("OutgoingPortsMin"), 0)
388     , m_outgoingPortsMax(BITTORRENT_SESSION_KEY("OutgoingPortsMax"), 0)
389     , m_UPnPLeaseDuration(BITTORRENT_SESSION_KEY("UPnPLeaseDuration"), 0)
390     , m_peerToS(BITTORRENT_SESSION_KEY("PeerToS"), 0x20)
391     , m_ignoreLimitsOnLAN(BITTORRENT_SESSION_KEY("IgnoreLimitsOnLAN"), false)
392     , m_includeOverheadInLimits(BITTORRENT_SESSION_KEY("IncludeOverheadInLimits"), false)
393     , m_announceIP(BITTORRENT_SESSION_KEY("AnnounceIP"))
394     , m_maxConcurrentHTTPAnnounces(BITTORRENT_SESSION_KEY("MaxConcurrentHTTPAnnounces"), 50)
395     , m_stopTrackerTimeout(BITTORRENT_SESSION_KEY("StopTrackerTimeout"), 5)
396     , m_maxConnections(BITTORRENT_SESSION_KEY("MaxConnections"), 500, lowerLimited(0, -1))
397     , m_maxUploads(BITTORRENT_SESSION_KEY("MaxUploads"), 20, lowerLimited(0, -1))
398     , m_maxConnectionsPerTorrent(BITTORRENT_SESSION_KEY("MaxConnectionsPerTorrent"), 100, lowerLimited(0, -1))
399     , m_maxUploadsPerTorrent(BITTORRENT_SESSION_KEY("MaxUploadsPerTorrent"), 4, lowerLimited(0, -1))
400     , m_btProtocol(BITTORRENT_SESSION_KEY("BTProtocol"), BTProtocol::Both
401         , clampValue(BTProtocol::Both, BTProtocol::UTP))
402     , m_isUTPRateLimited(BITTORRENT_SESSION_KEY("uTPRateLimited"), true)
403     , m_utpMixedMode(BITTORRENT_SESSION_KEY("uTPMixedMode"), MixedModeAlgorithm::TCP
404         , clampValue(MixedModeAlgorithm::TCP, MixedModeAlgorithm::Proportional))
405     , m_IDNSupportEnabled(BITTORRENT_SESSION_KEY("IDNSupportEnabled"), false)
406     , m_multiConnectionsPerIpEnabled(BITTORRENT_SESSION_KEY("MultiConnectionsPerIp"), false)
407     , m_validateHTTPSTrackerCertificate(BITTORRENT_SESSION_KEY("ValidateHTTPSTrackerCertificate"), true)
408     , m_SSRFMitigationEnabled(BITTORRENT_SESSION_KEY("SSRFMitigation"), true)
409     , m_blockPeersOnPrivilegedPorts(BITTORRENT_SESSION_KEY("BlockPeersOnPrivilegedPorts"), false)
410     , m_isAddTrackersEnabled(BITTORRENT_SESSION_KEY("AddTrackersEnabled"), false)
411     , m_additionalTrackers(BITTORRENT_SESSION_KEY("AdditionalTrackers"))
412     , m_globalMaxRatio(BITTORRENT_SESSION_KEY("GlobalMaxRatio"), -1, [](qreal r) { return r < 0 ? -1. : r;})
413     , m_globalMaxSeedingMinutes(BITTORRENT_SESSION_KEY("GlobalMaxSeedingMinutes"), -1, lowerLimited(-1))
414     , m_isAddTorrentPaused(BITTORRENT_SESSION_KEY("AddTorrentPaused"), false)
415     , m_torrentContentLayout(BITTORRENT_SESSION_KEY("TorrentContentLayout"), TorrentContentLayout::Original)
416     , m_isAppendExtensionEnabled(BITTORRENT_SESSION_KEY("AddExtensionToIncompleteFiles"), false)
417     , m_refreshInterval(BITTORRENT_SESSION_KEY("RefreshInterval"), 1500)
418     , m_isPreallocationEnabled(BITTORRENT_SESSION_KEY("Preallocation"), false)
419     , m_torrentExportDirectory(BITTORRENT_SESSION_KEY("TorrentExportDirectory"))
420     , m_finishedTorrentExportDirectory(BITTORRENT_SESSION_KEY("FinishedTorrentExportDirectory"))
421     , m_globalDownloadSpeedLimit(BITTORRENT_SESSION_KEY("GlobalDLSpeedLimit"), 0, lowerLimited(0))
422     , m_globalUploadSpeedLimit(BITTORRENT_SESSION_KEY("GlobalUPSpeedLimit"), 0, lowerLimited(0))
423     , m_altGlobalDownloadSpeedLimit(BITTORRENT_SESSION_KEY("AlternativeGlobalDLSpeedLimit"), 10, lowerLimited(0))
424     , m_altGlobalUploadSpeedLimit(BITTORRENT_SESSION_KEY("AlternativeGlobalUPSpeedLimit"), 10, lowerLimited(0))
425     , m_isAltGlobalSpeedLimitEnabled(BITTORRENT_SESSION_KEY("UseAlternativeGlobalSpeedLimit"), false)
426     , m_isBandwidthSchedulerEnabled(BITTORRENT_SESSION_KEY("BandwidthSchedulerEnabled"), false)
427     , m_saveResumeDataInterval(BITTORRENT_SESSION_KEY("SaveResumeDataInterval"), 60)
428     , m_port(BITTORRENT_SESSION_KEY("Port"), -1)
429     , m_useRandomPort(BITTORRENT_SESSION_KEY("UseRandomPort"), false)
430     , m_networkInterface(BITTORRENT_SESSION_KEY("Interface"))
431     , m_networkInterfaceName(BITTORRENT_SESSION_KEY("InterfaceName"))
432     , m_networkInterfaceAddress(BITTORRENT_SESSION_KEY("InterfaceAddress"))
433     , m_encryption(BITTORRENT_SESSION_KEY("Encryption"), 0)
434     , m_isProxyPeerConnectionsEnabled(BITTORRENT_SESSION_KEY("ProxyPeerConnections"), false)
435     , m_chokingAlgorithm(BITTORRENT_SESSION_KEY("ChokingAlgorithm"), ChokingAlgorithm::FixedSlots
436         , clampValue(ChokingAlgorithm::FixedSlots, ChokingAlgorithm::RateBased))
437     , m_seedChokingAlgorithm(BITTORRENT_SESSION_KEY("SeedChokingAlgorithm"), SeedChokingAlgorithm::FastestUpload
438         , clampValue(SeedChokingAlgorithm::RoundRobin, SeedChokingAlgorithm::AntiLeech))
439     , m_storedCategories(BITTORRENT_SESSION_KEY("Categories"))
440     , m_storedTags(BITTORRENT_SESSION_KEY("Tags"))
441     , m_maxRatioAction(BITTORRENT_SESSION_KEY("MaxRatioAction"), Pause)
442     , m_defaultSavePath(BITTORRENT_SESSION_KEY("DefaultSavePath"), specialFolderLocation(SpecialFolder::Downloads), normalizePath)
443     , m_tempPath(BITTORRENT_SESSION_KEY("TempPath"), defaultSavePath() + "temp/", normalizePath)
444     , m_isSubcategoriesEnabled(BITTORRENT_SESSION_KEY("SubcategoriesEnabled"), false)
445     , m_isTempPathEnabled(BITTORRENT_SESSION_KEY("TempPathEnabled"), false)
446     , m_isAutoTMMDisabledByDefault(BITTORRENT_SESSION_KEY("DisableAutoTMMByDefault"), true)
447     , m_isDisableAutoTMMWhenCategoryChanged(BITTORRENT_SESSION_KEY("DisableAutoTMMTriggers/CategoryChanged"), false)
448     , m_isDisableAutoTMMWhenDefaultSavePathChanged(BITTORRENT_SESSION_KEY("DisableAutoTMMTriggers/DefaultSavePathChanged"), true)
449     , m_isDisableAutoTMMWhenCategorySavePathChanged(BITTORRENT_SESSION_KEY("DisableAutoTMMTriggers/CategorySavePathChanged"), true)
450     , m_isTrackerEnabled(BITTORRENT_KEY("TrackerEnabled"), false)
451     , m_peerTurnover(BITTORRENT_SESSION_KEY("PeerTurnover"), 4)
452     , m_peerTurnoverCutoff(BITTORRENT_SESSION_KEY("PeerTurnoverCutOff"), 90)
453     , m_peerTurnoverInterval(BITTORRENT_SESSION_KEY("PeerTurnoverInterval"), 300)
454     , m_bannedIPs("State/BannedIPs"
455                   , QStringList()
456                   , [](const QStringList &value)
__anon73c796b40402(const QStringList &value) 457                         {
458                             QStringList tmp = value;
459                             tmp.sort();
460                             return tmp;
461                         }
462                  )
463 #if defined(Q_OS_WIN)
464     , m_OSMemoryPriority(BITTORRENT_KEY("OSMemoryPriority"), OSMemoryPriority::BelowNormal)
465 #endif
466     , m_resumeFolderLock {new QFile {this}}
467     , m_seedingLimitTimer {new QTimer {this}}
468     , m_resumeDataTimer {new QTimer {this}}
469     , m_statistics {new Statistics {this}}
470     , m_ioThread {new QThread {this}}
471     , m_recentErroredTorrentsTimer {new QTimer {this}}
472     , m_networkManager {new QNetworkConfigurationManager {this}}
473 {
474     if (port() < 0)
475         m_port = Utils::Random::rand(1024, 65535);
476 
477     initResumeFolder();
478 
479     m_recentErroredTorrentsTimer->setSingleShot(true);
480     m_recentErroredTorrentsTimer->setInterval(1000);
481     connect(m_recentErroredTorrentsTimer, &QTimer::timeout
__anon73c796b40502() 482         , this, [this]() { m_recentErroredTorrents.clear(); });
483 
484     m_seedingLimitTimer->setInterval(10000);
485     connect(m_seedingLimitTimer, &QTimer::timeout, this, &Session::processShareLimits);
486 
487     initializeNativeSession();
488     configureComponents();
489 
490     if (isBandwidthSchedulerEnabled())
491         enableBandwidthScheduler();
492 
493     m_categories = map_cast(m_storedCategories);
494     if (isSubcategoriesEnabled())
495     {
496         // if subcategories support changed manually
497         m_categories = expandCategories(m_categories);
498         m_storedCategories = map_cast(m_categories);
499     }
500 
501     m_tags = List::toSet(m_storedTags.get());
502 
503     enqueueRefresh();
504     updateSeedingLimitTimer();
505     populateAdditionalTrackers();
506 
507     enableTracker(isTrackerEnabled());
508 
509     connect(Net::ProxyConfigurationManager::instance()
510         , &Net::ProxyConfigurationManager::proxyConfigurationChanged
511         , this, &Session::configureDeferred);
512 
513     // Network configuration monitor
514     connect(m_networkManager, &QNetworkConfigurationManager::onlineStateChanged, this, &Session::networkOnlineStateChanged);
515     connect(m_networkManager, &QNetworkConfigurationManager::configurationAdded, this, &Session::networkConfigurationChange);
516     connect(m_networkManager, &QNetworkConfigurationManager::configurationRemoved, this, &Session::networkConfigurationChange);
517     connect(m_networkManager, &QNetworkConfigurationManager::configurationChanged, this, &Session::networkConfigurationChange);
518 
519     m_resumeDataSavingManager = new ResumeDataSavingManager {m_resumeFolderPath};
520     m_resumeDataSavingManager->moveToThread(m_ioThread);
521     connect(m_ioThread, &QThread::finished, m_resumeDataSavingManager, &QObject::deleteLater);
522 
523     m_fileSearcher = new FileSearcher;
524     m_fileSearcher->moveToThread(m_ioThread);
525     connect(m_ioThread, &QThread::finished, m_fileSearcher, &QObject::deleteLater);
526     connect(m_fileSearcher, &FileSearcher::searchFinished, this, &Session::fileSearchFinished);
527 
528     m_ioThread->start();
529 
530     // Regular saving of fastresume data
__anon73c796b40602() 531     connect(m_resumeDataTimer, &QTimer::timeout, this, [this]() { generateResumeData(); });
532     const int saveInterval = saveResumeDataInterval();
533     if (saveInterval > 0)
534     {
535         m_resumeDataTimer->setInterval(saveInterval * 60 * 1000);
536         m_resumeDataTimer->start();
537     }
538 
539     // initialize PortForwarder instance
540     new PortForwarderImpl {m_nativeSession};
541 
542     initMetrics();
543 }
544 
isDHTEnabled() const545 bool Session::isDHTEnabled() const
546 {
547     return m_isDHTEnabled;
548 }
549 
setDHTEnabled(bool enabled)550 void Session::setDHTEnabled(bool enabled)
551 {
552     if (enabled != m_isDHTEnabled)
553     {
554         m_isDHTEnabled = enabled;
555         configureDeferred();
556         LogMsg(tr("DHT support [%1]").arg(enabled ? tr("ON") : tr("OFF")), Log::INFO);
557     }
558 }
559 
isLSDEnabled() const560 bool Session::isLSDEnabled() const
561 {
562     return m_isLSDEnabled;
563 }
564 
setLSDEnabled(const bool enabled)565 void Session::setLSDEnabled(const bool enabled)
566 {
567     if (enabled != m_isLSDEnabled)
568     {
569         m_isLSDEnabled = enabled;
570         configureDeferred();
571         LogMsg(tr("Local Peer Discovery support [%1]").arg(enabled ? tr("ON") : tr("OFF"))
572             , Log::INFO);
573     }
574 }
575 
isPeXEnabled() const576 bool Session::isPeXEnabled() const
577 {
578     return m_isPeXEnabled;
579 }
580 
setPeXEnabled(const bool enabled)581 void Session::setPeXEnabled(const bool enabled)
582 {
583     m_isPeXEnabled = enabled;
584     if (m_wasPexEnabled != enabled)
585         LogMsg(tr("Restart is required to toggle PeX support"), Log::WARNING);
586 }
587 
isTempPathEnabled() const588 bool Session::isTempPathEnabled() const
589 {
590     return m_isTempPathEnabled;
591 }
592 
setTempPathEnabled(const bool enabled)593 void Session::setTempPathEnabled(const bool enabled)
594 {
595     if (enabled != isTempPathEnabled())
596     {
597         m_isTempPathEnabled = enabled;
598         for (TorrentImpl *const torrent : asConst(m_torrents))
599             torrent->handleTempPathChanged();
600     }
601 }
602 
isAppendExtensionEnabled() const603 bool Session::isAppendExtensionEnabled() const
604 {
605     return m_isAppendExtensionEnabled;
606 }
607 
setAppendExtensionEnabled(const bool enabled)608 void Session::setAppendExtensionEnabled(const bool enabled)
609 {
610     if (isAppendExtensionEnabled() != enabled)
611     {
612         m_isAppendExtensionEnabled = enabled;
613 
614         // append or remove .!qB extension for incomplete files
615         for (TorrentImpl *const torrent : asConst(m_torrents))
616             torrent->handleAppendExtensionToggled();
617     }
618 }
619 
refreshInterval() const620 int Session::refreshInterval() const
621 {
622     return m_refreshInterval;
623 }
624 
setRefreshInterval(const int value)625 void Session::setRefreshInterval(const int value)
626 {
627     if (value != refreshInterval())
628     {
629         m_refreshInterval = value;
630     }
631 }
632 
isPreallocationEnabled() const633 bool Session::isPreallocationEnabled() const
634 {
635     return m_isPreallocationEnabled;
636 }
637 
setPreallocationEnabled(const bool enabled)638 void Session::setPreallocationEnabled(const bool enabled)
639 {
640     m_isPreallocationEnabled = enabled;
641 }
642 
torrentExportDirectory() const643 QString Session::torrentExportDirectory() const
644 {
645     return Utils::Fs::toUniformPath(m_torrentExportDirectory);
646 }
647 
setTorrentExportDirectory(QString path)648 void Session::setTorrentExportDirectory(QString path)
649 {
650     path = Utils::Fs::toUniformPath(path);
651     if (path != torrentExportDirectory())
652         m_torrentExportDirectory = path;
653 }
654 
finishedTorrentExportDirectory() const655 QString Session::finishedTorrentExportDirectory() const
656 {
657     return Utils::Fs::toUniformPath(m_finishedTorrentExportDirectory);
658 }
659 
setFinishedTorrentExportDirectory(QString path)660 void Session::setFinishedTorrentExportDirectory(QString path)
661 {
662     path = Utils::Fs::toUniformPath(path);
663     if (path != finishedTorrentExportDirectory())
664         m_finishedTorrentExportDirectory = path;
665 }
666 
defaultSavePath() const667 QString Session::defaultSavePath() const
668 {
669     return Utils::Fs::toUniformPath(m_defaultSavePath);
670 }
671 
tempPath() const672 QString Session::tempPath() const
673 {
674     return Utils::Fs::toUniformPath(m_tempPath);
675 }
676 
torrentTempPath(const TorrentInfo & torrentInfo) const677 QString Session::torrentTempPath(const TorrentInfo &torrentInfo) const
678 {
679     if ((torrentInfo.filesCount() > 1) && !torrentInfo.hasRootFolder())
680         return tempPath() + torrentInfo.name() + '/';
681 
682     return tempPath();
683 }
684 
isValidCategoryName(const QString & name)685 bool Session::isValidCategoryName(const QString &name)
686 {
687     static const QRegularExpression re(R"(^([^\\\/]|[^\\\/]([^\\\/]|\/(?=[^\/]))*[^\\\/])$)");
688     if (!name.isEmpty() && (name.indexOf(re) != 0))
689     {
690         qDebug() << "Incorrect category name:" << name;
691         return false;
692     }
693 
694     return true;
695 }
696 
expandCategory(const QString & category)697 QStringList Session::expandCategory(const QString &category)
698 {
699     QStringList result;
700     if (!isValidCategoryName(category))
701         return result;
702 
703     int index = 0;
704     while ((index = category.indexOf('/', index)) >= 0)
705     {
706         result << category.left(index);
707         ++index;
708     }
709     result << category;
710 
711     return result;
712 }
713 
categories() const714 QStringMap Session::categories() const
715 {
716     return m_categories;
717 }
718 
categorySavePath(const QString & categoryName) const719 QString Session::categorySavePath(const QString &categoryName) const
720 {
721     const QString basePath = m_defaultSavePath;
722     if (categoryName.isEmpty()) return basePath;
723 
724     QString path = m_categories.value(categoryName);
725     if (path.isEmpty()) // use implicit save path
726         path = Utils::Fs::toValidFileSystemName(categoryName, true);
727 
728     if (!QDir::isAbsolutePath(path))
729         path.prepend(basePath);
730 
731     return normalizeSavePath(path);
732 }
733 
addCategory(const QString & name,const QString & savePath)734 bool Session::addCategory(const QString &name, const QString &savePath)
735 {
736     if (name.isEmpty()) return false;
737     if (!isValidCategoryName(name) || m_categories.contains(name))
738         return false;
739 
740     if (isSubcategoriesEnabled())
741     {
742         for (const QString &parent : asConst(expandCategory(name)))
743         {
744             if ((parent != name) && !m_categories.contains(parent))
745             {
746                 m_categories[parent] = "";
747                 emit categoryAdded(parent);
748             }
749         }
750     }
751 
752     m_categories[name] = savePath;
753     m_storedCategories = map_cast(m_categories);
754     emit categoryAdded(name);
755 
756     return true;
757 }
758 
editCategory(const QString & name,const QString & savePath)759 bool Session::editCategory(const QString &name, const QString &savePath)
760 {
761     if (!m_categories.contains(name)) return false;
762     if (categorySavePath(name) == savePath) return false;
763 
764     m_categories[name] = savePath;
765     m_storedCategories = map_cast(m_categories);
766     if (isDisableAutoTMMWhenCategorySavePathChanged())
767     {
768         for (TorrentImpl *const torrent : asConst(m_torrents))
769             if (torrent->category() == name)
770                 torrent->setAutoTMMEnabled(false);
771     }
772     else
773     {
774         for (TorrentImpl *const torrent : asConst(m_torrents))
775             if (torrent->category() == name)
776                 torrent->handleCategorySavePathChanged();
777     }
778 
779     return true;
780 }
781 
removeCategory(const QString & name)782 bool Session::removeCategory(const QString &name)
783 {
784     for (TorrentImpl *const torrent : asConst(m_torrents))
785         if (torrent->belongsToCategory(name))
786             torrent->setCategory("");
787 
788     // remove stored category and its subcategories if exist
789     bool result = false;
790     if (isSubcategoriesEnabled())
791     {
792         // remove subcategories
793         const QString test = name + '/';
794         Algorithm::removeIf(m_categories, [this, &test, &result](const QString &category, const QString &)
795         {
796             if (category.startsWith(test))
797             {
798                 result = true;
799                 emit categoryRemoved(category);
800                 return true;
801             }
802             return false;
803         });
804     }
805 
806     result = (m_categories.remove(name) > 0) || result;
807 
808     if (result)
809     {
810         // update stored categories
811         m_storedCategories = map_cast(m_categories);
812         emit categoryRemoved(name);
813     }
814 
815     return result;
816 }
817 
isSubcategoriesEnabled() const818 bool Session::isSubcategoriesEnabled() const
819 {
820     return m_isSubcategoriesEnabled;
821 }
822 
setSubcategoriesEnabled(const bool value)823 void Session::setSubcategoriesEnabled(const bool value)
824 {
825     if (isSubcategoriesEnabled() == value) return;
826 
827     if (value)
828     {
829         // expand categories to include all parent categories
830         m_categories = expandCategories(m_categories);
831         // update stored categories
832         m_storedCategories = map_cast(m_categories);
833     }
834     else
835     {
836         // reload categories
837         m_categories = map_cast(m_storedCategories);
838     }
839 
840     m_isSubcategoriesEnabled = value;
841     emit subcategoriesSupportChanged();
842 }
843 
tags() const844 QSet<QString> Session::tags() const
845 {
846     return m_tags;
847 }
848 
isValidTag(const QString & tag)849 bool Session::isValidTag(const QString &tag)
850 {
851     return (!tag.trimmed().isEmpty() && !tag.contains(','));
852 }
853 
hasTag(const QString & tag) const854 bool Session::hasTag(const QString &tag) const
855 {
856     return m_tags.contains(tag);
857 }
858 
addTag(const QString & tag)859 bool Session::addTag(const QString &tag)
860 {
861     if (!isValidTag(tag) || hasTag(tag))
862         return false;
863 
864     m_tags.insert(tag);
865     m_storedTags = m_tags.values();
866     emit tagAdded(tag);
867     return true;
868 }
869 
removeTag(const QString & tag)870 bool Session::removeTag(const QString &tag)
871 {
872     if (m_tags.remove(tag))
873     {
874         for (TorrentImpl *const torrent : asConst(m_torrents))
875             torrent->removeTag(tag);
876         m_storedTags = m_tags.values();
877         emit tagRemoved(tag);
878         return true;
879     }
880     return false;
881 }
882 
isAutoTMMDisabledByDefault() const883 bool Session::isAutoTMMDisabledByDefault() const
884 {
885     return m_isAutoTMMDisabledByDefault;
886 }
887 
setAutoTMMDisabledByDefault(const bool value)888 void Session::setAutoTMMDisabledByDefault(const bool value)
889 {
890     m_isAutoTMMDisabledByDefault = value;
891 }
892 
isDisableAutoTMMWhenCategoryChanged() const893 bool Session::isDisableAutoTMMWhenCategoryChanged() const
894 {
895     return m_isDisableAutoTMMWhenCategoryChanged;
896 }
897 
setDisableAutoTMMWhenCategoryChanged(const bool value)898 void Session::setDisableAutoTMMWhenCategoryChanged(const bool value)
899 {
900     m_isDisableAutoTMMWhenCategoryChanged = value;
901 }
902 
isDisableAutoTMMWhenDefaultSavePathChanged() const903 bool Session::isDisableAutoTMMWhenDefaultSavePathChanged() const
904 {
905     return m_isDisableAutoTMMWhenDefaultSavePathChanged;
906 }
907 
setDisableAutoTMMWhenDefaultSavePathChanged(const bool value)908 void Session::setDisableAutoTMMWhenDefaultSavePathChanged(const bool value)
909 {
910     m_isDisableAutoTMMWhenDefaultSavePathChanged = value;
911 }
912 
isDisableAutoTMMWhenCategorySavePathChanged() const913 bool Session::isDisableAutoTMMWhenCategorySavePathChanged() const
914 {
915     return m_isDisableAutoTMMWhenCategorySavePathChanged;
916 }
917 
setDisableAutoTMMWhenCategorySavePathChanged(const bool value)918 void Session::setDisableAutoTMMWhenCategorySavePathChanged(const bool value)
919 {
920     m_isDisableAutoTMMWhenCategorySavePathChanged = value;
921 }
922 
isAddTorrentPaused() const923 bool Session::isAddTorrentPaused() const
924 {
925     return m_isAddTorrentPaused;
926 }
927 
setAddTorrentPaused(const bool value)928 void Session::setAddTorrentPaused(const bool value)
929 {
930     m_isAddTorrentPaused = value;
931 }
932 
isTrackerEnabled() const933 bool Session::isTrackerEnabled() const
934 {
935     return m_isTrackerEnabled;
936 }
937 
setTrackerEnabled(const bool enabled)938 void Session::setTrackerEnabled(const bool enabled)
939 {
940     if (m_isTrackerEnabled != enabled)
941         m_isTrackerEnabled = enabled;
942 
943     // call enableTracker() unconditionally, otherwise port change won't trigger
944     // tracker restart
945     enableTracker(enabled);
946 }
947 
globalMaxRatio() const948 qreal Session::globalMaxRatio() const
949 {
950     return m_globalMaxRatio;
951 }
952 
953 // Torrents with a ratio superior to the given value will
954 // be automatically deleted
setGlobalMaxRatio(qreal ratio)955 void Session::setGlobalMaxRatio(qreal ratio)
956 {
957     if (ratio < 0)
958         ratio = -1.;
959 
960     if (ratio != globalMaxRatio())
961     {
962         m_globalMaxRatio = ratio;
963         updateSeedingLimitTimer();
964     }
965 }
966 
globalMaxSeedingMinutes() const967 int Session::globalMaxSeedingMinutes() const
968 {
969     return m_globalMaxSeedingMinutes;
970 }
971 
setGlobalMaxSeedingMinutes(int minutes)972 void Session::setGlobalMaxSeedingMinutes(int minutes)
973 {
974     if (minutes < 0)
975         minutes = -1;
976 
977     if (minutes != globalMaxSeedingMinutes())
978     {
979         m_globalMaxSeedingMinutes = minutes;
980         updateSeedingLimitTimer();
981     }
982 }
983 
984 // Main destructor
~Session()985 Session::~Session()
986 {
987     // Do some BT related saving
988     saveResumeData();
989 
990     // We must delete FilterParserThread
991     // before we delete lt::session
992     delete m_filterParser;
993 
994     // We must delete PortForwarderImpl before
995     // we delete lt::session
996     delete Net::PortForwarder::instance();
997 
998     qDebug("Deleting the session");
999     delete m_nativeSession;
1000 
1001     m_ioThread->quit();
1002     m_ioThread->wait();
1003 
1004     m_resumeFolderLock->close();
1005     m_resumeFolderLock->remove();
1006 }
1007 
initInstance()1008 void Session::initInstance()
1009 {
1010     if (!m_instance)
1011         m_instance = new Session;
1012 }
1013 
freeInstance()1014 void Session::freeInstance()
1015 {
1016     delete m_instance;
1017     m_instance = nullptr;
1018 }
1019 
instance()1020 Session *Session::instance()
1021 {
1022     return m_instance;
1023 }
1024 
adjustLimits()1025 void Session::adjustLimits()
1026 {
1027     if (isQueueingSystemEnabled())
1028     {
1029         lt::settings_pack settingsPack = m_nativeSession->get_settings();
1030         adjustLimits(settingsPack);
1031         m_nativeSession->apply_settings(settingsPack);
1032     }
1033 }
1034 
applyBandwidthLimits()1035 void Session::applyBandwidthLimits()
1036 {
1037         lt::settings_pack settingsPack = m_nativeSession->get_settings();
1038         applyBandwidthLimits(settingsPack);
1039         m_nativeSession->apply_settings(settingsPack);
1040 }
1041 
configure()1042 void Session::configure()
1043 {
1044     lt::settings_pack settingsPack = m_nativeSession->get_settings();
1045     loadLTSettings(settingsPack);
1046     m_nativeSession->apply_settings(settingsPack);
1047 
1048     configureComponents();
1049 
1050     m_deferredConfigureScheduled = false;
1051 }
1052 
configureComponents()1053 void Session::configureComponents()
1054 {
1055     // This function contains components/actions that:
1056     // 1. Need to be setup at start up
1057     // 2. When deferred configure is called
1058 
1059     configurePeerClasses();
1060 
1061     if (!m_IPFilteringConfigured)
1062     {
1063         if (isIPFilteringEnabled())
1064             enableIPFilter();
1065         else
1066             disableIPFilter();
1067         m_IPFilteringConfigured = true;
1068     }
1069 
1070 #if defined(Q_OS_WIN)
1071     applyOSMemoryPriority();
1072 #endif
1073 }
1074 
initializeNativeSession()1075 void Session::initializeNativeSession()
1076 {
1077     const lt::alert_category_t alertMask = lt::alert::error_notification
1078         | lt::alert::file_progress_notification
1079         | lt::alert::ip_block_notification
1080         | lt::alert::peer_notification
1081         | lt::alert::performance_warning
1082         | lt::alert::port_mapping_notification
1083         | lt::alert::status_notification
1084         | lt::alert::storage_notification
1085         | lt::alert::tracker_notification;
1086     const std::string peerId = lt::generate_fingerprint(PEER_ID, QBT_VERSION_MAJOR, QBT_VERSION_MINOR, QBT_VERSION_BUGFIX, QBT_VERSION_BUILD);
1087 
1088     lt::settings_pack pack;
1089     pack.set_int(lt::settings_pack::alert_mask, alertMask);
1090     pack.set_str(lt::settings_pack::peer_fingerprint, peerId);
1091     pack.set_bool(lt::settings_pack::listen_system_port_fallback, false);
1092     pack.set_str(lt::settings_pack::user_agent, USER_AGENT);
1093     pack.set_bool(lt::settings_pack::use_dht_as_fallback, false);
1094     // Speed up exit
1095     pack.set_int(lt::settings_pack::auto_scrape_interval, 1200); // 20 minutes
1096     pack.set_int(lt::settings_pack::auto_scrape_min_interval, 900); // 15 minutes
1097     pack.set_int(lt::settings_pack::connection_speed, 20); // default is 10
1098     // libtorrent 1.1 enables UPnP & NAT-PMP by default
1099     // turn them off before `lt::session` ctor to avoid split second effects
1100     pack.set_bool(lt::settings_pack::enable_upnp, false);
1101     pack.set_bool(lt::settings_pack::enable_natpmp, false);
1102 
1103 #if (LIBTORRENT_VERSION_NUM > 20000)
1104     // preserve the same behavior as in earlier libtorrent versions
1105     pack.set_bool(lt::settings_pack::enable_set_file_valid_data, true);
1106 #endif
1107 
1108     loadLTSettings(pack);
1109     lt::session_params sessionParams {pack, {}};
1110 #if (LIBTORRENT_VERSION_NUM >= 20000)
1111     sessionParams.disk_io_constructor = customDiskIOConstructor;
1112 #endif
1113     m_nativeSession = new lt::session {sessionParams};
1114 
1115     LogMsg(tr("Peer ID: ") + QString::fromStdString(peerId));
1116     LogMsg(tr("HTTP User-Agent is '%1'").arg(USER_AGENT));
1117     LogMsg(tr("DHT support [%1]").arg(isDHTEnabled() ? tr("ON") : tr("OFF")), Log::INFO);
1118     LogMsg(tr("Local Peer Discovery support [%1]").arg(isLSDEnabled() ? tr("ON") : tr("OFF")), Log::INFO);
1119     LogMsg(tr("PeX support [%1]").arg(isPeXEnabled() ? tr("ON") : tr("OFF")), Log::INFO);
1120     LogMsg(tr("Anonymous mode [%1]").arg(isAnonymousModeEnabled() ? tr("ON") : tr("OFF")), Log::INFO);
1121     LogMsg(tr("Encryption support [%1]").arg((encryption() == 0) ? tr("ON") :
1122         ((encryption() == 1) ? tr("FORCED") : tr("OFF"))), Log::INFO);
1123 
1124     m_nativeSession->set_alert_notify([this]()
1125     {
1126         QMetaObject::invokeMethod(this, &Session::readAlerts, Qt::QueuedConnection);
1127     });
1128 
1129     // Enabling plugins
1130     m_nativeSession->add_extension(&lt::create_smart_ban_plugin);
1131     m_nativeSession->add_extension(&lt::create_ut_metadata_plugin);
1132     if (isPeXEnabled())
1133         m_nativeSession->add_extension(&lt::create_ut_pex_plugin);
1134 
1135     m_nativeSession->add_extension(std::make_shared<NativeSessionExtension>());
1136 }
1137 
processBannedIPs(lt::ip_filter & filter)1138 void Session::processBannedIPs(lt::ip_filter &filter)
1139 {
1140     // First, import current filter
1141     for (const QString &ip : asConst(m_bannedIPs.get()))
1142     {
1143         lt::error_code ec;
1144         const lt::address addr = lt::make_address(ip.toLatin1().constData(), ec);
1145         Q_ASSERT(!ec);
1146         if (!ec)
1147             filter.add_rule(addr, addr, lt::ip_filter::blocked);
1148     }
1149 }
1150 
adjustLimits(lt::settings_pack & settingsPack) const1151 void Session::adjustLimits(lt::settings_pack &settingsPack) const
1152 {
1153     // Internally increase the queue limits to ensure that the magnet is started
1154     const int maxDownloads = maxActiveDownloads();
1155     const int maxActive = maxActiveTorrents();
1156 
1157     settingsPack.set_int(lt::settings_pack::active_downloads
1158                          , maxDownloads > -1 ? maxDownloads + m_extraLimit : maxDownloads);
1159     settingsPack.set_int(lt::settings_pack::active_limit
1160                          , maxActive > -1 ? maxActive + m_extraLimit : maxActive);
1161 }
1162 
applyBandwidthLimits(lt::settings_pack & settingsPack) const1163 void Session::applyBandwidthLimits(lt::settings_pack &settingsPack) const
1164 {
1165     const bool altSpeedLimitEnabled = isAltGlobalSpeedLimitEnabled();
1166     settingsPack.set_int(lt::settings_pack::download_rate_limit, altSpeedLimitEnabled ? altGlobalDownloadSpeedLimit() : globalDownloadSpeedLimit());
1167     settingsPack.set_int(lt::settings_pack::upload_rate_limit, altSpeedLimitEnabled ? altGlobalUploadSpeedLimit() : globalUploadSpeedLimit());
1168 }
1169 
initMetrics()1170 void Session::initMetrics()
1171 {
1172     const auto findMetricIndex = [](const char *name) -> int
1173     {
1174         const int index = lt::find_metric_idx(name);
1175         Q_ASSERT(index >= 0);
1176         return index;
1177     };
1178 
1179     // TODO: switch to "designated initializers" in C++20
1180     m_metricIndices.net.hasIncomingConnections = findMetricIndex("net.has_incoming_connections");
1181     m_metricIndices.net.sentPayloadBytes = findMetricIndex("net.sent_payload_bytes");
1182     m_metricIndices.net.recvPayloadBytes = findMetricIndex("net.recv_payload_bytes");
1183     m_metricIndices.net.sentBytes = findMetricIndex("net.sent_bytes");
1184     m_metricIndices.net.recvBytes = findMetricIndex("net.recv_bytes");
1185     m_metricIndices.net.sentIPOverheadBytes = findMetricIndex("net.sent_ip_overhead_bytes");
1186     m_metricIndices.net.recvIPOverheadBytes = findMetricIndex("net.recv_ip_overhead_bytes");
1187     m_metricIndices.net.sentTrackerBytes = findMetricIndex("net.sent_tracker_bytes");
1188     m_metricIndices.net.recvTrackerBytes = findMetricIndex("net.recv_tracker_bytes");
1189     m_metricIndices.net.recvRedundantBytes = findMetricIndex("net.recv_redundant_bytes");
1190     m_metricIndices.net.recvFailedBytes = findMetricIndex("net.recv_failed_bytes");
1191 
1192     m_metricIndices.peer.numPeersConnected = findMetricIndex("peer.num_peers_connected");
1193     m_metricIndices.peer.numPeersDownDisk = findMetricIndex("peer.num_peers_down_disk");
1194     m_metricIndices.peer.numPeersUpDisk = findMetricIndex("peer.num_peers_up_disk");
1195 
1196     m_metricIndices.dht.dhtBytesIn = findMetricIndex("dht.dht_bytes_in");
1197     m_metricIndices.dht.dhtBytesOut = findMetricIndex("dht.dht_bytes_out");
1198     m_metricIndices.dht.dhtNodes = findMetricIndex("dht.dht_nodes");
1199 
1200     m_metricIndices.disk.diskBlocksInUse = findMetricIndex("disk.disk_blocks_in_use");
1201     m_metricIndices.disk.numBlocksRead = findMetricIndex("disk.num_blocks_read");
1202 #if (LIBTORRENT_VERSION_NUM < 20000)
1203     m_metricIndices.disk.numBlocksCacheHits = findMetricIndex("disk.num_blocks_cache_hits");
1204 #endif
1205     m_metricIndices.disk.writeJobs = findMetricIndex("disk.num_write_ops");
1206     m_metricIndices.disk.readJobs = findMetricIndex("disk.num_read_ops");
1207     m_metricIndices.disk.hashJobs = findMetricIndex("disk.num_blocks_hashed");
1208     m_metricIndices.disk.queuedDiskJobs = findMetricIndex("disk.queued_disk_jobs");
1209     m_metricIndices.disk.diskJobTime = findMetricIndex("disk.disk_job_time");
1210 }
1211 
loadLTSettings(lt::settings_pack & settingsPack)1212 void Session::loadLTSettings(lt::settings_pack &settingsPack)
1213 {
1214     // from libtorrent doc:
1215     // It will not take affect until the listen_interfaces settings is updated
1216     settingsPack.set_int(lt::settings_pack::listen_queue_size, socketBacklogSize());
1217 
1218     configureNetworkInterfaces(settingsPack);
1219     applyBandwidthLimits(settingsPack);
1220 
1221     // The most secure, rc4 only so that all streams are encrypted
1222     settingsPack.set_int(lt::settings_pack::allowed_enc_level, lt::settings_pack::pe_rc4);
1223     settingsPack.set_bool(lt::settings_pack::prefer_rc4, true);
1224     switch (encryption())
1225     {
1226     case 0: // Enabled
1227         settingsPack.set_int(lt::settings_pack::out_enc_policy, lt::settings_pack::pe_enabled);
1228         settingsPack.set_int(lt::settings_pack::in_enc_policy, lt::settings_pack::pe_enabled);
1229         break;
1230     case 1: // Forced
1231         settingsPack.set_int(lt::settings_pack::out_enc_policy, lt::settings_pack::pe_forced);
1232         settingsPack.set_int(lt::settings_pack::in_enc_policy, lt::settings_pack::pe_forced);
1233         break;
1234     default: // Disabled
1235         settingsPack.set_int(lt::settings_pack::out_enc_policy, lt::settings_pack::pe_disabled);
1236         settingsPack.set_int(lt::settings_pack::in_enc_policy, lt::settings_pack::pe_disabled);
1237     }
1238 
1239     // proxy
1240     const auto proxyManager = Net::ProxyConfigurationManager::instance();
1241     const Net::ProxyConfiguration proxyConfig = proxyManager->proxyConfiguration();
1242 
1243     switch (proxyConfig.type)
1244     {
1245     case Net::ProxyType::HTTP:
1246         settingsPack.set_int(lt::settings_pack::proxy_type, lt::settings_pack::http);
1247         break;
1248     case Net::ProxyType::HTTP_PW:
1249         settingsPack.set_int(lt::settings_pack::proxy_type, lt::settings_pack::http_pw);
1250         break;
1251     case Net::ProxyType::SOCKS4:
1252         settingsPack.set_int(lt::settings_pack::proxy_type, lt::settings_pack::socks4);
1253         break;
1254     case Net::ProxyType::SOCKS5:
1255         settingsPack.set_int(lt::settings_pack::proxy_type, lt::settings_pack::socks5);
1256         break;
1257     case Net::ProxyType::SOCKS5_PW:
1258         settingsPack.set_int(lt::settings_pack::proxy_type, lt::settings_pack::socks5_pw);
1259         break;
1260     case Net::ProxyType::None:
1261     default:
1262         settingsPack.set_int(lt::settings_pack::proxy_type, lt::settings_pack::none);
1263     }
1264 
1265     if (proxyConfig.type != Net::ProxyType::None)
1266     {
1267         settingsPack.set_str(lt::settings_pack::proxy_hostname, proxyConfig.ip.toStdString());
1268         settingsPack.set_int(lt::settings_pack::proxy_port, proxyConfig.port);
1269 
1270         if (proxyManager->isAuthenticationRequired())
1271         {
1272             settingsPack.set_str(lt::settings_pack::proxy_username, proxyConfig.username.toStdString());
1273             settingsPack.set_str(lt::settings_pack::proxy_password, proxyConfig.password.toStdString());
1274         }
1275 
1276         settingsPack.set_bool(lt::settings_pack::proxy_peer_connections, isProxyPeerConnectionsEnabled());
1277     }
1278 
1279     settingsPack.set_bool(lt::settings_pack::announce_to_all_trackers, announceToAllTrackers());
1280     settingsPack.set_bool(lt::settings_pack::announce_to_all_tiers, announceToAllTiers());
1281 
1282     settingsPack.set_int(lt::settings_pack::peer_turnover, peerTurnover());
1283     settingsPack.set_int(lt::settings_pack::peer_turnover_cutoff, peerTurnoverCutoff());
1284     settingsPack.set_int(lt::settings_pack::peer_turnover_interval, peerTurnoverInterval());
1285 
1286     settingsPack.set_int(lt::settings_pack::aio_threads, asyncIOThreads());
1287 #if (LIBTORRENT_VERSION_NUM >= 20000)
1288     settingsPack.set_int(lt::settings_pack::hashing_threads, hashingThreads());
1289 #endif
1290     settingsPack.set_int(lt::settings_pack::file_pool_size, filePoolSize());
1291 
1292     const int checkingMemUsageSize = checkingMemUsage() * 64;
1293     settingsPack.set_int(lt::settings_pack::checking_mem_usage, checkingMemUsageSize);
1294 
1295 #if (LIBTORRENT_VERSION_NUM < 20000)
1296     const int cacheSize = (diskCacheSize() > -1) ? (diskCacheSize() * 64) : -1;
1297     settingsPack.set_int(lt::settings_pack::cache_size, cacheSize);
1298     settingsPack.set_int(lt::settings_pack::cache_expiry, diskCacheTTL());
1299 #endif
1300 
1301     lt::settings_pack::io_buffer_mode_t mode = useOSCache() ? lt::settings_pack::enable_os_cache
1302                                                               : lt::settings_pack::disable_os_cache;
1303     settingsPack.set_int(lt::settings_pack::disk_io_read_mode, mode);
1304     settingsPack.set_int(lt::settings_pack::disk_io_write_mode, mode);
1305 
1306 #if (LIBTORRENT_VERSION_NUM < 20000)
1307     settingsPack.set_bool(lt::settings_pack::coalesce_reads, isCoalesceReadWriteEnabled());
1308     settingsPack.set_bool(lt::settings_pack::coalesce_writes, isCoalesceReadWriteEnabled());
1309 #endif
1310 
1311     settingsPack.set_bool(lt::settings_pack::piece_extent_affinity, usePieceExtentAffinity());
1312 
1313     settingsPack.set_int(lt::settings_pack::suggest_mode, isSuggestModeEnabled()
1314                          ? lt::settings_pack::suggest_read_cache : lt::settings_pack::no_piece_suggestions);
1315 
1316     settingsPack.set_int(lt::settings_pack::send_buffer_watermark, sendBufferWatermark() * 1024);
1317     settingsPack.set_int(lt::settings_pack::send_buffer_low_watermark, sendBufferLowWatermark() * 1024);
1318     settingsPack.set_int(lt::settings_pack::send_buffer_watermark_factor, sendBufferWatermarkFactor());
1319 
1320     settingsPack.set_bool(lt::settings_pack::anonymous_mode, isAnonymousModeEnabled());
1321 
1322     // Queueing System
1323     if (isQueueingSystemEnabled())
1324     {
1325         adjustLimits(settingsPack);
1326 
1327         settingsPack.set_int(lt::settings_pack::active_seeds, maxActiveUploads());
1328         settingsPack.set_bool(lt::settings_pack::dont_count_slow_torrents, ignoreSlowTorrentsForQueueing());
1329         settingsPack.set_int(lt::settings_pack::inactive_down_rate, downloadRateForSlowTorrents() * 1024); // KiB to Bytes
1330         settingsPack.set_int(lt::settings_pack::inactive_up_rate, uploadRateForSlowTorrents() * 1024); // KiB to Bytes
1331         settingsPack.set_int(lt::settings_pack::auto_manage_startup, slowTorrentsInactivityTimer());
1332     }
1333     else
1334     {
1335         settingsPack.set_int(lt::settings_pack::active_downloads, -1);
1336         settingsPack.set_int(lt::settings_pack::active_seeds, -1);
1337         settingsPack.set_int(lt::settings_pack::active_limit, -1);
1338     }
1339     settingsPack.set_int(lt::settings_pack::active_tracker_limit, -1);
1340     settingsPack.set_int(lt::settings_pack::active_dht_limit, -1);
1341     settingsPack.set_int(lt::settings_pack::active_lsd_limit, -1);
1342     settingsPack.set_int(lt::settings_pack::alert_queue_size, std::numeric_limits<int>::max() / 2);
1343 
1344     // Outgoing ports
1345     settingsPack.set_int(lt::settings_pack::outgoing_port, outgoingPortsMin());
1346     settingsPack.set_int(lt::settings_pack::num_outgoing_ports, outgoingPortsMax() - outgoingPortsMin() + 1);
1347     // UPnP lease duration
1348     settingsPack.set_int(lt::settings_pack::upnp_lease_duration, UPnPLeaseDuration());
1349     // Type of service
1350     settingsPack.set_int(lt::settings_pack::peer_tos, peerToS());
1351     // Include overhead in transfer limits
1352     settingsPack.set_bool(lt::settings_pack::rate_limit_ip_overhead, includeOverheadInLimits());
1353     // IP address to announce to trackers
1354     settingsPack.set_str(lt::settings_pack::announce_ip, announceIP().toStdString());
1355     // Max concurrent HTTP announces
1356     settingsPack.set_int(lt::settings_pack::max_concurrent_http_announces, maxConcurrentHTTPAnnounces());
1357     // Stop tracker timeout
1358     settingsPack.set_int(lt::settings_pack::stop_tracker_timeout, stopTrackerTimeout());
1359     // * Max connections limit
1360     settingsPack.set_int(lt::settings_pack::connections_limit, maxConnections());
1361     // * Global max upload slots
1362     settingsPack.set_int(lt::settings_pack::unchoke_slots_limit, maxUploads());
1363     // uTP
1364     switch (btProtocol())
1365     {
1366     case BTProtocol::Both:
1367     default:
1368         settingsPack.set_bool(lt::settings_pack::enable_incoming_tcp, true);
1369         settingsPack.set_bool(lt::settings_pack::enable_outgoing_tcp, true);
1370         settingsPack.set_bool(lt::settings_pack::enable_incoming_utp, true);
1371         settingsPack.set_bool(lt::settings_pack::enable_outgoing_utp, true);
1372         break;
1373 
1374     case BTProtocol::TCP:
1375         settingsPack.set_bool(lt::settings_pack::enable_incoming_tcp, true);
1376         settingsPack.set_bool(lt::settings_pack::enable_outgoing_tcp, true);
1377         settingsPack.set_bool(lt::settings_pack::enable_incoming_utp, false);
1378         settingsPack.set_bool(lt::settings_pack::enable_outgoing_utp, false);
1379         break;
1380 
1381     case BTProtocol::UTP:
1382         settingsPack.set_bool(lt::settings_pack::enable_incoming_tcp, false);
1383         settingsPack.set_bool(lt::settings_pack::enable_outgoing_tcp, false);
1384         settingsPack.set_bool(lt::settings_pack::enable_incoming_utp, true);
1385         settingsPack.set_bool(lt::settings_pack::enable_outgoing_utp, true);
1386         break;
1387     }
1388 
1389     switch (utpMixedMode())
1390     {
1391     case MixedModeAlgorithm::TCP:
1392     default:
1393         settingsPack.set_int(lt::settings_pack::mixed_mode_algorithm, lt::settings_pack::prefer_tcp);
1394         break;
1395     case MixedModeAlgorithm::Proportional:
1396         settingsPack.set_int(lt::settings_pack::mixed_mode_algorithm, lt::settings_pack::peer_proportional);
1397         break;
1398     }
1399 
1400     settingsPack.set_bool(lt::settings_pack::allow_idna, isIDNSupportEnabled());
1401 
1402     settingsPack.set_bool(lt::settings_pack::allow_multiple_connections_per_ip, multiConnectionsPerIpEnabled());
1403 
1404     settingsPack.set_bool(lt::settings_pack::validate_https_trackers, validateHTTPSTrackerCertificate());
1405 
1406     settingsPack.set_bool(lt::settings_pack::ssrf_mitigation, isSSRFMitigationEnabled());
1407 
1408     settingsPack.set_bool(lt::settings_pack::no_connect_privileged_ports, blockPeersOnPrivilegedPorts());
1409 
1410     settingsPack.set_bool(lt::settings_pack::apply_ip_filter_to_trackers, isTrackerFilteringEnabled());
1411 
1412     settingsPack.set_bool(lt::settings_pack::enable_dht, isDHTEnabled());
1413     if (isDHTEnabled())
1414         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");
1415     settingsPack.set_bool(lt::settings_pack::enable_lsd, isLSDEnabled());
1416 
1417     switch (chokingAlgorithm())
1418     {
1419     case ChokingAlgorithm::FixedSlots:
1420     default:
1421         settingsPack.set_int(lt::settings_pack::choking_algorithm, lt::settings_pack::fixed_slots_choker);
1422         break;
1423     case ChokingAlgorithm::RateBased:
1424         settingsPack.set_int(lt::settings_pack::choking_algorithm, lt::settings_pack::rate_based_choker);
1425         break;
1426     }
1427 
1428     switch (seedChokingAlgorithm())
1429     {
1430     case SeedChokingAlgorithm::RoundRobin:
1431         settingsPack.set_int(lt::settings_pack::seed_choking_algorithm, lt::settings_pack::round_robin);
1432         break;
1433     case SeedChokingAlgorithm::FastestUpload:
1434     default:
1435         settingsPack.set_int(lt::settings_pack::seed_choking_algorithm, lt::settings_pack::fastest_upload);
1436         break;
1437     case SeedChokingAlgorithm::AntiLeech:
1438         settingsPack.set_int(lt::settings_pack::seed_choking_algorithm, lt::settings_pack::anti_leech);
1439         break;
1440     }
1441 }
1442 
configureNetworkInterfaces(lt::settings_pack & settingsPack)1443 void Session::configureNetworkInterfaces(lt::settings_pack &settingsPack)
1444 {
1445     if (m_listenInterfaceConfigured)
1446         return;
1447 
1448     const int port = useRandomPort() ? 0 : this->port();
1449     if (port > 0)  // user specified port
1450         settingsPack.set_int(lt::settings_pack::max_retry_port_bind, 0);
1451 
1452     QStringList endpoints;
1453     QStringList outgoingInterfaces;
1454     const QString portString = ':' + QString::number(port);
1455 
1456     for (const QString &ip : asConst(getListeningIPs()))
1457     {
1458         const QHostAddress addr {ip};
1459         if (!addr.isNull())
1460         {
1461             const bool isIPv6 = (addr.protocol() == QAbstractSocket::IPv6Protocol);
1462             const QString ip = isIPv6
1463                           ? Utils::Net::canonicalIPv6Addr(addr).toString()
1464                           : addr.toString();
1465 
1466             endpoints << ((isIPv6 ? ('[' + ip + ']') : ip) + portString);
1467 
1468             if ((ip != QLatin1String("0.0.0.0")) && (ip != QLatin1String("::")))
1469                 outgoingInterfaces << ip;
1470         }
1471         else
1472         {
1473             // ip holds an interface name
1474 #ifdef Q_OS_WIN
1475             // On Vista+ versions and after Qt 5.5 QNetworkInterface::name() returns
1476             // the interface's LUID and not the GUID.
1477             // Libtorrent expects GUIDs for the 'listen_interfaces' setting.
1478             const QString guid = convertIfaceNameToGuid(ip);
1479             if (!guid.isEmpty())
1480             {
1481                 endpoints << (guid + portString);
1482                 outgoingInterfaces << guid;
1483             }
1484             else
1485             {
1486                 LogMsg(tr("Could not get GUID of network interface: %1").arg(ip) , Log::WARNING);
1487                 // Since we can't get the GUID, we'll pass the interface name instead.
1488                 // Otherwise an empty string will be passed to outgoing_interface which will cause IP leak.
1489                 endpoints << (ip + portString);
1490                 outgoingInterfaces << ip;
1491             }
1492 #else
1493             endpoints << (ip + portString);
1494             outgoingInterfaces << ip;
1495 #endif
1496         }
1497     }
1498 
1499     const QString finalEndpoints = endpoints.join(',');
1500     settingsPack.set_str(lt::settings_pack::listen_interfaces, finalEndpoints.toStdString());
1501     LogMsg(tr("Trying to listen on: %1", "e.g: Trying to listen on: 192.168.0.1:6881")
1502            .arg(finalEndpoints), Log::INFO);
1503 
1504     settingsPack.set_str(lt::settings_pack::outgoing_interfaces, outgoingInterfaces.join(',').toStdString());
1505     m_listenInterfaceConfigured = true;
1506 }
1507 
configurePeerClasses()1508 void Session::configurePeerClasses()
1509 {
1510     lt::ip_filter f;
1511     // lt::make_address("255.255.255.255") crashes on some people's systems
1512     // so instead we use address_v4::broadcast()
1513     // Proactively do the same for 0.0.0.0 and address_v4::any()
1514     f.add_rule(lt::address_v4::any()
1515                , lt::address_v4::broadcast()
1516                , 1 << static_cast<LTUnderlyingType<lt::peer_class_t>>(lt::session::global_peer_class_id));
1517 
1518     // IPv6 may not be available on OS and the parsing
1519     // would result in an exception -> abnormal program termination
1520     // Affects Windows XP
1521     try
1522     {
1523         f.add_rule(lt::address_v6::any()
1524                    , lt::make_address("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
1525                    , 1 << static_cast<LTUnderlyingType<lt::peer_class_t>>(lt::session::global_peer_class_id));
1526     }
1527     catch (const std::exception &) {}
1528 
1529     if (ignoreLimitsOnLAN())
1530     {
1531         // local networks
1532         f.add_rule(lt::make_address("10.0.0.0")
1533                    , lt::make_address("10.255.255.255")
1534                    , 1 << static_cast<LTUnderlyingType<lt::peer_class_t>>(lt::session::local_peer_class_id));
1535         f.add_rule(lt::make_address("172.16.0.0")
1536                    , lt::make_address("172.31.255.255")
1537                    , 1 << static_cast<LTUnderlyingType<lt::peer_class_t>>(lt::session::local_peer_class_id));
1538         f.add_rule(lt::make_address("192.168.0.0")
1539                    , lt::make_address("192.168.255.255")
1540                    , 1 << static_cast<LTUnderlyingType<lt::peer_class_t>>(lt::session::local_peer_class_id));
1541         // link local
1542         f.add_rule(lt::make_address("169.254.0.0")
1543                    , lt::make_address("169.254.255.255")
1544                    , 1 << static_cast<LTUnderlyingType<lt::peer_class_t>>(lt::session::local_peer_class_id));
1545         // loopback
1546         f.add_rule(lt::make_address("127.0.0.0")
1547                    , lt::make_address("127.255.255.255")
1548                    , 1 << static_cast<LTUnderlyingType<lt::peer_class_t>>(lt::session::local_peer_class_id));
1549 
1550         // IPv6 may not be available on OS and the parsing
1551         // would result in an exception -> abnormal program termination
1552         // Affects Windows XP
1553         try
1554         {
1555             // link local
1556             f.add_rule(lt::make_address("fe80::")
1557                        , lt::make_address("febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
1558                        , 1 << static_cast<LTUnderlyingType<lt::peer_class_t>>(lt::session::local_peer_class_id));
1559             // unique local addresses
1560             f.add_rule(lt::make_address("fc00::")
1561                        , lt::make_address("fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
1562                        , 1 << static_cast<LTUnderlyingType<lt::peer_class_t>>(lt::session::local_peer_class_id));
1563             // loopback
1564             f.add_rule(lt::address_v6::loopback()
1565                        , lt::address_v6::loopback()
1566                        , 1 << static_cast<LTUnderlyingType<lt::peer_class_t>>(lt::session::local_peer_class_id));
1567         }
1568         catch (const std::exception &) {}
1569     }
1570     m_nativeSession->set_peer_class_filter(f);
1571 
1572     lt::peer_class_type_filter peerClassTypeFilter;
1573     peerClassTypeFilter.add(lt::peer_class_type_filter::tcp_socket, lt::session::tcp_peer_class_id);
1574     peerClassTypeFilter.add(lt::peer_class_type_filter::ssl_tcp_socket, lt::session::tcp_peer_class_id);
1575     peerClassTypeFilter.add(lt::peer_class_type_filter::i2p_socket, lt::session::tcp_peer_class_id);
1576     if (!isUTPRateLimited())
1577     {
1578         peerClassTypeFilter.disallow(lt::peer_class_type_filter::utp_socket
1579             , lt::session::global_peer_class_id);
1580         peerClassTypeFilter.disallow(lt::peer_class_type_filter::ssl_utp_socket
1581             , lt::session::global_peer_class_id);
1582     }
1583     m_nativeSession->set_peer_class_type_filter(peerClassTypeFilter);
1584 }
1585 
enableTracker(const bool enable)1586 void Session::enableTracker(const bool enable)
1587 {
1588     if (enable)
1589     {
1590         if (!m_tracker)
1591             m_tracker = new Tracker(this);
1592 
1593         m_tracker->start();
1594     }
1595     else
1596     {
1597         delete m_tracker;
1598     }
1599 }
1600 
enableBandwidthScheduler()1601 void Session::enableBandwidthScheduler()
1602 {
1603     if (!m_bwScheduler)
1604     {
1605         m_bwScheduler = new BandwidthScheduler(this);
1606         connect(m_bwScheduler.data(), &BandwidthScheduler::bandwidthLimitRequested
1607                 , this, &Session::setAltGlobalSpeedLimitEnabled);
1608     }
1609     m_bwScheduler->start();
1610 }
1611 
populateAdditionalTrackers()1612 void Session::populateAdditionalTrackers()
1613 {
1614     m_additionalTrackerList.clear();
1615 
1616     const QString trackers = additionalTrackers();
1617     for (QStringRef tracker : asConst(trackers.splitRef('\n')))
1618     {
1619         tracker = tracker.trimmed();
1620         if (!tracker.isEmpty())
1621             m_additionalTrackerList.append({tracker.toString()});
1622     }
1623 }
1624 
processShareLimits()1625 void Session::processShareLimits()
1626 {
1627     qDebug("Processing share limits...");
1628 
1629     // We shouldn't iterate over `m_torrents` in the loop below
1630     // since `deleteTorrent()` modifies it indirectly
1631     const QHash<TorrentID, TorrentImpl *> torrents {m_torrents};
1632     for (TorrentImpl *const torrent : torrents)
1633     {
1634         if (torrent->isSeed() && !torrent->isForced())
1635         {
1636             if (torrent->ratioLimit() != Torrent::NO_RATIO_LIMIT)
1637             {
1638                 const qreal ratio = torrent->realRatio();
1639                 qreal ratioLimit = torrent->ratioLimit();
1640                 if (ratioLimit == Torrent::USE_GLOBAL_RATIO)
1641                     // If Global Max Ratio is really set...
1642                     ratioLimit = globalMaxRatio();
1643 
1644                 if (ratioLimit >= 0)
1645                 {
1646                     qDebug("Ratio: %f (limit: %f)", ratio, ratioLimit);
1647 
1648                     if ((ratio <= Torrent::MAX_RATIO) && (ratio >= ratioLimit))
1649                     {
1650                         if (m_maxRatioAction == Remove)
1651                         {
1652                             LogMsg(tr("'%1' reached the maximum ratio you set. Removed.").arg(torrent->name()));
1653                             deleteTorrent(torrent->id());
1654                         }
1655                         else if (m_maxRatioAction == DeleteFiles)
1656                         {
1657                             LogMsg(tr("'%1' reached the maximum ratio you set. Removed torrent and its files.").arg(torrent->name()));
1658                             deleteTorrent(torrent->id(), DeleteTorrentAndFiles);
1659                         }
1660                         else if ((m_maxRatioAction == Pause) && !torrent->isPaused())
1661                         {
1662                             torrent->pause();
1663                             LogMsg(tr("'%1' reached the maximum ratio you set. Paused.").arg(torrent->name()));
1664                         }
1665                         else if ((m_maxRatioAction == EnableSuperSeeding) && !torrent->isPaused() && !torrent->superSeeding())
1666                         {
1667                             torrent->setSuperSeeding(true);
1668                             LogMsg(tr("'%1' reached the maximum ratio you set. Enabled super seeding for it.").arg(torrent->name()));
1669                         }
1670                         continue;
1671                     }
1672                 }
1673             }
1674 
1675             if (torrent->seedingTimeLimit() != Torrent::NO_SEEDING_TIME_LIMIT)
1676             {
1677                 const qlonglong seedingTimeInMinutes = torrent->seedingTime() / 60;
1678                 int seedingTimeLimit = torrent->seedingTimeLimit();
1679                 if (seedingTimeLimit == Torrent::USE_GLOBAL_SEEDING_TIME)
1680                 {
1681                      // If Global Seeding Time Limit is really set...
1682                     seedingTimeLimit = globalMaxSeedingMinutes();
1683                 }
1684 
1685                 if (seedingTimeLimit >= 0)
1686                 {
1687                     if ((seedingTimeInMinutes <= Torrent::MAX_SEEDING_TIME) && (seedingTimeInMinutes >= seedingTimeLimit))
1688                     {
1689                         if (m_maxRatioAction == Remove)
1690                         {
1691                             LogMsg(tr("'%1' reached the maximum seeding time you set. Removed.").arg(torrent->name()));
1692                             deleteTorrent(torrent->id());
1693                         }
1694                         else if (m_maxRatioAction == DeleteFiles)
1695                         {
1696                             LogMsg(tr("'%1' reached the maximum seeding time you set. Removed torrent and its files.").arg(torrent->name()));
1697                             deleteTorrent(torrent->id(), DeleteTorrentAndFiles);
1698                         }
1699                         else if ((m_maxRatioAction == Pause) && !torrent->isPaused())
1700                         {
1701                             torrent->pause();
1702                             LogMsg(tr("'%1' reached the maximum seeding time you set. Paused.").arg(torrent->name()));
1703                         }
1704                         else if ((m_maxRatioAction == EnableSuperSeeding) && !torrent->isPaused() && !torrent->superSeeding())
1705                         {
1706                             torrent->setSuperSeeding(true);
1707                             LogMsg(tr("'%1' reached the maximum seeding time you set. Enabled super seeding for it.").arg(torrent->name()));
1708                         }
1709                     }
1710                 }
1711             }
1712         }
1713     }
1714 }
1715 
1716 // Add to BitTorrent session the downloaded torrent file
handleDownloadFinished(const Net::DownloadResult & result)1717 void Session::handleDownloadFinished(const Net::DownloadResult &result)
1718 {
1719     switch (result.status)
1720     {
1721     case Net::DownloadStatus::Success:
1722         emit downloadFromUrlFinished(result.url);
1723         addTorrent(TorrentInfo::load(result.data), m_downloadedTorrents.take(result.url));
1724         break;
1725     case Net::DownloadStatus::RedirectedToMagnet:
1726         emit downloadFromUrlFinished(result.url);
1727         addTorrent(MagnetUri {result.magnet}, m_downloadedTorrents.take(result.url));
1728         break;
1729     default:
1730         emit downloadFromUrlFailed(result.url, result.errorString);
1731     }
1732 }
1733 
fileSearchFinished(const TorrentID & id,const QString & savePath,const QStringList & fileNames)1734 void Session::fileSearchFinished(const TorrentID &id, const QString &savePath, const QStringList &fileNames)
1735 {
1736     TorrentImpl *torrent = m_torrents.value(id);
1737     if (torrent)
1738     {
1739         torrent->fileSearchFinished(savePath, fileNames);
1740         return;
1741     }
1742 
1743     const auto loadingTorrentsIter = m_loadingTorrents.find(id);
1744     if (loadingTorrentsIter != m_loadingTorrents.end())
1745     {
1746         LoadTorrentParams params = loadingTorrentsIter.value();
1747         m_loadingTorrents.erase(loadingTorrentsIter);
1748 
1749         lt::add_torrent_params &p = params.ltAddTorrentParams;
1750 
1751         p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
1752         for (int i = 0; i < fileNames.size(); ++i)
1753             p.renamed_files[lt::file_index_t {i}] = fileNames[i].toStdString();
1754 
1755         loadTorrent(params);
1756     }
1757 }
1758 
1759 // Return the torrent handle, given its hash
findTorrent(const TorrentID & id) const1760 Torrent *Session::findTorrent(const TorrentID &id) const
1761 {
1762     return m_torrents.value(id);
1763 }
1764 
hasActiveTorrents() const1765 bool Session::hasActiveTorrents() const
1766 {
1767     return std::any_of(m_torrents.begin(), m_torrents.end(), [](TorrentImpl *torrent)
1768     {
1769         return TorrentFilter::ActiveTorrent.match(torrent);
1770     });
1771 }
1772 
hasUnfinishedTorrents() const1773 bool Session::hasUnfinishedTorrents() const
1774 {
1775     return std::any_of(m_torrents.begin(), m_torrents.end(), [](const TorrentImpl *torrent)
1776     {
1777         return (!torrent->isSeed() && !torrent->isPaused() && !torrent->isErrored());
1778     });
1779 }
1780 
hasRunningSeed() const1781 bool Session::hasRunningSeed() const
1782 {
1783     return std::any_of(m_torrents.begin(), m_torrents.end(), [](const TorrentImpl *torrent)
1784     {
1785         return (torrent->isSeed() && !torrent->isPaused());
1786     });
1787 }
1788 
banIP(const QString & ip)1789 void Session::banIP(const QString &ip)
1790 {
1791     QStringList bannedIPs = m_bannedIPs;
1792     if (!bannedIPs.contains(ip))
1793     {
1794         lt::ip_filter filter = m_nativeSession->get_ip_filter();
1795         lt::error_code ec;
1796         const lt::address addr = lt::make_address(ip.toLatin1().constData(), ec);
1797         Q_ASSERT(!ec);
1798         if (ec) return;
1799         filter.add_rule(addr, addr, lt::ip_filter::blocked);
1800         m_nativeSession->set_ip_filter(filter);
1801 
1802         bannedIPs << ip;
1803         bannedIPs.sort();
1804         m_bannedIPs = bannedIPs;
1805     }
1806 }
1807 
1808 // Delete a torrent from the session, given its hash
1809 // and from the disk, if the corresponding deleteOption is chosen
deleteTorrent(const TorrentID & id,const DeleteOption deleteOption)1810 bool Session::deleteTorrent(const TorrentID &id, const DeleteOption deleteOption)
1811 {
1812     TorrentImpl *const torrent = m_torrents.take(id);
1813     if (!torrent) return false;
1814 
1815     qDebug("Deleting torrent with ID: %s", qUtf8Printable(torrent->id().toString()));
1816     emit torrentAboutToBeRemoved(torrent);
1817 
1818     // Remove it from session
1819     if (deleteOption == DeleteTorrent)
1820     {
1821         m_removingTorrents[torrent->id()] = {torrent->name(), "", deleteOption};
1822 
1823         const lt::torrent_handle nativeHandle {torrent->nativeHandle()};
1824         const auto iter = std::find_if(m_moveStorageQueue.begin(), m_moveStorageQueue.end()
1825                                  , [&nativeHandle](const MoveStorageJob &job)
1826         {
1827             return job.torrentHandle == nativeHandle;
1828         });
1829         if (iter != m_moveStorageQueue.end())
1830         {
1831             // We shouldn't actually remove torrent until existing "move storage jobs" are done
1832             torrentQueuePositionBottom(nativeHandle);
1833             nativeHandle.unset_flags(lt::torrent_flags::auto_managed);
1834             nativeHandle.pause();
1835         }
1836         else
1837         {
1838             m_nativeSession->remove_torrent(nativeHandle, lt::session::delete_partfile);
1839         }
1840     }
1841     else
1842     {
1843         QString rootPath = torrent->rootPath(true);
1844         if (!rootPath.isEmpty() && torrent->useTempPath())
1845         {
1846             // torrent without root folder still has it in its temporary save path
1847             rootPath = torrent->actualStorageLocation();
1848         }
1849 
1850         m_removingTorrents[torrent->id()] = {torrent->name(), rootPath, deleteOption};
1851 
1852         if (m_moveStorageQueue.size() > 1)
1853         {
1854             // Delete "move storage job" for the deleted torrent
1855             // (note: we shouldn't delete active job)
1856             const auto iter = std::find_if(m_moveStorageQueue.begin() + 1, m_moveStorageQueue.end()
1857                                      , [torrent](const MoveStorageJob &job)
1858             {
1859                 return job.torrentHandle == torrent->nativeHandle();
1860             });
1861             if (iter != m_moveStorageQueue.end())
1862                 m_moveStorageQueue.erase(iter);
1863         }
1864 
1865         m_nativeSession->remove_torrent(torrent->nativeHandle(), lt::session::delete_files);
1866     }
1867 
1868     // Remove it from torrent resume directory
1869     const QString resumedataFile = QString::fromLatin1("%1.fastresume").arg(torrent->id().toString());
1870     const QString metadataFile = QString::fromLatin1("%1.torrent").arg(torrent->id().toString());
1871     QMetaObject::invokeMethod(m_resumeDataSavingManager, [this, resumedataFile, metadataFile]()
1872     {
1873         m_resumeDataSavingManager->remove(resumedataFile);
1874         m_resumeDataSavingManager->remove(metadataFile);
1875     });
1876 
1877     delete torrent;
1878     return true;
1879 }
1880 
cancelDownloadMetadata(const TorrentID & id)1881 bool Session::cancelDownloadMetadata(const TorrentID &id)
1882 {
1883     const auto downloadedMetadataIter = m_downloadedMetadata.find(id);
1884     if (downloadedMetadataIter == m_downloadedMetadata.end()) return false;
1885 
1886     m_downloadedMetadata.erase(downloadedMetadataIter);
1887     --m_extraLimit;
1888     adjustLimits();
1889     m_nativeSession->remove_torrent(m_nativeSession->find_torrent(id), lt::session::delete_files);
1890     return true;
1891 }
1892 
increaseTorrentsQueuePos(const QVector<TorrentID> & ids)1893 void Session::increaseTorrentsQueuePos(const QVector<TorrentID> &ids)
1894 {
1895     using ElementType = std::pair<int, const TorrentImpl *>;
1896     std::priority_queue<ElementType
1897         , std::vector<ElementType>
1898         , std::greater<ElementType>> torrentQueue;
1899 
1900     // Sort torrents by queue position
1901     for (const TorrentID &id : ids)
1902     {
1903         const TorrentImpl *torrent = m_torrents.value(id);
1904         if (!torrent) continue;
1905         if (const int position = torrent->queuePosition(); position >= 0)
1906             torrentQueue.emplace(position, torrent);
1907     }
1908 
1909     // Increase torrents queue position (starting with the one in the highest queue position)
1910     while (!torrentQueue.empty())
1911     {
1912         const TorrentImpl *torrent = torrentQueue.top().second;
1913         torrentQueuePositionUp(torrent->nativeHandle());
1914         torrentQueue.pop();
1915     }
1916 
1917     saveTorrentsQueue();
1918 }
1919 
decreaseTorrentsQueuePos(const QVector<TorrentID> & ids)1920 void Session::decreaseTorrentsQueuePos(const QVector<TorrentID> &ids)
1921 {
1922     using ElementType = std::pair<int, const TorrentImpl *>;
1923     std::priority_queue<ElementType> torrentQueue;
1924 
1925     // Sort torrents by queue position
1926     for (const TorrentID &id : ids)
1927     {
1928         const TorrentImpl *torrent = m_torrents.value(id);
1929         if (!torrent) continue;
1930         if (const int position = torrent->queuePosition(); position >= 0)
1931             torrentQueue.emplace(position, torrent);
1932     }
1933 
1934     // Decrease torrents queue position (starting with the one in the lowest queue position)
1935     while (!torrentQueue.empty())
1936     {
1937         const TorrentImpl *torrent = torrentQueue.top().second;
1938         torrentQueuePositionDown(torrent->nativeHandle());
1939         torrentQueue.pop();
1940     }
1941 
1942     for (auto i = m_downloadedMetadata.cbegin(); i != m_downloadedMetadata.cend(); ++i)
1943         torrentQueuePositionBottom(m_nativeSession->find_torrent(*i));
1944 
1945     saveTorrentsQueue();
1946 }
1947 
topTorrentsQueuePos(const QVector<TorrentID> & ids)1948 void Session::topTorrentsQueuePos(const QVector<TorrentID> &ids)
1949 {
1950     using ElementType = std::pair<int, const TorrentImpl *>;
1951     std::priority_queue<ElementType> torrentQueue;
1952 
1953     // Sort torrents by queue position
1954     for (const TorrentID &id : ids)
1955     {
1956         const TorrentImpl *torrent = m_torrents.value(id);
1957         if (!torrent) continue;
1958         if (const int position = torrent->queuePosition(); position >= 0)
1959             torrentQueue.emplace(position, torrent);
1960     }
1961 
1962     // Top torrents queue position (starting with the one in the lowest queue position)
1963     while (!torrentQueue.empty())
1964     {
1965         const TorrentImpl *torrent = torrentQueue.top().second;
1966         torrentQueuePositionTop(torrent->nativeHandle());
1967         torrentQueue.pop();
1968     }
1969 
1970     saveTorrentsQueue();
1971 }
1972 
bottomTorrentsQueuePos(const QVector<TorrentID> & ids)1973 void Session::bottomTorrentsQueuePos(const QVector<TorrentID> &ids)
1974 {
1975     using ElementType = std::pair<int, const TorrentImpl *>;
1976     std::priority_queue<ElementType
1977         , std::vector<ElementType>
1978         , std::greater<ElementType>> torrentQueue;
1979 
1980     // Sort torrents by queue position
1981     for (const TorrentID &id : ids)
1982     {
1983         const TorrentImpl *torrent = m_torrents.value(id);
1984         if (!torrent) continue;
1985         if (const int position = torrent->queuePosition(); position >= 0)
1986             torrentQueue.emplace(position, torrent);
1987     }
1988 
1989     // Bottom torrents queue position (starting with the one in the highest queue position)
1990     while (!torrentQueue.empty())
1991     {
1992         const TorrentImpl *torrent = torrentQueue.top().second;
1993         torrentQueuePositionBottom(torrent->nativeHandle());
1994         torrentQueue.pop();
1995     }
1996 
1997     for (auto i = m_downloadedMetadata.cbegin(); i != m_downloadedMetadata.cend(); ++i)
1998         torrentQueuePositionBottom(m_nativeSession->find_torrent(*i));
1999 
2000     saveTorrentsQueue();
2001 }
2002 
handleTorrentNeedSaveResumeData(const TorrentImpl * torrent)2003 void Session::handleTorrentNeedSaveResumeData(const TorrentImpl *torrent)
2004 {
2005     if (m_needSaveResumeDataTorrents.empty())
2006     {
2007         QMetaObject::invokeMethod(this, [this]()
2008         {
2009             for (const TorrentID &torrentID : asConst(m_needSaveResumeDataTorrents))
2010             {
2011                 TorrentImpl *torrent = m_torrents.value(torrentID);
2012                 if (torrent)
2013                     torrent->saveResumeData();
2014             }
2015             m_needSaveResumeDataTorrents.clear();
2016         }, Qt::QueuedConnection);
2017     }
2018 
2019     m_needSaveResumeDataTorrents.insert(torrent->id());
2020 }
2021 
handleTorrentSaveResumeDataRequested(const TorrentImpl * torrent)2022 void Session::handleTorrentSaveResumeDataRequested(const TorrentImpl *torrent)
2023 {
2024     qDebug("Saving resume data is requested for torrent '%s'...", qUtf8Printable(torrent->name()));
2025     ++m_numResumeData;
2026 }
2027 
torrents() const2028 QVector<Torrent *> Session::torrents() const
2029 {
2030     QVector<Torrent *> result;
2031     result.reserve(m_torrents.size());
2032     for (TorrentImpl *torrent : asConst(m_torrents))
2033         result << torrent;
2034 
2035     return result;
2036 }
2037 
addTorrent(const QString & source,const AddTorrentParams & params)2038 bool Session::addTorrent(const QString &source, const AddTorrentParams &params)
2039 {
2040     // `source`: .torrent file path/url or magnet uri
2041 
2042     if (Net::DownloadManager::hasSupportedScheme(source))
2043     {
2044         LogMsg(tr("Downloading '%1', please wait...", "e.g: Downloading 'xxx.torrent', please wait...").arg(source));
2045         // Launch downloader
2046         Net::DownloadManager::instance()->download(Net::DownloadRequest(source).limit(MAX_TORRENT_SIZE)
2047                                                    , this, &Session::handleDownloadFinished);
2048         m_downloadedTorrents[source] = params;
2049         return true;
2050     }
2051 
2052     const MagnetUri magnetUri {source};
2053     if (magnetUri.isValid())
2054         return addTorrent(magnetUri, params);
2055 
2056     TorrentFileGuard guard {source};
2057     if (addTorrent(TorrentInfo::loadFromFile(source), params))
2058     {
2059         guard.markAsAddedToSession();
2060         return true;
2061     }
2062 
2063     return false;
2064 }
2065 
addTorrent(const MagnetUri & magnetUri,const AddTorrentParams & params)2066 bool Session::addTorrent(const MagnetUri &magnetUri, const AddTorrentParams &params)
2067 {
2068     if (!magnetUri.isValid()) return false;
2069 
2070     return addTorrent_impl(magnetUri, params);
2071 }
2072 
addTorrent(const TorrentInfo & torrentInfo,const AddTorrentParams & params)2073 bool Session::addTorrent(const TorrentInfo &torrentInfo, const AddTorrentParams &params)
2074 {
2075     if (!torrentInfo.isValid()) return false;
2076 
2077     return addTorrent_impl(torrentInfo, params);
2078 }
2079 
initLoadTorrentParams(const AddTorrentParams & addTorrentParams)2080 LoadTorrentParams Session::initLoadTorrentParams(const AddTorrentParams &addTorrentParams)
2081 {
2082     LoadTorrentParams loadTorrentParams;
2083 
2084     loadTorrentParams.name = addTorrentParams.name;
2085     loadTorrentParams.firstLastPiecePriority = addTorrentParams.firstLastPiecePriority;
2086     loadTorrentParams.hasSeedStatus = addTorrentParams.skipChecking; // do not react on 'torrent_finished_alert' when skipping
2087     loadTorrentParams.contentLayout = addTorrentParams.contentLayout.value_or(torrentContentLayout());
2088     loadTorrentParams.forced = addTorrentParams.addForced;
2089     loadTorrentParams.paused = addTorrentParams.addPaused.value_or(isAddTorrentPaused());
2090     loadTorrentParams.ratioLimit = addTorrentParams.ratioLimit;
2091     loadTorrentParams.seedingTimeLimit = addTorrentParams.seedingTimeLimit;
2092 
2093     const bool useAutoTMM = addTorrentParams.useAutoTMM.value_or(!isAutoTMMDisabledByDefault());
2094     if (useAutoTMM)
2095         loadTorrentParams.savePath = "";
2096     else if (addTorrentParams.savePath.isEmpty())
2097         loadTorrentParams.savePath = defaultSavePath();
2098     else if (QDir(addTorrentParams.savePath).isRelative())
2099         loadTorrentParams.savePath = QDir(defaultSavePath()).absoluteFilePath(addTorrentParams.savePath);
2100     else
2101         loadTorrentParams.savePath = normalizePath(addTorrentParams.savePath);
2102 
2103     const QString category = addTorrentParams.category;
2104     if (!category.isEmpty() && !m_categories.contains(category) && !addCategory(category))
2105         loadTorrentParams.category = "";
2106     else
2107         loadTorrentParams.category = addTorrentParams.category;
2108 
2109     for (const QString &tag : addTorrentParams.tags)
2110     {
2111         if (hasTag(tag) || addTag(tag))
2112             loadTorrentParams.tags.insert(tag);
2113     }
2114 
2115     return loadTorrentParams;
2116 }
2117 
2118 // Add a torrent to the BitTorrent session
addTorrent_impl(const std::variant<MagnetUri,TorrentInfo> & source,const AddTorrentParams & addTorrentParams)2119 bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source, const AddTorrentParams &addTorrentParams)
2120 {
2121     const bool hasMetadata = std::holds_alternative<TorrentInfo>(source);
2122     TorrentInfo metadata = (hasMetadata ? std::get<TorrentInfo>(source) : TorrentInfo {});
2123     const MagnetUri &magnetUri = (hasMetadata ? MagnetUri {} : std::get<MagnetUri>(source));
2124     const auto id = TorrentID::fromInfoHash(hasMetadata ? metadata.infoHash() : magnetUri.infoHash());
2125 
2126     // It looks illogical that we don't just use an existing handle,
2127     // but as previous experience has shown, it actually creates unnecessary
2128     // problems and unwanted behavior due to the fact that it was originally
2129     // added with parameters other than those provided by the user.
2130     cancelDownloadMetadata(id);
2131 
2132     // We should not add the torrent if it is already
2133     // processed or is pending to add to session
2134     if (m_loadingTorrents.contains(id))
2135         return false;
2136 
2137     TorrentImpl *const torrent = m_torrents.value(id);
2138     if (torrent)
2139     {  // a duplicate torrent is added
2140         if (torrent->isPrivate() || (hasMetadata && metadata.isPrivate()))
2141             return false;
2142 
2143         // merge trackers and web seeds
2144         torrent->addTrackers(hasMetadata ? metadata.trackers() : magnetUri.trackers());
2145         torrent->addUrlSeeds(hasMetadata ? metadata.urlSeeds() : magnetUri.urlSeeds());
2146         return true;
2147     }
2148 
2149     LoadTorrentParams loadTorrentParams = initLoadTorrentParams(addTorrentParams);
2150     lt::add_torrent_params &p = loadTorrentParams.ltAddTorrentParams;
2151 
2152     bool isFindingIncompleteFiles = false;
2153 
2154     // If empty then Automatic mode, otherwise Manual mode
2155     const QString actualSavePath = loadTorrentParams.savePath.isEmpty() ? categorySavePath(loadTorrentParams.category) : loadTorrentParams.savePath;
2156     if (hasMetadata)
2157     {
2158         metadata.setContentLayout(loadTorrentParams.contentLayout);
2159 
2160         if (!loadTorrentParams.hasSeedStatus)
2161         {
2162             findIncompleteFiles(metadata, actualSavePath);
2163             isFindingIncompleteFiles = true;
2164         }
2165 
2166         // if torrent name wasn't explicitly set we handle the case of
2167         // initial renaming of torrent content and rename torrent accordingly
2168         if (loadTorrentParams.name.isEmpty())
2169         {
2170             QString contentName = metadata.rootFolder();
2171             if (contentName.isEmpty() && (metadata.filesCount() == 1))
2172                 contentName = metadata.fileName(0);
2173 
2174             if (!contentName.isEmpty() && (contentName != metadata.name()))
2175                 loadTorrentParams.name = contentName;
2176         }
2177 
2178         Q_ASSERT(p.file_priorities.empty());
2179         std::transform(addTorrentParams.filePriorities.cbegin(), addTorrentParams.filePriorities.cend()
2180                        , std::back_inserter(p.file_priorities), [](const DownloadPriority priority)
2181         {
2182             return static_cast<lt::download_priority_t>(
2183                         static_cast<lt::download_priority_t::underlying_type>(priority));
2184         });
2185 
2186         p.ti = metadata.nativeInfo();
2187     }
2188     else
2189     {
2190         p = magnetUri.addTorrentParams();
2191 
2192         if (loadTorrentParams.name.isEmpty() && !p.name.empty())
2193             loadTorrentParams.name = QString::fromStdString(p.name);
2194     }
2195 
2196     p.save_path = Utils::Fs::toNativePath(actualSavePath).toStdString();
2197 
2198     p.upload_limit = addTorrentParams.uploadLimit;
2199     p.download_limit = addTorrentParams.downloadLimit;
2200 
2201     // Preallocation mode
2202     p.storage_mode = isPreallocationEnabled() ? lt::storage_mode_allocate : lt::storage_mode_sparse;
2203 
2204     if (addTorrentParams.sequential)
2205         p.flags |= lt::torrent_flags::sequential_download;
2206     else
2207         p.flags &= ~lt::torrent_flags::sequential_download;
2208 
2209     // Seeding mode
2210     // Skip checking and directly start seeding
2211     if (addTorrentParams.skipChecking)
2212         p.flags |= lt::torrent_flags::seed_mode;
2213     else
2214         p.flags &= ~lt::torrent_flags::seed_mode;
2215 
2216     if (loadTorrentParams.paused || !loadTorrentParams.forced)
2217         p.flags |= lt::torrent_flags::paused;
2218     else
2219         p.flags &= ~lt::torrent_flags::paused;
2220     if (loadTorrentParams.paused || loadTorrentParams.forced)
2221         p.flags &= ~lt::torrent_flags::auto_managed;
2222     else
2223         p.flags |= lt::torrent_flags::auto_managed;
2224 
2225     if (!isFindingIncompleteFiles)
2226         return loadTorrent(loadTorrentParams);
2227 
2228     m_loadingTorrents.insert(id, loadTorrentParams);
2229     return true;
2230 }
2231 
2232 // Add a torrent to the BitTorrent session
loadTorrent(LoadTorrentParams params)2233 bool Session::loadTorrent(LoadTorrentParams params)
2234 {
2235     lt::add_torrent_params &p = params.ltAddTorrentParams;
2236 
2237 #if (LIBTORRENT_VERSION_NUM < 20000)
2238     p.storage = customStorageConstructor;
2239 #endif
2240     // Limits
2241     p.max_connections = maxConnectionsPerTorrent();
2242     p.max_uploads = maxUploadsPerTorrent();
2243 
2244     const bool hasMetadata = (p.ti && p.ti->is_valid());
2245 #if (LIBTORRENT_VERSION_NUM >= 20000)
2246     const auto id = TorrentID::fromInfoHash(hasMetadata ? p.ti->info_hashes() : p.info_hashes);
2247 #else
2248     const auto id = TorrentID::fromInfoHash(hasMetadata ? p.ti->info_hash() : p.info_hash);
2249 #endif
2250     m_loadingTorrents.insert(id, params);
2251 
2252     // Adding torrent to BitTorrent session
2253     m_nativeSession->async_add_torrent(p);
2254 
2255     return true;
2256 }
2257 
findIncompleteFiles(const TorrentInfo & torrentInfo,const QString & savePath) const2258 void Session::findIncompleteFiles(const TorrentInfo &torrentInfo, const QString &savePath) const
2259 {
2260     const auto searchId = TorrentID::fromInfoHash(torrentInfo.infoHash());
2261     const QStringList originalFileNames = torrentInfo.filePaths();
2262     const QString completeSavePath = savePath;
2263     const QString incompleteSavePath = (isTempPathEnabled() ? torrentTempPath(torrentInfo) : QString {});
2264     QMetaObject::invokeMethod(m_fileSearcher, [=]()
2265     {
2266         m_fileSearcher->search(searchId, originalFileNames, completeSavePath, incompleteSavePath);
2267     });
2268 }
2269 
2270 // Add a torrent to libtorrent session in hidden mode
2271 // and force it to download its metadata
downloadMetadata(const MagnetUri & magnetUri)2272 bool Session::downloadMetadata(const MagnetUri &magnetUri)
2273 {
2274     if (!magnetUri.isValid()) return false;
2275 
2276     const auto id = TorrentID::fromInfoHash(magnetUri.infoHash());
2277     const QString name = magnetUri.name();
2278 
2279     // We should not add torrent if it's already
2280     // processed or adding to session
2281     if (m_torrents.contains(id)) return false;
2282     if (m_loadingTorrents.contains(id)) return false;
2283     if (m_downloadedMetadata.contains(id)) return false;
2284 
2285     qDebug("Adding torrent to preload metadata...");
2286     qDebug(" -> Torrent ID: %s", qUtf8Printable(id.toString()));
2287     qDebug(" -> Name: %s", qUtf8Printable(name));
2288 
2289     lt::add_torrent_params p = magnetUri.addTorrentParams();
2290 
2291     // Flags
2292     // Preallocation mode
2293     if (isPreallocationEnabled())
2294         p.storage_mode = lt::storage_mode_allocate;
2295     else
2296         p.storage_mode = lt::storage_mode_sparse;
2297 
2298     // Limits
2299     p.max_connections = maxConnectionsPerTorrent();
2300     p.max_uploads = maxUploadsPerTorrent();
2301 
2302     const QString savePath = Utils::Fs::tempPath() + id.toString();
2303     p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
2304 
2305     // Forced start
2306     p.flags &= ~lt::torrent_flags::paused;
2307     p.flags &= ~lt::torrent_flags::auto_managed;
2308 
2309     // Solution to avoid accidental file writes
2310     p.flags |= lt::torrent_flags::upload_mode;
2311 
2312 #if (LIBTORRENT_VERSION_NUM < 20000)
2313     p.storage = customStorageConstructor;
2314 #endif
2315 
2316     // Adding torrent to libtorrent session
2317     lt::error_code ec;
2318     lt::torrent_handle h = m_nativeSession->add_torrent(p, ec);
2319     if (ec) return false;
2320 
2321     // waiting for metadata...
2322     m_downloadedMetadata.insert(h.info_hash());
2323     ++m_extraLimit;
2324     adjustLimits();
2325 
2326     return true;
2327 }
2328 
exportTorrentFile(const Torrent * torrent,TorrentExportFolder folder)2329 void Session::exportTorrentFile(const Torrent *torrent, TorrentExportFolder folder)
2330 {
2331     Q_ASSERT(((folder == TorrentExportFolder::Regular) && !torrentExportDirectory().isEmpty()) ||
2332              ((folder == TorrentExportFolder::Finished) && !finishedTorrentExportDirectory().isEmpty()));
2333 
2334     const QString validName = Utils::Fs::toValidFileSystemName(torrent->name());
2335     const QString torrentFilename = QString::fromLatin1("%1.torrent").arg(torrent->id().toString());
2336     QString torrentExportFilename = QString::fromLatin1("%1.torrent").arg(validName);
2337     const QString torrentPath = QDir(m_resumeFolderPath).absoluteFilePath(torrentFilename);
2338     const QDir exportPath(folder == TorrentExportFolder::Regular ? torrentExportDirectory() : finishedTorrentExportDirectory());
2339     if (exportPath.exists() || exportPath.mkpath(exportPath.absolutePath()))
2340     {
2341         QString newTorrentPath = exportPath.absoluteFilePath(torrentExportFilename);
2342         int counter = 0;
2343         while (QFile::exists(newTorrentPath) && !Utils::Fs::sameFiles(torrentPath, newTorrentPath))
2344         {
2345             // Append number to torrent name to make it unique
2346             torrentExportFilename = QString::fromLatin1("%1 %2.torrent").arg(validName).arg(++counter);
2347             newTorrentPath = exportPath.absoluteFilePath(torrentExportFilename);
2348         }
2349 
2350         if (!QFile::exists(newTorrentPath))
2351             QFile::copy(torrentPath, newTorrentPath);
2352     }
2353 }
2354 
generateResumeData()2355 void Session::generateResumeData()
2356 {
2357     for (TorrentImpl *const torrent : asConst(m_torrents))
2358     {
2359         if (!torrent->isValid()) continue;
2360 
2361         if (torrent->needSaveResumeData())
2362         {
2363             torrent->saveResumeData();
2364             m_needSaveResumeDataTorrents.remove(torrent->id());
2365         }
2366     }
2367 }
2368 
2369 // Called on exit
saveResumeData()2370 void Session::saveResumeData()
2371 {
2372     // Pause session
2373     m_nativeSession->pause();
2374 
2375     if (isQueueingSystemEnabled())
2376         saveTorrentsQueue();
2377     generateResumeData();
2378 
2379     while (m_numResumeData > 0)
2380     {
2381         const std::vector<lt::alert *> alerts = getPendingAlerts(lt::seconds {30});
2382         if (alerts.empty())
2383         {
2384             LogMsg(tr("Error: Aborted saving resume data for %1 outstanding torrents.").arg(QString::number(m_numResumeData))
2385                 , Log::CRITICAL);
2386             break;
2387         }
2388 
2389         for (const lt::alert *a : alerts)
2390         {
2391             switch (a->type())
2392             {
2393             case lt::save_resume_data_failed_alert::alert_type:
2394             case lt::save_resume_data_alert::alert_type:
2395                 dispatchTorrentAlert(a);
2396                 break;
2397             }
2398         }
2399     }
2400 }
2401 
saveTorrentsQueue() const2402 void Session::saveTorrentsQueue() const
2403 {
2404     // store hash in textual representation
2405     QMap<int, QString> queue; // Use QMap since it should be ordered by key
2406     for (const TorrentImpl *torrent : asConst(m_torrents))
2407     {
2408         // We require actual (non-cached) queue position here!
2409         const int queuePos = static_cast<LTUnderlyingType<lt::queue_position_t>>(torrent->nativeHandle().queue_position());
2410         if (queuePos >= 0)
2411             queue[queuePos] = torrent->id().toString();
2412     }
2413 
2414     QByteArray data;
2415     data.reserve(((TorrentID::length() * 2) + 1) * queue.size());
2416     for (const QString &torrentID : asConst(queue))
2417         data += (torrentID.toLatin1() + '\n');
2418 
2419     const QString filename = QLatin1String {"queue"};
2420     QMetaObject::invokeMethod(m_resumeDataSavingManager
2421         , [this, data, filename]() { m_resumeDataSavingManager->save(filename, data); });
2422 }
2423 
removeTorrentsQueue() const2424 void Session::removeTorrentsQueue() const
2425 {
2426     const QString filename = QLatin1String {"queue"};
2427     QMetaObject::invokeMethod(m_resumeDataSavingManager
2428         , [this, filename]() { m_resumeDataSavingManager->remove(filename); });
2429 }
2430 
setDefaultSavePath(QString path)2431 void Session::setDefaultSavePath(QString path)
2432 {
2433     path = normalizeSavePath(path);
2434     if (path == m_defaultSavePath) return;
2435 
2436     m_defaultSavePath = path;
2437 
2438     if (isDisableAutoTMMWhenDefaultSavePathChanged())
2439         for (TorrentImpl *const torrent : asConst(m_torrents))
2440             torrent->setAutoTMMEnabled(false);
2441     else
2442         for (TorrentImpl *const torrent : asConst(m_torrents))
2443             torrent->handleCategorySavePathChanged();
2444 }
2445 
setTempPath(QString path)2446 void Session::setTempPath(QString path)
2447 {
2448     path = normalizeSavePath(path, defaultSavePath() + "temp/");
2449     if (path == m_tempPath) return;
2450 
2451     m_tempPath = path;
2452 
2453     for (TorrentImpl *const torrent : asConst(m_torrents))
2454         torrent->handleTempPathChanged();
2455 }
2456 
networkOnlineStateChanged(const bool online)2457 void Session::networkOnlineStateChanged(const bool online)
2458 {
2459     LogMsg(tr("System network status changed to %1", "e.g: System network status changed to ONLINE").arg(online ? tr("ONLINE") : tr("OFFLINE")), Log::INFO);
2460 }
2461 
networkConfigurationChange(const QNetworkConfiguration & cfg)2462 void Session::networkConfigurationChange(const QNetworkConfiguration &cfg)
2463 {
2464     const QString configuredInterfaceName = networkInterface();
2465     // Empty means "Any Interface". In this case libtorrent has binded to 0.0.0.0 so any change to any interface will
2466     // be automatically picked up. Otherwise we would rebinding here to 0.0.0.0 again.
2467     if (configuredInterfaceName.isEmpty()) return;
2468 
2469     const QString changedInterface = cfg.name();
2470 
2471     if (configuredInterfaceName == changedInterface)
2472     {
2473         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);
2474         configureListeningInterface();
2475     }
2476 }
2477 
getListeningIPs() const2478 QStringList Session::getListeningIPs() const
2479 {
2480     QStringList IPs;
2481 
2482     const QString ifaceName = networkInterface();
2483     const QString ifaceAddr = networkInterfaceAddress();
2484     const QHostAddress configuredAddr(ifaceAddr);
2485     const bool allIPv4 = (ifaceAddr == QLatin1String("0.0.0.0")); // Means All IPv4 addresses
2486     const bool allIPv6 = (ifaceAddr == QLatin1String("::")); // Means All IPv6 addresses
2487 
2488     if (!ifaceAddr.isEmpty() && !allIPv4 && !allIPv6 && configuredAddr.isNull())
2489     {
2490         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);
2491         // Pass the invalid user configured interface name/address to libtorrent
2492         // in hopes that it will come online later.
2493         // This will not cause IP leak but allow user to reconnect the interface
2494         // and re-establish connection without restarting the client.
2495         IPs.append(ifaceAddr);
2496         return IPs;
2497     }
2498 
2499     if (ifaceName.isEmpty())
2500     {
2501         if (ifaceAddr.isEmpty())
2502             return {QLatin1String("0.0.0.0"), QLatin1String("::")}; // Indicates all interfaces + all addresses (aka default)
2503 
2504         if (allIPv4)
2505             return {QLatin1String("0.0.0.0")};
2506 
2507         if (allIPv6)
2508             return {QLatin1String("::")};
2509     }
2510 
2511     const auto checkAndAddIP = [allIPv4, allIPv6, &IPs](const QHostAddress &addr, const QHostAddress &match)
2512     {
2513         if ((allIPv4 && (addr.protocol() != QAbstractSocket::IPv4Protocol))
2514             || (allIPv6 && (addr.protocol() != QAbstractSocket::IPv6Protocol)))
2515             return;
2516 
2517         if ((match == addr) || allIPv4 || allIPv6)
2518             IPs.append(addr.toString());
2519     };
2520 
2521     if (ifaceName.isEmpty())
2522     {
2523         const QList<QHostAddress> addresses = QNetworkInterface::allAddresses();
2524         for (const auto &addr : addresses)
2525             checkAndAddIP(addr, configuredAddr);
2526 
2527         // At this point ifaceAddr was non-empty
2528         // If IPs.isEmpty() it means the configured Address was not found
2529         if (IPs.isEmpty())
2530         {
2531             LogMsg(tr("Can't find the configured address '%1' to listen on"
2532                     , "Can't find the configured address '192.168.1.3' to listen on")
2533                 .arg(ifaceAddr), Log::CRITICAL);
2534             IPs.append(ifaceAddr);
2535         }
2536 
2537         return IPs;
2538     }
2539 
2540     // Attempt to listen on provided interface
2541     const QNetworkInterface networkIFace = QNetworkInterface::interfaceFromName(ifaceName);
2542     if (!networkIFace.isValid())
2543     {
2544         qDebug("Invalid network interface: %s", qUtf8Printable(ifaceName));
2545         LogMsg(tr("The network interface defined is invalid: %1").arg(ifaceName), Log::CRITICAL);
2546         IPs.append(ifaceName);
2547         return IPs;
2548     }
2549 
2550     if (ifaceAddr.isEmpty())
2551     {
2552         IPs.append(ifaceName);
2553         return IPs; // On Windows calling code converts it to GUID
2554     }
2555 
2556     const QList<QNetworkAddressEntry> addresses = networkIFace.addressEntries();
2557     qDebug("This network interface has %d IP addresses", addresses.size());
2558     for (const QNetworkAddressEntry &entry : addresses)
2559         checkAndAddIP(entry.ip(), configuredAddr);
2560 
2561     // Make sure there is at least one IP
2562     // At this point there was an explicit interface and an explicit address set
2563     // and the address should have been found
2564     if (IPs.isEmpty())
2565     {
2566         LogMsg(tr("Can't find the configured address '%1' to listen on"
2567                   , "Can't find the configured address '192.168.1.3' to listen on")
2568             .arg(ifaceAddr), Log::CRITICAL);
2569         IPs.append(ifaceAddr);
2570     }
2571 
2572     return IPs;
2573 }
2574 
2575 // Set the ports range in which is chosen the port
2576 // the BitTorrent session will listen to
configureListeningInterface()2577 void Session::configureListeningInterface()
2578 {
2579     m_listenInterfaceConfigured = false;
2580     configureDeferred();
2581 }
2582 
globalDownloadSpeedLimit() const2583 int Session::globalDownloadSpeedLimit() const
2584 {
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     return m_globalDownloadSpeedLimit * 1024;
2588 }
2589 
setGlobalDownloadSpeedLimit(const int limit)2590 void Session::setGlobalDownloadSpeedLimit(const int limit)
2591 {
2592     // Unfortunately the value was saved as KiB instead of B.
2593     // But it is better to pass it around internally(+ webui) as Bytes.
2594     if (limit == globalDownloadSpeedLimit())
2595         return;
2596 
2597     if (limit <= 0)
2598         m_globalDownloadSpeedLimit = 0;
2599     else if (limit <= 1024)
2600         m_globalDownloadSpeedLimit = 1;
2601     else
2602         m_globalDownloadSpeedLimit = (limit / 1024);
2603 
2604     if (!isAltGlobalSpeedLimitEnabled())
2605         configureDeferred();
2606 }
2607 
globalUploadSpeedLimit() const2608 int Session::globalUploadSpeedLimit() const
2609 {
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     return m_globalUploadSpeedLimit * 1024;
2613 }
2614 
setGlobalUploadSpeedLimit(const int limit)2615 void Session::setGlobalUploadSpeedLimit(const int limit)
2616 {
2617     // Unfortunately the value was saved as KiB instead of B.
2618     // But it is better to pass it around internally(+ webui) as Bytes.
2619     if (limit == globalUploadSpeedLimit())
2620         return;
2621 
2622     if (limit <= 0)
2623         m_globalUploadSpeedLimit = 0;
2624     else if (limit <= 1024)
2625         m_globalUploadSpeedLimit = 1;
2626     else
2627         m_globalUploadSpeedLimit = (limit / 1024);
2628 
2629     if (!isAltGlobalSpeedLimitEnabled())
2630         configureDeferred();
2631 }
2632 
altGlobalDownloadSpeedLimit() const2633 int Session::altGlobalDownloadSpeedLimit() const
2634 {
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     return m_altGlobalDownloadSpeedLimit * 1024;
2638 }
2639 
setAltGlobalDownloadSpeedLimit(const int limit)2640 void Session::setAltGlobalDownloadSpeedLimit(const int limit)
2641 {
2642     // Unfortunately the value was saved as KiB instead of B.
2643     // But it is better to pass it around internally(+ webui) as Bytes.
2644     if (limit == altGlobalDownloadSpeedLimit())
2645         return;
2646 
2647     if (limit <= 0)
2648         m_altGlobalDownloadSpeedLimit = 0;
2649     else if (limit <= 1024)
2650         m_altGlobalDownloadSpeedLimit = 1;
2651     else
2652         m_altGlobalDownloadSpeedLimit = (limit / 1024);
2653 
2654     if (isAltGlobalSpeedLimitEnabled())
2655         configureDeferred();
2656 }
2657 
altGlobalUploadSpeedLimit() const2658 int Session::altGlobalUploadSpeedLimit() const
2659 {
2660     // Unfortunately the value was saved as KiB instead of B.
2661     // But it is better to pass it around internally(+ webui) as Bytes.
2662     return m_altGlobalUploadSpeedLimit * 1024;
2663 }
2664 
setAltGlobalUploadSpeedLimit(const int limit)2665 void Session::setAltGlobalUploadSpeedLimit(const int limit)
2666 {
2667     // Unfortunately the value was saved as KiB instead of B.
2668     // But it is better to pass it around internally(+ webui) as Bytes.
2669     if (limit == altGlobalUploadSpeedLimit())
2670         return;
2671 
2672     if (limit <= 0)
2673         m_altGlobalUploadSpeedLimit = 0;
2674     else if (limit <= 1024)
2675         m_altGlobalUploadSpeedLimit = 1;
2676     else
2677         m_altGlobalUploadSpeedLimit = (limit / 1024);
2678 
2679     if (isAltGlobalSpeedLimitEnabled())
2680         configureDeferred();
2681 }
2682 
downloadSpeedLimit() const2683 int Session::downloadSpeedLimit() const
2684 {
2685     return isAltGlobalSpeedLimitEnabled()
2686             ? altGlobalDownloadSpeedLimit()
2687             : globalDownloadSpeedLimit();
2688 }
2689 
setDownloadSpeedLimit(const int limit)2690 void Session::setDownloadSpeedLimit(const int limit)
2691 {
2692     if (isAltGlobalSpeedLimitEnabled())
2693         setAltGlobalDownloadSpeedLimit(limit);
2694     else
2695         setGlobalDownloadSpeedLimit(limit);
2696 }
2697 
uploadSpeedLimit() const2698 int Session::uploadSpeedLimit() const
2699 {
2700     return isAltGlobalSpeedLimitEnabled()
2701             ? altGlobalUploadSpeedLimit()
2702             : globalUploadSpeedLimit();
2703 }
2704 
setUploadSpeedLimit(const int limit)2705 void Session::setUploadSpeedLimit(const int limit)
2706 {
2707     if (isAltGlobalSpeedLimitEnabled())
2708         setAltGlobalUploadSpeedLimit(limit);
2709     else
2710         setGlobalUploadSpeedLimit(limit);
2711 }
2712 
isAltGlobalSpeedLimitEnabled() const2713 bool Session::isAltGlobalSpeedLimitEnabled() const
2714 {
2715     return m_isAltGlobalSpeedLimitEnabled;
2716 }
2717 
setAltGlobalSpeedLimitEnabled(const bool enabled)2718 void Session::setAltGlobalSpeedLimitEnabled(const bool enabled)
2719 {
2720     if (enabled == isAltGlobalSpeedLimitEnabled()) return;
2721 
2722     // Save new state to remember it on startup
2723     m_isAltGlobalSpeedLimitEnabled = enabled;
2724     applyBandwidthLimits();
2725     // Notify
2726     emit speedLimitModeChanged(m_isAltGlobalSpeedLimitEnabled);
2727 }
2728 
isBandwidthSchedulerEnabled() const2729 bool Session::isBandwidthSchedulerEnabled() const
2730 {
2731     return m_isBandwidthSchedulerEnabled;
2732 }
2733 
setBandwidthSchedulerEnabled(const bool enabled)2734 void Session::setBandwidthSchedulerEnabled(const bool enabled)
2735 {
2736     if (enabled != isBandwidthSchedulerEnabled())
2737     {
2738         m_isBandwidthSchedulerEnabled = enabled;
2739         if (enabled)
2740             enableBandwidthScheduler();
2741         else
2742             delete m_bwScheduler;
2743     }
2744 }
2745 
saveResumeDataInterval() const2746 int Session::saveResumeDataInterval() const
2747 {
2748     return m_saveResumeDataInterval;
2749 }
2750 
setSaveResumeDataInterval(const int value)2751 void Session::setSaveResumeDataInterval(const int value)
2752 {
2753     if (value == m_saveResumeDataInterval)
2754         return;
2755 
2756     m_saveResumeDataInterval = value;
2757 
2758     if (value > 0)
2759     {
2760         m_resumeDataTimer->setInterval(value * 60 * 1000);
2761         m_resumeDataTimer->start();
2762     }
2763     else
2764     {
2765         m_resumeDataTimer->stop();
2766     }
2767 }
2768 
port() const2769 int Session::port() const
2770 {
2771     return m_port;
2772 }
2773 
setPort(const int port)2774 void Session::setPort(const int port)
2775 {
2776     if (port != m_port)
2777     {
2778         m_port = port;
2779         configureListeningInterface();
2780     }
2781 }
2782 
useRandomPort() const2783 bool Session::useRandomPort() const
2784 {
2785     return m_useRandomPort;
2786 }
2787 
setUseRandomPort(const bool value)2788 void Session::setUseRandomPort(const bool value)
2789 {
2790     m_useRandomPort = value;
2791 }
2792 
networkInterface() const2793 QString Session::networkInterface() const
2794 {
2795     return m_networkInterface;
2796 }
2797 
setNetworkInterface(const QString & iface)2798 void Session::setNetworkInterface(const QString &iface)
2799 {
2800     if (iface != networkInterface())
2801     {
2802         m_networkInterface = iface;
2803         configureListeningInterface();
2804     }
2805 }
2806 
networkInterfaceName() const2807 QString Session::networkInterfaceName() const
2808 {
2809     return m_networkInterfaceName;
2810 }
2811 
setNetworkInterfaceName(const QString & name)2812 void Session::setNetworkInterfaceName(const QString &name)
2813 {
2814     m_networkInterfaceName = name;
2815 }
2816 
networkInterfaceAddress() const2817 QString Session::networkInterfaceAddress() const
2818 {
2819     return m_networkInterfaceAddress;
2820 }
2821 
setNetworkInterfaceAddress(const QString & address)2822 void Session::setNetworkInterfaceAddress(const QString &address)
2823 {
2824     if (address != networkInterfaceAddress())
2825     {
2826         m_networkInterfaceAddress = address;
2827         configureListeningInterface();
2828     }
2829 }
2830 
encryption() const2831 int Session::encryption() const
2832 {
2833     return m_encryption;
2834 }
2835 
setEncryption(const int state)2836 void Session::setEncryption(const int state)
2837 {
2838     if (state != encryption())
2839     {
2840         m_encryption = state;
2841         configureDeferred();
2842         LogMsg(tr("Encryption support [%1]").arg(
2843             state == 0 ? tr("ON") : ((state == 1) ? tr("FORCED") : tr("OFF")))
2844             , Log::INFO);
2845     }
2846 }
2847 
isProxyPeerConnectionsEnabled() const2848 bool Session::isProxyPeerConnectionsEnabled() const
2849 {
2850     return m_isProxyPeerConnectionsEnabled;
2851 }
2852 
setProxyPeerConnectionsEnabled(const bool enabled)2853 void Session::setProxyPeerConnectionsEnabled(const bool enabled)
2854 {
2855     if (enabled != isProxyPeerConnectionsEnabled())
2856     {
2857         m_isProxyPeerConnectionsEnabled = enabled;
2858         configureDeferred();
2859     }
2860 }
2861 
chokingAlgorithm() const2862 ChokingAlgorithm Session::chokingAlgorithm() const
2863 {
2864     return m_chokingAlgorithm;
2865 }
2866 
setChokingAlgorithm(const ChokingAlgorithm mode)2867 void Session::setChokingAlgorithm(const ChokingAlgorithm mode)
2868 {
2869     if (mode == m_chokingAlgorithm) return;
2870 
2871     m_chokingAlgorithm = mode;
2872     configureDeferred();
2873 }
2874 
seedChokingAlgorithm() const2875 SeedChokingAlgorithm Session::seedChokingAlgorithm() const
2876 {
2877     return m_seedChokingAlgorithm;
2878 }
2879 
setSeedChokingAlgorithm(const SeedChokingAlgorithm mode)2880 void Session::setSeedChokingAlgorithm(const SeedChokingAlgorithm mode)
2881 {
2882     if (mode == m_seedChokingAlgorithm) return;
2883 
2884     m_seedChokingAlgorithm = mode;
2885     configureDeferred();
2886 }
2887 
isAddTrackersEnabled() const2888 bool Session::isAddTrackersEnabled() const
2889 {
2890     return m_isAddTrackersEnabled;
2891 }
2892 
setAddTrackersEnabled(const bool enabled)2893 void Session::setAddTrackersEnabled(const bool enabled)
2894 {
2895     m_isAddTrackersEnabled = enabled;
2896 }
2897 
additionalTrackers() const2898 QString Session::additionalTrackers() const
2899 {
2900     return m_additionalTrackers;
2901 }
2902 
setAdditionalTrackers(const QString & trackers)2903 void Session::setAdditionalTrackers(const QString &trackers)
2904 {
2905     if (trackers != additionalTrackers())
2906     {
2907         m_additionalTrackers = trackers;
2908         populateAdditionalTrackers();
2909     }
2910 }
2911 
isIPFilteringEnabled() const2912 bool Session::isIPFilteringEnabled() const
2913 {
2914     return m_isIPFilteringEnabled;
2915 }
2916 
setIPFilteringEnabled(const bool enabled)2917 void Session::setIPFilteringEnabled(const bool enabled)
2918 {
2919     if (enabled != m_isIPFilteringEnabled)
2920     {
2921         m_isIPFilteringEnabled = enabled;
2922         m_IPFilteringConfigured = false;
2923         configureDeferred();
2924     }
2925 }
2926 
IPFilterFile() const2927 QString Session::IPFilterFile() const
2928 {
2929     return Utils::Fs::toUniformPath(m_IPFilterFile);
2930 }
2931 
setIPFilterFile(QString path)2932 void Session::setIPFilterFile(QString path)
2933 {
2934     path = Utils::Fs::toUniformPath(path);
2935     if (path != IPFilterFile())
2936     {
2937         m_IPFilterFile = path;
2938         m_IPFilteringConfigured = false;
2939         configureDeferred();
2940     }
2941 }
2942 
setBannedIPs(const QStringList & newList)2943 void Session::setBannedIPs(const QStringList &newList)
2944 {
2945     if (newList == m_bannedIPs)
2946         return; // do nothing
2947     // here filter out incorrect IP
2948     QStringList filteredList;
2949     for (const QString &ip : newList)
2950     {
2951         if (Utils::Net::isValidIP(ip))
2952         {
2953             // the same IPv6 addresses could be written in different forms;
2954             // QHostAddress::toString() result format follows RFC5952;
2955             // thus we avoid duplicate entries pointing to the same address
2956             filteredList << QHostAddress(ip).toString();
2957         }
2958         else
2959         {
2960             LogMsg(tr("%1 is not a valid IP address and was rejected while applying the list of banned addresses.")
2961                    .arg(ip)
2962                 , Log::WARNING);
2963         }
2964     }
2965     // now we have to sort IPs and make them unique
2966     filteredList.sort();
2967     filteredList.removeDuplicates();
2968     // Again ensure that the new list is different from the stored one.
2969     if (filteredList == m_bannedIPs)
2970         return; // do nothing
2971     // store to session settings
2972     // also here we have to recreate filter list including 3rd party ban file
2973     // and install it again into m_session
2974     m_bannedIPs = filteredList;
2975     m_IPFilteringConfigured = false;
2976     configureDeferred();
2977 }
2978 
bannedIPs() const2979 QStringList Session::bannedIPs() const
2980 {
2981     return m_bannedIPs;
2982 }
2983 
2984 #if defined(Q_OS_WIN)
getOSMemoryPriority() const2985 OSMemoryPriority Session::getOSMemoryPriority() const
2986 {
2987     return m_OSMemoryPriority;
2988 }
2989 
setOSMemoryPriority(const OSMemoryPriority priority)2990 void Session::setOSMemoryPriority(const OSMemoryPriority priority)
2991 {
2992     if (m_OSMemoryPriority == priority)
2993         return;
2994 
2995     m_OSMemoryPriority = priority;
2996     configureDeferred();
2997 }
2998 
applyOSMemoryPriority() const2999 void Session::applyOSMemoryPriority() const
3000 {
3001     using SETPROCESSINFORMATION = BOOL (WINAPI *)(HANDLE, PROCESS_INFORMATION_CLASS, LPVOID, DWORD);
3002     const auto setProcessInformation = Utils::Misc::loadWinAPI<SETPROCESSINFORMATION>("Kernel32.dll", "SetProcessInformation");
3003     if (!setProcessInformation)  // only available on Windows >= 8
3004         return;
3005 
3006 #if (_WIN32_WINNT < _WIN32_WINNT_WIN8)
3007     // this dummy struct is required to compile successfully when targeting older Windows version
3008     struct MEMORY_PRIORITY_INFORMATION
3009     {
3010         ULONG MemoryPriority;
3011     };
3012 
3013 #define MEMORY_PRIORITY_LOWEST 0
3014 #define MEMORY_PRIORITY_VERY_LOW 1
3015 #define MEMORY_PRIORITY_LOW 2
3016 #define MEMORY_PRIORITY_MEDIUM 3
3017 #define MEMORY_PRIORITY_BELOW_NORMAL 4
3018 #define MEMORY_PRIORITY_NORMAL 5
3019 #endif
3020 
3021     MEMORY_PRIORITY_INFORMATION prioInfo {};
3022     switch (getOSMemoryPriority())
3023     {
3024     case OSMemoryPriority::Normal:
3025     default:
3026         prioInfo.MemoryPriority = MEMORY_PRIORITY_NORMAL;
3027         break;
3028     case OSMemoryPriority::BelowNormal:
3029         prioInfo.MemoryPriority = MEMORY_PRIORITY_BELOW_NORMAL;
3030         break;
3031     case OSMemoryPriority::Medium:
3032         prioInfo.MemoryPriority = MEMORY_PRIORITY_MEDIUM;
3033         break;
3034     case OSMemoryPriority::Low:
3035         prioInfo.MemoryPriority = MEMORY_PRIORITY_LOW;
3036         break;
3037     case OSMemoryPriority::VeryLow:
3038         prioInfo.MemoryPriority = MEMORY_PRIORITY_VERY_LOW;
3039         break;
3040     }
3041     setProcessInformation(::GetCurrentProcess(), ProcessMemoryPriority, &prioInfo, sizeof(prioInfo));
3042 }
3043 #endif
3044 
maxConnectionsPerTorrent() const3045 int Session::maxConnectionsPerTorrent() const
3046 {
3047     return m_maxConnectionsPerTorrent;
3048 }
3049 
setMaxConnectionsPerTorrent(int max)3050 void Session::setMaxConnectionsPerTorrent(int max)
3051 {
3052     max = (max > 0) ? max : -1;
3053     if (max != maxConnectionsPerTorrent())
3054     {
3055         m_maxConnectionsPerTorrent = max;
3056 
3057         // Apply this to all session torrents
3058         for (const lt::torrent_handle &handle : m_nativeSession->get_torrents())
3059         {
3060             if (!handle.is_valid()) continue;
3061             try
3062             {
3063                 handle.set_max_connections(max);
3064             }
3065             catch (const std::exception &) {}
3066         }
3067     }
3068 }
3069 
maxUploadsPerTorrent() const3070 int Session::maxUploadsPerTorrent() const
3071 {
3072     return m_maxUploadsPerTorrent;
3073 }
3074 
setMaxUploadsPerTorrent(int max)3075 void Session::setMaxUploadsPerTorrent(int max)
3076 {
3077     max = (max > 0) ? max : -1;
3078     if (max != maxUploadsPerTorrent())
3079     {
3080         m_maxUploadsPerTorrent = max;
3081 
3082         // Apply this to all session torrents
3083         for (const lt::torrent_handle &handle : m_nativeSession->get_torrents())
3084         {
3085             if (!handle.is_valid()) continue;
3086             try
3087             {
3088                 handle.set_max_uploads(max);
3089             }
3090             catch (const std::exception &) {}
3091         }
3092     }
3093 }
3094 
announceToAllTrackers() const3095 bool Session::announceToAllTrackers() const
3096 {
3097     return m_announceToAllTrackers;
3098 }
3099 
setAnnounceToAllTrackers(const bool val)3100 void Session::setAnnounceToAllTrackers(const bool val)
3101 {
3102     if (val != m_announceToAllTrackers)
3103     {
3104         m_announceToAllTrackers = val;
3105         configureDeferred();
3106     }
3107 }
3108 
announceToAllTiers() const3109 bool Session::announceToAllTiers() const
3110 {
3111     return m_announceToAllTiers;
3112 }
3113 
setAnnounceToAllTiers(const bool val)3114 void Session::setAnnounceToAllTiers(const bool val)
3115 {
3116     if (val != m_announceToAllTiers)
3117     {
3118         m_announceToAllTiers = val;
3119         configureDeferred();
3120     }
3121 }
3122 
peerTurnover() const3123 int Session::peerTurnover() const
3124 {
3125     return m_peerTurnover;
3126 }
3127 
setPeerTurnover(const int val)3128 void Session::setPeerTurnover(const int val)
3129 {
3130     if (val == m_peerTurnover)
3131         return;
3132 
3133     m_peerTurnover = val;
3134     configureDeferred();
3135 }
3136 
peerTurnoverCutoff() const3137 int Session::peerTurnoverCutoff() const
3138 {
3139     return m_peerTurnoverCutoff;
3140 }
3141 
setPeerTurnoverCutoff(const int val)3142 void Session::setPeerTurnoverCutoff(const int val)
3143 {
3144     if (val == m_peerTurnoverCutoff)
3145         return;
3146 
3147     m_peerTurnoverCutoff = val;
3148     configureDeferred();
3149 }
3150 
peerTurnoverInterval() const3151 int Session::peerTurnoverInterval() const
3152 {
3153     return m_peerTurnoverInterval;
3154 }
3155 
setPeerTurnoverInterval(const int val)3156 void Session::setPeerTurnoverInterval(const int val)
3157 {
3158     if (val == m_peerTurnoverInterval)
3159         return;
3160 
3161     m_peerTurnoverInterval = val;
3162     configureDeferred();
3163 }
3164 
asyncIOThreads() const3165 int Session::asyncIOThreads() const
3166 {
3167     return qBound(1, m_asyncIOThreads.get(), 1024);
3168 }
3169 
setAsyncIOThreads(const int num)3170 void Session::setAsyncIOThreads(const int num)
3171 {
3172     if (num == m_asyncIOThreads)
3173         return;
3174 
3175     m_asyncIOThreads = num;
3176     configureDeferred();
3177 }
3178 
hashingThreads() const3179 int Session::hashingThreads() const
3180 {
3181     return qBound(1, m_hashingThreads.get(), 1024);
3182 }
3183 
setHashingThreads(const int num)3184 void Session::setHashingThreads(const int num)
3185 {
3186     if (num == m_hashingThreads)
3187         return;
3188 
3189     m_hashingThreads = num;
3190     configureDeferred();
3191 }
3192 
filePoolSize() const3193 int Session::filePoolSize() const
3194 {
3195     return m_filePoolSize;
3196 }
3197 
setFilePoolSize(const int size)3198 void Session::setFilePoolSize(const int size)
3199 {
3200     if (size == m_filePoolSize)
3201         return;
3202 
3203     m_filePoolSize = size;
3204     configureDeferred();
3205 }
3206 
checkingMemUsage() const3207 int Session::checkingMemUsage() const
3208 {
3209     return qMax(1, m_checkingMemUsage.get());
3210 }
3211 
setCheckingMemUsage(int size)3212 void Session::setCheckingMemUsage(int size)
3213 {
3214     size = qMax(size, 1);
3215 
3216     if (size == m_checkingMemUsage)
3217         return;
3218 
3219     m_checkingMemUsage = size;
3220     configureDeferred();
3221 }
3222 
diskCacheSize() const3223 int Session::diskCacheSize() const
3224 {
3225 #ifdef QBT_APP_64BIT
3226     return qMin(m_diskCacheSize.get(), 33554431);  // 32768GiB
3227 #else
3228     // When build as 32bit binary, set the maximum at less than 2GB to prevent crashes
3229     // allocate 1536MiB and leave 512MiB to the rest of program data in RAM
3230     return qMin(m_diskCacheSize.get(), 1536);
3231 #endif
3232 }
3233 
setDiskCacheSize(int size)3234 void Session::setDiskCacheSize(int size)
3235 {
3236 #ifdef QBT_APP_64BIT
3237     size = qMin(size, 33554431);  // 32768GiB
3238 #else
3239     // allocate 1536MiB and leave 512MiB to the rest of program data in RAM
3240     size = qMin(size, 1536);
3241 #endif
3242     if (size != m_diskCacheSize)
3243     {
3244         m_diskCacheSize = size;
3245         configureDeferred();
3246     }
3247 }
3248 
diskCacheTTL() const3249 int Session::diskCacheTTL() const
3250 {
3251     return m_diskCacheTTL;
3252 }
3253 
setDiskCacheTTL(const int ttl)3254 void Session::setDiskCacheTTL(const int ttl)
3255 {
3256     if (ttl != m_diskCacheTTL)
3257     {
3258         m_diskCacheTTL = ttl;
3259         configureDeferred();
3260     }
3261 }
3262 
useOSCache() const3263 bool Session::useOSCache() const
3264 {
3265     return m_useOSCache;
3266 }
3267 
setUseOSCache(const bool use)3268 void Session::setUseOSCache(const bool use)
3269 {
3270     if (use != m_useOSCache)
3271     {
3272         m_useOSCache = use;
3273         configureDeferred();
3274     }
3275 }
3276 
isCoalesceReadWriteEnabled() const3277 bool Session::isCoalesceReadWriteEnabled() const
3278 {
3279     return m_coalesceReadWriteEnabled;
3280 }
3281 
setCoalesceReadWriteEnabled(const bool enabled)3282 void Session::setCoalesceReadWriteEnabled(const bool enabled)
3283 {
3284     if (enabled == m_coalesceReadWriteEnabled) return;
3285 
3286     m_coalesceReadWriteEnabled = enabled;
3287     configureDeferred();
3288 }
3289 
isSuggestModeEnabled() const3290 bool Session::isSuggestModeEnabled() const
3291 {
3292     return m_isSuggestMode;
3293 }
3294 
usePieceExtentAffinity() const3295 bool Session::usePieceExtentAffinity() const
3296 {
3297     return m_usePieceExtentAffinity;
3298 }
3299 
setPieceExtentAffinity(const bool enabled)3300 void Session::setPieceExtentAffinity(const bool enabled)
3301 {
3302     if (enabled == m_usePieceExtentAffinity) return;
3303 
3304     m_usePieceExtentAffinity = enabled;
3305     configureDeferred();
3306 }
3307 
setSuggestMode(const bool mode)3308 void Session::setSuggestMode(const bool mode)
3309 {
3310     if (mode == m_isSuggestMode) return;
3311 
3312     m_isSuggestMode = mode;
3313     configureDeferred();
3314 }
3315 
sendBufferWatermark() const3316 int Session::sendBufferWatermark() const
3317 {
3318     return m_sendBufferWatermark;
3319 }
3320 
setSendBufferWatermark(const int value)3321 void Session::setSendBufferWatermark(const int value)
3322 {
3323     if (value == m_sendBufferWatermark) return;
3324 
3325     m_sendBufferWatermark = value;
3326     configureDeferred();
3327 }
3328 
sendBufferLowWatermark() const3329 int Session::sendBufferLowWatermark() const
3330 {
3331     return m_sendBufferLowWatermark;
3332 }
3333 
setSendBufferLowWatermark(const int value)3334 void Session::setSendBufferLowWatermark(const int value)
3335 {
3336     if (value == m_sendBufferLowWatermark) return;
3337 
3338     m_sendBufferLowWatermark = value;
3339     configureDeferred();
3340 }
3341 
sendBufferWatermarkFactor() const3342 int Session::sendBufferWatermarkFactor() const
3343 {
3344     return m_sendBufferWatermarkFactor;
3345 }
3346 
setSendBufferWatermarkFactor(const int value)3347 void Session::setSendBufferWatermarkFactor(const int value)
3348 {
3349     if (value == m_sendBufferWatermarkFactor) return;
3350 
3351     m_sendBufferWatermarkFactor = value;
3352     configureDeferred();
3353 }
3354 
socketBacklogSize() const3355 int Session::socketBacklogSize() const
3356 {
3357     return m_socketBacklogSize;
3358 }
3359 
setSocketBacklogSize(const int value)3360 void Session::setSocketBacklogSize(const int value)
3361 {
3362     if (value == m_socketBacklogSize) return;
3363 
3364     m_socketBacklogSize = value;
3365     configureDeferred();
3366 }
3367 
isAnonymousModeEnabled() const3368 bool Session::isAnonymousModeEnabled() const
3369 {
3370     return m_isAnonymousModeEnabled;
3371 }
3372 
setAnonymousModeEnabled(const bool enabled)3373 void Session::setAnonymousModeEnabled(const bool enabled)
3374 {
3375     if (enabled != m_isAnonymousModeEnabled)
3376     {
3377         m_isAnonymousModeEnabled = enabled;
3378         configureDeferred();
3379         LogMsg(tr("Anonymous mode [%1]").arg(isAnonymousModeEnabled() ? tr("ON") : tr("OFF"))
3380             , Log::INFO);
3381     }
3382 }
3383 
isQueueingSystemEnabled() const3384 bool Session::isQueueingSystemEnabled() const
3385 {
3386     return m_isQueueingEnabled;
3387 }
3388 
setQueueingSystemEnabled(const bool enabled)3389 void Session::setQueueingSystemEnabled(const bool enabled)
3390 {
3391     if (enabled != m_isQueueingEnabled)
3392     {
3393         m_isQueueingEnabled = enabled;
3394         configureDeferred();
3395 
3396         if (enabled)
3397             saveTorrentsQueue();
3398         else
3399             removeTorrentsQueue();
3400     }
3401 }
3402 
maxActiveDownloads() const3403 int Session::maxActiveDownloads() const
3404 {
3405     return m_maxActiveDownloads;
3406 }
3407 
setMaxActiveDownloads(int max)3408 void Session::setMaxActiveDownloads(int max)
3409 {
3410     max = std::max(max, -1);
3411     if (max != m_maxActiveDownloads)
3412     {
3413         m_maxActiveDownloads = max;
3414         configureDeferred();
3415     }
3416 }
3417 
maxActiveUploads() const3418 int Session::maxActiveUploads() const
3419 {
3420     return m_maxActiveUploads;
3421 }
3422 
setMaxActiveUploads(int max)3423 void Session::setMaxActiveUploads(int max)
3424 {
3425     max = std::max(max, -1);
3426     if (max != m_maxActiveUploads)
3427     {
3428         m_maxActiveUploads = max;
3429         configureDeferred();
3430     }
3431 }
3432 
maxActiveTorrents() const3433 int Session::maxActiveTorrents() const
3434 {
3435     return m_maxActiveTorrents;
3436 }
3437 
setMaxActiveTorrents(int max)3438 void Session::setMaxActiveTorrents(int max)
3439 {
3440     max = std::max(max, -1);
3441     if (max != m_maxActiveTorrents)
3442     {
3443         m_maxActiveTorrents = max;
3444         configureDeferred();
3445     }
3446 }
3447 
ignoreSlowTorrentsForQueueing() const3448 bool Session::ignoreSlowTorrentsForQueueing() const
3449 {
3450     return m_ignoreSlowTorrentsForQueueing;
3451 }
3452 
setIgnoreSlowTorrentsForQueueing(const bool ignore)3453 void Session::setIgnoreSlowTorrentsForQueueing(const bool ignore)
3454 {
3455     if (ignore != m_ignoreSlowTorrentsForQueueing)
3456     {
3457         m_ignoreSlowTorrentsForQueueing = ignore;
3458         configureDeferred();
3459     }
3460 }
3461 
downloadRateForSlowTorrents() const3462 int Session::downloadRateForSlowTorrents() const
3463 {
3464     return m_downloadRateForSlowTorrents;
3465 }
3466 
setDownloadRateForSlowTorrents(const int rateInKibiBytes)3467 void Session::setDownloadRateForSlowTorrents(const int rateInKibiBytes)
3468 {
3469     if (rateInKibiBytes == m_downloadRateForSlowTorrents)
3470         return;
3471 
3472     m_downloadRateForSlowTorrents = rateInKibiBytes;
3473     configureDeferred();
3474 }
3475 
uploadRateForSlowTorrents() const3476 int Session::uploadRateForSlowTorrents() const
3477 {
3478     return m_uploadRateForSlowTorrents;
3479 }
3480 
setUploadRateForSlowTorrents(const int rateInKibiBytes)3481 void Session::setUploadRateForSlowTorrents(const int rateInKibiBytes)
3482 {
3483     if (rateInKibiBytes == m_uploadRateForSlowTorrents)
3484         return;
3485 
3486     m_uploadRateForSlowTorrents = rateInKibiBytes;
3487     configureDeferred();
3488 }
3489 
slowTorrentsInactivityTimer() const3490 int Session::slowTorrentsInactivityTimer() const
3491 {
3492     return m_slowTorrentsInactivityTimer;
3493 }
3494 
setSlowTorrentsInactivityTimer(const int timeInSeconds)3495 void Session::setSlowTorrentsInactivityTimer(const int timeInSeconds)
3496 {
3497     if (timeInSeconds == m_slowTorrentsInactivityTimer)
3498         return;
3499 
3500     m_slowTorrentsInactivityTimer = timeInSeconds;
3501     configureDeferred();
3502 }
3503 
outgoingPortsMin() const3504 int Session::outgoingPortsMin() const
3505 {
3506     return m_outgoingPortsMin;
3507 }
3508 
setOutgoingPortsMin(const int min)3509 void Session::setOutgoingPortsMin(const int min)
3510 {
3511     if (min != m_outgoingPortsMin)
3512     {
3513         m_outgoingPortsMin = min;
3514         configureDeferred();
3515     }
3516 }
3517 
outgoingPortsMax() const3518 int Session::outgoingPortsMax() const
3519 {
3520     return m_outgoingPortsMax;
3521 }
3522 
setOutgoingPortsMax(const int max)3523 void Session::setOutgoingPortsMax(const int max)
3524 {
3525     if (max != m_outgoingPortsMax)
3526     {
3527         m_outgoingPortsMax = max;
3528         configureDeferred();
3529     }
3530 }
3531 
UPnPLeaseDuration() const3532 int Session::UPnPLeaseDuration() const
3533 {
3534     return m_UPnPLeaseDuration;
3535 }
3536 
setUPnPLeaseDuration(const int duration)3537 void Session::setUPnPLeaseDuration(const int duration)
3538 {
3539     if (duration != m_UPnPLeaseDuration)
3540     {
3541         m_UPnPLeaseDuration = duration;
3542         configureDeferred();
3543     }
3544 }
3545 
peerToS() const3546 int Session::peerToS() const
3547 {
3548     return m_peerToS;
3549 }
3550 
setPeerToS(const int value)3551 void Session::setPeerToS(const int value)
3552 {
3553     if (value == m_peerToS)
3554         return;
3555 
3556     m_peerToS = value;
3557     configureDeferred();
3558 }
3559 
ignoreLimitsOnLAN() const3560 bool Session::ignoreLimitsOnLAN() const
3561 {
3562     return m_ignoreLimitsOnLAN;
3563 }
3564 
setIgnoreLimitsOnLAN(const bool ignore)3565 void Session::setIgnoreLimitsOnLAN(const bool ignore)
3566 {
3567     if (ignore != m_ignoreLimitsOnLAN)
3568     {
3569         m_ignoreLimitsOnLAN = ignore;
3570         configureDeferred();
3571     }
3572 }
3573 
includeOverheadInLimits() const3574 bool Session::includeOverheadInLimits() const
3575 {
3576     return m_includeOverheadInLimits;
3577 }
3578 
setIncludeOverheadInLimits(const bool include)3579 void Session::setIncludeOverheadInLimits(const bool include)
3580 {
3581     if (include != m_includeOverheadInLimits)
3582     {
3583         m_includeOverheadInLimits = include;
3584         configureDeferred();
3585     }
3586 }
3587 
announceIP() const3588 QString Session::announceIP() const
3589 {
3590     return m_announceIP;
3591 }
3592 
setAnnounceIP(const QString & ip)3593 void Session::setAnnounceIP(const QString &ip)
3594 {
3595     if (ip != m_announceIP)
3596     {
3597         m_announceIP = ip;
3598         configureDeferred();
3599     }
3600 }
3601 
maxConcurrentHTTPAnnounces() const3602 int Session::maxConcurrentHTTPAnnounces() const
3603 {
3604     return m_maxConcurrentHTTPAnnounces;
3605 }
3606 
setMaxConcurrentHTTPAnnounces(const int value)3607 void Session::setMaxConcurrentHTTPAnnounces(const int value)
3608 {
3609     if (value == m_maxConcurrentHTTPAnnounces)
3610         return;
3611 
3612     m_maxConcurrentHTTPAnnounces = value;
3613     configureDeferred();
3614 }
3615 
stopTrackerTimeout() const3616 int Session::stopTrackerTimeout() const
3617 {
3618     return m_stopTrackerTimeout;
3619 }
3620 
setStopTrackerTimeout(const int value)3621 void Session::setStopTrackerTimeout(const int value)
3622 {
3623     if (value == m_stopTrackerTimeout)
3624         return;
3625 
3626     m_stopTrackerTimeout = value;
3627     configureDeferred();
3628 }
3629 
maxConnections() const3630 int Session::maxConnections() const
3631 {
3632     return m_maxConnections;
3633 }
3634 
setMaxConnections(int max)3635 void Session::setMaxConnections(int max)
3636 {
3637     max = (max > 0) ? max : -1;
3638     if (max != m_maxConnections)
3639     {
3640         m_maxConnections = max;
3641         configureDeferred();
3642     }
3643 }
3644 
maxUploads() const3645 int Session::maxUploads() const
3646 {
3647     return m_maxUploads;
3648 }
3649 
setMaxUploads(int max)3650 void Session::setMaxUploads(int max)
3651 {
3652     max = (max > 0) ? max : -1;
3653     if (max != m_maxUploads)
3654     {
3655         m_maxUploads = max;
3656         configureDeferred();
3657     }
3658 }
3659 
btProtocol() const3660 BTProtocol Session::btProtocol() const
3661 {
3662     return m_btProtocol;
3663 }
3664 
setBTProtocol(const BTProtocol protocol)3665 void Session::setBTProtocol(const BTProtocol protocol)
3666 {
3667     if ((protocol < BTProtocol::Both) || (BTProtocol::UTP < protocol))
3668         return;
3669 
3670     if (protocol == m_btProtocol) return;
3671 
3672     m_btProtocol = protocol;
3673     configureDeferred();
3674 }
3675 
isUTPRateLimited() const3676 bool Session::isUTPRateLimited() const
3677 {
3678     return m_isUTPRateLimited;
3679 }
3680 
setUTPRateLimited(const bool limited)3681 void Session::setUTPRateLimited(const bool limited)
3682 {
3683     if (limited != m_isUTPRateLimited)
3684     {
3685         m_isUTPRateLimited = limited;
3686         configureDeferred();
3687     }
3688 }
3689 
utpMixedMode() const3690 MixedModeAlgorithm Session::utpMixedMode() const
3691 {
3692     return m_utpMixedMode;
3693 }
3694 
setUtpMixedMode(const MixedModeAlgorithm mode)3695 void Session::setUtpMixedMode(const MixedModeAlgorithm mode)
3696 {
3697     if (mode == m_utpMixedMode) return;
3698 
3699     m_utpMixedMode = mode;
3700     configureDeferred();
3701 }
3702 
isIDNSupportEnabled() const3703 bool Session::isIDNSupportEnabled() const
3704 {
3705     return m_IDNSupportEnabled;
3706 }
3707 
setIDNSupportEnabled(const bool enabled)3708 void Session::setIDNSupportEnabled(const bool enabled)
3709 {
3710     if (enabled == m_IDNSupportEnabled) return;
3711 
3712     m_IDNSupportEnabled = enabled;
3713     configureDeferred();
3714 }
3715 
multiConnectionsPerIpEnabled() const3716 bool Session::multiConnectionsPerIpEnabled() const
3717 {
3718     return m_multiConnectionsPerIpEnabled;
3719 }
3720 
setMultiConnectionsPerIpEnabled(const bool enabled)3721 void Session::setMultiConnectionsPerIpEnabled(const bool enabled)
3722 {
3723     if (enabled == m_multiConnectionsPerIpEnabled) return;
3724 
3725     m_multiConnectionsPerIpEnabled = enabled;
3726     configureDeferred();
3727 }
3728 
validateHTTPSTrackerCertificate() const3729 bool Session::validateHTTPSTrackerCertificate() const
3730 {
3731     return m_validateHTTPSTrackerCertificate;
3732 }
3733 
setValidateHTTPSTrackerCertificate(const bool enabled)3734 void Session::setValidateHTTPSTrackerCertificate(const bool enabled)
3735 {
3736     if (enabled == m_validateHTTPSTrackerCertificate) return;
3737 
3738     m_validateHTTPSTrackerCertificate = enabled;
3739     configureDeferred();
3740 }
3741 
isSSRFMitigationEnabled() const3742 bool Session::isSSRFMitigationEnabled() const
3743 {
3744     return m_SSRFMitigationEnabled;
3745 }
3746 
setSSRFMitigationEnabled(const bool enabled)3747 void Session::setSSRFMitigationEnabled(const bool enabled)
3748 {
3749     if (enabled == m_SSRFMitigationEnabled) return;
3750 
3751     m_SSRFMitigationEnabled = enabled;
3752     configureDeferred();
3753 }
3754 
blockPeersOnPrivilegedPorts() const3755 bool Session::blockPeersOnPrivilegedPorts() const
3756 {
3757     return m_blockPeersOnPrivilegedPorts;
3758 }
3759 
setBlockPeersOnPrivilegedPorts(const bool enabled)3760 void Session::setBlockPeersOnPrivilegedPorts(const bool enabled)
3761 {
3762     if (enabled == m_blockPeersOnPrivilegedPorts) return;
3763 
3764     m_blockPeersOnPrivilegedPorts = enabled;
3765     configureDeferred();
3766 }
3767 
isTrackerFilteringEnabled() const3768 bool Session::isTrackerFilteringEnabled() const
3769 {
3770     return m_isTrackerFilteringEnabled;
3771 }
3772 
setTrackerFilteringEnabled(const bool enabled)3773 void Session::setTrackerFilteringEnabled(const bool enabled)
3774 {
3775     if (enabled != m_isTrackerFilteringEnabled)
3776     {
3777         m_isTrackerFilteringEnabled = enabled;
3778         configureDeferred();
3779     }
3780 }
3781 
isListening() const3782 bool Session::isListening() const
3783 {
3784     return m_nativeSession->is_listening();
3785 }
3786 
maxRatioAction() const3787 MaxRatioAction Session::maxRatioAction() const
3788 {
3789     return static_cast<MaxRatioAction>(m_maxRatioAction.get());
3790 }
3791 
setMaxRatioAction(const MaxRatioAction act)3792 void Session::setMaxRatioAction(const MaxRatioAction act)
3793 {
3794     m_maxRatioAction = static_cast<int>(act);
3795 }
3796 
3797 // If this functions returns true, we cannot add torrent to session,
3798 // but it is still possible to merge trackers in some cases
isKnownTorrent(const TorrentID & id) const3799 bool Session::isKnownTorrent(const TorrentID &id) const
3800 {
3801     return (m_torrents.contains(id)
3802             || m_loadingTorrents.contains(id)
3803             || m_downloadedMetadata.contains(id));
3804 }
3805 
updateSeedingLimitTimer()3806 void Session::updateSeedingLimitTimer()
3807 {
3808     if ((globalMaxRatio() == Torrent::NO_RATIO_LIMIT) && !hasPerTorrentRatioLimit()
3809         && (globalMaxSeedingMinutes() == Torrent::NO_SEEDING_TIME_LIMIT) && !hasPerTorrentSeedingTimeLimit())
3810         {
3811         if (m_seedingLimitTimer->isActive())
3812             m_seedingLimitTimer->stop();
3813     }
3814     else if (!m_seedingLimitTimer->isActive())
3815     {
3816         m_seedingLimitTimer->start();
3817     }
3818 }
3819 
handleTorrentShareLimitChanged(TorrentImpl * const torrent)3820 void Session::handleTorrentShareLimitChanged(TorrentImpl *const torrent)
3821 {
3822     updateSeedingLimitTimer();
3823 }
3824 
handleTorrentNameChanged(TorrentImpl * const torrent)3825 void Session::handleTorrentNameChanged(TorrentImpl *const torrent)
3826 {
3827     Q_UNUSED(torrent);
3828 }
3829 
handleTorrentSavePathChanged(TorrentImpl * const torrent)3830 void Session::handleTorrentSavePathChanged(TorrentImpl *const torrent)
3831 {
3832     emit torrentSavePathChanged(torrent);
3833 }
3834 
handleTorrentCategoryChanged(TorrentImpl * const torrent,const QString & oldCategory)3835 void Session::handleTorrentCategoryChanged(TorrentImpl *const torrent, const QString &oldCategory)
3836 {
3837     emit torrentCategoryChanged(torrent, oldCategory);
3838 }
3839 
handleTorrentTagAdded(TorrentImpl * const torrent,const QString & tag)3840 void Session::handleTorrentTagAdded(TorrentImpl *const torrent, const QString &tag)
3841 {
3842     emit torrentTagAdded(torrent, tag);
3843 }
3844 
handleTorrentTagRemoved(TorrentImpl * const torrent,const QString & tag)3845 void Session::handleTorrentTagRemoved(TorrentImpl *const torrent, const QString &tag)
3846 {
3847     emit torrentTagRemoved(torrent, tag);
3848 }
3849 
handleTorrentSavingModeChanged(TorrentImpl * const torrent)3850 void Session::handleTorrentSavingModeChanged(TorrentImpl *const torrent)
3851 {
3852     emit torrentSavingModeChanged(torrent);
3853 }
3854 
handleTorrentTrackersAdded(TorrentImpl * const torrent,const QVector<TrackerEntry> & newTrackers)3855 void Session::handleTorrentTrackersAdded(TorrentImpl *const torrent, const QVector<TrackerEntry> &newTrackers)
3856 {
3857     for (const TrackerEntry &newTracker : newTrackers)
3858         LogMsg(tr("Tracker '%1' was added to torrent '%2'").arg(newTracker.url, torrent->name()));
3859     emit trackersAdded(torrent, newTrackers);
3860     if (torrent->trackers().size() == newTrackers.size())
3861         emit trackerlessStateChanged(torrent, false);
3862     emit trackersChanged(torrent);
3863 }
3864 
handleTorrentTrackersRemoved(TorrentImpl * const torrent,const QVector<TrackerEntry> & deletedTrackers)3865 void Session::handleTorrentTrackersRemoved(TorrentImpl *const torrent, const QVector<TrackerEntry> &deletedTrackers)
3866 {
3867     for (const TrackerEntry &deletedTracker : deletedTrackers)
3868         LogMsg(tr("Tracker '%1' was deleted from torrent '%2'").arg(deletedTracker.url, torrent->name()));
3869     emit trackersRemoved(torrent, deletedTrackers);
3870     if (torrent->trackers().empty())
3871         emit trackerlessStateChanged(torrent, true);
3872     emit trackersChanged(torrent);
3873 }
3874 
handleTorrentTrackersChanged(TorrentImpl * const torrent)3875 void Session::handleTorrentTrackersChanged(TorrentImpl *const torrent)
3876 {
3877     emit trackersChanged(torrent);
3878 }
3879 
handleTorrentUrlSeedsAdded(TorrentImpl * const torrent,const QVector<QUrl> & newUrlSeeds)3880 void Session::handleTorrentUrlSeedsAdded(TorrentImpl *const torrent, const QVector<QUrl> &newUrlSeeds)
3881 {
3882     for (const QUrl &newUrlSeed : newUrlSeeds)
3883         LogMsg(tr("URL seed '%1' was added to torrent '%2'").arg(newUrlSeed.toString(), torrent->name()));
3884 }
3885 
handleTorrentUrlSeedsRemoved(TorrentImpl * const torrent,const QVector<QUrl> & urlSeeds)3886 void Session::handleTorrentUrlSeedsRemoved(TorrentImpl *const torrent, const QVector<QUrl> &urlSeeds)
3887 {
3888     for (const QUrl &urlSeed : urlSeeds)
3889         LogMsg(tr("URL seed '%1' was removed from torrent '%2'").arg(urlSeed.toString(), torrent->name()));
3890 }
3891 
handleTorrentMetadataReceived(TorrentImpl * const torrent)3892 void Session::handleTorrentMetadataReceived(TorrentImpl *const torrent)
3893 {
3894     // Save metadata
3895     const QDir resumeDataDir {m_resumeFolderPath};
3896     const QString torrentFileName {QString {"%1.torrent"}.arg(torrent->id().toString())};
3897     try
3898     {
3899         torrent->info().saveToFile(resumeDataDir.absoluteFilePath(torrentFileName));
3900         // Copy the torrent file to the export folder
3901         if (!torrentExportDirectory().isEmpty())
3902             exportTorrentFile(torrent);
3903     }
3904     catch (const RuntimeError &err)
3905     {
3906         LogMsg(tr("Couldn't save torrent metadata file '%1'. Reason: %2")
3907                .arg(torrentFileName, err.message()), Log::CRITICAL);
3908     }
3909 
3910     emit torrentMetadataReceived(torrent);
3911 }
3912 
handleTorrentPaused(TorrentImpl * const torrent)3913 void Session::handleTorrentPaused(TorrentImpl *const torrent)
3914 {
3915     emit torrentPaused(torrent);
3916 }
3917 
handleTorrentResumed(TorrentImpl * const torrent)3918 void Session::handleTorrentResumed(TorrentImpl *const torrent)
3919 {
3920     emit torrentResumed(torrent);
3921 }
3922 
handleTorrentChecked(TorrentImpl * const torrent)3923 void Session::handleTorrentChecked(TorrentImpl *const torrent)
3924 {
3925     emit torrentFinishedChecking(torrent);
3926 }
3927 
handleTorrentFinished(TorrentImpl * const torrent)3928 void Session::handleTorrentFinished(TorrentImpl *const torrent)
3929 {
3930     emit torrentFinished(torrent);
3931 
3932     qDebug("Checking if the torrent contains torrent files to download");
3933     // Check if there are torrent files inside
3934     for (int i = 0; i < torrent->filesCount(); ++i)
3935     {
3936         const QString torrentRelpath = torrent->filePath(i);
3937         if (torrentRelpath.endsWith(".torrent", Qt::CaseInsensitive))
3938         {
3939             qDebug("Found possible recursive torrent download.");
3940             const QString torrentFullpath = torrent->savePath(true) + '/' + torrentRelpath;
3941             qDebug("Full subtorrent path is %s", qUtf8Printable(torrentFullpath));
3942             TorrentInfo torrentInfo = TorrentInfo::loadFromFile(torrentFullpath);
3943             if (torrentInfo.isValid())
3944             {
3945                 qDebug("emitting recursiveTorrentDownloadPossible()");
3946                 emit recursiveTorrentDownloadPossible(torrent);
3947                 break;
3948             }
3949             else
3950             {
3951                 qDebug("Caught error loading torrent");
3952                 LogMsg(tr("Unable to decode '%1' torrent file.").arg(Utils::Fs::toNativePath(torrentFullpath)), Log::CRITICAL);
3953             }
3954         }
3955     }
3956 
3957     // Move .torrent file to another folder
3958     if (!finishedTorrentExportDirectory().isEmpty())
3959         exportTorrentFile(torrent, TorrentExportFolder::Finished);
3960 
3961     if (!hasUnfinishedTorrents())
3962         emit allTorrentsFinished();
3963 }
3964 
handleTorrentResumeDataReady(TorrentImpl * const torrent,const LoadTorrentParams & data)3965 void Session::handleTorrentResumeDataReady(TorrentImpl *const torrent, const LoadTorrentParams &data)
3966 {
3967     --m_numResumeData;
3968 
3969     // We need to adjust native libtorrent resume data
3970     lt::add_torrent_params p = data.ltAddTorrentParams;
3971     p.save_path = Profile::instance()->toPortablePath(QString::fromStdString(p.save_path)).toStdString();
3972     if (data.paused)
3973     {
3974         p.flags |= lt::torrent_flags::paused;
3975         p.flags &= ~lt::torrent_flags::auto_managed;
3976     }
3977     else
3978     {
3979         // Torrent can be actually "running" but temporarily "paused" to perform some
3980         // service jobs behind the scenes so we need to restore it as "running"
3981         if (!data.forced)
3982         {
3983             p.flags |= lt::torrent_flags::auto_managed;
3984         }
3985         else
3986         {
3987             p.flags &= ~lt::torrent_flags::paused;
3988             p.flags &= ~lt::torrent_flags::auto_managed;
3989         }
3990     }
3991 
3992     // Separated thread is used for the blocking IO which results in slow processing of many torrents.
3993     // Copying lt::entry objects around isn't cheap.
3994 
3995     auto resumeDataPtr = std::make_shared<lt::entry>(lt::write_resume_data(p));
3996     lt::entry &resumeData = *resumeDataPtr;
3997 
3998     resumeData["qBt-savePath"] = Profile::instance()->toPortablePath(data.savePath).toStdString();
3999     resumeData["qBt-ratioLimit"] = static_cast<int>(data.ratioLimit * 1000);
4000     resumeData["qBt-seedingTimeLimit"] = data.seedingTimeLimit;
4001     resumeData["qBt-category"] = data.category.toStdString();
4002     resumeData["qBt-tags"] = setToEntryList(data.tags);
4003     resumeData["qBt-name"] = data.name.toStdString();
4004     resumeData["qBt-seedStatus"] = data.hasSeedStatus;
4005     resumeData["qBt-contentLayout"] = Utils::String::fromEnum(data.contentLayout).toStdString();
4006     resumeData["qBt-firstLastPiecePriority"] = data.firstLastPiecePriority;
4007 
4008     const QString filename = QString::fromLatin1("%1.fastresume").arg(torrent->id().toString());
4009     QMetaObject::invokeMethod(m_resumeDataSavingManager
4010         , [this, filename, resumeDataPtr]() { m_resumeDataSavingManager->save(filename, resumeDataPtr); });
4011 }
4012 
handleTorrentTrackerReply(TorrentImpl * const torrent,const QString & trackerUrl)4013 void Session::handleTorrentTrackerReply(TorrentImpl *const torrent, const QString &trackerUrl)
4014 {
4015     emit trackerSuccess(torrent, trackerUrl);
4016 }
4017 
handleTorrentTrackerError(TorrentImpl * const torrent,const QString & trackerUrl)4018 void Session::handleTorrentTrackerError(TorrentImpl *const torrent, const QString &trackerUrl)
4019 {
4020     emit trackerError(torrent, trackerUrl);
4021 }
4022 
addMoveTorrentStorageJob(TorrentImpl * torrent,const QString & newPath,const MoveStorageMode mode)4023 bool Session::addMoveTorrentStorageJob(TorrentImpl *torrent, const QString &newPath, const MoveStorageMode mode)
4024 {
4025     Q_ASSERT(torrent);
4026 
4027     const lt::torrent_handle torrentHandle = torrent->nativeHandle();
4028     const QString currentLocation = torrent->actualStorageLocation();
4029 
4030     if (m_moveStorageQueue.size() > 1)
4031     {
4032         const auto iter = std::find_if(m_moveStorageQueue.begin() + 1, m_moveStorageQueue.end()
4033                                  , [&torrentHandle](const MoveStorageJob &job)
4034         {
4035             return job.torrentHandle == torrentHandle;
4036         });
4037 
4038         if (iter != m_moveStorageQueue.end())
4039         {
4040             // remove existing inactive job
4041             m_moveStorageQueue.erase(iter);
4042             LogMsg(tr("Cancelled moving \"%1\" from \"%2\" to \"%3\".").arg(torrent->name(), currentLocation, iter->path));
4043         }
4044     }
4045 
4046     if (!m_moveStorageQueue.isEmpty() && (m_moveStorageQueue.first().torrentHandle == torrentHandle))
4047     {
4048         // if there is active job for this torrent prevent creating meaningless
4049         // job that will move torrent to the same location as current one
4050         if (QDir {m_moveStorageQueue.first().path} == QDir {newPath})
4051         {
4052             LogMsg(tr("Couldn't enqueue move of \"%1\" to \"%2\". Torrent is currently moving to the same destination location.")
4053                    .arg(torrent->name(), newPath));
4054             return false;
4055         }
4056     }
4057     else
4058     {
4059         if (QDir {currentLocation} == QDir {newPath})
4060         {
4061             LogMsg(tr("Couldn't enqueue move of \"%1\" from \"%2\" to \"%3\". Both paths point to the same location.")
4062                    .arg(torrent->name(), currentLocation, newPath));
4063             return false;
4064         }
4065     }
4066 
4067     const MoveStorageJob moveStorageJob {torrentHandle, newPath, mode};
4068     m_moveStorageQueue << moveStorageJob;
4069     LogMsg(tr("Enqueued to move \"%1\" from \"%2\" to \"%3\".").arg(torrent->name(), currentLocation, newPath));
4070 
4071     if (m_moveStorageQueue.size() == 1)
4072         moveTorrentStorage(moveStorageJob);
4073 
4074     return true;
4075 }
4076 
moveTorrentStorage(const MoveStorageJob & job) const4077 void Session::moveTorrentStorage(const MoveStorageJob &job) const
4078 {
4079 #if (LIBTORRENT_VERSION_NUM >= 20000)
4080     const auto id = TorrentID::fromInfoHash(job.torrentHandle.info_hashes());
4081 #else
4082     const auto id = TorrentID::fromInfoHash(job.torrentHandle.info_hash());
4083 #endif
4084     const TorrentImpl *torrent = m_torrents.value(id);
4085     const QString torrentName = (torrent ? torrent->name() : id.toString());
4086     LogMsg(tr("Moving \"%1\" to \"%2\"...").arg(torrentName, job.path));
4087 
4088     job.torrentHandle.move_storage(job.path.toUtf8().constData()
4089                             , ((job.mode == MoveStorageMode::Overwrite)
4090                             ? lt::move_flags_t::always_replace_files : lt::move_flags_t::dont_replace));
4091 }
4092 
handleMoveTorrentStorageJobFinished()4093 void Session::handleMoveTorrentStorageJobFinished()
4094 {
4095     const MoveStorageJob finishedJob = m_moveStorageQueue.takeFirst();
4096     if (!m_moveStorageQueue.isEmpty())
4097         moveTorrentStorage(m_moveStorageQueue.first());
4098 
4099     const auto iter = std::find_if(m_moveStorageQueue.cbegin(), m_moveStorageQueue.cend()
4100                                    , [&finishedJob](const MoveStorageJob &job)
4101     {
4102         return job.torrentHandle == finishedJob.torrentHandle;
4103     });
4104 
4105     const bool torrentHasOutstandingJob = (iter != m_moveStorageQueue.cend());
4106 
4107     TorrentImpl *torrent = m_torrents.value(finishedJob.torrentHandle.info_hash());
4108     if (torrent)
4109     {
4110         torrent->handleMoveStorageJobFinished(torrentHasOutstandingJob);
4111     }
4112     else if (!torrentHasOutstandingJob)
4113     {
4114         // Last job is completed for torrent that being removing, so actually remove it
4115         const lt::torrent_handle nativeHandle {finishedJob.torrentHandle};
4116         const RemovingTorrentData &removingTorrentData = m_removingTorrents[nativeHandle.info_hash()];
4117         if (removingTorrentData.deleteOption == DeleteTorrent)
4118             m_nativeSession->remove_torrent(nativeHandle, lt::session::delete_partfile);
4119     }
4120 }
4121 
handleTorrentTrackerWarning(TorrentImpl * const torrent,const QString & trackerUrl)4122 void Session::handleTorrentTrackerWarning(TorrentImpl *const torrent, const QString &trackerUrl)
4123 {
4124     emit trackerWarning(torrent, trackerUrl);
4125 }
4126 
hasPerTorrentRatioLimit() const4127 bool Session::hasPerTorrentRatioLimit() const
4128 {
4129     return std::any_of(m_torrents.cbegin(), m_torrents.cend(), [](const TorrentImpl *torrent)
4130     {
4131         return (torrent->ratioLimit() >= 0);
4132     });
4133 }
4134 
hasPerTorrentSeedingTimeLimit() const4135 bool Session::hasPerTorrentSeedingTimeLimit() const
4136 {
4137     return std::any_of(m_torrents.cbegin(), m_torrents.cend(), [](const TorrentImpl *torrent)
4138     {
4139         return (torrent->seedingTimeLimit() >= 0);
4140     });
4141 }
4142 
initResumeFolder()4143 void Session::initResumeFolder()
4144 {
4145     m_resumeFolderPath = Utils::Fs::expandPathAbs(specialFolderLocation(SpecialFolder::Data) + RESUME_FOLDER);
4146     const QDir resumeFolderDir(m_resumeFolderPath);
4147     if (resumeFolderDir.exists() || resumeFolderDir.mkpath(resumeFolderDir.absolutePath()))
4148     {
4149         m_resumeFolderLock->setFileName(resumeFolderDir.absoluteFilePath("session.lock"));
4150         if (!m_resumeFolderLock->open(QFile::WriteOnly))
4151         {
4152             throw RuntimeError
4153             {tr("Cannot write to torrent resume folder: \"%1\"")
4154                 .arg(Utils::Fs::toNativePath(m_resumeFolderPath))};
4155         }
4156     }
4157     else
4158     {
4159         throw RuntimeError
4160         {tr("Cannot create torrent resume folder: \"%1\"")
4161             .arg(Utils::Fs::toNativePath(m_resumeFolderPath))};
4162     }
4163 }
4164 
configureDeferred()4165 void Session::configureDeferred()
4166 {
4167     if (m_deferredConfigureScheduled)
4168         return;
4169 
4170     m_deferredConfigureScheduled = true;
4171     QMetaObject::invokeMethod(this, qOverload<>(&Session::configure), Qt::QueuedConnection);
4172 }
4173 
4174 // Enable IP Filtering
4175 // this method creates ban list from scratch combining user ban list and 3rd party ban list file
enableIPFilter()4176 void Session::enableIPFilter()
4177 {
4178     qDebug("Enabling IPFilter");
4179     // 1. Parse the IP filter
4180     // 2. In the slot add the manually banned IPs to the provided lt::ip_filter
4181     // 3. Set the ip_filter in one go so there isn't a time window where there isn't an ip_filter
4182     //    set between clearing the old one and setting the new one.
4183     if (!m_filterParser)
4184     {
4185         m_filterParser = new FilterParserThread(this);
4186         connect(m_filterParser.data(), &FilterParserThread::IPFilterParsed, this, &Session::handleIPFilterParsed);
4187         connect(m_filterParser.data(), &FilterParserThread::IPFilterError, this, &Session::handleIPFilterError);
4188     }
4189     m_filterParser->processFilterFile(IPFilterFile());
4190 }
4191 
4192 // Disable IP Filtering
disableIPFilter()4193 void Session::disableIPFilter()
4194 {
4195     qDebug("Disabling IPFilter");
4196     if (m_filterParser)
4197     {
4198         disconnect(m_filterParser.data(), nullptr, this, nullptr);
4199         delete m_filterParser;
4200     }
4201 
4202     // Add the banned IPs after the IPFilter disabling
4203     // which creates an empty filter and overrides all previously
4204     // applied bans.
4205     lt::ip_filter filter;
4206     processBannedIPs(filter);
4207     m_nativeSession->set_ip_filter(filter);
4208 }
4209 
recursiveTorrentDownload(const TorrentID & id)4210 void Session::recursiveTorrentDownload(const TorrentID &id)
4211 {
4212     TorrentImpl *const torrent = m_torrents.value(id);
4213     if (!torrent) return;
4214 
4215     for (int i = 0; i < torrent->filesCount(); ++i)
4216     {
4217         const QString torrentRelpath = torrent->filePath(i);
4218         if (torrentRelpath.endsWith(".torrent"))
4219         {
4220             LogMsg(tr("Recursive download of file '%1' embedded in torrent '%2'"
4221                     , "Recursive download of 'test.torrent' embedded in torrent 'test2'")
4222                 .arg(Utils::Fs::toNativePath(torrentRelpath), torrent->name()));
4223             const QString torrentFullpath = torrent->savePath() + '/' + torrentRelpath;
4224 
4225             AddTorrentParams params;
4226             // Passing the save path along to the sub torrent file
4227             params.savePath = torrent->savePath();
4228             addTorrent(TorrentInfo::loadFromFile(torrentFullpath), params);
4229         }
4230     }
4231 }
4232 
status() const4233 const SessionStatus &Session::status() const
4234 {
4235     return m_status;
4236 }
4237 
cacheStatus() const4238 const CacheStatus &Session::cacheStatus() const
4239 {
4240     return m_cacheStatus;
4241 }
4242 
loadTorrentResumeData(const QByteArray & data,const TorrentInfo & metadata,LoadTorrentParams & torrentParams)4243 bool Session::loadTorrentResumeData(const QByteArray &data, const TorrentInfo &metadata, LoadTorrentParams &torrentParams)
4244 {
4245     torrentParams = {};
4246 
4247     lt::error_code ec;
4248     const lt::bdecode_node root = lt::bdecode(data, ec);
4249     if (ec || (root.type() != lt::bdecode_node::dict_t)) return false;
4250 
4251     torrentParams.restored = true;
4252     torrentParams.category = fromLTString(root.dict_find_string_value("qBt-category"));
4253     torrentParams.name = fromLTString(root.dict_find_string_value("qBt-name"));
4254     torrentParams.savePath = Profile::instance()->fromPortablePath(
4255         Utils::Fs::toUniformPath(fromLTString(root.dict_find_string_value("qBt-savePath"))));
4256     torrentParams.hasSeedStatus = root.dict_find_int_value("qBt-seedStatus");
4257     torrentParams.firstLastPiecePriority = root.dict_find_int_value("qBt-firstLastPiecePriority");
4258     torrentParams.seedingTimeLimit = root.dict_find_int_value("qBt-seedingTimeLimit", Torrent::USE_GLOBAL_SEEDING_TIME);
4259 
4260     // TODO: The following code is deprecated. Replace with the commented one after several releases in 4.4.x.
4261     // === BEGIN DEPRECATED CODE === //
4262     const lt::bdecode_node contentLayoutNode = root.dict_find("qBt-contentLayout");
4263     if (contentLayoutNode.type() == lt::bdecode_node::string_t)
4264     {
4265         const QString contentLayoutStr = fromLTString(contentLayoutNode.string_value());
4266         torrentParams.contentLayout = Utils::String::toEnum(contentLayoutStr, TorrentContentLayout::Original);
4267     }
4268     else
4269     {
4270         const bool hasRootFolder = root.dict_find_int_value("qBt-hasRootFolder");
4271         torrentParams.contentLayout = (hasRootFolder ? TorrentContentLayout::Original : TorrentContentLayout::NoSubfolder);
4272     }
4273     // === END DEPRECATED CODE === //
4274     // === BEGIN REPLACEMENT CODE === //
4275 //    torrentParams.contentLayout = Utils::String::parse(
4276 //                fromLTString(root.dict_find_string_value("qBt-contentLayout")), TorrentContentLayout::Default);
4277     // === END REPLACEMENT CODE === //
4278 
4279     const lt::string_view ratioLimitString = root.dict_find_string_value("qBt-ratioLimit");
4280     if (ratioLimitString.empty())
4281         torrentParams.ratioLimit = root.dict_find_int_value("qBt-ratioLimit", Torrent::USE_GLOBAL_RATIO * 1000) / 1000.0;
4282     else
4283         torrentParams.ratioLimit = fromLTString(ratioLimitString).toDouble();
4284 
4285     const lt::bdecode_node tagsNode = root.dict_find("qBt-tags");
4286     if (tagsNode.type() == lt::bdecode_node::list_t)
4287     {
4288         for (int i = 0; i < tagsNode.list_size(); ++i)
4289         {
4290             const QString tag = fromLTString(tagsNode.list_string_value_at(i));
4291             if (Session::isValidTag(tag))
4292                 torrentParams.tags << tag;
4293         }
4294     }
4295 
4296     // NOTE: Do we really need the following block in case of existing (restored) torrent?
4297     torrentParams.savePath = normalizePath(torrentParams.savePath);
4298     if (!torrentParams.category.isEmpty())
4299     {
4300         if (!m_categories.contains(torrentParams.category) && !addCategory(torrentParams.category))
4301             torrentParams.category = "";
4302     }
4303 
4304     lt::add_torrent_params &p = torrentParams.ltAddTorrentParams;
4305 
4306     p = lt::read_resume_data(root, ec);
4307     p.save_path = Profile::instance()->fromPortablePath(fromLTString(p.save_path)).toStdString();
4308     if (metadata.isValid())
4309         p.ti = metadata.nativeInfo();
4310 
4311     if (p.flags & lt::torrent_flags::stop_when_ready)
4312     {
4313         // If torrent has "stop_when_ready" flag set then it is actually "stopped"
4314         torrentParams.paused = true;
4315         torrentParams.forced = false;
4316         // ...but temporarily "resumed" to perform some service jobs (e.g. checking)
4317         p.flags &= ~lt::torrent_flags::paused;
4318         p.flags |= lt::torrent_flags::auto_managed;
4319     }
4320     else
4321     {
4322         torrentParams.paused = (p.flags & lt::torrent_flags::paused) && !(p.flags & lt::torrent_flags::auto_managed);
4323         torrentParams.forced = !(p.flags & lt::torrent_flags::paused) && !(p.flags & lt::torrent_flags::auto_managed);
4324     }
4325 
4326     const bool hasMetadata = (p.ti && p.ti->is_valid());
4327     if (!hasMetadata && !root.dict_find("info-hash"))
4328         return false;
4329 
4330     return true;
4331 }
4332 
4333 // Will resume torrents in backup directory
startUpTorrents()4334 void Session::startUpTorrents()
4335 {
4336     const QDir resumeDataDir {m_resumeFolderPath};
4337     QStringList fastresumes = resumeDataDir.entryList(
4338                 QStringList(QLatin1String("*.fastresume")), QDir::Files, QDir::Unsorted);
4339 
4340     const auto readFile = [](const QString &path, QByteArray &buf) -> bool
4341     {
4342         QFile file(path);
4343         if (!file.open(QIODevice::ReadOnly))
4344         {
4345             LogMsg(tr("Cannot read file %1: %2").arg(path, file.errorString()), Log::WARNING);
4346             return false;
4347         }
4348 
4349         buf = file.readAll();
4350         return true;
4351     };
4352 
4353     qDebug("Starting up torrents...");
4354     qDebug("Queue size: %d", fastresumes.size());
4355 
4356     const QRegularExpression rx(QLatin1String("^([A-Fa-f0-9]{40})\\.fastresume$"));
4357 
4358     if (isQueueingSystemEnabled())
4359     {
4360         QFile queueFile {resumeDataDir.absoluteFilePath(QLatin1String {"queue"})};
4361         QStringList queue;
4362         if (queueFile.open(QFile::ReadOnly))
4363         {
4364             QByteArray line;
4365             while (!(line = queueFile.readLine()).isEmpty())
4366                 queue.append(QString::fromLatin1(line.trimmed()) + QLatin1String {".fastresume"});
4367         }
4368         else
4369         {
4370             LogMsg(tr("Couldn't load torrents queue from '%1'. Error: %2")
4371                 .arg(queueFile.fileName(), queueFile.errorString()), Log::WARNING);
4372         }
4373 
4374         if (!queue.empty())
4375             fastresumes = queue + List::toSet(fastresumes).subtract(List::toSet(queue)).values();
4376     }
4377 
4378     int resumedTorrentsCount = 0;
4379     for (const QString &fastresumeName : asConst(fastresumes))
4380     {
4381         const QRegularExpressionMatch rxMatch = rx.match(fastresumeName);
4382         if (!rxMatch.hasMatch()) continue;
4383 
4384         const QString hash = rxMatch.captured(1);
4385         const QString fastresumePath = resumeDataDir.absoluteFilePath(fastresumeName);
4386         QByteArray data;
4387         LoadTorrentParams torrentParams;
4388         const QString torrentFilePath = resumeDataDir.filePath(QString::fromLatin1("%1.torrent").arg(hash));
4389         TorrentInfo metadata = TorrentInfo::loadFromFile(torrentFilePath);
4390         if (readFile(fastresumePath, data) && loadTorrentResumeData(data, metadata, torrentParams))
4391         {
4392             qDebug() << "Starting up torrent" << hash << "...";
4393             if (!loadTorrent(torrentParams))
4394                 LogMsg(tr("Unable to resume torrent '%1'.", "e.g: Unable to resume torrent 'hash'.")
4395                            .arg(hash), Log::CRITICAL);
4396 
4397             // process add torrent messages before message queue overflow
4398             if ((resumedTorrentsCount % 100) == 0) readAlerts();
4399 
4400             ++resumedTorrentsCount;
4401         }
4402         else
4403         {
4404             LogMsg(tr("Unable to resume torrent '%1'.", "e.g: Unable to resume torrent 'hash'.")
4405                        .arg(hash), Log::CRITICAL);
4406         }
4407     }
4408 }
4409 
getAlltimeDL() const4410 quint64 Session::getAlltimeDL() const
4411 {
4412     return m_statistics->getAlltimeDL();
4413 }
4414 
getAlltimeUL() const4415 quint64 Session::getAlltimeUL() const
4416 {
4417     return m_statistics->getAlltimeUL();
4418 }
4419 
enqueueRefresh()4420 void Session::enqueueRefresh()
4421 {
4422     Q_ASSERT(!m_refreshEnqueued);
4423 
4424     QTimer::singleShot(refreshInterval(), this, [this] ()
4425     {
4426         m_nativeSession->post_torrent_updates();
4427         m_nativeSession->post_session_stats();
4428     });
4429 
4430     m_refreshEnqueued = true;
4431 }
4432 
handleIPFilterParsed(const int ruleCount)4433 void Session::handleIPFilterParsed(const int ruleCount)
4434 {
4435     if (m_filterParser)
4436     {
4437         lt::ip_filter filter = m_filterParser->IPfilter();
4438         processBannedIPs(filter);
4439         m_nativeSession->set_ip_filter(filter);
4440     }
4441     LogMsg(tr("Successfully parsed the provided IP filter: %1 rules were applied.", "%1 is a number").arg(ruleCount));
4442     emit IPFilterParsed(false, ruleCount);
4443 }
4444 
handleIPFilterError()4445 void Session::handleIPFilterError()
4446 {
4447     lt::ip_filter filter;
4448     processBannedIPs(filter);
4449     m_nativeSession->set_ip_filter(filter);
4450 
4451     LogMsg(tr("Error: Failed to parse the provided IP filter."), Log::CRITICAL);
4452     emit IPFilterParsed(true, 0);
4453 }
4454 
getPendingAlerts(const lt::time_duration time) const4455 std::vector<lt::alert *> Session::getPendingAlerts(const lt::time_duration time) const
4456 {
4457     if (time > lt::time_duration::zero())
4458         m_nativeSession->wait_for_alert(time);
4459 
4460     std::vector<lt::alert *> alerts;
4461     m_nativeSession->pop_alerts(&alerts);
4462     return alerts;
4463 }
4464 
torrentContentLayout() const4465 TorrentContentLayout Session::torrentContentLayout() const
4466 {
4467     return m_torrentContentLayout;
4468 }
4469 
setTorrentContentLayout(const TorrentContentLayout value)4470 void Session::setTorrentContentLayout(const TorrentContentLayout value)
4471 {
4472     m_torrentContentLayout = value;
4473 }
4474 
4475 // Read alerts sent by the BitTorrent session
readAlerts()4476 void Session::readAlerts()
4477 {
4478     const std::vector<lt::alert *> alerts = getPendingAlerts();
4479     for (const lt::alert *a : alerts)
4480         handleAlert(a);
4481 }
4482 
handleAlert(const lt::alert * a)4483 void Session::handleAlert(const lt::alert *a)
4484 {
4485     try
4486     {
4487         switch (a->type())
4488         {
4489         case lt::file_renamed_alert::alert_type:
4490         case lt::file_completed_alert::alert_type:
4491         case lt::torrent_finished_alert::alert_type:
4492         case lt::save_resume_data_alert::alert_type:
4493         case lt::save_resume_data_failed_alert::alert_type:
4494         case lt::torrent_paused_alert::alert_type:
4495         case lt::torrent_resumed_alert::alert_type:
4496         case lt::tracker_error_alert::alert_type:
4497         case lt::tracker_reply_alert::alert_type:
4498         case lt::tracker_warning_alert::alert_type:
4499         case lt::fastresume_rejected_alert::alert_type:
4500         case lt::torrent_checked_alert::alert_type:
4501         case lt::metadata_received_alert::alert_type:
4502             dispatchTorrentAlert(a);
4503             break;
4504         case lt::state_update_alert::alert_type:
4505             handleStateUpdateAlert(static_cast<const lt::state_update_alert*>(a));
4506             break;
4507         case lt::session_stats_alert::alert_type:
4508             handleSessionStatsAlert(static_cast<const lt::session_stats_alert*>(a));
4509             break;
4510         case lt::file_error_alert::alert_type:
4511             handleFileErrorAlert(static_cast<const lt::file_error_alert*>(a));
4512             break;
4513         case lt::add_torrent_alert::alert_type:
4514             handleAddTorrentAlert(static_cast<const lt::add_torrent_alert*>(a));
4515             break;
4516         case lt::torrent_removed_alert::alert_type:
4517             handleTorrentRemovedAlert(static_cast<const lt::torrent_removed_alert*>(a));
4518             break;
4519         case lt::torrent_deleted_alert::alert_type:
4520             handleTorrentDeletedAlert(static_cast<const lt::torrent_deleted_alert*>(a));
4521             break;
4522         case lt::torrent_delete_failed_alert::alert_type:
4523             handleTorrentDeleteFailedAlert(static_cast<const lt::torrent_delete_failed_alert*>(a));
4524             break;
4525         case lt::portmap_error_alert::alert_type:
4526             handlePortmapWarningAlert(static_cast<const lt::portmap_error_alert*>(a));
4527             break;
4528         case lt::portmap_alert::alert_type:
4529             handlePortmapAlert(static_cast<const lt::portmap_alert*>(a));
4530             break;
4531         case lt::peer_blocked_alert::alert_type:
4532             handlePeerBlockedAlert(static_cast<const lt::peer_blocked_alert*>(a));
4533             break;
4534         case lt::peer_ban_alert::alert_type:
4535             handlePeerBanAlert(static_cast<const lt::peer_ban_alert*>(a));
4536             break;
4537         case lt::url_seed_alert::alert_type:
4538             handleUrlSeedAlert(static_cast<const lt::url_seed_alert*>(a));
4539             break;
4540         case lt::listen_succeeded_alert::alert_type:
4541             handleListenSucceededAlert(static_cast<const lt::listen_succeeded_alert*>(a));
4542             break;
4543         case lt::listen_failed_alert::alert_type:
4544             handleListenFailedAlert(static_cast<const lt::listen_failed_alert*>(a));
4545             break;
4546         case lt::external_ip_alert::alert_type:
4547             handleExternalIPAlert(static_cast<const lt::external_ip_alert*>(a));
4548             break;
4549         case lt::alerts_dropped_alert::alert_type:
4550             handleAlertsDroppedAlert(static_cast<const lt::alerts_dropped_alert *>(a));
4551             break;
4552         case lt::storage_moved_alert::alert_type:
4553             handleStorageMovedAlert(static_cast<const lt::storage_moved_alert*>(a));
4554             break;
4555         case lt::storage_moved_failed_alert::alert_type:
4556             handleStorageMovedFailedAlert(static_cast<const lt::storage_moved_failed_alert*>(a));
4557             break;
4558         case lt::socks5_alert::alert_type:
4559             handleSocks5Alert(static_cast<const lt::socks5_alert *>(a));
4560             break;
4561         }
4562     }
4563     catch (const std::exception &exc)
4564     {
4565         qWarning() << "Caught exception in " << Q_FUNC_INFO << ": " << QString::fromStdString(exc.what());
4566     }
4567 }
4568 
dispatchTorrentAlert(const lt::alert * a)4569 void Session::dispatchTorrentAlert(const lt::alert *a)
4570 {
4571     TorrentImpl *const torrent = m_torrents.value(static_cast<const lt::torrent_alert*>(a)->handle.info_hash());
4572     if (torrent)
4573     {
4574         torrent->handleAlert(a);
4575         return;
4576     }
4577 
4578     switch (a->type())
4579     {
4580     case lt::metadata_received_alert::alert_type:
4581         handleMetadataReceivedAlert(static_cast<const lt::metadata_received_alert*>(a));
4582         break;
4583     }
4584 }
4585 
createTorrent(const lt::torrent_handle & nativeHandle)4586 void Session::createTorrent(const lt::torrent_handle &nativeHandle)
4587 {
4588     Q_ASSERT(m_loadingTorrents.contains(nativeHandle.info_hash()));
4589 
4590     const LoadTorrentParams params = m_loadingTorrents.take(nativeHandle.info_hash());
4591 
4592     auto *const torrent = new TorrentImpl {this, m_nativeSession, nativeHandle, params};
4593     m_torrents.insert(torrent->id(), torrent);
4594 
4595     const bool hasMetadata = torrent->hasMetadata();
4596 
4597     if (params.restored)
4598     {
4599         LogMsg(tr("'%1' restored.", "'torrent name' restored.").arg(torrent->name()));
4600     }
4601     else
4602     {
4603         // The following is useless for newly added magnet
4604         if (hasMetadata)
4605         {
4606             // Backup torrent file
4607             const QDir resumeDataDir {m_resumeFolderPath};
4608             const QString torrentFileName {QString::fromLatin1("%1.torrent").arg(torrent->id().toString())};
4609             try
4610             {
4611                 torrent->info().saveToFile(resumeDataDir.absoluteFilePath(torrentFileName));
4612                 // Copy the torrent file to the export folder
4613                 if (!torrentExportDirectory().isEmpty())
4614                     exportTorrentFile(torrent);
4615             }
4616             catch (const RuntimeError &err)
4617             {
4618                 LogMsg(tr("Couldn't save torrent metadata file '%1'. Reason: %2")
4619                        .arg(torrentFileName, err.message()), Log::CRITICAL);
4620             }
4621         }
4622 
4623         if (isAddTrackersEnabled() && !torrent->isPrivate())
4624             torrent->addTrackers(m_additionalTrackerList);
4625 
4626         LogMsg(tr("'%1' added to download list.", "'torrent name' was added to download list.")
4627             .arg(torrent->name()));
4628 
4629         // In case of crash before the scheduled generation
4630         // of the fastresumes.
4631         torrent->saveResumeData();
4632     }
4633 
4634     if (((torrent->ratioLimit() >= 0) || (torrent->seedingTimeLimit() >= 0))
4635         && !m_seedingLimitTimer->isActive())
4636         m_seedingLimitTimer->start();
4637 
4638     // Send torrent addition signal
4639     emit torrentLoaded(torrent);
4640     // Send new torrent signal
4641     if (!params.restored)
4642         emit torrentAdded(torrent);
4643 
4644     // Torrent could have error just after adding to libtorrent
4645     if (torrent->hasError())
4646         LogMsg(tr("Torrent errored. Torrent: \"%1\". Error: %2.").arg(torrent->name(), torrent->error()), Log::WARNING);
4647 }
4648 
handleAddTorrentAlert(const lt::add_torrent_alert * p)4649 void Session::handleAddTorrentAlert(const lt::add_torrent_alert *p)
4650 {
4651     if (p->error)
4652     {
4653         qDebug("/!\\ Error: Failed to add torrent!");
4654         QString msg = QString::fromStdString(p->message());
4655         LogMsg(tr("Couldn't load torrent. Reason: %1").arg(msg), Log::WARNING);
4656         emit loadTorrentFailed(msg);
4657     }
4658     else if (m_loadingTorrents.contains(p->handle.info_hash()))
4659     {
4660         createTorrent(p->handle);
4661     }
4662 }
4663 
handleTorrentRemovedAlert(const lt::torrent_removed_alert * p)4664 void Session::handleTorrentRemovedAlert(const lt::torrent_removed_alert *p)
4665 {
4666 #if (LIBTORRENT_VERSION_NUM >= 20000)
4667     const auto id = TorrentID::fromInfoHash(p->info_hashes);
4668 #else
4669     const auto id = TorrentID::fromInfoHash(p->info_hash);
4670 #endif
4671 
4672     const auto removingTorrentDataIter = m_removingTorrents.find(id);
4673     if (removingTorrentDataIter != m_removingTorrents.end())
4674     {
4675         if (removingTorrentDataIter->deleteOption == DeleteTorrent)
4676         {
4677             LogMsg(tr("'%1' was removed from the transfer list.", "'xxx.avi' was removed...").arg(removingTorrentDataIter->name));
4678             m_removingTorrents.erase(removingTorrentDataIter);
4679         }
4680     }
4681 }
4682 
handleTorrentDeletedAlert(const lt::torrent_deleted_alert * p)4683 void Session::handleTorrentDeletedAlert(const lt::torrent_deleted_alert *p)
4684 {
4685 #if (LIBTORRENT_VERSION_NUM >= 20000)
4686     const auto id = TorrentID::fromInfoHash(p->info_hashes);
4687 #else
4688     const auto id = TorrentID::fromInfoHash(p->info_hash);
4689 #endif
4690 
4691     const auto removingTorrentDataIter = m_removingTorrents.find(id);
4692 
4693     if (removingTorrentDataIter == m_removingTorrents.end())
4694         return;
4695 
4696     Utils::Fs::smartRemoveEmptyFolderTree(removingTorrentDataIter->pathToRemove);
4697     LogMsg(tr("'%1' was removed from the transfer list and hard disk.", "'xxx.avi' was removed...").arg(removingTorrentDataIter->name));
4698     m_removingTorrents.erase(removingTorrentDataIter);
4699 }
4700 
handleTorrentDeleteFailedAlert(const lt::torrent_delete_failed_alert * p)4701 void Session::handleTorrentDeleteFailedAlert(const lt::torrent_delete_failed_alert *p)
4702 {
4703 #if (LIBTORRENT_VERSION_NUM >= 20000)
4704     const auto id = TorrentID::fromInfoHash(p->info_hashes);
4705 #else
4706     const auto id = TorrentID::fromInfoHash(p->info_hash);
4707 #endif
4708 
4709     const auto removingTorrentDataIter = m_removingTorrents.find(id);
4710 
4711     if (removingTorrentDataIter == m_removingTorrents.end())
4712         return;
4713 
4714     if (p->error)
4715     {
4716         // libtorrent won't delete the directory if it contains files not listed in the torrent,
4717         // so we remove the directory ourselves
4718         Utils::Fs::smartRemoveEmptyFolderTree(removingTorrentDataIter->pathToRemove);
4719 
4720         LogMsg(tr("'%1' was removed from the transfer list but the files couldn't be deleted. Error: %2", "'xxx.avi' was removed...")
4721                 .arg(removingTorrentDataIter->name, QString::fromLocal8Bit(p->error.message().c_str()))
4722             , Log::WARNING);
4723     }
4724     else // torrent without metadata, hence no files on disk
4725     {
4726         LogMsg(tr("'%1' was removed from the transfer list.", "'xxx.avi' was removed...").arg(removingTorrentDataIter->name));
4727     }
4728     m_removingTorrents.erase(removingTorrentDataIter);
4729 }
4730 
handleMetadataReceivedAlert(const lt::metadata_received_alert * p)4731 void Session::handleMetadataReceivedAlert(const lt::metadata_received_alert *p)
4732 {
4733 #if (LIBTORRENT_VERSION_NUM >= 20000)
4734     const auto id = TorrentID::fromInfoHash(p->handle.info_hashes());
4735 #else
4736     const auto id = TorrentID::fromInfoHash(p->handle.info_hash());
4737 #endif
4738 
4739     const auto downloadedMetadataIter = m_downloadedMetadata.find(id);
4740 
4741     if (downloadedMetadataIter != m_downloadedMetadata.end())
4742     {
4743         TorrentInfo metadata {p->handle.torrent_file()};
4744 
4745         m_downloadedMetadata.erase(downloadedMetadataIter);
4746         --m_extraLimit;
4747         adjustLimits();
4748         m_nativeSession->remove_torrent(p->handle, lt::session::delete_files);
4749 
4750         emit metadataDownloaded(metadata);
4751     }
4752 }
4753 
handleFileErrorAlert(const lt::file_error_alert * p)4754 void Session::handleFileErrorAlert(const lt::file_error_alert *p)
4755 {
4756     TorrentImpl *const torrent = m_torrents.value(p->handle.info_hash());
4757     if (!torrent)
4758         return;
4759 
4760     torrent->handleAlert(p);
4761 
4762     const TorrentID id = torrent->id();
4763     if (!m_recentErroredTorrents.contains(id))
4764     {
4765         m_recentErroredTorrents.insert(id);
4766 
4767         const QString msg = QString::fromStdString(p->message());
4768         LogMsg(tr("File error alert. Torrent: \"%1\". File: \"%2\". Reason: %3")
4769                 .arg(torrent->name(), p->filename(), msg)
4770             , Log::WARNING);
4771         emit fullDiskError(torrent, msg);
4772     }
4773 
4774     m_recentErroredTorrentsTimer->start();
4775 }
4776 
handlePortmapWarningAlert(const lt::portmap_error_alert * p)4777 void Session::handlePortmapWarningAlert(const lt::portmap_error_alert *p)
4778 {
4779     LogMsg(tr("UPnP/NAT-PMP: Port mapping failure, message: %1").arg(QString::fromStdString(p->message())), Log::CRITICAL);
4780 }
4781 
handlePortmapAlert(const lt::portmap_alert * p)4782 void Session::handlePortmapAlert(const lt::portmap_alert *p)
4783 {
4784     qDebug("UPnP Success, msg: %s", p->message().c_str());
4785     LogMsg(tr("UPnP/NAT-PMP: Port mapping successful, message: %1").arg(QString::fromStdString(p->message())), Log::INFO);
4786 }
4787 
handlePeerBlockedAlert(const lt::peer_blocked_alert * p)4788 void Session::handlePeerBlockedAlert(const lt::peer_blocked_alert *p)
4789 {
4790     QString reason;
4791     switch (p->reason)
4792     {
4793     case lt::peer_blocked_alert::ip_filter:
4794         reason = tr("IP filter", "this peer was blocked. Reason: IP filter.");
4795         break;
4796     case lt::peer_blocked_alert::port_filter:
4797         reason = tr("port filter", "this peer was blocked. Reason: port filter.");
4798         break;
4799     case lt::peer_blocked_alert::i2p_mixed:
4800         reason = tr("%1 mixed mode restrictions", "this peer was blocked. Reason: I2P mixed mode restrictions.").arg("I2P"); // don't translate I2P
4801         break;
4802     case lt::peer_blocked_alert::privileged_ports:
4803         reason = tr("use of privileged port", "this peer was blocked. Reason: use of privileged port.");
4804         break;
4805     case lt::peer_blocked_alert::utp_disabled:
4806         reason = tr("%1 is disabled", "this peer was blocked. Reason: uTP is disabled.").arg(QString::fromUtf8(C_UTP)); // don't translate μTP
4807         break;
4808     case lt::peer_blocked_alert::tcp_disabled:
4809         reason = tr("%1 is disabled", "this peer was blocked. Reason: TCP is disabled.").arg("TCP"); // don't translate TCP
4810         break;
4811     }
4812 
4813     const QString ip {toString(p->endpoint.address())};
4814     if (!ip.isEmpty())
4815         Logger::instance()->addPeer(ip, true, reason);
4816 }
4817 
handlePeerBanAlert(const lt::peer_ban_alert * p)4818 void Session::handlePeerBanAlert(const lt::peer_ban_alert *p)
4819 {
4820     const QString ip {toString(p->endpoint.address())};
4821     if (!ip.isEmpty())
4822         Logger::instance()->addPeer(ip, false);
4823 }
4824 
handleUrlSeedAlert(const lt::url_seed_alert * p)4825 void Session::handleUrlSeedAlert(const lt::url_seed_alert *p)
4826 {
4827     const TorrentImpl *torrent = m_torrents.value(p->handle.info_hash());
4828     if (!torrent)
4829         return;
4830 
4831     if (p->error)
4832     {
4833         LogMsg(tr("URL seed name lookup failed. Torrent: \"%1\". URL: \"%2\". Error: \"%3\"")
4834             .arg(torrent->name(), p->server_url(), QString::fromStdString(p->message()))
4835             , Log::WARNING);
4836     }
4837     else
4838     {
4839         LogMsg(tr("Received error message from a URL seed. Torrent: \"%1\". URL: \"%2\". Message: \"%3\"")
4840             .arg(torrent->name(), p->server_url(), p->error_message())
4841             , Log::WARNING);
4842     }
4843 }
4844 
handleListenSucceededAlert(const lt::listen_succeeded_alert * p)4845 void Session::handleListenSucceededAlert(const lt::listen_succeeded_alert *p)
4846 {
4847     const QString proto {toString(p->socket_type)};
4848     LogMsg(tr("Successfully listening on IP: %1, port: %2/%3"
4849               , "e.g: Successfully listening on IP: 192.168.0.1, port: TCP/6881")
4850             .arg(toString(p->address), proto, QString::number(p->port)), Log::INFO);
4851 
4852     // Force reannounce on all torrents because some trackers blacklist some ports
4853     for (const lt::torrent_handle &torrent : m_nativeSession->get_torrents())
4854         torrent.force_reannounce();
4855 }
4856 
handleListenFailedAlert(const lt::listen_failed_alert * p)4857 void Session::handleListenFailedAlert(const lt::listen_failed_alert *p)
4858 {
4859     const QString proto {toString(p->socket_type)};
4860     LogMsg(tr("Failed to listen on IP: %1, port: %2/%3. Reason: %4"
4861               , "e.g: Failed to listen on IP: 192.168.0.1, port: TCP/6881. Reason: already in use")
4862         .arg(toString(p->address), proto, QString::number(p->port)
4863             , QString::fromLocal8Bit(p->error.message().c_str())), Log::CRITICAL);
4864 }
4865 
handleExternalIPAlert(const lt::external_ip_alert * p)4866 void Session::handleExternalIPAlert(const lt::external_ip_alert *p)
4867 {
4868     LogMsg(tr("Detected external IP: %1", "e.g. Detected external IP: 1.1.1.1")
4869         .arg(toString(p->external_address)), Log::INFO);
4870 }
4871 
handleSessionStatsAlert(const lt::session_stats_alert * p)4872 void Session::handleSessionStatsAlert(const lt::session_stats_alert *p)
4873 {
4874     const qreal interval = lt::total_milliseconds(p->timestamp() - m_statsLastTimestamp) / 1000.;
4875     m_statsLastTimestamp = p->timestamp();
4876 
4877     const auto stats = p->counters();
4878 
4879     m_status.hasIncomingConnections = static_cast<bool>(stats[m_metricIndices.net.hasIncomingConnections]);
4880 
4881     const int64_t ipOverheadDownload = stats[m_metricIndices.net.recvIPOverheadBytes];
4882     const int64_t ipOverheadUpload = stats[m_metricIndices.net.sentIPOverheadBytes];
4883     const int64_t totalDownload = stats[m_metricIndices.net.recvBytes] + ipOverheadDownload;
4884     const int64_t totalUpload = stats[m_metricIndices.net.sentBytes] + ipOverheadUpload;
4885     const int64_t totalPayloadDownload = stats[m_metricIndices.net.recvPayloadBytes];
4886     const int64_t totalPayloadUpload = stats[m_metricIndices.net.sentPayloadBytes];
4887     const int64_t trackerDownload = stats[m_metricIndices.net.recvTrackerBytes];
4888     const int64_t trackerUpload = stats[m_metricIndices.net.sentTrackerBytes];
4889     const int64_t dhtDownload = stats[m_metricIndices.dht.dhtBytesIn];
4890     const int64_t dhtUpload = stats[m_metricIndices.dht.dhtBytesOut];
4891 
4892     auto calcRate = [interval](const quint64 previous, const quint64 current)
4893     {
4894         Q_ASSERT(current >= previous);
4895         return static_cast<quint64>((current - previous) / interval);
4896     };
4897 
4898     m_status.payloadDownloadRate = calcRate(m_status.totalPayloadDownload, totalPayloadDownload);
4899     m_status.payloadUploadRate = calcRate(m_status.totalPayloadUpload, totalPayloadUpload);
4900     m_status.downloadRate = calcRate(m_status.totalDownload, totalDownload);
4901     m_status.uploadRate = calcRate(m_status.totalUpload, totalUpload);
4902     m_status.ipOverheadDownloadRate = calcRate(m_status.ipOverheadDownload, ipOverheadDownload);
4903     m_status.ipOverheadUploadRate = calcRate(m_status.ipOverheadUpload, ipOverheadUpload);
4904     m_status.dhtDownloadRate = calcRate(m_status.dhtDownload, dhtDownload);
4905     m_status.dhtUploadRate = calcRate(m_status.dhtUpload, dhtUpload);
4906     m_status.trackerDownloadRate = calcRate(m_status.trackerDownload, trackerDownload);
4907     m_status.trackerUploadRate = calcRate(m_status.trackerUpload, trackerUpload);
4908 
4909     m_status.totalDownload = totalDownload;
4910     m_status.totalUpload = totalUpload;
4911     m_status.totalPayloadDownload = totalPayloadDownload;
4912     m_status.totalPayloadUpload = totalPayloadUpload;
4913     m_status.ipOverheadDownload = ipOverheadDownload;
4914     m_status.ipOverheadUpload = ipOverheadUpload;
4915     m_status.trackerDownload = trackerDownload;
4916     m_status.trackerUpload = trackerUpload;
4917     m_status.dhtDownload = dhtDownload;
4918     m_status.dhtUpload = dhtUpload;
4919     m_status.totalWasted = stats[m_metricIndices.net.recvRedundantBytes]
4920             + stats[m_metricIndices.net.recvFailedBytes];
4921     m_status.dhtNodes = stats[m_metricIndices.dht.dhtNodes];
4922     m_status.diskReadQueue = stats[m_metricIndices.peer.numPeersUpDisk];
4923     m_status.diskWriteQueue = stats[m_metricIndices.peer.numPeersDownDisk];
4924     m_status.peersCount = stats[m_metricIndices.peer.numPeersConnected];
4925 
4926     const int64_t numBlocksRead = stats[m_metricIndices.disk.numBlocksRead];
4927     m_cacheStatus.totalUsedBuffers = stats[m_metricIndices.disk.diskBlocksInUse];
4928     m_cacheStatus.jobQueueLength = stats[m_metricIndices.disk.queuedDiskJobs];
4929 
4930 #if (LIBTORRENT_VERSION_NUM < 20000)
4931     const int64_t numBlocksCacheHits = stats[m_metricIndices.disk.numBlocksCacheHits];
4932     m_cacheStatus.readRatio = static_cast<qreal>(numBlocksCacheHits) / std::max<int64_t>((numBlocksCacheHits + numBlocksRead), 1);
4933 #endif
4934 
4935     const int64_t totalJobs = stats[m_metricIndices.disk.writeJobs] + stats[m_metricIndices.disk.readJobs]
4936                   + stats[m_metricIndices.disk.hashJobs];
4937     m_cacheStatus.averageJobTime = (totalJobs > 0)
4938                                    ? (stats[m_metricIndices.disk.diskJobTime] / totalJobs) : 0;
4939 
4940     emit statsUpdated();
4941 
4942     if (m_refreshEnqueued)
4943         m_refreshEnqueued = false;
4944     else
4945         enqueueRefresh();
4946 }
4947 
handleAlertsDroppedAlert(const lt::alerts_dropped_alert * p) const4948 void Session::handleAlertsDroppedAlert(const lt::alerts_dropped_alert *p) const
4949 {
4950     LogMsg(tr("Error: Internal alert queue full and alerts were dropped, you might see degraded performance. Dropped alert types: %1. Message: %2")
4951         .arg(QString::fromStdString(p->dropped_alerts.to_string()), QString::fromStdString(p->message())), Log::CRITICAL);
4952 }
4953 
handleStorageMovedAlert(const lt::storage_moved_alert * p)4954 void Session::handleStorageMovedAlert(const lt::storage_moved_alert *p)
4955 {
4956     Q_ASSERT(!m_moveStorageQueue.isEmpty());
4957 
4958     const MoveStorageJob &currentJob = m_moveStorageQueue.first();
4959     Q_ASSERT(currentJob.torrentHandle == p->handle);
4960 
4961     const QString newPath {p->storage_path()};
4962     Q_ASSERT(newPath == currentJob.path);
4963 
4964 #if (LIBTORRENT_VERSION_NUM >= 20000)
4965     const auto id = TorrentID::fromInfoHash(currentJob.torrentHandle.info_hashes());
4966 #else
4967     const auto id = TorrentID::fromInfoHash(currentJob.torrentHandle.info_hash());
4968 #endif
4969 
4970     TorrentImpl *torrent = m_torrents.value(id);
4971     const QString torrentName = (torrent ? torrent->name() : id.toString());
4972     LogMsg(tr("\"%1\" is successfully moved to \"%2\".").arg(torrentName, newPath));
4973 
4974     handleMoveTorrentStorageJobFinished();
4975 }
4976 
handleStorageMovedFailedAlert(const lt::storage_moved_failed_alert * p)4977 void Session::handleStorageMovedFailedAlert(const lt::storage_moved_failed_alert *p)
4978 {
4979     Q_ASSERT(!m_moveStorageQueue.isEmpty());
4980 
4981     const MoveStorageJob &currentJob = m_moveStorageQueue.first();
4982     Q_ASSERT(currentJob.torrentHandle == p->handle);
4983 
4984 #if (LIBTORRENT_VERSION_NUM >= 20000)
4985     const auto id = TorrentID::fromInfoHash(currentJob.torrentHandle.info_hashes());
4986 #else
4987     const auto id = TorrentID::fromInfoHash(currentJob.torrentHandle.info_hash());
4988 #endif
4989 
4990     TorrentImpl *torrent = m_torrents.value(id);
4991     const QString torrentName = (torrent ? torrent->name() : id.toString());
4992     const QString currentLocation = QString::fromStdString(p->handle.status(lt::torrent_handle::query_save_path).save_path);
4993     const QString errorMessage = QString::fromStdString(p->message());
4994     LogMsg(tr("Failed to move \"%1\" from \"%2\" to \"%3\". Reason: %4.")
4995            .arg(torrentName, currentLocation, currentJob.path, errorMessage), Log::CRITICAL);
4996 
4997     handleMoveTorrentStorageJobFinished();
4998 }
4999 
handleStateUpdateAlert(const lt::state_update_alert * p)5000 void Session::handleStateUpdateAlert(const lt::state_update_alert *p)
5001 {
5002     QVector<Torrent *> updatedTorrents;
5003     updatedTorrents.reserve(static_cast<decltype(updatedTorrents)::size_type>(p->status.size()));
5004 
5005     for (const lt::torrent_status &status : p->status)
5006     {
5007 #if (LIBTORRENT_VERSION_NUM >= 20000)
5008         const auto id = TorrentID::fromInfoHash(status.info_hashes);
5009 #else
5010         const auto id = TorrentID::fromInfoHash(status.info_hash);
5011 #endif
5012         TorrentImpl *const torrent = m_torrents.value(id);
5013         if (!torrent)
5014             continue;
5015 
5016         torrent->handleStateUpdate(status);
5017         updatedTorrents.push_back(torrent);
5018     }
5019 
5020     if (!updatedTorrents.isEmpty())
5021         emit torrentsUpdated(updatedTorrents);
5022 
5023     if (m_refreshEnqueued)
5024         m_refreshEnqueued = false;
5025     else
5026         enqueueRefresh();
5027 }
5028 
handleSocks5Alert(const lt::socks5_alert * p) const5029 void Session::handleSocks5Alert(const lt::socks5_alert *p) const
5030 {
5031     if (p->error)
5032     {
5033         LogMsg(tr("SOCKS5 proxy error. Message: %1").arg(QString::fromStdString(p->message()))
5034             , Log::WARNING);
5035     }
5036 }
5037