qBittorrent/src/base/bittorrent/session.cpp

3717 lines
126 KiB
C++
Raw Normal View History

2015-04-19 18:17:47 +03:00
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#include "session.h"
#include <QCoreApplication>
#include <QDateTime>
2015-04-19 18:17:47 +03:00
#include <QDebug>
#include <QDir>
#include <QDirIterator>
2015-04-19 18:17:47 +03:00
#include <QHostAddress>
#include <QNetworkAddressEntry>
#include <QNetworkInterface>
2015-04-19 18:17:47 +03:00
#include <QProcess>
#include <QRegExp>
#include <QString>
#include <QThread>
#include <QTimer>
2015-04-19 18:17:47 +03:00
#include <cstdlib>
2015-04-19 18:17:47 +03:00
#include <queue>
#include <vector>
#include <libtorrent/alert_types.hpp>
2016-06-03 17:03:17 +03:00
#if LIBTORRENT_VERSION_NUM >= 10100
#include <libtorrent/bdecode.hpp>
#endif
2015-04-19 18:17:47 +03:00
#include <libtorrent/bencode.hpp>
#include <libtorrent/error_code.hpp>
#include <libtorrent/extensions/ut_metadata.hpp>
#include <libtorrent/extensions/ut_pex.hpp>
#include <libtorrent/extensions/smart_ban.hpp>
#include <libtorrent/identify_client.hpp>
#include <libtorrent/ip_filter.hpp>
2016-06-03 17:03:17 +03:00
#if LIBTORRENT_VERSION_NUM < 10100
#include <libtorrent/lazy_entry.hpp>
2016-06-03 17:03:17 +03:00
#endif
#include <libtorrent/magnet_uri.hpp>
#include <libtorrent/session.hpp>
#include <libtorrent/torrent_info.hpp>
2015-04-19 18:17:47 +03:00
2015-09-25 11:10:05 +03:00
#include "base/logger.h"
#include "base/profile.h"
2015-09-25 11:10:05 +03:00
#include "base/net/downloadhandler.h"
#include "base/net/downloadmanager.h"
2015-09-25 11:10:05 +03:00
#include "base/net/portforwarder.h"
#include "base/net/proxyconfigurationmanager.h"
#include "base/torrentfileguard.h"
#include "base/torrentfilter.h"
#include "base/unicodestrings.h"
#include "base/utils/net.h"
#include "base/utils/misc.h"
#include "base/utils/fs.h"
#include "base/utils/random.h"
2015-09-25 11:10:05 +03:00
#include "base/utils/string.h"
#include "cachestatus.h"
#include "magneturi.h"
2015-04-19 18:17:47 +03:00
#include "private/filterparserthread.h"
#include "private/statistics.h"
#include "private/bandwidthscheduler.h"
#include "private/resumedatasavingmanager.h"
2015-04-19 18:17:47 +03:00
#include "sessionstatus.h"
#include "torrenthandle.h"
#include "tracker.h"
#include "trackerentry.h"
2015-04-19 18:17:47 +03:00
2016-10-30 00:11:52 +03:00
static const char PEER_ID[] = "qB";
static const char RESUME_FOLDER[] = "BT_backup";
static const char USER_AGENT[] = "qBittorrent/" QBT_VERSION_2;
2015-04-19 18:17:47 +03:00
namespace libt = libtorrent;
using namespace BitTorrent;
namespace
{
bool readFile(const QString &path, QByteArray &buf);
bool loadTorrentResumeData(const QByteArray &data, AddTorrentData &torrentData, int &prio, MagnetUri &magnetUri);
void torrentQueuePositionUp(const libt::torrent_handle &handle);
void torrentQueuePositionDown(const libt::torrent_handle &handle);
void torrentQueuePositionTop(const libt::torrent_handle &handle);
void torrentQueuePositionBottom(const libt::torrent_handle &handle);
inline SettingsStorage *settings() { return SettingsStorage::instance(); }
QStringMap map_cast(const QVariantMap &map)
{
QStringMap result;
foreach (const QString &key, map.keys())
result[key] = map.value(key).toString();
return result;
}
QVariantMap map_cast(const QStringMap &map)
{
QVariantMap result;
foreach (const QString &key, map.keys())
result[key] = map.value(key);
return result;
}
QString normalizePath(const QString &path)
{
QString tmp = Utils::Fs::fromNativePath(path.trimmed());
if (!tmp.isEmpty() && !tmp.endsWith('/'))
return tmp + '/';
return tmp;
}
QString normalizeSavePath(QString path, const QString &defaultPath = specialFolderLocation(SpecialFolder::Downloads))
{
path = path.trimmed();
2016-03-06 09:25:55 +03:00
if (path.isEmpty())
path = Utils::Fs::fromNativePath(defaultPath.trimmed());
return normalizePath(path);
}
QStringMap expandCategories(const QStringMap &categories)
{
QStringMap expanded = categories;
2015-04-19 18:17:47 +03:00
foreach (const QString &category, categories.keys()) {
foreach (const QString &subcat, Session::expandCategory(category)) {
if (!expanded.contains(subcat))
expanded[subcat] = "";
}
}
return expanded;
}
QStringList findAllFiles(const QString &dirPath)
{
QStringList files;
QDirIterator it(dirPath, QDir::Files, QDirIterator::Subdirectories);
while (it.hasNext())
files << it.next();
return files;
}
template <typename T>
struct LowerLimited
{
LowerLimited(T limit, T ret)
: m_limit(limit)
, m_ret(ret)
{
}
explicit LowerLimited(T limit)
: LowerLimited(limit, limit)
{
}
T operator()(T val)
{
return val <= m_limit ? m_ret : val;
}
private:
const T m_limit;
const T m_ret;
};
template <typename T>
LowerLimited<T> lowerLimited(T limit) { return LowerLimited<T>(limit); }
template <typename T>
LowerLimited<T> lowerLimited(T limit, T ret) { return LowerLimited<T>(limit, ret); }
}
2015-04-19 18:17:47 +03:00
// Session
Session *Session::m_instance = nullptr;
2015-04-19 18:17:47 +03:00
#define BITTORRENT_KEY(name) "BitTorrent/" name
#define BITTORRENT_SESSION_KEY(name) BITTORRENT_KEY("Session/") name
2015-04-19 18:17:47 +03:00
Session::Session(QObject *parent)
: QObject(parent)
, m_deferredConfigureScheduled(false)
2016-06-03 17:03:17 +03:00
, m_IPFilteringChanged(false)
#if LIBTORRENT_VERSION_NUM >= 10100
, m_listenInterfaceChanged(true)
#endif
, m_isDHTEnabled(BITTORRENT_SESSION_KEY("DHTEnabled"), true)
, m_isLSDEnabled(BITTORRENT_SESSION_KEY("LSDEnabled"), true)
, m_isPeXEnabled(BITTORRENT_SESSION_KEY("PeXEnabled"), true)
, m_isIPFilteringEnabled(BITTORRENT_SESSION_KEY("IPFilteringEnabled"), false)
, m_isTrackerFilteringEnabled(BITTORRENT_SESSION_KEY("TrackerFilteringEnabled"), false)
, m_IPFilterFile(BITTORRENT_SESSION_KEY("IPFilter"))
, m_announceToAllTrackers(BITTORRENT_SESSION_KEY("AnnounceToAllTrackers"), true)
, m_diskCacheSize(BITTORRENT_SESSION_KEY("DiskCacheSize"), 0)
, m_diskCacheTTL(BITTORRENT_SESSION_KEY("DiskCacheTTL"), 60)
, m_useOSCache(BITTORRENT_SESSION_KEY("UseOSCache"), true)
, m_isAnonymousModeEnabled(BITTORRENT_SESSION_KEY("AnonymousModeEnabled"), false)
, m_isQueueingEnabled(BITTORRENT_SESSION_KEY("QueueingSystemEnabled"), true)
2016-10-30 00:10:42 +03:00
, m_maxActiveDownloads(BITTORRENT_SESSION_KEY("MaxActiveDownloads"), 3, lowerLimited(-1))
, m_maxActiveUploads(BITTORRENT_SESSION_KEY("MaxActiveUploads"), 3, lowerLimited(-1))
, m_maxActiveTorrents(BITTORRENT_SESSION_KEY("MaxActiveTorrents"), 5, lowerLimited(-1))
, m_ignoreSlowTorrentsForQueueing(BITTORRENT_SESSION_KEY("IgnoreSlowTorrentsForQueueing"), false)
, m_outgoingPortsMin(BITTORRENT_SESSION_KEY("OutgoingPortsMin"), 0)
, m_outgoingPortsMax(BITTORRENT_SESSION_KEY("OutgoingPortsMax"), 0)
, m_ignoreLimitsOnLAN(BITTORRENT_SESSION_KEY("IgnoreLimitsOnLAN"), true)
, m_includeOverheadInLimits(BITTORRENT_SESSION_KEY("IncludeOverheadInLimits"), false)
, m_announceIP(BITTORRENT_SESSION_KEY("AnnounceIP"))
, m_isSuperSeedingEnabled(BITTORRENT_SESSION_KEY("SuperSeedingEnabled"), false)
, m_maxConnections(BITTORRENT_SESSION_KEY("MaxConnections"), 500, lowerLimited(0, -1))
, m_maxHalfOpenConnections(BITTORRENT_SESSION_KEY("MaxHalfOpenConnections"), 20, lowerLimited(0, -1))
, m_maxUploads(BITTORRENT_SESSION_KEY("MaxUploads"), -1, lowerLimited(0, -1))
, m_maxConnectionsPerTorrent(BITTORRENT_SESSION_KEY("MaxConnectionsPerTorrent"), 100, lowerLimited(0, -1))
, m_maxUploadsPerTorrent(BITTORRENT_SESSION_KEY("MaxUploadsPerTorrent"), -1, lowerLimited(0, -1))
, m_isUTPEnabled(BITTORRENT_SESSION_KEY("uTPEnabled"), true)
, m_isUTPRateLimited(BITTORRENT_SESSION_KEY("uTPRateLimited"), true)
, m_isAddTrackersEnabled(BITTORRENT_SESSION_KEY("AddTrackersEnabled"), false)
, m_additionalTrackers(BITTORRENT_SESSION_KEY("AdditionalTrackers"))
, m_globalMaxRatio(BITTORRENT_SESSION_KEY("GlobalMaxRatio"), -1, [](qreal r) { return r < 0 ? -1. : r;})
, m_isAddTorrentPaused(BITTORRENT_SESSION_KEY("AddTorrentPaused"), false)
2017-04-15 17:21:24 +03:00
, m_isCreateTorrentSubfolder(BITTORRENT_SESSION_KEY("CreateTorrentSubfolder"), true)
, m_isAppendExtensionEnabled(BITTORRENT_SESSION_KEY("AddExtensionToIncompleteFiles"), false)
, m_refreshInterval(BITTORRENT_SESSION_KEY("RefreshInterval"), 1500)
, m_isPreallocationEnabled(BITTORRENT_SESSION_KEY("Preallocation"), false)
, m_torrentExportDirectory(BITTORRENT_SESSION_KEY("TorrentExportDirectory"))
, m_finishedTorrentExportDirectory(BITTORRENT_SESSION_KEY("FinishedTorrentExportDirectory"))
, m_globalDownloadSpeedLimit(BITTORRENT_SESSION_KEY("GlobalDLSpeedLimit"), 0, lowerLimited(0))
, m_globalUploadSpeedLimit(BITTORRENT_SESSION_KEY("GlobalUPSpeedLimit"), 0, lowerLimited(0))
, m_altGlobalDownloadSpeedLimit(BITTORRENT_SESSION_KEY("AlternativeGlobalDLSpeedLimit"), 10, lowerLimited(0))
, m_altGlobalUploadSpeedLimit(BITTORRENT_SESSION_KEY("AlternativeGlobalUPSpeedLimit"), 10, lowerLimited(0))
, m_isAltGlobalSpeedLimitEnabled(BITTORRENT_SESSION_KEY("UseAlternativeGlobalSpeedLimit"), false)
, m_isBandwidthSchedulerEnabled(BITTORRENT_SESSION_KEY("BandwidthSchedulerEnabled"), false)
, m_saveResumeDataInterval(BITTORRENT_SESSION_KEY("SaveResumeDataInterval"), 3)
, m_port(BITTORRENT_SESSION_KEY("Port"), 8999)
, m_useRandomPort(BITTORRENT_SESSION_KEY("UseRandomPort"), false)
, m_networkInterface(BITTORRENT_SESSION_KEY("Interface"))
, m_networkInterfaceName(BITTORRENT_SESSION_KEY("InterfaceName"))
, m_networkInterfaceAddress(BITTORRENT_SESSION_KEY("InterfaceAddress"))
, m_isIPv6Enabled(BITTORRENT_SESSION_KEY("IPv6Enabled"), false)
, m_encryption(BITTORRENT_SESSION_KEY("Encryption"), 0)
, m_isForceProxyEnabled(BITTORRENT_SESSION_KEY("ForceProxy"), true)
, m_isProxyPeerConnectionsEnabled(BITTORRENT_SESSION_KEY("ProxyPeerConnections"), false)
, m_storedCategories(BITTORRENT_SESSION_KEY("Categories"))
, m_maxRatioAction(BITTORRENT_SESSION_KEY("MaxRatioAction"), Pause)
, m_defaultSavePath(BITTORRENT_SESSION_KEY("DefaultSavePath"), specialFolderLocation(SpecialFolder::Downloads), normalizePath)
, m_tempPath(BITTORRENT_SESSION_KEY("TempPath"), defaultSavePath() + "temp/", normalizePath)
, m_isSubcategoriesEnabled(BITTORRENT_SESSION_KEY("SubcategoriesEnabled"), false)
, m_isTempPathEnabled(BITTORRENT_SESSION_KEY("TempPathEnabled"), false)
, m_isAutoTMMDisabledByDefault(BITTORRENT_SESSION_KEY("DisableAutoTMMByDefault"), true)
, m_isDisableAutoTMMWhenCategoryChanged(BITTORRENT_SESSION_KEY("DisableAutoTMMTriggers/CategoryChanged"), false)
, m_isDisableAutoTMMWhenDefaultSavePathChanged(BITTORRENT_SESSION_KEY("DisableAutoTMMTriggers/DefaultSavePathChanged"), true)
, m_isDisableAutoTMMWhenCategorySavePathChanged(BITTORRENT_SESSION_KEY("DisableAutoTMMTriggers/CategorySavePathChanged"), true)
, m_isTrackerEnabled(BITTORRENT_KEY("TrackerEnabled"), false)
, m_bannedIPs("State/BannedIPs"
, QStringList()
, [](const QStringList &value)
{
QStringList tmp = value;
tmp.sort();
return tmp;
}
)
, m_wasPexEnabled(m_isPeXEnabled)
2015-04-19 18:17:47 +03:00
, m_numResumeData(0)
, m_extraLimit(0)
, m_useProxy(false)
2015-04-19 18:17:47 +03:00
{
Logger* const logger = Logger::instance();
2015-04-19 18:17:47 +03:00
initResumeFolder();
m_bigRatioTimer = new QTimer(this);
m_bigRatioTimer->setInterval(10000);
connect(m_bigRatioTimer, SIGNAL(timeout()), SLOT(processBigRatios()));
// Set severity level of libtorrent session
int alertMask = libt::alert::error_notification
| libt::alert::peer_notification
| libt::alert::port_mapping_notification
| libt::alert::storage_notification
| libt::alert::tracker_notification
| libt::alert::status_notification
| libt::alert::ip_block_notification
| libt::alert::progress_notification
| libt::alert::stats_notification
;
2016-04-28 10:56:58 +03:00
#if LIBTORRENT_VERSION_NUM < 10100
libt::fingerprint fingerprint(PEER_ID, QBT_VERSION_MAJOR, QBT_VERSION_MINOR, QBT_VERSION_BUGFIX, QBT_VERSION_BUILD);
2016-06-03 17:03:17 +03:00
std::string peerId = fingerprint.to_string();
const ushort port = this->port();
std::pair<int, int> ports(port, port);
const QString ip = getListeningIPs().first();
m_nativeSession = new libt::session(fingerprint, ports, ip.isEmpty() ? 0 : ip.toLatin1().constData(), 0, alertMask);
libt::session_settings sessionSettings = m_nativeSession->settings();
sessionSettings.user_agent = USER_AGENT;
sessionSettings.upnp_ignore_nonrouters = true;
sessionSettings.use_dht_as_fallback = false;
// Disable support for SSL torrents for now
sessionSettings.ssl_listen = 0;
// To prevent ISPs from blocking seeding
sessionSettings.lazy_bitfields = true;
// Speed up exit
sessionSettings.stop_tracker_timeout = 1;
sessionSettings.auto_scrape_interval = 1200; // 20 minutes
sessionSettings.auto_scrape_min_interval = 900; // 15 minutes
sessionSettings.connection_speed = 20; // default is 10
sessionSettings.no_connect_privileged_ports = false;
sessionSettings.seed_choking_algorithm = libt::session_settings::fastest_upload;
configure(sessionSettings);
m_nativeSession->set_settings(sessionSettings);
configureListeningInterface();
2016-04-28 10:56:58 +03:00
m_nativeSession->set_alert_dispatch([this](std::auto_ptr<libt::alert> alertPtr)
{
dispatchAlerts(alertPtr.release());
2016-04-28 10:56:58 +03:00
});
#else
std::string peerId = libt::generate_fingerprint(PEER_ID, QBT_VERSION_MAJOR, QBT_VERSION_MINOR, QBT_VERSION_BUGFIX, QBT_VERSION_BUILD);
2016-06-03 17:03:17 +03:00
libt::settings_pack pack;
pack.set_int(libt::settings_pack::alert_mask, alertMask);
pack.set_str(libt::settings_pack::peer_fingerprint, peerId);
pack.set_bool(libt::settings_pack::listen_system_port_fallback, false);
pack.set_str(libt::settings_pack::user_agent, USER_AGENT);
pack.set_bool(libt::settings_pack::upnp_ignore_nonrouters, true);
pack.set_bool(libt::settings_pack::use_dht_as_fallback, false);
// Disable support for SSL torrents for now
pack.set_int(libt::settings_pack::ssl_listen, 0);
// To prevent ISPs from blocking seeding
pack.set_bool(libt::settings_pack::lazy_bitfields, true);
// Speed up exit
pack.set_int(libt::settings_pack::stop_tracker_timeout, 1);
pack.set_int(libt::settings_pack::auto_scrape_interval, 1200); // 20 minutes
pack.set_int(libt::settings_pack::auto_scrape_min_interval, 900); // 15 minutes
pack.set_int(libt::settings_pack::connection_speed, 20); // default is 10
pack.set_bool(libt::settings_pack::no_connect_privileged_ports, false);
pack.set_int(libt::settings_pack::seed_choking_algorithm, libt::settings_pack::fastest_upload);
configure(pack);
m_nativeSession = new libt::session(pack, 0);
2016-04-28 10:56:58 +03:00
m_nativeSession->set_alert_notify([this]()
{
QMetaObject::invokeMethod(this, "readAlerts", Qt::QueuedConnection);
});
#endif
2015-04-19 18:17:47 +03:00
// Enabling plugins
//m_nativeSession->add_extension(&libt::create_metadata_plugin);
m_nativeSession->add_extension(&libt::create_ut_metadata_plugin);
2016-06-03 17:03:17 +03:00
if (isPeXEnabled())
2015-04-19 18:17:47 +03:00
m_nativeSession->add_extension(&libt::create_ut_pex_plugin);
m_nativeSession->add_extension(&libt::create_smart_ban_plugin);
logger->addMessage(tr("Peer ID: ") + QString::fromStdString(peerId));
2016-06-03 17:03:17 +03:00
logger->addMessage(tr("HTTP User-Agent is '%1'").arg(USER_AGENT));
logger->addMessage(tr("DHT support [%1]").arg(isDHTEnabled() ? tr("ON") : tr("OFF")), Log::INFO);
logger->addMessage(tr("Local Peer Discovery support [%1]").arg(isLSDEnabled() ? tr("ON") : tr("OFF")), Log::INFO);
logger->addMessage(tr("PeX support [%1]").arg(isPeXEnabled() ? tr("ON") : tr("OFF")), Log::INFO);
logger->addMessage(tr("Anonymous mode [%1]").arg(isAnonymousModeEnabled() ? tr("ON") : tr("OFF")), Log::INFO);
logger->addMessage(tr("Encryption support [%1]")
.arg(encryption() == 0 ? tr("ON") : encryption() == 1 ? tr("FORCED") : tr("OFF"))
, Log::INFO);
if (isIPFilteringEnabled()) {
// Manually banned IPs are handled in that function too(in the slots)
2016-06-03 17:03:17 +03:00
enableIPFilter();
}
else {
// Add the banned IPs
libt::ip_filter filter;
processBannedIPs(filter);
m_nativeSession->set_ip_filter(filter);
}
2016-06-03 17:03:17 +03:00
m_categories = map_cast(m_storedCategories);
if (isSubcategoriesEnabled()) {
// if subcategories support changed manually
m_categories = expandCategories(m_categories);
m_storedCategories = map_cast(m_categories);
}
2015-04-19 18:17:47 +03:00
m_refreshTimer = new QTimer(this);
m_refreshTimer->setInterval(refreshInterval());
2015-04-19 18:17:47 +03:00
connect(m_refreshTimer, SIGNAL(timeout()), SLOT(refresh()));
m_refreshTimer->start();
// Regular saving of fastresume data
m_resumeDataTimer = new QTimer(this);
m_resumeDataTimer->setInterval(saveResumeDataInterval() * 60 * 1000);
2015-04-19 18:17:47 +03:00
connect(m_resumeDataTimer, SIGNAL(timeout()), SLOT(generateResumeData()));
m_statistics = new Statistics(this);
updateRatioTimer();
populateAdditionalTrackers();
enableTracker(isTrackerEnabled());
connect(Net::ProxyConfigurationManager::instance(), SIGNAL(proxyConfigurationChanged()), SLOT(configureDeferred()));
// Network configuration monitor
connect(&m_networkManager, SIGNAL(onlineStateChanged(bool)), SLOT(networkOnlineStateChanged(bool)));
connect(&m_networkManager, SIGNAL(configurationAdded(const QNetworkConfiguration&)), SLOT(networkConfigurationChange(const QNetworkConfiguration&)));
connect(&m_networkManager, SIGNAL(configurationRemoved(const QNetworkConfiguration&)), SLOT(networkConfigurationChange(const QNetworkConfiguration&)));
connect(&m_networkManager, SIGNAL(configurationChanged(const QNetworkConfiguration&)), SLOT(networkConfigurationChange(const QNetworkConfiguration&)));
m_ioThread = new QThread(this);
m_resumeDataSavingManager = new ResumeDataSavingManager(m_resumeFolderPath);
m_resumeDataSavingManager->moveToThread(m_ioThread);
connect(m_ioThread, SIGNAL(finished()), m_resumeDataSavingManager, SLOT(deleteLater()));
m_ioThread->start();
2015-04-19 18:17:47 +03:00
m_resumeDataTimer->start();
// initialize PortForwarder instance
Net::PortForwarder::initInstance(m_nativeSession);
qDebug("* BitTorrent Session constructed");
}
bool Session::isDHTEnabled() const
{
return m_isDHTEnabled;
}
void Session::setDHTEnabled(bool enabled)
{
if (enabled != m_isDHTEnabled) {
m_isDHTEnabled = enabled;
configureDeferred();
2016-06-03 17:03:17 +03:00
Logger::instance()->addMessage(
tr("DHT support [%1]").arg(enabled ? tr("ON") : tr("OFF")), Log::INFO);
}
2015-04-19 18:17:47 +03:00
}
bool Session::isLSDEnabled() const
{
return m_isLSDEnabled;
}
void Session::setLSDEnabled(bool enabled)
{
if (enabled != m_isLSDEnabled) {
m_isLSDEnabled = enabled;
configureDeferred();
2016-06-03 17:03:17 +03:00
Logger::instance()->addMessage(
tr("Local Peer Discovery support [%1]").arg(enabled ? tr("ON") : tr("OFF"))
, Log::INFO);
}
}
bool Session::isPeXEnabled() const
{
return m_isPeXEnabled;
}
void Session::setPeXEnabled(bool enabled)
{
m_isPeXEnabled = enabled;
if (m_wasPexEnabled != enabled)
Logger::instance()->addMessage(tr("Restart is required to toggle PeX support"), Log::WARNING);
2015-04-19 18:17:47 +03:00
}
bool Session::isTempPathEnabled() const
{
return m_isTempPathEnabled;
2015-04-19 18:17:47 +03:00
}
void Session::setTempPathEnabled(bool enabled)
2015-04-19 18:17:47 +03:00
{
if (enabled != isTempPathEnabled()) {
m_isTempPathEnabled = enabled;
foreach (TorrentHandle *const torrent, m_torrents)
torrent->handleTempPathChanged();
}
2015-04-19 18:17:47 +03:00
}
bool Session::isAppendExtensionEnabled() const
2015-04-19 18:17:47 +03:00
{
return m_isAppendExtensionEnabled;
}
void Session::setAppendExtensionEnabled(bool enabled)
{
if (isAppendExtensionEnabled() != enabled) {
// append or remove .!qB extension for incomplete files
foreach (TorrentHandle *const torrent, m_torrents)
torrent->handleAppendExtensionToggled();
m_isAppendExtensionEnabled = enabled;
}
}
uint Session::refreshInterval() const
{
return m_refreshInterval;
}
void Session::setRefreshInterval(uint value)
{
if (value != refreshInterval()) {
m_refreshTimer->setInterval(value);
m_refreshInterval = value;
}
}
bool Session::isPreallocationEnabled() const
{
return m_isPreallocationEnabled;
}
void Session::setPreallocationEnabled(bool enabled)
{
m_isPreallocationEnabled = enabled;
}
QString Session::torrentExportDirectory() const
{
return Utils::Fs::fromNativePath(m_torrentExportDirectory);
}
void Session::setTorrentExportDirectory(QString path)
{
path = Utils::Fs::fromNativePath(path);
if (path != torrentExportDirectory())
m_torrentExportDirectory = path;
}
QString Session::finishedTorrentExportDirectory() const
{
return Utils::Fs::fromNativePath(m_finishedTorrentExportDirectory);
}
void Session::setFinishedTorrentExportDirectory(QString path)
{
path = Utils::Fs::fromNativePath(path);
if (path != finishedTorrentExportDirectory())
m_finishedTorrentExportDirectory = path;
2015-04-19 18:17:47 +03:00
}
QString Session::defaultSavePath() const
{
return Utils::Fs::fromNativePath(m_defaultSavePath);
2015-04-19 18:17:47 +03:00
}
QString Session::tempPath() const
{
return Utils::Fs::fromNativePath(m_tempPath);
2015-04-19 18:17:47 +03:00
}
QString Session::torrentTempPath(const InfoHash &hash) const
{
return tempPath()
+ static_cast<QString>(hash).left(7)
+ "/";
}
bool Session::isValidCategoryName(const QString &name)
{
2016-03-06 09:25:55 +03:00
QRegExp re(R"(^([^\\\/]|[^\\\/]([^\\\/]|\/(?=[^\/]))*[^\\\/])$)");
if (!name.isEmpty() && (re.indexIn(name) != 0)) {
qDebug() << "Incorrect category name:" << name;
return false;
}
return true;
}
QStringList Session::expandCategory(const QString &category)
{
QStringList result;
if (!isValidCategoryName(category))
return result;
int index = 0;
while ((index = category.indexOf('/', index)) >= 0) {
result << category.left(index);
++index;
}
result << category;
return result;
}
QStringList Session::categories() const
{
return m_categories.keys();
}
QString Session::categorySavePath(const QString &categoryName) const
{
QString basePath = m_defaultSavePath;
if (categoryName.isEmpty()) return basePath;
2016-03-06 09:25:55 +03:00
QString path = m_categories.value(categoryName);
if (path.isEmpty()) // use implicit save path
path = Utils::Fs::toValidFileSystemName(categoryName, true);
if (!QDir::isAbsolutePath(path))
2016-03-06 09:25:55 +03:00
path.prepend(basePath);
2016-03-06 09:25:55 +03:00
return normalizeSavePath(path);
}
bool Session::addCategory(const QString &name, const QString &savePath)
{
if (name.isEmpty()) return false;
if (!isValidCategoryName(name) || m_categories.contains(name))
return false;
if (isSubcategoriesEnabled()) {
foreach (const QString &parent, expandCategory(name)) {
if ((parent != name) && !m_categories.contains(parent)) {
m_categories[parent] = "";
emit categoryAdded(parent);
}
}
}
m_categories[name] = savePath;
m_storedCategories = map_cast(m_categories);
emit categoryAdded(name);
return true;
}
bool Session::editCategory(const QString &name, const QString &savePath)
{
if (!m_categories.contains(name)) return false;
if (categorySavePath(name) == savePath) return false;
m_categories[name] = savePath;
2016-05-08 22:47:50 +03:00
if (isDisableAutoTMMWhenCategorySavePathChanged()) {
foreach (TorrentHandle *const torrent, torrents())
if (torrent->category() == name)
2016-05-08 22:47:50 +03:00
torrent->setAutoTMMEnabled(false);
}
else {
foreach (TorrentHandle *const torrent, torrents())
if (torrent->category() == name)
torrent->handleCategorySavePathChanged();
}
return true;
}
bool Session::removeCategory(const QString &name)
{
foreach (TorrentHandle *const torrent, torrents())
if (torrent->belongsToCategory(name))
torrent->setCategory("");
// remove stored category and its subcategories if exist
bool result = false;
if (isSubcategoriesEnabled()) {
// remove subcategories
QString test = name + "/";
foreach (const QString &category, m_categories.keys()) {
if (category.startsWith(test)) {
m_categories.remove(category);
result = true;
emit categoryRemoved(category);
}
}
}
result = (m_categories.remove(name) > 0) || result;
if (result) {
// update stored categories
m_storedCategories = map_cast(m_categories);
emit categoryRemoved(name);
}
return result;
}
bool Session::isSubcategoriesEnabled() const
{
return m_isSubcategoriesEnabled;
}
void Session::setSubcategoriesEnabled(bool value)
{
if (isSubcategoriesEnabled() == value) return;
if (value) {
// expand categories to include all parent categories
m_categories = expandCategories(m_categories);
// update stored categories
m_storedCategories = map_cast(m_categories);
}
else {
// reload categories
m_categories = map_cast(m_storedCategories);
}
m_isSubcategoriesEnabled = value;
emit subcategoriesSupportChanged();
}
2016-05-08 22:47:50 +03:00
bool Session::isAutoTMMDisabledByDefault() const
{
return m_isAutoTMMDisabledByDefault;
}
2016-05-08 22:47:50 +03:00
void Session::setAutoTMMDisabledByDefault(bool value)
{
m_isAutoTMMDisabledByDefault = value;
}
2016-05-08 22:47:50 +03:00
bool Session::isDisableAutoTMMWhenCategoryChanged() const
{
return m_isDisableAutoTMMWhenCategoryChanged;
}
2016-05-08 22:47:50 +03:00
void Session::setDisableAutoTMMWhenCategoryChanged(bool value)
{
m_isDisableAutoTMMWhenCategoryChanged = value;
}
2016-05-08 22:47:50 +03:00
bool Session::isDisableAutoTMMWhenDefaultSavePathChanged() const
{
return m_isDisableAutoTMMWhenDefaultSavePathChanged;
}
2016-05-08 22:47:50 +03:00
void Session::setDisableAutoTMMWhenDefaultSavePathChanged(bool value)
{
m_isDisableAutoTMMWhenDefaultSavePathChanged = value;
}
2016-05-08 22:47:50 +03:00
bool Session::isDisableAutoTMMWhenCategorySavePathChanged() const
{
return m_isDisableAutoTMMWhenCategorySavePathChanged;
}
2016-05-08 22:47:50 +03:00
void Session::setDisableAutoTMMWhenCategorySavePathChanged(bool value)
{
m_isDisableAutoTMMWhenCategorySavePathChanged = value;
}
bool Session::isAddTorrentPaused() const
{
return m_isAddTorrentPaused;
}
void Session::setAddTorrentPaused(bool value)
{
m_isAddTorrentPaused = value;
}
bool Session::isTrackerEnabled() const
{
return m_isTrackerEnabled;
}
void Session::setTrackerEnabled(bool enabled)
{
if (isTrackerEnabled() != enabled) {
enableTracker(enabled);
m_isTrackerEnabled = enabled;
}
}
2015-04-19 18:17:47 +03:00
qreal Session::globalMaxRatio() const
{
return m_globalMaxRatio;
}
// Torrents will a ratio superior to the given value will
// be automatically deleted
void Session::setGlobalMaxRatio(qreal ratio)
{
if (ratio < 0)
ratio = -1.;
if (ratio != globalMaxRatio()) {
m_globalMaxRatio = ratio;
updateRatioTimer();
}
}
2015-04-19 18:17:47 +03:00
// Main destructor
Session::~Session()
{
// Do some BT related saving
saveResumeData();
// We must delete FilterParserThread
// before we delete libtorrent::session
if (m_filterParser)
delete m_filterParser;
// We must delete PortForwarderImpl before
// we delete libtorrent::session
Net::PortForwarder::freeInstance();
qDebug("Deleting the session");
delete m_nativeSession;
m_ioThread->quit();
m_ioThread->wait();
2015-04-19 18:17:47 +03:00
m_resumeFolderLock.close();
m_resumeFolderLock.remove();
}
void Session::initInstance()
{
if (!m_instance) {
2015-04-19 18:17:47 +03:00
m_instance = new Session;
// BandwidthScheduler::start() depends on Session being fully constructed
if (m_instance->isBandwidthSchedulerEnabled())
m_instance->enableBandwidthScheduler();
}
2015-04-19 18:17:47 +03:00
}
void Session::freeInstance()
{
if (m_instance) {
delete m_instance;
m_instance = 0;
}
}
Session *Session::instance()
{
return m_instance;
}
void Session::adjustLimits()
2015-04-19 18:17:47 +03:00
{
if (isQueueingSystemEnabled()) {
2016-06-03 17:03:17 +03:00
#if LIBTORRENT_VERSION_NUM < 10100
libt::session_settings sessionSettings(m_nativeSession->settings());
2015-04-19 18:17:47 +03:00
adjustLimits(sessionSettings);
m_nativeSession->set_settings(sessionSettings);
2016-06-03 17:03:17 +03:00
#else
2016-10-30 00:11:52 +03:00
libt::settings_pack settingsPack = m_nativeSession->get_settings();
2016-06-03 17:03:17 +03:00
adjustLimits(settingsPack);
m_nativeSession->apply_settings(settingsPack);
#endif
2015-04-19 18:17:47 +03:00
}
2016-06-03 17:03:17 +03:00
}
2015-04-19 18:17:47 +03:00
2016-06-03 17:03:17 +03:00
// Set BitTorrent session configuration
void Session::configure()
{
qDebug("Configuring session");
if (!m_deferredConfigureScheduled) return; // Obtaining the lock is expensive, let's check early
QWriteLocker locker(&m_lock);
if (!m_deferredConfigureScheduled) return; // something might have changed while we were getting the lock
2016-01-20 10:15:10 +03:00
#if LIBTORRENT_VERSION_NUM < 10100
2016-06-03 17:03:17 +03:00
libt::session_settings sessionSettings = m_nativeSession->settings();
configure(sessionSettings);
m_nativeSession->set_settings(sessionSettings);
2016-01-20 10:15:10 +03:00
#else
2016-10-30 00:11:52 +03:00
libt::settings_pack settingsPack = m_nativeSession->get_settings();
2016-06-03 17:03:17 +03:00
configure(settingsPack);
m_nativeSession->apply_settings(settingsPack);
2016-01-20 10:15:10 +03:00
#endif
2016-06-03 17:03:17 +03:00
if (m_IPFilteringChanged) {
if (isIPFilteringEnabled())
2016-06-03 17:03:17 +03:00
enableIPFilter();
else
disableIPFilter();
m_IPFilteringChanged = false;
}
m_deferredConfigureScheduled = false;
qDebug("Session configured");
2015-04-19 18:17:47 +03:00
}
void Session::processBannedIPs(libt::ip_filter &filter)
2015-04-19 18:17:47 +03:00
{
2016-06-03 17:03:17 +03:00
// First, import current filter
foreach (const QString &ip, m_bannedIPs.value()) {
boost::system::error_code ec;
libt::address addr = libt::address::from_string(ip.toLatin1().constData(), ec);
Q_ASSERT(!ec);
2016-10-30 00:11:52 +03:00
if (!ec)
filter.add_rule(addr, addr, libt::ip_filter::blocked);
2015-04-19 18:17:47 +03:00
}
}
2016-06-03 17:03:17 +03:00
#if LIBTORRENT_VERSION_NUM >= 10100
void Session::adjustLimits(libt::settings_pack &settingsPack)
2015-04-19 18:17:47 +03:00
{
//Internally increase the queue limits to ensure that the magnet is started
2016-06-03 17:03:17 +03:00
int maxDownloads = maxActiveDownloads();
int maxActive = maxActiveTorrents();
2016-06-03 17:03:17 +03:00
settingsPack.set_int(libt::settings_pack::active_downloads
, maxDownloads > -1 ? maxDownloads + m_extraLimit : maxDownloads);
settingsPack.set_int(libt::settings_pack::active_limit
, maxActive > -1 ? maxActive + m_extraLimit : maxActive);
2015-04-19 18:17:47 +03:00
}
2016-06-03 17:03:17 +03:00
void Session::configure(libtorrent::settings_pack &settingsPack)
2015-04-19 18:17:47 +03:00
{
2016-06-03 17:03:17 +03:00
Logger* const logger = Logger::instance();
2015-04-19 18:17:47 +03:00
2016-06-03 17:03:17 +03:00
if (m_listenInterfaceChanged) {
const ushort port = this->port();
std::pair<int, int> ports(port, port);
settingsPack.set_int(libt::settings_pack::max_retry_port_bind, ports.second - ports.first);
foreach (QString ip, getListeningIPs()) {
libt::error_code ec;
std::string interfacesStr;
if (ip.isEmpty()) {
ip = QLatin1String("0.0.0.0");
interfacesStr = std::string((QString("%1:%2").arg(ip).arg(port)).toLatin1().constData());
logger->addMessage(tr("qBittorrent is trying to listen on any interface port: %1"
, "e.g: qBittorrent is trying to listen on any interface port: TCP/6881")
.arg(QString::number(port))
, Log::INFO);
settingsPack.set_str(libt::settings_pack::listen_interfaces, interfacesStr);
break;
}
libt::address addr = libt::address::from_string(ip.toLatin1().constData(), ec);
if (!ec) {
interfacesStr = std::string((addr.is_v6() ? QString("[%1]:%2") : QString("%1:%2"))
.arg(ip).arg(port).toLatin1().constData());
logger->addMessage(tr("qBittorrent is trying to listen on interface %1 port: %2"
, "e.g: qBittorrent is trying to listen on interface 192.168.0.1 port: TCP/6881")
.arg(ip).arg(port)
, Log::INFO);
settingsPack.set_str(libt::settings_pack::listen_interfaces, interfacesStr);
break;
}
}
2015-04-19 18:17:47 +03:00
2016-06-03 17:03:17 +03:00
m_listenInterfaceChanged = false;
2015-04-19 18:17:47 +03:00
}
2016-06-03 17:03:17 +03:00
const bool altSpeedLimitEnabled = isAltGlobalSpeedLimitEnabled();
settingsPack.set_int(libt::settings_pack::download_rate_limit, altSpeedLimitEnabled ? altGlobalDownloadSpeedLimit() : globalDownloadSpeedLimit());
settingsPack.set_int(libt::settings_pack::upload_rate_limit, altSpeedLimitEnabled ? altGlobalUploadSpeedLimit() : globalUploadSpeedLimit());
2015-04-19 18:17:47 +03:00
2016-06-03 17:03:17 +03:00
// The most secure, rc4 only so that all streams are encrypted
settingsPack.set_int(libt::settings_pack::allowed_enc_level, libt::settings_pack::pe_rc4);
settingsPack.set_bool(libt::settings_pack::prefer_rc4, true);
switch (encryption()) {
case 0: //Enabled
settingsPack.set_int(libt::settings_pack::out_enc_policy, libt::settings_pack::pe_enabled);
settingsPack.set_int(libt::settings_pack::in_enc_policy, libt::settings_pack::pe_enabled);
break;
case 1: // Forced
settingsPack.set_int(libt::settings_pack::out_enc_policy, libt::settings_pack::pe_forced);
settingsPack.set_int(libt::settings_pack::in_enc_policy, libt::settings_pack::pe_forced);
break;
default: // Disabled
settingsPack.set_int(libt::settings_pack::out_enc_policy, libt::settings_pack::pe_disabled);
settingsPack.set_int(libt::settings_pack::in_enc_policy, libt::settings_pack::pe_disabled);
}
auto proxyManager = Net::ProxyConfigurationManager::instance();
Net::ProxyConfiguration proxyConfig = proxyManager->proxyConfiguration();
if (m_useProxy || (proxyConfig.type != Net::ProxyType::None)) {
if (proxyConfig.type != Net::ProxyType::None) {
settingsPack.set_str(libt::settings_pack::proxy_hostname, proxyConfig.ip.toStdString());
2016-06-03 17:03:17 +03:00
settingsPack.set_int(libt::settings_pack::proxy_port, proxyConfig.port);
if (proxyManager->isAuthenticationRequired()) {
settingsPack.set_str(libt::settings_pack::proxy_username, proxyConfig.username.toStdString());
settingsPack.set_str(libt::settings_pack::proxy_password, proxyConfig.password.toStdString());
2016-06-03 17:03:17 +03:00
}
settingsPack.set_bool(libt::settings_pack::proxy_peer_connections, isProxyPeerConnectionsEnabled());
2015-04-19 18:17:47 +03:00
}
2016-06-03 17:03:17 +03:00
switch (proxyConfig.type) {
case Net::ProxyType::HTTP:
settingsPack.set_int(libt::settings_pack::proxy_type, libt::settings_pack::http);
break;
case Net::ProxyType::HTTP_PW:
settingsPack.set_int(libt::settings_pack::proxy_type, libt::settings_pack::http_pw);
break;
case Net::ProxyType::SOCKS4:
settingsPack.set_int(libt::settings_pack::proxy_type, libt::settings_pack::socks4);
break;
case Net::ProxyType::SOCKS5:
settingsPack.set_int(libt::settings_pack::proxy_type, libt::settings_pack::socks5);
break;
case Net::ProxyType::SOCKS5_PW:
settingsPack.set_int(libt::settings_pack::proxy_type, libt::settings_pack::socks5_pw);
break;
default:
settingsPack.set_int(libt::settings_pack::proxy_type, libt::settings_pack::none);
}
2015-04-19 18:17:47 +03:00
2016-06-03 17:03:17 +03:00
m_useProxy = (proxyConfig.type != Net::ProxyType::None);
2015-04-19 18:17:47 +03:00
}
2016-06-03 17:03:17 +03:00
settingsPack.set_bool(libt::settings_pack::force_proxy, m_useProxy ? isForceProxyEnabled() : false);
2015-04-19 18:17:47 +03:00
2016-06-03 17:03:17 +03:00
const bool announceToAll = announceToAllTrackers();
settingsPack.set_bool(libt::settings_pack::announce_to_all_trackers, announceToAll);
settingsPack.set_bool(libt::settings_pack::announce_to_all_tiers, announceToAll);
2015-04-19 18:17:47 +03:00
2016-06-03 17:03:17 +03:00
const int cacheSize = diskCacheSize();
settingsPack.set_int(libt::settings_pack::cache_size, (cacheSize > 0) ? cacheSize * 64 : -1);
settingsPack.set_int(libt::settings_pack::cache_expiry, diskCacheTTL());
qDebug() << "Using a disk cache size of" << cacheSize << "MiB";
2015-04-19 18:17:47 +03:00
2016-06-03 17:03:17 +03:00
libt::settings_pack::io_buffer_mode_t mode = useOSCache() ? libt::settings_pack::enable_os_cache
: libt::settings_pack::disable_os_cache;
settingsPack.set_int(libt::settings_pack::disk_io_read_mode, mode);
settingsPack.set_int(libt::settings_pack::disk_io_write_mode, mode);
2015-04-19 18:17:47 +03:00
2016-06-03 17:03:17 +03:00
settingsPack.set_bool(libt::settings_pack::anonymous_mode, isAnonymousModeEnabled());
2015-04-19 18:17:47 +03:00
2016-06-03 17:03:17 +03:00
// Queueing System
if (isQueueingSystemEnabled()) {
adjustLimits(settingsPack);
2015-04-19 18:17:47 +03:00
2016-06-03 17:03:17 +03:00
settingsPack.set_int(libt::settings_pack::active_seeds, maxActiveUploads());
settingsPack.set_bool(libt::settings_pack::dont_count_slow_torrents, ignoreSlowTorrentsForQueueing());
2015-04-19 18:17:47 +03:00
}
else {
2016-06-03 17:03:17 +03:00
settingsPack.set_int(libt::settings_pack::active_downloads, -1);
settingsPack.set_int(libt::settings_pack::active_seeds, -1);
settingsPack.set_int(libt::settings_pack::active_limit, -1);
2015-04-19 18:17:47 +03:00
}
2016-06-03 17:03:17 +03:00
settingsPack.set_int(libt::settings_pack::active_tracker_limit, -1);
settingsPack.set_int(libt::settings_pack::active_dht_limit, -1);
settingsPack.set_int(libt::settings_pack::active_lsd_limit, -1);
// 1 active torrent force 2 connections. If you have more active torrents * 2 than connection limit,
// connection limit will get extended. Multiply max connections or active torrents by 10 for queue.
// Ignore -1 values because we don't want to set a max int message queue
settingsPack.set_int(libt::settings_pack::alert_queue_size, std::max(1000,
10 * std::max(maxActiveTorrents() * 2, maxConnections())));
2015-04-19 18:17:47 +03:00
2016-06-03 17:03:17 +03:00
// Outgoing ports
settingsPack.set_int(libt::settings_pack::outgoing_port, outgoingPortsMin());
settingsPack.set_int(libt::settings_pack::num_outgoing_ports, outgoingPortsMax() - outgoingPortsMin() + 1);
// Ignore limits on LAN
settingsPack.set_bool(libt::settings_pack::ignore_limits_on_local_network, ignoreLimitsOnLAN());
// Include overhead in transfer limits
settingsPack.set_bool(libt::settings_pack::rate_limit_ip_overhead, includeOverheadInLimits());
// IP address to announce to trackers
settingsPack.set_str(libt::settings_pack::announce_ip, announceIP().toStdString());
2016-06-03 17:03:17 +03:00
// Super seeding
settingsPack.set_bool(libt::settings_pack::strict_super_seeding, isSuperSeedingEnabled());
// * Max Half-open connections
settingsPack.set_int(libt::settings_pack::half_open_limit, maxHalfOpenConnections());
// * Max connections limit
settingsPack.set_int(libt::settings_pack::connections_limit, maxConnections());
// * Global max upload slots
settingsPack.set_int(libt::settings_pack::unchoke_slots_limit, maxUploads());
// uTP
settingsPack.set_bool(libt::settings_pack::enable_incoming_utp, isUTPEnabled());
settingsPack.set_bool(libt::settings_pack::enable_outgoing_utp, isUTPEnabled());
// uTP rate limiting
settingsPack.set_bool(libt::settings_pack::rate_limit_utp, isUTPRateLimited());
settingsPack.set_int(libt::settings_pack::mixed_mode_algorithm, isUTPRateLimited()
? libt::settings_pack::prefer_tcp
: libt::settings_pack::peer_proportional);
settingsPack.set_bool(libt::settings_pack::apply_ip_filter_to_trackers, isTrackerFilteringEnabled());
settingsPack.set_bool(libt::settings_pack::enable_dht, isDHTEnabled());
if (isDHTEnabled())
settingsPack.set_str(libt::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");
2016-06-03 17:03:17 +03:00
settingsPack.set_bool(libt::settings_pack::enable_lsd, isLSDEnabled());
}
2016-06-03 17:03:17 +03:00
#else
void Session::adjustLimits(libt::session_settings &sessionSettings)
{
//Internally increase the queue limits to ensure that the magnet is started
2016-10-30 00:10:42 +03:00
int maxDownloads = maxActiveDownloads();
int maxActive = maxActiveTorrents();
2016-10-30 00:10:42 +03:00
sessionSettings.active_downloads = maxDownloads > -1 ? maxDownloads + m_extraLimit : maxDownloads;
sessionSettings.active_limit = maxActive > -1 ? maxActive + m_extraLimit : maxActive;
}
2016-06-03 17:03:17 +03:00
void Session::configure(libtorrent::session_settings &sessionSettings)
2015-04-19 18:17:47 +03:00
{
const bool altSpeedLimitEnabled = isAltGlobalSpeedLimitEnabled();
sessionSettings.download_rate_limit = altSpeedLimitEnabled ? altGlobalDownloadSpeedLimit() : globalDownloadSpeedLimit();
sessionSettings.upload_rate_limit = altSpeedLimitEnabled ? altGlobalUploadSpeedLimit() : globalUploadSpeedLimit();
2016-06-03 17:03:17 +03:00
// The most secure, rc4 only so that all streams are encrypted
libt::pe_settings encryptionSettings;
2015-04-19 18:17:47 +03:00
encryptionSettings.allowed_enc_level = libt::pe_settings::rc4;
encryptionSettings.prefer_rc4 = true;
2016-06-03 17:03:17 +03:00
switch (encryption()) {
2015-04-19 18:17:47 +03:00
case 0: //Enabled
encryptionSettings.out_enc_policy = libt::pe_settings::enabled;
encryptionSettings.in_enc_policy = libt::pe_settings::enabled;
break;
case 1: // Forced
encryptionSettings.out_enc_policy = libt::pe_settings::forced;
encryptionSettings.in_enc_policy = libt::pe_settings::forced;
break;
default: // Disabled
encryptionSettings.out_enc_policy = libt::pe_settings::disabled;
encryptionSettings.in_enc_policy = libt::pe_settings::disabled;
}
m_nativeSession->set_pe_settings(encryptionSettings);
2016-06-03 17:03:17 +03:00
auto proxyManager = Net::ProxyConfigurationManager::instance();
Net::ProxyConfiguration proxyConfig = proxyManager->proxyConfiguration();
if (m_useProxy || (proxyConfig.type != Net::ProxyType::None)) {
libt::proxy_settings proxySettings;
if (proxyConfig.type != Net::ProxyType::None) {
proxySettings.hostname = proxyConfig.ip.toStdString();
2016-06-03 17:03:17 +03:00
proxySettings.port = proxyConfig.port;
if (proxyManager->isAuthenticationRequired()) {
proxySettings.username = proxyConfig.username.toStdString();
proxySettings.password = proxyConfig.password.toStdString();
2016-06-03 17:03:17 +03:00
}
proxySettings.proxy_peer_connections = isProxyPeerConnectionsEnabled();
}
2016-06-03 17:03:17 +03:00
switch (proxyConfig.type) {
case Net::ProxyType::HTTP:
proxySettings.type = libt::proxy_settings::http;
break;
case Net::ProxyType::HTTP_PW:
proxySettings.type = libt::proxy_settings::http_pw;
break;
case Net::ProxyType::SOCKS4:
proxySettings.type = libt::proxy_settings::socks4;
break;
case Net::ProxyType::SOCKS5:
proxySettings.type = libt::proxy_settings::socks5;
break;
case Net::ProxyType::SOCKS5_PW:
proxySettings.type = libt::proxy_settings::socks5_pw;
break;
default:
proxySettings.type = libt::proxy_settings::none;
2015-04-19 18:17:47 +03:00
}
2016-06-03 17:03:17 +03:00
m_nativeSession->set_proxy(proxySettings);
m_useProxy = (proxyConfig.type != Net::ProxyType::None);
2015-04-19 18:17:47 +03:00
}
sessionSettings.force_proxy = m_useProxy ? isForceProxyEnabled() : false;
2015-04-19 18:17:47 +03:00
bool announceToAll = announceToAllTrackers();
sessionSettings.announce_to_all_trackers = announceToAll;
sessionSettings.announce_to_all_tiers = announceToAll;
int cacheSize = diskCacheSize();
2016-10-30 00:11:52 +03:00
sessionSettings.cache_size = (cacheSize > 0) ? cacheSize * 64 : -1;
sessionSettings.cache_expiry = diskCacheTTL();
qDebug() << "Using a disk cache size of" << cacheSize << "MiB";
libt::session_settings::io_buffer_mode_t mode = useOSCache() ? libt::session_settings::enable_os_cache
: libt::session_settings::disable_os_cache;
2015-04-19 18:17:47 +03:00
sessionSettings.disk_io_read_mode = mode;
sessionSettings.disk_io_write_mode = mode;
sessionSettings.anonymous_mode = isAnonymousModeEnabled();
2015-04-19 18:17:47 +03:00
// Queueing System
if (isQueueingSystemEnabled()) {
2015-04-19 18:17:47 +03:00
adjustLimits(sessionSettings);
sessionSettings.active_seeds = maxActiveUploads();
sessionSettings.dont_count_slow_torrents = ignoreSlowTorrentsForQueueing();
2015-04-19 18:17:47 +03:00
}
else {
sessionSettings.active_downloads = -1;
sessionSettings.active_seeds = -1;
sessionSettings.active_limit = -1;
}
sessionSettings.active_tracker_limit = -1;
sessionSettings.active_dht_limit = -1;
sessionSettings.active_lsd_limit = -1;
// 1 active torrent force 2 connections. If you have more active torrents * 2 than connection limit,
// connection limit will get extended. Multiply max connections or active torrents by 10 for queue.
// Ignore -1 values because we don't want to set a max int message queue
sessionSettings.alert_queue_size = std::max(1000,
10 * std::max(maxActiveTorrents() * 2, maxConnections()));
2015-04-19 18:17:47 +03:00
// Outgoing ports
sessionSettings.outgoing_ports = std::make_pair(outgoingPortsMin(), outgoingPortsMax());
2015-04-19 18:17:47 +03:00
// Ignore limits on LAN
sessionSettings.ignore_limits_on_local_network = ignoreLimitsOnLAN();
2015-04-19 18:17:47 +03:00
// Include overhead in transfer limits
sessionSettings.rate_limit_ip_overhead = includeOverheadInLimits();
2015-04-19 18:17:47 +03:00
// IP address to announce to trackers
sessionSettings.announce_ip = announceIP().toStdString();
2015-04-19 18:17:47 +03:00
// Super seeding
sessionSettings.strict_super_seeding = isSuperSeedingEnabled();
2015-04-19 18:17:47 +03:00
// * Max Half-open connections
sessionSettings.half_open_limit = maxHalfOpenConnections();
2015-04-19 18:17:47 +03:00
// * Max connections limit
sessionSettings.connections_limit = maxConnections();
2015-04-19 18:17:47 +03:00
// * Global max upload slots
sessionSettings.unchoke_slots_limit = maxUploads();
2015-04-19 18:17:47 +03:00
// uTP
sessionSettings.enable_incoming_utp = isUTPEnabled();
sessionSettings.enable_outgoing_utp = isUTPEnabled();
2015-04-19 18:17:47 +03:00
// uTP rate limiting
sessionSettings.rate_limit_utp = isUTPRateLimited();
2016-10-30 00:11:52 +03:00
sessionSettings.mixed_mode_algorithm = isUTPRateLimited()
? libt::session_settings::prefer_tcp
: libt::session_settings::peer_proportional;
2015-04-19 18:17:47 +03:00
sessionSettings.apply_ip_filter_to_trackers = isTrackerFilteringEnabled();
2015-04-19 18:17:47 +03:00
if (isDHTEnabled()) {
// Add first the routers and then start DHT.
m_nativeSession->add_dht_router(std::make_pair(std::string("dht.libtorrent.org"), 25401));
m_nativeSession->add_dht_router(std::make_pair(std::string("router.bittorrent.com"), 6881));
m_nativeSession->add_dht_router(std::make_pair(std::string("router.utorrent.com"), 6881));
m_nativeSession->add_dht_router(std::make_pair(std::string("dht.transmissionbt.com"), 6881));
m_nativeSession->add_dht_router(std::make_pair(std::string("dht.aelitis.com"), 6881)); // Vuze
2016-06-03 17:03:17 +03:00
m_nativeSession->start_dht();
}
else {
2016-06-03 17:03:17 +03:00
m_nativeSession->stop_dht();
}
2016-06-03 17:03:17 +03:00
if (isLSDEnabled())
m_nativeSession->start_lsd();
else
m_nativeSession->stop_lsd();
2015-04-19 18:17:47 +03:00
}
2016-06-03 17:03:17 +03:00
#endif
2015-04-19 18:17:47 +03:00
void Session::enableTracker(bool enable)
2015-04-19 18:17:47 +03:00
{
Logger *const logger = Logger::instance();
2015-04-19 18:17:47 +03:00
if (enable) {
2015-04-19 18:17:47 +03:00
if (!m_tracker)
m_tracker = new Tracker(this);
if (m_tracker->start())
logger->addMessage(tr("Embedded Tracker [ON]"), Log::INFO);
else
logger->addMessage(tr("Failed to start the embedded tracker!"), Log::CRITICAL);
}
else {
logger->addMessage(tr("Embedded Tracker [OFF]"), Log::INFO);
2015-04-19 18:17:47 +03:00
if (m_tracker)
delete m_tracker;
}
}
2015-04-19 18:17:47 +03:00
void Session::enableBandwidthScheduler()
{
if (!m_bwScheduler) {
m_bwScheduler = new BandwidthScheduler(this);
connect(m_bwScheduler.data(), SIGNAL(switchToAlternativeMode(bool)), this, SLOT(switchToAlternativeMode(bool)));
2015-04-19 18:17:47 +03:00
}
m_bwScheduler->start();
2015-04-19 18:17:47 +03:00
}
void Session::populateAdditionalTrackers()
2015-04-19 18:17:47 +03:00
{
m_additionalTrackerList.clear();
foreach (QString tracker, additionalTrackers().split("\n")) {
tracker = tracker.trimmed();
if (!tracker.isEmpty())
m_additionalTrackerList << tracker;
2015-04-19 18:17:47 +03:00
}
}
void Session::processBigRatios()
{
qDebug("Process big ratios...");
qreal globalMaxRatio = this->globalMaxRatio();
2015-04-19 18:17:47 +03:00
foreach (TorrentHandle *const torrent, m_torrents) {
if (torrent->isSeed()
&& (torrent->ratioLimit() != TorrentHandle::NO_RATIO_LIMIT)
&& !torrent->isForced()) {
2015-04-19 18:17:47 +03:00
const qreal ratio = torrent->realRatio();
qreal ratioLimit = torrent->ratioLimit();
2015-06-17 12:23:16 +03:00
if (ratioLimit == TorrentHandle::USE_GLOBAL_RATIO) {
// If Global Max Ratio is really set...
ratioLimit = globalMaxRatio;
if (ratioLimit < 0) continue;
2015-06-17 12:23:16 +03:00
}
2015-04-19 18:17:47 +03:00
qDebug("Ratio: %f (limit: %f)", ratio, ratioLimit);
Q_ASSERT(ratioLimit >= 0.f);
if ((ratio <= TorrentHandle::MAX_RATIO) && (ratio >= ratioLimit)) {
Logger* const logger = Logger::instance();
if (maxRatioAction() == Remove) {
logger->addMessage(tr("'%1' reached the maximum ratio you set. Removing...").arg(torrent->name()));
2015-04-19 18:17:47 +03:00
deleteTorrent(torrent->hash());
}
else {
// Pause it
if (!torrent->isPaused()) {
logger->addMessage(tr("'%1' reached the maximum ratio you set. Pausing...").arg(torrent->name()));
2015-04-19 18:17:47 +03:00
torrent->pause();
}
}
}
}
}
}
void Session::handleDownloadFailed(const QString &url, const QString &reason)
{
emit downloadFromUrlFailed(url, reason);
}
void Session::handleRedirectedToMagnet(const QString &url, const QString &magnetUri)
{
2016-01-01 16:28:40 +03:00
addTorrent_impl(m_downloadedTorrents.take(url), MagnetUri(magnetUri));
2015-04-19 18:17:47 +03:00
}
2015-07-22 08:51:23 +03:00
void Session::switchToAlternativeMode(bool alternative)
{
changeSpeedLimitMode_impl(alternative);
}
2015-04-19 18:17:47 +03:00
// Add to BitTorrent session the downloaded torrent file
void Session::handleDownloadFinished(const QString &url, const QString &filePath)
{
emit downloadFromUrlFinished(url);
2016-01-01 16:28:40 +03:00
addTorrent_impl(m_downloadedTorrents.take(url), MagnetUri(), TorrentInfo::loadFromFile(filePath));
Utils::Fs::forceRemove(filePath); // remove temporary file
2015-04-19 18:17:47 +03:00
}
// Return the torrent handle, given its hash
TorrentHandle *Session::findTorrent(const InfoHash &hash) const
{
return m_torrents.value(hash);
}
bool Session::hasActiveTorrents() const
{
foreach (TorrentHandle *const torrent, m_torrents)
if (TorrentFilter::ActiveTorrent.match(torrent))
return true;
return false;
}
bool Session::hasUnfinishedTorrents() const
{
foreach (TorrentHandle *const torrent, m_torrents)
if (!torrent->isSeed() && !torrent->isPaused())
return true;
return false;
}
void Session::banIP(const QString &ip)
{
QStringList bannedIPs = m_bannedIPs;
if (!bannedIPs.contains(ip)) {
2016-06-03 17:03:17 +03:00
libt::ip_filter filter = m_nativeSession->get_ip_filter();
boost::system::error_code ec;
libt::address addr = libt::address::from_string(ip.toLatin1().constData(), ec);
Q_ASSERT(!ec);
2016-10-30 00:11:52 +03:00
if (ec) return;
2016-06-03 17:03:17 +03:00
filter.add_rule(addr, addr, libt::ip_filter::blocked);
m_nativeSession->set_ip_filter(filter);
bannedIPs << ip;
bannedIPs.sort();
m_bannedIPs = bannedIPs;
}
2015-04-19 18:17:47 +03:00
}
// Delete a torrent from the session, given its hash
// deleteLocalFiles = true means that the torrent will be removed from the hard-drive too
bool Session::deleteTorrent(const QString &hash, bool deleteLocalFiles)
{
TorrentHandle *const torrent = m_torrents.take(hash);
if (!torrent) return false;
qDebug("Deleting torrent with hash: %s", qPrintable(torrent->hash()));
emit torrentAboutToBeRemoved(torrent);
// Remove it from session
if (deleteLocalFiles) {
if (torrent->savePath(true) == torrentTempPath(torrent->hash())) {
m_savePathsToRemove[torrent->hash()] = torrent->savePath(true);
}
else {
QString rootPath = torrent->rootPath(true);
if (!rootPath.isEmpty())
m_savePathsToRemove[torrent->hash()] = rootPath;
}
2015-04-19 18:17:47 +03:00
m_nativeSession->remove_torrent(torrent->nativeHandle(), libt::session::delete_files);
}
else {
QStringList unwantedFiles;
if (torrent->hasMetadata())
unwantedFiles = torrent->absoluteFilePathsUnwanted();
#if LIBTORRENT_VERSION_NUM < 10100
2015-04-19 18:17:47 +03:00
m_nativeSession->remove_torrent(torrent->nativeHandle());
#else
m_nativeSession->remove_torrent(torrent->nativeHandle(), libt::session::delete_partfile);
#endif
2015-04-19 18:17:47 +03:00
// Remove unwanted and incomplete files
foreach (const QString &unwantedFile, unwantedFiles) {
qDebug("Removing unwanted file: %s", qPrintable(unwantedFile));
Utils::Fs::forceRemove(unwantedFile);
const QString parentFolder = Utils::Fs::branchPath(unwantedFile);
2015-04-19 18:17:47 +03:00
qDebug("Attempt to remove parent folder (if empty): %s", qPrintable(parentFolder));
QDir().rmpath(parentFolder);
}
}
// Remove it from torrent resume directory
QDir resumeDataDir(m_resumeFolderPath);
QStringList filters;
filters << QString("%1.*").arg(torrent->hash());
const QStringList files = resumeDataDir.entryList(filters, QDir::Files, QDir::Unsorted);
foreach (const QString &file, files)
Utils::Fs::forceRemove(resumeDataDir.absoluteFilePath(file));
2015-04-19 18:17:47 +03:00
if (deleteLocalFiles)
Logger::instance()->addMessage(tr("'%1' was removed from transfer list and hard disk.", "'xxx.avi' was removed...").arg(torrent->name()));
else
Logger::instance()->addMessage(tr("'%1' was removed from transfer list.", "'xxx.avi' was removed...").arg(torrent->name()));
delete torrent;
qDebug("Torrent deleted.");
return true;
}
bool Session::cancelLoadMetadata(const InfoHash &hash)
{
if (!m_loadedMetadata.contains(hash)) return false;
m_loadedMetadata.remove(hash);
libt::torrent_handle torrent = m_nativeSession->find_torrent(hash);
if (!torrent.is_valid()) return false;
if (!torrent.status(0).has_metadata) {
// if hidden torrent is still loading metadata...
--m_extraLimit;
adjustLimits();
}
// Remove it from session
m_nativeSession->remove_torrent(torrent, libt::session::delete_files);
qDebug("Preloaded torrent deleted.");
return true;
}
void Session::increaseTorrentsPriority(const QStringList &hashes)
{
std::priority_queue<QPair<int, TorrentHandle *>,
std::vector<QPair<int, TorrentHandle *> >,
std::greater<QPair<int, TorrentHandle *> > > torrentQueue;
// Sort torrents by priority
foreach (const InfoHash &hash, hashes) {
TorrentHandle *const torrent = m_torrents.value(hash);
if (torrent && !torrent->isSeed())
torrentQueue.push(qMakePair(torrent->queuePosition(), torrent));
}
// Increase torrents priority (starting with the ones with highest priority)
while (!torrentQueue.empty()) {
TorrentHandle *const torrent = torrentQueue.top().second;
torrentQueuePositionUp(torrent->nativeHandle());
torrentQueue.pop();
}
}
void Session::decreaseTorrentsPriority(const QStringList &hashes)
{
std::priority_queue<QPair<int, TorrentHandle *>,
std::vector<QPair<int, TorrentHandle *> >,
std::less<QPair<int, TorrentHandle *> > > torrentQueue;
// Sort torrents by priority
foreach (const InfoHash &hash, hashes) {
TorrentHandle *const torrent = m_torrents.value(hash);
if (torrent && !torrent->isSeed())
torrentQueue.push(qMakePair(torrent->queuePosition(), torrent));
}
// Decrease torrents priority (starting with the ones with lowest priority)
while (!torrentQueue.empty()) {
TorrentHandle *const torrent = torrentQueue.top().second;
torrentQueuePositionDown(torrent->nativeHandle());
torrentQueue.pop();
}
foreach (const InfoHash &hash, m_loadedMetadata.keys())
torrentQueuePositionBottom(m_nativeSession->find_torrent(hash));
}
void Session::topTorrentsPriority(const QStringList &hashes)
{
std::priority_queue<QPair<int, TorrentHandle *>,
std::vector<QPair<int, TorrentHandle *> >,
std::greater<QPair<int, TorrentHandle *> > > torrentQueue;
// Sort torrents by priority
foreach (const InfoHash &hash, hashes) {
TorrentHandle *const torrent = m_torrents.value(hash);
if (torrent && !torrent->isSeed())
torrentQueue.push(qMakePair(torrent->queuePosition(), torrent));
}
// Top torrents priority (starting with the ones with highest priority)
while (!torrentQueue.empty()) {
TorrentHandle *const torrent = torrentQueue.top().second;
torrentQueuePositionTop(torrent->nativeHandle());
torrentQueue.pop();
}
}
void Session::bottomTorrentsPriority(const QStringList &hashes)
{
std::priority_queue<QPair<int, TorrentHandle *>,
std::vector<QPair<int, TorrentHandle *> >,
std::less<QPair<int, TorrentHandle *> > > torrentQueue;
// Sort torrents by priority
foreach (const InfoHash &hash, hashes) {
TorrentHandle *const torrent = m_torrents.value(hash);
if (torrent && !torrent->isSeed())
torrentQueue.push(qMakePair(torrent->queuePosition(), torrent));
}
// Bottom torrents priority (starting with the ones with lowest priority)
while (!torrentQueue.empty()) {
TorrentHandle *const torrent = torrentQueue.top().second;
torrentQueuePositionBottom(torrent->nativeHandle());
torrentQueue.pop();
}
foreach (const InfoHash &hash, m_loadedMetadata.keys())
torrentQueuePositionBottom(m_nativeSession->find_torrent(hash));
}
QHash<InfoHash, TorrentHandle *> Session::torrents() const
{
return m_torrents;
}
TorrentStatusReport Session::torrentStatusReport() const
{
return m_torrentStatusReport;
}
2016-01-01 16:28:40 +03:00
// source - .torrent file path/url or magnet uri
2015-04-19 18:17:47 +03:00
bool Session::addTorrent(QString source, const AddTorrentParams &params)
{
MagnetUri magnetUri(source);
if (magnetUri.isValid()) {
return addTorrent_impl(params, magnetUri);
2015-04-19 18:17:47 +03:00
}
else if (Utils::Misc::isUrl(source)) {
2015-04-19 18:17:47 +03:00
Logger::instance()->addMessage(tr("Downloading '%1', please wait...", "e.g: Downloading 'xxx.torrent', please wait...").arg(source));
// Launch downloader
Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(source, true, 10485760 /* 10MB */, true);
2015-04-19 18:17:47 +03:00
connect(handler, SIGNAL(downloadFinished(QString, QString)), this, SLOT(handleDownloadFinished(QString, QString)));
connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleDownloadFailed(QString, QString)));
connect(handler, SIGNAL(redirectedToMagnet(QString, QString)), this, SLOT(handleRedirectedToMagnet(QString, QString)));
m_downloadedTorrents[handler->url()] = params;
}
else {
TorrentFileGuard guard(source);
if (addTorrent_impl(params, MagnetUri(), TorrentInfo::loadFromFile(source))) {
guard.markAsAddedToSession();
return true;
}
2015-04-19 18:17:47 +03:00
}
return false;
}
bool Session::addTorrent(const TorrentInfo &torrentInfo, const AddTorrentParams &params)
{
if (!torrentInfo.isValid()) return false;
2016-01-01 16:28:40 +03:00
return addTorrent_impl(params, MagnetUri(), torrentInfo);
2015-04-19 18:17:47 +03:00
}
// Add a torrent to the BitTorrent session
bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri,
TorrentInfo torrentInfo, const QByteArray &fastresumeData)
2015-04-19 18:17:47 +03:00
{
2016-03-06 09:25:55 +03:00
addData.savePath = normalizeSavePath(
addData.savePath,
((!addData.resumed && isAutoTMMDisabledByDefault()) ? defaultSavePath() : ""));
if (!addData.category.isEmpty()) {
if (!m_categories.contains(addData.category) && !addCategory(addData.category)) {
qWarning() << "Couldn't create category" << addData.category;
addData.category = "";
2016-01-01 16:28:40 +03:00
}
}
2015-04-19 18:17:47 +03:00
libt::add_torrent_params p;
InfoHash hash;
std::vector<char> buf(fastresumeData.constData(), fastresumeData.constData() + fastresumeData.size());
std::vector<boost::uint8_t> filePriorities;
QString savePath;
2016-05-08 22:47:50 +03:00
if (addData.savePath.isEmpty()) // using Automatic mode
savePath = categorySavePath(addData.category);
2016-05-08 22:47:50 +03:00
else // using Manual mode
savePath = addData.savePath;
2015-04-19 18:17:47 +03:00
bool fromMagnetUri = magnetUri.isValid();
if (fromMagnetUri) {
hash = magnetUri.hash();
2016-01-01 16:28:40 +03:00
if (m_loadedMetadata.contains(hash)) {
// Adding preloaded torrent
m_loadedMetadata.remove(hash);
libt::torrent_handle handle = m_nativeSession->find_torrent(hash);
--m_extraLimit;
try {
handle.auto_managed(false);
handle.pause();
}
catch (std::exception &) {}
adjustLimits();
// use common 2nd step of torrent addition
m_addingTorrents.insert(hash, addData);
createTorrentHandle(handle);
return true;
}
p = magnetUri.addTorrentParams();
2015-04-19 18:17:47 +03:00
}
2015-06-04 11:03:19 +03:00
else if (torrentInfo.isValid()) {
if (!addData.resumed && !addData.hasRootFolder)
torrentInfo.stripRootFolder();
2015-04-19 18:17:47 +03:00
// Metadata
if (!addData.resumed && !addData.hasSeedStatus)
findIncompleteFiles(torrentInfo, savePath);
2015-04-19 18:17:47 +03:00
p.ti = torrentInfo.nativeInfo();
hash = torrentInfo.hash();
2015-06-04 11:03:19 +03:00
}
else {
// We can have an invalid torrentInfo when there isn't a matching
// .torrent file to the .fastresume we loaded. Possibly from a
// failed upgrade.
return false;
2015-06-04 11:03:19 +03:00
}
2015-04-19 18:17:47 +03:00
2015-06-04 11:03:19 +03:00
if (addData.resumed && !fromMagnetUri) {
// Set torrent fast resume data
p.resume_data = buf;
p.flags |= libt::add_torrent_params::flag_use_resume_save_path;
}
else {
foreach (int prio, addData.filePriorities)
filePriorities.push_back(prio);
p.file_priorities = filePriorities;
2015-04-19 18:17:47 +03:00
}
// We should not add torrent if it already
// processed or adding to session
if (m_addingTorrents.contains(hash) || m_loadedMetadata.contains(hash)) return false;
if (m_torrents.contains(hash)) {
TorrentHandle *const torrent = m_torrents.value(hash);
if (torrent->isPrivate() || (!fromMagnetUri && torrentInfo.isPrivate()))
return false;
2015-04-19 18:17:47 +03:00
torrent->addTrackers(fromMagnetUri ? magnetUri.trackers() : torrentInfo.trackers());
torrent->addUrlSeeds(fromMagnetUri ? magnetUri.urlSeeds() : torrentInfo.urlSeeds());
return true;
}
qDebug("Adding torrent...");
qDebug(" -> Hash: %s", qPrintable(hash));
// Preallocation mode
if (isPreallocationEnabled())
2015-04-19 18:17:47 +03:00
p.storage_mode = libt::storage_mode_allocate;
else
p.storage_mode = libt::storage_mode_sparse;
p.flags |= libt::add_torrent_params::flag_paused; // Start in pause
p.flags &= ~libt::add_torrent_params::flag_auto_managed; // Because it is added in paused state
p.flags &= ~libt::add_torrent_params::flag_duplicate_is_error; // Already checked
// Seeding mode
// Skip checking and directly start seeding (new in libtorrent v0.15)
if (addData.skipChecking)
2015-04-19 18:17:47 +03:00
p.flags |= libt::add_torrent_params::flag_seed_mode;
else
p.flags &= ~libt::add_torrent_params::flag_seed_mode;
// Limits
p.max_connections = maxConnectionsPerTorrent();
p.max_uploads = maxUploadsPerTorrent();
p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
2015-04-19 18:17:47 +03:00
m_addingTorrents.insert(hash, addData);
// Adding torrent to BitTorrent session
m_nativeSession->async_add_torrent(p);
return true;
}
bool Session::findIncompleteFiles(TorrentInfo &torrentInfo, QString &savePath) const
{
auto findInDir = [](const QString &dirPath, TorrentInfo &torrentInfo) -> bool
{
bool found = false;
if (torrentInfo.filesCount() == 1) {
const QString filePath = dirPath + torrentInfo.filePath(0);
if (QFile(filePath).exists()) {
found = true;
}
else if (QFile(filePath + QB_EXT).exists()) {
found = true;
torrentInfo.renameFile(0, torrentInfo.filePath(0) + QB_EXT);
}
}
else {
QSet<QString> allFiles;
int dirPathSize = dirPath.size();
foreach (const QString &file, findAllFiles(dirPath + torrentInfo.name()))
allFiles << file.mid(dirPathSize);
for (int i = 0; i < torrentInfo.filesCount(); ++i) {
QString filePath = torrentInfo.filePath(i);
if (allFiles.contains(filePath)) {
found = true;
}
else {
filePath += QB_EXT;
if (allFiles.contains(filePath)) {
found = true;
torrentInfo.renameFile(i, filePath);
}
}
}
}
return found;
};
bool found = findInDir(savePath, torrentInfo);
if (!found && isTempPathEnabled()) {
savePath = torrentTempPath(torrentInfo.hash());
found = findInDir(savePath, torrentInfo);
}
return found;
}
2015-04-19 18:17:47 +03:00
// Add a torrent to the BitTorrent session in hidden mode
// and force it to load its metadata
bool Session::loadMetadata(const MagnetUri &magnetUri)
2015-04-19 18:17:47 +03:00
{
if (!magnetUri.isValid()) return false;
2015-04-19 18:17:47 +03:00
InfoHash hash = magnetUri.hash();
QString name = magnetUri.name();
2015-04-19 18:17:47 +03:00
2015-05-04 02:09:30 +03:00
// We should not add torrent if it already
2015-04-19 18:17:47 +03:00
// processed or adding to session
if (m_torrents.contains(hash)) return false;
if (m_addingTorrents.contains(hash)) return false;
if (m_loadedMetadata.contains(hash)) return false;
qDebug("Adding torrent to preload metadata...");
qDebug(" -> Hash: %s", qPrintable(hash));
qDebug(" -> Name: %s", qPrintable(name));
libt::add_torrent_params p = magnetUri.addTorrentParams();
2015-04-19 18:17:47 +03:00
// Flags
// Preallocation mode
if (isPreallocationEnabled())
2015-04-19 18:17:47 +03:00
p.storage_mode = libt::storage_mode_allocate;
else
p.storage_mode = libt::storage_mode_sparse;
// Limits
p.max_connections = maxConnectionsPerTorrent();
p.max_uploads = maxUploadsPerTorrent();
2015-04-19 18:17:47 +03:00
QString savePath = QString("%1/%2").arg(QDir::tempPath()).arg(hash);
p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
2015-04-19 18:17:47 +03:00
// Forced start
p.flags &= ~libt::add_torrent_params::flag_paused;
p.flags &= ~libt::add_torrent_params::flag_auto_managed;
// Solution to avoid accidental file writes
p.flags |= libt::add_torrent_params::flag_upload_mode;
// Adding torrent to BitTorrent session
libt::error_code ec;
libt::torrent_handle h = m_nativeSession->add_torrent(p, ec);
if (ec) return false;
// waiting for metadata...
m_loadedMetadata.insert(h.info_hash(), TorrentInfo());
++m_extraLimit;
adjustLimits();
return true;
}
void Session::exportTorrentFile(TorrentHandle *const torrent, TorrentExportFolder folder)
{
Q_ASSERT(((folder == TorrentExportFolder::Regular) && !torrentExportDirectory().isEmpty()) ||
((folder == TorrentExportFolder::Finished) && !finishedTorrentExportDirectory().isEmpty()));
2015-04-19 18:17:47 +03:00
QString validName = Utils::Fs::toValidFileSystemName(torrent->name());
2015-04-19 18:17:47 +03:00
QString torrentFilename = QString("%1.torrent").arg(torrent->hash());
QString torrentExportFilename = QString("%1.torrent").arg(validName);
2015-04-19 18:17:47 +03:00
QString torrentPath = QDir(m_resumeFolderPath).absoluteFilePath(torrentFilename);
QDir exportPath(folder == TorrentExportFolder::Regular ? torrentExportDirectory() : finishedTorrentExportDirectory());
2015-04-19 18:17:47 +03:00
if (exportPath.exists() || exportPath.mkpath(exportPath.absolutePath())) {
QString newTorrentPath = exportPath.absoluteFilePath(torrentExportFilename);
int counter = 0;
while (QFile::exists(newTorrentPath) && !Utils::Fs::sameFiles(torrentPath, newTorrentPath)) {
// Append number to torrent name to make it unique
torrentExportFilename = QString("%1 %2.torrent").arg(validName).arg(++counter);
newTorrentPath = exportPath.absoluteFilePath(torrentExportFilename);
2015-04-19 18:17:47 +03:00
}
if (!QFile::exists(newTorrentPath))
QFile::copy(torrentPath, newTorrentPath);
2015-04-19 18:17:47 +03:00
}
}
2015-07-22 08:51:23 +03:00
void Session::changeSpeedLimitMode_impl(bool alternative)
{
qDebug() << Q_FUNC_INFO << alternative;
if (alternative == isAltGlobalSpeedLimitEnabled()) return;
2015-07-22 08:51:23 +03:00
// Save new state to remember it on startup
m_isAltGlobalSpeedLimitEnabled = alternative;
configureDeferred();
2015-07-22 08:51:23 +03:00
// Notify
emit speedLimitModeChanged(alternative);
}
2015-04-19 18:17:47 +03:00
void Session::generateResumeData(bool final)
{
foreach (TorrentHandle *const torrent, m_torrents) {
if (!torrent->isValid()) continue;
if (torrent->hasMissingFiles()) continue;
if (torrent->isChecking() || torrent->hasError()) continue;
if (!final && !torrent->needSaveResumeData()) continue;
saveTorrentResumeData(torrent, final);
2015-04-19 18:17:47 +03:00
qDebug("Saving fastresume data for %s", qPrintable(torrent->name()));
}
}
// Called on exit
void Session::saveResumeData()
{
qDebug("Saving fast resume data...");
// Pause session
m_nativeSession->pause();
generateResumeData(true);
while (m_numResumeData > 0) {
2016-04-28 10:56:58 +03:00
std::vector<libt::alert *> alerts;
2015-04-19 18:17:47 +03:00
getPendingAlerts(alerts, 30 * 1000);
if (alerts.empty()) {
std::cerr << " aborting with " << m_numResumeData
<< " outstanding torrents to save resume data for"
<< std::endl;
break;
}
2016-04-28 10:56:58 +03:00
for (const auto a: alerts) {
2015-04-19 18:17:47 +03:00
switch (a->type()) {
case libt::save_resume_data_failed_alert::alert_type:
case libt::save_resume_data_alert::alert_type:
2016-11-18 19:04:04 +03:00
dispatchTorrentAlert(a);
2015-04-19 18:17:47 +03:00
break;
}
2016-04-28 10:56:58 +03:00
#if LIBTORRENT_VERSION_NUM < 10100
2015-04-19 18:17:47 +03:00
delete a;
2016-04-28 10:56:58 +03:00
#endif
2015-04-19 18:17:47 +03:00
}
}
}
void Session::setDefaultSavePath(QString path)
2015-04-19 18:17:47 +03:00
{
2016-03-06 09:25:55 +03:00
path = normalizeSavePath(path);
if (path == m_defaultSavePath) return;
2015-04-19 18:17:47 +03:00
m_defaultSavePath = path;
2015-04-19 18:17:47 +03:00
2016-05-08 22:47:50 +03:00
if (isDisableAutoTMMWhenDefaultSavePathChanged())
foreach (TorrentHandle *const torrent, torrents())
2016-05-08 22:47:50 +03:00
torrent->setAutoTMMEnabled(false);
else
foreach (TorrentHandle *const torrent, torrents())
torrent->handleCategorySavePathChanged();
2015-04-19 18:17:47 +03:00
}
void Session::setTempPath(QString path)
2015-04-19 18:17:47 +03:00
{
path = normalizeSavePath(path, defaultSavePath() + "temp/");
if (path == m_tempPath) return;
m_tempPath = path;
foreach (TorrentHandle *const torrent, m_torrents)
torrent->handleTempPathChanged();
2015-04-19 18:17:47 +03:00
}
void Session::networkOnlineStateChanged(const bool online)
{
Logger::instance()->addMessage(tr("System network status changed to %1", "e.g: System network status changed to ONLINE").arg(online ? tr("ONLINE") : tr("OFFLINE")), Log::INFO);
}
void Session::networkConfigurationChange(const QNetworkConfiguration& cfg)
{
const QString configuredInterfaceName = networkInterface();
2015-12-05 22:22:01 +03:00
// Empty means "Any Interface". In this case libtorrent has binded to 0.0.0.0 so any change to any interface will
// be automatically picked up. Otherwise we would rebinding here to 0.0.0.0 again.
if (configuredInterfaceName.isEmpty()) return;
const QString changedInterface = cfg.name();
// workaround for QTBUG-52633: check interface IPs, react only if the IPs have changed
// seems to be present only with NetworkManager, hence Q_OS_LINUX
#if defined Q_OS_LINUX && QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) && QT_VERSION < QT_VERSION_CHECK(5, 7, 1)
static QStringList boundIPs = getListeningIPs();
const QStringList newBoundIPs = getListeningIPs();
if ((configuredInterfaceName == changedInterface) && (boundIPs != newBoundIPs)) {
boundIPs = newBoundIPs;
#else
if (configuredInterfaceName == changedInterface) {
#endif
Logger::instance()->addMessage(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);
configureListeningInterface();
}
}
const QStringList Session::getListeningIPs()
2015-04-19 18:17:47 +03:00
{
Logger* const logger = Logger::instance();
QStringList IPs;
2015-04-19 18:17:47 +03:00
const QString ifaceName = networkInterface();
const QString ifaceAddr = networkInterfaceAddress();
const bool listenIPv6 = isIPv6Enabled();
2015-04-19 18:17:47 +03:00
if (!ifaceAddr.isEmpty()) {
QHostAddress addr(ifaceAddr);
if (addr.isNull()) {
logger->addMessage(tr("Configured network interface address %1 isn't valid.", "Configured network interface address 124.5.1568.1 isn't valid.").arg(ifaceAddr), Log::CRITICAL);
IPs.append("127.0.0.1"); // Force listening to localhost and avoid accidental connection that will expose user data.
return IPs;
}
}
if (ifaceName.isEmpty()) {
if (!ifaceAddr.isEmpty())
IPs.append(ifaceAddr);
else
IPs.append(QString());
return IPs;
2015-04-19 18:17:47 +03:00
}
// Attempt to listen on provided interface
const QNetworkInterface networkIFace = QNetworkInterface::interfaceFromName(ifaceName);
if (!networkIFace.isValid()) {
qDebug("Invalid network interface: %s", qPrintable(ifaceName));
logger->addMessage(tr("The network interface defined is invalid: %1").arg(ifaceName), Log::CRITICAL);
IPs.append("127.0.0.1"); // Force listening to localhost and avoid accidental connection that will expose user data.
return IPs;
2015-04-19 18:17:47 +03:00
}
const QList<QNetworkAddressEntry> addresses = networkIFace.addressEntries();
qDebug("This network interface has %d IP addresses", addresses.size());
QHostAddress ip;
QString ipString;
QAbstractSocket::NetworkLayerProtocol protocol;
foreach (const QNetworkAddressEntry &entry, addresses) {
ip = entry.ip();
ipString = ip.toString();
protocol = ip.protocol();
Q_ASSERT(protocol == QAbstractSocket::IPv4Protocol || protocol == QAbstractSocket::IPv6Protocol);
if ((!listenIPv6 && (protocol == QAbstractSocket::IPv6Protocol))
|| (listenIPv6 && (protocol == QAbstractSocket::IPv4Protocol)))
2015-04-19 18:17:47 +03:00
continue;
//If an iface address has been defined only allow ip's that match it to go through
if (!ifaceAddr.isEmpty()) {
if (ifaceAddr == ipString) {
IPs.append(ipString);
break;
}
}
else {
IPs.append(ipString);
}
}
// Make sure there is at least one IP
// At this point there was a valid network interface, with no suitable IP.
if (IPs.size() == 0) {
logger->addMessage(tr("qBittorrent didn't find an %1 local address to listen on", "qBittorrent didn't find an IPv4 local address to listen on").arg(listenIPv6 ? "IPv6" : "IPv4"), Log::CRITICAL);
IPs.append("127.0.0.1"); // Force listening to localhost and avoid accidental connection that will expose user data.
return IPs;
}
return IPs;
}
// Set the ports range in which is chosen the port
// the BitTorrent session will listen to
void Session::configureListeningInterface()
{
2016-06-03 17:03:17 +03:00
#if LIBTORRENT_VERSION_NUM < 10100
const ushort port = this->port();
qDebug() << Q_FUNC_INFO << port;
Logger* const logger = Logger::instance();
std::pair<int, int> ports(port, port);
libt::error_code ec;
const QStringList IPs = getListeningIPs();
foreach (const QString ip, IPs) {
if (ip.isEmpty()) {
logger->addMessage(tr("qBittorrent is trying to listen on any interface port: %1", "e.g: qBittorrent is trying to listen on any interface port: TCP/6881").arg(QString::number(port)), Log::INFO);
m_nativeSession->listen_on(ports, ec, 0, libt::session::listen_no_system_port);
if (ec)
logger->addMessage(tr("qBittorrent failed to listen on any interface port: %1. Reason: %2.", "e.g: qBittorrent failed to listen on any interface port: TCP/6881. Reason: no such interface" ).arg(QString::number(port)).arg(QString::fromLocal8Bit(ec.message().c_str())), Log::CRITICAL);
return;
}
m_nativeSession->listen_on(ports, ec, ip.toLatin1().constData(), libt::session::listen_no_system_port);
2015-04-19 18:17:47 +03:00
if (!ec) {
logger->addMessage(tr("qBittorrent is trying to listen on interface %1 port: %2", "e.g: qBittorrent is trying to listen on interface 192.168.0.1 port: TCP/6881").arg(ip).arg(port), Log::INFO);
2015-04-19 18:17:47 +03:00
return;
}
}
2016-06-03 17:03:17 +03:00
#else
m_listenInterfaceChanged = true;
configureDeferred();
#endif
2015-04-19 18:17:47 +03:00
}
int Session::globalDownloadSpeedLimit() const
2015-04-19 18:17:47 +03:00
{
2016-11-01 17:52:32 +03:00
// Unfortunately the value was saved as KiB instead of B.
// But it is better to pass it around internally(+ webui) as Bytes.
return m_globalDownloadSpeedLimit * 1024;
2015-04-19 18:17:47 +03:00
}
void Session::setGlobalDownloadSpeedLimit(int limit)
2015-04-19 18:17:47 +03:00
{
2016-11-01 17:52:32 +03:00
// Unfortunately the value was saved as KiB instead of B.
// But it is better to pass it around internally(+ webui) as Bytes.
limit /= 1024;
if (limit < 0) limit = 0;
if (limit == globalDownloadSpeedLimit()) return;
m_globalDownloadSpeedLimit = limit;
if (!isAltGlobalSpeedLimitEnabled())
configureDeferred();
2015-04-19 18:17:47 +03:00
}
int Session::globalUploadSpeedLimit() const
2015-04-19 18:17:47 +03:00
{
2016-11-01 17:52:32 +03:00
// Unfortunately the value was saved as KiB instead of B.
// But it is better to pass it around internally(+ webui) as Bytes.
return m_globalUploadSpeedLimit * 1024;
2015-04-19 18:17:47 +03:00
}
void Session::setGlobalUploadSpeedLimit(int limit)
2015-04-19 18:17:47 +03:00
{
2016-11-01 17:52:32 +03:00
// Unfortunately the value was saved as KiB instead of B.
// But it is better to pass it around internally(+ webui) as Bytes.
limit /= 1024;
if (limit < 0) limit = 0;
if (limit == globalUploadSpeedLimit()) return;
m_globalUploadSpeedLimit = limit;
if (!isAltGlobalSpeedLimitEnabled())
configureDeferred();
2015-04-19 18:17:47 +03:00
}
int Session::altGlobalDownloadSpeedLimit() const
2015-04-19 18:17:47 +03:00
{
2016-11-01 17:52:32 +03:00
// Unfortunately the value was saved as KiB instead of B.
// But it is better to pass it around internally(+ webui) as Bytes.
return m_altGlobalDownloadSpeedLimit * 1024;
2015-04-19 18:17:47 +03:00
}
void Session::setAltGlobalDownloadSpeedLimit(int limit)
{
2016-11-01 17:52:32 +03:00
// Unfortunately the value was saved as KiB instead of B.
// But it is better to pass it around internally(+ webui) as Bytes.
limit /= 1024;
if (limit < 0) limit = 0;
if (limit == altGlobalDownloadSpeedLimit()) return;
m_altGlobalDownloadSpeedLimit = limit;
if (isAltGlobalSpeedLimitEnabled())
configureDeferred();
}
int Session::altGlobalUploadSpeedLimit() const
{
2016-11-01 17:52:32 +03:00
// Unfortunately the value was saved as KiB instead of B.
// But it is better to pass it around internally(+ webui) as Bytes.
return m_altGlobalUploadSpeedLimit * 1024;
}
void Session::setAltGlobalUploadSpeedLimit(int limit)
2015-04-19 18:17:47 +03:00
{
2016-11-01 17:52:32 +03:00
// Unfortunately the value was saved as KiB instead of B.
// But it is better to pass it around internally(+ webui) as Bytes.
limit /= 1024;
if (limit < 0) limit = 0;
if (limit == altGlobalUploadSpeedLimit()) return;
m_altGlobalUploadSpeedLimit = limit;
if (isAltGlobalSpeedLimitEnabled())
configureDeferred();
2015-04-19 18:17:47 +03:00
}
int Session::downloadSpeedLimit() const
2015-04-19 18:17:47 +03:00
{
return isAltGlobalSpeedLimitEnabled()
? altGlobalDownloadSpeedLimit()
: globalDownloadSpeedLimit();
2015-04-19 18:17:47 +03:00
}
void Session::setDownloadSpeedLimit(int limit)
2015-04-19 18:17:47 +03:00
{
if (isAltGlobalSpeedLimitEnabled())
setAltGlobalDownloadSpeedLimit(limit);
else
setGlobalDownloadSpeedLimit(limit);
2015-04-19 18:17:47 +03:00
}
int Session::uploadSpeedLimit() const
2015-04-19 18:17:47 +03:00
{
return isAltGlobalSpeedLimitEnabled()
? altGlobalUploadSpeedLimit()
: globalUploadSpeedLimit();
2015-04-19 18:17:47 +03:00
}
void Session::setUploadSpeedLimit(int limit)
2015-04-19 18:17:47 +03:00
{
if (isAltGlobalSpeedLimitEnabled())
setAltGlobalUploadSpeedLimit(limit);
else
setGlobalUploadSpeedLimit(limit);
2015-04-19 18:17:47 +03:00
}
bool Session::isAltGlobalSpeedLimitEnabled() const
2015-04-19 18:17:47 +03:00
{
return m_isAltGlobalSpeedLimitEnabled;
2015-04-19 18:17:47 +03:00
}
void Session::setAltGlobalSpeedLimitEnabled(bool enabled)
2015-06-07 15:03:30 +03:00
{
// Stop the scheduler when the user has manually changed the bandwidth mode
if (isBandwidthSchedulerEnabled())
setBandwidthSchedulerEnabled(false);
changeSpeedLimitMode_impl(enabled);
}
bool Session::isBandwidthSchedulerEnabled() const
{
return m_isBandwidthSchedulerEnabled;
2015-06-07 15:03:30 +03:00
}
void Session::setBandwidthSchedulerEnabled(bool enabled)
2015-04-19 18:17:47 +03:00
{
if (enabled != isBandwidthSchedulerEnabled()) {
m_isBandwidthSchedulerEnabled = enabled;
if (enabled)
enableBandwidthScheduler();
else
delete m_bwScheduler;
}
2015-04-19 18:17:47 +03:00
}
uint Session::saveResumeDataInterval() const
2015-04-19 18:17:47 +03:00
{
return m_saveResumeDataInterval;
2015-04-19 18:17:47 +03:00
}
void Session::setSaveResumeDataInterval(uint value)
2015-04-19 18:17:47 +03:00
{
if (value != saveResumeDataInterval()) {
m_saveResumeDataInterval = value;
m_resumeDataTimer->setInterval(value * 60 * 1000);
}
2015-04-19 18:17:47 +03:00
}
int Session::port() const
2015-04-19 18:17:47 +03:00
{
static int randomPort = Utils::Random::rand(1024, 65535);
if (useRandomPort())
return randomPort;
return m_port;
2015-04-19 18:17:47 +03:00
}
void Session::setPort(int port)
2015-04-19 18:17:47 +03:00
{
if (port != this->port()) {
m_port = port;
configureListeningInterface();
}
2015-04-19 18:17:47 +03:00
}
bool Session::useRandomPort() const
2015-04-19 18:17:47 +03:00
{
return m_useRandomPort;
}
2015-04-19 18:17:47 +03:00
void Session::setUseRandomPort(bool value)
{
m_useRandomPort = value;
}
2015-04-19 18:17:47 +03:00
QString Session::networkInterface() const
{
return m_networkInterface;
2015-04-19 18:17:47 +03:00
}
void Session::setNetworkInterface(const QString &interface)
2015-04-19 18:17:47 +03:00
{
if (interface != networkInterface()) {
m_networkInterface = interface;
configureListeningInterface();
}
2015-04-19 18:17:47 +03:00
}
QString Session::networkInterfaceName() const
{
return m_networkInterfaceName;
}
void Session::setNetworkInterfaceName(const QString &name)
{
m_networkInterfaceName = name;
}
QString Session::networkInterfaceAddress() const
2015-04-19 18:17:47 +03:00
{
return m_networkInterfaceAddress;
2015-04-19 18:17:47 +03:00
}
void Session::setNetworkInterfaceAddress(const QString &address)
2015-04-19 18:17:47 +03:00
{
if (address != networkInterfaceAddress()) {
m_networkInterfaceAddress = address;
configureListeningInterface();
}
2015-04-19 18:17:47 +03:00
}
bool Session::isIPv6Enabled() const
2015-04-19 18:17:47 +03:00
{
return m_isIPv6Enabled;
}
2015-04-19 18:17:47 +03:00
void Session::setIPv6Enabled(bool enabled)
{
if (enabled != isIPv6Enabled()) {
m_isIPv6Enabled = enabled;
configureListeningInterface();
2015-04-19 18:17:47 +03:00
}
}
2015-04-19 18:17:47 +03:00
int Session::encryption() const
{
return m_encryption;
}
2015-04-19 18:17:47 +03:00
void Session::setEncryption(int state)
{
if (state != encryption()) {
m_encryption = state;
2016-06-03 17:03:17 +03:00
configureDeferred();
Logger::instance()->addMessage(
tr("Encryption support [%1]")
.arg(state == 0 ? tr("ON") : state == 1 ? tr("FORCED") : tr("OFF"))
, Log::INFO);
}
2015-04-19 18:17:47 +03:00
}
bool Session::isForceProxyEnabled() const
2015-04-19 18:17:47 +03:00
{
return m_isForceProxyEnabled;
}
void Session::setForceProxyEnabled(bool enabled)
{
if (enabled != isForceProxyEnabled()) {
m_isForceProxyEnabled = enabled;
configureDeferred();
}
}
bool Session::isProxyPeerConnectionsEnabled() const
{
return m_isProxyPeerConnectionsEnabled;
}
void Session::setProxyPeerConnectionsEnabled(bool enabled)
{
if (enabled != isProxyPeerConnectionsEnabled()) {
m_isProxyPeerConnectionsEnabled = enabled;
configureDeferred();
}
}
bool Session::isAddTrackersEnabled() const
{
return m_isAddTrackersEnabled;
}
void Session::setAddTrackersEnabled(bool enabled)
{
m_isAddTrackersEnabled = enabled;
}
QString Session::additionalTrackers() const
{
return m_additionalTrackers;
}
void Session::setAdditionalTrackers(const QString &trackers)
{
if (trackers != additionalTrackers()) {
m_additionalTrackers = trackers;
populateAdditionalTrackers();
}
}
bool Session::isIPFilteringEnabled() const
{
return m_isIPFilteringEnabled;
}
void Session::setIPFilteringEnabled(bool enabled)
{
if (enabled != m_isIPFilteringEnabled) {
m_isIPFilteringEnabled = enabled;
2016-06-03 17:03:17 +03:00
m_IPFilteringChanged = true;
configureDeferred();
}
}
QString Session::IPFilterFile() const
{
return Utils::Fs::fromNativePath(m_IPFilterFile);
}
void Session::setIPFilterFile(QString path)
{
path = Utils::Fs::fromNativePath(path);
if (path != IPFilterFile()) {
m_IPFilterFile = path;
2016-06-03 17:03:17 +03:00
m_IPFilteringChanged = true;
configureDeferred();
}
}
void Session::setBannedIPs(const QStringList &newList)
{
if (newList == m_bannedIPs)
return; // do nothing
// here filter out incorrect IP
QStringList filteredList;
for (const QString &ip : newList) {
if (Utils::Net::isValidIP(ip)) {
// the same IPv6 addresses could be written in different forms;
// QHostAddress::toString() result format follows RFC5952;
// thus we avoid duplicate entries pointing to the same address
filteredList << QHostAddress(ip).toString();
}
else {
Logger::instance()->addMessage(
tr("%1 is not a valid IP address and was rejected while applying the list of banned addresses.")
.arg(ip)
, Log::WARNING);
}
}
// now we have to sort IPs and make them unique
filteredList.sort();
filteredList.removeDuplicates();
// Again ensure that the new list is different from the stored one.
if (filteredList == m_bannedIPs)
return; // do nothing
// store to session settings
// also here we have to recreate filter list including 3rd party ban file
// and install it again into m_session
m_bannedIPs = filteredList;
m_IPFilteringChanged = true;
configureDeferred();
}
QStringList Session::bannedIPs() const
{
return m_bannedIPs;
}
int Session::maxConnectionsPerTorrent() const
{
return m_maxConnectionsPerTorrent;
}
void Session::setMaxConnectionsPerTorrent(int max)
{
max = (max > 0) ? max : -1;
if (max != maxConnectionsPerTorrent()) {
m_maxConnectionsPerTorrent = max;
// Apply this to all session torrents
for (const auto &handle: m_nativeSession->get_torrents()) {
if (!handle.is_valid()) continue;
try {
handle.set_max_connections(max);
}
catch(std::exception) {}
}
}
}
int Session::maxUploadsPerTorrent() const
{
return m_maxUploadsPerTorrent;
}
void Session::setMaxUploadsPerTorrent(int max)
{
max = (max > 0) ? max : -1;
if (max != maxUploadsPerTorrent()) {
m_maxUploadsPerTorrent = max;
// Apply this to all session torrents
for (const auto &handle: m_nativeSession->get_torrents()) {
if (!handle.is_valid()) continue;
try {
handle.set_max_uploads(max);
}
catch(std::exception) {}
}
}
}
bool Session::announceToAllTrackers() const
{
return m_announceToAllTrackers;
}
void Session::setAnnounceToAllTrackers(bool val)
{
if (val != m_announceToAllTrackers) {
m_announceToAllTrackers = val;
configureDeferred();
}
}
uint Session::diskCacheSize() const
{
uint size = m_diskCacheSize;
// These macros may not be available on compilers other than MSVC and GCC
#if defined(__x86_64__) || defined(_M_X64)
size = qMin(size, 4096u); // 4GiB
#else
// When build as 32bit binary, set the maximum at less than 2GB to prevent crashes
// allocate 1536MiB and leave 512MiB to the rest of program data in RAM
size = qMin(size, 1536u);
#endif
return size;
}
void Session::setDiskCacheSize(uint size)
{
#if defined(__x86_64__) || defined(_M_X64)
size = qMin(size, 4096u); // 4GiB
#else
// allocate 1536MiB and leave 512MiB to the rest of program data in RAM
size = qMin(size, 1536u);
#endif
if (size != m_diskCacheSize) {
m_diskCacheSize = size;
configureDeferred();
}
}
uint Session::diskCacheTTL() const
{
return m_diskCacheTTL;
}
void Session::setDiskCacheTTL(uint ttl)
{
if (ttl != m_diskCacheTTL) {
m_diskCacheTTL = ttl;
configureDeferred();
}
}
bool Session::useOSCache() const
{
return m_useOSCache;
}
void Session::setUseOSCache(bool use)
{
if (use != m_useOSCache) {
m_useOSCache = use;
configureDeferred();
}
}
bool Session::isAnonymousModeEnabled() const
{
return m_isAnonymousModeEnabled;
}
void Session::setAnonymousModeEnabled(bool enabled)
{
if (enabled != m_isAnonymousModeEnabled) {
m_isAnonymousModeEnabled = enabled;
configureDeferred();
2016-06-03 17:03:17 +03:00
Logger::instance()->addMessage(
tr("Anonymous mode [%1]").arg(isAnonymousModeEnabled() ? tr("ON") : tr("OFF"))
, Log::INFO);
}
}
bool Session::isQueueingSystemEnabled() const
2015-04-19 18:17:47 +03:00
{
return m_isQueueingEnabled;
}
2015-04-19 18:17:47 +03:00
void Session::setQueueingSystemEnabled(bool enabled)
{
if (enabled != m_isQueueingEnabled) {
m_isQueueingEnabled = enabled;
configureDeferred();
2015-04-19 18:17:47 +03:00
}
}
int Session::maxActiveDownloads() const
2015-04-19 18:17:47 +03:00
{
return m_maxActiveDownloads;
2015-04-19 18:17:47 +03:00
}
void Session::setMaxActiveDownloads(int max)
2015-04-19 18:17:47 +03:00
{
max = std::max(max, -1);
if (max != m_maxActiveDownloads) {
m_maxActiveDownloads = max;
configureDeferred();
2015-04-19 18:17:47 +03:00
}
}
int Session::maxActiveUploads() const
2015-04-19 18:17:47 +03:00
{
return m_maxActiveUploads;
}
2015-04-19 18:17:47 +03:00
void Session::setMaxActiveUploads(int max)
{
max = std::max(max, -1);
if (max != m_maxActiveUploads) {
m_maxActiveUploads = max;
configureDeferred();
2015-04-19 18:17:47 +03:00
}
}
int Session::maxActiveTorrents() const
2015-07-22 08:51:23 +03:00
{
return m_maxActiveTorrents;
2015-07-22 08:51:23 +03:00
}
void Session::setMaxActiveTorrents(int max)
2015-04-19 18:17:47 +03:00
{
max = std::max(max, -1);
if (max != m_maxActiveTorrents) {
m_maxActiveTorrents = max;
configureDeferred();
2015-04-19 18:17:47 +03:00
}
}
bool Session::ignoreSlowTorrentsForQueueing() const
2015-04-19 18:17:47 +03:00
{
return m_ignoreSlowTorrentsForQueueing;
}
2015-04-19 18:17:47 +03:00
void Session::setIgnoreSlowTorrentsForQueueing(bool ignore)
{
if (ignore != m_ignoreSlowTorrentsForQueueing) {
m_ignoreSlowTorrentsForQueueing = ignore;
configureDeferred();
2015-04-19 18:17:47 +03:00
}
}
uint Session::outgoingPortsMin() const
2015-04-19 18:17:47 +03:00
{
return m_outgoingPortsMin;
2015-04-19 18:17:47 +03:00
}
void Session::setOutgoingPortsMin(uint min)
2015-04-19 18:17:47 +03:00
{
if (min != m_outgoingPortsMin) {
m_outgoingPortsMin = min;
configureDeferred();
}
}
uint Session::outgoingPortsMax() const
{
return m_outgoingPortsMax;
2015-04-19 18:17:47 +03:00
}
void Session::setOutgoingPortsMax(uint max)
2015-04-19 18:17:47 +03:00
{
if (max != m_outgoingPortsMax) {
m_outgoingPortsMax = max;
configureDeferred();
2015-04-19 18:17:47 +03:00
}
}
bool Session::ignoreLimitsOnLAN() const
{
return m_ignoreLimitsOnLAN;
}
void Session::setIgnoreLimitsOnLAN(bool ignore)
{
if (ignore != m_ignoreLimitsOnLAN) {
m_ignoreLimitsOnLAN = ignore;
configureDeferred();
}
}
bool Session::includeOverheadInLimits() const
2015-04-19 18:17:47 +03:00
{
return m_includeOverheadInLimits;
}
2015-04-19 18:17:47 +03:00
void Session::setIncludeOverheadInLimits(bool include)
{
if (include != m_includeOverheadInLimits) {
m_includeOverheadInLimits = include;
configureDeferred();
}
}
QString Session::announceIP() const
{
return m_announceIP;
}
2015-04-19 18:17:47 +03:00
void Session::setAnnounceIP(const QString &ip)
{
if (ip != m_announceIP) {
m_announceIP = ip;
configureDeferred();
2015-04-19 18:17:47 +03:00
}
}
2015-04-19 18:17:47 +03:00
bool Session::isSuperSeedingEnabled() const
{
return m_isSuperSeedingEnabled;
}
void Session::setSuperSeedingEnabled(bool enabled)
{
if (enabled != m_isSuperSeedingEnabled) {
m_isSuperSeedingEnabled = enabled;
configureDeferred();
}
}
int Session::maxConnections() const
{
return m_maxConnections;
}
void Session::setMaxConnections(int max)
{
max = (max > 0) ? max : -1;
if (max != m_maxConnections) {
m_maxConnections = max;
configureDeferred();
}
}
int Session::maxHalfOpenConnections() const
{
return m_maxHalfOpenConnections;
}
void Session::setMaxHalfOpenConnections(int max)
{
max = (max > 0) ? max : -1;
if (max != m_maxHalfOpenConnections) {
m_maxHalfOpenConnections = max;
configureDeferred();
}
}
int Session::maxUploads() const
{
return m_maxUploads;
}
void Session::setMaxUploads(int max)
{
max = (max > 0) ? max : -1;
if (max != m_maxUploads) {
m_maxUploads = max;
configureDeferred();
}
}
bool Session::isUTPEnabled() const
{
return m_isUTPEnabled;
}
void Session::setUTPEnabled(bool enabled)
{
if (enabled != m_isUTPEnabled) {
m_isUTPEnabled = enabled;
configureDeferred();
2015-04-19 18:17:47 +03:00
}
}
bool Session::isUTPRateLimited() const
2015-04-19 18:17:47 +03:00
{
return m_isUTPRateLimited;
2015-04-19 18:17:47 +03:00
}
void Session::setUTPRateLimited(bool limited)
2015-04-19 18:17:47 +03:00
{
if (limited != m_isUTPRateLimited) {
m_isUTPRateLimited = limited;
configureDeferred();
}
2015-04-19 18:17:47 +03:00
}
bool Session::isTrackerFilteringEnabled() const
2015-04-19 18:17:47 +03:00
{
return m_isTrackerFilteringEnabled;
2015-04-19 18:17:47 +03:00
}
void Session::setTrackerFilteringEnabled(bool enabled)
2015-04-19 18:17:47 +03:00
{
if (enabled != m_isTrackerFilteringEnabled) {
m_isTrackerFilteringEnabled = enabled;
configureDeferred();
}
2015-04-19 18:17:47 +03:00
}
bool Session::isListening() const
{
return m_nativeSession->is_listening();
}
MaxRatioAction Session::maxRatioAction() const
{
return static_cast<MaxRatioAction>(m_maxRatioAction.value());
}
void Session::setMaxRatioAction(MaxRatioAction act)
{
m_maxRatioAction = static_cast<int>(act);
2015-04-19 18:17:47 +03:00
}
// If this functions returns true, we cannot add torrent to session,
// but it is still possible to merge trackers in some case
bool Session::isKnownTorrent(const InfoHash &hash) const
{
return (m_torrents.contains(hash)
|| m_addingTorrents.contains(hash)
|| m_loadedMetadata.contains(hash));
}
void Session::updateRatioTimer()
{
if ((globalMaxRatio() == -1) && !hasPerTorrentRatioLimit()) {
2015-04-19 18:17:47 +03:00
if (m_bigRatioTimer->isActive())
m_bigRatioTimer->stop();
}
else if (!m_bigRatioTimer->isActive()) {
m_bigRatioTimer->start();
}
}
void Session::handleTorrentRatioLimitChanged(TorrentHandle *const torrent)
{
Q_UNUSED(torrent);
updateRatioTimer();
}
void Session::saveTorrentResumeData(TorrentHandle *const torrent, bool finalSave)
2015-04-19 18:17:47 +03:00
{
torrent->saveResumeData(finalSave);
2015-04-19 18:17:47 +03:00
++m_numResumeData;
}
void Session::handleTorrentSavePathChanged(TorrentHandle *const torrent)
{
emit torrentSavePathChanged(torrent);
}
void Session::handleTorrentCategoryChanged(TorrentHandle *const torrent, const QString &oldCategory)
2015-06-07 15:03:30 +03:00
{
emit torrentCategoryChanged(torrent, oldCategory);
}
void Session::handleTorrentSavingModeChanged(TorrentHandle * const torrent)
{
emit torrentSavingModeChanged(torrent);
2015-06-07 15:03:30 +03:00
}
2015-04-19 18:17:47 +03:00
void Session::handleTorrentTrackersAdded(TorrentHandle *const torrent, const QList<TrackerEntry> &newTrackers)
{
foreach (const TrackerEntry &newTracker, newTrackers)
Logger::instance()->addMessage(tr("Tracker '%1' was added to torrent '%2'").arg(newTracker.url()).arg(torrent->name()));
emit trackersAdded(torrent, newTrackers);
if (torrent->trackers().size() == newTrackers.size())
emit trackerlessStateChanged(torrent, false);
emit trackersChanged(torrent);
}
void Session::handleTorrentTrackersRemoved(TorrentHandle *const torrent, const QList<TrackerEntry> &deletedTrackers)
{
foreach (const TrackerEntry &deletedTracker, deletedTrackers)
Logger::instance()->addMessage(tr("Tracker '%1' was deleted from torrent '%2'").arg(deletedTracker.url()).arg(torrent->name()));
emit trackersRemoved(torrent, deletedTrackers);
if (torrent->trackers().size() == 0)
emit trackerlessStateChanged(torrent, true);
emit trackersChanged(torrent);
}
void Session::handleTorrentTrackersChanged(TorrentHandle *const torrent)
{
emit trackersChanged(torrent);
}
void Session::handleTorrentUrlSeedsAdded(TorrentHandle *const torrent, const QList<QUrl> &newUrlSeeds)
{
foreach (const QUrl &newUrlSeed, newUrlSeeds)
Logger::instance()->addMessage(tr("URL seed '%1' was added to torrent '%2'").arg(newUrlSeed.toString()).arg(torrent->name()));
}
void Session::handleTorrentUrlSeedsRemoved(TorrentHandle *const torrent, const QList<QUrl> &urlSeeds)
{
foreach (const QUrl &urlSeed, urlSeeds)
Logger::instance()->addMessage(tr("URL seed '%1' was removed from torrent '%2'").arg(urlSeed.toString()).arg(torrent->name()));
}
void Session::handleTorrentMetadataReceived(TorrentHandle *const torrent)
{
saveTorrentResumeData(torrent);
// Save metadata
const QDir resumeDataDir(m_resumeFolderPath);
QString torrentFile = resumeDataDir.absoluteFilePath(QString("%1.torrent").arg(torrent->hash()));
if (torrent->saveTorrentFile(torrentFile)) {
// Copy the torrent file to the export folder
if (!torrentExportDirectory().isEmpty())
2015-04-19 18:17:47 +03:00
exportTorrentFile(torrent);
}
emit torrentMetadataLoaded(torrent);
}
void Session::handleTorrentPaused(TorrentHandle *const torrent)
{
if (!torrent->hasError() && !torrent->hasMissingFiles())
saveTorrentResumeData(torrent);
emit torrentPaused(torrent);
}
void Session::handleTorrentResumed(TorrentHandle *const torrent)
{
emit torrentResumed(torrent);
}
void Session::handleTorrentChecked(TorrentHandle *const torrent)
{
emit torrentFinishedChecking(torrent);
}
void Session::handleTorrentFinished(TorrentHandle *const torrent)
{
if (!torrent->hasError() && !torrent->hasMissingFiles())
saveTorrentResumeData(torrent);
2015-04-19 18:17:47 +03:00
emit torrentFinished(torrent);
qDebug("Checking if the torrent contains torrent files to download");
// Check if there are torrent files inside
for (int i = 0; i < torrent->filesCount(); ++i) {
const QString torrentRelpath = torrent->filePath(i);
if (torrentRelpath.endsWith(".torrent", Qt::CaseInsensitive)) {
qDebug("Found possible recursive torrent download.");
const QString torrentFullpath = torrent->savePath(true) + "/" + torrentRelpath;
2015-04-19 18:17:47 +03:00
qDebug("Full subtorrent path is %s", qPrintable(torrentFullpath));
TorrentInfo torrentInfo = TorrentInfo::loadFromFile(torrentFullpath);
if (torrentInfo.isValid()) {
qDebug("emitting recursiveTorrentDownloadPossible()");
emit recursiveTorrentDownloadPossible(torrent);
break;
}
else {
qDebug("Caught error loading torrent");
Logger::instance()->addMessage(tr("Unable to decode '%1' torrent file.").arg(Utils::Fs::toNativePath(torrentFullpath)), Log::CRITICAL);
2015-04-19 18:17:47 +03:00
}
}
}
// Move .torrent file to another folder
if (!finishedTorrentExportDirectory().isEmpty())
2015-04-19 18:17:47 +03:00
exportTorrentFile(torrent, TorrentExportFolder::Finished);
if (!hasUnfinishedTorrents())
emit allTorrentsFinished();
}
void Session::handleTorrentResumeDataReady(TorrentHandle *const torrent, const libtorrent::entry &data)
{
--m_numResumeData;
// Separated thread is used for the blocking IO which results in slow processing of many torrents.
// Encoding data in parallel while doing IO saves time. Copying libtorrent::entry objects around
// isn't cheap too.
QByteArray out;
libt::bencode(std::back_inserter(out), data);
QMetaObject::invokeMethod(m_resumeDataSavingManager, "saveResumeData",
Q_ARG(QString, torrent->hash()), Q_ARG(QByteArray, out));
2015-04-19 18:17:47 +03:00
}
void Session::handleTorrentResumeDataFailed(TorrentHandle *const torrent)
{
Q_UNUSED(torrent)
--m_numResumeData;
}
void Session::handleTorrentTrackerReply(TorrentHandle *const torrent, const QString &trackerUrl)
{
emit trackerSuccess(torrent, trackerUrl);
}
void Session::handleTorrentTrackerError(TorrentHandle *const torrent, const QString &trackerUrl)
{
emit trackerError(torrent, trackerUrl);
}
void Session::handleTorrentTrackerAuthenticationRequired(TorrentHandle *const torrent, const QString &trackerUrl)
{
Q_UNUSED(trackerUrl);
emit trackerAuthenticationRequired(torrent);
}
void Session::handleTorrentTrackerWarning(TorrentHandle *const torrent, const QString &trackerUrl)
{
emit trackerWarning(torrent, trackerUrl);
}
bool Session::hasPerTorrentRatioLimit() const
{
foreach (TorrentHandle *const torrent, m_torrents)
if (torrent->ratioLimit() >= 0) return true;
return false;
}
void Session::initResumeFolder()
{
m_resumeFolderPath = Utils::Fs::expandPathAbs(specialFolderLocation(SpecialFolder::Data) + RESUME_FOLDER);
2015-04-19 18:17:47 +03:00
QDir resumeFolderDir(m_resumeFolderPath);
if (resumeFolderDir.exists() || resumeFolderDir.mkpath(resumeFolderDir.absolutePath())) {
m_resumeFolderLock.setFileName(resumeFolderDir.absoluteFilePath("session.lock"));
if (!m_resumeFolderLock.open(QFile::WriteOnly)) {
throw std::runtime_error("Cannot write to torrent resume folder.");
}
}
else {
throw std::runtime_error("Cannot create torrent resume folder.");
}
}
void Session::configureDeferred()
{
if (m_deferredConfigureScheduled) return; // Obtaining the lock is expensive, let's check early
QWriteLocker locker(&m_lock);
if (m_deferredConfigureScheduled) return; // something might have changed while we were getting the lock
QMetaObject::invokeMethod(this, "configure", Qt::QueuedConnection);
m_deferredConfigureScheduled = true;
}
2015-04-19 18:17:47 +03:00
// Enable IP Filtering
// this method creates ban list from scratch combining user ban list and 3rd party ban list file
2016-06-03 17:03:17 +03:00
void Session::enableIPFilter()
2015-04-19 18:17:47 +03:00
{
qDebug("Enabling IPFilter");
// 1. Parse the IP filter
// 2. In the slot add the manually banned IPs to the provided libtorrent::ip_filter
// 3. Set the ip_filter in one go so there isn't a time window where there isn't an ip_filter
// set between clearing the old one and setting the new one.
2015-04-19 18:17:47 +03:00
if (!m_filterParser) {
m_filterParser = new FilterParserThread(this);
2015-04-19 18:17:47 +03:00
connect(m_filterParser.data(), SIGNAL(IPFilterParsed(int)), SLOT(handleIPFilterParsed(int)));
connect(m_filterParser.data(), SIGNAL(IPFilterError()), SLOT(handleIPFilterError()));
}
2016-06-03 17:03:17 +03:00
m_filterParser->processFilterFile(IPFilterFile());
2015-04-19 18:17:47 +03:00
}
// Disable IP Filtering
void Session::disableIPFilter()
{
qDebug("Disabling IPFilter");
if (m_filterParser) {
disconnect(m_filterParser.data(), 0, this, 0);
delete m_filterParser;
}
2016-06-03 17:03:17 +03:00
// Add the banned IPs after the IPFilter disabling
// which creates an empty filter and overrides all previously
// applied bans.
libt::ip_filter filter;
processBannedIPs(filter);
m_nativeSession->set_ip_filter(filter);
2015-04-19 18:17:47 +03:00
}
void Session::recursiveTorrentDownload(const InfoHash &hash)
{
TorrentHandle *const torrent = m_torrents.value(hash);
if (!torrent) return;
for (int i = 0; i < torrent->filesCount(); ++i) {
const QString torrentRelpath = torrent->filePath(i);
if (torrentRelpath.endsWith(".torrent")) {
Logger::instance()->addMessage(
tr("Recursive download of file '%1' embedded in torrent '%2'"
, "Recursive download of 'test.torrent' embedded in torrent 'test2'")
.arg(Utils::Fs::toNativePath(torrentRelpath)).arg(torrent->name()));
2015-04-19 18:17:47 +03:00
const QString torrentFullpath = torrent->savePath() + "/" + torrentRelpath;
AddTorrentParams params;
// Passing the save path along to the sub torrent file
params.savePath = torrent->savePath();
addTorrent(TorrentInfo::loadFromFile(torrentFullpath), params);
}
}
}
SessionStatus Session::status() const
{
return m_nativeSession->status();
}
CacheStatus Session::cacheStatus() const
{
return m_nativeSession->get_cache_status();
}
// Will resume torrents in backup directory
void Session::startUpTorrents()
{
qDebug("Resuming torrents...");
const QDir resumeDataDir(m_resumeFolderPath);
QStringList fastresumes = resumeDataDir.entryList(
QStringList(QLatin1String("*.fastresume")), QDir::Files, QDir::Unsorted);
2015-04-19 18:17:47 +03:00
Logger *const logger = Logger::instance();
typedef struct
{
QString hash;
MagnetUri magnetUri;
AddTorrentData addTorrentData;
QByteArray data;
} TorrentResumeData;
int resumedTorrentsCount = 0;
const auto startupTorrent = [this, logger, &resumeDataDir, &resumedTorrentsCount](const TorrentResumeData &params)
{
QString filePath = resumeDataDir.filePath(QString("%1.torrent").arg(params.hash));
qDebug() << "Starting up torrent" << params.hash << "...";
if (!addTorrent_impl(params.addTorrentData, params.magnetUri, TorrentInfo::loadFromFile(filePath), params.data))
logger->addMessage(tr("Unable to resume torrent '%1'.", "e.g: Unable to resume torrent 'hash'.")
.arg(params.hash), Log::CRITICAL);
// process add torrent messages before message queue overflow
if (resumedTorrentsCount % 100 == 0) readAlerts();
++resumedTorrentsCount;
};
2015-04-19 18:17:47 +03:00
qDebug("Starting up torrents");
qDebug("Queue size: %d", fastresumes.size());
2015-04-19 18:17:47 +03:00
// Resume downloads
QMap<int, TorrentResumeData> queuedResumeData;
int nextQueuePosition = 1;
int numOfRemappedFiles = 0;
QRegExp rx(QLatin1String("^([A-Fa-f0-9]{40})\\.fastresume$"));
foreach (const QString &fastresumeName, fastresumes) {
if (rx.indexIn(fastresumeName) == -1) continue;
2015-04-19 18:17:47 +03:00
QString hash = rx.cap(1);
QString fastresumePath = resumeDataDir.absoluteFilePath(fastresumeName);
2015-04-19 18:17:47 +03:00
QByteArray data;
AddTorrentData resumeData;
2015-06-04 11:03:19 +03:00
MagnetUri magnetUri;
int queuePosition;
if (readFile(fastresumePath, data) && loadTorrentResumeData(data, resumeData, queuePosition, magnetUri)) {
if (queuePosition <= nextQueuePosition) {
startupTorrent({ hash, magnetUri, resumeData, data });
if (queuePosition == nextQueuePosition) {
++nextQueuePosition;
while (queuedResumeData.contains(nextQueuePosition)) {
startupTorrent(queuedResumeData.take(nextQueuePosition));
++nextQueuePosition;
}
}
}
else {
int q = queuePosition;
for(; queuedResumeData.contains(q); ++q) {
}
if (q != queuePosition) {
++numOfRemappedFiles;
}
queuedResumeData[q] = { hash, magnetUri, resumeData, data };
}
2015-04-19 18:17:47 +03:00
}
}
if (numOfRemappedFiles > 0) {
logger->addMessage(
QString(tr("Queue positions were corrected in %1 resume files")).arg(numOfRemappedFiles),
Log::CRITICAL);
}
// starting up downloading torrents (queue position > 0)
foreach (const TorrentResumeData &torrentResumeData, queuedResumeData)
startupTorrent(torrentResumeData);
2015-04-19 18:17:47 +03:00
}
quint64 Session::getAlltimeDL() const
{
return m_statistics->getAlltimeDL();
}
quint64 Session::getAlltimeUL() const
{
return m_statistics->getAlltimeUL();
}
void Session::refresh()
{
m_nativeSession->post_torrent_updates();
}
void Session::handleIPFilterParsed(int ruleCount)
{
if (m_filterParser) {
libt::ip_filter filter = m_filterParser->IPfilter();
processBannedIPs(filter);
m_nativeSession->set_ip_filter(filter);
}
2015-04-19 18:17:47 +03:00
Logger::instance()->addMessage(tr("Successfully parsed the provided IP filter: %1 rules were applied.", "%1 is a number").arg(ruleCount));
emit IPFilterParsed(false, ruleCount);
2015-04-19 18:17:47 +03:00
}
void Session::handleIPFilterError()
{
libt::ip_filter filter;
processBannedIPs(filter);
m_nativeSession->set_ip_filter(filter);
2015-04-19 18:17:47 +03:00
Logger::instance()->addMessage(tr("Error: Failed to parse the provided IP filter."), Log::CRITICAL);
emit IPFilterParsed(true, 0);
2015-04-19 18:17:47 +03:00
}
2016-04-28 10:56:58 +03:00
#if LIBTORRENT_VERSION_NUM < 10100
void Session::dispatchAlerts(libt::alert *alertPtr)
2015-04-19 18:17:47 +03:00
{
QMutexLocker lock(&m_alertsMutex);
2016-04-28 10:56:58 +03:00
bool wasEmpty = m_alerts.empty();
2015-04-19 18:17:47 +03:00
m_alerts.push_back(alertPtr);
2015-04-19 18:17:47 +03:00
if (wasEmpty) {
m_alertsWaitCondition.wakeAll();
QMetaObject::invokeMethod(this, "readAlerts", Qt::QueuedConnection);
}
}
2016-04-28 10:56:58 +03:00
#endif
2015-04-19 18:17:47 +03:00
2016-04-28 10:56:58 +03:00
void Session::getPendingAlerts(std::vector<libt::alert *> &out, ulong time)
2015-04-19 18:17:47 +03:00
{
Q_ASSERT(out.empty());
2016-04-28 10:56:58 +03:00
#if LIBTORRENT_VERSION_NUM < 10100
2015-04-19 18:17:47 +03:00
QMutexLocker lock(&m_alertsMutex);
if (m_alerts.empty())
2015-04-19 18:17:47 +03:00
m_alertsWaitCondition.wait(&m_alertsMutex, time);
m_alerts.swap(out);
2016-04-28 10:56:58 +03:00
#else
if (time > 0)
m_nativeSession->wait_for_alert(libt::milliseconds(time));
m_nativeSession->pop_alerts(&out);
#endif
2015-04-19 18:17:47 +03:00
}
2017-04-15 17:21:24 +03:00
bool Session::isCreateTorrentSubfolder() const
{
return m_isCreateTorrentSubfolder;
}
void Session::setCreateTorrentSubfolder(bool value)
{
m_isCreateTorrentSubfolder = value;
}
2015-04-19 18:17:47 +03:00
// Read alerts sent by the BitTorrent session
void Session::readAlerts()
{
2016-04-28 10:56:58 +03:00
std::vector<libt::alert *> alerts;
2015-04-19 18:17:47 +03:00
getPendingAlerts(alerts);
2016-04-28 10:56:58 +03:00
for (const auto a: alerts) {
2015-04-19 18:17:47 +03:00
handleAlert(a);
2016-04-28 10:56:58 +03:00
#if LIBTORRENT_VERSION_NUM < 10100
2015-04-19 18:17:47 +03:00
delete a;
2016-04-28 10:56:58 +03:00
#endif
2015-04-19 18:17:47 +03:00
}
}
void Session::handleAlert(libt::alert *a)
{
try {
switch (a->type()) {
case libt::stats_alert::alert_type:
case libt::file_renamed_alert::alert_type:
case libt::file_completed_alert::alert_type:
case libt::torrent_finished_alert::alert_type:
case libt::save_resume_data_alert::alert_type:
case libt::save_resume_data_failed_alert::alert_type:
case libt::storage_moved_alert::alert_type:
case libt::storage_moved_failed_alert::alert_type:
case libt::torrent_paused_alert::alert_type:
case libt::tracker_error_alert::alert_type:
case libt::tracker_reply_alert::alert_type:
case libt::tracker_warning_alert::alert_type:
case libt::fastresume_rejected_alert::alert_type:
case libt::torrent_checked_alert::alert_type:
dispatchTorrentAlert(a);
break;
case libt::metadata_received_alert::alert_type:
handleMetadataReceivedAlert(static_cast<libt::metadata_received_alert*>(a));
dispatchTorrentAlert(a);
break;
case libt::state_update_alert::alert_type:
handleStateUpdateAlert(static_cast<libt::state_update_alert*>(a));
break;
case libt::file_error_alert::alert_type:
handleFileErrorAlert(static_cast<libt::file_error_alert*>(a));
break;
case libt::add_torrent_alert::alert_type:
handleAddTorrentAlert(static_cast<libt::add_torrent_alert*>(a));
break;
case libt::torrent_removed_alert::alert_type:
handleTorrentRemovedAlert(static_cast<libt::torrent_removed_alert*>(a));
break;
case libt::torrent_deleted_alert::alert_type:
handleTorrentDeletedAlert(static_cast<libt::torrent_deleted_alert*>(a));
break;
case libt::torrent_delete_failed_alert::alert_type:
handleTorrentDeleteFailedAlert(static_cast<libt::torrent_delete_failed_alert*>(a));
break;
2015-04-19 18:17:47 +03:00
case libt::portmap_error_alert::alert_type:
handlePortmapWarningAlert(static_cast<libt::portmap_error_alert*>(a));
break;
case libt::portmap_alert::alert_type:
handlePortmapAlert(static_cast<libt::portmap_alert*>(a));
break;
case libt::peer_blocked_alert::alert_type:
handlePeerBlockedAlert(static_cast<libt::peer_blocked_alert*>(a));
break;
case libt::peer_ban_alert::alert_type:
handlePeerBanAlert(static_cast<libt::peer_ban_alert*>(a));
break;
case libt::url_seed_alert::alert_type:
handleUrlSeedAlert(static_cast<libt::url_seed_alert*>(a));
break;
case libt::listen_succeeded_alert::alert_type:
handleListenSucceededAlert(static_cast<libt::listen_succeeded_alert*>(a));
break;
case libt::listen_failed_alert::alert_type:
handleListenFailedAlert(static_cast<libt::listen_failed_alert*>(a));
break;
case libt::external_ip_alert::alert_type:
handleExternalIPAlert(static_cast<libt::external_ip_alert*>(a));
break;
}
}
catch (std::exception &exc) {
qWarning() << "Caught exception in " << Q_FUNC_INFO << ": " << QString::fromStdString(exc.what());
2015-04-19 18:17:47 +03:00
}
}
void Session::dispatchTorrentAlert(libt::alert *a)
{
TorrentHandle *const torrent = m_torrents.value(static_cast<libt::torrent_alert*>(a)->handle.info_hash());
2015-04-19 18:17:47 +03:00
if (torrent)
torrent->handleAlert(a);
}
2016-01-20 09:57:02 +03:00
void Session::createTorrentHandle(const libt::torrent_handle &nativeHandle)
2015-04-19 18:17:47 +03:00
{
// Magnet added for preload its metadata
2016-01-20 09:57:02 +03:00
if (!m_addingTorrents.contains(nativeHandle.info_hash())) return;
2015-04-19 18:17:47 +03:00
2016-01-20 09:57:02 +03:00
AddTorrentData data = m_addingTorrents.take(nativeHandle.info_hash());
2015-04-19 18:17:47 +03:00
2016-01-20 09:57:02 +03:00
TorrentHandle *const torrent = new TorrentHandle(this, nativeHandle, data);
2015-04-19 18:17:47 +03:00
m_torrents.insert(torrent->hash(), torrent);
2016-01-20 09:57:02 +03:00
Logger *const logger = Logger::instance();
2015-04-19 18:17:47 +03:00
bool fromMagnetUri = !torrent->hasMetadata();
if (data.resumed) {
2017-04-24 11:59:16 +03:00
if (fromMagnetUri && !data.addPaused)
torrent->resume(data.addForced);
2015-06-04 11:03:19 +03:00
2015-04-19 18:17:47 +03:00
logger->addMessage(tr("'%1' resumed. (fast resume)", "'torrent name' was resumed. (fast resume)")
.arg(torrent->name()));
}
else {
qDebug("This is a NEW torrent (first time)...");
// The following is useless for newly added magnet
if (!fromMagnetUri) {
// Backup torrent file
const QDir resumeDataDir(m_resumeFolderPath);
const QString newFile = resumeDataDir.absoluteFilePath(QString("%1.torrent").arg(torrent->hash()));
if (torrent->saveTorrentFile(newFile)) {
// Copy the torrent file to the export folder
if (!torrentExportDirectory().isEmpty())
2015-04-19 18:17:47 +03:00
exportTorrentFile(torrent);
}
else {
logger->addMessage(tr("Couldn't save '%1.torrent'").arg(torrent->hash()), Log::CRITICAL);
2015-04-19 18:17:47 +03:00
}
}
if (isAddTrackersEnabled() && !torrent->isPrivate())
torrent->addTrackers(m_additionalTrackerList);
2015-04-19 18:17:47 +03:00
// Start torrent because it was added in paused state
2017-04-24 11:59:16 +03:00
if (!data.addPaused)
2015-04-19 18:17:47 +03:00
torrent->resume();
logger->addMessage(tr("'%1' added to download list.", "'torrent name' was added to download list.")
.arg(torrent->name()));
// In case of crash before the scheduled generation
// of the fastresumes.
saveTorrentResumeData(torrent);
2015-04-19 18:17:47 +03:00
}
2015-06-17 12:23:16 +03:00
if ((torrent->ratioLimit() >= 0) && !m_bigRatioTimer->isActive())
m_bigRatioTimer->start();
2015-04-19 18:17:47 +03:00
// Send torrent addition signal
emit torrentAdded(torrent);
// Send new torrent signal
if (!data.resumed)
emit torrentNew(torrent);
2015-04-19 18:17:47 +03:00
}
2016-01-20 09:57:02 +03:00
void Session::handleAddTorrentAlert(libt::add_torrent_alert *p)
{
if (p->error) {
qDebug("/!\\ Error: Failed to add torrent!");
QString msg = QString::fromStdString(p->message());
2016-01-20 09:57:02 +03:00
Logger::instance()->addMessage(tr("Couldn't add torrent. Reason: %1").arg(msg), Log::WARNING);
emit addTorrentFailed(msg);
}
else {
createTorrentHandle(p->handle);
}
}
void Session::handleTorrentRemovedAlert(libt::torrent_removed_alert *p)
2015-04-19 18:17:47 +03:00
{
if (m_loadedMetadata.contains(p->info_hash))
emit metadataLoaded(m_loadedMetadata.take(p->info_hash));
}
void Session::handleTorrentDeletedAlert(libt::torrent_deleted_alert *p)
{
const QString path = m_savePathsToRemove.take(p->info_hash);
if (path == torrentTempPath(p->info_hash))
Utils::Fs::smartRemoveEmptyFolderTree(path);
}
void Session::handleTorrentDeleteFailedAlert(libt::torrent_delete_failed_alert *p)
{
// libtorrent won't delete the directory if it contains files not listed in the torrent,
// so we remove the directory ourselves
Utils::Fs::smartRemoveEmptyFolderTree(m_savePathsToRemove.take(p->info_hash));
2015-04-19 18:17:47 +03:00
}
void Session::handleMetadataReceivedAlert(libt::metadata_received_alert *p)
{
InfoHash hash = p->handle.info_hash();
if (m_loadedMetadata.contains(hash)) {
--m_extraLimit;
adjustLimits();
m_loadedMetadata[hash] = TorrentInfo(p->handle.torrent_file());
m_nativeSession->remove_torrent(p->handle, libt::session::delete_files);
}
}
void Session::handleFileErrorAlert(libt::file_error_alert *p)
{
qDebug() << Q_FUNC_INFO;
// NOTE: Check this function!
TorrentHandle *const torrent = m_torrents.value(p->handle.info_hash());
if (torrent) {
QString msg = QString::fromStdString(p->message());
2015-04-19 18:17:47 +03:00
Logger::instance()->addMessage(tr("An I/O error occurred, '%1' paused. %2")
.arg(torrent->name()).arg(msg));
emit fullDiskError(torrent, msg);
}
}
void Session::handlePortmapWarningAlert(libt::portmap_error_alert *p)
{
Logger::instance()->addMessage(tr("UPnP/NAT-PMP: Port mapping failure, message: %1").arg(QString::fromStdString(p->message())), Log::CRITICAL);
2015-04-19 18:17:47 +03:00
}
void Session::handlePortmapAlert(libt::portmap_alert *p)
{
qDebug("UPnP Success, msg: %s", p->message().c_str());
Logger::instance()->addMessage(tr("UPnP/NAT-PMP: Port mapping successful, message: %1").arg(QString::fromStdString(p->message())), Log::INFO);
2015-04-19 18:17:47 +03:00
}
void Session::handlePeerBlockedAlert(libt::peer_blocked_alert *p)
{
boost::system::error_code ec;
std::string ip = p->ip.to_string(ec);
QString reason;
switch (p->reason) {
case libt::peer_blocked_alert::ip_filter:
reason = tr("due to IP filter.", "this peer was blocked due to ip filter.");
break;
case libt::peer_blocked_alert::port_filter:
reason = tr("due to port filter.", "this peer was blocked due to port filter.");
break;
case libt::peer_blocked_alert::i2p_mixed:
reason = tr("due to i2p mixed mode restrictions.", "this peer was blocked due to i2p mixed mode restrictions.");
break;
case libt::peer_blocked_alert::privileged_ports:
reason = tr("because it has a low port.", "this peer was blocked because it has a low port.");
break;
case libt::peer_blocked_alert::utp_disabled:
reason = trUtf8("because %1 is disabled.", "this peer was blocked because uTP is disabled.").arg(QString::fromUtf8(C_UTP)); // don't translate μTP
2015-04-19 18:17:47 +03:00
break;
case libt::peer_blocked_alert::tcp_disabled:
reason = tr("because %1 is disabled.", "this peer was blocked because TCP is disabled.").arg("TCP"); // don't translate TCP
2015-04-19 18:17:47 +03:00
break;
}
if (!ec)
Logger::instance()->addPeer(QString::fromLatin1(ip.c_str()), true, reason);
}
void Session::handlePeerBanAlert(libt::peer_ban_alert *p)
{
boost::system::error_code ec;
std::string ip = p->ip.address().to_string(ec);
if (!ec)
Logger::instance()->addPeer(QString::fromLatin1(ip.c_str()), false);
}
void Session::handleUrlSeedAlert(libt::url_seed_alert *p)
{
Logger::instance()->addMessage(tr("URL seed lookup failed for URL: '%1', message: %2").arg(QString::fromStdString(p->url)).arg(QString::fromStdString(p->message())), Log::CRITICAL);
2015-04-19 18:17:47 +03:00
}
void Session::handleListenSucceededAlert(libt::listen_succeeded_alert *p)
{
boost::system::error_code ec;
QString proto = "TCP";
if (p->sock_type == libt::listen_succeeded_alert::udp)
proto = "UDP";
else if (p->sock_type == libt::listen_succeeded_alert::tcp)
proto = "TCP";
else if (p->sock_type == libt::listen_succeeded_alert::tcp_ssl)
proto = "TCP_SSL";
qDebug() << "Successfully listening on " << proto << p->endpoint.address().to_string(ec).c_str() << "/" << p->endpoint.port();
Logger::instance()->addMessage(tr("qBittorrent is successfully listening on interface %1 port: %2/%3", "e.g: qBittorrent is successfully listening on interface 192.168.0.1 port: TCP/6881").arg(p->endpoint.address().to_string(ec).c_str()).arg(proto).arg(QString::number(p->endpoint.port())), Log::INFO);
// Force reannounce on all torrents because some trackers blacklist some ports
std::vector<libt::torrent_handle> torrents = m_nativeSession->get_torrents();
std::vector<libt::torrent_handle>::iterator it = torrents.begin();
std::vector<libt::torrent_handle>::iterator itend = torrents.end();
for ( ; it != itend; ++it)
it->force_reannounce();
}
void Session::handleListenFailedAlert(libt::listen_failed_alert *p)
{
boost::system::error_code ec;
2015-04-19 18:17:47 +03:00
QString proto = "TCP";
if (p->sock_type == libt::listen_failed_alert::udp)
proto = "UDP";
else if (p->sock_type == libt::listen_failed_alert::tcp)
proto = "TCP";
else if (p->sock_type == libt::listen_failed_alert::tcp_ssl)
proto = "TCP_SSL";
else if (p->sock_type == libt::listen_failed_alert::i2p)
proto = "I2P";
else if (p->sock_type == libt::listen_failed_alert::socks5)
proto = "SOCKS5";
qDebug() << "Failed listening on " << proto << p->endpoint.address().to_string(ec).c_str() << "/" << p->endpoint.port();
Logger::instance()->addMessage(
tr("qBittorrent failed listening on interface %1 port: %2/%3. Reason: %4.",
2016-01-20 09:57:02 +03:00
"e.g: qBittorrent failed listening on interface 192.168.0.1 port: TCP/6881. Reason: already in use.")
2015-04-19 18:17:47 +03:00
.arg(p->endpoint.address().to_string(ec).c_str()).arg(proto).arg(QString::number(p->endpoint.port()))
.arg(QString::fromLocal8Bit(p->error.message().c_str())), Log::CRITICAL);
2015-04-19 18:17:47 +03:00
}
void Session::handleExternalIPAlert(libt::external_ip_alert *p)
{
boost::system::error_code ec;
Logger::instance()->addMessage(tr("External IP: %1", "e.g. External IP: 192.168.0.1").arg(p->external_address.to_string(ec).c_str()), Log::INFO);
}
void Session::handleStateUpdateAlert(libt::state_update_alert *p)
{
foreach (const libt::torrent_status &status, p->status) {
TorrentHandle *const torrent = m_torrents.value(status.info_hash);
if (torrent)
torrent->handleStateUpdate(status);
2015-04-19 18:17:47 +03:00
}
m_torrentStatusReport = TorrentStatusReport();
2015-04-19 18:17:47 +03:00
foreach (TorrentHandle *const torrent, m_torrents) {
if (torrent->isDownloading())
++m_torrentStatusReport.nbDownloading;
2015-04-19 18:17:47 +03:00
if (torrent->isUploading())
++m_torrentStatusReport.nbSeeding;
2015-04-19 18:17:47 +03:00
if (torrent->isCompleted())
++m_torrentStatusReport.nbCompleted;
2015-04-19 18:17:47 +03:00
if (torrent->isPaused())
++m_torrentStatusReport.nbPaused;
2015-04-19 18:17:47 +03:00
if (torrent->isResumed())
++m_torrentStatusReport.nbResumed;
2015-04-19 18:17:47 +03:00
if (torrent->isActive())
++m_torrentStatusReport.nbActive;
2015-04-19 18:17:47 +03:00
if (torrent->isInactive())
++m_torrentStatusReport.nbInactive;
2015-11-11 09:51:22 +03:00
if (torrent->isErrored())
++m_torrentStatusReport.nbErrored;
2015-04-19 18:17:47 +03:00
}
emit torrentsUpdated();
2015-04-19 18:17:47 +03:00
}
namespace
2015-04-19 18:17:47 +03:00
{
bool readFile(const QString &path, QByteArray &buf)
{
QFile file(path);
if (!file.open(QIODevice::ReadOnly)) {
qDebug("Cannot read file %s: %s", qPrintable(path), qPrintable(file.errorString()));
return false;
}
2015-04-19 18:17:47 +03:00
buf = file.readAll();
return true;
2016-01-01 16:28:40 +03:00
}
bool loadTorrentResumeData(const QByteArray &data, AddTorrentData &torrentData, int &prio, MagnetUri &magnetUri)
{
torrentData = AddTorrentData();
torrentData.resumed = true;
torrentData.skipChecking = false;
libt::error_code ec;
2016-06-03 17:03:17 +03:00
#if LIBTORRENT_VERSION_NUM < 10100
libt::lazy_entry fast;
libt::lazy_bdecode(data.constData(), data.constData() + data.size(), fast, ec);
if (ec || (fast.type() != libt::lazy_entry::dict_t)) return false;
2016-06-03 17:03:17 +03:00
#else
libt::bdecode_node fast;
libt::bdecode(data.constData(), data.constData() + data.size(), fast, ec);
if (ec || (fast.type() != libt::bdecode_node::dict_t)) return false;
#endif
torrentData.savePath = Profile::instance().fromPortablePath(
Utils::Fs::fromNativePath(QString::fromStdString(fast.dict_find_string_value("qBt-savePath"))));
torrentData.ratioLimit = QString::fromStdString(fast.dict_find_string_value("qBt-ratioLimit")).toDouble();
// **************************************************************************************
// Workaround to convert legacy label to category
// TODO: Should be removed in future
torrentData.category = QString::fromStdString(fast.dict_find_string_value("qBt-label"));
if (torrentData.category.isEmpty())
// **************************************************************************************
torrentData.category = QString::fromStdString(fast.dict_find_string_value("qBt-category"));
torrentData.name = QString::fromStdString(fast.dict_find_string_value("qBt-name"));
torrentData.hasSeedStatus = fast.dict_find_int_value("qBt-seedStatus");
torrentData.disableTempPath = fast.dict_find_int_value("qBt-tempPathDisabled");
torrentData.hasRootFolder = fast.dict_find_int_value("qBt-hasRootFolder");
magnetUri = MagnetUri(QString::fromStdString(fast.dict_find_string_value("qBt-magnetUri")));
2017-04-24 11:59:16 +03:00
torrentData.addPaused = fast.dict_find_int_value("qBt-paused");
torrentData.addForced = fast.dict_find_int_value("qBt-forced");
torrentData.firstLastPiecePriority = fast.dict_find_int_value("qBt-firstLastPiecePriority");
torrentData.sequential = fast.dict_find_int_value("qBt-sequential");
prio = fast.dict_find_int_value("qBt-queuePosition");
2015-04-19 18:17:47 +03:00
return true;
2015-04-19 18:17:47 +03:00
}
void torrentQueuePositionUp(const libt::torrent_handle &handle)
{
try {
handle.queue_position_up();
}
catch (std::exception &exc) {
qDebug() << Q_FUNC_INFO << " fails: " << exc.what();
}
2015-04-19 18:17:47 +03:00
}
void torrentQueuePositionDown(const libt::torrent_handle &handle)
{
try {
handle.queue_position_down();
}
catch (std::exception &exc) {
qDebug() << Q_FUNC_INFO << " fails: " << exc.what();
}
2015-04-19 18:17:47 +03:00
}
void torrentQueuePositionTop(const libt::torrent_handle &handle)
{
try {
handle.queue_position_top();
}
catch (std::exception &exc) {
qDebug() << Q_FUNC_INFO << " fails: " << exc.what();
}
2015-04-19 18:17:47 +03:00
}
void torrentQueuePositionBottom(const libt::torrent_handle &handle)
{
try {
handle.queue_position_bottom();
}
catch (std::exception &exc) {
qDebug() << Q_FUNC_INFO << " fails: " << exc.what();
}
2015-04-19 18:17:47 +03:00
}
}