qBittorrent/src/qtlibtorrent/qbtsession.cpp

2938 lines
109 KiB
C++
Raw Normal View History

/*
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2006 Christophe Dumez
*
* 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.
*
* Contact : chris@qbittorrent.org
*/
#include <QDir>
#include <QDateTime>
#include <QString>
#include <QNetworkInterface>
#include <QHostAddress>
#include <QNetworkAddressEntry>
#include <QProcess>
#include "smtp.h"
#include "filesystemwatcher.h"
2010-12-18 18:34:38 +03:00
#include "torrentspeedmonitor.h"
#include "torrentstatistics.h"
#include "qbtsession.h"
#include "misc.h"
#include "fs_utils.h"
#include "downloadthread.h"
#include "filterparserthread.h"
#include "preferences.h"
#include "scannedfoldersmodel.h"
2010-01-03 01:20:37 +03:00
#ifndef DISABLE_GUI
#include "shutdownconfirm.h"
#include "geoipmanager.h"
2010-01-03 01:20:37 +03:00
#endif
#include "torrentpersistentdata.h"
2009-11-18 20:46:59 +03:00
#include "httpserver.h"
#include "qinisettings.h"
#include "bandwidthscheduler.h"
#include <libtorrent/version.hpp>
#include <libtorrent/extensions/ut_metadata.hpp>
#include <libtorrent/version.hpp>
2009-11-18 20:46:59 +03:00
#include <libtorrent/extensions/lt_trackers.hpp>
2007-07-23 16:46:36 +04:00
#include <libtorrent/extensions/ut_pex.hpp>
#include <libtorrent/extensions/smart_ban.hpp>
2009-10-24 18:36:17 +04:00
//#include <libtorrent/extensions/metadata_transfer.hpp>
#include <libtorrent/lazy_entry.hpp>
2007-07-23 16:46:36 +04:00
#include <libtorrent/bencode.hpp>
2011-04-17 14:29:44 +04:00
#include <libtorrent/error_code.hpp>
2007-07-23 16:46:36 +04:00
#include <libtorrent/identify_client.hpp>
#include <libtorrent/alert_types.hpp>
#include <libtorrent/torrent_info.hpp>
2013-10-23 23:16:23 +04:00
#include <libtorrent/error_code.hpp>
#include <queue>
2011-01-23 22:31:48 +03:00
#include <string.h>
#include "dnsupdater.h"
2007-07-23 16:46:36 +04:00
#if LIBTORRENT_VERSION_NUM < 10000
2014-01-01 02:10:41 +04:00
#include <libtorrent/upnp.hpp>
#include <libtorrent/natpmp.hpp>
#endif
//initialize static member variables
QHash<QString, TorrentTempData::TorrentData> TorrentTempData::data = QHash<QString, TorrentTempData::TorrentData>();
QHash<QString, bool> HiddenData::data = QHash<QString, bool>();
unsigned int HiddenData::metadata_counter = 0;
using namespace libtorrent;
QBtSession* QBtSession::m_instance = 0;
const qreal QBtSession::MAX_RATIO = 9999.;
2010-03-03 20:27:25 +03:00
const int MAX_TRACKER_ERRORS = 2;
2012-02-16 23:04:02 +04:00
/* Converts a QString hash into a libtorrent sha1_hash */
static libtorrent::sha1_hash QStringToSha1(const QString& s) {
QByteArray raw = s.toLatin1();
2012-02-16 23:04:02 +04:00
Q_ASSERT(raw.size() == 40);
libtorrent::sha1_hash ret;
from_hex(raw.constData(), 40, (char*)&ret[0]);
return ret;
}
// Main constructor
2010-10-17 18:46:01 +04:00
QBtSession::QBtSession()
: m_scanFolders(ScanFoldersModel::instance(this)),
2012-05-15 20:57:31 +04:00
preAllocateAll(false), global_ratio_limit(-1),
LSDEnabled(false),
DHTEnabled(false), current_dht_port(0), queueingEnabled(false),
m_torrentExportEnabled(false),
m_finishedTorrentExportEnabled(false)
#ifndef DISABLE_GUI
, geoipDBLoaded(false), resolve_countries(false)
#endif
2014-01-01 02:10:41 +04:00
, m_tracker(0), m_shutdownAct(NO_SHUTDOWN)
#if LIBTORRENT_VERSION_NUM < 10000
2014-01-01 02:10:41 +04:00
, m_upnp(0), m_natpmp(0)
#endif
, m_dynDNSUpdater(0)
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
, m_alertDispatcher(0)
2010-03-03 20:27:25 +03:00
{
BigRatioTimer = new QTimer(this);
BigRatioTimer->setInterval(10000);
connect(BigRatioTimer, SIGNAL(timeout()), SLOT(processBigRatios()));
Preferences pref;
// Creating Bittorrent session
QList<int> version;
version << VERSION_MAJOR;
version << VERSION_MINOR;
version << VERSION_BUGFIX;
2011-06-18 17:35:25 +04:00
version << 0;
const QString peer_id = "qB";
// Construct session
2010-03-04 23:19:25 +03:00
s = new session(fingerprint(peer_id.toLocal8Bit().constData(), version.at(0), version.at(1), version.at(2), version.at(3)), 0);
2012-06-24 10:59:12 +04:00
//std::cout << "Peer ID: " << fingerprint(peer_id.toLocal8Bit().constData(), version.at(0), version.at(1), version.at(2), version.at(3)).to_string() << std::endl;
2010-03-04 23:19:25 +03:00
addConsoleMessage("Peer ID: "+misc::toQString(fingerprint(peer_id.toLocal8Bit().constData(), version.at(0), version.at(1), version.at(2), version.at(3)).to_string()));
2009-09-03 15:29:15 +04:00
// Set severity level of libtorrent session
s->set_alert_mask(alert::error_notification | alert::peer_notification | alert::port_mapping_notification | alert::storage_notification | alert::tracker_notification | alert::status_notification | alert::ip_block_notification | alert::progress_notification);
// Load previous state
loadSessionState();
// Enabling plugins
//s->add_extension(&create_metadata_plugin);
s->add_extension(&create_ut_metadata_plugin);
2012-02-20 21:30:53 +04:00
if (pref.trackerExchangeEnabled())
s->add_extension(&create_lt_trackers_plugin);
2012-02-20 21:30:53 +04:00
if (pref.isPeXEnabled()) {
PeXEnabled = true;
s->add_extension(&create_ut_pex_plugin);
} else {
PeXEnabled = false;
}
s->add_extension(&create_smart_ban_plugin);
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
m_alertDispatcher = new QAlertDispatcher(s, this);
connect(m_alertDispatcher, SIGNAL(alertsReceived()), SLOT(readAlerts()));
appendLabelToSavePath = pref.appendTorrentLabel();
appendqBExtension = pref.useIncompleteFilesExtension();
connect(m_scanFolders, SIGNAL(torrentsAdded(QStringList&)), SLOT(addTorrentsFromScanFolder(QStringList&)));
// Apply user settings to Bittorrent session
configureSession();
2010-12-18 18:34:38 +03:00
// Torrent speed monitor
m_speedMonitor = new TorrentSpeedMonitor(this);
m_speedMonitor->start();
m_torrentStatistics = new TorrentStatistics(this, this);
// To download from urls
downloader = new DownloadThread(this);
connect(downloader, SIGNAL(downloadFinished(QString, QString)), SLOT(processDownloadedFile(QString, QString)));
connect(downloader, SIGNAL(downloadFailure(QString, QString)), SLOT(handleDownloadFailure(QString, QString)));
// Regular saving of fastresume data
connect(&resumeDataTimer, SIGNAL(timeout()), SLOT(saveTempFastResumeData()));
resumeDataTimer.start(170000); // 3min
qDebug("* BTSession constructed");
}
2010-11-22 22:14:50 +03:00
// Main destructor
QBtSession::~QBtSession() {
qDebug("BTSession destructor IN");
2010-12-18 18:34:38 +03:00
delete m_speedMonitor;
qDebug("Deleted the torrent speed monitor");
// Do some BT related saving
saveSessionState();
saveFastResumeData();
// Delete our objects
2012-02-20 21:30:53 +04:00
if (m_tracker)
delete m_tracker;
2012-02-20 21:30:53 +04:00
if (BigRatioTimer)
delete BigRatioTimer;
2012-02-20 21:30:53 +04:00
if (filterParser)
delete filterParser;
delete downloader;
2012-02-20 21:30:53 +04:00
if (bd_scheduler)
delete bd_scheduler;
2009-11-18 20:46:59 +03:00
// HTTP Server
2012-02-20 21:30:53 +04:00
if (httpServer)
2009-11-18 20:46:59 +03:00
delete httpServer;
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
delete m_alertDispatcher;
delete m_torrentStatistics;
qDebug("Deleting the session");
delete s;
qDebug("BTSession destructor OUT");
2011-04-05 21:29:55 +04:00
#ifndef DISABLE_GUI
2012-02-20 21:30:53 +04:00
if (m_shutdownAct != NO_SHUTDOWN) {
qDebug() << "Sending computer shutdown/suspend signal...";
misc::shutdownComputer(m_shutdownAct == SUSPEND_COMPUTER);
}
2011-04-05 21:29:55 +04:00
#endif
}
2010-10-17 18:46:01 +04:00
void QBtSession::preAllocateAllFiles(bool b) {
2010-03-03 20:27:25 +03:00
const bool change = (preAllocateAll != b);
2012-02-20 21:30:53 +04:00
if (change) {
qDebug("PreAllocateAll changed, reloading all torrents!");
preAllocateAll = b;
}
}
2010-10-17 18:46:01 +04:00
void QBtSession::processBigRatios() {
qDebug("Process big ratios...");
2010-08-20 13:02:27 +04:00
std::vector<torrent_handle> torrents = s->get_torrents();
2012-07-14 02:28:23 +04:00
std::vector<torrent_handle>::iterator torrentIT = torrents.begin();
std::vector<torrent_handle>::iterator torrentITend = torrents.end();
for ( ; torrentIT != torrentITend; ++torrentIT) {
const QTorrentHandle h(*torrentIT);
2012-02-20 21:30:53 +04:00
if (!h.is_valid()) continue;
if (h.is_seed()) {
2010-03-03 20:27:25 +03:00
const QString hash = h.hash();
2014-05-14 02:09:45 +04:00
const qreal ratio = getRealRatio(h.status(torrent_handle::query_accurate_download_counters));
qreal ratio_limit = TorrentPersistentData::getRatioLimit(hash);
2012-02-20 21:30:53 +04:00
if (ratio_limit == TorrentPersistentData::USE_GLOBAL_RATIO)
ratio_limit = global_ratio_limit;
if (ratio_limit == TorrentPersistentData::NO_RATIO_LIMIT)
continue;
qDebug("Ratio: %f (limit: %f)", ratio, ratio_limit);
Q_ASSERT(ratio_limit >= 0.f);
2012-02-20 21:30:53 +04:00
if (ratio <= MAX_RATIO && ratio >= ratio_limit) {
if (high_ratio_action == REMOVE_ACTION) {
addConsoleMessage(tr("%1 reached the maximum ratio you set.").arg(h.name()));
addConsoleMessage(tr("Removing torrent %1...").arg(h.name()));
deleteTorrent(hash);
} else {
// Pause it
2012-02-20 21:30:53 +04:00
if (!h.is_paused()) {
addConsoleMessage(tr("%1 reached the maximum ratio you set.").arg(h.name()));
addConsoleMessage(tr("Pausing torrent %1...").arg(h.name()));
pauseTorrent(hash);
}
}
//emit torrent_ratio_deleted(fileName);
}
}
}
}
2010-10-17 18:46:01 +04:00
void QBtSession::setDownloadLimit(QString hash, long val) {
QTorrentHandle h = getTorrentHandle(hash);
2012-02-20 21:30:53 +04:00
if (h.is_valid()) {
h.set_download_limit(val);
}
}
2010-10-17 18:46:01 +04:00
void QBtSession::setUploadLimit(QString hash, long val) {
2007-08-04 10:23:44 +04:00
qDebug("Set upload limit rate to %ld", val);
QTorrentHandle h = getTorrentHandle(hash);
2012-02-20 21:30:53 +04:00
if (h.is_valid()) {
h.set_upload_limit(val);
}
}
2010-10-17 18:46:01 +04:00
void QBtSession::handleDownloadFailure(QString url, QString reason) {
emit downloadFromUrlFailure(url, reason);
// Clean up
const QUrl qurl = QUrl::fromEncoded(url.toUtf8());
url_skippingDlg.removeOne(qurl);
savepathLabel_fromurl.remove(qurl);
}
2010-10-17 18:46:01 +04:00
void QBtSession::setQueueingEnabled(bool enable) {
2012-02-20 21:30:53 +04:00
if (queueingEnabled != enable) {
2008-08-19 04:28:44 +04:00
qDebug("Queueing system is changing state...");
queueingEnabled = enable;
2008-07-14 23:20:18 +04:00
}
}
// Set BT session configuration
2010-10-17 18:46:01 +04:00
void QBtSession::configureSession() {
qDebug("Configuring session");
Preferences pref;
if (pref.useRandomPort()) {
pref.setSessionPort(rand() % USHRT_MAX + 1025);
}
const unsigned short old_listenPort = getListenPort();
const unsigned short new_listenPort = pref.getSessionPort();
if (old_listenPort != new_listenPort) {
qDebug("Session port changes in program preferences: %d -> %d", old_listenPort, new_listenPort);
setListeningPort(new_listenPort);
}
// Downloads
// * Save path
defaultSavePath = pref.getSavePath();
2012-02-20 21:30:53 +04:00
if (pref.isTempPathEnabled()) {
setDefaultTempPath(pref.getTempPath());
} else {
setDefaultTempPath(QString::null);
}
setAppendLabelToSavePath(pref.appendTorrentLabel());
setAppendqBExtension(pref.useIncompleteFilesExtension());
preAllocateAllFiles(pref.preAllocateAllFiles());
// * Torrent export directory
const bool torrentExportEnabled = pref.isTorrentExportEnabled();
if (m_torrentExportEnabled != torrentExportEnabled) {
m_torrentExportEnabled = torrentExportEnabled;
if (m_torrentExportEnabled) {
qDebug("Torrent export is enabled, exporting the current torrents");
exportTorrentFiles(pref.getTorrentExportDir());
}
}
// * Finished Torrent export directory
const bool finishedTorrentExportEnabled = pref.isFinishedTorrentExportEnabled();
if (m_finishedTorrentExportEnabled != finishedTorrentExportEnabled)
m_finishedTorrentExportEnabled = finishedTorrentExportEnabled;
// Connection
// * Global download limit
const bool alternative_speeds = pref.isAltBandwidthEnabled();
int down_limit;
2012-02-20 21:30:53 +04:00
if (alternative_speeds)
down_limit = pref.getAltGlobalDownloadLimit();
else
down_limit = pref.getGlobalDownloadLimit();
2012-02-20 21:30:53 +04:00
if (down_limit <= 0) {
// Download limit disabled
setDownloadRateLimit(-1);
} else {
// Enabled
setDownloadRateLimit(down_limit*1024);
}
int up_limit;
2012-02-20 21:30:53 +04:00
if (alternative_speeds)
up_limit = pref.getAltGlobalUploadLimit();
else
up_limit = pref.getGlobalUploadLimit();
// * Global Upload limit
2012-02-20 21:30:53 +04:00
if (up_limit <= 0) {
// Upload limit disabled
setUploadRateLimit(-1);
} else {
// Enabled
setUploadRateLimit(up_limit*1024);
}
2012-02-20 21:30:53 +04:00
if (pref.isSchedulerEnabled()) {
if (!bd_scheduler) {
bd_scheduler = new BandwidthScheduler(this);
connect(bd_scheduler, SIGNAL(switchToAlternativeMode(bool)), this, SLOT(useAlternativeSpeedsLimit(bool)));
}
bd_scheduler->start();
} else {
delete bd_scheduler;
}
2010-01-03 01:20:37 +03:00
#ifndef DISABLE_GUI
// Resolve countries
qDebug("Loading country resolution settings");
const bool new_resolv_countries = pref.resolvePeerCountries();
2012-02-20 21:30:53 +04:00
if (resolve_countries != new_resolv_countries) {
2013-11-19 01:38:43 +04:00
qDebug("in country resolution settings");
resolve_countries = new_resolv_countries;
2012-02-20 21:30:53 +04:00
if (resolve_countries && !geoipDBLoaded) {
qDebug("Loading geoip database");
GeoIPManager::loadDatabase(s);
geoipDBLoaded = true;
}
// Update torrent handles
2010-08-20 13:02:27 +04:00
std::vector<torrent_handle> torrents = s->get_torrents();
2012-07-14 02:28:23 +04:00
std::vector<torrent_handle>::iterator torrentIT = torrents.begin();
std::vector<torrent_handle>::iterator torrentITend = torrents.end();
for ( ; torrentIT != torrentITend; ++torrentIT) {
QTorrentHandle h = QTorrentHandle(*torrentIT);
2012-02-20 21:30:53 +04:00
if (h.is_valid())
h.resolve_countries(resolve_countries);
}
}
2010-01-03 01:20:37 +03:00
#endif
// * UPnP / NAT-PMP
2012-02-20 21:30:53 +04:00
if (pref.isUPnPEnabled()) {
enableUPnP(true);
addConsoleMessage(tr("UPnP / NAT-PMP support [ON]"), QString::fromUtf8("blue"));
} else {
enableUPnP(false);
addConsoleMessage(tr("UPnP / NAT-PMP support [OFF]"), QString::fromUtf8("blue"));
}
// * Session settings
2011-04-17 14:29:44 +04:00
session_settings sessionSettings = s->settings();
sessionSettings.user_agent = "qBittorrent "VERSION;
2012-06-24 10:59:12 +04:00
//std::cout << "HTTP user agent is " << sessionSettings.user_agent << std::endl;
addConsoleMessage(tr("HTTP user agent is %1").arg(misc::toQString(sessionSettings.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;
2009-11-28 18:03:27 +03:00
// Speed up exit
sessionSettings.stop_tracker_timeout = 1;
//sessionSettings.announce_to_all_trackers = true;
sessionSettings.auto_scrape_interval = 1200; // 20 minutes
bool announce_to_all = pref.announceToAllTrackers();
sessionSettings.announce_to_all_trackers = announce_to_all;
sessionSettings.announce_to_all_tiers = announce_to_all;
sessionSettings.auto_scrape_min_interval = 900; // 15 minutes
int cache_size = pref.diskCacheSize();
2012-12-01 16:08:46 +04:00
sessionSettings.cache_size = cache_size ? cache_size * 64 : -1;
sessionSettings.cache_expiry = pref.diskCacheTTL();
qDebug() << "Using a disk cache size of" << cache_size << "MiB";
sessionSettings.anonymous_mode = pref.isAnonymousModeEnabled();
if (sessionSettings.anonymous_mode) {
addConsoleMessage(tr("Anonymous mode [ON]"), "blue");
} else {
addConsoleMessage(tr("Anonymous mode [OFF]"), "blue");
}
// Queueing System
2012-02-20 21:30:53 +04:00
if (pref.isQueueingSystemEnabled()) {
int max_downloading = pref.getMaxActiveDownloads();
int max_active = pref.getMaxActiveTorrents();
if (max_downloading > -1)
sessionSettings.active_downloads = max_downloading + HiddenData::getDownloadingSize();
else
sessionSettings.active_downloads = max_downloading;
if (max_active > -1) {
int limit = max_active + HiddenData::getDownloadingSize();
sessionSettings.active_limit = limit;
sessionSettings.active_tracker_limit = limit;
sessionSettings.active_dht_limit = limit;
sessionSettings.active_lsd_limit = limit;
}
else {
sessionSettings.active_limit = max_active;
sessionSettings.active_tracker_limit = max_active;
sessionSettings.active_dht_limit = max_active;
sessionSettings.active_lsd_limit = max_active;
}
sessionSettings.active_seeds = pref.getMaxActiveUploads();
sessionSettings.dont_count_slow_torrents = pref.ignoreSlowTorrentsForQueueing();
setQueueingEnabled(true);
} 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;
setQueueingEnabled(false);
}
// Outgoing ports
sessionSettings.outgoing_ports = std::make_pair(pref.outgoingPortsMin(), pref.outgoingPortsMax());
// Ignore limits on LAN
2010-11-24 23:31:14 +03:00
qDebug() << "Ignore limits on LAN" << pref.ignoreLimitsOnLAN();
sessionSettings.ignore_limits_on_local_network = pref.ignoreLimitsOnLAN();
// Include overhead in transfer limits
sessionSettings.rate_limit_ip_overhead = pref.includeOverheadInLimits();
// IP address to announce to trackers
QString announce_ip = pref.getNetworkAddress();
2013-10-23 23:16:23 +04:00
if (!announce_ip.isEmpty())
sessionSettings.announce_ip = announce_ip.toStdString();
2010-11-29 00:12:42 +03:00
// Super seeding
sessionSettings.strict_super_seeding = pref.isSuperSeedingEnabled();
// * Max Half-open connections
sessionSettings.half_open_limit = pref.getMaxHalfOpenConnections();
// * Max connections limit
sessionSettings.connections_limit = pref.getMaxConnecs();
// * Global max upload slots
sessionSettings.unchoke_slots_limit = pref.getMaxUploads();
2011-04-17 18:42:38 +04:00
// uTP
2013-06-27 16:29:02 +04:00
sessionSettings.enable_incoming_utp = pref.isuTPEnabled();
sessionSettings.enable_outgoing_utp = pref.isuTPEnabled();
2011-04-17 18:42:38 +04:00
// uTP rate limiting
sessionSettings.rate_limit_utp = pref.isuTPRateLimited();
if (sessionSettings.rate_limit_utp)
sessionSettings.mixed_mode_algorithm = session_settings::prefer_tcp;
else
sessionSettings.mixed_mode_algorithm = session_settings::peer_proportional;
sessionSettings.connection_speed = 20; //default is 10
qDebug() << "Settings SessionSettings";
setSessionSettings(sessionSettings);
// Bittorrent
// * Max connections per torrent limit
setMaxConnectionsPerTorrent(pref.getMaxConnecsPerTorrent());
// * Max uploads per torrent limit
setMaxUploadsPerTorrent(pref.getMaxUploadsPerTorrent());
// * DHT
2012-02-20 21:30:53 +04:00
if (pref.isDHTEnabled()) {
// Set DHT Port
2012-02-20 21:30:53 +04:00
if (enableDHT(true)) {
int dht_port = 0;
#if LIBTORRENT_VERSION_NUM < 10000
if (!pref.isDHTPortSameAsBT())
dht_port = pref.getDHTPort();
setDHTPort(dht_port);
#endif
2012-02-20 21:30:53 +04:00
if (dht_port == 0) dht_port = new_listenPort;
addConsoleMessage(tr("DHT support [ON], port: UDP/%1").arg(dht_port), QString::fromUtf8("blue"));
} else {
addConsoleMessage(tr("DHT support [OFF]"), QString::fromUtf8("red"));
}
} else {
enableDHT(false);
addConsoleMessage(tr("DHT support [OFF]"), QString::fromUtf8("blue"));
}
// * PeX
2012-02-20 21:30:53 +04:00
if (PeXEnabled) {
addConsoleMessage(tr("PeX support [ON]"), QString::fromUtf8("blue"));
} else {
addConsoleMessage(tr("PeX support [OFF]"), QString::fromUtf8("red"));
}
2012-02-20 21:30:53 +04:00
if (PeXEnabled != pref.isPeXEnabled()) {
addConsoleMessage(tr("Restart is required to toggle PeX support"), QString::fromUtf8("red"));
}
// * LSD
2012-02-20 21:30:53 +04:00
if (pref.isLSDEnabled()) {
enableLSD(true);
2010-12-05 19:36:02 +03:00
addConsoleMessage(tr("Local Peer Discovery support [ON]"), QString::fromUtf8("blue"));
} else {
enableLSD(false);
addConsoleMessage(tr("Local Peer Discovery support [OFF]"), QString::fromUtf8("blue"));
}
// * Encryption
const int encryptionState = pref.getEncryptionSetting();
// The most secure, rc4 only so that all streams and encrypted
pe_settings encryptionSettings;
encryptionSettings.allowed_enc_level = pe_settings::rc4;
encryptionSettings.prefer_rc4 = true;
switch(encryptionState) {
case 0: //Enabled
encryptionSettings.out_enc_policy = pe_settings::enabled;
encryptionSettings.in_enc_policy = pe_settings::enabled;
addConsoleMessage(tr("Encryption support [ON]"), QString::fromUtf8("blue"));
break;
case 1: // Forced
encryptionSettings.out_enc_policy = pe_settings::forced;
encryptionSettings.in_enc_policy = pe_settings::forced;
addConsoleMessage(tr("Encryption support [FORCED]"), QString::fromUtf8("blue"));
break;
default: // Disabled
encryptionSettings.out_enc_policy = pe_settings::disabled;
encryptionSettings.in_enc_policy = pe_settings::disabled;
addConsoleMessage(tr("Encryption support [OFF]"), QString::fromUtf8("blue"));
}
applyEncryptionSettings(encryptionSettings);
// * Maximum ratio
high_ratio_action = pref.getMaxRatioAction();
setGlobalMaxRatio(pref.getGlobalMaxRatio());
updateRatioTimer();
// Ip Filter
FilterParserThread::processFilterList(s, pref.bannedIPs());
2012-02-20 21:30:53 +04:00
if (pref.isFilteringEnabled()) {
enableIPFilter(pref.getFilter());
}else{
disableIPFilter();
}
2009-11-18 20:46:59 +03:00
// Update Web UI
// Use a QTimer because the function can be called from qBtSession constructor
QTimer::singleShot(0, this, SLOT(initWebUi()));
// * Proxy settings
proxy_settings proxySettings;
2012-02-20 21:30:53 +04:00
if (pref.isProxyEnabled()) {
qDebug("Enabling P2P proxy");
proxySettings.hostname = pref.getProxyIp().toStdString();
qDebug("hostname is %s", proxySettings.hostname.c_str());
proxySettings.port = pref.getProxyPort();
qDebug("port is %d", proxySettings.port);
2012-02-20 21:30:53 +04:00
if (pref.isProxyAuthEnabled()) {
proxySettings.username = pref.getProxyUsername().toStdString();
proxySettings.password = pref.getProxyPassword().toStdString();
qDebug("username is %s", proxySettings.username.c_str());
qDebug("password is %s", proxySettings.password.c_str());
}
}
switch(pref.getProxyType()) {
case Proxy::HTTP:
qDebug("type: http");
proxySettings.type = proxy_settings::http;
break;
case Proxy::HTTP_PW:
qDebug("type: http_pw");
proxySettings.type = proxy_settings::http_pw;
break;
case Proxy::SOCKS4:
proxySettings.type = proxy_settings::socks4;
break;
case Proxy::SOCKS5:
qDebug("type: socks5");
proxySettings.type = proxy_settings::socks5;
break;
case Proxy::SOCKS5_PW:
qDebug("type: socks5_pw");
proxySettings.type = proxy_settings::socks5_pw;
break;
default:
proxySettings.type = proxy_settings::none;
}
setProxySettings(proxySettings);
// Tracker
2012-02-20 21:30:53 +04:00
if (pref.isTrackerEnabled()) {
if (!m_tracker) {
m_tracker = new QTracker(this);
}
2012-02-20 21:30:53 +04:00
if (m_tracker->start()) {
addConsoleMessage(tr("Embedded Tracker [ON]"), QString::fromUtf8("blue"));
} else {
addConsoleMessage(tr("Failed to start the embedded tracker!"), QString::fromUtf8("red"));
}
} else {
addConsoleMessage(tr("Embedded Tracker [OFF]"));
2012-02-20 21:30:53 +04:00
if (m_tracker)
delete m_tracker;
}
2011-04-13 21:32:28 +04:00
// * Scan dirs
const QStringList scan_dirs = pref.getScanDirs();
QList<bool> downloadInDirList = pref.getDownloadInScanDirs();
while(scan_dirs.size() > downloadInDirList.size()) {
downloadInDirList << false;
}
int i = 0;
foreach (const QString &dir, scan_dirs) {
qDebug() << "Adding scan dir" << dir << downloadInDirList.at(i);
m_scanFolders->addPath(dir, downloadInDirList.at(i));
++i;
}
qDebug("Session configured");
}
void QBtSession::initWebUi() {
Preferences pref;
if (pref.isWebUiEnabled()) {
const quint16 port = pref.getWebUiPort();
const QString username = pref.getWebUiUsername();
const QString password = pref.getWebUiPassword();
2012-02-20 21:30:53 +04:00
if (httpServer) {
if (httpServer->serverPort() != port) {
httpServer->close();
}
} else {
httpServer = new HttpServer(this);
}
#ifndef QT_NO_OPENSSL
if (pref.isWebUiHttpsEnabled()) {
QSslCertificate cert(pref.getWebUiHttpsCertificate());
QSslKey key;
const QByteArray raw_key = pref.getWebUiHttpsKey();
key = QSslKey(raw_key, QSsl::Rsa);
if (!cert.isNull() && !key.isNull())
httpServer->enableHttps(cert, key);
else
httpServer->disableHttps();
} else {
httpServer->disableHttps();
}
#endif
httpServer->setAuthorization(username, password);
httpServer->setlocalAuthEnabled(pref.isWebUiLocalAuthEnabled());
2012-02-20 21:30:53 +04:00
if (!httpServer->isListening()) {
bool success = httpServer->listen(QHostAddress::Any, port);
if (success)
addConsoleMessage(tr("The Web UI is listening on port %1").arg(port));
else
addConsoleMessage(tr("Web User Interface Error - Unable to bind Web UI to port %1").arg(port), "red");
}
// DynDNS
2012-02-20 21:30:53 +04:00
if (pref.isDynDNSEnabled()) {
if (!m_dynDNSUpdater)
m_dynDNSUpdater = new DNSUpdater(this);
else
m_dynDNSUpdater->updateCredentials();
} else {
2012-02-20 21:30:53 +04:00
if (m_dynDNSUpdater) {
delete m_dynDNSUpdater;
m_dynDNSUpdater = 0;
}
}
} else {
2012-02-20 21:30:53 +04:00
if (httpServer)
delete httpServer;
2012-02-20 21:30:53 +04:00
if (m_dynDNSUpdater) {
delete m_dynDNSUpdater;
m_dynDNSUpdater = 0;
}
}
2009-11-18 20:46:59 +03:00
}
2010-10-17 18:46:01 +04:00
void QBtSession::useAlternativeSpeedsLimit(bool alternative) {
2010-12-02 20:10:34 +03:00
qDebug() << Q_FUNC_INFO << alternative;
// Save new state to remember it on startup
Preferences pref;
// Stop the scheduler when the user has manually changed the bandwidth mode
if (!pref.isSchedulerEnabled())
delete bd_scheduler;
pref.setAltBandwidthEnabled(alternative);
// Apply settings to the bittorrent session
2010-12-02 20:02:13 +03:00
int down_limit = alternative ? pref.getAltGlobalDownloadLimit() : pref.getGlobalDownloadLimit();
2012-02-20 21:30:53 +04:00
if (down_limit <= 0) {
2010-12-02 20:02:13 +03:00
down_limit = -1;
} else {
2010-12-02 20:02:13 +03:00
down_limit *= 1024;
}
2010-12-02 20:02:13 +03:00
setDownloadRateLimit(down_limit);
// Upload rate
int up_limit = alternative ? pref.getAltGlobalUploadLimit() : pref.getGlobalUploadLimit();
2012-02-20 21:30:53 +04:00
if (up_limit <= 0) {
2010-12-02 20:02:13 +03:00
up_limit = -1;
} else {
up_limit *= 1024;
}
setUploadRateLimit(up_limit);
// Notify
emit alternativeSpeedsModeChanged(alternative);
}
// Return the torrent handle, given its hash
2012-02-20 23:32:58 +04:00
QTorrentHandle QBtSession::getTorrentHandle(const QString &hash) const {
2012-02-16 23:04:02 +04:00
return QTorrentHandle(s->find_torrent(QStringToSha1(hash)));
}
2010-10-17 18:46:01 +04:00
bool QBtSession::hasActiveTorrents() const {
2010-08-20 13:02:27 +04:00
std::vector<torrent_handle> torrents = s->get_torrents();
2012-07-14 02:28:23 +04:00
std::vector<torrent_handle>::iterator torrentIT = torrents.begin();
std::vector<torrent_handle>::iterator torrentITend = torrents.end();
for ( ; torrentIT != torrentITend; ++torrentIT) {
const QTorrentHandle h(*torrentIT);
2012-02-20 21:30:53 +04:00
if (h.is_valid() && !h.is_paused() && !h.is_queued())
return true;
}
return false;
}
2010-10-17 18:46:01 +04:00
bool QBtSession::hasDownloadingTorrents() const {
2010-08-20 13:02:27 +04:00
std::vector<torrent_handle> torrents = s->get_torrents();
2012-07-14 02:28:23 +04:00
std::vector<torrent_handle>::iterator torrentIT = torrents.begin();
std::vector<torrent_handle>::iterator torrentITend = torrents.end();
for ( ; torrentIT != torrentITend; ++torrentIT) {
2012-02-20 21:30:53 +04:00
if (torrentIT->is_valid()) {
2010-08-20 13:02:27 +04:00
try {
const torrent_status status = torrentIT->status();
if (status.state != torrent_status::finished && status.state != torrent_status::seeding
&& !status.paused)
2010-08-20 13:02:27 +04:00
return true;
} catch(std::exception) {}
}
}
return false;
}
2010-10-17 18:46:01 +04:00
void QBtSession::banIP(QString ip) {
FilterParserThread::processFilterList(s, QStringList(ip));
Preferences().banIP(ip);
}
// Delete a torrent from the session, given its hash
// permanent = true means that the torrent will be removed from the hard-drive too
2011-02-26 22:56:15 +03:00
void QBtSession::deleteTorrent(const QString &hash, bool delete_local_files) {
2010-03-04 23:19:25 +03:00
qDebug("Deleting torrent with hash: %s", qPrintable(hash));
2010-07-23 18:05:53 +04:00
const QTorrentHandle h = getTorrentHandle(hash);
2012-02-20 21:30:53 +04:00
if (!h.is_valid()) {
qDebug("/!\\ Error: Invalid handle");
return;
}
emit torrentAboutToBeRemoved(h);
qDebug("h is valid, getting name or hash...");
QString fileName;
2012-02-20 21:30:53 +04:00
if (h.has_metadata())
fileName = h.name();
else
fileName = h.hash();
// Remove it from session
2012-02-20 21:30:53 +04:00
if (delete_local_files) {
if (h.has_metadata()) {
QDir save_dir(h.save_path());
if (save_dir != QDir(defaultSavePath) && (defaultTempPath.isEmpty() || save_dir != QDir(defaultTempPath))) {
savePathsToRemove[hash] = save_dir.absolutePath();
qDebug() << "Save path to remove (async): " << save_dir.absolutePath();
}
}
s->remove_torrent(h, session::delete_files);
} else {
QStringList uneeded_files;
2012-02-20 21:30:53 +04:00
if (h.has_metadata())
2011-04-17 14:29:44 +04:00
uneeded_files = h.absolute_files_path_uneeded();
s->remove_torrent(h);
// Remove unneeded and incomplete files
2012-02-20 21:30:53 +04:00
foreach (const QString &uneeded_file, uneeded_files) {
qDebug("Removing uneeded file: %s", qPrintable(uneeded_file));
fsutils::forceRemove(uneeded_file);
const QString parent_folder = fsutils::branchPath(uneeded_file);
qDebug("Attempt to remove parent folder (if empty): %s", qPrintable(parent_folder));
QDir().rmpath(parent_folder);
}
}
// Remove it from torrent backup directory
QDir torrentBackup(fsutils::BTBackupLocation());
QStringList filters;
filters << hash+".*";
2010-07-23 18:05:53 +04:00
const QStringList files = torrentBackup.entryList(filters, QDir::Files, QDir::Unsorted);
2012-02-20 21:30:53 +04:00
foreach (const QString &file, files) {
fsutils::forceRemove(torrentBackup.absoluteFilePath(file));
}
TorrentPersistentData::deletePersistentData(hash);
TorrentTempData::deleteTempData(hash);
HiddenData::deleteData(hash);
// Remove tracker errors
trackersInfos.remove(hash);
2012-02-20 21:30:53 +04:00
if (delete_local_files)
addConsoleMessage(tr("'%1' was removed from transfer list and hard disk.", "'xxx.avi' was removed...").arg(fileName));
else
addConsoleMessage(tr("'%1' was removed from transfer list.", "'xxx.avi' was removed...").arg(fileName));
qDebug("Torrent deleted.");
2008-05-16 11:10:50 +04:00
emit deletedTorrent(hash);
qDebug("Deleted signal emitted.");
}
2010-10-17 18:46:01 +04:00
void QBtSession::pauseAllTorrents() {
2010-08-20 13:02:27 +04:00
std::vector<torrent_handle> torrents = s->get_torrents();
2012-07-14 02:28:23 +04:00
std::vector<torrent_handle>::iterator torrentIT = torrents.begin();
std::vector<torrent_handle>::iterator torrentITend = torrents.end();
for ( ; torrentIT != torrentITend; ++torrentIT) {
2010-11-14 22:22:39 +03:00
try {
QTorrentHandle h = QTorrentHandle(*torrentIT);
2012-02-20 21:30:53 +04:00
if (!h.is_paused()) {
2010-11-14 22:22:39 +03:00
h.pause();
emit pausedTorrent(h);
}
} catch(invalid_handle&) {}
}
2008-05-16 11:10:50 +04:00
}
2010-10-17 18:46:01 +04:00
std::vector<torrent_handle> QBtSession::getTorrents() const {
2010-08-20 13:02:27 +04:00
return s->get_torrents();
}
2010-10-17 18:46:01 +04:00
void QBtSession::resumeAllTorrents() {
2010-08-20 13:02:27 +04:00
std::vector<torrent_handle> torrents = s->get_torrents();
2012-07-14 02:28:23 +04:00
std::vector<torrent_handle>::iterator torrentIT = torrents.begin();
std::vector<torrent_handle>::iterator torrentITend = torrents.end();
for ( ; torrentIT != torrentITend; ++torrentIT) {
2010-11-14 22:22:39 +03:00
try {
QTorrentHandle h = QTorrentHandle(*torrentIT);
2012-02-20 21:30:53 +04:00
if (h.is_paused()) {
2010-11-14 22:22:39 +03:00
h.resume();
emit resumedTorrent(h);
}
} catch(invalid_handle&) {}
}
2008-05-16 11:10:50 +04:00
}
2011-02-26 22:56:15 +03:00
void QBtSession::pauseTorrent(const QString &hash) {
QTorrentHandle h = getTorrentHandle(hash);
2012-02-20 21:30:53 +04:00
if (!h.is_paused()) {
h.pause();
emit pausedTorrent(h);
}
2008-12-27 13:14:16 +03:00
}
2011-02-26 22:56:15 +03:00
void QBtSession::resumeTorrent(const QString &hash) {
QTorrentHandle h = getTorrentHandle(hash);
2012-02-20 21:30:53 +04:00
if (h.is_paused()) {
h.resume();
emit resumedTorrent(h);
}
2008-12-27 13:14:16 +03:00
}
2011-02-26 22:56:15 +03:00
bool QBtSession::loadFastResumeData(const QString &hash, std::vector<char> &buf) {
const QString fastresume_path = QDir(fsutils::BTBackupLocation()).absoluteFilePath(hash+QString(".fastresume"));
2010-10-17 18:46:01 +04:00
qDebug("Trying to load fastresume data: %s", qPrintable(fastresume_path));
2011-01-23 22:31:48 +03:00
QFile fastresume_file(fastresume_path);
if (fastresume_file.size() <= 0)
return false;
if (!fastresume_file.open(QIODevice::ReadOnly))
return false;
2011-01-23 22:31:48 +03:00
const QByteArray content = fastresume_file.readAll();
const int content_size = content.size();
Q_ASSERT(content_size > 0);
2011-01-23 22:31:48 +03:00
buf.resize(content_size);
memcpy(&buf[0], content.data(), content_size);
fastresume_file.close();
2011-01-23 22:31:48 +03:00
return true;
2010-10-17 18:46:01 +04:00
}
2011-02-26 22:56:15 +03:00
void QBtSession::loadTorrentSettings(QTorrentHandle& h) {
Preferences pref;
2010-10-17 18:46:01 +04:00
// Connections limit per torrent
h.set_max_connections(pref.getMaxConnecsPerTorrent());
2010-10-17 18:46:01 +04:00
// Uploads limit per torrent
h.set_max_uploads(pref.getMaxUploadsPerTorrent());
2010-10-17 18:46:01 +04:00
#ifndef DISABLE_GUI
// Resolve countries
h.resolve_countries(resolve_countries);
#endif
}
QTorrentHandle QBtSession::addMagnetUri(QString magnet_uri, bool resumed, bool fromScanDir, const QString &filePath)
{
Q_UNUSED(fromScanDir);
Q_UNUSED(filePath);
2012-05-15 20:57:31 +04:00
Preferences pref;
QTorrentHandle h;
add_torrent_params p;
libtorrent::error_code ec;
libtorrent::parse_magnet_uri(magnet_uri.toUtf8().constData(), p, ec);
if (ec) {
2013-10-24 02:57:57 +04:00
addConsoleMessage(tr("Couldn't parse this Magnet URI: '%1'").arg(magnet_uri));
return h;
}
const QString hash(misc::toQString(p.info_hash));
2012-02-20 21:30:53 +04:00
if (hash.isEmpty()) {
addConsoleMessage(tr("'%1' is not a valid magnet URI.").arg(magnet_uri));
return h;
}
const QDir torrentBackup(fsutils::BTBackupLocation());
2012-02-20 21:30:53 +04:00
if (resumed) {
// Load metadata
2010-10-17 18:46:01 +04:00
const QString torrent_path = torrentBackup.absoluteFilePath(hash+".torrent");
2012-02-20 21:30:53 +04:00
if (QFile::exists(torrent_path))
2010-10-17 18:46:01 +04:00
return addTorrent(torrent_path, false, QString::null, true);
}
2010-10-17 18:46:01 +04:00
qDebug("Adding a magnet URI: %s", qPrintable(hash));
Q_ASSERT(magnet_uri.startsWith("magnet:", Qt::CaseInsensitive));
// limit h_ex scope
{
// Check for duplicate torrent
QTorrentHandle h_ex = QTorrentHandle(s->find_torrent(p.info_hash));
if (h_ex.is_valid()) {
qDebug("/!\\ Torrent is already in download list");
addConsoleMessage(tr("'%1' is already in download list.", "e.g: 'xxx.avi' is already in download list.").arg(magnet_uri));
// Check if the torrent contains trackers or url seeds we don't know about
// and add them
mergeTorrents(h_ex, magnet_uri);
return h;
}
}
initializeAddTorrentParams(hash, p);
2010-10-17 18:46:01 +04:00
// Get save path
QString savePath;
if (!resumed && savepathLabel_fromurl.contains(magnet_uri)) {
QPair<QString, QString> savePath_label = savepathLabel_fromurl.take(magnet_uri);
if(!savePath_label.first.isEmpty())
savePath = savePath_label.first;
// Remember label
if(!savePath_label.second.isEmpty())
TorrentTempData::setLabel(hash, savePath_label.second);
}
if (savePath.isEmpty())
savePath = getSavePath(hash, false);
if (!defaultTempPath.isEmpty() && !TorrentPersistentData::isSeed(hash)) {
qDebug("addMagnetURI: Temp folder is enabled.");
QString torrent_tmp_path = defaultTempPath;
p.save_path = fsutils::toNativePath(torrent_tmp_path).toUtf8().constData();
// Check if save path exists, creating it otherwise
2012-02-20 21:30:53 +04:00
if (!QDir(torrent_tmp_path).exists())
QDir().mkpath(torrent_tmp_path);
qDebug("addTorrent: using save_path: %s", qPrintable(torrent_tmp_path));
} else {
p.save_path = fsutils::toNativePath(savePath).toUtf8().constData();
// Check if save path exists, creating it otherwise
2012-02-20 21:30:53 +04:00
if (!QDir(savePath).exists()) QDir().mkpath(savePath);
qDebug("addTorrent: using save_path: %s", qPrintable(savePath));
}
qDebug("Adding magnet URI: %s", qPrintable(magnet_uri));
// Adding torrent to Bittorrent session
try {
h = QTorrentHandle(s->add_torrent(p));
}catch(std::exception &e) {
qDebug("Error: %s", e.what());
}
// Check if it worked
2012-02-20 21:30:53 +04:00
if (!h.is_valid()) {
// No need to keep on, it failed.
qDebug("/!\\ Error: Invalid handle");
return h;
}
Q_ASSERT(h.hash() == hash);
2010-10-17 18:46:01 +04:00
loadTorrentSettings(h);
// Load filtered files
2012-02-20 21:30:53 +04:00
if (!resumed) {
2010-10-17 18:46:01 +04:00
loadTorrentTempData(h, savePath, true);
}
if (HiddenData::hasData(hash) && pref.isQueueingSystemEnabled()) {
//Internally increase the queue limits to ensure that the magnet is started
libtorrent::session_settings sessionSettings(s->settings());
int max_downloading = pref.getMaxActiveDownloads();
int max_active = pref.getMaxActiveTorrents();
if (max_downloading > -1)
sessionSettings.active_downloads = max_downloading + HiddenData::getDownloadingSize();
else
sessionSettings.active_downloads = max_downloading;
if (max_active > -1)
sessionSettings.active_limit = max_active + HiddenData::getDownloadingSize();
else
sessionSettings.active_limit = max_active;
s->set_settings(sessionSettings);
h.queue_position_top();
}
if (!pref.addTorrentsInPause() || HiddenData::hasData(hash)) {
// Start torrent because it was added in paused state
h.resume();
}
// Send torrent addition signal
2010-12-01 22:05:26 +03:00
addConsoleMessage(tr("'%1' added to download list.", "'/home/y/xxx.torrent' was added to download list.").arg(magnet_uri));
if (!HiddenData::hasData(hash))
emit addedTorrent(h);
return h;
}
// Add a torrent to the Bittorrent session
2010-10-17 18:46:01 +04:00
QTorrentHandle QBtSession::addTorrent(QString path, bool fromScanDir, QString from_url, bool resumed) {
QTorrentHandle h;
2012-05-15 20:57:31 +04:00
Preferences pref;
2010-10-17 18:46:01 +04:00
// Check if BT_backup directory exists
const QDir torrentBackup(fsutils::BTBackupLocation());
2012-02-20 21:30:53 +04:00
if (!torrentBackup.exists()) return h;
2010-10-17 18:46:01 +04:00
// Fix the input path if necessary
path = fsutils::fromNativePath(path);
#ifdef Q_OS_WIN
2010-06-04 00:08:19 +04:00
// Windows hack
2012-02-20 21:30:53 +04:00
if (!path.endsWith(".torrent"))
if (QFile::rename(path, path+".torrent")) path += ".torrent";
2010-05-30 21:51:40 +04:00
#endif
2012-02-20 21:30:53 +04:00
if (path.startsWith("file:", Qt::CaseInsensitive))
path = QUrl::fromEncoded(path.toLocal8Bit()).toLocalFile();
2012-02-20 21:30:53 +04:00
if (path.isEmpty()) return h;
2010-10-17 18:46:01 +04:00
2010-12-30 22:12:03 +03:00
Q_ASSERT(!misc::isUrl(path));
2010-12-30 22:12:03 +03:00
qDebug("Adding %s to download list", qPrintable(path));
2010-10-17 18:46:01 +04:00
boost::intrusive_ptr<torrent_info> t;
try {
2011-04-09 13:03:04 +04:00
qDebug() << "Loading torrent at" << path;
// Getting torrent file informations
t = new torrent_info(fsutils::toNativePath(path).toUtf8().constData());
2012-02-20 21:30:53 +04:00
if (!t->is_valid())
throw std::exception();
} catch(std::exception& e) {
2012-02-20 21:30:53 +04:00
if (!from_url.isNull()) {
addConsoleMessage(tr("Unable to decode torrent file: '%1'", "e.g: Unable to decode torrent file: '/home/y/xxx.torrent'").arg(from_url), QString::fromUtf8("red"));
addConsoleMessage(misc::toQString(e.what()), "red");
//emit invalidTorrent(from_url);
fsutils::forceRemove(path);
}else{
addConsoleMessage(tr("Unable to decode torrent file: '%1'", "e.g: Unable to decode torrent file: '/home/y/xxx.torrent'").arg(fsutils::toNativePath(path)), QString::fromUtf8("red"));
2010-12-30 22:12:03 +03:00
//emit invalidTorrent(path);
}
addConsoleMessage(tr("This file is either corrupted or this isn't a torrent."),QString::fromUtf8("red"));
2012-02-20 21:30:53 +04:00
if (fromScanDir) {
// Remove file
fsutils::forceRemove(path);
}
return h;
}
2010-10-17 18:46:01 +04:00
const QString hash = misc::toQString(t->info_hash());
2010-03-03 20:27:25 +03:00
qDebug(" -> Hash: %s", qPrintable(hash));
qDebug(" -> Name: %s", t->name().c_str());
2010-10-17 18:46:01 +04:00
// Check for duplicate
2012-02-20 21:30:53 +04:00
if (s->find_torrent(t->info_hash()).is_valid()) {
qDebug("/!\\ Torrent is already in download list");
// Update info Bar
2012-02-20 21:30:53 +04:00
if (!from_url.isNull()) {
addConsoleMessage(tr("'%1' is already in download list.", "e.g: 'xxx.avi' is already in download list.").arg(from_url));
}else{
addConsoleMessage(tr("'%1' is already in download list.", "e.g: 'xxx.avi' is already in download list.").arg(fsutils::toNativePath(path)));
}
// Check if the torrent contains trackers or url seeds we don't know about
// and add them
2011-02-26 22:56:15 +03:00
QTorrentHandle h_ex = getTorrentHandle(hash);
mergeTorrents(h_ex, t);
2010-10-17 16:18:34 +04:00
2010-10-17 18:46:01 +04:00
// Delete file if temporary
2012-02-20 21:30:53 +04:00
if (!from_url.isNull() || fromScanDir)
fsutils::forceRemove(path);
return h;
}
2010-10-17 18:46:01 +04:00
// Check number of files
2012-02-20 21:30:53 +04:00
if (t->num_files() < 1) {
addConsoleMessage(tr("Error: The torrent %1 does not contain any file.").arg(misc::toQStringU(t->name())));
2010-10-17 18:46:01 +04:00
// Delete file if temporary
2012-02-20 21:30:53 +04:00
if (!from_url.isNull() || fromScanDir)
fsutils::forceRemove(path);
return h;
}
2010-10-17 18:46:01 +04:00
// Actually add the torrent
add_torrent_params p;
initializeAddTorrentParams(hash, p);
2010-10-17 18:46:01 +04:00
p.ti = t;
// Get fast resume data if existing
bool fastResume = false;
std::vector<char> buf; // Needs to stay in the function scope
2012-02-20 21:30:53 +04:00
if (resumed) {
if (loadFastResumeData(hash, buf)) {
fastResume = true;
#if LIBTORRENT_VERSION_NUM < 10000
p.resume_data = &buf;
2014-01-01 02:10:41 +04:00
#else
p.resume_data = buf;
#endif
2010-10-17 18:46:01 +04:00
qDebug("Successfully loaded fast resume data");
}
}
recoverPersistentData(hash, buf);
QString savePath;
2012-02-20 21:30:53 +04:00
if (!from_url.isEmpty() && savepathLabel_fromurl.contains(QUrl::fromEncoded(from_url.toUtf8()))) {
// Enforcing the save path defined before URL download (from RSS for example)
QPair<QString, QString> savePath_label = savepathLabel_fromurl.take(QUrl::fromEncoded(from_url.toUtf8()));
2012-02-20 21:30:53 +04:00
if (savePath_label.first.isEmpty())
2012-05-15 20:57:31 +04:00
savePath = getSavePath(hash, fromScanDir, path);
else
savePath = savePath_label.first;
// Remember label
TorrentTempData::setLabel(hash, savePath_label.second);
} else {
2012-05-15 20:57:31 +04:00
savePath = getSavePath(hash, fromScanDir, path);
}
if (!defaultTempPath.isEmpty() && !TorrentPersistentData::isSeed(hash)) {
qDebug("addTorrent::Temp folder is enabled.");
QString torrent_tmp_path = defaultTempPath;
p.save_path = fsutils::toNativePath(torrent_tmp_path).toUtf8().constData();
// Check if save path exists, creating it otherwise
2012-02-20 21:30:53 +04:00
if (!QDir(torrent_tmp_path).exists()) QDir().mkpath(torrent_tmp_path);
qDebug("addTorrent: using save_path: %s", qPrintable(torrent_tmp_path));
} else {
p.save_path = fsutils::toNativePath(savePath).toUtf8().constData();
// Check if save path exists, creating it otherwise
2012-02-20 21:30:53 +04:00
if (!QDir(savePath).exists()) QDir().mkpath(savePath);
2010-03-03 20:27:25 +03:00
qDebug("addTorrent: using save_path: %s", qPrintable(savePath));
}
// Adding torrent to Bittorrent session
try {
h = QTorrentHandle(s->add_torrent(p));
}catch(std::exception &e) {
qDebug("Error: %s", e.what());
}
// Check if it worked
2012-02-20 21:30:53 +04:00
if (!h.is_valid()) {
qDebug("/!\\ Error: Invalid handle");
2012-02-20 21:30:53 +04:00
if (!from_url.isNull())
fsutils::forceRemove(path);
return h;
}
2010-10-17 18:46:01 +04:00
loadTorrentSettings(h);
2012-02-20 21:30:53 +04:00
if (!resumed) {
qDebug("This is a NEW torrent (first time)...");
2010-10-17 18:46:01 +04:00
loadTorrentTempData(h, savePath, false);
// Append .!qB to incomplete files
2012-02-20 21:30:53 +04:00
if (appendqBExtension)
appendqBextensionToTorrent(h, true);
2012-02-18 18:44:20 +04:00
// Backup torrent file
2010-07-23 18:05:53 +04:00
const QString newFile = torrentBackup.absoluteFilePath(hash + ".torrent");
2012-02-20 21:30:53 +04:00
if (path != newFile)
2010-12-30 22:12:03 +03:00
QFile::copy(path, newFile);
// Copy the torrent file to the export folder
if (m_torrentExportEnabled)
2010-10-17 18:46:01 +04:00
exportTorrentFile(h);
}
2010-10-17 18:46:01 +04:00
2012-05-15 20:57:31 +04:00
if (!fastResume && !pref.addTorrentsInPause()) {
// Start torrent because it was added in paused state
h.resume();
}
2010-10-17 18:46:01 +04:00
// If temporary file, remove it
2012-02-20 21:30:53 +04:00
if (!from_url.isNull() || fromScanDir)
fsutils::forceRemove(path);
2010-10-17 18:46:01 +04:00
// Display console message
2012-02-20 21:30:53 +04:00
if (!from_url.isNull()) {
if (fastResume)
addConsoleMessage(tr("'%1' resumed. (fast resume)", "'/home/y/xxx.torrent' was resumed. (fast resume)").arg(from_url));
else
addConsoleMessage(tr("'%1' added to download list.", "'/home/y/xxx.torrent' was added to download list.").arg(from_url));
}else{
2012-02-20 21:30:53 +04:00
if (fastResume)
addConsoleMessage(tr("'%1' resumed. (fast resume)", "'/home/y/xxx.torrent' was resumed. (fast resume)").arg(fsutils::toNativePath(path)));
else
addConsoleMessage(tr("'%1' added to download list.", "'/home/y/xxx.torrent' was added to download list.").arg(fsutils::toNativePath(path)));
}
2010-10-17 18:46:01 +04:00
// Send torrent addition signal
emit addedTorrent(h);
return h;
}
void QBtSession::exportTorrentFile(const QTorrentHandle& h, TorrentExportFolder folder) {
Q_ASSERT((folder == RegularTorrentExportFolder && m_torrentExportEnabled) ||
(folder == FinishedTorrentExportFolder && m_finishedTorrentExportEnabled));
QString torrent_path = QDir(fsutils::BTBackupLocation()).absoluteFilePath(h.hash()+".torrent");
QDir exportPath(folder == RegularTorrentExportFolder ? Preferences().getTorrentExportDir() : Preferences().getFinishedTorrentExportDir());
2012-02-20 21:30:53 +04:00
if (exportPath.exists() || exportPath.mkpath(exportPath.absolutePath())) {
2010-10-17 18:46:01 +04:00
QString new_torrent_path = exportPath.absoluteFilePath(h.name()+".torrent");
if (QFile::exists(new_torrent_path) && fsutils::sameFiles(torrent_path, new_torrent_path)) {
2010-10-17 18:46:01 +04:00
// Append hash to torrent name to make it unique
new_torrent_path = exportPath.absoluteFilePath(h.name()+"-"+h.hash()+".torrent");
}
QFile::copy(torrent_path, new_torrent_path);
}
}
void QBtSession::initializeAddTorrentParams(const QString &hash, add_torrent_params &p) {
2010-10-17 18:46:01 +04:00
// Seeding mode
// Skip checking and directly start seeding (new in libtorrent v0.15)
2012-02-20 21:30:53 +04:00
if (TorrentTempData::isSeedingMode(hash))
p.flags |= add_torrent_params::flag_seed_mode;
2010-10-17 18:46:01 +04:00
else
p.flags &= ~add_torrent_params::flag_seed_mode;
2010-10-17 18:46:01 +04:00
// Preallocation mode
2012-02-20 21:30:53 +04:00
if (preAllocateAll)
2010-10-17 18:46:01 +04:00
p.storage_mode = storage_mode_allocate;
else
p.storage_mode = storage_mode_sparse;
// Priorities
2013-10-23 23:16:23 +04:00
/*if (TorrentTempData::hasTempData(hash)) {
std::vector<int> fp;
TorrentTempData::getFilesPriority(hash, fp);
2012-02-20 21:30:53 +04:00
if (!fp.empty()) {
std::vector<boost::uint8_t> *fp_conv = new std::vector<boost::uint8_t>();
2012-02-20 21:30:53 +04:00
for (uint i=0; i<fp.size(); ++i) {
fp_conv->push_back(fp[i]);
}
p.file_priorities = fp_conv;
}
2013-10-23 23:16:23 +04:00
}*/
2010-10-17 18:46:01 +04:00
// Start in pause
p.flags |= add_torrent_params::flag_paused;
p.flags &= ~add_torrent_params::flag_duplicate_is_error; // Already checked
p.flags &= ~add_torrent_params::flag_auto_managed; // Because it is added in paused state
2010-10-17 18:46:01 +04:00
}
2011-02-26 22:56:15 +03:00
void QBtSession::loadTorrentTempData(QTorrentHandle &h, QString savePath, bool magnet) {
qDebug("loadTorrentTempdata() - ENTER");
2010-10-17 18:46:01 +04:00
const QString hash = h.hash();
// Sequential download
2012-02-20 21:30:53 +04:00
if (TorrentTempData::hasTempData(hash)) {
// sequential download
h.set_sequential_download(TorrentTempData::isSequential(hash));
// The following is useless for newly added magnet
2012-02-20 21:30:53 +04:00
if (!magnet) {
// Files priorities
vector<int> fp;
TorrentTempData::getFilesPriority(hash, fp);
h.prioritize_files(fp);
// Prioritize first/last piece
h.prioritize_first_last_piece(TorrentTempData::isSequential(hash));
// Update file names
const QStringList files_path = TorrentTempData::getFilesPath(hash);
bool force_recheck = false;
QDir base_dir(h.save_path());
2012-02-20 21:30:53 +04:00
if (files_path.size() == h.num_files()) {
for (int i=0; i<h.num_files(); ++i) {
const QString &path = files_path.at(i);
if (!force_recheck && base_dir.exists(path))
force_recheck = true;
qDebug("Renaming file to %s", qPrintable(path));
h.rename_file(i, path);
2010-10-17 18:46:01 +04:00
}
// Force recheck
2012-02-20 21:30:53 +04:00
if (force_recheck) h.force_recheck();
2010-10-17 18:46:01 +04:00
}
}
}
// Save persistent data for new torrent
qDebug("Saving torrent persistant data");
2012-02-20 21:30:53 +04:00
if (defaultTempPath.isEmpty())
2010-10-17 18:46:01 +04:00
TorrentPersistentData::saveTorrentPersistentData(h, QString::null, magnet);
else
TorrentPersistentData::saveTorrentPersistentData(h, fsutils::fromNativePath(savePath), magnet);
2010-10-17 18:46:01 +04:00
}
void QBtSession::mergeTorrents(QTorrentHandle& h_ex, const QString& magnet_uri)
{
QList<QUrl> new_trackers = misc::magnetUriToTrackers(magnet_uri);
bool trackers_added = false;
foreach (const QUrl& new_tracker, new_trackers) {
bool found = false;
std::vector<announce_entry> existing_trackers = h_ex.trackers();
foreach (const announce_entry& existing_tracker, existing_trackers) {
if (new_tracker == QUrl(existing_tracker.url.c_str())) {
found = true;
break;
}
}
if (!found) {
h_ex.add_tracker(announce_entry(new_tracker.toString().toStdString()));
trackers_added = true;
}
}
if (trackers_added)
addConsoleMessage(tr("Note: new trackers were added to the existing torrent."));
}
2011-02-26 22:56:15 +03:00
void QBtSession::mergeTorrents(QTorrentHandle &h_ex, boost::intrusive_ptr<torrent_info> t) {
2010-10-17 16:18:34 +04:00
// Check if the torrent contains trackers or url seeds we don't know about
// and add them
2012-02-20 21:30:53 +04:00
if (!h_ex.is_valid()) return;
std::vector<announce_entry> existing_trackers = h_ex.trackers();
2010-10-17 18:46:01 +04:00
std::vector<announce_entry> new_trackers = t->trackers();
bool trackers_added = false;
foreach (const announce_entry& new_tracker, new_trackers) {
std::string new_tracker_url = new_tracker.url;
// Check if existing torrent has this tracker
2010-10-17 18:46:01 +04:00
bool found = false;
foreach (const announce_entry& existing_tracker, existing_trackers) {
2012-02-20 21:30:53 +04:00
if (QUrl(new_tracker_url.c_str()) == QUrl(existing_tracker.url.c_str())) {
2010-10-17 18:46:01 +04:00
found = true;
break;
2010-10-17 16:18:34 +04:00
}
}
if (!found) {
h_ex.add_tracker(announce_entry(new_tracker_url));
2010-10-17 18:46:01 +04:00
trackers_added = true;
2010-10-17 16:18:34 +04:00
}
2010-10-17 18:46:01 +04:00
}
if (trackers_added)
2010-10-17 18:46:01 +04:00
addConsoleMessage(tr("Note: new trackers were added to the existing torrent."));
2010-10-17 18:46:01 +04:00
bool urlseeds_added = false;
const QStringList old_urlseeds = h_ex.url_seeds();
2011-04-17 14:29:44 +04:00
std::vector<web_seed_entry> new_urlseeds = t->web_seeds();
2012-07-14 02:28:23 +04:00
std::vector<web_seed_entry>::iterator it = new_urlseeds.begin();
std::vector<web_seed_entry>::iterator itend = new_urlseeds.end();
for ( ; it != itend; ++it) {
2011-04-17 14:29:44 +04:00
const QString new_url = misc::toQString(it->url.c_str());
if (!old_urlseeds.contains(new_url)) {
2011-04-17 14:29:44 +04:00
urlseeds_added = true;
h_ex.add_url_seed(new_url);
}
}
2012-02-20 21:30:53 +04:00
if (urlseeds_added)
2010-10-17 18:46:01 +04:00
addConsoleMessage(tr("Note: new URL seeds were added to the existing torrent."));
2010-10-17 16:18:34 +04:00
}
2010-10-17 18:46:01 +04:00
void QBtSession::exportTorrentFiles(QString path) {
Q_ASSERT(m_torrentExportEnabled);
QDir exportDir(path);
2012-02-20 21:30:53 +04:00
if (!exportDir.exists()) {
if (!exportDir.mkpath(exportDir.absolutePath())) {
2010-03-03 20:27:25 +03:00
std::cerr << "Error: Could not create torrent export directory: " << qPrintable(exportDir.absolutePath()) << std::endl;
return;
}
}
QDir torrentBackup(fsutils::BTBackupLocation());
std::vector<torrent_handle> handles = s->get_torrents();
2012-07-14 02:28:23 +04:00
std::vector<torrent_handle>::iterator itr=handles.begin();
std::vector<torrent_handle>::iterator itrend=handles.end();
for ( ; itr != itrend; ++itr) {
const QTorrentHandle h(*itr);
2012-02-20 21:30:53 +04:00
if (!h.is_valid()) {
std::cerr << "Torrent Export: torrent is invalid, skipping..." << std::endl;
continue;
}
2010-07-23 18:05:53 +04:00
const QString src_path(torrentBackup.absoluteFilePath(h.hash()+".torrent"));
2012-02-20 21:30:53 +04:00
if (QFile::exists(src_path)) {
QString dst_path = exportDir.absoluteFilePath(h.name()+".torrent");
2012-02-20 21:30:53 +04:00
if (QFile::exists(dst_path)) {
if (!fsutils::sameFiles(src_path, dst_path)) {
dst_path = exportDir.absoluteFilePath(h.name()+"-"+h.hash()+".torrent");
} else {
qDebug("Torrent Export: Destination file exists, skipping...");
continue;
}
}
2010-03-03 20:27:25 +03:00
qDebug("Export Torrent: %s -> %s", qPrintable(src_path), qPrintable(dst_path));
QFile::copy(src_path, dst_path);
} else {
2010-03-03 20:27:25 +03:00
std::cerr << "Error: could not export torrent "<< qPrintable(h.hash()) << ", maybe it has not metadata yet." <<std::endl;
}
}
}
2010-10-17 18:46:01 +04:00
void QBtSession::setMaxConnectionsPerTorrent(int max) {
2010-12-02 20:10:34 +03:00
qDebug() << Q_FUNC_INFO << max;
// Apply this to all session torrents
std::vector<torrent_handle> handles = s->get_torrents();
2012-07-14 02:28:23 +04:00
std::vector<torrent_handle>::const_iterator it = handles.begin();
std::vector<torrent_handle>::const_iterator itend = handles.end();
for ( ; it != itend; ++it) {
2012-02-20 21:30:53 +04:00
if (!it->is_valid())
continue;
2010-07-18 16:53:16 +04:00
try {
it->set_max_connections(max);
} catch(std::exception) {}
}
}
2010-10-17 18:46:01 +04:00
void QBtSession::setMaxUploadsPerTorrent(int max) {
2010-12-02 20:10:34 +03:00
qDebug() << Q_FUNC_INFO << max;
// Apply this to all session torrents
std::vector<torrent_handle> handles = s->get_torrents();
2012-07-14 02:28:23 +04:00
std::vector<torrent_handle>::const_iterator it = handles.begin();
std::vector<torrent_handle>::const_iterator itend = handles.end();
for ( ; it != itend; ++it) {
2012-02-20 21:30:53 +04:00
if (!it->is_valid())
continue;
2010-07-18 16:53:16 +04:00
try {
it->set_max_uploads(max);
} catch(std::exception) {}
}
}
2010-10-17 18:46:01 +04:00
void QBtSession::enableUPnP(bool b) {
Preferences pref;
2012-02-20 21:30:53 +04:00
if (b) {
2014-01-01 02:10:41 +04:00
qDebug("Enabling UPnP / NAT-PMP");
#if LIBTORRENT_VERSION_NUM < 10000
2014-01-01 02:10:41 +04:00
m_upnp = s->start_upnp();
m_natpmp = s->start_natpmp();
#else
s->start_upnp();
s->start_natpmp();
#endif
// Use UPnP/NAT-PMP for Web UI too
2012-02-20 21:30:53 +04:00
if (pref.isWebUiEnabled() && pref.useUPnPForWebUIPort()) {
const qint16 port = pref.getWebUiPort();
#if LIBTORRENT_VERSION_NUM < 10000
m_upnp->add_mapping(upnp::tcp, port, port);
m_natpmp->add_mapping(natpmp::tcp, port, port);
2014-01-01 02:10:41 +04:00
#else
s->add_port_mapping(session::tcp, port, port);
#endif
}
} else {
2014-01-01 02:10:41 +04:00
qDebug("Disabling UPnP / NAT-PMP");
s->stop_upnp();
s->stop_natpmp();
#if LIBTORRENT_VERSION_NUM < 10000
2014-01-01 02:10:41 +04:00
m_upnp = 0;
m_natpmp = 0;
#endif
}
}
2010-10-17 18:46:01 +04:00
void QBtSession::enableLSD(bool b) {
2012-02-20 21:30:53 +04:00
if (b) {
if (!LSDEnabled) {
2010-12-05 19:36:02 +03:00
qDebug("Enabling Local Peer Discovery");
s->start_lsd();
LSDEnabled = true;
}
} else {
2012-02-20 21:30:53 +04:00
if (LSDEnabled) {
2010-12-05 19:36:02 +03:00
qDebug("Disabling Local Peer Discovery");
s->stop_lsd();
LSDEnabled = false;
}
}
}
2010-10-17 18:46:01 +04:00
void QBtSession::loadSessionState() {
const QString state_path = fsutils::cacheLocation()+"/"+QString::fromUtf8("ses_state");
2012-02-20 21:30:53 +04:00
if (!QFile::exists(state_path)) return;
if (QFile(state_path).size() == 0) {
2010-07-18 16:53:16 +04:00
// Remove empty invalid state file
fsutils::forceRemove(state_path);
return;
}
2011-01-23 22:31:48 +03:00
QFile state_file(state_path);
2012-02-20 21:30:53 +04:00
if (!state_file.open(QIODevice::ReadOnly)) return;
std::vector<char> in;
const qint64 content_size = state_file.bytesAvailable();
2012-02-20 21:30:53 +04:00
if (content_size <= 0) return;
2011-01-23 22:31:48 +03:00
in.resize(content_size);
state_file.read(&in[0], content_size);
2011-01-23 22:31:48 +03:00
// bdecode
lazy_entry e;
libtorrent::error_code ec;
2011-04-17 14:29:44 +04:00
lazy_bdecode(&in[0], &in[0] + in.size(), e, ec);
2013-10-23 23:16:23 +04:00
if (!ec)
2011-01-23 22:31:48 +03:00
s->load_state(e);
}
2010-10-17 18:46:01 +04:00
void QBtSession::saveSessionState() {
qDebug("Saving session state to disk...");
const QString state_path = fsutils::cacheLocation()+"/"+QString::fromUtf8("ses_state");
entry session_state;
s->save_state(session_state);
vector<char> out;
bencode(back_inserter(out), session_state);
QFile session_file(state_path);
if (!out.empty() && session_file.open(QIODevice::WriteOnly)) {
session_file.write(&out[0], out.size());
session_file.close();
}
}
// Enable DHT
2010-10-17 18:46:01 +04:00
bool QBtSession::enableDHT(bool b) {
2012-02-20 21:30:53 +04:00
if (b) {
if (!DHTEnabled) {
try {
2010-11-28 15:25:24 +03:00
qDebug() << "Starting DHT...";
Q_ASSERT(!s->is_dht_running());
s->start_dht();
s->add_dht_router(std::make_pair(std::string("router.bittorrent.com"), 6881));
s->add_dht_router(std::make_pair(std::string("router.utorrent.com"), 6881));
2010-07-18 16:53:16 +04:00
s->add_dht_router(std::make_pair(std::string("dht.transmissionbt.com"), 6881));
2011-10-16 12:30:50 +04:00
s->add_dht_router(std::make_pair(std::string("dht.aelitis.com"), 6881)); // Vuze
DHTEnabled = true;
qDebug("DHT enabled");
}catch(std::exception &e) {
qDebug("Could not enable DHT, reason: %s", e.what());
return false;
}
}
} else {
2012-02-20 21:30:53 +04:00
if (DHTEnabled) {
DHTEnabled = false;
s->stop_dht();
qDebug("DHT disabled");
}
}
return true;
}
2014-05-14 02:09:45 +04:00
qreal QBtSession::getRealRatio(const libtorrent::torrent_status &status) const {
libtorrent::size_type all_time_upload = status.all_time_upload;
libtorrent::size_type all_time_download = status.all_time_download;
libtorrent::size_type total_done = status.total_done;
if (all_time_download < total_done) {
// We have more data on disk than we downloaded
// either because the user imported the file
// or because of crash the download histroy was lost.
// Otherwise will get weird ratios
// eg when downloaded 1KB and uploaded 700MB of a
// 700MB torrent.
all_time_download = total_done;
}
2012-02-20 21:30:53 +04:00
if (all_time_download == 0) {
if (all_time_upload == 0)
2014-05-14 02:09:45 +04:00
return 0.0;
2011-02-24 20:36:20 +03:00
return MAX_RATIO+1;
}
qreal ratio = all_time_upload / (float) all_time_download;
Q_ASSERT(ratio >= 0.);
2012-02-20 21:30:53 +04:00
if (ratio > MAX_RATIO)
2011-02-24 20:36:20 +03:00
ratio = MAX_RATIO;
return ratio;
}
// Called periodically
2010-10-17 18:46:01 +04:00
void QBtSession::saveTempFastResumeData() {
2010-03-21 00:21:42 +03:00
std::vector<torrent_handle> torrents = s->get_torrents();
2012-07-14 02:28:23 +04:00
std::vector<torrent_handle>::iterator torrentIT = torrents.begin();
std::vector<torrent_handle>::iterator torrentITend = torrents.end();
for ( ; torrentIT != torrentITend; ++torrentIT) {
2010-03-21 00:21:42 +03:00
QTorrentHandle h = QTorrentHandle(*torrentIT);
2010-07-18 16:53:16 +04:00
try {
2012-02-20 21:30:53 +04:00
if (!h.is_valid() || !h.has_metadata() /*|| h.is_seed() || h.is_paused()*/) continue;
if (!h.need_save_resume_data()) continue;
if (h.state() == torrent_status::checking_files || h.state() == torrent_status::queued_for_checking || h.has_error()) continue;
2010-07-18 16:53:16 +04:00
qDebug("Saving fastresume data for %s", qPrintable(h.name()));
h.save_resume_data();
}catch(std::exception &e) {}
2010-03-21 00:21:42 +03:00
}
}
// Only save fast resume data for unfinished and unpaused torrents (Optimization)
// Called on exit
2010-10-17 18:46:01 +04:00
void QBtSession::saveFastResumeData() {
qDebug("Saving fast resume data...");
2008-11-02 02:58:53 +03:00
// Stop listening for alerts
2010-03-21 00:21:42 +03:00
resumeDataTimer.stop();
2008-11-02 02:58:53 +03:00
int num_resume_data = 0;
// Pause session
2008-11-02 02:58:53 +03:00
s->pause();
std::vector<torrent_handle> torrents = s->get_torrents();
2012-07-14 02:28:23 +04:00
std::vector<torrent_handle>::iterator torrentIT = torrents.begin();
std::vector<torrent_handle>::iterator torrentITend = torrents.end();
for ( ; torrentIT != torrentITend; ++torrentIT) {
QTorrentHandle h = QTorrentHandle(*torrentIT);
if (!h.is_valid())
continue;
try {
2012-02-20 21:30:53 +04:00
if (isQueueingEnabled())
TorrentPersistentData::savePriority(h);
if (!h.has_metadata())
continue;
// Actually with should save fast resume data for paused files too
2012-02-20 21:30:53 +04:00
//if (h.is_paused()) continue;
if (h.state() == torrent_status::checking_files || h.state() == torrent_status::queued_for_checking || h.has_error()) continue;
h.save_resume_data();
++num_resume_data;
} catch(libtorrent::invalid_handle&) {}
2008-11-02 02:58:53 +03:00
}
while (num_resume_data > 0) {
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
std::deque<alert*> alerts;
m_alertDispatcher->getPendingAlerts(alerts);
for (std::deque<alert*>::const_iterator i = alerts.begin(), end = alerts.end(); i != end; ++i)
{
alert const* a = *i;
// Saving fastresume data can fail
save_resume_data_failed_alert const* rda = dynamic_cast<save_resume_data_failed_alert const*>(a);
if (rda) {
--num_resume_data;
try {
// Remove torrent from session
if (rda->handle.is_valid())
s->remove_torrent(rda->handle);
}catch(libtorrent::libtorrent_exception) {}
continue;
}
save_resume_data_alert const* rd = dynamic_cast<save_resume_data_alert const*>(a);
if (!rd) {
continue;
}
// Saving fast resume data was successful
2008-11-02 02:58:53 +03:00
--num_resume_data;
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
if (!rd->resume_data) continue;
QDir torrentBackup(fsutils::BTBackupLocation());
const QTorrentHandle h(rd->handle);
if (!h.is_valid()) continue;
try {
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
// Remove old fastresume file if it exists
backupPersistentData(h.hash(), rd->resume_data);
vector<char> out;
bencode(back_inserter(out), *rd->resume_data);
const QString filepath = torrentBackup.absoluteFilePath(h.hash()+".fastresume");
QFile resume_file(filepath);
if (resume_file.exists())
fsutils::forceRemove(filepath);
if (!out.empty() && resume_file.open(QIODevice::WriteOnly)) {
resume_file.write(&out[0], out.size());
resume_file.close();
}
// Remove torrent from session
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
s->remove_torrent(rd->handle);
} catch(libtorrent::invalid_handle&) {}
delete a;
}
}
}
2010-01-03 01:20:37 +03:00
#ifdef DISABLE_GUI
2010-10-17 18:46:01 +04:00
void QBtSession::addConsoleMessage(QString msg, QString) {
emit newConsoleMessage(QDateTime::currentDateTime().toString("dd/MM/yyyy hh:mm:ss") + " - " + msg);
2010-01-03 01:20:37 +03:00
#else
2010-10-17 18:46:01 +04:00
void QBtSession::addConsoleMessage(QString msg, QColor color) {
2012-02-20 21:30:53 +04:00
if (consoleMessages.size() > MAX_LOG_MESSAGES) {
consoleMessages.removeFirst();
}
msg = "<font color='grey'>"+ QDateTime::currentDateTime().toString(QString::fromUtf8("dd/MM/yyyy hh:mm:ss")) + "</font> - <font color='" + color.name() + "'>" + msg + "</font>";
consoleMessages.append(msg);
emit newConsoleMessage(msg);
2010-01-03 01:20:37 +03:00
#endif
}
2010-10-17 18:46:01 +04:00
void QBtSession::addPeerBanMessage(QString ip, bool from_ipfilter) {
2012-02-20 21:30:53 +04:00
if (peerBanMessages.size() > MAX_LOG_MESSAGES) {
peerBanMessages.removeFirst();
}
QString msg;
2012-02-20 21:30:53 +04:00
if (from_ipfilter)
msg = "<font color='grey'>" + QDateTime::currentDateTime().toString(QString::fromUtf8("dd/MM/yyyy hh:mm:ss")) + "</font> - " + tr("<font color='red'>%1</font> was blocked", "x.y.z.w was blocked").arg(ip);
else
msg = "<font color='grey'>" + QDateTime::currentDateTime().toString(QString::fromUtf8("dd/MM/yyyy hh:mm:ss")) + "</font> - " + tr("<font color='red'>%1</font> was banned", "x.y.z.w was banned").arg(ip);
peerBanMessages.append(msg);
emit newBanMessage(msg);
}
2012-02-20 23:32:58 +04:00
bool QBtSession::isFilePreviewPossible(const QString &hash) const {
// See if there are supported files in the torrent
const QTorrentHandle h = getTorrentHandle(hash);
2012-02-20 21:30:53 +04:00
if (!h.is_valid() || !h.has_metadata()) {
return false;
}
const unsigned int nbFiles = h.num_files();
2012-02-20 21:30:53 +04:00
for (unsigned int i=0; i<nbFiles; ++i) {
QString filename = h.filename_at(i);
if (filename.endsWith(".!qB"))
filename.chop(4);
const QString extension = fsutils::fileExtension(filename);
2012-02-20 21:30:53 +04:00
if (misc::isPreviewable(extension))
return true;
}
return false;
}
2010-10-17 18:46:01 +04:00
void QBtSession::addTorrentsFromScanFolder(QStringList &pathList) {
2012-02-20 21:30:53 +04:00
foreach (const QString &file, pathList) {
qDebug("File %s added", qPrintable(file));
if (file.endsWith(".magnet")) {
QFile f(file);
if (!f.open(QIODevice::ReadOnly)) {
qDebug("Failed to open magnet file: %s", qPrintable(f.errorString()));
} else {
const QString link = QString::fromLocal8Bit(f.readAll());
addMagnetUri(link, false, true, file);
f.remove();
}
continue;
}
try {
torrent_info t(fsutils::toNativePath(file).toUtf8().constData());
2012-02-20 21:30:53 +04:00
if (t.is_valid())
addTorrent(file, true);
} catch(std::exception&) {
qDebug("Ignoring incomplete torrent file: %s", qPrintable(file));
}
}
}
void QBtSession::setDefaultSavePath(const QString &savepath) {
if (savepath.isEmpty())
return;
2012-12-01 17:40:18 +04:00
defaultSavePath = fsutils::fromNativePath(savepath);
}
void QBtSession::setDefaultTempPath(const QString &temppath) {
2012-12-01 17:40:18 +04:00
if (QDir(defaultTempPath) == QDir(temppath))
return;
2012-12-01 17:40:18 +04:00
2012-02-20 21:30:53 +04:00
if (temppath.isEmpty()) {
// Disabling temp dir
// Moving all torrents to their destination folder
std::vector<torrent_handle> torrents = s->get_torrents();
2012-07-14 02:28:23 +04:00
std::vector<torrent_handle>::iterator torrentIT = torrents.begin();
std::vector<torrent_handle>::iterator torrentITend = torrents.end();
for ( ; torrentIT != torrentITend; ++torrentIT) {
QTorrentHandle h = QTorrentHandle(*torrentIT);
2012-02-20 21:30:53 +04:00
if (!h.is_valid()) continue;
h.move_storage(getSavePath(h.hash()));
}
} else {
qDebug("Enabling default temp path...");
// Moving all downloading torrents to temporary save path
std::vector<torrent_handle> torrents = s->get_torrents();
2012-07-14 02:28:23 +04:00
std::vector<torrent_handle>::iterator torrentIT = torrents.begin();
std::vector<torrent_handle>::iterator torrentITend = torrents.end();
for ( ; torrentIT != torrentITend; ++torrentIT) {
QTorrentHandle h = QTorrentHandle(*torrentIT);
2012-02-20 21:30:53 +04:00
if (!h.is_valid()) continue;
if (!h.is_seed()) {
qDebug("Moving torrent to its temp save path: %s", qPrintable(temppath));
h.move_storage(temppath);
}
}
}
defaultTempPath = fsutils::fromNativePath(temppath);
}
2010-11-24 23:31:14 +03:00
void QBtSession::appendqBextensionToTorrent(const QTorrentHandle &h, bool append) {
2012-02-20 21:30:53 +04:00
if (!h.is_valid() || !h.has_metadata()) return;
std::vector<size_type> fp;
h.file_progress(fp);
2012-02-20 21:30:53 +04:00
for (int i=0; i<h.num_files(); ++i) {
if (append) {
const qulonglong file_size = h.filesize_at(i);
2012-02-20 21:30:53 +04:00
if (file_size > 0 && (fp[i]/(double)file_size) < 1.) {
const QString name = h.filepath_at(i);
2012-02-20 21:30:53 +04:00
if (!name.endsWith(".!qB")) {
const QString new_name = name+".!qB";
qDebug("Renaming %s to %s", qPrintable(name), qPrintable(new_name));
h.rename_file(i, new_name);
}
}
} else {
QString name = h.filepath_at(i);
2012-02-20 21:30:53 +04:00
if (name.endsWith(".!qB")) {
const QString old_name = name;
name.chop(4);
qDebug("Renaming %s to %s", qPrintable(old_name), qPrintable(name));
h.rename_file(i, name);
}
}
}
}
2010-11-24 23:31:14 +03:00
void QBtSession::changeLabelInTorrentSavePath(const QTorrentHandle &h, QString old_label, QString new_label) {
2012-02-20 21:30:53 +04:00
if (!h.is_valid()) return;
if (!appendLabelToSavePath) return;
QString old_save_path = fsutils::fromNativePath(TorrentPersistentData::getSavePath(h.hash()));
2012-02-20 21:30:53 +04:00
if (!old_save_path.startsWith(defaultSavePath)) return;
QString new_save_path = fsutils::updateLabelInSavePath(defaultSavePath, old_save_path, old_label, new_label);
2012-02-20 21:30:53 +04:00
if (new_save_path != old_save_path) {
// Move storage
qDebug("Moving storage to %s", qPrintable(new_save_path));
QDir().mkpath(new_save_path);
h.move_storage(new_save_path);
}
}
2010-11-24 23:31:14 +03:00
void QBtSession::appendLabelToTorrentSavePath(const QTorrentHandle& h) {
2012-02-20 21:30:53 +04:00
if (!h.is_valid()) return;
const QString label = TorrentPersistentData::getLabel(h.hash());
2012-02-20 21:30:53 +04:00
if (label.isEmpty()) return;
// Current save path
QString old_save_path = fsutils::fromNativePath(TorrentPersistentData::getSavePath(h.hash()));
QString new_save_path = fsutils::updateLabelInSavePath(defaultSavePath, old_save_path, "", label);
2012-02-20 21:30:53 +04:00
if (old_save_path != new_save_path) {
// Move storage
QDir().mkpath(new_save_path);
h.move_storage(new_save_path);
}
}
2010-10-17 18:46:01 +04:00
void QBtSession::setAppendLabelToSavePath(bool append) {
2012-02-20 21:30:53 +04:00
if (appendLabelToSavePath != append) {
appendLabelToSavePath = !appendLabelToSavePath;
2012-02-20 21:30:53 +04:00
if (appendLabelToSavePath) {
// Move torrents storage to sub folder with label name
2010-08-20 13:02:27 +04:00
std::vector<torrent_handle> torrents = s->get_torrents();
2012-07-14 02:28:23 +04:00
std::vector<torrent_handle>::iterator torrentIT = torrents.begin();
std::vector<torrent_handle>::iterator torrentITend = torrents.end();
for ( ; torrentIT != torrentITend; ++torrentIT) {
QTorrentHandle h = QTorrentHandle(*torrentIT);
appendLabelToTorrentSavePath(h);
}
}
}
}
2010-10-17 18:46:01 +04:00
void QBtSession::setAppendqBExtension(bool append) {
2012-02-20 21:30:53 +04:00
if (appendqBExtension != append) {
appendqBExtension = !appendqBExtension;
// append or remove .!qB extension for incomplete files
std::vector<torrent_handle> torrents = s->get_torrents();
2012-07-14 02:28:23 +04:00
std::vector<torrent_handle>::iterator torrentIT = torrents.begin();
std::vector<torrent_handle>::iterator torrentITend = torrents.end();
for ( ; torrentIT != torrentITend; ++torrentIT) {
QTorrentHandle h = QTorrentHandle(*torrentIT);
appendqBextensionToTorrent(h, appendqBExtension);
}
}
}
// Set the ports range in which is chosen the port the Bittorrent
// session will listen to
2010-10-17 18:46:01 +04:00
void QBtSession::setListeningPort(int port) {
2011-05-01 16:00:23 +04:00
qDebug() << Q_FUNC_INFO << port;
Preferences pref;
std::pair<int,int> ports(port, port);
libtorrent::error_code ec;
const QString iface_name = pref.getNetworkInterface();
2012-02-20 21:30:53 +04:00
if (iface_name.isEmpty()) {
addConsoleMessage(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)), "blue");
s->listen_on(ports, ec, 0, session::listen_no_system_port);
return;
}
// Attempt to listen on provided interface
const QNetworkInterface network_iface = QNetworkInterface::interfaceFromName(iface_name);
2012-02-20 21:30:53 +04:00
if (!network_iface.isValid()) {
qDebug("Invalid network interface: %s", qPrintable(iface_name));
addConsoleMessage(tr("The network interface defined is invalid: %1").arg(iface_name), "red");
return;
}
QString ip;
qDebug("This network interface has %d IP addresses", network_iface.addressEntries().size());
2012-02-20 21:30:53 +04:00
foreach (const QNetworkAddressEntry &entry, network_iface.addressEntries()) {
qDebug("Trying to listen on IP %s (%s)", qPrintable(entry.ip().toString()), qPrintable(iface_name));
s->listen_on(ports, ec, entry.ip().toString().toLatin1().constData(), session::listen_no_system_port);
2012-02-20 21:30:53 +04:00
if (!ec) {
ip = entry.ip().toString();
addConsoleMessage(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(QString::number(port)), "blue");
break;
}
}
}
// Set download rate limit
// -1 to disable
2010-10-17 18:46:01 +04:00
void QBtSession::setDownloadRateLimit(long rate) {
2010-12-02 20:02:13 +03:00
qDebug() << Q_FUNC_INFO << rate;
Q_ASSERT(rate == -1 || rate >= 0);
2011-04-17 14:29:44 +04:00
session_settings settings = s->settings();
settings.download_rate_limit = rate;
s->set_settings(settings);
}
// Set upload rate limit
// -1 to disable
2010-10-17 18:46:01 +04:00
void QBtSession::setUploadRateLimit(long rate) {
qDebug() << Q_FUNC_INFO << rate;
Q_ASSERT(rate == -1 || rate >= 0);
2011-04-17 14:29:44 +04:00
session_settings settings = s->settings();
settings.upload_rate_limit = rate;
s->set_settings(settings);
}
// Torrents will a ratio superior to the given value will
// be automatically deleted
void QBtSession::setGlobalMaxRatio(qreal ratio) {
2012-02-20 21:30:53 +04:00
if (ratio < 0) ratio = -1.;
if (global_ratio_limit != ratio) {
global_ratio_limit = ratio;
qDebug("* Set global deleteRatio to %.1f", global_ratio_limit);
updateRatioTimer();
}
}
void QBtSession::setMaxRatioPerTorrent(const QString &hash, qreal ratio)
{
if (ratio < 0)
ratio = -1;
if (ratio > MAX_RATIO)
ratio = MAX_RATIO;
qDebug("* Set individual max ratio for torrent %s to %.1f.",
qPrintable(hash), ratio);
TorrentPersistentData::setRatioLimit(hash, ratio);
updateRatioTimer();
}
void QBtSession::removeRatioPerTorrent(const QString &hash)
{
qDebug("* Remove individual max ratio for torrent %s.", qPrintable(hash));
TorrentPersistentData::setRatioLimit(hash, TorrentPersistentData::USE_GLOBAL_RATIO);
updateRatioTimer();
}
qreal QBtSession::getMaxRatioPerTorrent(const QString &hash, bool *usesGlobalRatio) const
{
qreal ratio_limit = TorrentPersistentData::getRatioLimit(hash);
2012-02-20 21:30:53 +04:00
if (ratio_limit == TorrentPersistentData::USE_GLOBAL_RATIO) {
ratio_limit = global_ratio_limit;
*usesGlobalRatio = true;
} else {
*usesGlobalRatio = false;
}
return ratio_limit;
}
void QBtSession::updateRatioTimer()
{
if (global_ratio_limit == -1 && !TorrentPersistentData::hasPerTorrentRatioLimit()) {
if (BigRatioTimer->isActive())
BigRatioTimer->stop();
} else if (!BigRatioTimer->isActive()) {
BigRatioTimer->start();
}
}
#if LIBTORRENT_VERSION_NUM < 10000
// Set DHT port (>= 1 or 0 if same as BT)
2010-10-17 18:46:01 +04:00
void QBtSession::setDHTPort(int dht_port) {
2012-02-20 21:30:53 +04:00
if (dht_port >= 0) {
if (dht_port == current_dht_port) return;
struct dht_settings DHTSettings;
DHTSettings.service_port = dht_port;
s->set_dht_settings(DHTSettings);
current_dht_port = dht_port;
qDebug("Set DHT Port to %d", dht_port);
}
}
#endif
// Enable IP Filtering
void QBtSession::enableIPFilter(const QString &filter_path, bool force) {
qDebug("Enabling IPFiler");
2012-02-20 21:30:53 +04:00
if (!filterParser) {
filterParser = new FilterParserThread(this, s);
connect(filterParser.data(), SIGNAL(IPFilterParsed(int)), SLOT(handleIPFilterParsed(int)));
connect(filterParser.data(), SIGNAL(IPFilterError()), SLOT(handleIPFilterError()));
}
if (filterPath.isEmpty() || filterPath != fsutils::fromNativePath(filter_path) || force) {
filterPath = fsutils::fromNativePath(filter_path);
filterParser->processFilterFile(fsutils::fromNativePath(filter_path));
}
}
// Disable IP Filtering
2010-10-17 18:46:01 +04:00
void QBtSession::disableIPFilter() {
qDebug("Disabling IPFilter");
s->set_ip_filter(ip_filter());
2012-02-20 21:30:53 +04:00
if (filterParser) {
disconnect(filterParser.data(), 0, this, 0);
delete filterParser;
}
filterPath = "";
}
// Set BT session settings (user_agent)
2010-10-17 18:46:01 +04:00
void QBtSession::setSessionSettings(const session_settings &sessionSettings) {
qDebug("Set session settings");
s->set_settings(sessionSettings);
}
// Set Proxy
void QBtSession::setProxySettings(proxy_settings proxySettings) {
qDebug() << Q_FUNC_INFO;
proxySettings.proxy_peer_connections = Preferences().proxyPeerConnections();
s->set_proxy(proxySettings);
// Define environment variable
QString proxy_str;
switch(proxySettings.type) {
case proxy_settings::http_pw:
proxy_str = "http://"+misc::toQString(proxySettings.username)+":"+misc::toQString(proxySettings.password)+"@"+misc::toQString(proxySettings.hostname)+":"+QString::number(proxySettings.port);
break;
case proxy_settings::http:
proxy_str = "http://"+misc::toQString(proxySettings.hostname)+":"+QString::number(proxySettings.port);
break;
case proxy_settings::socks5:
proxy_str = misc::toQString(proxySettings.hostname)+":"+QString::number(proxySettings.port);
break;
case proxy_settings::socks5_pw:
proxy_str = misc::toQString(proxySettings.username)+":"+misc::toQString(proxySettings.password)+"@"+misc::toQString(proxySettings.hostname)+":"+QString::number(proxySettings.port);
break;
default:
qDebug("Disabling HTTP communications proxy");
qputenv("http_proxy", QByteArray());
qputenv("sock_proxy", QByteArray());
return;
}
// We need this for urllib in search engine plugins
qDebug("HTTP communications proxy string: %s", qPrintable(proxy_str));
2012-02-20 21:30:53 +04:00
if (proxySettings.type == proxy_settings::socks5 || proxySettings.type == proxy_settings::socks5_pw)
qputenv("sock_proxy", proxy_str.toLocal8Bit());
else
qputenv("http_proxy", proxy_str.toLocal8Bit());
}
2010-10-17 18:46:01 +04:00
void QBtSession::recursiveTorrentDownload(const QTorrentHandle &h) {
try {
2012-02-20 21:30:53 +04:00
for (int i=0; i<h.num_files(); ++i) {
2011-04-17 14:29:44 +04:00
const QString torrent_relpath = h.filepath_at(i);
2012-02-20 21:30:53 +04:00
if (torrent_relpath.endsWith(".torrent")) {
addConsoleMessage(tr("Recursive download of file %1 embedded in torrent %2", "Recursive download of test.torrent embedded in torrent test2").arg(fsutils::toNativePath(torrent_relpath)).arg(h.name()));
const QString torrent_fullpath = h.save_path()+"/"+torrent_relpath;
boost::intrusive_ptr<torrent_info> t = new torrent_info(fsutils::toNativePath(torrent_fullpath).toUtf8().constData());
const QString sub_hash = misc::toQString(t->info_hash());
// Passing the save path along to the sub torrent file
TorrentTempData::setSavePath(sub_hash, h.save_path());
addTorrent(torrent_fullpath);
}
}
} catch(std::exception&) {
qDebug("Caught error loading torrent");
}
}
void QBtSession::autoRunExternalProgram(const QTorrentHandle &h) {
2012-02-20 21:30:53 +04:00
if (!h.is_valid()) return;
QString program = Preferences().getAutoRunProgram().trimmed();
2012-02-20 21:30:53 +04:00
if (program.isEmpty()) return;
// Replace %f by torrent path
QString torrent_path;
2012-02-20 21:30:53 +04:00
if (h.num_files() == 1)
torrent_path = h.firstFileSavePath();
else
torrent_path = h.save_path();
program.replace("%f", torrent_path);
// Replace %n by torrent name
program.replace("%n", h.name());
QProcess::startDetached(program);
}
2011-02-26 22:56:15 +03:00
void QBtSession::sendNotificationEmail(const QTorrentHandle &h) {
2014-05-14 02:09:45 +04:00
libtorrent::torrent_status status = h.status(torrent_handle::query_accurate_download_counters);
// Prepare mail content
QString content = tr("Torrent name: %1").arg(h.name()) + "\n";
2014-05-14 02:09:45 +04:00
content += tr("Torrent size: %1").arg(misc::friendlyUnit(status.total_wanted)) + "\n";
content += tr("Save path: %1").arg(TorrentPersistentData::getSavePath(h.hash())) + "\n\n";
2014-05-14 02:09:45 +04:00
content += tr("The torrent was downloaded in %1.", "The torrent was downloaded in 1 hour and 20 seconds").arg(misc::userFriendlyDuration(status.active_time)) + "\n\n\n";
content += tr("Thank you for using qBittorrent.") + "\n";
// Send the notification email
Smtp *sender = new Smtp(this);
sender->sendMail("notification@qbittorrent.org", Preferences().getMailNotificationEmail(), tr("[qBittorrent] %1 has finished downloading").arg(h.name()), content);
}
// Read alerts sent by the Bittorrent session
2010-10-17 18:46:01 +04:00
void QBtSession::readAlerts() {
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
typedef std::deque<alert*> alerts_t;
alerts_t alerts;
m_alertDispatcher->getPendingAlertsNoWait(alerts);
for (alerts_t::const_iterator i = alerts.begin(), end = alerts.end(); i != end; ++i) {
handleAlert(*i);
delete *i;
}
}
void QBtSession::handleAlert(libtorrent::alert* a) {
try {
if (torrent_finished_alert* p = dynamic_cast<torrent_finished_alert*>(a)) {
QTorrentHandle h(p->handle);
if (h.is_valid()) {
const QString hash = h.hash();
qDebug("Got a torrent finished alert for %s", qPrintable(h.name()));
// Remove .!qB extension if necessary
if (appendqBExtension)
appendqBextensionToTorrent(h, false);
const bool was_already_seeded = TorrentPersistentData::isSeed(hash);
qDebug("Was already seeded: %d", was_already_seeded);
if (!was_already_seeded) {
h.save_resume_data();
qDebug("Checking if the torrent contains torrent files to download");
// Check if there are torrent files inside
for (int i=0; i<h.num_files(); ++i) {
const QString torrent_relpath = h.filepath_at(i);
qDebug() << "File path:" << torrent_relpath;
if (torrent_relpath.endsWith(".torrent", Qt::CaseInsensitive)) {
qDebug("Found possible recursive torrent download.");
const QString torrent_fullpath = h.save_path()+"/"+torrent_relpath;
qDebug("Full subtorrent path is %s", qPrintable(torrent_fullpath));
try {
boost::intrusive_ptr<torrent_info> t = new torrent_info(fsutils::toNativePath(torrent_fullpath).toUtf8().constData());
if (t->is_valid()) {
qDebug("emitting recursiveTorrentDownloadPossible()");
emit recursiveTorrentDownloadPossible(h);
break;
2010-02-04 22:16:04 +03:00
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
} catch(std::exception&) {
qDebug("Caught error loading torrent");
addConsoleMessage(tr("Unable to decode %1 torrent file.").arg(fsutils::toNativePath(torrent_fullpath)), QString::fromUtf8("red"));
}
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
}
// Move to download directory if necessary
if (!defaultTempPath.isEmpty()) {
// Check if directory is different
const QDir current_dir(h.save_path());
const QDir save_dir(getSavePath(hash));
if (current_dir != save_dir) {
qDebug("Moving torrent from the temp folder");
h.move_storage(save_dir.absolutePath());
2010-10-02 16:52:06 +04:00
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
}
// Remember finished state
qDebug("Saving seed status");
TorrentPersistentData::saveSeedStatus(h);
// Recheck if the user asked to
Preferences pref;
if (pref.recheckTorrentsOnCompletion()) {
h.force_recheck();
}
qDebug("Emitting finishedTorrent() signal");
emit finishedTorrent(h);
qDebug("Received finished alert for %s", qPrintable(h.name()));
#ifndef DISABLE_GUI
bool will_shutdown = (pref.shutdownWhenDownloadsComplete() ||
pref.shutdownqBTWhenDownloadsComplete() ||
pref.suspendWhenDownloadsComplete())
&& !hasDownloadingTorrents();
#else
bool will_shutdown = false;
#endif
// AutoRun program
if (pref.isAutoRunEnabled())
autoRunExternalProgram(h);
// Move .torrent file to another folder
if (pref.isFinishedTorrentExportEnabled())
exportTorrentFile(h, FinishedTorrentExportFolder);
// Mail notification
if (pref.isMailNotificationEnabled())
sendNotificationEmail(h);
#ifndef DISABLE_GUI
// Auto-Shutdown
if (will_shutdown) {
bool suspend = pref.suspendWhenDownloadsComplete();
bool shutdown = pref.shutdownWhenDownloadsComplete();
// Confirm shutdown
QString confirm_msg;
if (suspend) {
confirm_msg = tr("The computer will now go to sleep mode unless you cancel within the next 15 seconds...");
} else if (shutdown) {
confirm_msg = tr("The computer will now be switched off unless you cancel within the next 15 seconds...");
} else {
confirm_msg = tr("qBittorrent will now exit unless you cancel within the next 15 seconds...");
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
if (!ShutdownConfirmDlg::askForConfirmation(confirm_msg))
return;
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
// Actually shut down
if (suspend || shutdown) {
qDebug("Preparing for auto-shutdown because all downloads are complete!");
// Disabling it for next time
pref.setShutdownWhenDownloadsComplete(false);
pref.setSuspendWhenDownloadsComplete(false);
// Make sure preferences are synced before exiting
if (suspend)
m_shutdownAct = SUSPEND_COMPUTER;
else
m_shutdownAct = SHUTDOWN_COMPUTER;
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
qDebug("Exiting the application");
qApp->exit();
return;
2012-08-10 11:04:28 +04:00
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
#endif // DISABLE_GUI
}
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
}
else if (save_resume_data_alert* p = dynamic_cast<save_resume_data_alert*>(a)) {
const QDir torrentBackup(fsutils::BTBackupLocation());
const QTorrentHandle h(p->handle);
if (h.is_valid() && p->resume_data) {
const QString filepath = torrentBackup.absoluteFilePath(h.hash()+".fastresume");
QFile resume_file(filepath);
if (resume_file.exists())
fsutils::forceRemove(filepath);
qDebug("Saving fastresume data in %s", qPrintable(filepath));
backupPersistentData(h.hash(), p->resume_data);
vector<char> out;
bencode(back_inserter(out), *p->resume_data);
if (!out.empty() && resume_file.open(QIODevice::WriteOnly)) {
resume_file.write(&out[0], out.size());
resume_file.close();
2010-06-04 00:08:19 +04:00
}
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
}
else if (file_renamed_alert* p = dynamic_cast<file_renamed_alert*>(a)) {
QTorrentHandle h(p->handle);
if (h.is_valid()) {
if (h.num_files() > 1) {
// Check if folders were renamed
QStringList old_path_parts = h.orig_filepath_at(p->index).split("/");
old_path_parts.removeLast();
QString old_path = old_path_parts.join("/");
QStringList new_path_parts = fsutils::fromNativePath(misc::toQStringU(p->name)).split("/");
new_path_parts.removeLast();
if (!new_path_parts.isEmpty() && old_path != new_path_parts.join("/")) {
qDebug("Old_path(%s) != new_path(%s)", qPrintable(old_path), qPrintable(new_path_parts.join("/")));
old_path = h.save_path()+"/"+old_path;
qDebug("Detected folder renaming, attempt to delete old folder: %s", qPrintable(old_path));
QDir().rmpath(old_path);
2012-08-10 11:04:28 +04:00
}
} else {
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
// Single-file torrent
// Renaming a file corresponds to changing the save path
emit savePathChanged(h);
}
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
}
else if (torrent_deleted_alert* p = dynamic_cast<torrent_deleted_alert*>(a)) {
qDebug("A torrent was deleted from the hard disk, attempting to remove the root folder too...");
QString hash = misc::toQString(p->info_hash);
if (!hash.isEmpty()) {
if (savePathsToRemove.contains(hash)) {
const QString dirpath = savePathsToRemove.take(hash);
qDebug() << "Removing save path: " << dirpath << "...";
bool ok = fsutils::smartRemoveEmptyFolderTree(dirpath);
Q_UNUSED(ok);
qDebug() << "Folder was removed: " << ok;
}
} else {
// Fallback
qDebug() << "hash is empty, use fallback to remove save path";
foreach (const QString& key, savePathsToRemove.keys()) {
// Attempt to delete
if (QDir().rmdir(savePathsToRemove[key])) {
savePathsToRemove.remove(key);
2012-08-10 11:04:28 +04:00
}
}
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
}
else if (storage_moved_alert* p = dynamic_cast<storage_moved_alert*>(a)) {
QTorrentHandle h(p->handle);
if (h.is_valid()) {
// Attempt to remove old folder if empty
const QString old_save_path = fsutils::fromNativePath(TorrentPersistentData::getPreviousPath(h.hash()));
const QString new_save_path = fsutils::fromNativePath(misc::toQStringU(p->path.c_str()));
qDebug("Torrent moved from %s to %s", qPrintable(old_save_path), qPrintable(new_save_path));
QDir old_save_dir(old_save_path);
if (old_save_dir != QDir(defaultSavePath) && old_save_dir != QDir(defaultTempPath)) {
qDebug("Attempting to remove %s", qPrintable(old_save_path));
QDir().rmpath(old_save_path);
}
if (defaultTempPath.isEmpty() || !new_save_path.startsWith(defaultTempPath)) {
qDebug("Storage has been moved, updating save path to %s", qPrintable(new_save_path));
TorrentPersistentData::saveSavePath(h.hash(), new_save_path);
2012-08-10 11:04:28 +04:00
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
emit savePathChanged(h);
//h.force_recheck();
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
}
else if (metadata_received_alert* p = dynamic_cast<metadata_received_alert*>(a)) {
QTorrentHandle h(p->handle);
Preferences pref;
if (h.is_valid()) {
QString hash(h.hash());
if (HiddenData::hasData(hash)) {
HiddenData::gotMetadata(hash);
if (pref.isQueueingSystemEnabled()) {
//Internally decrease the queue limits to ensure that that other queued items aren't started
libtorrent::session_settings sessionSettings(s->settings());
int max_downloading = pref.getMaxActiveDownloads();
int max_active = pref.getMaxActiveTorrents();
if (max_downloading > -1)
sessionSettings.active_downloads = max_downloading + HiddenData::getDownloadingSize();
else
sessionSettings.active_downloads = max_downloading;
if (max_active > -1)
sessionSettings.active_limit = max_active + HiddenData::getDownloadingSize();
else
sessionSettings.active_limit = max_active;
s->set_settings(sessionSettings);
2012-08-10 11:04:28 +04:00
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
h.pause();
}
qDebug("Received metadata for %s", qPrintable(h.hash()));
// Save metadata
const QDir torrentBackup(fsutils::BTBackupLocation());
if (!QFile::exists(torrentBackup.absoluteFilePath(h.hash()+QString(".torrent"))))
h.save_torrent_file(torrentBackup.absoluteFilePath(h.hash()+QString(".torrent")));
// Copy the torrent file to the export folder
if (m_torrentExportEnabled)
exportTorrentFile(h);
// Append .!qB to incomplete files
if (appendqBExtension)
appendqBextensionToTorrent(h, true);
if (!HiddenData::hasData(hash))
emit metadataReceived(h);
else
emit metadataReceivedHidden(h);
if (h.is_paused() && !HiddenData::hasData(hash)) {
// XXX: Unfortunately libtorrent-rasterbar does not send a torrent_paused_alert
// and the torrent can be paused when metadata is received
emit pausedTorrent(h);
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
}
else if (file_error_alert* p = dynamic_cast<file_error_alert*>(a)) {
QTorrentHandle h(p->handle);
if (h.is_valid()) {
h.pause();
std::cerr << "File Error: " << p->message().c_str() << std::endl;
addConsoleMessage(tr("An I/O error occurred, '%1' paused.").arg(h.name()));
addConsoleMessage(tr("Reason: %1").arg(misc::toQStringU(p->message())));
if (h.is_valid()) {
emit fullDiskError(h, misc::toQStringU(p->message()));
//h.pause();
emit pausedTorrent(h);
}
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
}
else if (file_completed_alert* p = dynamic_cast<file_completed_alert*>(a)) {
QTorrentHandle h(p->handle);
qDebug("A file completed download in torrent %s", qPrintable(h.name()));
if (appendqBExtension) {
qDebug("appendqBTExtension is true");
QString name = h.filepath_at(p->index);
if (name.endsWith(".!qB")) {
const QString old_name = name;
name.chop(4);
qDebug("Renaming %s to %s", qPrintable(old_name), qPrintable(name));
h.rename_file(p->index, name);
2012-08-10 11:04:28 +04:00
}
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
}
else if (torrent_paused_alert* p = dynamic_cast<torrent_paused_alert*>(a)) {
if (p->handle.is_valid()) {
QTorrentHandle h(p->handle);
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
if (!HiddenData::hasData(h.hash())) {
if (!h.has_error())
h.save_resume_data();
emit pausedTorrent(h);
2012-08-10 11:04:28 +04:00
}
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
}
else if (tracker_error_alert* p = dynamic_cast<tracker_error_alert*>(a)) {
// Level: fatal
QTorrentHandle h(p->handle);
if (h.is_valid()) {
// Authentication
if (p->status_code != 401) {
qDebug("Received a tracker error for %s: %s", p->url.c_str(), p->msg.c_str());
2010-07-23 18:05:53 +04:00
const QString tracker_url = misc::toQString(p->url);
QHash<QString, TrackerInfos> trackers_data = trackersInfos.value(h.hash(), QHash<QString, TrackerInfos>());
TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url));
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
data.last_message = misc::toQStringU(p->msg);
trackers_data.insert(tracker_url, data);
trackersInfos[h.hash()] = trackers_data;
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
} else {
emit trackerAuthenticationRequired(h);
}
2009-11-19 11:14:04 +03:00
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
}
else if (tracker_reply_alert* p = dynamic_cast<tracker_reply_alert*>(a)) {
const QTorrentHandle h(p->handle);
if (h.is_valid()) {
qDebug("Received a tracker reply from %s (Num_peers=%d)", p->url.c_str(), p->num_peers);
// Connection was successful now. Remove possible old errors
QHash<QString, TrackerInfos> trackers_data = trackersInfos.value(h.hash(), QHash<QString, TrackerInfos>());
const QString tracker_url = misc::toQString(p->url);
TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url));
data.last_message = ""; // Reset error/warning message
data.num_peers = p->num_peers;
trackers_data.insert(tracker_url, data);
trackersInfos[h.hash()] = trackers_data;
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
}
else if (tracker_warning_alert* p = dynamic_cast<tracker_warning_alert*>(a)) {
const QTorrentHandle h(p->handle);
if (h.is_valid()) {
// Connection was successful now but there is a warning message
QHash<QString, TrackerInfos> trackers_data = trackersInfos.value(h.hash(), QHash<QString, TrackerInfos>());
const QString tracker_url = misc::toQString(p->url);
TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url));
data.last_message = misc::toQStringU(p->msg); // Store warning message
trackers_data.insert(tracker_url, data);
trackersInfos[h.hash()] = trackers_data;
qDebug("Received a tracker warning from %s: %s", p->url.c_str(), p->msg.c_str());
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
}
else if (portmap_error_alert* p = dynamic_cast<portmap_error_alert*>(a)) {
addConsoleMessage(tr("UPnP/NAT-PMP: Port mapping failure, message: %1").arg(misc::toQStringU(p->message())), "red");
//emit UPnPError(QString(p->msg().c_str()));
}
else if (portmap_alert* p = dynamic_cast<portmap_alert*>(a)) {
qDebug("UPnP Success, msg: %s", p->message().c_str());
addConsoleMessage(tr("UPnP/NAT-PMP: Port mapping successful, message: %1").arg(misc::toQStringU(p->message())), "blue");
//emit UPnPSuccess(QString(p->msg().c_str()));
}
else if (peer_blocked_alert* p = dynamic_cast<peer_blocked_alert*>(a)) {
boost::system::error_code ec;
string ip = p->ip.to_string(ec);
if (!ec) {
addPeerBanMessage(QString::fromLatin1(ip.c_str()), true);
//emit peerBlocked(QString::fromLatin1(ip.c_str()));
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
}
else if (peer_ban_alert* p = dynamic_cast<peer_ban_alert*>(a)) {
boost::system::error_code ec;
string ip = p->ip.address().to_string(ec);
if (!ec) {
addPeerBanMessage(QString::fromLatin1(ip.c_str()), false);
//emit peerBlocked(QString::fromLatin1(ip.c_str()));
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
}
else if (fastresume_rejected_alert* p = dynamic_cast<fastresume_rejected_alert*>(a)) {
QTorrentHandle h(p->handle);
if (h.is_valid()) {
qDebug("/!\\ Fast resume failed for %s, reason: %s", qPrintable(h.name()), p->message().c_str());
if (p->error.value() == 134 && TorrentPersistentData::isSeed(h.hash()) && h.has_missing_files()) {
const QString hash = h.hash();
// Mismatching file size (files were probably moved
addConsoleMessage(tr("File sizes mismatch for torrent %1, pausing it.").arg(h.name()));
TorrentPersistentData::setErrorState(hash, true);
pauseTorrent(hash);
} else {
addConsoleMessage(tr("Fast resume data was rejected for torrent %1, checking again...").arg(h.name()), QString::fromUtf8("red"));
addConsoleMessage(tr("Reason: %1").arg(misc::toQStringU(p->message())));
}
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
}
else if (url_seed_alert* p = dynamic_cast<url_seed_alert*>(a)) {
addConsoleMessage(tr("Url seed lookup failed for url: %1, message: %2").arg(misc::toQString(p->url)).arg(misc::toQStringU(p->message())), QString::fromUtf8("red"));
//emit urlSeedProblem(QString::fromUtf8(p->url.c_str()), QString::fromUtf8(p->msg().c_str()));
}
else if (listen_succeeded_alert *p = dynamic_cast<listen_succeeded_alert*>(a)) {
boost::system::error_code ec;
QString proto = "TCP";
#if LIBTORRENT_VERSION_NUM >= 10000
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
if (p->sock_type == listen_succeeded_alert::udp)
proto = "UDP";
else if (p->sock_type == listen_succeeded_alert::tcp)
proto = "TCP";
else if (p->sock_type == listen_succeeded_alert::tcp_ssl)
proto = "TCP_SSL";
#endif
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
qDebug() << "Successfully listening on " << proto << p->endpoint.address().to_string(ec).c_str() << "/" << p->endpoint.port();
addConsoleMessage(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())), "blue");
// Force reannounce on all torrents because some trackers blacklist some ports
std::vector<torrent_handle> torrents = s->get_torrents();
std::vector<torrent_handle>::iterator it = torrents.begin();
std::vector<torrent_handle>::iterator itend = torrents.end();
for ( ; it != itend; ++it) {
it->force_reannounce();
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
}
else if (listen_failed_alert *p = dynamic_cast<listen_failed_alert*>(a)) {
boost::system::error_code ec;
QString proto = "TCP";
#if LIBTORRENT_VERSION_NUM >= 10000
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
if (p->sock_type == listen_failed_alert::udp)
proto = "UDP";
else if (p->sock_type == listen_failed_alert::tcp)
proto = "TCP";
else if (p->sock_type == listen_failed_alert::tcp_ssl)
proto = "TCP_SSL";
else if (p->sock_type == listen_failed_alert::i2p)
proto = "I2P";
else if (p->sock_type == listen_failed_alert::socks5)
proto = "SOCKS5";
#endif
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
qDebug() << "Failed listening on " << proto << p->endpoint.address().to_string(ec).c_str() << "/" << p->endpoint.port();
addConsoleMessage(tr("qBittorrent failed listening on interface %1 port: %2/%3. Reason: %4", "e.g: qBittorrent failed listening on interface 192.168.0.1 port: TCP/6881. Reason: already in use").arg(p->endpoint.address().to_string(ec).c_str()).arg(proto).arg(QString::number(p->endpoint.port())).arg(misc::toQStringU(p->error.message())), "red");
}
else if (torrent_checked_alert* p = dynamic_cast<torrent_checked_alert*>(a)) {
QTorrentHandle h(p->handle);
if (h.is_valid()) {
const QString hash = h.hash();
qDebug("%s have just finished checking", qPrintable(hash));
// Save seed status
TorrentPersistentData::saveSeedStatus(h);
// Move to temp directory if necessary
if (!h.is_seed() && !defaultTempPath.isEmpty()) {
// Check if directory is different
const QDir current_dir(h.save_path());
const QDir save_dir(getSavePath(h.hash()));
if (current_dir == save_dir) {
qDebug("Moving the torrent to the temp directory...");
QString torrent_tmp_path = defaultTempPath;
h.move_storage(torrent_tmp_path);
2012-08-10 11:04:28 +04:00
}
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
emit torrentFinishedChecking(h);
if (torrentsToPausedAfterChecking.contains(hash)) {
torrentsToPausedAfterChecking.removeOne(hash);
h.pause();
emit pausedTorrent(h);
}
}
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
else if (external_ip_alert *p = dynamic_cast<external_ip_alert*>(a)) {
boost::system::error_code ec;
addConsoleMessage(tr("External IP: %1", "e.g. External IP: 192.168.0.1").arg(p->external_address.to_string(ec).c_str()), "blue");
}
else if (state_update_alert *p = dynamic_cast<state_update_alert *>(a)) {
emit stateUpdate(p->status);
}
use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. When QAlertDispatcher is destoyed, libtorrent still can call QAlertDispatcher::dispatch a few times after destruction. This is handled by passing a "tag". A tag is a object that references QAlertDispatch. Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called with invalid tag it simply discard an alert. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp
2014-05-17 13:04:33 +04:00
} catch (const std::exception& e) {
qWarning() << "Caught exception in readAlerts(): " << e.what();
}
}
2011-02-26 22:56:15 +03:00
void QBtSession::recheckTorrent(const QString &hash) {
QTorrentHandle h = getTorrentHandle(hash);
2012-02-20 21:30:53 +04:00
if (h.is_valid() && h.has_metadata()) {
if (h.is_paused()) {
if (!torrentsToPausedAfterChecking.contains(h.hash())) {
torrentsToPausedAfterChecking << h.hash();
h.resume();
2010-01-20 21:02:26 +03:00
}
}
h.force_recheck();
}
}
2010-01-20 21:02:26 +03:00
2012-02-20 23:32:58 +04:00
QHash<QString, TrackerInfos> QBtSession::getTrackersInfo(const QString &hash) const {
return trackersInfos.value(hash, QHash<QString, TrackerInfos>());
}
2012-02-20 23:32:58 +04:00
int QBtSession::getListenPort() const {
2010-12-02 20:36:08 +03:00
qDebug() << Q_FUNC_INFO << s->listen_port();
return s->listen_port();
}
2012-02-20 23:32:58 +04:00
session_status QBtSession::getSessionStatus() const {
return s->status();
}
2012-05-15 20:57:31 +04:00
QString QBtSession::getSavePath(const QString &hash, bool fromScanDir, QString filePath) {
QString savePath;
2012-02-20 21:30:53 +04:00
if (TorrentTempData::hasTempData(hash)) {
savePath = fsutils::fromNativePath(TorrentTempData::getSavePath(hash));
2012-02-20 21:30:53 +04:00
if (savePath.isEmpty()) {
savePath = defaultSavePath;
}
2012-02-20 21:30:53 +04:00
if (appendLabelToSavePath) {
qDebug("appendLabelToSavePath is true");
const QString label = TorrentTempData::getLabel(hash);
2012-02-20 21:30:53 +04:00
if (!label.isEmpty()) {
savePath = fsutils::updateLabelInSavePath(defaultSavePath, savePath, "", label);
}
}
qDebug("getSavePath, got save_path from temp data: %s", qPrintable(savePath));
} else {
savePath = fsutils::fromNativePath(TorrentPersistentData::getSavePath(hash));
qDebug("SavePath got from persistant data is %s", qPrintable(savePath));
2012-02-20 21:30:53 +04:00
if (savePath.isEmpty()) {
if (fromScanDir && m_scanFolders->downloadInTorrentFolder(filePath)) {
savePath = QFileInfo(filePath).dir().path();
} else {
savePath = defaultSavePath;
}
}
2012-02-20 21:30:53 +04:00
if (!fromScanDir && appendLabelToSavePath) {
const QString label = TorrentPersistentData::getLabel(hash);
2012-02-20 21:30:53 +04:00
if (!label.isEmpty()) {
qDebug("Torrent label is %s", qPrintable(label));
savePath = fsutils::updateLabelInSavePath(defaultSavePath, savePath, "", label);
}
}
qDebug("getSavePath, got save_path from persistent data: %s", qPrintable(savePath));
}
// Clean path
savePath = fsutils::expandPathAbs(savePath);
2012-02-20 21:30:53 +04:00
if (!savePath.endsWith("/"))
savePath += "/";
return savePath;
}
// Take an url string to a torrent file,
// download the torrent file to a tmp location, then
// add it to download list
2012-05-20 17:03:10 +04:00
void QBtSession::downloadFromUrl(const QString &url, const QList<QNetworkCookie>& cookies)
{
addConsoleMessage(tr("Downloading '%1', please wait...", "e.g: Downloading 'xxx.torrent', please wait...").arg(url)
#ifndef DISABLE_GUI
, QPalette::WindowText
#endif
);
//emit aboutToDownloadFromUrl(url);
// Launch downloader thread
2012-05-20 17:03:10 +04:00
downloader->downloadTorrentUrl(url, cookies);
}
2010-10-17 18:46:01 +04:00
void QBtSession::downloadFromURLList(const QStringList& urls) {
2012-02-20 21:30:53 +04:00
foreach (const QString &url, urls) {
downloadFromUrl(url);
}
}
void QBtSession::addMagnetInteractive(const QString& uri)
{
emit newMagnetLink(uri);
}
void QBtSession::addMagnetSkipAddDlg(const QString& uri, const QString& save_path, const QString& label) {
if (!save_path.isEmpty() || !label.isEmpty())
savepathLabel_fromurl[uri] = qMakePair(fsutils::fromNativePath(save_path), label);
addMagnetUri(uri, false);
emit newDownloadedTorrentFromRss(uri);
}
void QBtSession::downloadUrlAndSkipDialog(QString url, QString save_path, QString label, const QList<QNetworkCookie>& cookies) {
//emit aboutToDownloadFromUrl(url);
const QUrl qurl = QUrl::fromEncoded(url.toUtf8());
if (!save_path.isEmpty() || !label.isEmpty())
savepathLabel_fromurl[qurl] = qMakePair(fsutils::fromNativePath(save_path), label);
url_skippingDlg << qurl;
// Launch downloader thread
downloader->downloadTorrentUrl(url, cookies);
}
// Add to Bittorrent session the downloaded torrent file
2010-10-17 18:46:01 +04:00
void QBtSession::processDownloadedFile(QString url, QString file_path) {
2012-05-15 20:57:31 +04:00
Preferences pref;
const int index = url_skippingDlg.indexOf(QUrl::fromEncoded(url.toUtf8()));
2012-02-20 21:30:53 +04:00
if (index < 0) {
// Add file to torrent download list
file_path = fsutils::fromNativePath(file_path);
#ifdef Q_OS_WIN
// Windows hack
2012-02-20 21:30:53 +04:00
if (!file_path.endsWith(".torrent", Qt::CaseInsensitive)) {
Q_ASSERT(QFile::exists(file_path));
qDebug("Torrent name does not end with .torrent, from %s", qPrintable(file_path));
2012-02-20 21:30:53 +04:00
if (QFile::rename(file_path, file_path+".torrent")) {
file_path += ".torrent";
} else {
qDebug("Failed to rename torrent file!");
}
}
qDebug("Downloading torrent at path: %s", qPrintable(file_path));
#endif
emit newDownloadedTorrent(file_path, url);
} else {
url_skippingDlg.removeAt(index);
QTorrentHandle h = addTorrent(file_path, false, url, false);
// Pause torrent if necessary
2012-05-15 20:57:31 +04:00
if (h.is_valid() && pref.addTorrentsInPause() && Preferences().useAdditionDialog())
h.pause();
emit newDownloadedTorrentFromRss(url);
}
fsutils::forceRemove(file_path);
}
// Return current download rate for the BT
// session. Payload means that it only take into
// account "useful" part of the rate
2012-02-20 23:32:58 +04:00
qreal QBtSession::getPayloadDownloadRate() const {
return s->status().payload_download_rate;
}
// Return current upload rate for the BT
// session. Payload means that it only take into
// account "useful" part of the rate
2012-02-20 23:32:58 +04:00
qreal QBtSession::getPayloadUploadRate() const {
return s->status().payload_upload_rate;
}
2010-10-17 18:46:01 +04:00
void QBtSession::applyEncryptionSettings(pe_settings se) {
qDebug("Applying encryption settings");
s->set_pe_settings(se);
}
// Will fast resume torrents in
// backup directory
2010-10-17 18:46:01 +04:00
void QBtSession::startUpTorrents() {
qDebug("Resuming unfinished torrents");
const QDir torrentBackup(fsutils::BTBackupLocation());
const QStringList known_torrents = TorrentPersistentData::knownTorrents();
// Safety measure because some people reported torrent loss since
// we switch the v1.5 way of resuming torrents on startup
QStringList filters;
filters << "*.torrent";
const QStringList torrents_on_hd = torrentBackup.entryList(filters, QDir::Files, QDir::Unsorted);
2012-02-20 21:30:53 +04:00
foreach (QString hash, torrents_on_hd) {
hash.chop(8); // remove trailing .torrent
2012-02-20 21:30:53 +04:00
if (!known_torrents.contains(hash)) {
qDebug("found torrent with hash: %s on hard disk", qPrintable(hash));
std::cerr << "ERROR Detected!!! Adding back torrent " << qPrintable(hash) << " which got lost for some reason." << std::endl;
addTorrent(torrentBackup.path()+"/"+hash+".torrent", false, QString(), true);
}
}
// End of safety measure
qDebug("Starting up torrents");
2012-02-20 21:30:53 +04:00
if (isQueueingEnabled()) {
priority_queue<QPair<int, QString>, vector<QPair<int, QString> >, std::greater<QPair<int, QString> > > torrent_queue;
2012-02-20 21:30:53 +04:00
foreach (const QString &hash, known_torrents) {
const int prio = TorrentPersistentData::getPriority(hash);
torrent_queue.push(qMakePair(prio, hash));
}
qDebug("Priority_queue size: %ld", (long)torrent_queue.size());
// Resume downloads
while(!torrent_queue.empty()) {
const QString hash = torrent_queue.top().second;
torrent_queue.pop();
qDebug("Starting up torrent %s", qPrintable(hash));
2012-02-20 21:30:53 +04:00
if (TorrentPersistentData::isMagnet(hash)) {
addMagnetUri(TorrentPersistentData::getMagnetUri(hash), true);
} else {
addTorrent(torrentBackup.path()+"/"+hash+".torrent", false, QString(), true);
}
}
} else {
// Resume downloads
2012-02-20 21:30:53 +04:00
foreach (const QString &hash, known_torrents) {
qDebug("Starting up torrent %s", qPrintable(hash));
2012-02-20 21:30:53 +04:00
if (TorrentPersistentData::isMagnet(hash))
addMagnetUri(TorrentPersistentData::getMagnetUri(hash), true);
else
addTorrent(torrentBackup.path()+"/"+hash+".torrent", false, QString(), true);
}
}
QIniSettings settings;
settings.setValue("ported_to_new_savepath_system", true);
qDebug("Unfinished torrents resumed");
}
QBtSession * QBtSession::instance()
{
2012-02-20 21:30:53 +04:00
if (!m_instance) {
m_instance = new QBtSession;
}
return m_instance;
}
void QBtSession::drop()
{
2012-02-20 21:30:53 +04:00
if (m_instance) {
delete m_instance;
m_instance = 0;
}
}
2010-12-18 18:34:38 +03:00
2014-05-14 02:09:45 +04:00
qlonglong QBtSession::getETA(const QString &hash, const libtorrent::torrent_status &status) const
2010-12-18 18:34:38 +03:00
{
2014-05-14 02:09:45 +04:00
return m_speedMonitor->getETA(hash, status);
2010-12-18 18:34:38 +03:00
}
2013-11-14 23:56:13 +04:00
quint64 QBtSession::getAlltimeDL() const {
return m_torrentStatistics->getAlltimeDL();
2013-11-14 23:56:13 +04:00
}
quint64 QBtSession::getAlltimeUL() const {
return m_torrentStatistics->getAlltimeUL();
2013-11-14 23:56:13 +04:00
}
void QBtSession::postTorrentUpdate() {
s->post_torrent_updates();
}
void QBtSession::handleIPFilterParsed(int ruleCount)
{
2012-02-20 23:46:02 +04:00
addConsoleMessage(tr("Successfully parsed the provided IP filter: %1 rules were applied.", "%1 is a number").arg(ruleCount));
emit ipFilterParsed(false, ruleCount);
}
void QBtSession::handleIPFilterError()
{
addConsoleMessage(tr("Error: Failed to parse the provided IP filter."), "red");
emit ipFilterParsed(true, 0);
}
void QBtSession::recoverPersistentData(const QString &hash, const std::vector<char> &buf) {
if (TorrentPersistentData::isKnownTorrent(hash) || TorrentTempData::hasTempData(hash) || buf.empty())
return;
libtorrent::lazy_entry fast;
libtorrent::error_code ec;
libtorrent::lazy_bdecode(&(buf.front()), &(buf.back()), fast, ec);
if (fast.type() != libtorrent::lazy_entry::dict_t && !ec)
return;
QString savePath = fsutils::fromNativePath(QString::fromUtf8(fast.dict_find_string_value("qBt-savePath").c_str()));
qreal ratioLimit = QString::fromUtf8(fast.dict_find_string_value("qBt-ratioLimit").c_str()).toDouble();
QDateTime addedDate = QDateTime::fromTime_t(fast.dict_find_int_value("added_time"));
QString previousSavePath = QString::fromUtf8(fast.dict_find_string_value("qBt-previousSavePath").c_str());
QDateTime seedDate = QDateTime::fromTime_t(fast.dict_find_int_value("qBt-seedDate"));
QString label = QString::fromUtf8(fast.dict_find_string_value("qBt-label").c_str());
int priority = fast.dict_find_int_value("qBt-queuePosition");
bool seedStatus = fast.dict_find_int_value("qBt-seedStatus");
TorrentPersistentData::saveSavePath(hash, savePath);
TorrentPersistentData::setRatioLimit(hash, ratioLimit);
TorrentPersistentData::setAddedDate(hash, addedDate);
TorrentPersistentData::setPreviousSavePath(hash, previousSavePath);
TorrentPersistentData::saveSeedDate(hash, seedDate);
TorrentPersistentData::saveLabel(hash, label);
TorrentPersistentData::savePriority(hash, priority);
TorrentPersistentData::saveSeedStatus(hash, seedStatus);
}
void QBtSession::backupPersistentData(const QString &hash, boost::shared_ptr<libtorrent::entry> data) {
(*data)["qBt-savePath"] = fsutils::fromNativePath(TorrentPersistentData::getSavePath(hash)).toUtf8().constData();
(*data)["qBt-ratioLimit"] = QString::number(TorrentPersistentData::getRatioLimit(hash)).toUtf8().constData();
(*data)["qBt-previousSavePath"] = fsutils::fromNativePath(TorrentPersistentData::getPreviousPath(hash)).toUtf8().constData();
(*data)["qBt-seedDate"] = TorrentPersistentData::getSeedDate(hash).toTime_t();
(*data)["qBt-label"] = TorrentPersistentData::getLabel(hash).toUtf8().constData();
(*data)["qBt-queuePosition"] = TorrentPersistentData::getPriority(hash);
(*data)["qBt-seedStatus"] = (int)TorrentPersistentData::isSeed(hash);
}
void QBtSession::unhideMagnet(const QString &hash) {
Preferences pref;
HiddenData::deleteData(hash);
QString save_path = getSavePath(hash, false); //appends label if necessary
QTorrentHandle h(getTorrentHandle(hash));
if (!h.is_valid()) {
if (pref.isQueueingSystemEnabled()) {
//Internally decrease the queue limits to ensure that other queued items aren't started
libtorrent::session_settings sessionSettings(s->settings());
int max_downloading = pref.getMaxActiveDownloads();
int max_active = pref.getMaxActiveTorrents();
if (max_downloading > -1)
sessionSettings.active_downloads = max_downloading + HiddenData::getDownloadingSize();
else
sessionSettings.active_downloads = max_downloading;
if (max_active > -1)
sessionSettings.active_limit = max_active + HiddenData::getDownloadingSize();
else
sessionSettings.active_limit = max_active;
s->set_settings(sessionSettings);
}
TorrentTempData::deleteTempData(hash);
return;
}
if (!h.has_metadata()) {
if (pref.isQueueingSystemEnabled()) {
//Internally decrease the queue limits to ensure that other queued items aren't started
libtorrent::session_settings sessionSettings(s->settings());
int max_downloading = pref.getMaxActiveDownloads();
int max_active = pref.getMaxActiveTorrents();
if (max_downloading > -1)
sessionSettings.active_downloads = max_downloading + HiddenData::getDownloadingSize();
else
sessionSettings.active_downloads = max_downloading;
if (max_active > -1)
sessionSettings.active_limit = max_active + HiddenData::getDownloadingSize();
else
sessionSettings.active_limit = max_active;
s->set_settings(sessionSettings);
}
if (pref.addTorrentsInPause())
h.pause();
}
h.queue_position_bottom();
loadTorrentTempData(h, h.save_path(), !h.has_metadata()); //TempData are deleted by a call to TorrentPersistentData::saveTorrentPersistentData()
if (!pref.addTorrentsInPause())
h.resume();
h.move_storage(save_path);
emit addedTorrent(h);
}