/* * 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 "filesystemwatcher.h" #include "bittorrent.h" #include "misc.h" #include "downloadthread.h" #include "filterparserthread.h" #include "preferences.h" #include "scannedfoldersmodel.h" #ifndef DISABLE_GUI #include "geoip.h" #endif #include "torrentpersistentdata.h" #include "httpserver.h" #include "bandwidthscheduler.h" #include #include #if LIBTORRENT_VERSION_MINOR > 14 #include #endif #include #include //#include #include #include #include #include #include #include #include const int MAX_TRACKER_ERRORS = 2; const float MAX_RATIO = 100.; enum ProxyType {HTTP=1, SOCKS5=2, HTTP_PW=3, SOCKS5_PW=4, SOCKS4=5}; enum VersionType { NORMAL,ALPHA,BETA,RELEASE_CANDIDATE,DEVEL }; // Main constructor Bittorrent::Bittorrent() : m_scanFolders(ScanFoldersModel::instance(this)), preAllocateAll(false), addInPause(false), ratio_limit(-1), UPnPEnabled(false), NATPMPEnabled(false), LSDEnabled(false), DHTEnabled(false), current_dht_port(0), queueingEnabled(false), torrentExport(false), exiting(false) #ifndef DISABLE_GUI , geoipDBLoaded(false), resolve_countries(false) #endif { // To avoid some exceptions fs::path::default_name_check(fs::no_check); // For backward compatibility // Move .qBittorrent content to XDG folder // TODO: Remove after some releases (introduced in v2.1.0) misc::moveToXDGFolders(); // Creating Bittorrent session // Check if we should spoof utorrent QList version; version << VERSION_MAJOR; version << VERSION_MINOR; version << VERSION_BUGFIX; version << VERSION_TYPE; QString peer_id = Preferences::getPeerID(); if(peer_id.size() != 2) peer_id = "qB"; if(peer_id != "qB") { QStringList peer_ver = Preferences::getClientVersion().split('.'); while(peer_ver.size() < 4) { peer_ver << "0"; } for(int i=0; iset_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); // Load previous state loadSessionState(); // Enabling plugins //s->add_extension(&create_metadata_plugin); s->add_extension(&create_ut_metadata_plugin); #if LIBTORRENT_VERSION_MINOR > 14 s->add_extension(create_lt_trackers_plugin); #endif if(Preferences::isPeXEnabled()) { PeXEnabled = true; s->add_extension(&create_ut_pex_plugin); } else { PeXEnabled = false; } s->add_extension(&create_smart_ban_plugin); timerAlerts = new QTimer(); connect(timerAlerts, SIGNAL(timeout()), this, SLOT(readAlerts())); timerAlerts->start(3000); connect(&resumeDataTimer, SIGNAL(timeout()), this, SLOT(saveTempFastResumeData())); resumeDataTimer.start(180000); // 3min // To download from urls downloader = new downloadThread(this); connect(downloader, SIGNAL(downloadFinished(QString, QString)), this, SLOT(processDownloadedFile(QString, QString))); connect(downloader, SIGNAL(downloadFailure(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString))); appendLabelToSavePath = Preferences::appendTorrentLabel(); #if LIBTORRENT_VERSION_MINOR > 14 appendqBExtension = Preferences::useIncompleteFilesExtension(); #endif connect(m_scanFolders, SIGNAL(torrentsAdded(QStringList&)), this, SLOT(addTorrentsFromScanFolder(QStringList&))); // Apply user settings to Bittorrent session configureSession(); qDebug("* BTSession constructed"); } session_proxy Bittorrent::asyncDeletion() { qDebug("Bittorrent session async deletion IN"); exiting = true; // Do some BT related saving #if LIBTORRENT_VERSION_MINOR < 15 saveDHTEntry(); #endif saveSessionState(); saveFastResumeData(); // Delete session session_proxy sp = s->abort(); delete s; qDebug("Bittorrent session async deletion OUT"); return sp; } // Main destructor Bittorrent::~Bittorrent() { qDebug("BTSession destructor IN"); if(!exiting) { // Do some BT related saving #if LIBTORRENT_VERSION_MINOR < 15 saveDHTEntry(); #endif saveSessionState(); saveFastResumeData(); // Delete session session_proxy sp = s->abort(); delete s; } // Delete our objects delete timerAlerts; if(BigRatioTimer) delete BigRatioTimer; if(filterParser) delete filterParser; delete downloader; if(bd_scheduler) delete bd_scheduler; // HTTP Server if(httpServer) delete httpServer; if(timerETA) delete timerETA; qDebug("BTSession destructor OUT"); } void Bittorrent::preAllocateAllFiles(bool b) { const bool change = (preAllocateAll != b); if(change) { qDebug("PreAllocateAll changed, reloading all torrents!"); preAllocateAll = b; } } ScanFoldersModel* Bittorrent::getScanFoldersModel() const { return m_scanFolders; } void Bittorrent::processBigRatios() { if(ratio_limit <= 0) return; std::vector torrents = getTorrents(); std::vector::iterator torrentIT; for(torrentIT = torrents.begin(); torrentIT != torrents.end(); torrentIT++) { const QTorrentHandle h(*torrentIT); if(!h.is_valid()) continue; if(h.is_seed()) { const QString hash = h.hash(); const float ratio = getRealRatio(hash); if(ratio <= MAX_RATIO && ratio > ratio_limit) { addConsoleMessage(tr("%1 reached the maximum ratio you set.").arg(h.name())); if(high_ratio_action == REMOVE_ACTION) { addConsoleMessage(tr("Removing torrent %1...").arg(h.name())); deleteTorrent(hash); } else { // Pause it addConsoleMessage(tr("Pausing torrent %1...").arg(h.name())); pauseTorrent(hash); } //emit torrent_ratio_deleted(fileName); } } } } void Bittorrent::setDownloadLimit(QString hash, long val) { QTorrentHandle h = getTorrentHandle(hash); if(h.is_valid()) { h.set_download_limit(val); } } bool Bittorrent::isQueueingEnabled() const { return queueingEnabled; } void Bittorrent::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 Bittorrent::handleDownloadFailure(QString url, QString reason) { emit downloadFromUrlFailure(url, reason); // Clean up const QUrl &qurl = QUrl::fromEncoded(url.toLocal8Bit()); const int index = url_skippingDlg.indexOf(qurl); if(index >= 0) url_skippingDlg.removeAt(index); if(savepath_fromurl.contains(qurl)) savepath_fromurl.remove(qurl); } void Bittorrent::startTorrentsInPause(bool b) { addInPause = b; } void Bittorrent::setQueueingEnabled(bool enable) { if(queueingEnabled != enable) { qDebug("Queueing system is changing state..."); queueingEnabled = enable; } } // Set BT session configuration void Bittorrent::configureSession() { qDebug("Configuring session"); // Downloads // * Save path defaultSavePath = Preferences::getSavePath(); if(Preferences::isTempPathEnabled()) { setDefaultTempPath(Preferences::getTempPath()); } else { setDefaultTempPath(QString::null); } setAppendLabelToSavePath(Preferences::appendTorrentLabel()); #if LIBTORRENT_VERSION_MINOR > 14 setAppendqBExtension(Preferences::useIncompleteFilesExtension()); #endif preAllocateAllFiles(Preferences::preAllocateAllFiles()); startTorrentsInPause(Preferences::addTorrentsInPause()); // * Scan dirs const QStringList &scan_dirs = Preferences::getScanDirs(); QList downloadInDirList = Preferences::getDownloadInScanDirs(); while(scan_dirs.size() > downloadInDirList.size()) { downloadInDirList << false; } int i = 0; foreach (const QString &dir, scan_dirs) { m_scanFolders->addPath(dir, downloadInDirList.at(i)); ++i; } // * Export Dir const bool newTorrentExport = Preferences::isTorrentExportEnabled(); if(torrentExport != newTorrentExport) { torrentExport = newTorrentExport; if(torrentExport) { qDebug("Torrent export is enabled, exporting the current torrents"); exportTorrentFiles(Preferences::getExportDir()); } } // Connection // * Ports binding const unsigned short old_listenPort = getListenPort(); const unsigned short new_listenPort = Preferences::getSessionPort(); if(old_listenPort != new_listenPort) { setListeningPort(new_listenPort); addConsoleMessage(tr("qBittorrent is bound to port: TCP/%1", "e.g: qBittorrent is bound to port: 6881").arg(QString::number(new_listenPort))); } // * Global download limit const bool alternative_speeds = Preferences::isAltBandwidthEnabled(); int down_limit; if(alternative_speeds) down_limit = Preferences::getAltGlobalDownloadLimit(); else down_limit = Preferences::getGlobalDownloadLimit(); if(down_limit <= 0) { // Download limit disabled setDownloadRateLimit(-1); } else { // Enabled setDownloadRateLimit(down_limit*1024); } int up_limit; if(alternative_speeds) up_limit = Preferences::getAltGlobalUploadLimit(); else up_limit = Preferences::getGlobalUploadLimit(); // * Global Upload limit if(up_limit <= 0) { // Upload limit disabled setUploadRateLimit(-1); } else { // Enabled setUploadRateLimit(up_limit*1024); } if(Preferences::isSchedulerEnabled()) { if(!bd_scheduler) { bd_scheduler = new BandwidthScheduler(this); connect(bd_scheduler, SIGNAL(switchToAlternativeMode(bool)), this, SLOT(useAlternativeSpeedsLimit(bool))); } bd_scheduler->start(); } else { if(bd_scheduler) delete bd_scheduler; } #ifndef DISABLE_GUI // Resolve countries qDebug("Loading country resolution settings"); const bool new_resolv_countries = Preferences::resolvePeerCountries(); if(resolve_countries != new_resolv_countries) { qDebug("in country reoslution settings"); resolve_countries = new_resolv_countries; if(resolve_countries && !geoipDBLoaded) { qDebug("Loading geoip database"); GeoIP::loadDatabase(s); geoipDBLoaded = true; } // Update torrent handles std::vector torrents = getTorrents(); std::vector::iterator torrentIT; for(torrentIT = torrents.begin(); torrentIT != torrents.end(); torrentIT++) { QTorrentHandle h = QTorrentHandle(*torrentIT); if(h.is_valid()) h.resolve_countries(resolve_countries); } } #endif // * UPnP if(Preferences::isUPnPEnabled()) { enableUPnP(true); addConsoleMessage(tr("UPnP support [ON]"), QString::fromUtf8("blue")); } else { enableUPnP(false); addConsoleMessage(tr("UPnP support [OFF]"), QString::fromUtf8("blue")); } // * NAT-PMP if(Preferences::isNATPMPEnabled()) { enableNATPMP(true); addConsoleMessage(tr("NAT-PMP support [ON]"), QString::fromUtf8("blue")); } else { enableNATPMP(false); addConsoleMessage(tr("NAT-PMP support [OFF]"), QString::fromUtf8("blue")); } // * Session settings session_settings sessionSettings; QString peer_id = Preferences::getPeerID(); if(peer_id.size() != 2) peer_id = "qB"; if(peer_id == "UT") { QString version = Preferences::getClientVersion().replace(".", ""); while(version.size() < 4) version.append("0"); const QString build = Preferences::getClientBuild(); sessionSettings.user_agent = QString("uTorrent/"+version+"("+build+")").toStdString(); } else { if(peer_id == "AZ") { QStringList version = Preferences::getClientVersion().split("."); while(version.size() < 4) version << "0"; sessionSettings.user_agent = QString("Azureus "+version.join(".")).toStdString(); } else { if(peer_id == "KT") { sessionSettings.user_agent = QString("KTorrent/"+Preferences::getClientVersion()).toStdString(); } else { sessionSettings.user_agent = "qBittorrent "VERSION; } } } std::cout << "HTTP user agent is " << sessionSettings.user_agent << std::endl; addConsoleMessage(tr("HTTP user agent is %1").arg(misc::toQString(sessionSettings.user_agent))); sessionSettings.upnp_ignore_nonrouters = true; sessionSettings.use_dht_as_fallback = false; // 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 #if LIBTORRENT_VERSION_MINOR > 14 sessionSettings.announce_to_all_trackers = true; sessionSettings.announce_to_all_tiers = true; //uTorrent behavior sessionSettings.auto_scrape_min_interval = 900; // 15 minutes #endif // To keep same behavior as in qBittorrent v1.2.0 sessionSettings.rate_limit_ip_overhead = false; sessionSettings.cache_size = Preferences::diskCacheSize()*64; addConsoleMessage(tr("Using a disk cache size of %1 MiB").arg(Preferences::diskCacheSize())); // Queueing System if(Preferences::isQueueingSystemEnabled()) { sessionSettings.active_downloads = Preferences::getMaxActiveDownloads(); sessionSettings.active_seeds = Preferences::getMaxActiveUploads(); sessionSettings.active_limit = Preferences::getMaxActiveTorrents(); sessionSettings.dont_count_slow_torrents = false; setQueueingEnabled(true); } else { sessionSettings.active_downloads = -1; sessionSettings.active_seeds = -1; sessionSettings.active_limit = -1; setQueueingEnabled(false); } // Outgoing ports sessionSettings.outgoing_ports = std::make_pair(Preferences::outgoingPortsMin(), Preferences::outgoingPortsMax()); setSessionSettings(sessionSettings); // Ignore limits on LAN sessionSettings.ignore_limits_on_local_network = Preferences::ignoreLimitsOnLAN(); // Include overhead in transfer limits sessionSettings.rate_limit_ip_overhead = Preferences::includeOverheadInLimits(); // Bittorrent // * Max Half-open connections s->set_max_half_open_connections(Preferences::getMaxHalfOpenConnections()); // * Max connections limit setMaxConnections(Preferences::getMaxConnecs()); // * Max connections per torrent limit setMaxConnectionsPerTorrent(Preferences::getMaxConnecsPerTorrent()); // * Max uploads per torrent limit setMaxUploadsPerTorrent(Preferences::getMaxUploadsPerTorrent()); // * DHT if(Preferences::isDHTEnabled()) { // Set DHT Port if(enableDHT(true)) { int dht_port; if(Preferences::isDHTPortSameAsBT()) dht_port = 0; else dht_port = Preferences::getDHTPort(); setDHTPort(dht_port); if(dht_port == 0) dht_port = new_listenPort; addConsoleMessage(tr("DHT support [ON], port: UDP/%1").arg(dht_port), QString::fromUtf8("blue")); } else { addConsoleMessage(tr("DHT support [OFF]"), QString::fromUtf8("red")); } } else { enableDHT(false); addConsoleMessage(tr("DHT support [OFF]"), QString::fromUtf8("blue")); } // * PeX if(PeXEnabled) { addConsoleMessage(tr("PeX support [ON]"), QString::fromUtf8("blue")); } else { addConsoleMessage(tr("PeX support [OFF]"), QString::fromUtf8("red")); } if(PeXEnabled != Preferences::isPeXEnabled()) { addConsoleMessage(tr("Restart is required to toggle PeX support"), QString::fromUtf8("red")); } // * LSD if(Preferences::isLSDEnabled()) { enableLSD(true); addConsoleMessage(tr("Local Peer Discovery [ON]"), QString::fromUtf8("blue")); } else { enableLSD(false); addConsoleMessage(tr("Local Peer Discovery support [OFF]"), QString::fromUtf8("blue")); } // * Encryption const int encryptionState = Preferences::getEncryptionSetting(); // The most secure, rc4 only so that all streams and encrypted pe_settings encryptionSettings; encryptionSettings.allowed_enc_level = pe_settings::rc4; encryptionSettings.prefer_rc4 = true; switch(encryptionState) { case 0: //Enabled encryptionSettings.out_enc_policy = pe_settings::enabled; encryptionSettings.in_enc_policy = pe_settings::enabled; addConsoleMessage(tr("Encryption support [ON]"), QString::fromUtf8("blue")); break; case 1: // Forced encryptionSettings.out_enc_policy = pe_settings::forced; encryptionSettings.in_enc_policy = pe_settings::forced; addConsoleMessage(tr("Encryption support [FORCED]"), QString::fromUtf8("blue")); break; default: // Disabled encryptionSettings.out_enc_policy = pe_settings::disabled; encryptionSettings.in_enc_policy = pe_settings::disabled; addConsoleMessage(tr("Encryption support [OFF]"), QString::fromUtf8("blue")); } applyEncryptionSettings(encryptionSettings); // * Maximum ratio high_ratio_action = Preferences::getMaxRatioAction(); setMaxRatio(Preferences::getMaxRatio()); // Ip Filter FilterParserThread::processFilterList(s, Preferences::bannedIPs()); if(Preferences::isFilteringEnabled()) { enableIPFilter(Preferences::getFilter()); }else{ disableIPFilter(); } // Update Web UI if (Preferences::isWebUiEnabled()) { const quint16 port = Preferences::getWebUiPort(); const QString username = Preferences::getWebUiUsername(); const QString password = Preferences::getWebUiPassword(); initWebUi(username, password, port); } else if(httpServer) { delete httpServer; } // * Proxy settings proxy_settings proxySettings; if(Preferences::isPeerProxyEnabled()) { qDebug("Enabling P2P proxy"); proxySettings.hostname = Preferences::getPeerProxyIp().toStdString(); qDebug("hostname is %s", proxySettings.hostname.c_str()); proxySettings.port = Preferences::getPeerProxyPort(); qDebug("port is %d", proxySettings.port); if(Preferences::isPeerProxyAuthEnabled()) { proxySettings.username = Preferences::getPeerProxyUsername().toStdString(); proxySettings.password = Preferences::getPeerProxyPassword().toStdString(); qDebug("username is %s", proxySettings.username.c_str()); qDebug("password is %s", proxySettings.password.c_str()); } } switch(Preferences::getPeerProxyType()) { case HTTP: qDebug("type: http"); proxySettings.type = proxy_settings::http; break; case HTTP_PW: qDebug("type: http_pw"); proxySettings.type = proxy_settings::http_pw; break; case SOCKS4: proxySettings.type = proxy_settings::socks4; case SOCKS5: qDebug("type: socks5"); proxySettings.type = proxy_settings::socks5; break; case SOCKS5_PW: qDebug("type: socks5_pw"); proxySettings.type = proxy_settings::socks5_pw; break; default: proxySettings.type = proxy_settings::none; } setPeerProxySettings(proxySettings); // HTTP Proxy proxy_settings http_proxySettings; qDebug("HTTP Communications proxy type: %d", Preferences::getHTTPProxyType()); switch(Preferences::getHTTPProxyType()) { case HTTP_PW: http_proxySettings.type = proxy_settings::http_pw; http_proxySettings.username = Preferences::getHTTPProxyUsername().toStdString(); http_proxySettings.password = Preferences::getHTTPProxyPassword().toStdString(); http_proxySettings.hostname = Preferences::getHTTPProxyIp().toStdString(); http_proxySettings.port = Preferences::getHTTPProxyPort(); break; case HTTP: http_proxySettings.type = proxy_settings::http; http_proxySettings.hostname = Preferences::getHTTPProxyIp().toStdString(); http_proxySettings.port = Preferences::getHTTPProxyPort(); break; case SOCKS5: http_proxySettings.type = proxy_settings::socks5; http_proxySettings.hostname = Preferences::getHTTPProxyIp().toStdString(); http_proxySettings.port = Preferences::getHTTPProxyPort(); break; case SOCKS5_PW: http_proxySettings.type = proxy_settings::socks5_pw; http_proxySettings.username = Preferences::getHTTPProxyUsername().toStdString(); http_proxySettings.password = Preferences::getHTTPProxyPassword().toStdString(); http_proxySettings.hostname = Preferences::getHTTPProxyIp().toStdString(); http_proxySettings.port = Preferences::getHTTPProxyPort(); break; default: http_proxySettings.type = proxy_settings::none; } setHTTPProxySettings(http_proxySettings); qDebug("Session configured"); } bool Bittorrent::initWebUi(QString username, QString password, int port) { if(httpServer) { if(httpServer->serverPort() != port) { httpServer->close(); } } else { httpServer = new HttpServer(this, 3000, this); } httpServer->setAuthorization(username, password); bool success = true; if(!httpServer->isListening()) { success = httpServer->listen(QHostAddress::Any, port); if (success) addConsoleMessage(tr("The Web UI is listening on port %1").arg(port)); else addConsoleMessage(tr("Web User Interface Error - Unable to bind Web UI to port %1").arg(port), "red"); } return success; } void Bittorrent::useAlternativeSpeedsLimit(bool alternative) { // Save new state to remember it on startup Preferences::setAltBandwidthEnabled(alternative); // Apply settings to the bittorrent session if(alternative) { s->set_download_rate_limit(Preferences::getAltGlobalDownloadLimit()*1024); s->set_upload_rate_limit(Preferences::getAltGlobalUploadLimit()*1024); } else { s->set_download_rate_limit(Preferences::getGlobalDownloadLimit()*1024); s->set_upload_rate_limit(Preferences::getGlobalUploadLimit()*1024); } emit alternativeSpeedsModeChanged(alternative); } void Bittorrent::takeETASamples() { bool change = false;; foreach(const QString &hash, ETA_samples.keys()) { const QTorrentHandle &h = getTorrentHandle(hash); if(h.is_valid() && !h.is_paused() && !h.is_seed()) { QList samples = ETA_samples.value(h.hash(), QList()); if(samples.size() >= MAX_SAMPLES) samples.removeFirst(); samples.append(h.download_payload_rate()); ETA_samples[h.hash()] = samples; change = true; } else { ETA_samples.remove(hash); } } if(!change && timerETA) { delete timerETA; } } // This algorithm was inspired from KTorrent - http://www.ktorrent.org // Calculate the ETA using a combination of several algorithms: // GASA: Global Average Speed Algorithm // CSA: Current Speed Algorithm // WINX: Window of X Algorithm qlonglong Bittorrent::getETA(QString hash) { const QTorrentHandle &h = getTorrentHandle(hash); if(!h.is_valid() || h.state() != torrent_status::downloading || !h.active_time()) return -1; // See if the torrent is going to be completed soon const qulonglong bytes_left = h.actual_size() - h.total_wanted_done(); if(h.actual_size() > 10485760L) { // Size > 10MiB if(h.progress() >= (float)0.99 && bytes_left < 10485760L) { // Progress>99% but less than 10MB left. // Compute by taking samples if(!ETA_samples.contains(h.hash())) { ETA_samples[h.hash()] = QList(); } if(!timerETA) { timerETA = new QTimer(this); connect(timerETA, SIGNAL(timeout()), this, SLOT(takeETASamples())); timerETA->start(); } else { const QList &samples = ETA_samples.value(h.hash(), QList()); const int nb_samples = samples.size(); if(nb_samples > 3) { long sum_samples = 0; foreach(const int val, samples) { sum_samples += val; } // Use WINX return (qlonglong)(((double)bytes_left) / (((double)sum_samples) / ((double)nb_samples))); } } } } // Normal case: Use GASA double avg_speed = (double)h.all_time_download() / h.active_time(); return (qlonglong) floor((double) (bytes_left) / avg_speed); } std::vector Bittorrent::getTorrents() const { return s->get_torrents(); } // Return the torrent handle, given its hash QTorrentHandle Bittorrent::getTorrentHandle(QString hash) const{ return QTorrentHandle(s->find_torrent(misc::QStringToSha1(hash))); } bool Bittorrent::hasActiveTorrents() const { std::vector torrents = getTorrents(); std::vector::iterator torrentIT; for(torrentIT = torrents.begin(); torrentIT != torrents.end(); torrentIT++) { const QTorrentHandle h(*torrentIT); if(h.is_valid() && !h.is_paused() && !h.is_queued()) return true; } return false; } void Bittorrent::banIP(QString ip) { FilterParserThread::processFilterList(s, QStringList(ip)); Preferences::banIP(ip); } // Delete a torrent from the session, given its hash // permanent = true means that the torrent will be removed from the hard-drive too void Bittorrent::deleteTorrent(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; } const QString &fileName = h.name(); // Remove it from session if(delete_local_files) s->remove_torrent(h.get_torrent_handle(), session::delete_files); else s->remove_torrent(h.get_torrent_handle()); // Remove it from torrent backup directory QDir torrentBackup(misc::BTBackupLocation()); QStringList filters; filters << hash+".*"; const QStringList &files = torrentBackup.entryList(filters, QDir::Files, QDir::Unsorted); foreach(const QString &file, files) { torrentBackup.remove(file); } TorrentPersistentData::deletePersistentData(hash); // Remove tracker errors trackersInfos.remove(hash); if(delete_local_files) addConsoleMessage(tr("'%1' was removed from transfer list and hard disk.", "'xxx.avi' was removed...").arg(fileName)); else addConsoleMessage(tr("'%1' was removed from transfer list.", "'xxx.avi' was removed...").arg(fileName)); emit deletedTorrent(hash); } void Bittorrent::pauseAllTorrents() { std::vector torrents = getTorrents(); std::vector::iterator torrentIT; for(torrentIT = torrents.begin(); torrentIT != torrents.end(); torrentIT++) { QTorrentHandle h = QTorrentHandle(*torrentIT); if(!h.is_valid()) continue; if(!h.is_paused()) { h.pause(); emit pausedTorrent(h); } } } void Bittorrent::resumeAllTorrents() { std::vector torrents = getTorrents(); std::vector::iterator torrentIT; for(torrentIT = torrents.begin(); torrentIT != torrents.end(); torrentIT++) { QTorrentHandle h = QTorrentHandle(*torrentIT); if(!h.is_valid()) continue; if(h.is_paused()) { h.resume(); emit resumedTorrent(h); } } } void Bittorrent::pauseTorrent(QString hash) { QTorrentHandle h = getTorrentHandle(hash); if(!h.is_paused()) { h.pause(); emit pausedTorrent(h); } } void Bittorrent::resumeTorrent(QString hash) { QTorrentHandle h = getTorrentHandle(hash); if(h.is_paused()) { h.resume(); emit resumedTorrent(h); } } QTorrentHandle Bittorrent::addMagnetUri(QString magnet_uri, bool resumed) { QTorrentHandle h; const QString &hash = misc::magnetUriToHash(magnet_uri); if(hash.isEmpty()) { addConsoleMessage(tr("'%1' is not a valid magnet URI.").arg(magnet_uri)); return h; } const QDir torrentBackup(misc::BTBackupLocation()); if(resumed) { qDebug("Resuming magnet URI: %s", qPrintable(hash)); // Load metadata if(QFile::exists(torrentBackup.path()+QDir::separator()+hash+QString(".torrent"))) return addTorrent(torrentBackup.path()+QDir::separator()+hash+QString(".torrent"), false, false, true); } else { qDebug("Adding new magnet URI"); } bool fastResume=false; Q_ASSERT(magnet_uri.startsWith("magnet:")); // Check if torrent is already in download list if(s->find_torrent(sha1_hash(hash.toLocal8Bit().constData())).is_valid()) { qDebug("/!\\ Torrent is already in download list"); // Update info Bar addConsoleMessage(tr("'%1' is already in download list.", "e.g: 'xxx.avi' is already in download list.").arg(magnet_uri)); return h; } add_torrent_params p; //Getting fast resume data if existing std::vector buf; if(resumed) { const QString fastresume_path = torrentBackup.path()+QDir::separator()+hash+QString(".fastresume"); qDebug("Trying to load fastresume data: %s", qPrintable(fastresume_path)); if (load_file(fastresume_path.toLocal8Bit().constData(), buf) == 0) { fastResume = true; p.resume_data = &buf; qDebug("Successfuly loaded"); } } QString torrent_name = misc::magnetUriToName(magnet_uri); const QString &savePath = getSavePath(hash, false, QString::null, torrent_name); if(!defaultTempPath.isEmpty() && resumed && !TorrentPersistentData::isSeed(hash)) { qDebug("addMagnetURI: Temp folder is enabled."); p.save_path = defaultTempPath.toLocal8Bit().constData(); qDebug("addMagnetURI: using save_path: %s", qPrintable(defaultTempPath)); } else { p.save_path = savePath.toLocal8Bit().constData(); qDebug("addMagnetURI: using save_path: %s", qPrintable(savePath)); } #if LIBTORRENT_VERSION_MINOR > 14 // Skip checking and directly start seeding (new in libtorrent v0.15) if(TorrentTempData::isSeedingMode(hash)){ p.seed_mode=true; } else { p.seed_mode=false; } #endif // Preallocate all? if(preAllocateAll) p.storage_mode = storage_mode_allocate; else p.storage_mode = storage_mode_sparse; // Start in pause p.paused = true; p.duplicate_is_error = false; // Already checked p.auto_managed = false; // Because it is added in paused state // Adding torrent to Bittorrent session try { h = QTorrentHandle(add_magnet_uri(*s, magnet_uri.toStdString(), 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); // If temp path is enabled, move torrent if(!defaultTempPath.isEmpty() && !resumed) { qDebug("Temp folder is enabled, moving new torrent to temp folder"); h.move_storage(defaultTempPath); } // Connections limit per torrent h.set_max_connections(Preferences::getMaxConnecsPerTorrent()); // Uploads limit per torrent h.set_max_uploads(Preferences::getMaxUploadsPerTorrent()); #ifndef DISABLE_GUI // Resolve countries h.resolve_countries(resolve_countries); #endif // Load filtered files if(!resumed) { // Sequential download if(TorrentTempData::hasTempData(hash)) { qDebug("addMagnetURI Setting download as sequential (from tmp data)"); h.set_sequential_download(TorrentTempData::isSequential(hash)); } const QString &label = TorrentTempData::getLabel(hash); // Save persistent data for new torrent TorrentPersistentData::saveTorrentPersistentData(h, true); // Save Label if(!label.isEmpty()) { TorrentPersistentData::saveLabel(hash, label); } // Save save_path if(!defaultTempPath.isEmpty()) { qDebug("addTorrent: Saving save_path in persistent data: %s", qPrintable(savePath)); TorrentPersistentData::saveSavePath(hash, savePath); } } if(!fastResume && (!addInPause || (Preferences::useAdditionDialog()))) { // Start torrent because it was added in paused state h.resume(); } // Send torrent addition signal if(fastResume) addConsoleMessage(tr("'%1' resumed. (fast resume)", "'/home/y/xxx.torrent' was resumed. (fast resume)").arg(magnet_uri)); else addConsoleMessage(tr("'%1' added to download list.", "'/home/y/xxx.torrent' was added to download list.").arg(magnet_uri)); emit addedTorrent(h); return h; } // Add a torrent to the Bittorrent session QTorrentHandle Bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url, bool resumed) { QTorrentHandle h; bool fastResume=false; const QDir torrentBackup(misc::BTBackupLocation()); QString hash; boost::intrusive_ptr t; #ifdef Q_WS_WIN // Windows hack if(!path.endsWith(".torrent")) { if(QFile::rename(path, path+".torrent")) path += ".torrent"; } qDebug("Downloading torrent at path: %s", qPrintable(path)); #endif // Checking if BT_backup Dir exists // create it if it is not if(! torrentBackup.exists()) { if(! torrentBackup.mkpath(torrentBackup.path())) { std::cerr << "Couldn't create the directory: '" << qPrintable(torrentBackup.path()) << "'\n"; exit(1); } } // Processing torrents const QString file = path.trimmed().replace("file://", "", Qt::CaseInsensitive); if(file.isEmpty()) { return h; } Q_ASSERT(!file.startsWith("http://", Qt::CaseInsensitive) && !file.startsWith("https://", Qt::CaseInsensitive) && !file.startsWith("ftp://", Qt::CaseInsensitive)); qDebug("Adding %s to download list", qPrintable(file)); try { // Getting torrent file informations t = new torrent_info(file.toUtf8().constData()); if(!t->is_valid()) throw std::exception(); } catch(std::exception&) { if(!from_url.isNull()) { addConsoleMessage(tr("Unable to decode torrent file: '%1'", "e.g: Unable to decode torrent file: '/home/y/xxx.torrent'").arg(from_url), QString::fromUtf8("red")); //emit invalidTorrent(from_url); QFile::remove(file); }else{ addConsoleMessage(tr("Unable to decode torrent file: '%1'", "e.g: Unable to decode torrent file: '/home/y/xxx.torrent'").arg(file), QString::fromUtf8("red")); //emit invalidTorrent(file); } addConsoleMessage(tr("This file is either corrupted or this isn't a torrent."),QString::fromUtf8("red")); if(fromScanDir) { // Remove file QFile::remove(file); } return h; } hash = misc::toQString(t->info_hash()); qDebug(" -> Hash: %s", qPrintable(hash)); qDebug(" -> Name: %s", t->name().c_str()); // Check if torrent is already in download list if(s->find_torrent(t->info_hash()).is_valid()) { qDebug("/!\\ Torrent is already in download list"); // Update info Bar if(!from_url.isNull()) { // If download from url, remove temp file QFile::remove(file); addConsoleMessage(tr("'%1' is already in download list.", "e.g: 'xxx.avi' is already in download list.").arg(from_url)); //emit duplicateTorrent(from_url); }else{ addConsoleMessage(tr("'%1' is already in download list.", "e.g: 'xxx.avi' is already in download list.").arg(file)); //emit duplicateTorrent(file); } // Check if the torrent contains trackers or url seeds we don't know about // and add them QTorrentHandle h_ex = getTorrentHandle(hash); if(h_ex.is_valid()) { std::vector old_trackers = h_ex.trackers(); std::vector new_trackers = t->trackers(); bool trackers_added = false; for(std::vector::iterator it=new_trackers.begin();it!=new_trackers.end();it++) { std::string tracker_url = it->url; bool found = false; for(std::vector::iterator itold=old_trackers.begin();itold!=old_trackers.end();itold++) { if(tracker_url == itold->url) { found = true; break; } } if(found) { trackers_added = true; announce_entry entry(tracker_url); h_ex.add_tracker(entry); } } if(trackers_added) { addConsoleMessage(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->url_seeds(); for(std::vector::iterator it = new_urlseeds.begin(); it != new_urlseeds.end(); it++) { const QString &new_url = misc::toQString(it->c_str()); if(!old_urlseeds.contains(new_url)) { urlseeds_added = true; h_ex.add_url_seed(new_url); } } if(urlseeds_added) { addConsoleMessage(tr("Note: new URL seeds were added to the existing torrent.")); } } if(fromScanDir) { // Delete torrent from scan dir QFile::remove(file); } return h; } QString root_folder = misc::truncateRootFolder(t); add_torrent_params p; //Getting fast resume data if existing std::vector buf; if(resumed) { const QString fastresume_path = torrentBackup.path()+QDir::separator()+hash+QString(".fastresume"); qDebug("Trying to load fastresume data: %s", qPrintable(fastresume_path)); if (load_file(fastresume_path.toLocal8Bit().constData(), buf) == 0) { fastResume = true; p.resume_data = &buf; qDebug("Successfuly loaded"); } } QString savePath; if(!from_url.isEmpty() && savepath_fromurl.contains(QUrl::fromEncoded(from_url.toLocal8Bit()))) { // Enforcing the save path defined before URL download (from RSS for example) savePath = savepath_fromurl.take(QUrl::fromEncoded(from_url.toLocal8Bit())); } else { savePath = getSavePath(hash, fromScanDir, path, root_folder); } if(!defaultTempPath.isEmpty() && resumed && !TorrentPersistentData::isSeed(hash)) { qDebug("addTorrent::Temp folder is enabled."); p.save_path = defaultTempPath.toLocal8Bit().constData(); qDebug("addTorrent: using save_path: %s", qPrintable(defaultTempPath)); } else { p.save_path = savePath.toLocal8Bit().constData(); qDebug("addTorrent: using save_path: %s", qPrintable(savePath)); } #if LIBTORRENT_VERSION_MINOR > 14 // Skip checking and directly start seeding (new in libtorrent v0.15) if(TorrentTempData::isSeedingMode(hash)){ p.seed_mode=true; } else { p.seed_mode=false; } #endif p.ti = t; // Preallocate all? if(preAllocateAll) p.storage_mode = storage_mode_allocate; else p.storage_mode = storage_mode_sparse; // Start in pause p.paused = true; p.duplicate_is_error = false; // Already checked p.auto_managed = false; // Because it is added in paused state // 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"); // If download from url, remove temp file if(!from_url.isNull()) QFile::remove(file); return h; } // If temp path is enabled, move torrent if(!defaultTempPath.isEmpty() && !resumed) { qDebug("Temp folder is enabled, moving new torrent to temp folder"); h.move_storage(defaultTempPath); } // Connections limit per torrent h.set_max_connections(Preferences::getMaxConnecsPerTorrent()); // Uploads limit per torrent h.set_max_uploads(Preferences::getMaxUploadsPerTorrent()); #ifndef DISABLE_GUI // Resolve countries qDebug("AddTorrent: Resolve_countries: %d", (int)resolve_countries); h.resolve_countries(resolve_countries); #endif if(!resumed) { // Sequential download if(TorrentTempData::hasTempData(hash)) { qDebug("addTorrent: Setting download as sequential (from tmp data)"); h.prioritize_files(TorrentTempData::getFilesPriority(hash)); h.set_sequential_download(TorrentTempData::isSequential(hash)); // Import Files names from torrent addition dialog const QStringList &files_path = TorrentTempData::getFilesPath(hash); bool force_recheck = false; if(files_path.size() == h.num_files()) { for(int i=0; i 14 // Append .!qB to incomplete files if(appendqBExtension) appendqBextensionToTorrent(h, true); #endif // Backup torrent file const QString &newFile = torrentBackup.absoluteFilePath(hash + ".torrent"); if(file != newFile) { // Delete file from torrentBackup directory in case it exists because // QFile::copy() do not overwrite QFile::remove(newFile); // Copy it to torrentBackup directory QFile::copy(file, newFile); } // Copy the torrent file to the export folder if(torrentExport) { QDir exportPath(Preferences::getExportDir()); if(exportPath.exists() || exportPath.mkpath(exportPath.absolutePath())) { QString torrent_path = exportPath.absoluteFilePath(h.name()+".torrent"); if(QFile::exists(torrent_path) && misc::sameFiles(file, torrent_path)) { // Append hash to torrent name to make it unique torrent_path = exportPath.absoluteFilePath(h.name()+"-"+h.hash()+".torrent"); } QFile::copy(file, torrent_path); //h.save_torrent_file(torrent_path); } } } if(!fastResume && (!addInPause || (Preferences::useAdditionDialog() && !fromScanDir))) { // Start torrent because it was added in paused state h.resume(); } // If download from url, remove temp file if(!from_url.isNull()) QFile::remove(file); // Delete from scan dir to avoid trying to download it again if(fromScanDir) { QFile::remove(file); } // Send torrent addition signal if(!from_url.isNull()) { if(fastResume) addConsoleMessage(tr("'%1' resumed. (fast resume)", "'/home/y/xxx.torrent' was resumed. (fast resume)").arg(from_url)); else addConsoleMessage(tr("'%1' added to download list.", "'/home/y/xxx.torrent' was added to download list.").arg(from_url)); }else{ if(fastResume) addConsoleMessage(tr("'%1' resumed. (fast resume)", "'/home/y/xxx.torrent' was resumed. (fast resume)").arg(file)); else addConsoleMessage(tr("'%1' added to download list.", "'/home/y/xxx.torrent' was added to download list.").arg(file)); } emit addedTorrent(h); return h; } void Bittorrent::exportTorrentFiles(QString path) { Q_ASSERT(torrentExport); 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(misc::BTBackupLocation()); std::vector handles = s->get_torrents(); std::vector::iterator itr; for(itr=handles.begin(); itr != handles.end(); 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(!misc::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." <set_max_connections(maxConnec); } void Bittorrent::setMaxConnectionsPerTorrent(int max) { // Apply this to all session torrents std::vector handles = s->get_torrents(); unsigned int nbHandles = handles.size(); for(unsigned int i=0; i handles = s->get_torrents(); unsigned int nbHandles = handles.size(); for(unsigned int i=0; istart_upnp(); UPnPEnabled = true; } } else { if(UPnPEnabled) { qDebug("Disabling UPnP"); s->stop_upnp(); UPnPEnabled = false; } } } void Bittorrent::enableNATPMP(bool b) { if(b) { if(!NATPMPEnabled) { qDebug("Enabling NAT-PMP"); s->start_natpmp(); NATPMPEnabled = true; } } else { if(NATPMPEnabled) { qDebug("Disabling NAT-PMP"); s->stop_natpmp(); NATPMPEnabled = false; } } } void Bittorrent::enableLSD(bool b) { if(b) { if(!LSDEnabled) { qDebug("Enabling LSD"); s->start_lsd(); LSDEnabled = true; } } else { if(LSDEnabled) { qDebug("Disabling LSD"); s->stop_lsd(); LSDEnabled = false; } } } void Bittorrent::loadSessionState() { const QString state_path = misc::cacheLocation()+QDir::separator()+QString::fromUtf8("ses_state"); if(!QFile::exists(state_path)) return; if(QFile(state_path).size() == 0) { // Remove empty state file QFile::remove(state_path); return; } #if LIBTORRENT_VERSION_MINOR > 14 std::vector in; if (load_file(state_path.toLocal8Bit().constData(), in) == 0) { lazy_entry e; if (lazy_bdecode(&in[0], &in[0] + in.size(), e) == 0) s->load_state(e); } #else boost::filesystem::ifstream ses_state_file(state_path.toLocal8Bit().constData() , std::ios_base::binary); ses_state_file.unsetf(std::ios_base::skipws); s->load_state(bdecode( std::istream_iterator(ses_state_file) , std::istream_iterator())); #endif } void Bittorrent::saveSessionState() { qDebug("Saving session state to disk..."); const QString state_path = misc::cacheLocation()+QDir::separator()+QString::fromUtf8("ses_state"); #if LIBTORRENT_VERSION_MINOR > 14 entry session_state; s->save_state(session_state); std::vector out; bencode(std::back_inserter(out), session_state); file f; error_code ec; if (!f.open(state_path.toLocal8Bit().constData(), file::write_only, ec)) return; if (ec) return; file::iovec_t b = {&out[0], out.size()}; f.writev(0, &b, 1, ec); #else entry session_state = s->state(); boost::filesystem::ofstream out(state_path.toLocal8Bit().constData() , std::ios_base::binary); out.unsetf(std::ios_base::skipws); bencode(std::ostream_iterator(out), session_state); #endif } // Enable DHT bool Bittorrent::enableDHT(bool b) { if(b) { if(!DHTEnabled) { #if LIBTORRENT_VERSION_MINOR < 15 entry dht_state; const QString dht_state_path = misc::cacheLocation()+QDir::separator()+QString::fromUtf8("dht_state"); if(QFile::exists(dht_state_path)) { boost::filesystem::ifstream dht_state_file(dht_state_path.toLocal8Bit().constData(), std::ios_base::binary); dht_state_file.unsetf(std::ios_base::skipws); try{ dht_state = bdecode(std::istream_iterator(dht_state_file), std::istream_iterator()); }catch (std::exception&) {} } #endif try { #if LIBTORRENT_VERSION_MINOR > 14 s->start_dht(); #else s->start_dht(dht_state); #endif 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("router.bitcomet.com"), 6881)); DHTEnabled = true; qDebug("DHT enabled"); }catch(std::exception e) { qDebug("Could not enable DHT, reason: %s", e.what()); return false; } } } else { if(DHTEnabled) { DHTEnabled = false; s->stop_dht(); qDebug("DHT disabled"); } } return true; } float Bittorrent::getRealRatio(QString hash) const{ QTorrentHandle h = getTorrentHandle(hash); if(!h.is_valid()) { return 0.; } Q_ASSERT(h.total_done() >= 0); Q_ASSERT(h.all_time_upload() >= 0); if(h.total_done() == 0) { if(h.all_time_upload() == 0) return 0; return 101; } float ratio = (float)h.all_time_upload()/(float)h.total_done(); Q_ASSERT(ratio >= 0.); if(ratio > 100.) ratio = 100.; return ratio; } void Bittorrent::saveTempFastResumeData() { std::vector torrents = s->get_torrents(); std::vector::iterator torrentIT; for(torrentIT = torrents.begin(); torrentIT != torrents.end(); torrentIT++) { QTorrentHandle h = QTorrentHandle(*torrentIT); if(!h.is_valid() || !h.has_metadata() || h.is_seed() || h.is_paused()) continue; if(h.state() == torrent_status::checking_files || h.state() == torrent_status::queued_for_checking) continue; qDebug("Saving fastresume data for %s", qPrintable(h.name())); h.save_resume_data(); } } // Only save fast resume data for unfinished and unpaused torrents (Optimization) // Called periodically and on exit void Bittorrent::saveFastResumeData() { // Stop listening for alerts resumeDataTimer.stop(); timerAlerts->stop(); int num_resume_data = 0; // Pause session s->pause(); std::vector torrents = s->get_torrents(); std::vector::iterator torrentIT; for(torrentIT = torrents.begin(); torrentIT != torrents.end(); torrentIT++) { QTorrentHandle h = QTorrentHandle(*torrentIT); if(!h.is_valid() || !h.has_metadata()) continue; if(isQueueingEnabled()) TorrentPersistentData::savePriority(h); // 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) continue; h.save_resume_data(); ++num_resume_data; } while (num_resume_data > 0) { alert const* a = s->wait_for_alert(seconds(30)); if (a == 0) { std::cerr << " aborting with " << num_resume_data << " outstanding " "torrents to save resume data for" << std::endl; break; } // Saving fastresume data can fail save_resume_data_failed_alert const* rda = dynamic_cast(a); if (rda) { --num_resume_data; s->pop_alert(); try { // Remove torrent from session s->remove_torrent(rda->handle); }catch(libtorrent::libtorrent_exception){} continue; } save_resume_data_alert const* rd = dynamic_cast(a); if (!rd) { s->pop_alert(); continue; } // Saving fast resume data was successful --num_resume_data; if (!rd->resume_data) continue; QDir torrentBackup(misc::BTBackupLocation()); const QTorrentHandle h(rd->handle); if(!h.is_valid()) continue; // Remove old fastresume file if it exists QFile::remove(torrentBackup.path()+QDir::separator()+ h.hash() + ".fastresume"); const QString file = h.hash()+".fastresume"; boost::filesystem::ofstream out(fs::path(torrentBackup.path().toLocal8Bit().constData()) / file.toLocal8Bit().constData(), std::ios_base::binary); out.unsetf(std::ios_base::skipws); bencode(std::ostream_iterator(out), *rd->resume_data); // Remove torrent from session s->remove_torrent(rd->handle); s->pop_alert(); } } QStringList Bittorrent::getConsoleMessages() const { return consoleMessages; } QStringList Bittorrent::getPeerBanMessages() const { return peerBanMessages; } #ifdef DISABLE_GUI void Bittorrent::addConsoleMessage(QString msg, QString) { #else void Bittorrent::addConsoleMessage(QString msg, QColor color) { if(consoleMessages.size() > 100) { consoleMessages.removeFirst(); } #ifdef Q_WS_WIN msg = msg.replace("/", "\\"); #endif consoleMessages.append(QString::fromUtf8("")+ QDateTime::currentDateTime().toString(QString::fromUtf8("dd/MM/yyyy hh:mm:ss")) + QString::fromUtf8(" - ") + msg + QString::fromUtf8("")); #endif emit newConsoleMessage(QDateTime::currentDateTime().toString("dd/MM/yyyy hh:mm:ss") + " - " + msg); } void Bittorrent::addPeerBanMessage(QString ip, bool from_ipfilter) { if(peerBanMessages.size() > 100) { peerBanMessages.removeFirst(); } if(from_ipfilter) peerBanMessages.append(QString::fromUtf8("")+ QDateTime::currentDateTime().toString(QString::fromUtf8("dd/MM/yyyy hh:mm:ss")) + QString::fromUtf8(" - ")+tr("%1 was blocked due to your IP filter", "x.y.z.w was blocked").arg(ip)); else peerBanMessages.append(QString::fromUtf8("")+ QDateTime::currentDateTime().toString(QString::fromUtf8("dd/MM/yyyy hh:mm:ss")) + QString::fromUtf8(" - ")+tr("%1 was banned due to corrupt pieces", "x.y.z.w was banned").arg(ip)); } bool Bittorrent::isFilePreviewPossible(QString hash) const{ // See if there are supported files in the torrent const QTorrentHandle &h = getTorrentHandle(hash); if(!h.is_valid()) { qDebug("/!\\ Error: Invalid handle"); return false; } const unsigned int nbFiles = h.num_files(); for(unsigned int i=0; i torrents = getTorrents(); std::vector::iterator torrentIT; for(torrentIT = torrents.begin(); torrentIT != torrents.end(); torrentIT++) { QTorrentHandle h = QTorrentHandle(*torrentIT); if(!h.is_valid()) continue; h.move_storage(getSavePath(h.hash())); } } else { // Moving all downloading torrents to temporary save path std::vector torrents = getTorrents(); std::vector::iterator torrentIT; for(torrentIT = torrents.begin(); torrentIT != torrents.end(); torrentIT++) { QTorrentHandle h = QTorrentHandle(*torrentIT); if(!h.is_valid()) continue; if(!h.is_seed()) h.move_storage(temppath); } } defaultTempPath = temppath; } #if LIBTORRENT_VERSION_MINOR > 14 void Bittorrent::appendqBextensionToTorrent(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 = misc::toQStringU(h.get_torrent_info().file_at(i).path.string()); 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 = misc::toQStringU(h.get_torrent_info().file_at(i).path.string()); 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); } } } } #endif void Bittorrent::changeLabelInTorrentSavePath(QTorrentHandle h, QString old_label, QString new_label) { if(!h.is_valid()) return; if(!appendLabelToSavePath) return; QString old_save_path = TorrentPersistentData::getSavePath(h.hash()); if(!old_save_path.startsWith(defaultSavePath)) return; QString new_save_path = misc::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 Bittorrent::appendLabelToTorrentSavePath(QTorrentHandle h) { if(!h.is_valid()) return; const QString &label = TorrentPersistentData::getLabel(h.hash()); if(label.isEmpty()) return; // Current save path QString old_save_path = TorrentPersistentData::getSavePath(h.hash()); QString new_save_path = misc::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 Bittorrent::setAppendLabelToSavePath(bool append) { if(appendLabelToSavePath != append) { appendLabelToSavePath = !appendLabelToSavePath; if(appendLabelToSavePath) { // Move torrents storage to sub folder with label name std::vector torrents = getTorrents(); std::vector::iterator torrentIT; for(torrentIT = torrents.begin(); torrentIT != torrents.end(); torrentIT++) { QTorrentHandle h = QTorrentHandle(*torrentIT); appendLabelToTorrentSavePath(h); } } } } #if LIBTORRENT_VERSION_MINOR > 14 void Bittorrent::setAppendqBExtension(bool append) { if(appendqBExtension != append) { appendqBExtension = !appendqBExtension; // append or remove .!qB extension for incomplete files std::vector torrents = getTorrents(); std::vector::iterator torrentIT; for(torrentIT = torrents.begin(); torrentIT != torrents.end(); torrentIT++) { QTorrentHandle h = QTorrentHandle(*torrentIT); appendqBextensionToTorrent(h, appendqBExtension); } } } #endif // Set the ports range in which is chosen the port the Bittorrent // session will listen to void Bittorrent::setListeningPort(int port) { std::pair ports(port, port); const QString& iface_name = Preferences::getNetworkInterface(); if(iface_name.isEmpty()) { s->listen_on(ports); return; } QNetworkInterface network_iface = QNetworkInterface::interfaceFromName(iface_name); if(!network_iface.isValid()) { s->listen_on(ports); return; } QString ip = "127.0.0.1"; if(!network_iface.addressEntries().isEmpty()) { ip = network_iface.addressEntries().first().ip().toString(); } qDebug("Listening on interface %s with ip %s", qPrintable(iface_name), qPrintable(ip)); s->listen_on(ports, ip.toLocal8Bit().constData()); } // Set download rate limit // -1 to disable void Bittorrent::setDownloadRateLimit(long rate) { qDebug("Setting a global download rate limit at %ld", rate); s->set_download_rate_limit(rate); } session* Bittorrent::getSession() const{ return s; } // Set upload rate limit // -1 to disable void Bittorrent::setUploadRateLimit(long rate) { qDebug("set upload_limit to %fkb/s", rate/1024.); s->set_upload_rate_limit(rate); } // Torrents will a ratio superior to the given value will // be automatically deleted void Bittorrent::setMaxRatio(float ratio) { if(ratio <= 0) ratio = -1.; if(ratio_limit == -1 && ratio != -1) { Q_ASSERT(!BigRatioTimer); BigRatioTimer = new QTimer(this); connect(BigRatioTimer, SIGNAL(timeout()), this, SLOT(processBigRatios())); BigRatioTimer->start(5000); } else { if(ratio_limit != -1 && ratio == -1) { delete BigRatioTimer; } } if(ratio_limit != ratio) { ratio_limit = ratio; qDebug("* Set deleteRatio to %.1f", ratio_limit); processBigRatios(); } } // Set DHT port (>= 1 or 0 if same as BT) void Bittorrent::setDHTPort(int dht_port) { if(dht_port >= 0) { if(dht_port == current_dht_port) return; struct dht_settings DHTSettings; DHTSettings.service_port = dht_port; s->set_dht_settings(DHTSettings); current_dht_port = dht_port; qDebug("Set DHT Port to %d", dht_port); } } // Enable IP Filtering void Bittorrent::enableIPFilter(QString filter) { qDebug("Enabling IPFiler"); if(!filterParser) { filterParser = new FilterParserThread(this, s); } if(filterPath.isEmpty() || filterPath != filter) { filterPath = filter; filterParser->processFilterFile(filter); } } // Disable IP Filtering void Bittorrent::disableIPFilter() { qDebug("Disabling IPFilter"); s->set_ip_filter(ip_filter()); if(filterParser) { delete filterParser; } filterPath = ""; } // Set BT session settings (user_agent) void Bittorrent::setSessionSettings(const session_settings &sessionSettings) { qDebug("Set session settings"); s->set_settings(sessionSettings); } // Set Proxy void Bittorrent::setPeerProxySettings(const proxy_settings &proxySettings) { qDebug("Set Peer Proxy settings"); s->set_peer_proxy(proxySettings); s->set_dht_proxy(proxySettings); } void Bittorrent::setHTTPProxySettings(const proxy_settings &proxySettings) { s->set_tracker_proxy(proxySettings); s->set_web_seed_proxy(proxySettings); 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"); #ifdef Q_WS_WIN #ifdef MINGW putenv("http_proxy="); putenv("sock_proxy="); #else SetEnvironmentVariableA("http_proxy", ""); SetEnvironmentVariableA("sock_proxy", ""); #endif #else unsetenv("http_proxy"); unsetenv("sock_proxy"); #endif return; } // We need this for urllib in search engine plugins #ifdef Q_WS_WIN QString type_str; if(proxySettings.type == proxy_settings::socks5 || proxySettings.type == proxy_settings::socks5_pw) type_str = "sock_proxy"; else type_str = "http_proxy"; #ifdef MINGW QString tmp = type_str+"="+proxy_str; putenv(tmp.toLocal8Bit().constData()); #else SetEnvironmentVariableA(type_str.toLocal8Bit().constData(), proxy_str.toLocal8Bit().constData()); #endif #else qDebug("HTTP communications proxy string: %s", qPrintable(proxy_str)); if(proxySettings.type == proxy_settings::socks5 || proxySettings.type == proxy_settings::socks5_pw) setenv("sock_proxy", proxy_str.toLocal8Bit().constData(), 1); else setenv("http_proxy", proxy_str.toLocal8Bit().constData(), 1); #endif } void Bittorrent::recursiveTorrentDownload(const QTorrentHandle &h) { for(int i=0; i t = new torrent_info(torrent_fullpath.toUtf8().constData()); const QString &sub_hash = misc::toQString(t->info_hash()); // Passing the save path along to the sub torrent file TorrentTempData::setSavePath(sub_hash, h.save_path()); addTorrent(torrent_fullpath); } catch(std::exception&) { qDebug("Caught error loading torrent"); addConsoleMessage(tr("Unable to decode %1 torrent file.").arg(torrent_fullpath), QString::fromUtf8("red")); } } } } // Read alerts sent by the Bittorrent session void Bittorrent::readAlerts() { // look at session alerts and display some infos std::auto_ptr a = s->pop_alert(); while (a.get()) { if (torrent_finished_alert* p = dynamic_cast(a.get())) { QTorrentHandle h(p->handle); if(h.is_valid()) { emit finishedTorrent(h); const QString &hash = h.hash(); #if LIBTORRENT_VERSION_MINOR > 14 // Remove .!qB extension if necessary if(appendqBExtension) appendqBextensionToTorrent(h, false); #endif // 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) { h.move_storage(save_dir.path()); } } const bool was_already_seeded = TorrentPersistentData::isSeed(hash); 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 t = new torrent_info(torrent_fullpath.toUtf8().constData()); if(t->is_valid()) { qDebug("emitting recursiveTorrentDownloadPossible()"); emit recursiveTorrentDownloadPossible(h); break; } } catch(std::exception&) { qDebug("Caught error loading torrent"); addConsoleMessage(tr("Unable to decode %1 torrent file.").arg(torrent_fullpath), QString::fromUtf8("red")); } } } } // Recheck if the user asked to if(Preferences::recheckTorrentsOnCompletion() && !was_already_seeded) { // Remember finished state TorrentPersistentData::saveSeedStatus(h); h.force_recheck(); } else { // Remember finished state TorrentPersistentData::saveSeedStatus(h); } qDebug("Received finished alert for %s", qPrintable(h.name())); } } else if (save_resume_data_alert* p = dynamic_cast(a.get())) { const QDir torrentBackup(misc::BTBackupLocation()); const QTorrentHandle h(p->handle); if(h.is_valid()) { const QString file = h.hash()+".fastresume"; // Delete old fastresume file if necessary if(QFile::exists(file)) QFile::remove(file); qDebug("Saving fastresume data in %s", qPrintable(file)); if (p->resume_data) { boost::filesystem::ofstream out(fs::path(torrentBackup.path().toLocal8Bit().constData()) / file.toLocal8Bit().constData(), std::ios_base::binary); out.unsetf(std::ios_base::skipws); bencode(std::ostream_iterator(out), *p->resume_data); } } } else if (file_renamed_alert* p = dynamic_cast(a.get())) { QTorrentHandle h(p->handle); if(h.is_valid() && h.num_files() > 1) { // Check if folders were renamed QStringList old_path_parts = misc::toQStringU(h.get_torrent_info().orig_files().at(p->index).path.string()).split("/"); old_path_parts.removeLast(); QString old_path = old_path_parts.join("/"); QStringList new_path_parts = misc::toQStringU(p->name).split("/"); new_path_parts.removeLast(); if(old_path != 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 if (storage_moved_alert* p = dynamic_cast(a.get())) { QTorrentHandle h(p->handle); if(h.is_valid()) { // Attempt to remove old folder if empty const QString& old_save_path = TorrentPersistentData::getSavePath(h.hash()); const QString new_save_path = QString::fromLocal8Bit(p->path.c_str()); qDebug("Torrent moved from %s to %s", qPrintable(old_save_path), qPrintable(new_save_path)); qDebug("Attempting to remove %s", qPrintable(old_save_path)); if(old_save_path != defaultSavePath && old_save_path != defaultTempPath) QDir().rmdir(old_save_path); if(new_save_path != defaultTempPath) TorrentPersistentData::saveSavePath(h.hash(), new_save_path); emit savePathChanged(h); //h.force_recheck(); } } else if (metadata_received_alert* p = dynamic_cast(a.get())) { QTorrentHandle h(p->handle); if(h.is_valid()) { qDebug("Received metadata for %s", qPrintable(h.hash())); // Save metadata const QDir torrentBackup(misc::BTBackupLocation()); if(!QFile::exists(torrentBackup.path()+QDir::separator()+h.hash()+QString(".torrent"))) h.save_torrent_file(torrentBackup.path()+QDir::separator()+h.hash()+QString(".torrent")); // Copy the torrent file to the export folder if(torrentExport) { QDir exportPath(Preferences::getExportDir()); if(exportPath.exists() || exportPath.mkpath(exportPath.absolutePath())) { QString torrent_path = exportPath.absoluteFilePath(h.name()+".torrent"); bool duplicate = false; if(QFile::exists(torrent_path)) { // Append hash to torrent name to make it unique torrent_path = exportPath.absoluteFilePath(h.name()+"-"+h.hash()+".torrent"); duplicate = true; } h.save_torrent_file(torrent_path); if(duplicate) { // Remove duplicate file if indentical if(misc::sameFiles(exportPath.absoluteFilePath(h.name()+".torrent"), torrent_path)) { QFile::remove(torrent_path); } } } } #if LIBTORRENT_VERSION_MINOR > 14 // Append .!qB to incomplete files if(appendqBExtension) appendqBextensionToTorrent(h, true); #endif // Truncate root folder misc::truncateRootFolder(p->handle); emit metadataReceived(h); if(h.is_paused()) { // XXX: Unfortunately libtorrent-rasterbar does not send a torrent_paused_alert // and the torrent can be paused when metadata is received emit pausedTorrent(h); } } } else if (file_error_alert* p = dynamic_cast(a.get())) { QTorrentHandle h(p->handle); if(h.is_valid()) { h.auto_managed(false); std::cerr << "File Error: " << p->message().c_str() << std::endl; addConsoleMessage(tr("An I/O error occured, '%1' paused.").arg(h.name())); addConsoleMessage(tr("Reason: %1").arg(misc::toQString(p->message()))); if(h.is_valid()) { emit fullDiskError(h, misc::toQString(p->message())); //h.pause(); emit pausedTorrent(h); } } } #if LIBTORRENT_VERSION_MINOR > 14 else if (file_completed_alert* p = dynamic_cast(a.get())) { QTorrentHandle h(p->handle); if(appendqBExtension) { QString name = misc::toQStringU(h.get_torrent_info().file_at(p->index).path.string()); 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); } } } #endif else if (torrent_paused_alert* p = dynamic_cast(a.get())) { if(p->handle.is_valid()) { p->handle.save_resume_data(); } } else if (tracker_error_alert* p = dynamic_cast(a.get())) { // 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::toQString(p->msg); #if LIBTORRENT_VERSION_MINOR < 15 data.verified = false; ++data.fail_count; #endif trackers_data.insert(tracker_url, data); trackersInfos[h.hash()] = trackers_data; } else { emit trackerAuthenticationRequired(h); } } } else if (tracker_reply_alert* p = dynamic_cast(a.get())) { 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; #if LIBTORRENT_VERSION_MINOR < 15 data.fail_count = 0; data.verified = true; #endif trackers_data.insert(tracker_url, data); trackersInfos[h.hash()] = trackers_data; } } else if (tracker_warning_alert* p = dynamic_cast(a.get())) { 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::toQString(p->msg); // Store warning message #if LIBTORRENT_VERSION_MINOR < 15 data.verified = true; data.fail_count = 0; #endif 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()); } } else if (portmap_error_alert* p = dynamic_cast(a.get())) { addConsoleMessage(tr("UPnP/NAT-PMP: Port mapping failure, message: %1").arg(misc::toQString(p->message())), "red"); //emit UPnPError(QString(p->msg().c_str())); } else if (portmap_alert* p = dynamic_cast(a.get())) { qDebug("UPnP Success, msg: %s", p->message().c_str()); addConsoleMessage(tr("UPnP/NAT-PMP: Port mapping successful, message: %1").arg(misc::toQString(p->message())), "blue"); //emit UPnPSuccess(QString(p->msg().c_str())); } else if (peer_blocked_alert* p = dynamic_cast(a.get())) { addPeerBanMessage(QString(p->ip.to_string().c_str()), true); //emit peerBlocked(QString::fromUtf8(p->ip.to_string().c_str())); } else if (peer_ban_alert* p = dynamic_cast(a.get())) { addPeerBanMessage(QString(p->ip.address().to_string().c_str()), false); //emit peerBlocked(QString::fromUtf8(p->ip.to_string().c_str())); } else if (fastresume_rejected_alert* p = dynamic_cast(a.get())) { const QTorrentHandle h(p->handle); if(h.is_valid()){ qDebug("/!\\ Fast resume failed for %s, reason: %s", qPrintable(h.name()), p->message().c_str()); addConsoleMessage(tr("Fast resume data was rejected for torrent %1, checking again...").arg(h.name()), QString::fromUtf8("red")); addConsoleMessage(tr("Reason: %1").arg(misc::toQString(p->message()))); //emit fastResumeDataRejected(h.name()); } } else if (url_seed_alert* p = dynamic_cast(a.get())) { addConsoleMessage(tr("Url seed lookup failed for url: %1, message: %2").arg(misc::toQString(p->url)).arg(misc::toQString(p->message())), QString::fromUtf8("red")); //emit urlSeedProblem(QString::fromUtf8(p->url.c_str()), QString::fromUtf8(p->msg().c_str())); } else if (torrent_checked_alert* p = dynamic_cast(a.get())) { QTorrentHandle h(p->handle); if(h.is_valid()){ const QString &hash = h.hash(); qDebug("%s have just finished checking", qPrintable(hash)); // Save seed status TorrentPersistentData::saveSeedStatus(h); // Move to temp directory if necessary if(!h.is_seed() && !defaultTempPath.isEmpty()) { // Check if directory is different const QDir current_dir(h.save_path()); const QDir save_dir(getSavePath(h.hash())); if(current_dir == save_dir) { h.move_storage(defaultTempPath); } } emit torrentFinishedChecking(h); if(torrentsToPausedAfterChecking.contains(hash)) { torrentsToPausedAfterChecking.removeOne(hash); h.pause(); emit pausedTorrent(h); } } } a = s->pop_alert(); } } void Bittorrent::recheckTorrent(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 Bittorrent::getTrackersInfo(QString hash) const{ return trackersInfos.value(hash, QHash()); } int Bittorrent::getListenPort() const{ qDebug("LISTEN PORT: %d", s->listen_port()); return s->listen_port(); } session_status Bittorrent::getSessionStatus() const{ return s->status(); } QString Bittorrent::getSavePath(QString hash, bool fromScanDir, QString filePath, QString root_folder) { QString savePath; if(TorrentTempData::hasTempData(hash)) { savePath = TorrentTempData::getSavePath(hash); if(savePath.isEmpty()) { savePath = defaultSavePath; } if(appendLabelToSavePath) { qDebug("appendLabelToSavePath is true"); const QString &label = TorrentTempData::getLabel(hash); if(!label.isEmpty()) { savePath = misc::updateLabelInSavePath(defaultSavePath, savePath, "", label); } } qDebug("getSavePath, got save_path from temp data: %s", qPrintable(savePath)); } else { savePath = TorrentPersistentData::getSavePath(hash); qDebug("SavePath got from persistant data is %s", qPrintable(savePath)); bool append_root_folder = false; if(savePath.isEmpty()) { if(fromScanDir && m_scanFolders->downloadInTorrentFolder(filePath)) savePath = QFileInfo(filePath).dir().path(); else { savePath = defaultSavePath; append_root_folder = true; } } else { QSettings settings("qBittorrent", "qBittorrent"); if(!settings.value("ported_to_new_savepath_system", false).toBool()) { append_root_folder = true; } } if(!fromScanDir && appendLabelToSavePath) { const QString &label = TorrentPersistentData::getLabel(hash); if(!label.isEmpty()) { qDebug("Torrent label is %s", qPrintable(label)); savePath = misc::updateLabelInSavePath(defaultSavePath, savePath, "", label); } } if(append_root_folder && !root_folder.isEmpty()) { // Append torrent root folder to the save path if(!savePath.endsWith(QDir::separator())) savePath += QDir::separator(); savePath += root_folder; qDebug("Torrent root folder is %s", qPrintable(root_folder)); TorrentPersistentData::saveSavePath(hash, savePath); } qDebug("getSavePath, got save_path from persistent data: %s", qPrintable(savePath)); } // Clean path savePath = misc::expandPath(savePath); // Checking if savePath Dir exists // create it if it is not QDir saveDir(savePath); if(!saveDir.exists()) { if(!saveDir.mkpath(saveDir.absolutePath())) { std::cerr << "Couldn't create the save directory: " << qPrintable(saveDir.path()) << "\n"; // XXX: Do something else? } else { qDebug("Created save folder at %s", qPrintable(saveDir.path())); } } 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 Bittorrent::downloadFromUrl(QString url) { addConsoleMessage(tr("Downloading '%1', please wait...", "e.g: Downloading 'xxx.torrent', please wait...").arg(url) #ifndef DISABLE_GUI , QPalette::WindowText #endif ); //emit aboutToDownloadFromUrl(url); // Launch downloader thread downloader->downloadTorrentUrl(url); } void Bittorrent::downloadFromURLList(const QStringList& urls) { foreach(const QString &url, urls) { downloadFromUrl(url); } } void Bittorrent::addMagnetSkipAddDlg(QString uri) { addMagnetUri(uri, false); } void Bittorrent::downloadUrlAndSkipDialog(QString url, QString save_path) { //emit aboutToDownloadFromUrl(url); const QUrl &qurl = QUrl::fromEncoded(url.toLocal8Bit()); if(!save_path.isEmpty()) savepath_fromurl[qurl] = save_path; url_skippingDlg << qurl; // Launch downloader thread downloader->downloadUrl(url); } // Add to Bittorrent session the downloaded torrent file void Bittorrent::processDownloadedFile(QString url, QString file_path) { const int index = url_skippingDlg.indexOf(QUrl::fromEncoded(url.toLocal8Bit())); if(index < 0) { // Add file to torrent download list #ifdef Q_WS_WIN // Windows hack if(!file_path.endsWith(".torrent")) { Q_ASSERT(QFile::exists(file_path)); qDebug("Torrent name does not end with .torrent, fixing..."); 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); addTorrent(file_path, false, url, false); } } // Return current download rate for the BT // session. Payload means that it only take into // account "useful" part of the rate float Bittorrent::getPayloadDownloadRate() const{ const session_status &sessionStatus = s->status(); return sessionStatus.payload_download_rate; } // Return current upload rate for the BT // session. Payload means that it only take into // account "useful" part of the rate float Bittorrent::getPayloadUploadRate() const{ const session_status &sessionStatus = s->status(); return sessionStatus.payload_upload_rate; } #if LIBTORRENT_VERSION_MINOR < 15 // Save DHT entry to hard drive void Bittorrent::saveDHTEntry() { // Save DHT entry if(DHTEnabled) { try{ entry dht_state = s->dht_state(); const QString dht_path = misc::cacheLocation()+QDir::separator()+QString::fromUtf8("dht_state"); boost::filesystem::ofstream out(dht_path.toLocal8Bit().constData(), std::ios_base::binary); out.unsetf(std::ios_base::skipws); bencode(std::ostream_iterator(out), dht_state); qDebug("DHT entry saved"); }catch (std::exception& e) { std::cerr << e.what() << "\n"; } } } #endif void Bittorrent::applyEncryptionSettings(pe_settings se) { qDebug("Applying encryption settings"); s->set_pe_settings(se); } // Will fast resume torrents in // backup directory void Bittorrent::startUpTorrents() { qDebug("Resuming unfinished torrents"); const QDir torrentBackup(misc::BTBackupLocation()); const QStringList &known_torrents = TorrentPersistentData::knownTorrents(); // Safety measure because some people reported torrent loss since // we switch the v1.5 way of resuming torrents on startup QStringList filters; filters << "*.torrent"; const QStringList &torrents_on_hd = torrentBackup.entryList(filters, QDir::Files, QDir::Unsorted); foreach(QString hash, torrents_on_hd) { qDebug("found torrent with hash: %s on hard disk", qPrintable(hash)); hash.chop(8); // remove trailing .torrent if(!known_torrents.contains(hash)) { std::cerr << "ERROR Detected!!! Adding back torrent " << qPrintable(hash) << " which got lost for some reason." << std::endl; addTorrent(torrentBackup.path()+QDir::separator()+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) { QString filePath; if(TorrentPersistentData::isMagnet(hash)) { filePath = TorrentPersistentData::getMagnetUri(hash); } else { filePath = torrentBackup.path()+QDir::separator()+hash+".torrent"; } const int prio = TorrentPersistentData::getPriority(hash); torrent_queue.push(qMakePair(prio, hash)); } qDebug("Priority_queue size: %ld", (long)torrent_queue.size()); // Resume downloads while(!torrent_queue.empty()) { const QString hash = torrent_queue.top().second; torrent_queue.pop(); qDebug("Starting up torrent %s", qPrintable(hash)); if(TorrentPersistentData::isMagnet(hash)) { addMagnetUri(TorrentPersistentData::getMagnetUri(hash), true); } else { addTorrent(torrentBackup.path()+QDir::separator()+hash+".torrent", false, QString(), true); } } } else { // Resume downloads foreach(const QString &hash, known_torrents) { qDebug("Starting up torrent %s", qPrintable(hash)); if(TorrentPersistentData::isMagnet(hash)) addMagnetUri(TorrentPersistentData::getMagnetUri(hash), true); else addTorrent(torrentBackup.path()+QDir::separator()+hash+".torrent", false, QString(), true); } } QSettings settings("qBittorrent", "qBittorrent"); settings.setValue("ported_to_new_savepath_system", true); qDebug("Unfinished torrents resumed"); }