diff --git a/src/core/preferences.cpp b/src/core/preferences.cpp index 28682951c..db50ac328 100644 --- a/src/core/preferences.cpp +++ b/src/core/preferences.cpp @@ -502,19 +502,19 @@ QList Preferences::getDownloadInScanDirs() const return Utils::Misc::boolListfromStringList(value("Preferences/Downloads/DownloadInScanDirs").toStringList()); } -void Preferences::setDownloadPathsInScanDir(const QStringList &downloadpaths) -{ - setValue(QString::fromUtf8("Preferences/Downloads/DownloadPaths"), downloadpaths); -} - -QStringList Preferences::getDownloadPathsInScanDir() const -{ - return value(QString::fromUtf8("Preferences/Downloads/DownloadPaths"), QStringList()).toStringList(); -} - void Preferences::setDownloadInScanDirs(const QList &list) { - setValue("Preferences/Downloads/DownloadInScanDirs", Utils::Misc::toStringList(list)); + setValue("Preferences/Downloads/ScanDirsDownloadPaths", Utils::Misc::toStringList(list)); +} + +void Preferences::setScanDirsDownloadPaths(const QStringList &downloadpaths) +{ + setValue("Preferences/Downloads/ScanDirsDownloadPaths", downloadpaths); +} + +QStringList Preferences::getScanDirsDownloadPaths() const +{ + return value("Preferences/Downloads/DownloadPaths").toStringList(); } QString Preferences::getScanDirsLastPath() const diff --git a/src/core/preferences.h b/src/core/preferences.h index 432fdd6b3..e076d4ee0 100644 --- a/src/core/preferences.h +++ b/src/core/preferences.h @@ -179,8 +179,8 @@ public: QList getDownloadInScanDirs() const; void setDownloadInScanDirs(const QList &list); QString getScanDirsLastPath() const; - void setDownloadPathsInScanDir(const QStringList &downloadpaths); - QStringList getDownloadPathsInScanDir() const; + void setScanDirsDownloadPaths(const QStringList &downloadpaths); + QStringList getScanDirsDownloadPaths() const; void setScanDirsLastPath(const QString &path); bool isTorrentExportEnabled() const; QString getTorrentExportDir() const; diff --git a/src/core/qtlibtorrent/qbtsession.cpp b/src/core/qtlibtorrent/qbtsession.cpp deleted file mode 100644 index 26abf0028..000000000 --- a/src/core/qtlibtorrent/qbtsession.cpp +++ /dev/null @@ -1,3145 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#include "smtp.h" -#include "filesystemwatcher.h" -#include "torrentspeedmonitor.h" -#include "torrentstatistics.h" -#include "qbtsession.h" -#include "alertdispatcher.h" -#include "misc.h" -#include "fs_utils.h" -#include "downloadthread.h" -#include "filterparserthread.h" -#include "preferences.h" -#include "scannedfoldersmodel.h" -#include "qtracker.h" -#include "logger.h" -#ifndef DISABLE_GUI -#include "shutdownconfirm.h" -#include "geoipmanager.h" -#endif -#include "torrentpersistentdata.h" -#include "bandwidthscheduler.h" -#include -#include -#include -#include -#include -#include -//#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if LIBTORRENT_VERSION_NUM < 10000 -#include -#include -#endif - -using namespace libtorrent; - -QBtSession* QBtSession::m_instance = 0; -const qreal QBtSession::MAX_RATIO = 9999.; - -const int MAX_TRACKER_ERRORS = 2; - -/* Converts a QString hash into a libtorrent sha1_hash */ -static libtorrent::sha1_hash QStringToSha1(const QString& s) { - QByteArray raw = s.toLatin1(); - Q_ASSERT(raw.size() == 40); - libtorrent::sha1_hash ret; - from_hex(raw.constData(), 40, (char*)&ret[0]); - return ret; -} - -// Main constructor -QBtSession::QBtSession() - : m_scanFolders(ScanFoldersModel::instance(this)), - preAllocateAll(false), global_ratio_limit(-1), - LSDEnabled(false), - DHTEnabled(false), queueingEnabled(false), - m_torrentExportEnabled(false), - m_finishedTorrentExportEnabled(false) -#ifndef DISABLE_GUI - , geoipDBLoaded(false), resolve_countries(false) -#endif - , m_tracker(0), m_shutdownAct(NO_SHUTDOWN) -#if LIBTORRENT_VERSION_NUM < 10000 - , m_upnp(0), m_natpmp(0) -#endif - , m_alertDispatcher(0) -{ - BigRatioTimer = new QTimer(this); - BigRatioTimer->setInterval(10000); - connect(BigRatioTimer, SIGNAL(timeout()), SLOT(processBigRatios())); - Preferences* const pref = Preferences::instance(); - // Creating Bittorrent session - QList version; - version << VERSION_MAJOR; - version << VERSION_MINOR; - version << VERSION_BUGFIX; - version << VERSION_BUILD; - const QString peer_id = "qB"; - // Construct session - s = new session(fingerprint(peer_id.toLocal8Bit().constData(), version.at(0), version.at(1), version.at(2), version.at(3)), 0); - //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; - Logger::instance()->addMessage("Peer ID: "+misc::toQString(fingerprint(peer_id.toLocal8Bit().constData(), version.at(0), version.at(1), version.at(2), version.at(3)).to_string())); - - // 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 | alert::stats_notification); - // Load previous state - loadSessionState(); - // Enabling plugins - //s->add_extension(&create_metadata_plugin); - s->add_extension(&create_ut_metadata_plugin); - if (pref->trackerExchangeEnabled()) - s->add_extension(&create_lt_trackers_plugin); - if (pref->isPeXEnabled()) { - PeXEnabled = true; - s->add_extension(&create_ut_pex_plugin); - } else { - PeXEnabled = false; - } - s->add_extension(&create_smart_ban_plugin); - 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(); - connect(pref, SIGNAL(changed()), SLOT(configureSession())); - // Torrent speed monitor - m_speedMonitor = new TorrentSpeedMonitor(this); - 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))); - connect(downloader, SIGNAL(magnetRedirect(QString, QString)), SLOT(handleMagnetRedirect(QString, QString))); - // Regular saving of fastresume data - connect(&resumeDataTimer, SIGNAL(timeout()), SLOT(saveTempFastResumeData())); - resumeDataTimer.start(pref->saveResumeDataInterval() * 60 * 1000); - qDebug("* BTSession constructed"); -} - -// Main destructor -QBtSession::~QBtSession() { - qDebug("BTSession destructor IN"); - delete m_speedMonitor; - qDebug("Deleted the torrent speed monitor"); - // Do some BT related saving - saveSessionState(); - saveFastResumeData(); - // Delete our objects - if (m_tracker) - delete m_tracker; - if (BigRatioTimer) - delete BigRatioTimer; - if (filterParser) - delete filterParser; - delete downloader; - if (bd_scheduler) - delete bd_scheduler; - delete m_alertDispatcher; - delete m_torrentStatistics; - qDebug("Deleting the session"); - delete s; - qDebug("BTSession destructor OUT"); -#ifndef DISABLE_GUI - if (m_shutdownAct != NO_SHUTDOWN) { - qDebug() << "Sending computer shutdown/suspend/hibernate signal..."; - misc::shutdownComputer(m_shutdownAct); - } -#endif -} - -void QBtSession::preAllocateAllFiles(bool b) { - const bool change = (preAllocateAll != b); - if (change) { - qDebug("PreAllocateAll changed, reloading all torrents!"); - preAllocateAll = b; - } -} - -void QBtSession::processBigRatios() { - qDebug("Process big ratios..."); - std::vector torrents = s->get_torrents(); - - std::vector::iterator torrentIT = torrents.begin(); - std::vector::iterator torrentITend = torrents.end(); - for ( ; torrentIT != torrentITend; ++torrentIT) { - const QTorrentHandle h(*torrentIT); - if (!h.is_valid()) continue; - if (h.is_seed()) { - const QString hash = h.hash(); - const qreal ratio = getRealRatio(h.status(torrent_handle::query_accurate_download_counters)); - qreal ratio_limit = TorrentPersistentData::instance()->getRatioLimit(hash); - 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); - if (ratio <= MAX_RATIO && ratio >= ratio_limit) { - Logger* const logger = Logger::instance(); - if (high_ratio_action == REMOVE_ACTION) { - logger->addMessage(tr("%1 reached the maximum ratio you set.").arg(h.name())); - logger->addMessage(tr("Removing torrent %1...").arg(h.name())); - deleteTorrent(hash); - } else { - // Pause it - if (!h.is_paused()) { - logger->addMessage(tr("%1 reached the maximum ratio you set.").arg(h.name())); - logger->addMessage(tr("Pausing torrent %1...").arg(h.name())); - pauseTorrent(hash); - } - } - //emit torrent_ratio_deleted(fileName); - } - } - } -} - -void QBtSession::setDownloadLimit(QString hash, long val) { - QTorrentHandle h = getTorrentHandle(hash); - if (h.is_valid()) { - h.set_download_limit(val); - } -} - -void QBtSession::setUploadLimit(QString hash, long val) { - qDebug("Set upload limit rate to %ld", val); - QTorrentHandle h = getTorrentHandle(hash); - if (h.is_valid()) { - h.set_upload_limit(val); - } -} - -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); -#ifndef DISABLE_GUI - addpaused_fromurl.remove(qurl); -#endif -} - -void QBtSession::handleMagnetRedirect(const QString &url_new, const QString &url_old) { - if (url_skippingDlg.contains(url_old)) { - url_skippingDlg.removeOne(url_old); - QPair savePath_label; - if (savepathLabel_fromurl.contains(url_old)) { - savePath_label = savepathLabel_fromurl.take(url_old); - } -#ifndef DISABLE_GUI - RssDownloadRule::AddPausedState state = RssDownloadRule::USE_GLOBAL; - if (addpaused_fromurl.contains(url_old)) { - state = addpaused_fromurl.take(url_old); - } -#endif - addMagnetSkipAddDlg(url_new, savePath_label.first, savePath_label.second, -#ifndef DISABLE_GUI - state, -#endif - url_old); - } - else - addMagnetInteractive(url_new); -} - -void QBtSession::setQueueingEnabled(bool enable) { - if (queueingEnabled != enable) { - qDebug("Queueing system is changing state..."); - queueingEnabled = enable; - } -} - -// Set BT session configuration -void QBtSession::configureSession() { - qDebug("Configuring session"); - Preferences* const pref = Preferences::instance(); - - 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(); - 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; - if (alternative_speeds) - down_limit = pref->getAltGlobalDownloadLimit(); - else - down_limit = pref->getGlobalDownloadLimit(); - if (down_limit <= 0) { - // Download limit disabled - setDownloadRateLimit(-1); - } else { - // Enabled - setDownloadRateLimit(down_limit*1024); - } - int up_limit; - if (alternative_speeds) - up_limit = pref->getAltGlobalUploadLimit(); - else - up_limit = pref->getGlobalUploadLimit(); - // * Global Upload limit - if (up_limit <= 0) { - // Upload limit disabled - setUploadRateLimit(-1); - } else { - // Enabled - setUploadRateLimit(up_limit*1024); - } - 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; - } -#ifndef DISABLE_GUI - // Resolve countries - qDebug("Loading country resolution settings"); - const bool new_resolv_countries = pref->resolvePeerCountries(); - if (resolve_countries != new_resolv_countries) { - qDebug("in country resolution settings"); - resolve_countries = new_resolv_countries; - if (resolve_countries && !geoipDBLoaded) { - qDebug("Loading geoip database"); - GeoIPManager::loadDatabase(s); - geoipDBLoaded = true; - } - // Update torrent handles - std::vector torrents = s->get_torrents(); - - std::vector::iterator torrentIT = torrents.begin(); - std::vector::iterator torrentITend = torrents.end(); - for ( ; torrentIT != torrentITend; ++torrentIT) { - QTorrentHandle h = QTorrentHandle(*torrentIT); - if (h.is_valid()) - h.resolve_countries(resolve_countries); - } - } -#endif - // * UPnP / NAT-PMP - Logger* const logger = Logger::instance(); - if (pref->isUPnPEnabled()) { - enableUPnP(true); - logger->addMessage(tr("UPnP / NAT-PMP support [ON]"), Log::INFO); - } else { - enableUPnP(false); - logger->addMessage(tr("UPnP / NAT-PMP support [OFF]"), Log::INFO); - } - // * Session settings - session_settings sessionSettings = s->settings(); - sessionSettings.user_agent = "qBittorrent " VERSION; - //std::cout << "HTTP user agent is " << sessionSettings.user_agent << std::endl; - logger->addMessage(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; - // 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(); - sessionSettings.cache_size = cache_size ? cache_size * 64 : -1; - sessionSettings.cache_expiry = pref->diskCacheTTL(); - qDebug() << "Using a disk cache size of" << cache_size << "MiB"; - session_settings::io_buffer_mode_t mode = pref->osCache() ? session_settings::enable_os_cache : session_settings::disable_os_cache; - sessionSettings.disk_io_read_mode = mode; - sessionSettings.disk_io_write_mode = mode; - resumeDataTimer.setInterval(pref->saveResumeDataInterval() * 60 * 1000); - sessionSettings.anonymous_mode = pref->isAnonymousModeEnabled(); - if (sessionSettings.anonymous_mode) { - logger->addMessage(tr("Anonymous mode [ON]"), Log::INFO); - } else { - logger->addMessage(tr("Anonymous mode [OFF]"), Log::INFO); - } - // Queueing System - 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 - 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(); - if (!announce_ip.isEmpty()) - sessionSettings.announce_ip = announce_ip.toStdString(); - // 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(); - // uTP - sessionSettings.enable_incoming_utp = pref->isuTPEnabled(); - sessionSettings.enable_outgoing_utp = pref->isuTPEnabled(); - // 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 -#if LIBTORRENT_VERSION_NUM >= 10000 - if (pref->isProxyEnabled()) - sessionSettings.force_proxy = pref->getForceProxy(); - else - sessionSettings.force_proxy = false; -#endif - sessionSettings.no_connect_privileged_ports = false; - sessionSettings.seed_choking_algorithm = session_settings::fastest_upload; - qDebug() << "Settings SessionSettings"; - setSessionSettings(sessionSettings); - // Bittorrent - // * Max connections per torrent limit - setMaxConnectionsPerTorrent(pref->getMaxConnecsPerTorrent()); - // * Max uploads per torrent limit - setMaxUploadsPerTorrent(pref->getMaxUploadsPerTorrent()); - // * DHT - enableDHT(pref->isDHTEnabled()); - // * PeX - if (PeXEnabled) { - logger->addMessage(tr("PeX support [ON]"), Log::INFO); - } else { - logger->addMessage(tr("PeX support [OFF]"), Log::CRITICAL); - } - if (PeXEnabled != pref->isPeXEnabled()) { - logger->addMessage(tr("Restart is required to toggle PeX support"), Log::CRITICAL); - } - // * LSD - if (pref->isLSDEnabled()) { - enableLSD(true); - logger->addMessage(tr("Local Peer Discovery support [ON]"), Log::INFO); - } else { - enableLSD(false); - logger->addMessage(tr("Local Peer Discovery support [OFF]"), Log::INFO); - } - // * 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; - logger->addMessage(tr("Encryption support [ON]"), Log::INFO); - break; - case 1: // Forced - encryptionSettings.out_enc_policy = pe_settings::forced; - encryptionSettings.in_enc_policy = pe_settings::forced; - logger->addMessage(tr("Encryption support [FORCED]"), Log::INFO); - break; - default: // Disabled - encryptionSettings.out_enc_policy = pe_settings::disabled; - encryptionSettings.in_enc_policy = pe_settings::disabled; - logger->addMessage(tr("Encryption support [OFF]"), Log::INFO); - } - applyEncryptionSettings(encryptionSettings); - // * Maximum ratio - high_ratio_action = pref->getMaxRatioAction(); - setGlobalMaxRatio(pref->getGlobalMaxRatio()); - updateRatioTimer(); - // Ip Filter - FilterParserThread::processFilterList(s, pref->bannedIPs()); - if (pref->isFilteringEnabled()) { - enableIPFilter(pref->getFilter()); - }else{ - disableIPFilter(); - } - // * Proxy settings - proxy_settings proxySettings; - 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); - 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 - if (pref->isTrackerEnabled()) { - if (!m_tracker) { - m_tracker = new QTracker(this); - } - if (m_tracker->start()) { - logger->addMessage(tr("Embedded Tracker [ON]"), Log::INFO); - } else { - logger->addMessage(tr("Failed to start the embedded tracker!"), Log::CRITICAL); - } - } else { - logger->addMessage(tr("Embedded Tracker [OFF]")); - if (m_tracker) - delete m_tracker; - } - // * Scan dirs - const QStringList scan_dirs = pref->getScanDirs(); - QStringList download_paths = pref->getDownloadPathsInScanDir(); - QList downloadInDirList = pref->getDownloadInScanDirs(); - while(scan_dirs.size() > downloadInDirList.size()) { - downloadInDirList << true; - } - while(scan_dirs.size() > download_paths.size()) { - download_paths << ""; - } - int i = 0; - foreach (const QString &dir, scan_dirs) { - qDebug() << "Adding scan dir" << dir << downloadInDirList.at(i) << "download to" << download_paths.at(i); - m_scanFolders->addPath(dir, downloadInDirList.at(i), download_paths.at(i)); - ++i; - } - qDebug("Session configured"); -} - -void QBtSession::useAlternativeSpeedsLimit(bool alternative) { - qDebug() << Q_FUNC_INFO << alternative; - // Save new state to remember it on startup - Preferences* const pref = Preferences::instance(); - // 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 - int down_limit = alternative ? pref->getAltGlobalDownloadLimit() : pref->getGlobalDownloadLimit(); - if (down_limit <= 0) { - down_limit = -1; - } else { - down_limit *= 1024; - } - setDownloadRateLimit(down_limit); - // Upload rate - int up_limit = alternative ? pref->getAltGlobalUploadLimit() : pref->getGlobalUploadLimit(); - if (up_limit <= 0) { - up_limit = -1; - } else { - up_limit *= 1024; - } - setUploadRateLimit(up_limit); - // Notify - emit alternativeSpeedsModeChanged(alternative); -} - -// Return the torrent handle, given its hash -QTorrentHandle QBtSession::getTorrentHandle(const QString &hash) const { - return QTorrentHandle(s->find_torrent(QStringToSha1(hash))); -} - -bool QBtSession::hasActiveTorrents() const { - std::vector torrents = s->get_torrents(); - - std::vector::iterator torrentIT = torrents.begin(); - std::vector::iterator torrentITend = torrents.end(); - for ( ; torrentIT != torrentITend; ++torrentIT) { - const QTorrentHandle h(*torrentIT); - if (h.is_valid() && !h.is_paused() && !h.is_queued()) - return true; - } - return false; -} - -bool QBtSession::hasDownloadingTorrents() const { - std::vector torrents = s->get_torrents(); - - std::vector::iterator torrentIT = torrents.begin(); - std::vector::iterator torrentITend = torrents.end(); - for ( ; torrentIT != torrentITend; ++torrentIT) { - if (torrentIT->is_valid()) { - try { - const torrent_status status = torrentIT->status(); - if (status.state != torrent_status::finished && status.state != torrent_status::seeding - && !(status.paused && !status.auto_managed)) - return true; - } catch(std::exception) {} - } - } - return false; -} - -void QBtSession::banIP(QString ip) { - FilterParserThread::processFilterList(s, QStringList(ip)); - Preferences::instance()->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 -void QBtSession::deleteTorrent(const QString &hash, bool delete_local_files) { - qDebug("Deleting torrent with hash: %s", qPrintable(hash)); - const QTorrentHandle h = getTorrentHandle(hash); - if (!h.is_valid()) { - qDebug("/!\\ Error: Invalid handle"); - return; - } - emit torrentAboutToBeRemoved(h); - qDebug("h is valid, getting name or hash..."); - QString fileName; - if (h.has_metadata()) - fileName = h.name(); - else - fileName = h.hash(); - // Remove it from session - 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; - if (h.has_metadata()) - uneeded_files = h.absolute_files_path_uneeded(); - s->remove_torrent(h); - // Remove unneeded and incomplete files - 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+".*"; - const QStringList files = torrentBackup.entryList(filters, QDir::Files, QDir::Unsorted); - foreach (const QString &file, files) { - fsutils::forceRemove(torrentBackup.absoluteFilePath(file)); - } - TorrentPersistentData::instance()->deletePersistentData(hash); - TorrentTempData::deleteTempData(hash); - HiddenData::deleteData(hash); - // Remove tracker errors - trackersInfos.remove(hash); - if (delete_local_files) - Logger::instance()->addMessage(tr("'%1' was removed from transfer list and hard disk.", "'xxx.avi' was removed...").arg(fileName)); - else - Logger::instance()->addMessage(tr("'%1' was removed from transfer list.", "'xxx.avi' was removed...").arg(fileName)); - qDebug("Torrent deleted."); -} - -void QBtSession::pauseAllTorrents() { - std::vector torrents = s->get_torrents(); - - std::vector::iterator torrentIT = torrents.begin(); - std::vector::iterator torrentITend = torrents.end(); - for ( ; torrentIT != torrentITend; ++torrentIT) { - try { - QTorrentHandle h = QTorrentHandle(*torrentIT); - if (!h.is_paused()) { - h.pause(); - emit pausedTorrent(h); - } - } catch(invalid_handle&) {} - } -} - -std::vector QBtSession::getTorrents() const { - return s->get_torrents(); -} - -void QBtSession::resumeAllTorrents() { - std::vector torrents = s->get_torrents(); - - std::vector::iterator torrentIT = torrents.begin(); - std::vector::iterator torrentITend = torrents.end(); - for ( ; torrentIT != torrentITend; ++torrentIT) { - try { - QTorrentHandle h = QTorrentHandle(*torrentIT); - if (h.is_paused()) { - h.resume(); - emit resumedTorrent(h); - } - } catch(invalid_handle&) {} - } -} - -void QBtSession::pauseTorrent(const QString &hash) { - QTorrentHandle h = getTorrentHandle(hash); - if (!h.is_paused()) { - h.pause(); - emit pausedTorrent(h); - } -} - -void QBtSession::resumeTorrent(const QString &hash) { - QTorrentHandle h = getTorrentHandle(hash); - if (h.is_paused()) { - h.resume(); - emit resumedTorrent(h); - } -} - -bool QBtSession::loadFastResumeData(const QString &hash, std::vector &buf) { - const QString fastresume_path = QDir(fsutils::BTBackupLocation()).absoluteFilePath(hash+QString(".fastresume")); - qDebug("Trying to load fastresume data: %s", qPrintable(fastresume_path)); - QFile fastresume_file(fastresume_path); - if (fastresume_file.size() <= 0) - return false; - if (!fastresume_file.open(QIODevice::ReadOnly)) - return false; - const QByteArray content = fastresume_file.readAll(); - const int content_size = content.size(); - Q_ASSERT(content_size > 0); - buf.resize(content_size); - memcpy(&buf[0], content.data(), content_size); - fastresume_file.close(); - return true; -} - -void QBtSession::loadTorrentSettings(QTorrentHandle& h) { - Preferences* const pref = Preferences::instance(); - // Connections limit per torrent - h.set_max_connections(pref->getMaxConnecsPerTorrent()); - // Uploads limit per torrent - h.set_max_uploads(pref->getMaxUploadsPerTorrent()); -#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); - Preferences* const pref = Preferences::instance(); - Logger* const logger = Logger::instance(); - QTorrentHandle h; - add_torrent_params p; - libtorrent::error_code ec; - - libtorrent::parse_magnet_uri(magnet_uri.toUtf8().constData(), p, ec); - if (ec) { - logger->addMessage(tr("Couldn't parse this Magnet URI: '%1'").arg(magnet_uri)); - return h; - } - const QString hash(misc::toQString(p.info_hash)); - if (hash.isEmpty()) { - logger->addMessage(tr("'%1' is not a valid magnet URI.").arg(magnet_uri)); - return h; - } - const QDir torrentBackup(fsutils::BTBackupLocation()); - if (resumed) { - // Load metadata - const QString torrent_path = torrentBackup.absoluteFilePath(hash+".torrent"); - if (QFile::exists(torrent_path)) - return addTorrent(torrent_path, false, QString::null, true); - } - 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"); - logger->addMessage(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); - - // Get save path - QString savePath; - if (!resumed && savepathLabel_fromurl.contains(magnet_uri)) { - QPair 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::instance()->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 - 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 - 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 - if (!h.is_valid()) { - // No need to keep on, it failed. - qDebug("/!\\ Error: Invalid handle"); - return h; - } - Q_ASSERT(h.hash() == hash); - - loadTorrentSettings(h); - - // Load filtered files - bool add_paused = pref->addTorrentsInPause(); - if (!resumed) { - if (TorrentTempData::hasTempData(hash)) - add_paused = TorrentTempData::isAddPaused(hash); - 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 (!add_paused || HiddenData::hasData(hash)) { - // Start torrent because it was added in paused state - h.resume(); - } - // Send torrent addition signal - logger->addMessage(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 -QTorrentHandle QBtSession::addTorrent(QString path, bool fromScanDir, QString from_url, bool resumed, bool imported) { - QTorrentHandle h; - Preferences* const pref = Preferences::instance(); - Logger* const logger = Logger::instance(); - - // Check if BT_backup directory exists - const QDir torrentBackup(fsutils::BTBackupLocation()); - if (!torrentBackup.exists()) { - // If temporary file, remove it - if (!from_url.isNull() || fromScanDir) - fsutils::forceRemove(path); - return h; - } - - // Fix the input path if necessary - path = fsutils::fromNativePath(path); -#ifdef Q_OS_WIN - // Windows hack - if (!path.endsWith(".torrent")) - if (QFile::rename(path, path+".torrent")) path += ".torrent"; -#endif - if (path.startsWith("file:", Qt::CaseInsensitive)) - path = QUrl::fromEncoded(path.toLocal8Bit()).toLocalFile(); - if (path.isEmpty()) return h; - - Q_ASSERT(!misc::isUrl(path)); - - qDebug("Adding %s to download list", qPrintable(path)); - boost::intrusive_ptr t; - try { - qDebug() << "Loading torrent at" << path; - // Getting torrent file informations - std::vector buffer; - lazy_entry entry; - libtorrent::error_code ec; - misc::loadBencodedFile(path, buffer, entry, ec); - t = new torrent_info(entry); - if (!t->is_valid()) - throw std::exception(); - } catch(std::exception& e) { - if (!from_url.isNull()) { - logger->addMessage(tr("Unable to decode torrent file: '%1'", "e.g: Unable to decode torrent file: '/home/y/xxx.torrent'").arg(from_url), Log::CRITICAL); - logger->addMessage(misc::toQStringU(e.what()), Log::CRITICAL); - //emit invalidTorrent(from_url); - fsutils::forceRemove(path); - }else{ - logger->addMessage(tr("Unable to decode torrent file: '%1'", "e.g: Unable to decode torrent file: '/home/y/xxx.torrent'").arg(fsutils::toNativePath(path)), Log::CRITICAL); - //emit invalidTorrent(path); - } - logger->addMessage(tr("This file is either corrupted or this isn't a torrent."), Log::CRITICAL); - if (fromScanDir) { - // Remove file - fsutils::forceRemove(path); - } - return h; - } - - const QString hash = misc::toQString(t->info_hash()); - - qDebug(" -> Hash: %s", qPrintable(hash)); - qDebug(" -> Name: %s", t->name().c_str()); - - // Check for duplicate - if (s->find_torrent(t->info_hash()).is_valid()) { - qDebug("/!\\ Torrent is already in download list"); - // Update info Bar - if (!from_url.isNull()) { - logger->addMessage(tr("'%1' is already in download list.", "e.g: 'xxx.avi' is already in download list.").arg(from_url)); - }else{ - logger->addMessage(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 - QTorrentHandle h_ex = getTorrentHandle(hash); - mergeTorrents(h_ex, t); - - // Delete file if temporary - if (!from_url.isNull() || fromScanDir) - fsutils::forceRemove(path); - return h; - } - - // Check number of files - if (t->num_files() < 1) { - logger->addMessage(tr("Error: The torrent %1 does not contain any file.").arg(misc::toQStringU(t->name()))); - // Delete file if temporary - if (!from_url.isNull() || fromScanDir) - fsutils::forceRemove(path); - return h; - } - - // Actually add the torrent - add_torrent_params p; - initializeAddTorrentParams(hash, p); - p.ti = t; - - // Get fast resume data if existing - bool fastResume = false; - std::vector buf; // Needs to stay in the function scope - if (resumed) { - if (loadFastResumeData(hash, buf)) { - fastResume = true; -#if LIBTORRENT_VERSION_NUM < 10000 - p.resume_data = &buf; -#else - p.resume_data = buf; -#endif - qDebug("Successfully loaded fast resume data"); - } - } - - recoverPersistentData(hash, buf); - QString savePath; - 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 savePath_label = savepathLabel_fromurl.take(QUrl::fromEncoded(from_url.toUtf8())); - if (savePath_label.first.isEmpty()) - savePath = getSavePath(hash, fromScanDir, path); - else - savePath = savePath_label.first; - // Remember label - TorrentTempData::setLabel(hash, savePath_label.second); - } else { - savePath = getSavePath(hash, fromScanDir, path, imported); - } - if (!imported && !defaultTempPath.isEmpty() && !TorrentPersistentData::instance()->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 - 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 - if (!QDir(savePath).exists()) QDir().mkpath(savePath); - 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 - if (!h.is_valid()) { - qDebug("/!\\ Error: Invalid handle"); - // If temporary file, remove it - if (!from_url.isNull() || fromScanDir) - fsutils::forceRemove(path); - return h; - } - - loadTorrentSettings(h); - - bool add_paused = pref->addTorrentsInPause(); - if (!resumed) { - qDebug("This is a NEW torrent (first time)..."); - if (TorrentTempData::hasTempData(hash)) - add_paused = TorrentTempData::isAddPaused(hash); - - loadTorrentTempData(h, savePath, false); - - // Append .!qB to incomplete files - if (appendqBExtension) - appendqBextensionToTorrent(h, true); - - // Backup torrent file - const QString newFile = torrentBackup.absoluteFilePath(hash + ".torrent"); - if (path != newFile) - QFile::copy(path, newFile); - // Copy the torrent file to the export folder - if (m_torrentExportEnabled) - exportTorrentFile(h); - } - - if (!fastResume && !add_paused) { - // Start torrent because it was added in paused state - h.resume(); - } - - // If temporary file, remove it - if (!from_url.isNull() || fromScanDir) - fsutils::forceRemove(path); - - // Display console message - if (!from_url.isNull()) { - if (fastResume) - logger->addMessage(tr("'%1' resumed. (fast resume)", "'/home/y/xxx.torrent' was resumed. (fast resume)").arg(from_url)); - else - logger->addMessage(tr("'%1' added to download list.", "'/home/y/xxx.torrent' was added to download list.").arg(from_url)); - }else{ - if (fastResume) - logger->addMessage(tr("'%1' resumed. (fast resume)", "'/home/y/xxx.torrent' was resumed. (fast resume)").arg(fsutils::toNativePath(path))); - else - logger->addMessage(tr("'%1' added to download list.", "'/home/y/xxx.torrent' was added to download list.").arg(fsutils::toNativePath(path))); - } - - // 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::instance()->getTorrentExportDir() : Preferences::instance()->getFinishedTorrentExportDir()); - if (exportPath.exists() || exportPath.mkpath(exportPath.absolutePath())) { - QString new_torrent_path = exportPath.absoluteFilePath(h.name()+".torrent"); - if (QFile::exists(new_torrent_path) && fsutils::sameFiles(torrent_path, new_torrent_path)) { - // 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) { - // Seeding mode - // Skip checking and directly start seeding (new in libtorrent v0.15) - if (TorrentTempData::isSeedingMode(hash)) - p.flags |= add_torrent_params::flag_seed_mode; - else - p.flags &= ~add_torrent_params::flag_seed_mode; - - // Preallocation mode - if (preAllocateAll) - p.storage_mode = storage_mode_allocate; - else - p.storage_mode = storage_mode_sparse; - - // Priorities - /*if (TorrentTempData::hasTempData(hash)) { - std::vector fp; - TorrentTempData::getFilesPriority(hash, fp); - if (!fp.empty()) { - std::vector *fp_conv = new std::vector(); - for (uint i=0; ipush_back(fp[i]); - } - p.file_priorities = fp_conv; - } - }*/ - - // 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 -} - -void QBtSession::loadTorrentTempData(QTorrentHandle &h, QString savePath, bool magnet) { - qDebug("loadTorrentTempdata() - ENTER"); - const QString hash = h.hash(); - // Sequential download - if (TorrentTempData::hasTempData(hash)) { - // sequential download - h.set_sequential_download(TorrentTempData::isSequential(hash)); - - // The following is useless for newly added magnet - if (!magnet) { - // Files priorities - vector 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()); - if (files_path.size() == h.num_files()) { - for (int i=0; isaveTorrentPersistentData(h, QString::null, magnet); - else - TorrentPersistentData::instance()->saveTorrentPersistentData(h, fsutils::fromNativePath(savePath), magnet); -} - -void QBtSession::mergeTorrents(QTorrentHandle& h_ex, const QString& magnet_uri) -{ - QList new_trackers = misc::magnetUriToTrackers(magnet_uri); - bool trackers_added = false; - foreach (const QUrl& new_tracker, new_trackers) { - bool found = false; - std::vector 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) - Logger::instance()->addMessage(tr("Note: new trackers were added to the existing torrent.")); -} - -void QBtSession::mergeTorrents(QTorrentHandle &h_ex, boost::intrusive_ptr t) { - // Check if the torrent contains trackers or url seeds we don't know about - // and add them - if (!h_ex.is_valid()) return; - std::vector existing_trackers = h_ex.trackers(); - std::vector 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 - bool found = false; - foreach (const announce_entry& existing_tracker, existing_trackers) { - if (QUrl(new_tracker_url.c_str()) == QUrl(existing_tracker.url.c_str())) { - found = true; - break; - } - } - - if (!found) { - h_ex.add_tracker(announce_entry(new_tracker_url)); - trackers_added = true; - } - } - - Logger* const logger = Logger::instance(); - if (trackers_added) - logger->addMessage(tr("Note: new trackers were added to the existing torrent.")); - - bool urlseeds_added = false; - const QStringList old_urlseeds = h_ex.url_seeds(); - std::vector new_urlseeds = t->web_seeds(); - - std::vector::iterator it = new_urlseeds.begin(); - std::vector::iterator itend = new_urlseeds.end(); - for ( ; it != itend; ++it) { - const QString new_url = misc::toQString(it->url.c_str()); - if (!old_urlseeds.contains(new_url)) { - urlseeds_added = true; - h_ex.add_url_seed(new_url); - } - } - if (urlseeds_added) - logger->addMessage(tr("Note: new URL seeds were added to the existing torrent.")); -} - -void QBtSession::exportTorrentFiles(QString path) { - Q_ASSERT(m_torrentExportEnabled); - QDir exportDir(path); - if (!exportDir.exists()) { - if (!exportDir.mkpath(exportDir.absolutePath())) { - std::cerr << "Error: Could not create torrent export directory: " << qPrintable(exportDir.absolutePath()) << std::endl; - return; - } - } - QDir torrentBackup(fsutils::BTBackupLocation()); - std::vector handles = s->get_torrents(); - - std::vector::iterator itr=handles.begin(); - std::vector::iterator itrend=handles.end(); - for ( ; itr != itrend; ++itr) { - const QTorrentHandle h(*itr); - if (!h.is_valid()) { - std::cerr << "Torrent Export: torrent is invalid, skipping..." << std::endl; - continue; - } - const QString src_path(torrentBackup.absoluteFilePath(h.hash()+".torrent")); - if (QFile::exists(src_path)) { - QString dst_path = exportDir.absoluteFilePath(h.name()+".torrent"); - 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; - } - } - qDebug("Export Torrent: %s -> %s", qPrintable(src_path), qPrintable(dst_path)); - QFile::copy(src_path, dst_path); - } else { - std::cerr << "Error: could not export torrent "<< qPrintable(h.hash()) << ", maybe it has not metadata yet." < handles = s->get_torrents(); - - std::vector::const_iterator it = handles.begin(); - std::vector::const_iterator itend = handles.end(); - for ( ; it != itend; ++it) { - if (!it->is_valid()) - continue; - try { - it->set_max_connections(max); - } catch(std::exception) {} - } -} - -void QBtSession::setMaxUploadsPerTorrent(int max) { - qDebug() << Q_FUNC_INFO << max; - // Apply this to all session torrents - std::vector handles = s->get_torrents(); - - std::vector::const_iterator it = handles.begin(); - std::vector::const_iterator itend = handles.end(); - for ( ; it != itend; ++it) { - if (!it->is_valid()) - continue; - try { - it->set_max_uploads(max); - } catch(std::exception) {} - } -} - -void QBtSession::enableUPnP(bool b) { - Preferences* const pref = Preferences::instance(); - if (b) { - qDebug("Enabling UPnP / NAT-PMP"); -#if LIBTORRENT_VERSION_NUM < 10000 - m_upnp = s->start_upnp(); - m_natpmp = s->start_natpmp(); -#else - s->start_upnp(); - s->start_natpmp(); -#endif - // TODO: Remove dependency from WebUI - // Use UPnP/NAT-PMP for Web UI too - 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); -#else - s->add_port_mapping(session::tcp, port, port); -#endif - } - } else { - qDebug("Disabling UPnP / NAT-PMP"); - s->stop_upnp(); - s->stop_natpmp(); - -#if LIBTORRENT_VERSION_NUM < 10000 - m_upnp = 0; - m_natpmp = 0; -#endif - } -} - -void QBtSession::enableLSD(bool b) { - if (b) { - if (!LSDEnabled) { - qDebug("Enabling Local Peer Discovery"); - s->start_lsd(); - LSDEnabled = true; - } - } else { - if (LSDEnabled) { - qDebug("Disabling Local Peer Discovery"); - s->stop_lsd(); - LSDEnabled = false; - } - } -} - -void QBtSession::loadSessionState() { - const QString state_path = fsutils::cacheLocation()+"/"+QString::fromUtf8("ses_state"); - if (!QFile::exists(state_path)) return; - if (QFile(state_path).size() == 0) { - // Remove empty invalid state file - fsutils::forceRemove(state_path); - return; - } - std::vector in; - lazy_entry e; - libtorrent::error_code ec; - misc::loadBencodedFile(state_path, in, e, ec); - if (!ec) - s->load_state(e); -} - -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 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 -void QBtSession::enableDHT(bool b) { - Logger* const logger = Logger::instance(); - if (b) { - if (!DHTEnabled) { - try { - 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)); - s->add_dht_router(std::make_pair(std::string("dht.transmissionbt.com"), 6881)); - s->add_dht_router(std::make_pair(std::string("dht.aelitis.com"), 6881)); // Vuze - DHTEnabled = true; - logger->addMessage(tr("DHT support [ON]"), Log::INFO); - qDebug("DHT enabled"); - } - catch(std::exception &e) { - qDebug("Could not enable DHT, reason: %s", e.what()); - logger->addMessage(tr("DHT support [OFF]. Reason: %1").arg(misc::toQStringU(e.what())), Log::CRITICAL); - } - } - } - else { - if (DHTEnabled) { - DHTEnabled = false; - s->stop_dht(); - logger->addMessage(tr("DHT support [OFF]"), Log::INFO); - qDebug("DHT disabled"); - } - } -} - -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; - } - - if (all_time_download == 0) { - if (all_time_upload == 0) - return 0.0; - return MAX_RATIO+1; - } - - qreal ratio = all_time_upload / (float) all_time_download; - Q_ASSERT(ratio >= 0.); - if (ratio > MAX_RATIO) - ratio = MAX_RATIO; - return ratio; -} - -// Called periodically -void QBtSession::saveTempFastResumeData() { - std::vector torrents = s->get_torrents(); - - std::vector::iterator torrentIT = torrents.begin(); - std::vector::iterator torrentITend = torrents.end(); - for ( ; torrentIT != torrentITend; ++torrentIT) { - QTorrentHandle h = QTorrentHandle(*torrentIT); - try { - 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() - || TorrentPersistentData::instance()->getHasMissingFiles(h.hash())) continue; - qDebug("Saving fastresume data for %s", qPrintable(h.name())); - h.save_resume_data(); - }catch(std::exception &e) {} - } -} - -// Only save fast resume data for unfinished and unpaused torrents (Optimization) -// Called on exit -void QBtSession::saveFastResumeData() { - qDebug("Saving fast resume data..."); - // Stop listening for alerts - resumeDataTimer.stop(); - int num_resume_data = 0; - // Pause session - s->pause(); - std::vector torrents = s->get_torrents(); - - std::vector::iterator torrentIT = torrents.begin(); - std::vector::iterator torrentITend = torrents.end(); - for ( ; torrentIT != torrentITend; ++torrentIT) { - QTorrentHandle h = QTorrentHandle(*torrentIT); - if (!h.is_valid()) - continue; - try { - if (isQueueingEnabled()) - TorrentPersistentData::instance()->savePriority(h); - if (!h.has_metadata()) - continue; - // Actually with should save fast resume data for paused files too - //if (h.is_paused()) continue; - if (h.state() == torrent_status::checking_files || h.state() == torrent_status::queued_for_checking || h.has_error()) continue; - if (TorrentPersistentData::instance()->getHasMissingFiles(h.hash())) { - TorrentPersistentData::instance()->setHasMissingFiles(h.hash(), false); - continue; - } - h.save_resume_data(); - ++num_resume_data; - } catch(libtorrent::invalid_handle&) {} - } - while (num_resume_data > 0) { - std::vector alerts; - m_alertDispatcher->getPendingAlerts(alerts, 30*1000); - if (alerts.empty()) { - std::cerr << " aborting with " << num_resume_data << " outstanding " - "torrents to save resume data for" << std::endl; - break; - } - - for (std::vector::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(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) {} - delete a; - continue; - } - save_resume_data_alert const* rd = dynamic_cast(a); - if (!rd) { - delete a; - continue; - } - // Saving fast resume data was successful - --num_resume_data; - if (!rd->resume_data) { - delete a; - continue; - } - QDir torrentBackup(fsutils::BTBackupLocation()); - const QTorrentHandle h(rd->handle); - if (!h.is_valid()) { - delete a; - continue; - } - try { - // Remove old fastresume file if it exists - backupPersistentData(h.hash(), rd->resume_data); - vector 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 - s->remove_torrent(rd->handle); - } catch(libtorrent::invalid_handle&) {} - - delete a; - } - } -} - -bool QBtSession::isFilePreviewPossible(const QString &hash) const { - // See if there are supported files in the torrent - const QTorrentHandle h = getTorrentHandle(hash); - if (!h.is_valid() || !h.has_metadata()) { - return false; - } - const unsigned int nbFiles = h.num_files(); - for (unsigned int i=0; i buffer; - lazy_entry entry; - libtorrent::error_code ec; - misc::loadBencodedFile(file, buffer, entry, ec); - torrent_info t(entry); - 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; - - defaultSavePath = fsutils::fromNativePath(savepath); -} - -void QBtSession::setDefaultTempPath(const QString &temppath) { - if (QDir(defaultTempPath) == QDir(temppath)) - return; - - if (temppath.isEmpty()) { - // Disabling temp dir - // Moving all torrents to their destination folder - std::vector torrents = s->get_torrents(); - - std::vector::iterator torrentIT = torrents.begin(); - std::vector::iterator torrentITend = torrents.end(); - for ( ; torrentIT != torrentITend; ++torrentIT) { - QTorrentHandle h = QTorrentHandle(*torrentIT); - 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 torrents = s->get_torrents(); - - std::vector::iterator torrentIT = torrents.begin(); - std::vector::iterator torrentITend = torrents.end(); - for ( ; torrentIT != torrentITend; ++torrentIT) { - QTorrentHandle h = QTorrentHandle(*torrentIT); - 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); -} - -void QBtSession::appendqBextensionToTorrent(const QTorrentHandle &h, bool append) { - if (!h.is_valid() || !h.has_metadata()) return; - std::vector fp; - h.file_progress(fp); - for (int i=0; i 0 && (fp[i]/(double)file_size) < 1.) { - const QString name = h.filepath_at(i); - 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); - 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); - } - } - } -} - -void QBtSession::changeLabelInTorrentSavePath(const QTorrentHandle &h, QString old_label, QString new_label) { - if (!h.is_valid()) return; - if (!appendLabelToSavePath) return; - QString old_save_path = fsutils::fromNativePath(TorrentPersistentData::instance()->getSavePath(h.hash())); - if (!old_save_path.startsWith(defaultSavePath)) return; - QString new_save_path = fsutils::updateLabelInSavePath(defaultSavePath, old_save_path, old_label, new_label); - 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); - } -} - -void QBtSession::appendLabelToTorrentSavePath(const QTorrentHandle& h) { - if (!h.is_valid()) return; - const TorrentPersistentData* const TorPersistent = TorrentPersistentData::instance(); - const QString label = TorPersistent->getLabel(h.hash()); - if (label.isEmpty()) return; - // Current save path - QString old_save_path = fsutils::fromNativePath(TorPersistent->getSavePath(h.hash())); - QString new_save_path = fsutils::updateLabelInSavePath(defaultSavePath, old_save_path, "", label); - if (old_save_path != new_save_path) { - // Move storage - QDir().mkpath(new_save_path); - h.move_storage(new_save_path); - } -} - -void QBtSession::setAppendLabelToSavePath(bool append) { - if (appendLabelToSavePath != append) { - appendLabelToSavePath = !appendLabelToSavePath; - if (appendLabelToSavePath) { - // Move torrents storage to sub folder with label name - std::vector torrents = s->get_torrents(); - - std::vector::iterator torrentIT = torrents.begin(); - std::vector::iterator torrentITend = torrents.end(); - for ( ; torrentIT != torrentITend; ++torrentIT) { - QTorrentHandle h = QTorrentHandle(*torrentIT); - appendLabelToTorrentSavePath(h); - } - } - } -} - -void QBtSession::setAppendqBExtension(bool append) { - if (appendqBExtension != append) { - appendqBExtension = !appendqBExtension; - // append or remove .!qB extension for incomplete files - std::vector torrents = s->get_torrents(); - - std::vector::iterator torrentIT = torrents.begin(); - std::vector::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 -void QBtSession::setListeningPort(int port) { - qDebug() << Q_FUNC_INFO << port; - Preferences* const pref = Preferences::instance(); - Logger* const logger = Logger::instance(); - std::pair ports(port, port); - libtorrent::error_code ec; - const QString iface_name = pref->getNetworkInterface(); - const bool listen_ipv6 = pref->getListenIPv6(); - if (iface_name.isEmpty()) { - logger->addMessage(tr("qBittorrent is trying to listen on any interface port: %1", "e.g: qBittorrent is trying to listen on any interface port: TCP/6881").arg(QString::number(port)), Log::INFO); - s->listen_on(ports, ec, 0, session::listen_no_system_port); - - if (ec) - logger->addMessage(tr("qBittorrent failed to listen on any interface port: %1. Reason: %2", "e.g: qBittorrent failed to listen on any interface port: TCP/6881. Reason: no such interface" ).arg(QString::number(port)).arg(misc::toQStringU(ec.message())), Log::CRITICAL); - - return; - } - // Attempt to listen on provided interface - const QNetworkInterface network_iface = QNetworkInterface::interfaceFromName(iface_name); - if (!network_iface.isValid()) { - qDebug("Invalid network interface: %s", qPrintable(iface_name)); - logger->addMessage(tr("The network interface defined is invalid: %1").arg(iface_name), Log::CRITICAL); - return; - } - QString ip; - qDebug("This network interface has %d IP addresses", network_iface.addressEntries().size()); - foreach (const QNetworkAddressEntry &entry, network_iface.addressEntries()) { - if ((!listen_ipv6 && (entry.ip().protocol() == QAbstractSocket::IPv6Protocol)) - || (listen_ipv6 && (entry.ip().protocol() == QAbstractSocket::IPv4Protocol))) - continue; - 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); - if (!ec) { - ip = entry.ip().toString(); - logger->addMessage(tr("qBittorrent is trying to listen on interface %1 port: %2", "e.g: qBittorrent is trying to listen on interface 192.168.0.1 port: TCP/6881").arg(ip).arg(QString::number(port)), Log::INFO); - return; - } - } - logger->addMessage(tr("qBittorrent didn't find an %1 local address to listen on", "qBittorrent didn't find an IPv4 local address to listen on").arg(listen_ipv6 ? "IPv6" : "IPv4"), Log::CRITICAL); -} - -// Set download rate limit -// -1 to disable -void QBtSession::setDownloadRateLimit(long rate) { - qDebug() << Q_FUNC_INFO << rate; - Q_ASSERT(rate == -1 || rate >= 0); - session_settings settings = s->settings(); - settings.download_rate_limit = rate; - s->set_settings(settings); -} - -// Set upload rate limit -// -1 to disable -void QBtSession::setUploadRateLimit(long rate) { - qDebug() << Q_FUNC_INFO << rate; - Q_ASSERT(rate == -1 || rate >= 0); - 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) { - 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::instance()->setRatioLimit(hash, ratio); - updateRatioTimer(); -} - -void QBtSession::removeRatioPerTorrent(const QString &hash) -{ - qDebug("* Remove individual max ratio for torrent %s.", qPrintable(hash)); - TorrentPersistentData::instance()->setRatioLimit(hash, TorrentPersistentData::USE_GLOBAL_RATIO); - updateRatioTimer(); -} - -qreal QBtSession::getMaxRatioPerTorrent(const QString &hash, bool *usesGlobalRatio) const -{ - qreal ratio_limit = TorrentPersistentData::instance()->getRatioLimit(hash); - if (ratio_limit == TorrentPersistentData::USE_GLOBAL_RATIO) { - ratio_limit = global_ratio_limit; - if (usesGlobalRatio) - *usesGlobalRatio = true; - } else { - if (usesGlobalRatio) - *usesGlobalRatio = false; - } - return ratio_limit; -} - -void QBtSession::updateRatioTimer() -{ - if (global_ratio_limit == -1 && !TorrentPersistentData::instance()->hasPerTorrentRatioLimit()) { - if (BigRatioTimer->isActive()) - BigRatioTimer->stop(); - } else if (!BigRatioTimer->isActive()) { - BigRatioTimer->start(); - } -} - -// Enable IP Filtering -void QBtSession::enableIPFilter(const QString &filter_path, bool force) { - qDebug("Enabling IPFiler"); - 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 -void QBtSession::disableIPFilter() { - qDebug("Disabling IPFilter"); - s->set_ip_filter(ip_filter()); - if (filterParser) { - disconnect(filterParser.data(), 0, this, 0); - delete filterParser; - } - filterPath = ""; -} - -void QBtSession::recursiveTorrentDownload(const QTorrentHandle &h) -{ - try { - for (int i=0; iaddMessage(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; - - std::vector buffer; - lazy_entry entry; - libtorrent::error_code ec; - misc::loadBencodedFile(torrent_fullpath, buffer, entry, ec); - boost::intrusive_ptr t = new torrent_info(entry); - 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) { - if (!h.is_valid()) return; - QString program = Preferences::instance()->getAutoRunProgram().trimmed(); - if (program.isEmpty()) return; - // Replace %f by torrent path - QString torrent_path; - 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); -} - -void QBtSession::sendNotificationEmail(const QTorrentHandle &h) { - 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"; - content += tr("Torrent size: %1").arg(misc::friendlyUnit(status.total_wanted)) + "\n"; - content += tr("Save path: %1").arg(TorrentPersistentData::instance()->getSavePath(h.hash())) + "\n\n"; - 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::instance()->getMailNotificationEmail(), tr("[qBittorrent] %1 has finished downloading").arg(h.name()), content); -} - -// Read alerts sent by the Bittorrent session -void QBtSession::readAlerts() { - - typedef std::vector 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 { - switch (a->type()) { - case torrent_finished_alert::alert_type: - handleTorrentFinishedAlert(static_cast(a)); - break; - case save_resume_data_alert::alert_type: - handleSaveResumeDataAlert(static_cast(a)); - break; - case file_renamed_alert::alert_type: - handleFileRenamedAlert(static_cast(a)); - break; - case torrent_deleted_alert::alert_type: - handleTorrentDeletedAlert(static_cast(a)); - break; - case storage_moved_alert::alert_type: - handleStorageMovedAlert(static_cast(a)); - break; - case storage_moved_failed_alert::alert_type: - handleStorageMovedFailedAlert(static_cast(a)); - break; - case metadata_received_alert::alert_type: - handleMetadataReceivedAlert(static_cast(a)); - break; - case file_error_alert::alert_type: - handleFileErrorAlert(static_cast(a)); - break; - case file_completed_alert::alert_type: - handleFileCompletedAlert(static_cast(a)); - break; - case torrent_paused_alert::alert_type: - handleTorrentPausedAlert(static_cast(a)); - break; - case tracker_error_alert::alert_type: - handleTrackerErrorAlert(static_cast(a)); - break; - case tracker_reply_alert::alert_type: - handleTrackerReplyAlert(static_cast(a)); - break; - case tracker_warning_alert::alert_type: - handleTrackerWarningAlert(static_cast(a)); - break; - case portmap_error_alert::alert_type: - handlePortmapWarningAlert(static_cast(a)); - break; - case portmap_alert::alert_type: - handlePortmapAlert(static_cast(a)); - break; - case peer_blocked_alert::alert_type: - handlePeerBlockedAlert(static_cast(a)); - break; - case peer_ban_alert::alert_type: - handlePeerBanAlert(static_cast(a)); - break; - case fastresume_rejected_alert::alert_type: - handleFastResumeRejectedAlert(static_cast(a)); - break; - case url_seed_alert::alert_type: - handleUrlSeedAlert(static_cast(a)); - break; - case listen_succeeded_alert::alert_type: - handleListenSucceededAlert(static_cast(a)); - break; - case listen_failed_alert::alert_type: - handleListenFailedAlert(static_cast(a)); - break; - case torrent_checked_alert::alert_type: - handleTorrentCheckedAlert(static_cast(a)); - break; - case external_ip_alert::alert_type: - handleExternalIPAlert(static_cast(a)); - break; - case state_update_alert::alert_type: - handleStateUpdateAlert(static_cast(a)); - break; - case stats_alert::alert_type: - handleStatsAlert(static_cast(a)); - break; - } - } catch (const std::exception& e) { - qWarning() << "Caught exception in readAlerts(): " << misc::toQStringU(e.what()); - } -} - -void QBtSession::handleTorrentFinishedAlert(libtorrent::torrent_finished_alert* p) { - 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); - - TorrentPersistentData* const TorPersistent = TorrentPersistentData::instance(); - const bool was_already_seeded = TorPersistent->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 buffer; - lazy_entry entry; - libtorrent::error_code ec; - misc::loadBencodedFile(torrent_fullpath, buffer, entry, ec); - boost::intrusive_ptr t = new torrent_info(entry); - if (t->is_valid()) { - qDebug("emitting recursiveTorrentDownloadPossible()"); - emit recursiveTorrentDownloadPossible(h); - break; - } - } - catch(std::exception&) { - qDebug("Caught error loading torrent"); - Logger::instance()->addMessage(tr("Unable to decode %1 torrent file.").arg(fsutils::toNativePath(torrent_fullpath)), Log::CRITICAL); - } - } - } - // 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()); - } - } - // Remember finished state - qDebug("Saving seed status"); - TorPersistent->saveSeedStatus(h); - // Recheck if the user asked to - Preferences* const pref = Preferences::instance(); - 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() || - pref->hibernateWhenDownloadsComplete()) - && !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 hibernate = pref->hibernateWhenDownloadsComplete(); - bool shutdown = pref->shutdownWhenDownloadsComplete(); - // Confirm shutdown - shutDownAction action = NO_SHUTDOWN; - - if (suspend) - action = SUSPEND_COMPUTER; - else if (hibernate) - action = HIBERNATE_COMPUTER; - else if (shutdown) - action = SHUTDOWN_COMPUTER; - if (!ShutdownConfirmDlg::askForConfirmation(action)) - return; - - // Actually shut down - if (suspend || hibernate || shutdown) { - qDebug("Preparing for auto-shutdown because all downloads are complete!"); - // Disabling it for next time - pref->setShutdownWhenDownloadsComplete(false); - pref->setSuspendWhenDownloadsComplete(false); - pref->setHibernateWhenDownloadsComplete(false); - // Make sure preferences are synced before exiting - m_shutdownAct = action; - } - qDebug("Exiting the application"); - qApp->exit(); - return; - } -#endif // DISABLE_GUI - } - } -} - -void QBtSession::handleSaveResumeDataAlert(libtorrent::save_resume_data_alert* p) { - 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 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(); - } - } -} - -void QBtSession::handleFileRenamedAlert(libtorrent::file_renamed_alert* p) { - 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); - } - } else { - // Single-file torrent - // Renaming a file corresponds to changing the save path - emit savePathChanged(h); - } - } -} - -void QBtSession::handleTorrentDeletedAlert(libtorrent::torrent_deleted_alert* p) { - 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); - } - } - } -} - -void QBtSession::handleStorageMovedAlert(libtorrent::storage_moved_alert* p) { - QTorrentHandle h(p->handle); - if (!h.is_valid()) { - qWarning("invalid handle received in storage_moved_alert"); - return; - } - - QString hash = h.hash(); - - if (!TorrentTempData::isMoveInProgress(hash)) { - qWarning("unexpected storage_moved_alert received"); - return; - } - - QString new_save_path = fsutils::fromNativePath(misc::toQStringU(p->path.c_str())); - if (new_save_path != fsutils::fromNativePath(TorrentTempData::getNewPath(hash))) { - qWarning("new path received in handleStorageMovedAlert() doesn't match a path in a queue"); - return; - } - - QString oldPath = fsutils::fromNativePath(TorrentTempData::getOldPath(hash)); - - qDebug("Torrent is successfully moved from %s to %s", qPrintable(oldPath), qPrintable(new_save_path)); - - // Attempt to remove old folder if empty - QDir old_save_dir(oldPath); - if (old_save_dir != QDir(defaultSavePath) && old_save_dir != QDir(defaultTempPath)) { - qDebug("Attempting to remove %s", qPrintable(oldPath)); - QDir().rmpath(oldPath); - } - if (defaultTempPath.isEmpty() || !new_save_path.startsWith(defaultTempPath)) { - qDebug("Storage has been moved, updating save path to %s", qPrintable(new_save_path)); - TorrentPersistentData::instance()->saveSavePath(h.hash(), new_save_path); - } - emit savePathChanged(h); - //h.force_recheck(); - - QString queued = TorrentTempData::getQueuedPath(hash); - if (!queued.isEmpty()) { - TorrentTempData::finishMove(hash); - h.move_storage(queued); - } - else { - TorrentTempData::finishMove(hash); - } -} - -void QBtSession::handleStorageMovedFailedAlert(libtorrent::storage_moved_failed_alert* p) { - - QTorrentHandle h(p->handle); - if (!h.is_valid()) { - qWarning("invalid handle received in storage_moved_failed_alert"); - return; - } - - QString hash = h.hash(); - - if (!TorrentTempData::isMoveInProgress(hash)) { - qWarning("unexpected storage_moved_alert received"); - return; - } - - Logger* const logger = Logger::instance(); - logger->addMessage(tr("Could not move torrent: '%1'. Reason: %2").arg(h.name()).arg(misc::toQStringU(p->message())), Log::CRITICAL); - - QString queued = TorrentTempData::getQueuedPath(hash); - if (!queued.isEmpty()) { - TorrentTempData::finishMove(hash); - logger->addMessage(tr("Attempting to move torrent: '%1' to path: '%2'.").arg(h.name()).arg(fsutils::toNativePath(queued))); - h.move_storage(queued); - } - else { - TorrentTempData::finishMove(hash); - } -} - -void QBtSession::handleMetadataReceivedAlert(libtorrent::metadata_received_alert* p) { - QTorrentHandle h(p->handle); - Preferences* const pref = Preferences::instance(); - 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); - } - 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); - } - } -} - -void QBtSession::handleFileErrorAlert(libtorrent::file_error_alert* p) { - QTorrentHandle h(p->handle); - if (h.is_valid()) { - h.pause(); - std::cerr << "File Error: " << p->message().c_str() << std::endl; - Logger* const logger = Logger::instance(); - logger->addMessage(tr("An I/O error occurred, '%1' paused.").arg(h.name())); - logger->addMessage(tr("Reason: %1").arg(misc::toQStringU(p->message()))); - if (h.is_valid()) { - emit fullDiskError(h, misc::toQStringU(p->message())); - //h.pause(); - emit pausedTorrent(h); - } - } -} - -void QBtSession::handleFileCompletedAlert(libtorrent::file_completed_alert* p) { - 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); - } - } -} - -void QBtSession::handleTorrentPausedAlert(libtorrent::torrent_paused_alert* p) { - if (p->handle.is_valid()) { - QTorrentHandle h(p->handle); - if (!HiddenData::hasData(h.hash())) { - if (!h.has_error() && !TorrentPersistentData::instance()->getHasMissingFiles(h.hash())) - h.save_resume_data(); - emit pausedTorrent(h); - } - } -} - -void QBtSession::handleTrackerErrorAlert(libtorrent::tracker_error_alert* p) { - // 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()); - const QString tracker_url = misc::toQString(p->url); - QHash trackers_data = trackersInfos.value(h.hash(), QHash()); - TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url)); - data.last_message = misc::toQStringU(p->msg); - trackers_data.insert(tracker_url, data); - trackersInfos[h.hash()] = trackers_data; - } else { - emit trackerAuthenticationRequired(h); - } - } -} - -void QBtSession::handleTrackerReplyAlert(libtorrent::tracker_reply_alert* p) { - 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 trackers_data = trackersInfos.value(h.hash(), QHash()); - 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; - } -} - -void QBtSession::handleTrackerWarningAlert(libtorrent::tracker_warning_alert* p) { - const QTorrentHandle h(p->handle); - if (h.is_valid()) { - // Connection was successful now but there is a warning message - QHash trackers_data = trackersInfos.value(h.hash(), QHash()); - 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()); - } -} - -void QBtSession::handlePortmapWarningAlert(libtorrent::portmap_error_alert* p) { - Logger::instance()->addMessage(tr("UPnP/NAT-PMP: Port mapping failure, message: %1").arg(misc::toQStringU(p->message())), Log::CRITICAL); -} - -void QBtSession::handlePortmapAlert(libtorrent::portmap_alert* p) { - qDebug("UPnP Success, msg: %s", p->message().c_str()); - Logger::instance()->addMessage(tr("UPnP/NAT-PMP: Port mapping successful, message: %1").arg(misc::toQStringU(p->message())), Log::INFO); -} - -void QBtSession::handlePeerBlockedAlert(libtorrent::peer_blocked_alert* p) -{ - boost::system::error_code ec; - string ip = p->ip.to_string(ec); -#if LIBTORRENT_VERSION_NUM < 10000 - if (!ec) - Logger::instance()->addPeer(QString::fromLatin1(ip.c_str()), true); -#else - QString reason; - switch (p->reason) { - case peer_blocked_alert::ip_filter: - reason = tr("due to IP filter.", "this peer was blocked due to ip filter."); - break; - case peer_blocked_alert::port_filter: - reason = tr("due to port filter.", "this peer was blocked due to port filter."); - break; - case peer_blocked_alert::i2p_mixed: - reason = tr("due to i2p mixed mode restrictions.", "this peer was blocked due to i2p mixed mode restrictions."); - break; - case peer_blocked_alert::privileged_ports: - reason = tr("because it has a low port.", "this peer was blocked because it has a low port."); - break; - case peer_blocked_alert::utp_disabled: - reason = tr("because μTP is disabled.", "this peer was blocked because μTP is disabled."); - break; - case peer_blocked_alert::tcp_disabled: - reason = tr("because TCP is disabled.", "this peer was blocked because TCP is disabled."); - break; - } - - if (!ec) - Logger::instance()->addPeer(QString::fromLatin1(ip.c_str()), true, reason); -#endif -} - -void QBtSession::handlePeerBanAlert(libtorrent::peer_ban_alert* p) { - boost::system::error_code ec; - string ip = p->ip.address().to_string(ec); - if (!ec) - Logger::instance()->addPeer(QString::fromLatin1(ip.c_str()), false); -} - -void QBtSession::handleFastResumeRejectedAlert(libtorrent::fastresume_rejected_alert* p) { - Logger* const logger = Logger::instance(); - 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() == errors::mismatching_file_size) { - // Mismatching file size (files were probably moved) - const QString hash = h.hash(); - logger->addMessage(tr("File sizes mismatch for torrent %1, pausing it.").arg(h.name()), Log::CRITICAL); - TorrentPersistentData::instance()->setHasMissingFiles(h.hash(), true); - pauseTorrent(hash); - } else { - logger->addMessage(tr("Fast resume data was rejected for torrent %1, checking again...").arg(h.name()), Log::CRITICAL); - logger->addMessage(tr("Reason: %1").arg(misc::toQStringU(p->message()))); - } - } -} - -void QBtSession::handleUrlSeedAlert(libtorrent::url_seed_alert* p) { - Logger::instance()->addMessage(tr("Url seed lookup failed for url: %1, message: %2").arg(misc::toQString(p->url)).arg(misc::toQStringU(p->message())), Log::CRITICAL); -} - -void QBtSession::handleListenSucceededAlert(libtorrent::listen_succeeded_alert *p) { - boost::system::error_code ec; - QString proto = "TCP"; -#if LIBTORRENT_VERSION_NUM >= 10000 - 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 - qDebug() << "Successfully listening on " << proto << p->endpoint.address().to_string(ec).c_str() << "/" << p->endpoint.port(); - Logger::instance()->addMessage(tr("qBittorrent is successfully listening on interface %1 port: %2/%3", "e.g: qBittorrent is successfully listening on interface 192.168.0.1 port: TCP/6881").arg(p->endpoint.address().to_string(ec).c_str()).arg(proto).arg(QString::number(p->endpoint.port())), Log::INFO); - // Force reannounce on all torrents because some trackers blacklist some ports - std::vector torrents = s->get_torrents(); - - std::vector::iterator it = torrents.begin(); - std::vector::iterator itend = torrents.end(); - for ( ; it != itend; ++it) { - it->force_reannounce(); - } -} - -void QBtSession::handleListenFailedAlert(libtorrent::listen_failed_alert *p) { - boost::system::error_code ec; - QString proto = "TCP"; -#if LIBTORRENT_VERSION_NUM >= 10000 - 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 - qDebug() << "Failed listening on " << proto << p->endpoint.address().to_string(ec).c_str() << "/" << p->endpoint.port(); - Logger::instance()->addMessage(tr("qBittorrent failed listening on interface %1 port: %2/%3. Reason: %4", "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())), Log::CRITICAL); - -} - -void QBtSession::handleTorrentCheckedAlert(libtorrent::torrent_checked_alert* p) { - 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::instance()->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); - } - } - emit torrentFinishedChecking(h); - if (torrentsToPausedAfterChecking.contains(hash)) { - torrentsToPausedAfterChecking.removeOne(hash); - h.pause(); - emit pausedTorrent(h); - } - } -} - -void QBtSession::handleExternalIPAlert(libtorrent::external_ip_alert *p) { - boost::system::error_code ec; - Logger::instance()->addMessage(tr("External IP: %1", "e.g. External IP: 192.168.0.1").arg(p->external_address.to_string(ec).c_str()), Log::INFO); -} - -void QBtSession::handleStateUpdateAlert(libtorrent::state_update_alert *p) { - emit stateUpdate(p->status); -} - -void QBtSession::handleStatsAlert(libtorrent::stats_alert *p) { - emit statsReceived(*p); -} - -void QBtSession::recheckTorrent(const QString &hash) { - QTorrentHandle h = getTorrentHandle(hash); - if (h.is_valid() && h.has_metadata()) { - if (h.is_paused()) { - if (!torrentsToPausedAfterChecking.contains(h.hash())) { - torrentsToPausedAfterChecking << h.hash(); - h.resume(); - } - } - h.force_recheck(); - } -} - -QHash QBtSession::getTrackersInfo(const QString &hash) const { - return trackersInfos.value(hash, QHash()); -} - -int QBtSession::getListenPort() const { - qDebug() << Q_FUNC_INFO << s->listen_port(); - return s->listen_port(); -} - -session_status QBtSession::getSessionStatus() const { - return s->status(); -} - -void QBtSession::applyEncryptionSettings(pe_settings se) { - qDebug("Applying encryption settings"); - s->set_pe_settings(se); -} - -// Set Proxy -void QBtSession::setProxySettings(proxy_settings proxySettings) { - qDebug() << Q_FUNC_INFO; - - proxySettings.proxy_peer_connections = Preferences::instance()->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)); - 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()); -} - -// Set BT session settings (user_agent) -void QBtSession::setSessionSettings(const session_settings &sessionSettings) { - qDebug("Set session settings"); - s->set_settings(sessionSettings); -} - -QString QBtSession::getSavePath(const QString &hash, bool fromScanDir, QString filePath, bool imported) { - QString savePath; - if (TorrentTempData::hasTempData(hash)) { - savePath = fsutils::fromNativePath(TorrentTempData::getSavePath(hash)); - if (savePath.isEmpty()) { - savePath = defaultSavePath; - } - if (!imported && appendLabelToSavePath) { - qDebug("appendLabelToSavePath is true"); - const QString label = TorrentTempData::getLabel(hash); - 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::instance()->getSavePath(hash)); - qDebug("SavePath got from persistant data is %s", qPrintable(savePath)); - if (savePath.isEmpty()) { - if (fromScanDir && m_scanFolders->downloadInTorrentFolder(filePath)) { - savePath = QFileInfo(filePath).dir().path(); - } else { - savePath = m_scanFolders->downloadPathTorrentFolder(filePath); - if (savePath.isEmpty()) - savePath = defaultSavePath; - } - } - if (!fromScanDir && appendLabelToSavePath) { - const QString label = TorrentPersistentData::instance()->getLabel(hash); - 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); - 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 -void QBtSession::downloadFromUrl(const QString &url, const QList& cookies) -{ - Logger::instance()->addMessage(tr("Downloading '%1', please wait...", "e.g: Downloading 'xxx.torrent', please wait...").arg(url)); - // Launch downloader thread - downloader->downloadTorrentUrl(url, cookies); -} - -void QBtSession::downloadFromURLList(const QStringList& urls) { - foreach (const QString &url, urls) { - downloadFromUrl(url); - } -} - -void QBtSession::addMagnetInteractive(const QString& uri) -{ - emit newMagnetLink(uri); -} - -#ifndef DISABLE_GUI -void QBtSession::addMagnetSkipAddDlg(const QString& uri, const QString& save_path, const QString& label, - const RssDownloadRule::AddPausedState &aps, const QString &uri_old) { -#else -void QBtSession::addMagnetSkipAddDlg(const QString& uri, const QString& save_path, const QString& label, const QString &uri_old) { -#endif - if (!save_path.isEmpty() || !label.isEmpty()) - savepathLabel_fromurl[uri] = qMakePair(fsutils::fromNativePath(save_path), label); - -#ifndef DISABLE_GUI - QString hash = misc::magnetUriToHash(uri); - switch (aps) { - case RssDownloadRule::ALWAYS_PAUSED: - TorrentTempData::setAddPaused(hash, true); - break; - case RssDownloadRule::NEVER_PAUSED: - TorrentTempData::setAddPaused(hash, false); - break; - case RssDownloadRule::USE_GLOBAL: - default:; - // Use global preferences - } -#endif - - addMagnetUri(uri, false); - emit newDownloadedTorrentFromRss(uri_old.isEmpty() ? uri : uri_old); -} - -#ifndef DISABLE_GUI -void QBtSession::downloadUrlAndSkipDialog(QString url, QString save_path, QString label, - const QList& cookies, const RssDownloadRule::AddPausedState &aps) { -#else -void QBtSession::downloadUrlAndSkipDialog(QString url, QString save_path, QString label, const QList& cookies) { -#endif - //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); - -#ifndef DISABLE_GUI - if (aps != RssDownloadRule::USE_GLOBAL) - addpaused_fromurl[qurl] = aps; -#endif - url_skippingDlg << qurl; - // Launch downloader thread - downloader->downloadTorrentUrl(url, cookies); -} - -// Add to Bittorrent session the downloaded torrent file -void QBtSession::processDownloadedFile(QString url, QString file_path) { - const int index = url_skippingDlg.indexOf(QUrl::fromEncoded(url.toUtf8())); - if (index < 0) { - // Add file to torrent download list - file_path = fsutils::fromNativePath(file_path); -#ifdef Q_OS_WIN - // Windows hack - 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)); - 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); - -#ifndef DISABLE_GUI - libtorrent::error_code ec; - // Get hash - libtorrent::torrent_info ti(file_path.toStdString(), ec); - QString hash; - - if (!ec) { - hash = misc::toQString(ti.info_hash()); - RssDownloadRule::AddPausedState aps = addpaused_fromurl[url]; - addpaused_fromurl.remove(url); - switch (aps) { - case RssDownloadRule::ALWAYS_PAUSED: - TorrentTempData::setAddPaused(hash, true); - break; - case RssDownloadRule::NEVER_PAUSED: - TorrentTempData::setAddPaused(hash, false); - break; - case RssDownloadRule::USE_GLOBAL: - default:; - // Use global preferences - } - } -#endif - - addTorrent(file_path, false, url, false); - emit newDownloadedTorrentFromRss(url); - } -} - -// Return current download rate for the BT -// session. Payload means that it only take into -// account "useful" part of the rate -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 -qreal QBtSession::getPayloadUploadRate() const { - return s->status().payload_upload_rate; -} - -// Will fast resume torrents in -// backup directory -void QBtSession::startUpTorrents() { - qDebug("Resuming unfinished torrents"); - const QDir torrentBackup(fsutils::BTBackupLocation()); - const TorrentPersistentData* const TorPersistent = TorrentPersistentData::instance(); - const QStringList known_torrents = TorPersistent->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); - foreach (QString hash, torrents_on_hd) { - hash.chop(8); // remove trailing .torrent - 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"); - if (isQueueingEnabled()) { - priority_queue, vector >, std::greater > > torrent_queue; - foreach (const QString &hash, known_torrents) { - const int prio = TorPersistent->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)); - if (TorPersistent->isMagnet(hash)) { - addMagnetUri(TorPersistent->getMagnetUri(hash), true); - } else { - addTorrent(torrentBackup.path()+"/"+hash+".torrent", false, QString(), true); - } - } - } else { - // Resume downloads - foreach (const QString &hash, known_torrents) { - qDebug("Starting up torrent %s", qPrintable(hash)); - if (TorPersistent->isMagnet(hash)) - addMagnetUri(TorPersistent->getMagnetUri(hash), true); - else - addTorrent(torrentBackup.path()+"/"+hash+".torrent", false, QString(), true); - } - } - qDebug("Unfinished torrents resumed"); -} - -QBtSession * QBtSession::instance() -{ - if (!m_instance) { - m_instance = new QBtSession; - } - return m_instance; -} - -void QBtSession::drop() -{ - if (m_instance) { - delete m_instance; - m_instance = 0; - } -} - -qlonglong QBtSession::getETA(const QString &hash, const libtorrent::torrent_status &status) const -{ - return m_speedMonitor->getETA(hash, status); -} - -quint64 QBtSession::getAlltimeDL() const { - return m_torrentStatistics->getAlltimeDL(); -} - -quint64 QBtSession::getAlltimeUL() const { - return m_torrentStatistics->getAlltimeUL(); -} - -void QBtSession::postTorrentUpdate() { - s->post_torrent_updates(); -} - -void QBtSession::handleIPFilterParsed(int ruleCount) -{ - Logger::instance()->addMessage(tr("Successfully parsed the provided IP filter: %1 rules were applied.", "%1 is a number").arg(ruleCount)); - emit ipFilterParsed(false, ruleCount); -} - -void QBtSession::handleIPFilterError() -{ - Logger::instance()->addMessage(tr("Error: Failed to parse the provided IP filter."), Log::CRITICAL); - emit ipFilterParsed(true, 0); -} - -void QBtSession::recoverPersistentData(const QString &hash, const std::vector &buf) { - TorrentPersistentData* const TorPersistent = TorrentPersistentData::instance(); - if (TorPersistent->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 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"); - - TorPersistent->saveSavePath(hash, savePath); - TorPersistent->setRatioLimit(hash, ratioLimit); - TorPersistent->setAddedDate(hash, addedDate); - TorPersistent->saveLabel(hash, label); - TorPersistent->savePriority(hash, priority); - TorPersistent->saveSeedStatus(hash, seedStatus); -} - -void QBtSession::backupPersistentData(const QString &hash, boost::shared_ptr data) { - const TorrentPersistentData* const TorPersistent = TorrentPersistentData::instance(); - (*data)["qBt-savePath"] = fsutils::fromNativePath(TorPersistent->getSavePath(hash)).toUtf8().constData(); - (*data)["qBt-ratioLimit"] = QString::number(TorPersistent->getRatioLimit(hash)).toUtf8().constData(); - (*data)["qBt-label"] = TorPersistent->getLabel(hash).toUtf8().constData(); - (*data)["qBt-queuePosition"] = TorPersistent->getPriority(hash); - (*data)["qBt-seedStatus"] = (int)TorPersistent->isSeed(hash); -} - -void QBtSession::unhideMagnet(const QString &hash) { - Preferences* const pref = Preferences::instance(); - 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; - } - - bool add_paused = pref->addTorrentsInPause(); - if (TorrentTempData::hasTempData(hash)) { - add_paused = TorrentTempData::isAddPaused(hash); - } - - 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 (add_paused) - h.pause(); - } - - h.queue_position_bottom(); - loadTorrentTempData(h, h.save_path(), !h.has_metadata()); //TempData are deleted by a call to TorrentPersistentData::instance()->saveTorrentPersistentData() - if (!add_paused) - h.resume(); - h.move_storage(save_path); - - emit addedTorrent(h); -} diff --git a/src/core/scanfoldersmodel.cpp b/src/core/scanfoldersmodel.cpp index 73ef8c9d1..7c544fd63 100644 --- a/src/core/scanfoldersmodel.cpp +++ b/src/core/scanfoldersmodel.cpp @@ -45,6 +45,7 @@ namespace { const int PathColumn = 0; const int DownloadAtTorrentColumn = 1; + const int DownloadPath = 2; } class ScanFoldersModel::PathData @@ -53,17 +54,20 @@ public: PathData(const QString &path) : path(path) , downloadAtPath(false) + , downloadPath(path) { } - PathData(const QString &path, bool download_at_path) + PathData(const QString &path, bool downloadAtPath, const QString &downloadPath) : path(path) - , downloadAtPath(download_at_path) + , downloadAtPath(downloadAtPath) + , downloadPath(downloadPath) { } - const QString path; - bool downloadAtPath; + const QString path; //watching directory + bool downloadAtPath; //if TRUE save data to watching directory + QString downloadPath; //if 'downloadAtPath' FALSE use this path for save data }; ScanFoldersModel *ScanFoldersModel::m_instance = 0; @@ -112,7 +116,7 @@ int ScanFoldersModel::rowCount(const QModelIndex &parent) const int ScanFoldersModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); - return 2; + return 3; } QVariant ScanFoldersModel::data(const QModelIndex &index, int role) const @@ -121,13 +125,24 @@ QVariant ScanFoldersModel::data(const QModelIndex &index, int role) const return QVariant(); const PathData *pathData = m_pathList.at(index.row()); - if ((index.column() == PathColumn) && (role == Qt::DisplayRole)) - return Utils::Fs::toNativePath(pathData->path); + QVariant value; - if ((index.column() == DownloadAtTorrentColumn) && (role == Qt::CheckStateRole)) - return (pathData->downloadAtPath ? Qt::Checked : Qt::Unchecked); + switch (index.column()) { + case PathColumn: + if (role == Qt::DisplayRole) + value = Utils::Fs::toNativePath(pathData->path); + break; + case DownloadAtTorrentColumn: + if (role == Qt::CheckStateRole) + value = pathData->downloadAtPath ? Qt::Checked : Qt::Unchecked; + break; + case DownloadPath: + if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::ToolTipRole) + value = Utils::Fs::toNativePath(pathData->downloadPath); + break; + } - return QVariant(); + return value; } QVariant ScanFoldersModel::headerData(int section, Qt::Orientation orientation, int role) const @@ -135,32 +150,80 @@ QVariant ScanFoldersModel::headerData(int section, Qt::Orientation orientation, if ((orientation != Qt::Horizontal) || (role != Qt::DisplayRole) || (section < 0) || (section >= columnCount())) return QVariant(); - if (section == PathColumn) - return tr("Watched Folder"); + QVariant title; - return tr("Download here"); + switch (section) { + case PathColumn: + title = tr("Watched Folder"); + break; + case DownloadAtTorrentColumn: + title = tr("Download here"); + break; + case DownloadPath: + title = tr("Download path"); + break; + } + + return title; } Qt::ItemFlags ScanFoldersModel::flags(const QModelIndex &index) const { - if (!index.isValid() || (index.row() >= rowCount()) || (index.column() != DownloadAtTorrentColumn)) + if (!index.isValid() || (index.row() >= rowCount())) return QAbstractTableModel::flags(index); - return (QAbstractTableModel::flags(index) | Qt::ItemIsUserCheckable); + const PathData *pathData = m_pathList.at(index.row()); + Qt::ItemFlags flags; + + switch (index.column()) { + case PathColumn: + flags = QAbstractTableModel::flags(index); + break; + case DownloadAtTorrentColumn: + flags = QAbstractTableModel::flags(index) | Qt::ItemIsUserCheckable; + break; + case DownloadPath: + if (pathData->downloadAtPath == false) + flags = QAbstractTableModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsEnabled; + else + flags = QAbstractTableModel::flags(index) ^ Qt::ItemIsEnabled; //dont edit DownloadPath if checked 'downloadAtPath' + break; + } + + return flags; } bool ScanFoldersModel::setData(const QModelIndex &index, const QVariant &value, int role) { - if (!index.isValid() || (index.row() >= rowCount()) || (index.column() > DownloadAtTorrentColumn) || (role != Qt::CheckStateRole)) + if (!index.isValid() || (index.row() >= rowCount()) || (index.column() > DownloadPath)) return false; - Q_ASSERT(index.column() == DownloadAtTorrentColumn); - m_pathList[index.row()]->downloadAtPath = (value.toInt() == Qt::Checked); - emit dataChanged(index, index); - return true; + bool success = true; + + switch (index.column()) { + case PathColumn: + success = false; + break; + case DownloadAtTorrentColumn: + if (role == Qt::CheckStateRole) { + Q_ASSERT(index.column() == DownloadAtTorrentColumn); + m_pathList[index.row()]->downloadAtPath = (value.toInt() == Qt::Checked); + emit dataChanged(index, index); + success = true; + } + break; + case DownloadPath: + Q_ASSERT(index.column() == DownloadPath); + m_pathList[index.row()]->downloadPath = value.toString(); + emit dataChanged(index, index); + success = true; + break; + } + + return success; } -ScanFoldersModel::PathStatus ScanFoldersModel::addPath(const QString &path, bool downloadAtPath) +ScanFoldersModel::PathStatus ScanFoldersModel::addPath(const QString &path, bool downloadAtPath, const QString &downloadPath) { QDir dir(path); if (!dir.exists()) return DoesNotExist; @@ -175,7 +238,8 @@ ScanFoldersModel::PathStatus ScanFoldersModel::addPath(const QString &path, bool } beginInsertRows(QModelIndex(), rowCount(), rowCount()); - m_pathList << new PathData(canonicalPath, downloadAtPath); + QString downloadToPath = downloadPath.isEmpty() ? path : downloadPath; + m_pathList << new PathData(canonicalPath, downloadAtPath, downloadToPath); endInsertRows(); // Start scanning @@ -227,6 +291,13 @@ bool ScanFoldersModel::downloadInTorrentFolder(const QString &filePath) const return m_pathList.at(row)->downloadAtPath; } +QString ScanFoldersModel::downloadPathTorrentFolder(const QString &filePath) const +{ + const int row = findPathData(QFileInfo(filePath).dir().path()); + Q_ASSERT(row != -1); + return m_pathList.at(row)->downloadPath; +} + int ScanFoldersModel::findPathData(const QString &path) const { for (int i = 0; i < m_pathList.count(); ++i) @@ -241,13 +312,16 @@ void ScanFoldersModel::makePersistent() Preferences *const pref = Preferences::instance(); QStringList paths; QList downloadInFolderInfo; + QStringList downloadPaths; foreach (const PathData *pathData, m_pathList) { paths << pathData->path; downloadInFolderInfo << pathData->downloadAtPath; + downloadPaths << pathData->downloadPath; } pref->setScanDirs(paths); pref->setDownloadInScanDirs(downloadInFolderInfo); + pref->setScanDirsDownloadPaths(downloadPaths); } void ScanFoldersModel::configure() @@ -255,10 +329,12 @@ void ScanFoldersModel::configure() Preferences *const pref = Preferences::instance(); int i = 0; + QStringList downloadPaths = pref->getScanDirsDownloadPaths(); QList downloadInDirList = pref->getDownloadInScanDirs(); foreach (const QString &dir, pref->getScanDirs()) { - bool downloadInDir = downloadInDirList.value(i, false); - addPath(dir, downloadInDir); + bool downloadInDir = downloadInDirList.value(i, true); + QString downloadPath = downloadPaths.value(i); //empty string if out-of-bounds + addPath(dir, downloadInDir, downloadPath); ++i; } } @@ -281,6 +357,8 @@ void ScanFoldersModel::addTorrentsToSession(const QStringList &pathList) BitTorrent::AddTorrentParams params; if (downloadInTorrentFolder(file)) params.savePath = QFileInfo(file).dir().path(); + else + params.savePath = downloadPathTorrentFolder(file); //if empty it will use the default savePath BitTorrent::TorrentInfo torrentInfo = BitTorrent::TorrentInfo::loadFromFile(file); if (torrentInfo.isValid()) { diff --git a/src/core/scanfoldersmodel.h b/src/core/scanfoldersmodel.h index 1b00e8ddf..ecbac4820 100644 --- a/src/core/scanfoldersmodel.h +++ b/src/core/scanfoldersmodel.h @@ -67,12 +67,13 @@ public: // TODO: removePaths(); singular version becomes private helper functions; // also: remove functions should take modelindexes - PathStatus addPath(const QString &path, bool downloadAtPath); + PathStatus addPath(const QString &path, bool downloadAtPath, const QString &downloadPath); void removePath(int row); bool removePath(const QString &path); PathStatus setDownloadAtPath(int row, bool downloadAtPath); bool downloadInTorrentFolder(const QString &filePath) const; + QString downloadPathTorrentFolder(const QString &filePath) const; void makePersistent(); private slots: diff --git a/src/core/scannedfoldersmodel.cpp b/src/core/scannedfoldersmodel.cpp deleted file mode 100644 index 5c957a116..000000000 --- a/src/core/scannedfoldersmodel.cpp +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2010 Christian Kandeler, 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 "scannedfoldersmodel.h" -#include "preferences.h" -#include "filesystemwatcher.h" - -#include -#include -#include -#include -#include "misc.h" - -namespace { - const int PathColumn = 0; - const int DownloadAtTorrentColumn = 1; - const int DownloadPath = 2; -} - -class ScanFoldersModel::PathData { -public: - PathData(const QString &path) : path(path), downloadAtPath(false), downloadPath(path) {} - PathData(const QString &path, bool download_at_path, const QString &download_path) : path(path), downloadAtPath(download_at_path), downloadPath(download_path) {} - const QString path; //watching directory - bool downloadAtPath; //if TRUE save data to watching directory - QString downloadPath; //if 'downloadAtPath' FALSE use this path for save data -}; - -ScanFoldersModel *ScanFoldersModel::instance(QObject *parent) { - //Q_ASSERT(!parent != !m_instance); - if (!m_instance) - m_instance = new ScanFoldersModel(parent); - return m_instance; -} - -ScanFoldersModel::ScanFoldersModel(QObject *parent) : - QAbstractTableModel(parent), m_fsWatcher(0) -{ } - -ScanFoldersModel::~ScanFoldersModel() { - qDeleteAll(m_pathList); -} - -int ScanFoldersModel::rowCount(const QModelIndex &parent) const { - return parent.isValid() ? 0 : m_pathList.count(); -} - -int ScanFoldersModel::columnCount(const QModelIndex &parent) const { - Q_UNUSED(parent); - return 3; -} - -QVariant ScanFoldersModel::data(const QModelIndex &index, int role) const { - if (!index.isValid() || index.row() >= rowCount()) - return QVariant(); - - const PathData* pathData = m_pathList.at(index.row()); - - switch (index.column()) { - case PathColumn: { - if (role == Qt::DisplayRole) - return fsutils::toNativePath(pathData->path); - break; - } - case DownloadAtTorrentColumn: { - if (role == Qt::CheckStateRole) - return pathData->downloadAtPath ? Qt::Checked : Qt::Unchecked; - break; - } - case DownloadPath: { - if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::ToolTipRole){ - return fsutils::toNativePath(pathData->downloadPath); - } - break; - } - } - - return QVariant(); -} - -QVariant ScanFoldersModel::headerData(int section, Qt::Orientation orientation, int role) const { - if (orientation != Qt::Horizontal || role != Qt::DisplayRole || section < 0 || section >= columnCount()) - return QVariant(); - - switch (section) { - case PathColumn: return tr("Watched Folder"); - case DownloadAtTorrentColumn: return tr("Download here"); - case DownloadPath: return tr("Download path"); - } - - return QVariant(); -} - -Qt::ItemFlags ScanFoldersModel::flags(const QModelIndex &index) const { - if (!index.isValid() || index.row() >= rowCount()) - return QAbstractTableModel::flags(index); - - const PathData* pathData = m_pathList.at(index.row()); - - switch (index.column()) { - case PathColumn: return QAbstractTableModel::flags(index); - case DownloadAtTorrentColumn: return QAbstractTableModel::flags(index) | Qt::ItemIsUserCheckable; - case DownloadPath: { - if (pathData->downloadAtPath == false){ - return QAbstractTableModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsEnabled; - } - else{ //dont edit DownloadPath if checked 'downloadAtPath' - return QAbstractTableModel::flags(index) ^ Qt::ItemIsEnabled; - } - } - } - return QAbstractTableModel::flags(index); -} - -bool ScanFoldersModel::setData(const QModelIndex &index, const QVariant &value, int role) { - if (!index.isValid() || index.row() >= rowCount() || index.column() > DownloadPath) - return false; - - if (index.column() == PathColumn) - return false; - - if (index.column() == DownloadAtTorrentColumn && role == Qt::CheckStateRole) { - Q_ASSERT(index.column() == DownloadAtTorrentColumn); - m_pathList[index.row()]->downloadAtPath = (value.toInt() == Qt::Checked); - emit dataChanged(index, index); - return true; - } - - if (index.column() == DownloadPath) { - Q_ASSERT(index.column() == DownloadPath); - m_pathList[index.row()]->downloadPath = value.toString(); - emit dataChanged(index, index); - return true; - } - return true; -} - -ScanFoldersModel::PathStatus ScanFoldersModel::addPath(const QString &path, bool download_at_path, const QString &download_path) { - QDir dir(path); - if (!dir.exists()) - return DoesNotExist; - if (!dir.isReadable()) - return CannotRead; - const QString &canonicalPath = dir.canonicalPath(); - if (findPathData(canonicalPath) != -1) - return AlreadyInList; - if (!m_fsWatcher) { - m_fsWatcher = new FileSystemWatcher(this); - connect(m_fsWatcher, SIGNAL(torrentsAdded(QStringList&)), this, SIGNAL(torrentsAdded(QStringList&))); - } - beginInsertRows(QModelIndex(), rowCount(), rowCount()); - QString download_to_path = download_path.isEmpty() ? path:download_path; - m_pathList << new PathData(canonicalPath, download_at_path, download_to_path); - endInsertRows(); - // Start scanning - m_fsWatcher->addPath(canonicalPath); - return Ok; -} - -void ScanFoldersModel::removePath(int row) { - Q_ASSERT(row >= 0 && row < rowCount()); - beginRemoveRows(QModelIndex(), row, row); - m_fsWatcher->removePath(m_pathList.at(row)->path); - m_pathList.removeAt(row); - endRemoveRows(); -} - -bool ScanFoldersModel::removePath(const QString &path) { - const int row = findPathData(path); - if (row == -1) - return false; - removePath(row); - return true; -} - -ScanFoldersModel::PathStatus ScanFoldersModel::setDownloadAtPath(int row, bool downloadAtPath) { - Q_ASSERT(row >= 0 && row < rowCount()); - - bool &oldValue = m_pathList[row]->downloadAtPath; - if (oldValue != downloadAtPath) { - if (downloadAtPath) { - QTemporaryFile testFile(m_pathList[row]->path + "/tmpFile"); - if (!testFile.open()) - return CannotWrite; - } - oldValue = downloadAtPath; - const QModelIndex changedIndex = index(row, DownloadAtTorrentColumn); - emit dataChanged(changedIndex, changedIndex); - } - return Ok; -} - -bool ScanFoldersModel::downloadInTorrentFolder(const QString &filePath) const { - const int row = findPathData(QFileInfo(filePath).dir().path()); - Q_ASSERT(row != -1); - return m_pathList.at(row)->downloadAtPath; -} - -QString ScanFoldersModel::downloadPathTorrentFolder(const QString &filePath) const { - const int row = findPathData(QFileInfo(filePath).dir().path()); - Q_ASSERT(row != -1); - return m_pathList.at(row)->downloadPath; -} - -int ScanFoldersModel::findPathData(const QString &path) const { - for (int i = 0; i < m_pathList.count(); ++i) { - const PathData* pathData = m_pathList.at(i); - if (pathData->path == fsutils::fromNativePath(path)) - return i; - } - - return -1; -} -void ScanFoldersModel::makePersistent() { - Preferences* const pref = Preferences::instance(); - QStringList paths; - QStringList downloadpaths; - QList downloadInFolderInfo; - foreach (const PathData* pathData, m_pathList) { - paths << pathData->path; - downloadpaths << pathData->downloadPath; - downloadInFolderInfo << pathData->downloadAtPath; - } - pref->setScanDirs(paths); - pref->setDownloadInScanDirs(downloadInFolderInfo); - pref->setDownloadPathsInScanDir(downloadpaths); -} - -ScanFoldersModel *ScanFoldersModel::m_instance = 0; diff --git a/src/core/scannedfoldersmodel.h b/src/core/scannedfoldersmodel.h deleted file mode 100644 index 3c363221e..000000000 --- a/src/core/scannedfoldersmodel.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2010 Christian Kandeler, 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 - */ - -#ifndef SCANNEDFOLDERSMODEL_H -#define SCANNEDFOLDERSMODEL_H - -#include -#include -#include - -class FileSystemWatcher; - -class ScanFoldersModel : public QAbstractTableModel { - Q_OBJECT - Q_DISABLE_COPY(ScanFoldersModel) - -public: - enum PathStatus { Ok, DoesNotExist, CannotRead, CannotWrite, AlreadyInList }; - static ScanFoldersModel *instance(QObject *parent = 0); - virtual ~ScanFoldersModel(); - - virtual int rowCount(const QModelIndex & parent = QModelIndex()) const; - virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - virtual Qt::ItemFlags flags(const QModelIndex &index) const; - - // TODO: removePaths(); singular version becomes private helper functions; - // also: remove functions should take modelindexes - PathStatus addPath(const QString &path, bool download_at_path, const QString &download_path); - void removePath(int row); - bool removePath(const QString &path); - PathStatus setDownloadAtPath(int row, bool downloadAtPath); - - bool downloadInTorrentFolder(const QString &filePath) const; - QString downloadPathTorrentFolder(const QString &filePath) const; - void makePersistent(); - -signals: - // The absolute paths of new torrent files in the scanned directories. - void torrentsAdded(QStringList &pathList); - -private: - explicit ScanFoldersModel(QObject *parent); - virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); - static ScanFoldersModel *m_instance; - class PathData; - int findPathData(const QString &path) const; - - QList m_pathList; - FileSystemWatcher *m_fsWatcher; -}; - -#endif // SCANNEDFOLDERSMODEL_H diff --git a/src/gui/options.ui b/src/gui/options.ui index e09d4d019..22a5ebea1 100644 --- a/src/gui/options.ui +++ b/src/gui/options.ui @@ -162,8 +162,8 @@ 0 0 - 484 - 692 + 454 + 610 @@ -519,9 +519,9 @@ 0 - -282 - 487 - 965 + 0 + 454 + 942 @@ -1036,8 +1036,8 @@ 0 0 - 480 - 658 + 361 + 586 @@ -1566,8 +1566,8 @@ 0 0 - 502 - 538 + 275 + 401 @@ -1819,12 +1819,8 @@ false - - hh:mm - - - false - + + 1 1000000 @@ -1846,9 +1842,6 @@ KiB/s - - hh:mm - @@ -1957,8 +1950,8 @@ 0 0 - 555 - 410 + 440 + 481 @@ -2351,8 +2344,8 @@ 0 0 - 419 - 537 + 332 + 480 @@ -2737,8 +2730,8 @@ 0 0 - 498 - 366 + 89 + 28 diff --git a/src/webui/prefjson.cpp b/src/webui/prefjson.cpp index 5ae29afa7..6d74b6e1a 100644 --- a/src/webui/prefjson.cpp +++ b/src/webui/prefjson.cpp @@ -58,16 +58,16 @@ QByteArray prefjson::getPreferences() data["temp_path"] = Utils::Fs::toNativePath(pref->getTempPath()); data["preallocate_all"] = pref->preAllocateAllFiles(); data["incomplete_files_ext"] = pref->useIncompleteFilesExtension(); - QVariantList l; + QVariantList scanDirs; foreach (const QString& s, pref->getScanDirs()) { - l << Utils::Fs::toNativePath(s); + scanDirs << Utils::Fs::toNativePath(s); } - data["scan_dirs"] = l; - QVariantList l2; - foreach (const QString& s, pref->getDownloadPathsInScanDir()) { - l2 << fsutils::toNativePath(s); - } - data["download_paths"] = l2; + data["scan_dirs"] = scanDirs; + QVariantList ScanDirsDownloadPaths; + foreach (const QString& s, pref->getScanDirsDownloadPaths()) { + ScanDirsDownloadPaths << Utils::Fs::toNativePath(s); + } + data["scan_dirs_download_paths"] = ScanDirsDownloadPaths; QVariantList var_list; foreach (bool b, pref->getDownloadInScanDirs()) { var_list << b; @@ -188,7 +188,7 @@ void prefjson::setPreferences(const QString& json) pref->preAllocateAllFiles(m["preallocate_all"].toBool()); if (m.contains("incomplete_files_ext")) pref->useIncompleteFilesExtension(m["incomplete_files_ext"].toBool()); - if (m.contains("scan_dirs") && m.contains("download_in_scan_dirs") && m.contains("download_paths")) { + if (m.contains("scan_dirs") && m.contains("download_in_scan_dirs") && m.contains("scan_dirs_download_paths")) { QVariantList download_at_path_tmp = m["download_in_scan_dirs"].toList(); QList download_at_path; foreach (QVariant var, download_at_path_tmp) { @@ -196,11 +196,11 @@ void prefjson::setPreferences(const QString& json) } QStringList old_folders = pref->getScanDirs(); QStringList new_folders = m["scan_dirs"].toStringList(); - QStringList download_paths = m["download_paths"].toStringList(); + QStringList download_paths = m["scan_dirs_download_paths"].toStringList(); if (download_at_path.size() == new_folders.size()) { pref->setScanDirs(new_folders); pref->setDownloadInScanDirs(download_at_path); - pref->setDownloadPathsInScanDir(download_paths); + pref->setScanDirsDownloadPaths(download_paths); foreach (const QString &old_folder, old_folders) { // Update deleted folders if (!new_folders.contains(old_folder)) {