diff --git a/src/app/application.cpp b/src/app/application.cpp index 79e814c06..6aab748d0 100644 --- a/src/app/application.cpp +++ b/src/app/application.cpp @@ -32,8 +32,10 @@ #include #include #include +#include #ifndef DISABLE_GUI +#include "gui/guiiconprovider.h" #ifdef Q_OS_WIN #include #include @@ -46,26 +48,38 @@ #endif // Q_OS_MAC #include "mainwindow.h" #include "addnewtorrentdialog.h" +#include "shutdownconfirm.h" #else // DISABLE_GUI #include #endif // DISABLE_GUI #ifndef DISABLE_WEBUI -#include "../webui/webui.h" +#include "webui/webui.h" #endif #include "application.h" #include "core/logger.h" #include "core/preferences.h" -#include "qbtsession.h" -#include "core/torrentpersistentdata.h" +#include "core/misc.h" +#include "core/iconprovider.h" +#include "core/scanfoldersmodel.h" +#include "core/net/smtp.h" +#include "core/net/downloadmanager.h" +#include "core/bittorrent/session.h" +#include "core/bittorrent/torrenthandle.h" static const char PARAMS_SEPARATOR[] = "|"; Application::Application(const QString &id, int &argc, char **argv) : BaseApplication(id, argc, argv) , m_running(false) +#ifndef DISABLE_GUI + , m_shutdownAct(NO_SHUTDOWN) +#endif { + Logger::initInstance(); + Preferences::initInstance(); + #if defined(Q_OS_MACX) && !defined(DISABLE_GUI) if (QSysInfo::MacintoshVersion > QSysInfo::MV_10_8) { // fix Mac OS X 10.9 (mavericks) font issue @@ -85,6 +99,8 @@ Application::Application(const QString &id, int &argc, char **argv) connect(this, SIGNAL(messageReceived(const QString &)), SLOT(processMessage(const QString &))); connect(this, SIGNAL(aboutToQuit()), SLOT(cleanup())); + + Logger::instance()->addMessage(tr("qBittorrent %1 started", "qBittorrent v3.2.0alpha started").arg(VERSION)); } void Application::processMessage(const QString &message) @@ -98,6 +114,87 @@ void Application::processMessage(const QString &message) m_paramsQueue.append(params); } +void Application::sendNotificationEmail(BitTorrent::TorrentHandle *const torrent) +{ + // Prepare mail content + QString content = QObject::tr("Torrent name: %1").arg(torrent->name()) + "\n"; + content += QObject::tr("Torrent size: %1").arg(misc::friendlyUnit(torrent->wantedSize())) + "\n"; + content += QObject::tr("Save path: %1").arg(torrent->savePath()) + "\n\n"; + content += QObject::tr("The torrent was downloaded in %1.", + "The torrent was downloaded in 1 hour and 20 seconds") + .arg(misc::userFriendlyDuration(torrent->activeTime())) + "\n\n\n"; + content += QObject::tr("Thank you for using qBittorrent.") + "\n"; + + // Send the notification email + Net::Smtp *sender = new Net::Smtp; + sender->sendMail("notification@qbittorrent.org", + Preferences::instance()->getMailNotificationEmail(), + QObject::tr("[qBittorrent] %1 has finished downloading").arg(torrent->name()), + content); +} + +void Application::torrentFinished(BitTorrent::TorrentHandle *const torrent) +{ + Preferences *const pref = Preferences::instance(); + + // AutoRun program + if (pref->isAutoRunEnabled()) { + QString program = pref->getAutoRunProgram().trimmed(); + // Replace %f by torrent path + program.replace("%f", torrent->savePathParsed()); + // Replace %n by torrent name + program.replace("%n", torrent->name()); + QProcess::startDetached(program); + } + + // Mail notification + if (pref->isMailNotificationEnabled()) + sendNotificationEmail(torrent); +} + +void Application::allTorrentsFinished() +{ + Preferences *const pref = Preferences::instance(); + +#ifndef DISABLE_GUI + bool will_shutdown = (pref->shutdownWhenDownloadsComplete() + || pref->shutdownqBTWhenDownloadsComplete() + || pref->suspendWhenDownloadsComplete() + || pref->hibernateWhenDownloadsComplete()); + + // 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"); + exit(); + } +#endif // DISABLE_GUI +} + bool Application::sendParams(const QStringList ¶ms) { return sendMessage(params.join(QLatin1String(PARAMS_SEPARATOR))); @@ -114,45 +211,32 @@ void Application::processParams(const QStringList ¶ms) m_window->activate(); // show UI return; } - - const bool useTorrentAdditionDialog = Preferences::instance()->useAdditionDialog(); #endif foreach (QString param, params) { param = param.trimmed(); - if (misc::isUrl(param)) { - QBtSession::instance()->downloadFromUrl(param); - } - else { - if (param.startsWith("bc://bt/", Qt::CaseInsensitive)) { - qDebug("Converting bc link to magnet link"); - param = misc::bcLinkToMagnet(param); - } - - if (param.startsWith("magnet:", Qt::CaseInsensitive)) { #ifndef DISABLE_GUI - if (useTorrentAdditionDialog) - AddNewTorrentDialog::showMagnet(param, m_window); - else + if (Preferences::instance()->useAdditionDialog()) + AddNewTorrentDialog::show(param, m_window); + else #endif - QBtSession::instance()->addMagnetUri(param); - } - else { -#ifndef DISABLE_GUI - if (useTorrentAdditionDialog) - AddNewTorrentDialog::showTorrent(param, QString(), m_window); - else -#endif - QBtSession::instance()->addTorrent(param); - } - } + BitTorrent::Session::instance()->addTorrent(param); } } int Application::exec(const QStringList ¶ms) { - // Resume unfinished torrents - QBtSession::instance()->startUpTorrents(); + Net::DownloadManager::initInstance(); +#ifdef DISABLE_GUI + IconProvider::initInstance(); +#else + GuiIconProvider::initInstance(); +#endif + BitTorrent::Session::initInstance(); + connect(BitTorrent::Session::instance(), SIGNAL(torrentFinished(BitTorrent::TorrentHandle *const)), SLOT(torrentFinished(BitTorrent::TorrentHandle *const))); + connect(BitTorrent::Session::instance(), SIGNAL(allTorrentsFinished()), SLOT(allTorrentsFinished())); + + ScanFoldersModel::initInstance(this); #ifndef DISABLE_WEBUI m_webui = new WebUI; @@ -293,6 +377,8 @@ void Application::initializeTranslation() #if (!defined(DISABLE_GUI) && defined(Q_OS_WIN)) void Application::shutdownCleanup(QSessionManager &manager) { + Q_UNUSED(manager); + // This is only needed for a special case on Windows XP. // (but is called for every Windows version) // If a process takes too much time to exit during OS @@ -356,10 +442,13 @@ void Application::cleanup() #ifndef DISABLE_WEBUI delete m_webui; #endif - QBtSession::drop(); - TorrentPersistentData::drop(); - Preferences::drop(); - Logger::drop(); + + ScanFoldersModel::freeInstance(); + BitTorrent::Session::freeInstance(); + Preferences::freeInstance(); + Logger::freeInstance(); + IconProvider::freeInstance(); + Net::DownloadManager::freeInstance(); #ifndef DISABLE_GUI #ifdef Q_OS_WIN typedef BOOL (WINAPI *PSHUTDOWNBRDESTROY)(HWND); @@ -369,6 +458,9 @@ void Application::cleanup() shutdownBRDestroy((HWND)m_window->effectiveWinId()); #endif // Q_OS_WIN delete m_window; + if (m_shutdownAct != NO_SHUTDOWN) { + qDebug() << "Sending computer shutdown/suspend/hibernate signal..."; + misc::shutdownComputer(m_shutdownAct); + } #endif - } diff --git a/src/app/application.h b/src/app/application.h index c0491a291..73e277de5 100644 --- a/src/app/application.h +++ b/src/app/application.h @@ -50,10 +50,17 @@ QT_END_NAMESPACE typedef QtSingleCoreApplication BaseApplication; #endif +#include "core/misc.h" + #ifndef DISABLE_WEBUI class WebUI; #endif +namespace BitTorrent +{ + class TorrentHandle; +} + class Application : public BaseApplication { Q_OBJECT @@ -77,6 +84,8 @@ protected: private slots: void processMessage(const QString &message); + void torrentFinished(BitTorrent::TorrentHandle *const torrent); + void allTorrentsFinished(); void cleanup(); #if (!defined(DISABLE_GUI) && defined(Q_OS_WIN)) void shutdownCleanup(QSessionManager &manager); @@ -87,6 +96,7 @@ private: #ifndef DISABLE_GUI QPointer m_window; + ShutDownAction m_shutdownAct; #endif #ifndef DISABLE_WEBUI @@ -99,6 +109,7 @@ private: void initializeTranslation(); void processParams(const QStringList ¶ms); + void sendNotificationEmail(BitTorrent::TorrentHandle *const torrent); }; #endif // APPLICATION_H diff --git a/src/app/main.cpp b/src/app/main.cpp index 03a010d9e..a9c9d2904 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -68,7 +68,6 @@ Q_IMPORT_PLUGIN(qico) #include "application.h" #include "core/misc.h" #include "core/preferences.h" -#include "core/logger.h" // Signal handlers #if defined(Q_OS_UNIX) || defined(STACKTRACE_WIN) @@ -120,8 +119,6 @@ QBtCommandLineParameters parseCommandLine(); // Main int main(int argc, char *argv[]) { - //Initialize logger singleton here to avoid threading issues - Logger::instance()->addMessage(QObject::tr("qBittorrent %1 started", "qBittorrent v3.2.0alpha started").arg(VERSION)); // We must save it here because QApplication constructor may change it bool isOneArg = (argc == 2); diff --git a/src/core/bittorrent/cachestatus.cpp b/src/core/bittorrent/cachestatus.cpp new file mode 100644 index 000000000..b7a7e960d --- /dev/null +++ b/src/core/bittorrent/cachestatus.cpp @@ -0,0 +1,64 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include "cachestatus.h" + +using namespace BitTorrent; + +CacheStatus::CacheStatus(const libtorrent::cache_status &nativeStatus) + : m_nativeStatus(nativeStatus) +{ +} + +int CacheStatus::totalUsedBuffers() const +{ + return m_nativeStatus.total_used_buffers; +} + +qreal CacheStatus::readRatio() const +{ + if (m_nativeStatus.blocks_read > 0) + return (static_cast(m_nativeStatus.blocks_read_hit) / m_nativeStatus.blocks_read); + else + return -1; +} + +int CacheStatus::jobQueueLength() const +{ + return m_nativeStatus.job_queue_length; +} + +int CacheStatus::averageJobTime() const +{ + return m_nativeStatus.average_job_time; +} + +qlonglong CacheStatus::queuedBytes() const +{ + return m_nativeStatus.queued_bytes; +} diff --git a/src/core/bittorrent/cachestatus.h b/src/core/bittorrent/cachestatus.h new file mode 100644 index 000000000..d42a7b4ae --- /dev/null +++ b/src/core/bittorrent/cachestatus.h @@ -0,0 +1,53 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * 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. + */ + +#ifndef BITTORRENT_CACHESTATUS_H +#define BITTORRENT_CACHESTATUS_H + +#include +#include + +namespace BitTorrent +{ + class CacheStatus + { + public: + CacheStatus(const libtorrent::cache_status &nativeStatus); + + int totalUsedBuffers() const; + qreal readRatio() const; + int jobQueueLength() const; + int averageJobTime() const; + qlonglong queuedBytes() const; + + private: + libtorrent::cache_status m_nativeStatus; + }; +} + +#endif // BITTORRENT_CACHESTATUS_H diff --git a/src/core/bittorrent/infohash.cpp b/src/core/bittorrent/infohash.cpp new file mode 100644 index 000000000..b492fcb44 --- /dev/null +++ b/src/core/bittorrent/infohash.cpp @@ -0,0 +1,98 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include +#include "infohash.h" + +using namespace BitTorrent; + +InfoHash::InfoHash() + : m_valid(false) +{ +} + +InfoHash::InfoHash(const libtorrent::sha1_hash &nativeHash) + : m_valid(true) + , m_nativeHash(nativeHash) +{ + char out[(libtorrent::sha1_hash::size * 2) + 1]; + libtorrent::to_hex((char const*)&m_nativeHash[0], libtorrent::sha1_hash::size, out); + m_hashString = QString(out); +} + +InfoHash::InfoHash(const QString &hashString) + : m_valid(false) + , m_hashString(hashString) +{ + QByteArray raw = m_hashString.toLatin1(); + if (raw.size() == 40) + m_valid = libtorrent::from_hex(raw.constData(), 40, (char*)&m_nativeHash[0]); +} + + +InfoHash::InfoHash(const InfoHash &other) + : m_valid(other.m_valid) + , m_nativeHash(other.m_nativeHash) + , m_hashString(other.m_hashString) +{ +} + + +bool InfoHash::isValid() const +{ + return m_valid; +} + + +InfoHash::operator libtorrent::sha1_hash() const +{ + return m_nativeHash; +} + + +InfoHash::operator QString() const +{ + return m_hashString; +} + + +bool InfoHash::operator==(const InfoHash &other) const +{ + return (m_nativeHash == other.m_nativeHash); +} + + +bool InfoHash::operator!=(const InfoHash &other) const +{ + return (m_nativeHash != other.m_nativeHash); +} + +uint qHash(const InfoHash &key, uint seed) +{ + return qHash(static_cast(key), seed); +} diff --git a/src/core/bittorrent/infohash.h b/src/core/bittorrent/infohash.h new file mode 100644 index 000000000..688736c15 --- /dev/null +++ b/src/core/bittorrent/infohash.h @@ -0,0 +1,67 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * 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. + */ + +#ifndef BITTORRENT_INFOHASH_H +#define BITTORRENT_INFOHASH_H + +#include +#if LIBTORRENT_VERSION_NUM < 10000 +#include +#else +#include +#endif + +#include + +namespace BitTorrent +{ + class InfoHash + { + public: + InfoHash(); + InfoHash(const libtorrent::sha1_hash &nativeHash); + InfoHash(const QString &hashString); + InfoHash(const InfoHash &other); + + bool isValid() const; + + operator libtorrent::sha1_hash() const; + operator QString() const; + bool operator==(const InfoHash &other) const; + bool operator!=(const InfoHash &other) const; + + private: + bool m_valid; + libtorrent::sha1_hash m_nativeHash; + QString m_hashString; + }; +} + +uint qHash(const BitTorrent::InfoHash &key, uint seed); + +#endif // BITTORRENT_INFOHASH_H diff --git a/src/core/bittorrent/magneturi.cpp b/src/core/bittorrent/magneturi.cpp new file mode 100644 index 000000000..6f8189f54 --- /dev/null +++ b/src/core/bittorrent/magneturi.cpp @@ -0,0 +1,95 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include +#include +#include + +#include "core/utils/string.h" +#include "magneturi.h" + +namespace libt = libtorrent; +using namespace BitTorrent; + +MagnetUri::MagnetUri(const QString &url) + : m_valid(false) + , m_url(url) +{ + if (url.isEmpty()) return; + + libt::error_code ec; + libt::parse_magnet_uri(url.toUtf8().constData(), m_addTorrentParams, ec); + if (ec) return; + + m_valid = true; + m_hash = m_addTorrentParams.info_hash; + m_name = String::fromStdString(m_addTorrentParams.name); + + foreach (const std::string &tracker, m_addTorrentParams.trackers) + m_trackers.append(String::fromStdString(tracker)); + +#if LIBTORRENT_VERSION_NUM >= 10000 + foreach (const std::string &urlSeed, m_addTorrentParams.url_seeds) + m_urlSeeds.append(QUrl(urlSeed.c_str())); +#endif +} + +bool MagnetUri::isValid() const +{ + return m_valid; +} + +InfoHash MagnetUri::hash() const +{ + return m_hash; +} + +QString MagnetUri::name() const +{ + return m_name; +} + +QList MagnetUri::trackers() const +{ + return m_trackers; +} + +QList MagnetUri::urlSeeds() const +{ + return m_urlSeeds; +} + +QString MagnetUri::url() const +{ + return m_url; +} + +libtorrent::add_torrent_params MagnetUri::addTorrentParams() const +{ + return m_addTorrentParams; +} diff --git a/src/core/bittorrent/magneturi.h b/src/core/bittorrent/magneturi.h new file mode 100644 index 000000000..b4b303753 --- /dev/null +++ b/src/core/bittorrent/magneturi.h @@ -0,0 +1,68 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * 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. + */ + +#ifndef BITTORRENT_MAGNETURI_H +#define BITTORRENT_MAGNETURI_H + +#include +#include +#include + +#include + +#include "infohash.h" +#include "trackerentry.h" + +namespace BitTorrent +{ + class MagnetUri + { + public: + explicit MagnetUri(const QString &url = QString()); + + bool isValid() const; + InfoHash hash() const; + QString name() const; + QList trackers() const; + QList urlSeeds() const; + QString url() const; + + libtorrent::add_torrent_params addTorrentParams() const; + + private: + bool m_valid; + QString m_url; + InfoHash m_hash; + QString m_name; + QList m_trackers; + QList m_urlSeeds; + libtorrent::add_torrent_params m_addTorrentParams; + }; +} + +#endif // BITTORRENT_MAGNETURI_H diff --git a/src/core/bittorrent/peerinfo.cpp b/src/core/bittorrent/peerinfo.cpp new file mode 100644 index 000000000..6c8653e7c --- /dev/null +++ b/src/core/bittorrent/peerinfo.cpp @@ -0,0 +1,271 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include + +#include "core/utils/string.h" +#include "peerinfo.h" + +namespace libt = libtorrent; +using namespace BitTorrent; + +// PeerAddress + +PeerAddress::PeerAddress() + : port(0) +{ +} + +PeerAddress::PeerAddress(QHostAddress ip, ushort port) + : ip(ip) + , port(port) +{ +} + +// PeerInfo + +PeerInfo::PeerInfo(const libt::peer_info &nativeInfo) + : m_nativeInfo(nativeInfo) +{ +} + +bool PeerInfo::fromDHT() const +{ + return (m_nativeInfo.source & libt::peer_info::dht); +} + +bool PeerInfo::fromPeX() const +{ + return (m_nativeInfo.source & libt::peer_info::pex); +} + +bool PeerInfo::fromLSD() const +{ + return (m_nativeInfo.source & libt::peer_info::lsd); +} + +QString PeerInfo::country() const +{ + return QString(QByteArray(m_nativeInfo.country, 2)); +} + + +bool PeerInfo::isInteresting() const +{ + return (m_nativeInfo.flags & libt::peer_info::interesting); +} + +bool PeerInfo::isChocked() const +{ + return (m_nativeInfo.flags & libt::peer_info::choked); +} + +bool PeerInfo::isRemoteInterested() const +{ + return (m_nativeInfo.flags & libt::peer_info::remote_interested); +} + +bool PeerInfo::isRemoteChocked() const +{ + return (m_nativeInfo.flags & libt::peer_info::remote_choked); +} + + +bool PeerInfo::isSupportsExtensions() const +{ + return (m_nativeInfo.flags & libt::peer_info::supports_extensions); +} + +bool PeerInfo::isLocalConnection() const +{ + return (m_nativeInfo.flags & libt::peer_info::local_connection); +} + +bool PeerInfo::isHandshake() const +{ + return (m_nativeInfo.flags & libt::peer_info::handshake); +} + + +bool PeerInfo::isConnecting() const +{ + return (m_nativeInfo.flags & libt::peer_info::connecting); +} + +bool PeerInfo::isQueued() const +{ + return (m_nativeInfo.flags & libt::peer_info::queued); +} + + +bool PeerInfo::isOnParole() const +{ + return (m_nativeInfo.flags & libt::peer_info::on_parole); +} + +bool PeerInfo::isSeed() const +{ + return (m_nativeInfo.flags & libt::peer_info::seed); +} + +bool PeerInfo::optimisticUnchoke() const +{ + return (m_nativeInfo.flags & libt::peer_info::optimistic_unchoke); +} + + +bool PeerInfo::isSnubbed() const +{ + return (m_nativeInfo.flags & libt::peer_info::snubbed); +} + +bool PeerInfo::isUploadOnly() const +{ + return (m_nativeInfo.flags & libt::peer_info::upload_only); +} + +bool PeerInfo::isEndgameMode() const +{ + return (m_nativeInfo.flags & libt::peer_info::endgame_mode); +} + + +bool PeerInfo::isHolepunched() const +{ + return (m_nativeInfo.flags & libt::peer_info::holepunched); +} + +bool PeerInfo::useI2PSocket() const +{ + return (m_nativeInfo.flags & libt::peer_info::i2p_socket); +} + +bool PeerInfo::useUTPSocket() const +{ +#if LIBTORRENT_VERSION_NUM < 10000 + return (m_nativeInfo.connection_type & libt::peer_info::bittorrent_utp); +#else + return (m_nativeInfo.flags & libt::peer_info::utp_socket); +#endif +} + +bool PeerInfo::useSSLSocket() const +{ +#if LIBTORRENT_VERSION_NUM < 10000 + return false; +#else + return (m_nativeInfo.flags & libt::peer_info::ssl_socket); +#endif +} + +bool PeerInfo::isRC4Encrypted() const +{ + return (m_nativeInfo.flags & libt::peer_info::rc4_encrypted); +} + +bool PeerInfo::isPlaintextEncrypted() const +{ + return (m_nativeInfo.flags & libt::peer_info::plaintext_encrypted); +} + + +PeerAddress PeerInfo::address() const +{ + return PeerAddress(QHostAddress(QString::fromStdString(m_nativeInfo.ip.address().to_string())), + m_nativeInfo.ip.port()); +} + +QString PeerInfo::client() const +{ + return String::fromStdString(m_nativeInfo.client); +} + + +qreal PeerInfo::progress() const +{ + return m_nativeInfo.progress; +} + +int PeerInfo::payloadUpSpeed() const +{ + return m_nativeInfo.payload_up_speed; +} + + +int PeerInfo::payloadDownSpeed() const +{ + return m_nativeInfo.payload_down_speed; +} + +qlonglong PeerInfo::totalUpload() const +{ + return m_nativeInfo.total_upload; +} + + +qlonglong PeerInfo::totalDownload() const +{ + return m_nativeInfo.total_download; +} + +QBitArray PeerInfo::pieces() const +{ + QBitArray result(m_nativeInfo.pieces.size()); + +#if LIBTORRENT_VERSION_NUM < 10000 + typedef size_t pieces_size_t; +#else + typedef int pieces_size_t; +#endif + for (pieces_size_t i = 0; i < m_nativeInfo.pieces.size(); ++i) + result.setBit(i, m_nativeInfo.pieces.get_bit(i)); + + return result; +} + +QString PeerInfo::connectionType() const +{ +#if LIBTORRENT_VERSION_NUM < 10000 + if (m_nativeInfo.connection_type & libt::peer_info::bittorrent_utp) +#else + if (m_nativeInfo.flags & libt::peer_info::utp_socket) +#endif + return QString::fromUtf8("μTP"); + + QString connection; + switch(m_nativeInfo.connection_type) { + case libt::peer_info::http_seed: + case libt::peer_info::web_seed: + connection = "Web"; + break; + default: + connection = "BT"; + } + + return connection; +} diff --git a/src/core/bittorrent/peerinfo.h b/src/core/bittorrent/peerinfo.h new file mode 100644 index 000000000..f04a2b9c8 --- /dev/null +++ b/src/core/bittorrent/peerinfo.h @@ -0,0 +1,99 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * 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. + */ + +#ifndef BITTORRENT_PEERINFO_H +#define BITTORRENT_PEERINFO_H + +#include + +#include +#include + +namespace BitTorrent +{ + struct PeerAddress + { + QHostAddress ip; + ushort port; + + PeerAddress(); + PeerAddress(QHostAddress ip, ushort port); + }; + + class PeerInfo + { + public: + PeerInfo(const libtorrent::peer_info &nativeInfo); + + bool fromDHT() const; + bool fromPeX() const; + bool fromLSD() const; + + bool isInteresting() const; + bool isChocked() const; + bool isRemoteInterested() const; + bool isRemoteChocked() const; + bool isSupportsExtensions() const; + bool isLocalConnection() const; + + bool isHandshake() const; + bool isConnecting() const; + bool isQueued() const; + bool isOnParole() const; + bool isSeed() const; + + bool optimisticUnchoke() const; + bool isSnubbed() const; + bool isUploadOnly() const; + bool isEndgameMode() const; + bool isHolepunched() const; + + bool useI2PSocket() const; + bool useUTPSocket() const; + bool useSSLSocket() const; + + bool isRC4Encrypted() const; + bool isPlaintextEncrypted() const; + + PeerAddress address() const; + QString client() const; + qreal progress() const; + int payloadUpSpeed() const; + int payloadDownSpeed() const; + qlonglong totalUpload() const; + qlonglong totalDownload() const; + QBitArray pieces() const; + QString connectionType() const; + QString country() const; + + private: + libtorrent::peer_info m_nativeInfo; + }; +} + +#endif // BITTORRENT_PEERINFO_H diff --git a/src/core/bittorrent/private/bandwidthscheduler.cpp b/src/core/bittorrent/private/bandwidthscheduler.cpp new file mode 100644 index 000000000..2a8c0e3a8 --- /dev/null +++ b/src/core/bittorrent/private/bandwidthscheduler.cpp @@ -0,0 +1,96 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2010 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 "core/preferences.h" +#include "bandwidthscheduler.h" + +BandwidthScheduler::BandwidthScheduler(QObject *parent) + : QTimer(parent) +{ + Q_ASSERT(Preferences::instance()->isSchedulerEnabled()); + // Single shot, we call start() again manually + setSingleShot(true); + // Connect Signals/Slots + connect(this, SIGNAL(timeout()), this, SLOT(start())); +} + +void BandwidthScheduler::start() +{ + const Preferences* const pref = Preferences::instance(); + Q_ASSERT(pref->isSchedulerEnabled()); + bool alt_bw_enabled = pref->isAltBandwidthEnabled(); + + QTime start = pref->getSchedulerStartTime(); + QTime end = pref->getSchedulerEndTime(); + QTime now = QTime::currentTime(); + int sched_days = pref->getSchedulerDays(); + int day = QDateTime::currentDateTime().toLocalTime().date().dayOfWeek(); + bool new_mode = false; + bool reverse = false; + + if (start > end) { + QTime temp = start; + start = end; + end = temp; + reverse = true; + } + + if ((start <= now) && (end >= now)) { + switch(sched_days) { + case EVERY_DAY: + new_mode = true; + break; + case WEEK_ENDS: + if ((day == 6) || (day == 7)) + new_mode = true; + break; + case WEEK_DAYS: + if ((day != 6) && (day != 7)) + new_mode = true; + break; + default: + if (day == (sched_days - 2)) + new_mode = true; + } + } + + if (reverse) + new_mode = !new_mode; + + if (new_mode != alt_bw_enabled) + emit switchToAlternativeMode(new_mode); + + // Timeout regularly to accomodate for external system clock changes + // eg from the user or from a timesync utility + QTimer::start(1500); +} diff --git a/src/core/bittorrent/private/bandwidthscheduler.h b/src/core/bittorrent/private/bandwidthscheduler.h new file mode 100644 index 000000000..5b68f82b6 --- /dev/null +++ b/src/core/bittorrent/private/bandwidthscheduler.h @@ -0,0 +1,50 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2010 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 BANDWIDTHSCHEDULER_H +#define BANDWIDTHSCHEDULER_H + +#include + +class BandwidthScheduler : public QTimer +{ + Q_OBJECT + +public: + BandwidthScheduler(QObject *parent = 0); + +public slots: + void start(); + +signals: + void switchToAlternativeMode(bool alternative); +}; + +#endif // BANDWIDTHSCHEDULER_H diff --git a/src/core/bittorrent/private/filterparserthread.cpp b/src/core/bittorrent/private/filterparserthread.cpp new file mode 100644 index 000000000..f7cee59dd --- /dev/null +++ b/src/core/bittorrent/private/filterparserthread.cpp @@ -0,0 +1,439 @@ +/* + * Bittorrent Client using Qt and libt. + * 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 "core/logger.h" +#include "filterparserthread.h" + +namespace libt = libtorrent; + +FilterParserThread::FilterParserThread(libt::session *s, QObject *parent) + : QThread(parent) + , m_session(s) + , m_abort(false) +{ +} + +FilterParserThread::~FilterParserThread() +{ + m_abort = true; + wait(); +} + +// Parser for eMule ip filter in DAT format +int FilterParserThread::parseDATFilterFile(QString m_filePath, libt::ip_filter &filter) +{ + int ruleCount = 0; + QFile file(m_filePath); + if (!file.exists()) return ruleCount; + + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + Logger::instance()->addMessage(tr("I/O Error: Could not open ip filer file in read mode."), Log::CRITICAL); + return ruleCount; + } + + unsigned int nbLine = 0; + while (!file.atEnd() && !m_abort) { + ++nbLine; + QByteArray line = file.readLine(); + // Ignoring empty lines + line = line.trimmed(); + if (line.isEmpty()) continue; + // Ignoring commented lines + if (line.startsWith('#') || line.startsWith("//")) continue; + + // Line should be splitted by commas + QList partsList = line.split(','); + const uint nbElem = partsList.size(); + + // IP Range should be splitted by a dash + QList IPs = partsList.first().split('-'); + if (IPs.size() != 2) { + qDebug("Ipfilter.dat: line %d is malformed.", nbLine); + qDebug("Line was %s", line.constData()); + continue; + } + + boost::system::error_code ec; + const QString strStartIP = cleanupIPAddress(IPs.at(0)); + if (strStartIP.isEmpty()) { + qDebug("Ipfilter.dat: line %d is malformed.", nbLine); + qDebug("Start IP of the range is malformated: %s", qPrintable(strStartIP)); + continue; + } + libt::address startAddr = libt::address::from_string(qPrintable(strStartIP), ec); + if (ec) { + qDebug("Ipfilter.dat: line %d is malformed.", nbLine); + qDebug("Start IP of the range is malformated: %s", qPrintable(strStartIP)); + continue; + } + + const QString strEndIP = cleanupIPAddress(IPs.at(1)); + if (strEndIP.isEmpty()) { + qDebug("Ipfilter.dat: line %d is malformed.", nbLine); + qDebug("End IP of the range is malformated: %s", qPrintable(strEndIP)); + continue; + } + + libt::address endAddr = libt::address::from_string(qPrintable(strEndIP), ec); + if (ec) { + qDebug("Ipfilter.dat: line %d is malformed.", nbLine); + qDebug("End IP of the range is malformated: %s", qPrintable(strEndIP)); + continue; + } + + if (startAddr.is_v4() != endAddr.is_v4()) { + qDebug("Ipfilter.dat: line %d is malformed.", nbLine); + qDebug("One IP is IPv4 and the other is IPv6!"); + continue; + } + + // Check if there is an access value (apparently not mandatory) + int nbAccess = 0; + if (nbElem > 1) { + // There is possibly one + nbAccess = partsList.at(1).trimmed().toInt(); + } + + if (nbAccess > 127) { + // Ignoring this rule because access value is too high + continue; + } + + // Now Add to the filter + try { + filter.add_rule(startAddr, endAddr, libt::ip_filter::blocked); + ++ruleCount; + } + catch(std::exception &) { + qDebug("Bad line in filter file, avoided crash..."); + } + } + + file.close(); + return ruleCount; +} + +// Parser for PeerGuardian ip filter in p2p format +int FilterParserThread::parseP2PFilterFile(QString m_filePath, libt::ip_filter &filter) +{ + int ruleCount = 0; + QFile file(m_filePath); + if (!file.exists()) return ruleCount; + + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + Logger::instance()->addMessage(tr("I/O Error: Could not open ip filer file in read mode."), Log::CRITICAL); + return ruleCount; + } + + unsigned int nbLine = 0; + while (!file.atEnd() && !m_abort) { + ++nbLine; + QByteArray line = file.readLine().trimmed(); + if (line.isEmpty()) continue; + // Ignoring commented lines + if (line.startsWith('#') || line.startsWith("//")) continue; + + // Line is splitted by : + QList partsList = line.split(':'); + if (partsList.size() < 2) { + qDebug("p2p file: line %d is malformed.", nbLine); + continue; + } + + // Get IP range + QList IPs = partsList.last().split('-'); + if (IPs.size() != 2) { + qDebug("p2p file: line %d is malformed.", nbLine); + qDebug("line was: %s", line.constData()); + continue; + } + + boost::system::error_code ec; + QString strStartIP = cleanupIPAddress(IPs.at(0)); + if (strStartIP.isEmpty()) { + qDebug("p2p file: line %d is malformed.", nbLine); + qDebug("Start IP is invalid: %s", qPrintable(strStartIP)); + continue; + } + + libt::address startAddr = libt::address::from_string(qPrintable(strStartIP), ec); + if (ec) { + qDebug("p2p file: line %d is malformed.", nbLine); + qDebug("Start IP is invalid: %s", qPrintable(strStartIP)); + continue; + } + + QString strEndIP = cleanupIPAddress(IPs.at(1)); + if (strEndIP.isEmpty()) { + qDebug("p2p file: line %d is malformed.", nbLine); + qDebug("End IP is invalid: %s", qPrintable(strStartIP)); + continue; + } + + libt::address endAddr = libt::address::from_string(qPrintable(strEndIP), ec); + if (ec) { + qDebug("p2p file: line %d is malformed.", nbLine); + qDebug("End IP is invalid: %s", qPrintable(strStartIP)); + continue; + } + + if (startAddr.is_v4() != endAddr.is_v4()) { + qDebug("p2p file: line %d is malformed.", nbLine); + qDebug("Line was: %s", line.constData()); + continue; + } + + try { + filter.add_rule(startAddr, endAddr, libt::ip_filter::blocked); + ++ruleCount; + } + catch(std::exception &) { + qDebug("p2p file: line %d is malformed.", nbLine); + qDebug("Line was: %s", line.constData()); + continue; + } + } + + file.close(); + return ruleCount; +} + +int FilterParserThread::getlineInStream(QDataStream &stream, std::string &name, char delim) +{ + char c; + int total_read = 0; + int read; + do { + read = stream.readRawData(&c, 1); + total_read += read; + if (read > 0) { + if (c != delim) { + name += c; + } + else { + // Delim found + return total_read; + } + } + } + while(read > 0); + + return total_read; +} + +// Parser for PeerGuardian ip filter in p2p format +int FilterParserThread::parseP2BFilterFile(QString m_filePath, libt::ip_filter &filter) +{ + int ruleCount = 0; + QFile file(m_filePath); + if (!file.exists()) return ruleCount; + + if (!file.open(QIODevice::ReadOnly)) { + Logger::instance()->addMessage(tr("I/O Error: Could not open ip filer file in read mode."), Log::CRITICAL); + return ruleCount; + } + + QDataStream stream(&file); + // Read header + char buf[7]; + unsigned char version; + if (!stream.readRawData(buf, sizeof(buf)) + || memcmp(buf, "\xFF\xFF\xFF\xFFP2B", 7) + || !stream.readRawData((char*)&version, sizeof(version))) { + Logger::instance()->addMessage(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL); + return ruleCount; + } + + if ((version == 1) || (version == 2)) { + qDebug ("p2b version 1 or 2"); + unsigned int start, end; + + std::string name; + while(getlineInStream(stream, name, '\0') && !m_abort) { + if (!stream.readRawData((char*)&start, sizeof(start)) + || !stream.readRawData((char*)&end, sizeof(end))) { + Logger::instance()->addMessage(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL); + return ruleCount; + } + + // Network byte order to Host byte order + // asio address_v4 contructor expects it + // that way + libt::address_v4 first(ntohl(start)); + libt::address_v4 last(ntohl(end)); + // Apply to bittorrent session + try { + filter.add_rule(first, last, libt::ip_filter::blocked); + ++ruleCount; + } + catch(std::exception &) {} + } + } + else if (version == 3) { + qDebug ("p2b version 3"); + unsigned int namecount; + if (!stream.readRawData((char*)&namecount, sizeof(namecount))) { + Logger::instance()->addMessage(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL); + return ruleCount; + } + + namecount = ntohl(namecount); + // Reading names although, we don't really care about them + for (unsigned int i = 0; i < namecount; ++i) { + std::string name; + if (!getlineInStream(stream, name, '\0')) { + Logger::instance()->addMessage(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL); + return ruleCount; + } + + if (m_abort) return ruleCount; + } + + // Reading the ranges + unsigned int rangecount; + if (!stream.readRawData((char*)&rangecount, sizeof(rangecount))) { + Logger::instance()->addMessage(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL); + return ruleCount; + } + + rangecount = ntohl(rangecount); + unsigned int name, start, end; + for (unsigned int i = 0; i < rangecount; ++i) { + if (!stream.readRawData((char*)&name, sizeof(name)) + || !stream.readRawData((char*)&start, sizeof(start)) + || !stream.readRawData((char*)&end, sizeof(end))) { + Logger::instance()->addMessage(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL); + return ruleCount; + } + + // Network byte order to Host byte order + // asio address_v4 contructor expects it + // that way + libt::address_v4 first(ntohl(start)); + libt::address_v4 last(ntohl(end)); + // Apply to bittorrent session + try { + filter.add_rule(first, last, libt::ip_filter::blocked); + ++ruleCount; + } + catch(std::exception &) {} + + if (m_abort) return ruleCount; + } + } + else { + Logger::instance()->addMessage(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL); + } + + file.close(); + return ruleCount; +} + +// Process ip filter file +// Supported formats: +// * eMule IP list (DAT): http://wiki.phoenixlabs.org/wiki/DAT_Format +// * PeerGuardian Text (P2P): http://wiki.phoenixlabs.org/wiki/P2P_Format +// * PeerGuardian Binary (P2B): http://wiki.phoenixlabs.org/wiki/P2B_Format +void FilterParserThread::processFilterFile(QString _filePath) +{ + if (isRunning()) { + // Already parsing a filter, m_abort first + m_abort = true; + wait(); + } + + m_abort = false; + m_filePath = _filePath; + // Run it + start(); +} + +void FilterParserThread::processFilterList(libt::session *s, const QStringList &IPs) +{ + // First, import current filter + libt::ip_filter filter = s->get_ip_filter(); + foreach (const QString &ip, IPs) { + qDebug("Manual ban of peer %s", ip.toLocal8Bit().constData()); + boost::system::error_code ec; + libt::address addr = libt::address::from_string(ip.toLocal8Bit().constData(), ec); + Q_ASSERT(!ec); + if (!ec) + filter.add_rule(addr, addr, libt::ip_filter::blocked); + } + + s->set_ip_filter(filter); +} + +QString FilterParserThread::cleanupIPAddress(QString _ip) +{ + QHostAddress ip(_ip.trimmed()); + if (ip.isNull()) return QString(); + + return ip.toString(); +} + +void FilterParserThread::run() +{ + qDebug("Processing filter file"); + libt::ip_filter filter = m_session->get_ip_filter(); + int ruleCount = 0; + if (m_filePath.endsWith(".p2p", Qt::CaseInsensitive)) { + // PeerGuardian p2p file + ruleCount = parseP2PFilterFile(m_filePath, filter); + } + else if (m_filePath.endsWith(".p2b", Qt::CaseInsensitive)) { + // PeerGuardian p2b file + ruleCount = parseP2BFilterFile(m_filePath, filter); + } + else if (m_filePath.endsWith(".dat", Qt::CaseInsensitive)) { + // eMule DAT format + ruleCount = parseDATFilterFile(m_filePath, filter); + } + + if (m_abort) return; + + try { + m_session->set_ip_filter(filter); + emit IPFilterParsed(ruleCount); + } + catch(std::exception &) { + emit IPFilterError(); + } + + qDebug("IP Filter thread: finished parsing, filter applied"); +} diff --git a/src/core/qtlibtorrent/filterparserthread.h b/src/core/bittorrent/private/filterparserthread.h similarity index 76% rename from src/core/qtlibtorrent/filterparserthread.h rename to src/core/bittorrent/private/filterparserthread.h index a95086540..b54520b0d 100644 --- a/src/core/qtlibtorrent/filterparserthread.h +++ b/src/core/bittorrent/private/filterparserthread.h @@ -1,5 +1,5 @@ /* - * Bittorrent Client using Qt4 and libtorrent. + * Bittorrent Client using Qt and libtorrent. * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -32,38 +32,30 @@ #define FILTERPARSERTHREAD_H #include -#include -#include -namespace libtorrent { -class session; -struct ip_filter; +class QDataStream; +class QStringList; + +namespace libtorrent +{ + class session; + struct ip_filter; } -using namespace std; - -// P2B Stuff -#include -#ifdef Q_OS_WIN -#include -#else -#include -#endif -// End of P2B stuff - -class FilterParserThread : public QThread { +class FilterParserThread : public QThread +{ Q_OBJECT public: - FilterParserThread(QObject* parent, libtorrent::session *s); + FilterParserThread(libtorrent::session *s, QObject *parent = 0); ~FilterParserThread(); - int parseDATFilterFile(QString filePath, libtorrent::ip_filter& filter); - int parseP2PFilterFile(QString filePath, libtorrent::ip_filter& filter); - int getlineInStream(QDataStream& stream, string& name, char delim); - int parseP2BFilterFile(QString filePath, libtorrent::ip_filter& filter); + int parseDATFilterFile(QString filePath, libtorrent::ip_filter &filter); + int parseP2PFilterFile(QString filePath, libtorrent::ip_filter &filter); + int getlineInStream(QDataStream &stream, std::string &name, char delim); + int parseP2BFilterFile(QString filePath, libtorrent::ip_filter &filter); void processFilterFile(QString _filePath); - static void processFilterList(libtorrent::session *s, const QStringList& IPs); + static void processFilterList(libtorrent::session *s, const QStringList &IPs); signals: void IPFilterParsed(int ruleCount); @@ -74,9 +66,9 @@ protected: void run(); private: - libtorrent::session *s; - bool abort; - QString filePath; + libtorrent::session *m_session; + bool m_abort; + QString m_filePath; }; -#endif +#endif // BITTORRENT_FILTERPARSERTHREAD_H diff --git a/src/core/bittorrent/private/sessionprivate.h b/src/core/bittorrent/private/sessionprivate.h new file mode 100644 index 000000000..38733c983 --- /dev/null +++ b/src/core/bittorrent/private/sessionprivate.h @@ -0,0 +1,83 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * 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. + */ + +#ifndef SESSIONPRIVATE_H +#define SESSIONPRIVATE_H + +class QString; +class QUrl; +template class QList; + +namespace libtorrent +{ + class entry; +} + +namespace BitTorrent +{ + class TorrentHandle; + class TrackerEntry; +} + +struct SessionPrivate +{ + virtual bool isQueueingEnabled() const = 0; + virtual bool isTempPathEnabled() const = 0; + virtual bool isAppendExtensionEnabled() const = 0; + virtual bool useAppendLabelToSavePath() const = 0; +#ifndef DISABLE_COUNTRIES_RESOLUTION + virtual bool isResolveCountriesEnabled() const = 0; +#endif + virtual QString defaultSavePath() const = 0; + virtual QString tempPath() const = 0; + virtual qreal globalMaxRatio() const = 0; + + virtual void handleTorrentRatioLimitChanged(BitTorrent::TorrentHandle *const torrent) = 0; + virtual void handleTorrentSavePathChanged(BitTorrent::TorrentHandle *const torrent) = 0; + virtual void handleTorrentMetadataReceived(BitTorrent::TorrentHandle *const torrent) = 0; + virtual void handleTorrentPaused(BitTorrent::TorrentHandle *const torrent) = 0; + virtual void handleTorrentResumed(BitTorrent::TorrentHandle *const torrent) = 0; + virtual void handleTorrentChecked(BitTorrent::TorrentHandle *const torrent) = 0; + virtual void handleTorrentFinished(BitTorrent::TorrentHandle *const torrent) = 0; + virtual void handleTorrentTrackersAdded(BitTorrent::TorrentHandle *const torrent, const QList &newTrackers) = 0; + virtual void handleTorrentTrackersRemoved(BitTorrent::TorrentHandle *const torrent, const QList &deletedTrackers) = 0; + virtual void handleTorrentTrackersChanged(BitTorrent::TorrentHandle *const torrent) = 0; + virtual void handleTorrentUrlSeedsAdded(BitTorrent::TorrentHandle *const torrent, const QList &newUrlSeeds) = 0; + virtual void handleTorrentUrlSeedsRemoved(BitTorrent::TorrentHandle *const torrent, const QList &urlSeeds) = 0; + virtual void handleTorrentResumeDataReady(BitTorrent::TorrentHandle *const torrent, const libtorrent::entry &data) = 0; + virtual void handleTorrentResumeDataFailed(BitTorrent::TorrentHandle *const torrent) = 0; + virtual void handleTorrentTrackerReply(BitTorrent::TorrentHandle *const torrent, const QString &trackerUrl) = 0; + virtual void handleTorrentTrackerWarning(BitTorrent::TorrentHandle *const torrent, const QString &trackerUrl) = 0; + virtual void handleTorrentTrackerError(BitTorrent::TorrentHandle *const torrent, const QString &trackerUrl) = 0; + virtual void handleTorrentTrackerAuthenticationRequired(BitTorrent::TorrentHandle *const torrent, const QString &trackerUrl) = 0; + +protected: + ~SessionPrivate() {} +}; + +#endif // SESSIONPRIVATE_H diff --git a/src/core/bittorrent/private/speedmonitor.cpp b/src/core/bittorrent/private/speedmonitor.cpp new file mode 100644 index 000000000..49ce65f58 --- /dev/null +++ b/src/core/bittorrent/private/speedmonitor.cpp @@ -0,0 +1,56 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * Copyright (C) 2011 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. + */ + +#include +#include "speedmonitor.h" + +void SpeedMonitor::addSample(const SpeedSample &sample) +{ + m_speedSamples.push_back(sample); + m_sum += sample; + if (m_speedSamples.size() > MAX_SAMPLES) { + m_sum -= m_speedSamples.front(); + m_speedSamples.pop_front(); + } +} + +SpeedSampleAvg SpeedMonitor::average() const +{ + if (m_speedSamples.empty()) + return SpeedSampleAvg(); + + qreal k = qreal(1.) / m_speedSamples.size(); + return SpeedSampleAvg(m_sum.download * k, m_sum.upload * k); +} + +void SpeedMonitor::reset() +{ + m_sum = SpeedSample(); + m_speedSamples.clear(); +} diff --git a/src/core/bittorrent/private/speedmonitor.h b/src/core/bittorrent/private/speedmonitor.h new file mode 100644 index 000000000..61cb33baa --- /dev/null +++ b/src/core/bittorrent/private/speedmonitor.h @@ -0,0 +1,84 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * Copyright (C) 2011 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. + */ + +#ifndef SPEEDMONITOR_H +#define SPEEDMONITOR_H + +template class QList; + +template +struct Sample +{ + Sample() + : download() + , upload() + { + } + + Sample(T dl, T ul) + : download(dl) + , upload(ul) + { + } + + Sample &operator+=(const Sample &other) + { + download += other.download; + upload += other.upload; + return *this; + } + + Sample &operator-=(const Sample &other) + { + download -= other.download; + upload -= other.upload; + return *this; + } + + T download; + T upload; +}; + +typedef Sample SpeedSample; +typedef Sample SpeedSampleAvg; + +class SpeedMonitor +{ +public: + void addSample(const SpeedSample &sample); + SpeedSampleAvg average() const; + void reset(); + +private: + static const int MAX_SAMPLES = 30; + QList m_speedSamples; + SpeedSample m_sum; +}; + +#endif // SPEEDMONITOR_H diff --git a/src/core/bittorrent/private/statistics.cpp b/src/core/bittorrent/private/statistics.cpp new file mode 100644 index 000000000..f57a9a7c0 --- /dev/null +++ b/src/core/bittorrent/private/statistics.cpp @@ -0,0 +1,115 @@ +#include + +#include + +#include "core/qinisettings.h" +#include "core/preferences.h" +#include "core/bittorrent/sessionstatus.h" +#include "core/bittorrent/session.h" +#include "statistics.h" + +static const qint64 SAVE_INTERVAL = 15 * 60 * 1000; + +namespace libt = libtorrent; +using namespace BitTorrent; + +Statistics::Statistics(Session *session) + : QObject(session) + , m_session(session) + , m_sessionUL(0) + , m_sessionDL(0) + , m_lastWrite(0) + , m_dirty(false) +{ + load(); + connect(&m_timer, SIGNAL(timeout()), this, SLOT(gather())); + m_timer.start(60 * 1000); +} + +Statistics::~Statistics() +{ + if (m_dirty) + m_lastWrite = 0; + save(); +} + +quint64 Statistics::getAlltimeDL() const +{ + return m_alltimeDL + m_sessionDL; +} + +quint64 Statistics::getAlltimeUL() const +{ + return m_alltimeUL + m_sessionUL; +} + +void Statistics::gather() +{ + SessionStatus ss = m_session->status(); + if (ss.totalDownload() > m_sessionDL) { + m_sessionDL = ss.totalDownload(); + m_dirty = true; + } + if (ss.totalUpload() > m_sessionUL) { + m_sessionUL = ss.totalUpload(); + m_dirty = true; + } + + save(); +} + +void Statistics::save() const +{ + qint64 now = QDateTime::currentMSecsSinceEpoch(); + + if (!m_dirty || ((now - m_lastWrite) < SAVE_INTERVAL)) + return; + + QIniSettings s("qBittorrent", "qBittorrent-data"); + QVariantHash v; + v.insert("AlltimeDL", m_alltimeDL + m_sessionDL); + v.insert("AlltimeUL", m_alltimeUL + m_sessionUL); + s.setValue("Stats/AllStats", v); + m_dirty = false; + m_lastWrite = now; +} + +void Statistics::load() +{ + // Temp code. Versions v3.1.4 and v3.1.5 saved the data in the qbittorrent.ini file. + // This code reads the data from there, writes it to the new file, and removes the keys + // from the old file. This code should be removed after some time has passed. + // e.g. When we reach v3.3.0 + // Don't forget to remove: + // 1. Preferences::getStats() + // 2. Preferences::removeStats() + // 3. #include "core/preferences.h" + Preferences* const pref = Preferences::instance(); + QIniSettings s("qBittorrent", "qBittorrent-data"); + QVariantHash v = pref->getStats(); + + // Let's test if the qbittorrent.ini holds the key + if (!v.isEmpty()) { + m_dirty = true; + + // If the user has used qbt > 3.1.5 and then reinstalled/used + // qbt < 3.1.6, there will be stats in qbittorrent-data.ini too + // so we need to merge those 2. + if (s.contains("Stats/AllStats")) { + QVariantHash tmp = s.value("Stats/AllStats").toHash(); + v["AlltimeDL"] = v["AlltimeDL"].toULongLong() + tmp["AlltimeDL"].toULongLong(); + v["AlltimeUL"] = v["AlltimeUL"].toULongLong() + tmp["AlltimeUL"].toULongLong(); + } + } + else { + v = s.value("Stats/AllStats").toHash(); + } + + m_alltimeDL = v["AlltimeDL"].toULongLong(); + m_alltimeUL = v["AlltimeUL"].toULongLong(); + + if (m_dirty) { + save(); + pref->removeStats(); + } +} diff --git a/src/core/bittorrent/private/statistics.h b/src/core/bittorrent/private/statistics.h new file mode 100644 index 000000000..64b4015fc --- /dev/null +++ b/src/core/bittorrent/private/statistics.h @@ -0,0 +1,41 @@ +#ifndef STATISTICS_H +#define STATISTICS_H + +#include +#include + +namespace BitTorrent { class Session; } + +class Statistics : QObject +{ + Q_OBJECT + Q_DISABLE_COPY(Statistics) + +public: + Statistics(BitTorrent::Session *session); + ~Statistics(); + + quint64 getAlltimeDL() const; + quint64 getAlltimeUL() const; + +private slots: + void gather(); + +private: + void save() const; + void load(); + +private: + BitTorrent::Session *m_session; + // Will overflow at 15.9 EiB + quint64 m_alltimeUL; + quint64 m_alltimeDL; + qint64 m_sessionUL; + qint64 m_sessionDL; + mutable qint64 m_lastWrite; + mutable bool m_dirty; + + QTimer m_timer; +}; + +#endif // STATISTICS_H diff --git a/src/core/bittorrent/private/torrenthandleprivate.h b/src/core/bittorrent/private/torrenthandleprivate.h new file mode 100644 index 000000000..31c2fde9c --- /dev/null +++ b/src/core/bittorrent/private/torrenthandleprivate.h @@ -0,0 +1,53 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * 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. + */ + +#ifndef TORRENTHANDLEPRIVATE_H +#define TORRENTHANDLEPRIVATE_H + +namespace libtorrent +{ + class alert; + struct torrent_status; +} + +struct TorrentHandlePrivate +{ + virtual void handleAlert(libtorrent::alert *) = 0; + virtual void handleStateUpdate(const libtorrent::torrent_status &) = 0; + virtual void handleDefaultSavePathChanged() = 0; + virtual void handleTempPathChanged() = 0; + virtual void handleAppendExtensionToggled() = 0; +#ifndef DISABLE_COUNTRIES_RESOLUTION + virtual void handleResolveCountriesToggled() = 0; +#endif + +protected: + ~TorrentHandlePrivate() {} +}; + +#endif // TORRENTHANDLEPRIVATE_H diff --git a/src/core/bittorrent/session.cpp b/src/core/bittorrent/session.cpp new file mode 100644 index 000000000..17eb33f32 --- /dev/null +++ b/src/core/bittorrent/session.cpp @@ -0,0 +1,2440 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * 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. + */ + +#include "session.h" + +using namespace BitTorrent; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include + +#ifndef DISABLE_COUNTRIES_RESOLUTION +#include "geoipmanager.h" +#endif + +#include "core/misc.h" +#include "core/fs_utils.h" +#include "core/utils/string.h" +#include "core/logger.h" +#include "core/preferences.h" +#include "core/torrentfilter.h" +#include "core/net/downloadmanager.h" +#include "core/net/downloadhandler.h" +#include "core/net/portforwarder.h" +#include "core/utils/string.h" +#include "private/filterparserthread.h" +#include "private/statistics.h" +#include "private/bandwidthscheduler.h" +#include "trackerentry.h" +#include "tracker.h" +#include "magneturi.h" +#include "cachestatus.h" +#include "sessionstatus.h" +#include "torrenthandle.h" +#include "session.h" + +static const char PEER_ID[] = "qB"; +static const char RESUME_FOLDER[] = "ResumeData"; +static const int MAX_TRACKER_ERRORS = 2; + +namespace libt = libtorrent; +using namespace BitTorrent; + +static bool readFile(const QString &path, QByteArray &buf); +static bool loadTorrentResumeData(const QByteArray &data, AddTorrentData &out); + +static void torrentQueuePositionUp(const libt::torrent_handle &handle); +static void torrentQueuePositionDown(const libt::torrent_handle &handle); +static void torrentQueuePositionTop(const libt::torrent_handle &handle); +static void torrentQueuePositionBottom(const libt::torrent_handle &handle); + +// AddTorrentParams + +AddTorrentParams::AddTorrentParams() + : disableTempPath(false) + , sequential(false) + , ignoreShareRatio(false) + , skipChecking(false) +{ +} + +// TorrentStatusReport + +TorrentStatusReport::TorrentStatusReport() + : nbDownloading(0) + , nbSeeding(0) + , nbCompleted(0) + , nbActive(0) + , nbInactive(0) + , nbPaused(0) + , nbResumed(0) +{ +} + +// Session + +Session *Session::m_instance = 0; + +Session::Session(QObject *parent) + : QObject(parent) + , m_LSDEnabled(false) + , m_DHTEnabled(false) + , m_PeXEnabled(false) + , m_queueingEnabled(false) + , m_torrentExportEnabled(false) + , m_finishedTorrentExportEnabled(false) + , m_preAllocateAll(false) + , m_globalMaxRatio(-1) + , m_numResumeData(0) + , m_extraLimit(0) +#ifndef DISABLE_COUNTRIES_RESOLUTION + , m_geoipDBLoaded(false) + , m_resolveCountries(false) +#endif + , m_appendLabelToSavePath(false) + , m_appendExtension(false) + , m_refreshInterval(0) + , m_highRatioAction(MaxRatioAction::Pause) +{ + Preferences* const pref = Preferences::instance(); + + initResumeFolder(); + + m_bigRatioTimer = new QTimer(this); + m_bigRatioTimer->setInterval(10000); + connect(m_bigRatioTimer, SIGNAL(timeout()), SLOT(processBigRatios())); + + // Creating BitTorrent session + + // Construct session + libt::fingerprint fingerprint(PEER_ID, VERSION_MAJOR, VERSION_MINOR, VERSION_BUGFIX, VERSION_BUILD); + m_nativeSession = new libt::session(fingerprint, 0); + Logger::instance()->addMessage("Peer ID: " + String::fromStdString(fingerprint.to_string())); + + m_nativeSession->set_alert_dispatch(boost::bind(&Session::dispatchAlerts, this, _1)); + + // Set severity level of libtorrent session + m_nativeSession->set_alert_mask( + libt::alert::error_notification + | libt::alert::peer_notification + | libt::alert::port_mapping_notification + | libt::alert::storage_notification + | libt::alert::tracker_notification + | libt::alert::status_notification + | libt::alert::ip_block_notification + | libt::alert::progress_notification + | libt::alert::stats_notification + ); + + // Load previous state + loadState(); + + // Enabling plugins + //m_nativeSession->add_extension(&libt::create_metadata_plugin); + m_nativeSession->add_extension(&libt::create_ut_metadata_plugin); + if (pref->trackerExchangeEnabled()) + m_nativeSession->add_extension(&libt::create_lt_trackers_plugin); + m_PeXEnabled = pref->isPeXEnabled(); + if (m_PeXEnabled) + m_nativeSession->add_extension(&libt::create_ut_pex_plugin); + m_nativeSession->add_extension(&libt::create_smart_ban_plugin); + + m_refreshTimer = new QTimer(this); + m_refreshTimer->setInterval(2000); + connect(m_refreshTimer, SIGNAL(timeout()), SLOT(refresh())); + m_refreshTimer->start(); + + // Regular saving of fastresume data + m_resumeDataTimer = new QTimer(this); + connect(m_resumeDataTimer, SIGNAL(timeout()), SLOT(generateResumeData())); + + m_statistics = new Statistics(this); + + // Apply user settings to BitTorrent session + configure(); + connect(pref, SIGNAL(changed()), SLOT(configure())); + + m_resumeDataTimer->start(); + + // initialize PortForwarder instance + Net::PortForwarder::initInstance(m_nativeSession); + + qDebug("* BitTorrent Session constructed"); + startUpTorrents(); +} + +bool Session::isDHTEnabled() const +{ + return m_DHTEnabled; +} + +bool Session::isLSDEnabled() const +{ + return m_LSDEnabled; +} + +bool Session::isPexEnabled() const +{ + return m_PeXEnabled; +} + +bool Session::isQueueingEnabled() const +{ + return m_queueingEnabled; +} + +bool Session::isTempPathEnabled() const +{ + return !m_tempPath.isEmpty(); +} + +bool Session::isAppendExtensionEnabled() const +{ + return m_appendExtension; +} + +bool Session::useAppendLabelToSavePath() const +{ + return m_appendLabelToSavePath; +} + +#ifndef DISABLE_COUNTRIES_RESOLUTION +bool Session::isResolveCountriesEnabled() const +{ + return m_resolveCountries; +} +#endif + +QString Session::defaultSavePath() const +{ + return m_defaultSavePath; +} + +QString Session::tempPath() const +{ + return m_tempPath; +} + +qreal Session::globalMaxRatio() const +{ + return m_globalMaxRatio; +} + +// Main destructor +Session::~Session() +{ + // Do some BT related saving + saveState(); + saveResumeData(); + + // We must delete FilterParserThread + // before we delete libtorrent::session + if (m_filterParser) + delete m_filterParser; + + // We must delete PortForwarderImpl before + // we delete libtorrent::session + Net::PortForwarder::freeInstance(); + + qDebug("Deleting the session"); + delete m_nativeSession; + + m_resumeFolderLock.close(); + m_resumeFolderLock.remove(); +} + +void Session::initInstance() +{ + if (!m_instance) + m_instance = new Session; +} + +void Session::freeInstance() +{ + if (m_instance) { + delete m_instance; + m_instance = 0; + } +} + +Session *Session::instance() +{ + return m_instance; +} + +void Session::loadState() +{ + const QString statePath = fsutils::cacheLocation() + QLatin1String("/ses_state"); + if (!QFile::exists(statePath)) return; + + if (QFile(statePath).size() == 0) { + // Remove empty invalid state file + fsutils::forceRemove(statePath); + return; + } + + QFile file(statePath); + if (file.open(QIODevice::ReadOnly)) { + QByteArray buf = file.readAll(); + // bdecode + libt::lazy_entry entry; + libt::error_code ec; + libt::lazy_bdecode(buf.constData(), buf.constData() + buf.size(), entry, ec); + if (!ec) + m_nativeSession->load_state(entry); + } +} + +void Session::saveState() +{ + qDebug("Saving session state to disk..."); + + const QString state_path = fsutils::cacheLocation() + QLatin1String("/ses_state"); + libt::entry session_state; + m_nativeSession->save_state(session_state); + std::vector out; + libt::bencode(std::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(); + } +} + +void Session::setSessionSettings() +{ + Preferences* const pref = Preferences::instance(); + Logger* const logger = Logger::instance(); + + libt::session_settings sessionSettings = m_nativeSession->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(String::fromStdString(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.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"; + libt::session_settings::io_buffer_mode_t mode = pref->osCache() ? libt::session_settings::enable_os_cache : libt::session_settings::disable_os_cache; + sessionSettings.disk_io_read_mode = mode; + sessionSettings.disk_io_write_mode = mode; + + m_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 + m_queueingEnabled = pref->isQueueingSystemEnabled(); + if (m_queueingEnabled) { + adjustLimits(sessionSettings); + + sessionSettings.active_seeds = pref->getMaxActiveUploads(); + sessionSettings.dont_count_slow_torrents = pref->ignoreSlowTorrentsForQueueing(); + } + 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; + } + + // 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 = String::toStdString(announce_ip); + // 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 = libt::session_settings::prefer_tcp; + else + sessionSettings.mixed_mode_algorithm = libt::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 = libt::session_settings::fastest_upload; + qDebug() << "Set session settings"; + m_nativeSession->set_settings(sessionSettings); +} + +void Session::adjustLimits() +{ + if (m_queueingEnabled) { + libt::session_settings sessionSettings(m_nativeSession->settings()); + adjustLimits(sessionSettings); + m_nativeSession->set_settings(sessionSettings); + } +} + +void Session::adjustLimits(libt::session_settings &sessionSettings) +{ + Preferences *const pref = Preferences::instance(); + + //Internally increase the queue limits to ensure that the magnet is started + int max_downloading = pref->getMaxActiveDownloads(); + int max_active = pref->getMaxActiveTorrents(); + if (max_downloading > -1) + sessionSettings.active_downloads = max_downloading + m_extraLimit; + else + sessionSettings.active_downloads = max_downloading; + if (max_active > -1) { + int limit = max_active + m_extraLimit; + 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; + } +} + +// Set BitTorrent session configuration +void Session::configure() +{ + qDebug("Configuring session"); + Preferences* const pref = Preferences::instance(); + + const unsigned short oldListenPort = m_nativeSession->listen_port(); + const unsigned short newListenPort = pref->getSessionPort(); + if (oldListenPort != newListenPort) { + qDebug("Session port changes in program preferences: %d -> %d", oldListenPort, newListenPort); + setListeningPort(newListenPort); + } + + // * Save path + setDefaultSavePath(pref->getSavePath()); + + // * Temp path + if (pref->isTempPathEnabled()) + setDefaultTempPath(pref->getTempPath()); + else + setDefaultTempPath(); + + uint newRefreshInterval = pref->getRefreshInterval(); + if (newRefreshInterval != m_refreshInterval) { + m_refreshInterval = newRefreshInterval; + m_refreshTimer->setInterval(m_refreshInterval); + } + + setAppendLabelToSavePath(pref->appendTorrentLabel()); + setAppendExtension(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 (!m_bwScheduler) { + m_bwScheduler = new BandwidthScheduler(this); + connect(m_bwScheduler, SIGNAL(switchToAlternativeMode(bool)), this, SLOT(changeSpeedLimitMode(bool))); + } + m_bwScheduler->start(); + } + else { + delete m_bwScheduler; + } + +#ifndef DISABLE_COUNTRIES_RESOLUTION + // Resolve countries + qDebug("Loading country resolution settings"); + const bool new_resolv_countries = pref->resolvePeerCountries(); + if (m_resolveCountries != new_resolv_countries) { + qDebug("in country resolution settings"); + m_resolveCountries = new_resolv_countries; + if (m_resolveCountries && !m_geoipDBLoaded) { + qDebug("Loading geoip database"); + GeoIPManager::loadDatabase(m_nativeSession); + m_geoipDBLoaded = true; + } + + // Update torrent handles + foreach (TorrentHandlePrivate *const torrent, m_torrents) + torrent->handleResolveCountriesToggled(); + } +#endif + + Logger* const logger = Logger::instance(); + + // * Session settings + setSessionSettings(); + + // Bittorrent + // * Max connections per torrent limit + setMaxConnectionsPerTorrent(pref->getMaxConnecsPerTorrent()); + // * Max uploads per torrent limit + setMaxUploadsPerTorrent(pref->getMaxUploadsPerTorrent()); + // * DHT + enableDHT(pref->isDHTEnabled()); + + // * PeX + if (m_PeXEnabled) + logger->addMessage(tr("PeX support [ON]"), Log::INFO); + else + logger->addMessage(tr("PeX support [OFF]"), Log::CRITICAL); + if (m_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 + libt::pe_settings encryptionSettings; + encryptionSettings.allowed_enc_level = libt::pe_settings::rc4; + encryptionSettings.prefer_rc4 = true; + switch(encryptionState) { + case 0: //Enabled + encryptionSettings.out_enc_policy = libt::pe_settings::enabled; + encryptionSettings.in_enc_policy = libt::pe_settings::enabled; + logger->addMessage(tr("Encryption support [ON]"), Log::INFO); + break; + case 1: // Forced + encryptionSettings.out_enc_policy = libt::pe_settings::forced; + encryptionSettings.in_enc_policy = libt::pe_settings::forced; + logger->addMessage(tr("Encryption support [FORCED]"), Log::INFO); + break; + default: // Disabled + encryptionSettings.out_enc_policy = libt::pe_settings::disabled; + encryptionSettings.in_enc_policy = libt::pe_settings::disabled; + logger->addMessage(tr("Encryption support [OFF]"), Log::INFO); + } + + qDebug("Applying encryption settings"); + m_nativeSession->set_pe_settings(encryptionSettings); + + // * Maximum ratio + m_highRatioAction = pref->getMaxRatioAction(); + setGlobalMaxRatio(pref->getGlobalMaxRatio()); + + // Ip Filter + FilterParserThread::processFilterList(m_nativeSession, pref->bannedIPs()); + if (pref->isFilteringEnabled()) + enableIPFilter(pref->getFilter()); + else + disableIPFilter(); + + // * Proxy settings + libt::proxy_settings proxySettings; + if (pref->isProxyEnabled()) { + qDebug("Enabling P2P proxy"); + proxySettings.hostname = String::toStdString(pref->getProxyIp()); + qDebug("hostname is %s", proxySettings.hostname.c_str()); + proxySettings.port = pref->getProxyPort(); + qDebug("port is %d", proxySettings.port); + if (pref->isProxyAuthEnabled()) { + proxySettings.username = String::toStdString(pref->getProxyUsername()); + proxySettings.password = String::toStdString(pref->getProxyPassword()); + 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 = libt::proxy_settings::http; + break; + case Proxy::HTTP_PW: + qDebug("type: http_pw"); + proxySettings.type = libt::proxy_settings::http_pw; + break; + case Proxy::SOCKS4: + proxySettings.type = libt::proxy_settings::socks4; + break; + case Proxy::SOCKS5: + qDebug("type: socks5"); + proxySettings.type = libt::proxy_settings::socks5; + break; + case Proxy::SOCKS5_PW: + qDebug("type: socks5_pw"); + proxySettings.type = libt::proxy_settings::socks5_pw; + break; + default: + proxySettings.type = libt::proxy_settings::none; + } + + setProxySettings(proxySettings); + + // Tracker + if (pref->isTrackerEnabled()) { + if (!m_tracker) + m_tracker = new Tracker(this); + + if (m_tracker->start()) + logger->addMessage(tr("Embedded Tracker [ON]"), Log::INFO); + else + logger->addMessage(tr("Failed to start the embedded tracker!"), Log::CRITICAL); + } + else { + logger->addMessage(tr("Embedded Tracker [OFF]")); + if (m_tracker) + delete m_tracker; + } + + qDebug("Session configured"); +} + +void Session::preAllocateAllFiles(bool b) +{ + const bool change = (m_preAllocateAll != b); + if (change) { + qDebug("PreAllocateAll changed, reloading all torrents!"); + m_preAllocateAll = b; + } +} + +void Session::processBigRatios() +{ + qDebug("Process big ratios..."); + + foreach (TorrentHandle *const torrent, m_torrents) { + if (torrent->isSeed() && (torrent->ratioLimit() != TorrentHandle::NO_RATIO_LIMIT)) { + const qreal ratio = torrent->realRatio(); + qreal ratioLimit = torrent->ratioLimit(); + if (ratioLimit == TorrentHandle::USE_GLOBAL_RATIO) + ratioLimit = m_globalMaxRatio; + qDebug("Ratio: %f (limit: %f)", ratio, ratioLimit); + Q_ASSERT(ratioLimit >= 0.f); + + if ((ratio <= TorrentHandle::MAX_RATIO) && (ratio >= ratioLimit)) { + Logger* const logger = Logger::instance(); + if (m_highRatioAction == MaxRatioAction::Remove) { + logger->addMessage(tr("%1 reached the maximum ratio you set. Removing...").arg(torrent->name())); + deleteTorrent(torrent->hash()); + } + else { + // Pause it + if (!torrent->isPaused()) { + logger->addMessage(tr("%1 reached the maximum ratio you set. Pausing...").arg(torrent->name())); + torrent->pause(); + } + } + } + } + } +} + +void Session::handleDownloadFailed(const QString &url, const QString &reason) +{ + emit downloadFromUrlFailed(url, reason); +} + +void Session::handleRedirectedToMagnet(const QString &url, const QString &magnetUri) +{ + addTorrent_impl(m_downloadedTorrents.take(url), MagnetUri(magnetUri)); +} + +// Add to BitTorrent session the downloaded torrent file +void Session::handleDownloadFinished(const QString &url, const QString &filePath) +{ + emit downloadFromUrlFinished(url); + addTorrent_impl(m_downloadedTorrents.take(url), MagnetUri(), TorrentInfo::loadFromFile(filePath)); + fsutils::forceRemove(filePath); // remove temporary file +} + +void Session::changeSpeedLimitMode(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 m_bwScheduler; + pref->setAltBandwidthEnabled(alternative); + + // Apply settings to the bittorrent session + int downLimit = alternative ? pref->getAltGlobalDownloadLimit() : pref->getGlobalDownloadLimit(); + if (downLimit <= 0) + downLimit = -1; + else + downLimit *= 1024; + setDownloadRateLimit(downLimit); + + // Upload rate + int upLimit = alternative ? pref->getAltGlobalUploadLimit() : pref->getGlobalUploadLimit(); + if (upLimit <= 0) + upLimit = -1; + else + upLimit *= 1024; + setUploadRateLimit(upLimit); + + // Notify + emit speedLimitModeChanged(alternative); +} + +// Return the torrent handle, given its hash +TorrentHandle *Session::findTorrent(const InfoHash &hash) const +{ + return m_torrents.value(hash); +} + +bool Session::hasActiveTorrents() const +{ + foreach (TorrentHandle *const torrent, m_torrents) + if (TorrentFilter::ActiveTorrent.match(torrent)) + return true; + + return false; +} + +bool Session::hasUnfinishedTorrents() const +{ + foreach (TorrentHandle *const torrent, m_torrents) + if (!torrent->isSeed() && !torrent->isPaused()) + return true; + + return false; +} + +void Session::banIP(const QString &ip) +{ + FilterParserThread::processFilterList(m_nativeSession, QStringList(ip)); + Preferences::instance()->banIP(ip); +} + +// Delete a torrent from the session, given its hash +// deleteLocalFiles = true means that the torrent will be removed from the hard-drive too +bool Session::deleteTorrent(const QString &hash, bool deleteLocalFiles) +{ + TorrentHandle *const torrent = m_torrents.take(hash); + if (!torrent) return false; + + qDebug("Deleting torrent with hash: %s", qPrintable(torrent->hash())); + emit torrentAboutToBeRemoved(torrent); + + // Remove it from session + if (deleteLocalFiles) { + QDir saveDir(torrent->actualSavePath()); + if ((saveDir != QDir(m_defaultSavePath)) && (saveDir != QDir(m_tempPath))) { + m_savePathsToRemove[torrent->hash()] = saveDir.absolutePath(); + qDebug() << "Save path to remove (async): " << saveDir.absolutePath(); + } + m_nativeSession->remove_torrent(torrent->nativeHandle(), libt::session::delete_files); + } + else { + QStringList unwantedFiles; + if (torrent->hasMetadata()) + unwantedFiles = torrent->absoluteFilePathsUnwanted(); + m_nativeSession->remove_torrent(torrent->nativeHandle()); + // Remove unwanted and incomplete files + foreach (const QString &unwantedFile, unwantedFiles) { + qDebug("Removing unwanted file: %s", qPrintable(unwantedFile)); + fsutils::forceRemove(unwantedFile); + const QString parentFolder = fsutils::branchPath(unwantedFile); + qDebug("Attempt to remove parent folder (if empty): %s", qPrintable(parentFolder)); + QDir().rmpath(parentFolder); + } + } + + // Remove it from torrent resume directory + QDir resumeDataDir(m_resumeFolderPath); + QStringList filters; + filters << QString("%1.*").arg(torrent->hash()); + const QStringList files = resumeDataDir.entryList(filters, QDir::Files, QDir::Unsorted); + foreach (const QString &file, files) + fsutils::forceRemove(resumeDataDir.absoluteFilePath(file)); + + if (deleteLocalFiles) + Logger::instance()->addMessage(tr("'%1' was removed from transfer list and hard disk.", "'xxx.avi' was removed...").arg(torrent->name())); + else + Logger::instance()->addMessage(tr("'%1' was removed from transfer list.", "'xxx.avi' was removed...").arg(torrent->name())); + + delete torrent; + qDebug("Torrent deleted."); + return true; +} + +bool Session::cancelLoadMetadata(const InfoHash &hash) +{ + if (!m_loadedMetadata.contains(hash)) return false; + + m_loadedMetadata.remove(hash); + libt::torrent_handle torrent = m_nativeSession->find_torrent(hash); + if (!torrent.is_valid()) return false; + + if (!torrent.status(0).has_metadata) { + // if hidden torrent is still loading metadata... + --m_extraLimit; + adjustLimits(); + } + + // Remove it from session + m_nativeSession->remove_torrent(torrent, libt::session::delete_files); + qDebug("Preloaded torrent deleted."); + return true; +} + +void Session::increaseTorrentsPriority(const QStringList &hashes) +{ + std::priority_queue, + std::vector >, + std::greater > > torrentQueue; + + // Sort torrents by priority + foreach (const InfoHash &hash, hashes) { + TorrentHandle *const torrent = m_torrents.value(hash); + if (torrent && !torrent->isSeed()) + torrentQueue.push(qMakePair(torrent->queuePosition(), torrent)); + } + + // Increase torrents priority (starting with the ones with highest priority) + while (!torrentQueue.empty()) { + TorrentHandle *const torrent = torrentQueue.top().second; + torrentQueuePositionUp(torrent->nativeHandle()); + torrentQueue.pop(); + } +} + +void Session::decreaseTorrentsPriority(const QStringList &hashes) +{ + std::priority_queue, + std::vector >, + std::less > > torrentQueue; + + // Sort torrents by priority + foreach (const InfoHash &hash, hashes) { + TorrentHandle *const torrent = m_torrents.value(hash); + if (torrent && !torrent->isSeed()) + torrentQueue.push(qMakePair(torrent->queuePosition(), torrent)); + } + + // Decrease torrents priority (starting with the ones with lowest priority) + while (!torrentQueue.empty()) { + TorrentHandle *const torrent = torrentQueue.top().second; + torrentQueuePositionDown(torrent->nativeHandle()); + torrentQueue.pop(); + } + + foreach (const InfoHash &hash, m_loadedMetadata.keys()) + torrentQueuePositionBottom(m_nativeSession->find_torrent(hash)); +} + +void Session::topTorrentsPriority(const QStringList &hashes) +{ + std::priority_queue, + std::vector >, + std::greater > > torrentQueue; + + // Sort torrents by priority + foreach (const InfoHash &hash, hashes) { + TorrentHandle *const torrent = m_torrents.value(hash); + if (torrent && !torrent->isSeed()) + torrentQueue.push(qMakePair(torrent->queuePosition(), torrent)); + } + + // Top torrents priority (starting with the ones with highest priority) + while (!torrentQueue.empty()) { + TorrentHandle *const torrent = torrentQueue.top().second; + torrentQueuePositionTop(torrent->nativeHandle()); + torrentQueue.pop(); + } +} + +void Session::bottomTorrentsPriority(const QStringList &hashes) +{ + std::priority_queue, + std::vector >, + std::less > > torrentQueue; + + // Sort torrents by priority + foreach (const InfoHash &hash, hashes) { + TorrentHandle *const torrent = m_torrents.value(hash); + if (torrent && !torrent->isSeed()) + torrentQueue.push(qMakePair(torrent->queuePosition(), torrent)); + } + + // Bottom torrents priority (starting with the ones with lowest priority) + while (!torrentQueue.empty()) { + TorrentHandle *const torrent = torrentQueue.top().second; + torrentQueuePositionBottom(torrent->nativeHandle()); + torrentQueue.pop(); + } + + foreach (const InfoHash &hash, m_loadedMetadata.keys()) + torrentQueuePositionBottom(m_nativeSession->find_torrent(hash)); +} + +QHash Session::torrents() const +{ + return m_torrents; +} + +// source - .torrent file path/url or magnet uri (hash for preloaded torrent) +bool Session::addTorrent(QString source, const AddTorrentParams ¶ms) +{ + InfoHash hash = source; + if (hash.isValid() && m_loadedMetadata.contains(hash)) { + // Adding preloaded torrent + m_loadedMetadata.remove(hash); + libt::torrent_handle handle = m_nativeSession->find_torrent(hash); + --m_extraLimit; + + try { + handle.auto_managed(false); + handle.pause(); + } + catch (std::exception &) {} + + adjustLimits(); + + // use common 2nd step of torrent adddition + libt::add_torrent_alert *alert = new libt::add_torrent_alert(handle, libt::add_torrent_params(), libt::error_code()); + m_addingTorrents.insert(hash, AddTorrentData(params)); + handleAddTorrentAlert(alert); + delete alert; + return true; + } + + if (source.startsWith("bc://bt/", Qt::CaseInsensitive)) { + qDebug("Converting bc link to magnet link"); + source = misc::bcLinkToMagnet(source); + } + + if (misc::isUrl(source)) { + Logger::instance()->addMessage(tr("Downloading '%1', please wait...", "e.g: Downloading 'xxx.torrent', please wait...").arg(source)); + // Launch downloader + Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(source, 10485760 /* 10MB */); + connect(handler, SIGNAL(downloadFinished(QString, QString)), this, SLOT(handleDownloadFinished(QString, QString))); + connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleDownloadFailed(QString, QString))); + connect(handler, SIGNAL(redirectedToMagnet(QString, QString)), this, SLOT(handleRedirectedToMagnet(QString, QString))); + m_downloadedTorrents[handler->url()] = params; + } + else if (source.startsWith("magnet:", Qt::CaseInsensitive)) { + return addTorrent_impl(params, MagnetUri(source)); + } + else { + return addTorrent_impl(params, MagnetUri(), TorrentInfo::loadFromFile(source)); + } + + return false; +} + +bool Session::addTorrent(const TorrentInfo &torrentInfo, const AddTorrentParams ¶ms) +{ + if (!torrentInfo.isValid()) return false; + + return addTorrent_impl(params, MagnetUri(), torrentInfo); +} + +// Add a torrent to the BitTorrent session +bool Session::addTorrent_impl(const AddTorrentData &addData, const MagnetUri &magnetUri, + const TorrentInfo &torrentInfo, const QByteArray &fastresumeData) +{ + libt::add_torrent_params p; + InfoHash hash; + std::vector buf(fastresumeData.constData(), fastresumeData.constData() + fastresumeData.size()); + std::vector filePriorities; + + bool fromMagnetUri = magnetUri.isValid(); + if (fromMagnetUri) { + p = magnetUri.addTorrentParams(); + hash = magnetUri.hash(); + } + else { + if (!torrentInfo.isValid()) return false; + + // Metadata + p.ti = torrentInfo.nativeInfo(); + hash = torrentInfo.hash(); + + if (addData.resumed) { + // Set torrent fast resume data +#if LIBTORRENT_VERSION_NUM < 10000 + p.resume_data = &buf; +#else + p.resume_data = buf; + p.flags |= libt::add_torrent_params::flag_use_resume_save_path; +#endif + p.flags |= libt::add_torrent_params::flag_merge_resume_trackers; + + } + else { + foreach (int prio, addData.filePriorities) + filePriorities.push_back(prio); +#if LIBTORRENT_VERSION_NUM < 10000 + p.file_priorities = &filePriorities; +#else + p.file_priorities = filePriorities; +#endif + } + } + + // We should not add torrent if it already + // processed or adding to session + if (m_addingTorrents.contains(hash) || m_loadedMetadata.contains(hash)) return false; + + if (m_torrents.contains(hash)) { + TorrentHandle *const torrent = m_torrents.value(hash); + torrent->addTrackers(fromMagnetUri ? magnetUri.trackers() : torrentInfo.trackers()); + torrent->addUrlSeeds(fromMagnetUri ? magnetUri.urlSeeds() : torrentInfo.urlSeeds()); + return true; + } + + qDebug("Adding torrent..."); + qDebug(" -> Hash: %s", qPrintable(hash)); + + // Preallocation mode + if (m_preAllocateAll) + p.storage_mode = libt::storage_mode_allocate; + else + p.storage_mode = libt::storage_mode_sparse; + + p.flags |= libt::add_torrent_params::flag_paused; // Start in pause + p.flags &= ~libt::add_torrent_params::flag_auto_managed; // Because it is added in paused state + p.flags &= ~libt::add_torrent_params::flag_duplicate_is_error; // Already checked + + // Seeding mode + // Skip checking and directly start seeding (new in libtorrent v0.15) + if (addData.hasSeedStatus) + p.flags |= libt::add_torrent_params::flag_seed_mode; + else + p.flags &= ~libt::add_torrent_params::flag_seed_mode; + +#if LIBTORRENT_VERSION_NUM >= 10000 + // Limits + Preferences *const pref = Preferences::instance(); + p.max_connections = pref->getMaxConnecsPerTorrent(); + p.max_uploads = pref->getMaxUploadsPerTorrent(); +#endif + + QString savePath; + // Set actual save path (e.g. temporary folder) + if (isTempPathEnabled() && !addData.disableTempPath && !addData.hasSeedStatus) { + savePath = m_tempPath; + } + else { + savePath = addData.savePath; + if (savePath.isEmpty()) { + savePath = m_defaultSavePath; + if (m_appendLabelToSavePath && !addData.label.isEmpty()) + savePath += QString("%1/").arg(addData.label); + } + else if (!savePath.endsWith("/")) + savePath += "/"; + } + + p.save_path = String::toStdString(fsutils::toNativePath(savePath)); + // Check if save path exists, creating it otherwise + if (!QDir(savePath).exists()) + QDir().mkpath(savePath); + + m_addingTorrents.insert(hash, addData); + // Adding torrent to BitTorrent session + m_nativeSession->async_add_torrent(p); + return true; +} + +// Add a torrent to the BitTorrent session in hidden mode +// and force it to load its metadata +bool Session::loadMetadata(const QString &magnetUri) +{ + Q_ASSERT(magnetUri.startsWith("magnet:", Qt::CaseInsensitive)); + + MagnetUri magnet(magnetUri); + if (!magnet.isValid()) return false; + + InfoHash hash = magnet.hash(); + QString name = magnet.name(); + + // We should not add tarrent if it already + // processed or adding to session + if (m_torrents.contains(hash)) return false; + if (m_addingTorrents.contains(hash)) return false; + if (m_loadedMetadata.contains(hash)) return false; + + qDebug("Adding torrent to preload metadata..."); + qDebug(" -> Hash: %s", qPrintable(hash)); + qDebug(" -> Name: %s", qPrintable(name)); + + libt::add_torrent_params p = magnet.addTorrentParams(); + + // Flags + // Preallocation mode + if (m_preAllocateAll) + p.storage_mode = libt::storage_mode_allocate; + else + p.storage_mode = libt::storage_mode_sparse; + + Preferences *const pref = Preferences::instance(); +#if LIBTORRENT_VERSION_NUM >= 10000 + // Limits + p.max_connections = pref->getMaxConnecsPerTorrent(); + p.max_uploads = pref->getMaxUploadsPerTorrent(); +#endif + + QString savePath = QString("%1/%2").arg(QDir::tempPath()).arg(hash); + p.save_path = String::toStdString(fsutils::toNativePath(savePath)); + // Check if save path exists, creating it otherwise + if (!QDir(savePath).exists()) + QDir().mkpath(savePath); + + // Forced start + p.flags &= ~libt::add_torrent_params::flag_paused; + p.flags &= ~libt::add_torrent_params::flag_auto_managed; + // Solution to avoid accidental file writes + p.flags |= libt::add_torrent_params::flag_upload_mode; + + // Adding torrent to BitTorrent session + libt::error_code ec; + libt::torrent_handle h = m_nativeSession->add_torrent(p, ec); + if (ec) return false; + +#if LIBTORRENT_VERSION_NUM < 10000 + h.set_max_connections(pref->getMaxConnecsPerTorrent()); + h.set_max_uploads(pref->getMaxUploadsPerTorrent()); +#endif + + // waiting for metadata... + m_loadedMetadata.insert(h.info_hash(), TorrentInfo()); + ++m_extraLimit; + adjustLimits(); + + return true; +} + +void Session::exportTorrentFile(TorrentHandle *const torrent, TorrentExportFolder folder) +{ + Q_ASSERT(((folder == TorrentExportFolder::Regular) && m_torrentExportEnabled) || + ((folder == TorrentExportFolder::Finished) && m_finishedTorrentExportEnabled)); + + QString torrentFilename = QString("%1.torrent").arg(torrent->hash()); + QString torrentPath = QDir(m_resumeFolderPath).absoluteFilePath(torrentFilename); + QDir exportPath(folder == TorrentExportFolder::Regular ? Preferences::instance()->getTorrentExportDir() : Preferences::instance()->getFinishedTorrentExportDir()); + if (exportPath.exists() || exportPath.mkpath(exportPath.absolutePath())) { + QString newTorrentPath = exportPath.absoluteFilePath(torrentFilename); + if (QFile::exists(newTorrentPath) && fsutils::sameFiles(torrentPath, newTorrentPath)) { + // Append hash to torrent name to make it unique + newTorrentPath = exportPath.absoluteFilePath(torrent->name() + "-" + torrentFilename); + } + QFile::copy(torrentPath, newTorrentPath); + } +} + +void Session::exportTorrentFiles(QString path) +{ + // NOTE: Maybe create files from current metadata here? + Q_ASSERT(m_torrentExportEnabled); + + QDir exportDir(path); + if (!exportDir.exists()) { + if (!exportDir.mkpath(exportDir.absolutePath())) { + Logger::instance()->addMessage(tr("Error: Could not create torrent export directory: %1").arg(exportDir.absolutePath()), Log::CRITICAL); + return; + } + } + + QDir resumeDataDir(m_resumeFolderPath); + foreach (TorrentHandle *const torrent, m_torrents) { + if (!torrent->isValid()) { + Logger::instance()->addMessage(tr("Torrent Export: torrent is invalid, skipping..."), Log::CRITICAL); + continue; + } + + const QString srcPath(resumeDataDir.absoluteFilePath(QString("%1.torrent").arg(torrent->hash()))); + if (QFile::exists(srcPath)) { + QString dstPath = exportDir.absoluteFilePath(QString("%1.torrent").arg(torrent->name())); + if (QFile::exists(dstPath)) { + if (!fsutils::sameFiles(srcPath, dstPath)) { + dstPath = exportDir.absoluteFilePath(QString("%1-%2.torrent").arg(torrent->name()).arg(torrent->hash())); + } + else { + qDebug("Torrent Export: Destination file exists, skipping..."); + continue; + } + } + qDebug("Export Torrent: %s -> %s", qPrintable(srcPath), qPrintable(dstPath)); + QFile::copy(srcPath, dstPath); + } + else { + Logger::instance()->addMessage(tr("Error: could not export torrent %1, maybe it has not metadata yet.").arg(torrent->hash()), Log::CRITICAL); + } + } +} + +void Session::setMaxConnectionsPerTorrent(int max) +{ + qDebug() << Q_FUNC_INFO << max; + + // Apply this to all session torrents + std::vector handles = m_nativeSession->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 Session::setMaxUploadsPerTorrent(int max) +{ + qDebug() << Q_FUNC_INFO << max; + + // Apply this to all session torrents + std::vector handles = m_nativeSession->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 Session::enableLSD(bool enable) +{ + if (enable) { + if (!m_LSDEnabled) { + qDebug("Enabling Local Peer Discovery"); + m_nativeSession->start_lsd(); + m_LSDEnabled = true; + } + } + else { + if (m_LSDEnabled) { + qDebug("Disabling Local Peer Discovery"); + m_nativeSession->stop_lsd(); + m_LSDEnabled = false; + } + } +} + +// Enable DHT +void Session::enableDHT(bool enable) +{ + Logger* const logger = Logger::instance(); + + if (enable) { + if (!m_DHTEnabled) { + try { + qDebug() << "Starting DHT..."; + Q_ASSERT(!m_nativeSession->is_dht_running()); + m_nativeSession->start_dht(); + m_nativeSession->add_dht_router(std::make_pair(std::string("router.bittorrent.com"), 6881)); + m_nativeSession->add_dht_router(std::make_pair(std::string("router.utorrent.com"), 6881)); + m_nativeSession->add_dht_router(std::make_pair(std::string("dht.transmissionbt.com"), 6881)); + m_nativeSession->add_dht_router(std::make_pair(std::string("dht.aelitis.com"), 6881)); // Vuze + m_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(String::fromStdString(e.what())), Log::CRITICAL); + } + } + } + else { + if (m_DHTEnabled) { + m_DHTEnabled = false; + m_nativeSession->stop_dht(); + logger->addMessage(tr("DHT support [OFF]"), Log::INFO); + qDebug("DHT disabled"); + } + } +} + +void Session::generateResumeData(bool final) +{ + foreach (TorrentHandle *const torrent, m_torrents) { + if (!torrent->isValid()) continue; + if (!torrent->hasMetadata()) continue; + if (torrent->hasMissingFiles()) continue; + if (torrent->isChecking() || torrent->hasError()) continue; + if (!final && !torrent->needSaveResumeData()) continue; + + saveTorrentResumeData(torrent); + qDebug("Saving fastresume data for %s", qPrintable(torrent->name())); + } +} + +// Called on exit +void Session::saveResumeData() +{ + qDebug("Saving fast resume data..."); + + // Pause session + m_nativeSession->pause(); + + generateResumeData(true); + + while (m_numResumeData > 0) { + QVector alerts; + getPendingAlerts(alerts, 30 * 1000); + if (alerts.empty()) { + std::cerr << " aborting with " << m_numResumeData + << " outstanding torrents to save resume data for" + << std::endl; + break; + } + + foreach (libt::alert *const a, alerts) { + switch (a->type()) { + case libt::save_resume_data_failed_alert::alert_type: + case libt::save_resume_data_alert::alert_type: + TorrentHandlePrivate *torrent = m_torrents.take(static_cast(a)->handle.info_hash()); + if (torrent) + torrent->handleAlert(a); + break; + } + + delete a; + } + } +} + +void Session::setDefaultSavePath(const QString &path) +{ + if (path.isEmpty()) return; + + QString defaultSavePath = fsutils::fromNativePath(path); + if (!defaultSavePath.endsWith("/")) + defaultSavePath += "/"; + if (m_defaultSavePath != defaultSavePath) { + m_defaultSavePath = defaultSavePath; + foreach (TorrentHandlePrivate *const torrent, m_torrents) + torrent->handleDefaultSavePathChanged(); + } +} + +void Session::setDefaultTempPath(const QString &path) +{ + QString tempPath; + + if (!path.isEmpty()) { + tempPath = fsutils::fromNativePath(path); + if (!tempPath.endsWith("/")) + tempPath += "/"; + } + + if (m_tempPath != tempPath) { + m_tempPath = tempPath; + foreach (TorrentHandlePrivate *const torrent, m_torrents) + torrent->handleTempPathChanged(); + } +} + +void Session::setAppendLabelToSavePath(bool append) +{ + if (m_appendLabelToSavePath != append) { + m_appendLabelToSavePath = append; + foreach (TorrentHandlePrivate *const torrent, m_torrents) + torrent->handleDefaultSavePathChanged(); + } +} + +void Session::setAppendExtension(bool append) +{ + if (m_appendExtension != append) { + m_appendExtension = append; + // append or remove .!qB extension for incomplete files + foreach (TorrentHandlePrivate *const torrent, m_torrents) + torrent->handleAppendExtensionToggled(); + } +} + +// Set the ports range in which is chosen the port +// the BitTorrent session will listen to +void Session::setListeningPort(int port) +{ + qDebug() << Q_FUNC_INFO << port; + Preferences* const pref = Preferences::instance(); + Logger* const logger = Logger::instance(); + + std::pair ports(port, port); + libt::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); + m_nativeSession->listen_on(ports, ec, 0, libt::session::listen_no_system_port); + + if (ec) + logger->addMessage(tr("qBittorrent failed to listen on any interface port: %1. Reason: %2", "e.g: qBittorrent failed to listen on any interface port: TCP/6881. Reason: no such interface" ).arg(QString::number(port)).arg(String::fromStdString(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)); + m_nativeSession->listen_on(ports, ec, entry.ip().toString().toLatin1().constData(), libt::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 Session::setDownloadRateLimit(int rate) +{ + qDebug() << Q_FUNC_INFO << rate; + Q_ASSERT((rate == -1) || (rate >= 0)); + libt::session_settings settings = m_nativeSession->settings(); + settings.download_rate_limit = rate; + m_nativeSession->set_settings(settings); +} + +// Set upload rate limit +// -1 to disable +void Session::setUploadRateLimit(int rate) +{ + qDebug() << Q_FUNC_INFO << rate; + Q_ASSERT((rate == -1) || (rate >= 0)); + libt::session_settings settings = m_nativeSession->settings(); + settings.upload_rate_limit = rate; + m_nativeSession->set_settings(settings); +} + +int Session::downloadRateLimit() const +{ + return m_nativeSession->settings().download_rate_limit; +} + +int Session::uploadRateLimit() const +{ + return m_nativeSession->settings().upload_rate_limit; +} + +bool Session::isListening() const +{ + return m_nativeSession->is_listening(); +} + +// Torrents will a ratio superior to the given value will +// be automatically deleted +void Session::setGlobalMaxRatio(qreal ratio) +{ + if (ratio < 0) + ratio = -1.; + if (m_globalMaxRatio != ratio) { + m_globalMaxRatio = ratio; + qDebug("* Set globalMaxRatio to %.1f", m_globalMaxRatio); + updateRatioTimer(); + } +} + +// If this functions returns true, we cannot add torrent to session, +// but it is still possible to merge trackers in some case +bool Session::isKnownTorrent(const InfoHash &hash) const +{ + return (m_torrents.contains(hash) + || m_addingTorrents.contains(hash) + || m_loadedMetadata.contains(hash)); +} + +void Session::updateRatioTimer() +{ + if ((m_globalMaxRatio == -1) && !hasPerTorrentRatioLimit()) { + if (m_bigRatioTimer->isActive()) + m_bigRatioTimer->stop(); + } + else if (!m_bigRatioTimer->isActive()) { + m_bigRatioTimer->start(); + } +} + +void Session::handleTorrentRatioLimitChanged(TorrentHandle *const torrent) +{ + Q_UNUSED(torrent); + updateRatioTimer(); +} + +void Session::saveTorrentResumeData(TorrentHandle *const torrent) +{ + torrent->saveResumeData(); + ++m_numResumeData; +} + +void Session::handleTorrentSavePathChanged(TorrentHandle *const torrent) +{ + emit torrentSavePathChanged(torrent); +} + +void Session::handleTorrentTrackersAdded(TorrentHandle *const torrent, const QList &newTrackers) +{ + foreach (const TrackerEntry &newTracker, newTrackers) + Logger::instance()->addMessage(tr("Tracker '%1' was added to torrent '%2'").arg(newTracker.url()).arg(torrent->name())); + emit trackersAdded(torrent, newTrackers); + if (torrent->trackers().size() == newTrackers.size()) + emit trackerlessStateChanged(torrent, false); + emit trackersChanged(torrent); +} + +void Session::handleTorrentTrackersRemoved(TorrentHandle *const torrent, const QList &deletedTrackers) +{ + foreach (const TrackerEntry &deletedTracker, deletedTrackers) + Logger::instance()->addMessage(tr("Tracker '%1' was deleted from torrent '%2'").arg(deletedTracker.url()).arg(torrent->name())); + emit trackersRemoved(torrent, deletedTrackers); + if (torrent->trackers().size() == 0) + emit trackerlessStateChanged(torrent, true); + emit trackersChanged(torrent); +} + +void Session::handleTorrentTrackersChanged(TorrentHandle *const torrent) +{ + emit trackersChanged(torrent); +} + +void Session::handleTorrentUrlSeedsAdded(TorrentHandle *const torrent, const QList &newUrlSeeds) +{ + foreach (const QUrl &newUrlSeed, newUrlSeeds) + Logger::instance()->addMessage(tr("URL seed '%1' was added to torrent '%2'").arg(newUrlSeed.toString()).arg(torrent->name())); +} + +void Session::handleTorrentUrlSeedsRemoved(TorrentHandle *const torrent, const QList &urlSeeds) +{ + foreach (const QUrl &urlSeed, urlSeeds) + Logger::instance()->addMessage(tr("URL seed '%1' was removed from torrent '%2'").arg(urlSeed.toString()).arg(torrent->name())); +} + +void Session::handleTorrentMetadataReceived(TorrentHandle *const torrent) +{ + saveTorrentResumeData(torrent); + + // Save metadata + const QDir resumeDataDir(m_resumeFolderPath); + QString torrentFile = resumeDataDir.absoluteFilePath(QString("%1.torrent").arg(torrent->hash())); + if (torrent->saveTorrentFile(torrentFile)) { + // Copy the torrent file to the export folder + if (m_torrentExportEnabled) + exportTorrentFile(torrent); + } + + emit torrentMetadataLoaded(torrent); +} + +void Session::handleTorrentPaused(TorrentHandle *const torrent) +{ + if (!torrent->hasError() && !torrent->hasMissingFiles()) + saveTorrentResumeData(torrent); + emit torrentPaused(torrent); +} + +void Session::handleTorrentResumed(TorrentHandle *const torrent) +{ + emit torrentResumed(torrent); +} + +void Session::handleTorrentChecked(TorrentHandle *const torrent) +{ + emit torrentFinishedChecking(torrent); +} + +void Session::handleTorrentFinished(TorrentHandle *const torrent) +{ + saveTorrentResumeData(torrent); + emit torrentFinished(torrent); + + qDebug("Checking if the torrent contains torrent files to download"); + // Check if there are torrent files inside + for (int i = 0; i < torrent->filesCount(); ++i) { + const QString torrentRelpath = torrent->filePath(i); + if (torrentRelpath.endsWith(".torrent", Qt::CaseInsensitive)) { + qDebug("Found possible recursive torrent download."); + const QString torrentFullpath = torrent->actualSavePath() + "/" + torrentRelpath; + qDebug("Full subtorrent path is %s", qPrintable(torrentFullpath)); + TorrentInfo torrentInfo = TorrentInfo::loadFromFile(torrentFullpath); + if (torrentInfo.isValid()) { + qDebug("emitting recursiveTorrentDownloadPossible()"); + emit recursiveTorrentDownloadPossible(torrent); + break; + } + else { + qDebug("Caught error loading torrent"); + Logger::instance()->addMessage(tr("Unable to decode %1 torrent file.").arg(fsutils::toNativePath(torrentFullpath)), Log::CRITICAL); + } + } + } + + Preferences *const pref = Preferences::instance(); + // Move .torrent file to another folder + if (pref->isFinishedTorrentExportEnabled()) + exportTorrentFile(torrent, TorrentExportFolder::Finished); + + if (!hasUnfinishedTorrents()) + emit allTorrentsFinished(); +} + +void Session::handleTorrentResumeDataReady(TorrentHandle *const torrent, const libtorrent::entry &data) +{ + --m_numResumeData; + writeResumeDataFile(torrent, data); +} + +void Session::handleTorrentResumeDataFailed(TorrentHandle *const torrent) +{ + Q_UNUSED(torrent) + --m_numResumeData; +} + +void Session::handleTorrentTrackerReply(TorrentHandle *const torrent, const QString &trackerUrl) +{ + emit trackerSuccess(torrent, trackerUrl); +} + +void Session::handleTorrentTrackerError(TorrentHandle *const torrent, const QString &trackerUrl) +{ + emit trackerError(torrent, trackerUrl); +} + +void Session::handleTorrentTrackerAuthenticationRequired(TorrentHandle *const torrent, const QString &trackerUrl) +{ + Q_UNUSED(trackerUrl); + emit trackerAuthenticationRequired(torrent); +} + +void Session::handleTorrentTrackerWarning(TorrentHandle *const torrent, const QString &trackerUrl) +{ + emit trackerWarning(torrent, trackerUrl); +} + +bool Session::hasPerTorrentRatioLimit() const +{ + foreach (TorrentHandle *const torrent, m_torrents) + if (torrent->ratioLimit() >= 0) return true; + + return false; +} + +void Session::initResumeFolder() +{ + m_resumeFolderPath = fsutils::expandPathAbs(fsutils::QDesktopServicesDataLocation() + RESUME_FOLDER); + QDir resumeFolderDir(m_resumeFolderPath); + if (resumeFolderDir.exists() || resumeFolderDir.mkpath(resumeFolderDir.absolutePath())) { + m_resumeFolderLock.setFileName(resumeFolderDir.absoluteFilePath("session.lock")); + if (!m_resumeFolderLock.open(QFile::WriteOnly)) { + throw std::runtime_error("Cannot write to torrent resume folder."); + } + } + else { + throw std::runtime_error("Cannot create torrent resume folder."); + } +} + +// Enable IP Filtering +void Session::enableIPFilter(const QString &filterPath, bool force) +{ + qDebug("Enabling IPFiler"); + if (!m_filterParser) { + m_filterParser = new FilterParserThread(m_nativeSession, this); + connect(m_filterParser.data(), SIGNAL(IPFilterParsed(int)), SLOT(handleIPFilterParsed(int))); + connect(m_filterParser.data(), SIGNAL(IPFilterError()), SLOT(handleIPFilterError())); + } + if (m_filterPath.isEmpty() || m_filterPath != fsutils::fromNativePath(filterPath) || force) { + m_filterPath = fsutils::fromNativePath(filterPath); + m_filterParser->processFilterFile(fsutils::fromNativePath(filterPath)); + } +} + +// Disable IP Filtering +void Session::disableIPFilter() +{ + qDebug("Disabling IPFilter"); + m_nativeSession->set_ip_filter(libt::ip_filter()); + if (m_filterParser) { + disconnect(m_filterParser.data(), 0, this, 0); + delete m_filterParser; + } + m_filterPath = ""; +} + +void Session::recursiveTorrentDownload(const InfoHash &hash) +{ + TorrentHandle *const torrent = m_torrents.value(hash); + if (!torrent) return; + + for (int i = 0; i < torrent->filesCount(); ++i) { + const QString torrentRelpath = torrent->filePath(i); + if (torrentRelpath.endsWith(".torrent")) { + Logger::instance()->addMessage( + tr("Recursive download of file %1 embedded in torrent %2" + , "Recursive download of test.torrent embedded in torrent test2") + .arg(fsutils::toNativePath(torrentRelpath)).arg(torrent->name())); + const QString torrentFullpath = torrent->savePath() + "/" + torrentRelpath; + + AddTorrentParams params; + // Passing the save path along to the sub torrent file + params.savePath = torrent->savePath(); + addTorrent(TorrentInfo::loadFromFile(torrentFullpath), params); + } + } +} + +SessionStatus Session::status() const +{ + return m_nativeSession->status(); +} + +CacheStatus Session::cacheStatus() const +{ + return m_nativeSession->get_cache_status(); +} + +// Set Proxy +void Session::setProxySettings(libt::proxy_settings proxySettings) +{ + qDebug() << Q_FUNC_INFO; + + proxySettings.proxy_peer_connections = Preferences::instance()->proxyPeerConnections(); + m_nativeSession->set_proxy(proxySettings); + + // Define environment variable + QString proxy_str; + switch(proxySettings.type) { + case libt::proxy_settings::http_pw: + proxy_str = QString("http://%1:%2@%3:%4").arg(String::fromStdString(proxySettings.username)).arg(String::fromStdString(proxySettings.password)) + .arg(String::fromStdString(proxySettings.hostname)).arg(proxySettings.port); + break; + case libt::proxy_settings::http: + proxy_str = QString("http://%1:%2").arg(String::fromStdString(proxySettings.hostname)).arg(proxySettings.port); + break; + case libt::proxy_settings::socks5: + proxy_str = QString("%1:%2").arg(String::fromStdString(proxySettings.hostname)).arg(proxySettings.port); + break; + case libt::proxy_settings::socks5_pw: + proxy_str = QString("%1:%2@%3:%4").arg(String::fromStdString(proxySettings.username)).arg(String::fromStdString(proxySettings.password)) + .arg(String::fromStdString(proxySettings.hostname)).arg(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 == libt::proxy_settings::socks5) || (proxySettings.type == libt::proxy_settings::socks5_pw)) + qputenv("sock_proxy", proxy_str.toLocal8Bit()); + else + qputenv("http_proxy", proxy_str.toLocal8Bit()); +} + +// Will resume torrents in backup directory +void Session::startUpTorrents() +{ + qDebug("Resuming torrents..."); + + const QDir resumeDataDir(m_resumeFolderPath); + QStringList fastresumes = resumeDataDir.entryList( + QStringList(QLatin1String("*.fastresume.*")), QDir::Files, QDir::Unsorted); + + typedef QPair PrioHashPair; + typedef std::vector PrioHashVector; + typedef std::greater PrioHashGreater; + std::priority_queue torrentQueue; + // Fastresume file name format: + // .fastresume. + // E.g.: + // fc8a15a2faf2734dbb1dc5f7afdc5c9beaeb1f59.fastresume.2 + QRegExp rx(QLatin1String("^([A-Fa-f0-9]{40})\\.fastresume\\.(\\d+)$")); + foreach (const QString &fastresume, fastresumes) { + if (rx.indexIn(fastresume) != -1) { + PrioHashPair p = qMakePair(rx.cap(2).toInt(), rx.cap(1)); + torrentQueue.push(p); + } + } + + QString filePath; + Logger *const logger = Logger::instance(); + + qDebug("Starting up torrents"); + qDebug("Priority queue size: %ld", (long)torrentQueue.size()); + // Resume downloads + while (!torrentQueue.empty()) { + const int prio = torrentQueue.top().first; + const QString hash = torrentQueue.top().second; + torrentQueue.pop(); + + QString fastresumePath = + resumeDataDir.absoluteFilePath(QString("%1.fastresume.%2").arg(hash).arg(prio)); + QByteArray data; + AddTorrentData resumeData; + if (readFile(fastresumePath, data) && loadTorrentResumeData(data, resumeData)) { + filePath = resumeDataDir.filePath(QString("%1.torrent").arg(hash)); + if (QFile(filePath).exists()) { + qDebug("Starting up torrent %s ...", qPrintable(hash)); + if (!addTorrent_impl(resumeData, MagnetUri(), TorrentInfo::loadFromFile(filePath), data)) + logger->addMessage(tr("Unable to resume torrent '%1'.", "e.g: Unable to resume torrent 'hash'.") + .arg(fsutils::toNativePath(hash)), Log::CRITICAL); + } + else { + logger->addMessage(tr("Unable to resume torrent '%1': torrent file not found.", "e.g: Unable to resume torrent 'hash': torrent file not found.") + .arg(fsutils::toNativePath(hash)), Log::CRITICAL); + } + } + } + + qDebug("Unfinished torrents resumed."); +} + +quint64 Session::getAlltimeDL() const +{ + return m_statistics->getAlltimeDL(); +} + +quint64 Session::getAlltimeUL() const +{ + return m_statistics->getAlltimeUL(); +} + +void Session::refresh() +{ + m_nativeSession->post_torrent_updates(); +} + +void Session::handleIPFilterParsed(int ruleCount) +{ + 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 Session::handleIPFilterError() +{ + Logger::instance()->addMessage(tr("Error: Failed to parse the provided IP filter."), Log::CRITICAL); + emit ipFilterParsed(true, 0); +} + +void Session::dispatchAlerts(std::auto_ptr alertPtr) +{ + QMutexLocker lock(&m_alertsMutex); + + bool wasEmpty = m_alerts.isEmpty(); + + m_alerts.append(alertPtr.release()); + + if (wasEmpty) { + m_alertsWaitCondition.wakeAll(); + QMetaObject::invokeMethod(this, "readAlerts", Qt::QueuedConnection); + } +} + +void Session::getPendingAlerts(QVector &out, ulong time) +{ + Q_ASSERT(out.empty()); + + QMutexLocker lock(&m_alertsMutex); + + while (m_alerts.empty()) + m_alertsWaitCondition.wait(&m_alertsMutex, time); + + m_alerts.swap(out); +} + +// Read alerts sent by the BitTorrent session +void Session::readAlerts() +{ + QVector alerts; + getPendingAlerts(alerts); + + foreach (libt::alert *const a, alerts) { + handleAlert(a); + delete a; + } +} + +void Session::handleAlert(libt::alert *a) +{ + try { + switch (a->type()) { + case libt::stats_alert::alert_type: + case libt::file_renamed_alert::alert_type: + case libt::file_completed_alert::alert_type: + case libt::torrent_finished_alert::alert_type: + case libt::save_resume_data_alert::alert_type: + case libt::save_resume_data_failed_alert::alert_type: + case libt::storage_moved_alert::alert_type: + case libt::storage_moved_failed_alert::alert_type: + case libt::torrent_paused_alert::alert_type: + case libt::tracker_error_alert::alert_type: + case libt::tracker_reply_alert::alert_type: + case libt::tracker_warning_alert::alert_type: + case libt::fastresume_rejected_alert::alert_type: + case libt::torrent_checked_alert::alert_type: + dispatchTorrentAlert(a); + break; + case libt::metadata_received_alert::alert_type: + handleMetadataReceivedAlert(static_cast(a)); + dispatchTorrentAlert(a); + break; + + case libt::state_update_alert::alert_type: + handleStateUpdateAlert(static_cast(a)); + break; + case libt::file_error_alert::alert_type: + handleFileErrorAlert(static_cast(a)); + break; + case libt::add_torrent_alert::alert_type: + handleAddTorrentAlert(static_cast(a)); + break; + case libt::torrent_removed_alert::alert_type: + handleTorrentRemovedAlert(static_cast(a)); + break; + case libt::torrent_deleted_alert::alert_type: + handleTorrentDeletedAlert(static_cast(a)); + break; + case libt::portmap_error_alert::alert_type: + handlePortmapWarningAlert(static_cast(a)); + break; + case libt::portmap_alert::alert_type: + handlePortmapAlert(static_cast(a)); + break; + case libt::peer_blocked_alert::alert_type: + handlePeerBlockedAlert(static_cast(a)); + break; + case libt::peer_ban_alert::alert_type: + handlePeerBanAlert(static_cast(a)); + break; + case libt::url_seed_alert::alert_type: + handleUrlSeedAlert(static_cast(a)); + break; + case libt::listen_succeeded_alert::alert_type: + handleListenSucceededAlert(static_cast(a)); + break; + case libt::listen_failed_alert::alert_type: + handleListenFailedAlert(static_cast(a)); + break; + case libt::external_ip_alert::alert_type: + handleExternalIPAlert(static_cast(a)); + break; + } + } + catch (std::exception &exc) { + qWarning() << "Caught exception in readAlerts(): " << String::fromStdString(exc.what()); + } +} + +void Session::dispatchTorrentAlert(libt::alert *a) +{ + TorrentHandlePrivate *const torrent = m_torrents.value(static_cast(a)->handle.info_hash()); + if (torrent) + torrent->handleAlert(a); +} + +void Session::handleAddTorrentAlert(libtorrent::add_torrent_alert *p) +{ + Logger *const logger = Logger::instance(); + if (p->error) { + qDebug("/!\\ Error: Failed to add torrent!"); + QString msg = String::fromStdString(p->message()); + logger->addMessage(tr("Couldn't add torrent. Reason: %1").arg(msg), Log::WARNING); + emit addTorrentFailed(msg); + return; + } + + // Magnet added for preload its metadata + if (!m_addingTorrents.contains(p->handle.info_hash())) return; + + AddTorrentData data = m_addingTorrents.take(p->handle.info_hash()); + + TorrentHandle *const torrent = new TorrentHandle(this, p->handle, data); + m_torrents.insert(torrent->hash(), torrent); + + Preferences *const pref = Preferences::instance(); +#if LIBTORRENT_VERSION_NUM < 10000 + try { + p->handle.set_max_connections(pref->getMaxConnecsPerTorrent()); + p->handle.set_max_uploads(pref->getMaxUploadsPerTorrent()); + } + catch (std::exception &) {} +#endif + + bool fromMagnetUri = !torrent->hasMetadata(); + + if (data.resumed) { + logger->addMessage(tr("'%1' resumed. (fast resume)", "'torrent name' was resumed. (fast resume)") + .arg(torrent->name())); + } + else { + qDebug("This is a NEW torrent (first time)..."); + + // The following is useless for newly added magnet + if (!fromMagnetUri) { + // Backup torrent file + const QDir resumeDataDir(m_resumeFolderPath); + const QString newFile = resumeDataDir.absoluteFilePath(QString("%1.torrent").arg(torrent->hash())); + if (torrent->saveTorrentFile(newFile)) { + // Copy the torrent file to the export folder + if (m_torrentExportEnabled) + exportTorrentFile(torrent); + } + else { + logger->addMessage(tr("Couldn't save 1%.torrent").arg(torrent->hash()), Log::CRITICAL); + } + } + + bool addPaused = data.addPaused; + if (data.addPaused == TriStateBool::Undefined) + addPaused = pref->addTorrentsInPause(); + + // Start torrent because it was added in paused state + if (!addPaused) + torrent->resume(); + logger->addMessage(tr("'%1' added to download list.", "'torrent name' was added to download list.") + .arg(torrent->name())); + } + + saveTorrentResumeData(torrent); + + // Send torrent addition signal + emit torrentAdded(torrent); +} + +void Session::handleTorrentRemovedAlert(libtorrent::torrent_removed_alert *p) +{ + if (m_loadedMetadata.contains(p->info_hash)) + emit metadataLoaded(m_loadedMetadata.take(p->info_hash)); +} + +void Session::handleTorrentDeletedAlert(libt::torrent_deleted_alert *p) +{ + if (m_savePathsToRemove.contains(p->info_hash)) { + qDebug("A torrent was deleted from the hard disk, attempting to remove the root folder too..."); + const QString dirpath = m_savePathsToRemove.take(p->info_hash); + qDebug() << "Removing save path: " << dirpath << "..."; + bool ok = fsutils::smartRemoveEmptyFolderTree(dirpath); + Q_UNUSED(ok); + qDebug() << "Folder was removed: " << ok; + } +} + +void Session::handleMetadataReceivedAlert(libt::metadata_received_alert *p) +{ + InfoHash hash = p->handle.info_hash(); + + if (m_loadedMetadata.contains(hash)) { + --m_extraLimit; + adjustLimits(); +#if LIBTORRENT_VERSION_NUM < 10000 + m_loadedMetadata[hash] = TorrentInfo(&p->handle.get_torrent_info()); +#else + m_loadedMetadata[hash] = TorrentInfo(p->handle.torrent_file()); +#endif + m_nativeSession->remove_torrent(p->handle, libt::session::delete_files); + } +} + +void Session::handleFileErrorAlert(libt::file_error_alert *p) +{ + qDebug() << Q_FUNC_INFO; + // NOTE: Check this function! + TorrentHandle *const torrent = m_torrents.value(p->handle.info_hash()); + if (torrent) { + QString msg = String::fromStdString(p->message()); + Logger::instance()->addMessage(tr("An I/O error occurred, '%1' paused. %2") + .arg(torrent->name()).arg(msg)); + emit fullDiskError(torrent, msg); + } +} + +void Session::handlePortmapWarningAlert(libt::portmap_error_alert *p) +{ + Logger::instance()->addMessage(tr("UPnP/NAT-PMP: Port mapping failure, message: %1").arg(String::fromStdString(p->message())), Log::CRITICAL); +} + +void Session::handlePortmapAlert(libt::portmap_alert *p) +{ + qDebug("UPnP Success, msg: %s", p->message().c_str()); + Logger::instance()->addMessage(tr("UPnP/NAT-PMP: Port mapping successful, message: %1").arg(String::fromStdString(p->message())), Log::INFO); +} + +void Session::handlePeerBlockedAlert(libt::peer_blocked_alert *p) +{ + boost::system::error_code ec; + std::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 libt::peer_blocked_alert::ip_filter: + reason = tr("due to IP filter.", "this peer was blocked due to ip filter."); + break; + case libt::peer_blocked_alert::port_filter: + reason = tr("due to port filter.", "this peer was blocked due to port filter."); + break; + case libt::peer_blocked_alert::i2p_mixed: + reason = tr("due to i2p mixed mode restrictions.", "this peer was blocked due to i2p mixed mode restrictions."); + break; + case libt::peer_blocked_alert::privileged_ports: + reason = tr("because it has a low port.", "this peer was blocked because it has a low port."); + break; + case libt::peer_blocked_alert::utp_disabled: + reason = tr("because μTP is disabled.", "this peer was blocked because μTP is disabled."); + break; + case libt::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 Session::handlePeerBanAlert(libt::peer_ban_alert *p) +{ + boost::system::error_code ec; + std::string ip = p->ip.address().to_string(ec); + if (!ec) + Logger::instance()->addPeer(QString::fromLatin1(ip.c_str()), false); +} + +void Session::handleUrlSeedAlert(libt::url_seed_alert *p) +{ + Logger::instance()->addMessage(tr("Url seed lookup failed for url: %1, message: %2").arg(String::fromStdString(p->url)).arg(String::fromStdString(p->message())), Log::CRITICAL); +} + +void Session::handleListenSucceededAlert(libt::listen_succeeded_alert *p) +{ + boost::system::error_code ec; + QString proto = "TCP"; +#if LIBTORRENT_VERSION_NUM >= 10000 + if (p->sock_type == libt::listen_succeeded_alert::udp) + proto = "UDP"; + else if (p->sock_type == libt::listen_succeeded_alert::tcp) + proto = "TCP"; + else if (p->sock_type == libt::listen_succeeded_alert::tcp_ssl) + proto = "TCP_SSL"; +#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 = m_nativeSession->get_torrents(); + std::vector::iterator it = torrents.begin(); + std::vector::iterator itend = torrents.end(); + for ( ; it != itend; ++it) + it->force_reannounce(); +} + +void Session::handleListenFailedAlert(libt::listen_failed_alert *p) +{ + boost::system::error_code ec; + QString proto = "TCP"; +#if LIBTORRENT_VERSION_NUM >= 10000 + if (p->sock_type == libt::listen_failed_alert::udp) + proto = "UDP"; + else if (p->sock_type == libt::listen_failed_alert::tcp) + proto = "TCP"; + else if (p->sock_type == libt::listen_failed_alert::tcp_ssl) + proto = "TCP_SSL"; + else if (p->sock_type == libt::listen_failed_alert::i2p) + proto = "I2P"; + else if (p->sock_type == libt::listen_failed_alert::socks5) + proto = "SOCKS5"; +#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(String::fromStdString(p->error.message())), Log::CRITICAL); +} + +void Session::handleExternalIPAlert(libt::external_ip_alert *p) +{ + boost::system::error_code ec; + Logger::instance()->addMessage(tr("External IP: %1", "e.g. External IP: 192.168.0.1").arg(p->external_address.to_string(ec).c_str()), Log::INFO); +} + +void Session::handleStateUpdateAlert(libt::state_update_alert *p) +{ + foreach (const libt::torrent_status &status, p->status) { + TorrentHandle *const torrent = m_torrents.value(status.info_hash); + if (torrent) { + static_cast(torrent)->handleStateUpdate(status); + emit torrentStatusUpdated(torrent); + } + } + + TorrentStatusReport torrentStatusReport; + foreach (TorrentHandle *const torrent, m_torrents) { + if (torrent->isDownloading()) + ++torrentStatusReport.nbDownloading; + if (torrent->isUploading()) + ++torrentStatusReport.nbSeeding; + if (torrent->isCompleted()) + ++torrentStatusReport.nbCompleted; + if (torrent->isPaused()) + ++torrentStatusReport.nbPaused; + if (torrent->isResumed()) + ++torrentStatusReport.nbResumed; + if (torrent->isActive()) + ++torrentStatusReport.nbActive; + if (torrent->isInactive()) + ++torrentStatusReport.nbInactive; + } + + emit torrentsUpdated(torrentStatusReport); +} + +bool readFile(const QString &path, QByteArray &buf) +{ + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) { + qDebug("Cannot read file %s: %s", qPrintable(path), qPrintable(file.errorString())); + return false; + } + + buf = file.readAll(); + return true; +} + +bool loadTorrentResumeData(const QByteArray &data, AddTorrentData &out) +{ + out = AddTorrentData(); + out.resumed = true; + + libt::lazy_entry fast; + libt::error_code ec; + libt::lazy_bdecode(data.constData(), data.constData() + data.size(), fast, ec); + if ((fast.type() != libt::lazy_entry::dict_t) && !ec) return false; + + out.addedTime = QDateTime::fromTime_t(fast.dict_find_int_value("qBt-addedTime")); + out.savePath = fsutils::fromNativePath(QString::fromUtf8(fast.dict_find_string_value("qBt-savePath").c_str())); + out.ratioLimit = QString::fromUtf8(fast.dict_find_string_value("qBt-ratioLimit").c_str()).toDouble(); + out.label = QString::fromUtf8(fast.dict_find_string_value("qBt-label").c_str()); + out.name = QString::fromUtf8(fast.dict_find_string_value("qBt-name").c_str()); + out.hasSeedStatus = fast.dict_find_int_value("qBt-seedStatus"); + out.disableTempPath = fast.dict_find_int_value("qBt-tempPathDisabled"); + + return true; +} + +bool Session::writeResumeDataFile(TorrentHandle *const torrent, const libt::entry &data) +{ + const QDir resumeDataDir(m_resumeFolderPath); + + QStringList filters(QString("%1.fastresume.*").arg(torrent->hash())); + const QStringList files = resumeDataDir.entryList(filters, QDir::Files, QDir::Unsorted); + foreach (const QString &file, files) + fsutils::forceRemove(resumeDataDir.absoluteFilePath(file)); + + QString filename = QString("%1.fastresume.%2").arg(torrent->hash()).arg(torrent->queuePosition()); + QString filepath = resumeDataDir.absoluteFilePath(filename); + + qDebug("Saving resume data in %s", qPrintable(filepath)); + QFile resumeFile(filepath); + QVector out; + libt::bencode(std::back_inserter(out), data); + if (resumeFile.open(QIODevice::WriteOnly)) + return (resumeFile.write(&out[0], out.size()) == out.size()); + + return false; +} + +void torrentQueuePositionUp(const libt::torrent_handle &handle) +{ + try { + handle.queue_position_up(); + } + catch (std::exception &exc) { + qDebug() << "torrent_handle::queue_position_up() fails:" << exc.what(); + } +} + +void torrentQueuePositionDown(const libt::torrent_handle &handle) +{ + try { + handle.queue_position_down(); + } + catch (std::exception &exc) { + qDebug() << "torrent_handle::queue_position_down() fails:" << exc.what(); + } +} + +void torrentQueuePositionTop(const libt::torrent_handle &handle) +{ + try { + handle.queue_position_top(); + } + catch (std::exception &exc) { + qDebug() << "torrent_handle::queue_position_top() fails:" << exc.what(); + } +} + +void torrentQueuePositionBottom(const libt::torrent_handle &handle) +{ + try { + handle.queue_position_bottom(); + } + catch (std::exception &exc) { + qDebug() << "torrent_handle::queue_position_bottom() fails:" << exc.what(); + } +} diff --git a/src/core/bittorrent/session.h b/src/core/bittorrent/session.h new file mode 100644 index 000000000..fa64c40d2 --- /dev/null +++ b/src/core/bittorrent/session.h @@ -0,0 +1,365 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * 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. + */ + +#ifndef BITTORRENT_SESSION_H +#define BITTORRENT_SESSION_H + +#include +#include +#include +#include +#include +#include + +#include "core/tristatebool.h" +#include "core/types.h" +#include "private/sessionprivate.h" +#include "torrentinfo.h" + +namespace libtorrent +{ + class session; + struct add_torrent_params; + struct pe_settings; + struct proxy_settings; + struct session_settings; + struct session_status; + + class alert; + struct torrent_alert; + struct state_update_alert; + struct stats_alert; + struct add_torrent_alert; + struct torrent_checked_alert; + struct torrent_finished_alert; + struct torrent_removed_alert; + struct torrent_deleted_alert; + struct torrent_paused_alert; + struct torrent_resumed_alert; + struct save_resume_data_alert; + struct save_resume_data_failed_alert; + struct file_renamed_alert; + struct storage_moved_alert; + struct storage_moved_failed_alert; + struct metadata_received_alert; + struct file_error_alert; + struct file_completed_alert; + struct tracker_error_alert; + struct tracker_reply_alert; + struct tracker_warning_alert; + struct portmap_error_alert; + struct portmap_alert; + struct peer_blocked_alert; + struct peer_ban_alert; + struct fastresume_rejected_alert; + struct url_seed_alert; + struct listen_succeeded_alert; + struct listen_failed_alert; + struct external_ip_alert; +} + +QT_BEGIN_NAMESPACE +class QTimer; +class QStringList; +QT_END_NAMESPACE + +class FilterParserThread; +class BandwidthScheduler; +class Statistics; + +typedef QPair QStringPair; + +namespace BitTorrent +{ + class InfoHash; + class CacheStatus; + class SessionStatus; + class TorrentHandle; + class Tracker; + class MagnetUri; + class TrackerEntry; + struct AddTorrentData; + + struct AddTorrentParams + { + QString name; + QString label; + QString savePath; + bool disableTempPath; // e.g. for imported torrents + bool sequential; + TriStateBool addPaused; + QVector filePriorities; // used if TorrentInfo is set + bool ignoreShareRatio; + bool skipChecking; + + AddTorrentParams(); + }; + + struct TorrentStatusReport + { + uint nbDownloading; + uint nbSeeding; + uint nbCompleted; + uint nbActive; + uint nbInactive; + uint nbPaused; + uint nbResumed; + + TorrentStatusReport(); + }; + + class Session : public QObject, public SessionPrivate + { + Q_OBJECT + Q_DISABLE_COPY(Session) + + public: + static void initInstance(); + static void freeInstance(); + static Session *instance(); + + bool isDHTEnabled() const; + bool isLSDEnabled() const; + bool isPexEnabled() const; + bool isQueueingEnabled() const; + qreal globalMaxRatio() const; + + TorrentHandle *findTorrent(const InfoHash &hash) const; + QHash torrents() const; + bool hasActiveTorrents() const; + bool hasUnfinishedTorrents() const; + SessionStatus status() const; + CacheStatus cacheStatus() const; + quint64 getAlltimeDL() const; + quint64 getAlltimeUL() const; + int downloadRateLimit() const; + int uploadRateLimit() const; + bool isListening() const; + + void changeSpeedLimitMode(bool alternative); + void setDownloadRateLimit(int rate); + void setUploadRateLimit(int rate); + void setGlobalMaxRatio(qreal ratio); + void enableIPFilter(const QString &filterPath, bool force = false); + void disableIPFilter(); + void banIP(const QString &ip); + + bool isKnownTorrent(const InfoHash &hash) const; + bool addTorrent(QString source, const AddTorrentParams ¶ms = AddTorrentParams()); + bool addTorrent(const TorrentInfo &torrentInfo, const AddTorrentParams ¶ms = AddTorrentParams()); + bool deleteTorrent(const QString &hash, bool deleteLocalFiles = false); + bool loadMetadata(const QString &magnetUri); + bool cancelLoadMetadata(const InfoHash &hash); + + void recursiveTorrentDownload(const InfoHash &hash); + void increaseTorrentsPriority(const QStringList &hashes); + void decreaseTorrentsPriority(const QStringList &hashes); + void topTorrentsPriority(const QStringList &hashes); + void bottomTorrentsPriority(const QStringList &hashes); + + signals: + void torrentsUpdated(const BitTorrent::TorrentStatusReport &torrentStatusReport = BitTorrent::TorrentStatusReport()); + void addTorrentFailed(const QString &error); + void torrentAdded(BitTorrent::TorrentHandle *const torrent); + void torrentAboutToBeRemoved(BitTorrent::TorrentHandle *const torrent); + void torrentStatusUpdated(BitTorrent::TorrentHandle *const torrent); + void torrentPaused(BitTorrent::TorrentHandle *const torrent); + void torrentResumed(BitTorrent::TorrentHandle *const torrent); + void torrentFinished(BitTorrent::TorrentHandle *const torrent); + void torrentFinishedChecking(BitTorrent::TorrentHandle *const torrent); + void torrentSavePathChanged(BitTorrent::TorrentHandle *const torrent); + void allTorrentsFinished(); + void metadataLoaded(const BitTorrent::TorrentInfo &info); + void torrentMetadataLoaded(BitTorrent::TorrentHandle *const torrent); + void fullDiskError(BitTorrent::TorrentHandle *const torrent, const QString &msg); + void trackerSuccess(BitTorrent::TorrentHandle *const torrent, const QString &tracker); + void trackerWarning(BitTorrent::TorrentHandle *const torrent, const QString &tracker); + void trackerError(BitTorrent::TorrentHandle *const torrent, const QString &tracker); + void trackerAuthenticationRequired(BitTorrent::TorrentHandle *const torrent); + void recursiveTorrentDownloadPossible(BitTorrent::TorrentHandle *const torrent); + void speedLimitModeChanged(bool alternative); + void ipFilterParsed(bool error, int ruleCount); + void trackersAdded(BitTorrent::TorrentHandle *const torrent, const QList &trackers); + void trackersRemoved(BitTorrent::TorrentHandle *const torrent, const QList &trackers); + void trackersChanged(BitTorrent::TorrentHandle *const torrent); + void trackerlessStateChanged(BitTorrent::TorrentHandle *const torrent, bool trackerless); + void downloadFromUrlFailed(const QString &url, const QString &reason); + void downloadFromUrlFinished(const QString &url); + + private slots: + void configure(); + void readAlerts(); + void refresh(); + void processBigRatios(); + void generateResumeData(bool final = false); + void handleIPFilterParsed(int ruleCount); + void handleIPFilterError(); + void handleDownloadFinished(const QString &url, const QString &filePath); + void handleDownloadFailed(const QString &url, const QString &reason); + void handleRedirectedToMagnet(const QString &url, const QString &magnetUri); + + private: + explicit Session(QObject *parent = 0); + ~Session(); + + bool hasPerTorrentRatioLimit() const; + + void initResumeFolder(); + void loadState(); + void saveState(); + + // Session configuration + void setSessionSettings(); + void setProxySettings(libtorrent::proxy_settings proxySettings); + void adjustLimits(); + void adjustLimits(libtorrent::session_settings &sessionSettings); + void setListeningPort(int port); + void setDefaultSavePath(const QString &path); + void setDefaultTempPath(const QString &path = QString()); + void preAllocateAllFiles(bool b); + void setMaxConnectionsPerTorrent(int max); + void setMaxUploadsPerTorrent(int max); + void enableLSD(bool enable); + void enableDHT(bool enable); + + void setAppendLabelToSavePath(bool append); + void setAppendExtension(bool append); + + void startUpTorrents(); + bool addTorrent_impl(const AddTorrentData &addData, const MagnetUri &magnetUri, + const TorrentInfo &torrentInfo = TorrentInfo(), + const QByteArray &fastresumeData = QByteArray()); + + void updateRatioTimer(); + void exportTorrentFile(TorrentHandle *const torrent, TorrentExportFolder folder = TorrentExportFolder::Regular); + void exportTorrentFiles(QString path); + void saveTorrentResumeData(TorrentHandle *const torrent); + + void handleAlert(libtorrent::alert *a); + void dispatchTorrentAlert(libtorrent::alert *a); + void handleAddTorrentAlert(libtorrent::add_torrent_alert *p); + void handleStateUpdateAlert(libtorrent::state_update_alert *p); + void handleMetadataReceivedAlert(libtorrent::metadata_received_alert *p); + void handleFileErrorAlert(libtorrent::file_error_alert *p); + void handleTorrentRemovedAlert(libtorrent::torrent_removed_alert *p); + void handleTorrentDeletedAlert(libtorrent::torrent_deleted_alert *p); + void handlePortmapWarningAlert(libtorrent::portmap_error_alert *p); + void handlePortmapAlert(libtorrent::portmap_alert *p); + void handlePeerBlockedAlert(libtorrent::peer_blocked_alert *p); + void handlePeerBanAlert(libtorrent::peer_ban_alert *p); + void handleUrlSeedAlert(libtorrent::url_seed_alert *p); + void handleListenSucceededAlert(libtorrent::listen_succeeded_alert *p); + void handleListenFailedAlert(libtorrent::listen_failed_alert *p); + void handleExternalIPAlert(libtorrent::external_ip_alert *p); + + bool isTempPathEnabled() const; + bool isAppendExtensionEnabled() const; + bool useAppendLabelToSavePath() const; + #ifndef DISABLE_COUNTRIES_RESOLUTION + bool isResolveCountriesEnabled() const; + #endif + QString defaultSavePath() const; + QString tempPath() const; + void handleTorrentRatioLimitChanged(TorrentHandle *const torrent); + void handleTorrentSavePathChanged(TorrentHandle *const torrent); + void handleTorrentMetadataReceived(TorrentHandle *const torrent); + void handleTorrentPaused(TorrentHandle *const torrent); + void handleTorrentResumed(TorrentHandle *const torrent); + void handleTorrentChecked(TorrentHandle *const torrent); + void handleTorrentFinished(TorrentHandle *const torrent); + void handleTorrentTrackersAdded(TorrentHandle *const torrent, const QList &newTrackers); + void handleTorrentTrackersRemoved(TorrentHandle *const torrent, const QList &deletedTrackers); + void handleTorrentTrackersChanged(TorrentHandle *const torrent); + void handleTorrentUrlSeedsAdded(TorrentHandle *const torrent, const QList &newUrlSeeds); + void handleTorrentUrlSeedsRemoved(TorrentHandle *const torrent, const QList &urlSeeds); + void handleTorrentResumeDataReady(TorrentHandle *const torrent, const libtorrent::entry &data); + void handleTorrentResumeDataFailed(TorrentHandle *const torrent); + void handleTorrentTrackerReply(TorrentHandle *const torrent, const QString &trackerUrl); + void handleTorrentTrackerWarning(TorrentHandle *const torrent, const QString &trackerUrl); + void handleTorrentTrackerError(TorrentHandle *const torrent, const QString &trackerUrl); + void handleTorrentTrackerAuthenticationRequired(TorrentHandle *const torrent, const QString &trackerUrl); + + void saveResumeData(); + bool writeResumeDataFile(TorrentHandle *const torrent, const libtorrent::entry &data); + + void dispatchAlerts(std::auto_ptr alertPtr); + void getPendingAlerts(QVector &out, ulong time = 0); + + // BitTorrent + libtorrent::session *m_nativeSession; + + bool m_LSDEnabled; + bool m_DHTEnabled; + bool m_PeXEnabled; + bool m_queueingEnabled; + bool m_torrentExportEnabled; + bool m_finishedTorrentExportEnabled; + bool m_preAllocateAll; + qreal m_globalMaxRatio; + int m_numResumeData; + int m_extraLimit; + #ifndef DISABLE_COUNTRIES_RESOLUTION + bool m_geoipDBLoaded; + bool m_resolveCountries; + #endif + bool m_appendLabelToSavePath; + bool m_appendExtension; + uint m_refreshInterval; + MaxRatioAction m_highRatioAction; + QString m_defaultSavePath; + QString m_tempPath; + QString m_filterPath; + QString m_resumeFolderPath; + QFile m_resumeFolderLock; + QHash m_savePathsToRemove; + + QTimer *m_refreshTimer; + QTimer *m_bigRatioTimer; + QTimer *m_resumeDataTimer; + Statistics *m_statistics; + // IP filtering + QPointer m_filterParser; + QPointer m_bwScheduler; + // Tracker + QPointer m_tracker; + + QHash m_loadedMetadata; + QHash m_torrents; + QHash m_addingTorrents; + QHash m_downloadedTorrents; + + QMutex m_alertsMutex; + QWaitCondition m_alertsWaitCondition; + QVector m_alerts; + + static Session *m_instance; + }; +} + +#endif // BITTORRENT_SESSION_H diff --git a/src/core/bittorrent/sessionstatus.cpp b/src/core/bittorrent/sessionstatus.cpp new file mode 100644 index 000000000..81c7f700f --- /dev/null +++ b/src/core/bittorrent/sessionstatus.cpp @@ -0,0 +1,96 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include "sessionstatus.h" + +using namespace BitTorrent; + +SessionStatus::SessionStatus(const libtorrent::session_status &nativeStatus) + : m_nativeStatus(nativeStatus) +{ +} + +bool SessionStatus::hasIncomingConnections() const +{ + return m_nativeStatus.has_incoming_connections; +} + +int SessionStatus::payloadDownloadRate() const +{ + return m_nativeStatus.payload_download_rate; +} + +int SessionStatus::payloadUploadRate() const +{ + return m_nativeStatus.payload_upload_rate; +} + +qlonglong SessionStatus::totalDownload() const +{ + return m_nativeStatus.total_download; +} + +qlonglong SessionStatus::totalUpload() const +{ + return m_nativeStatus.total_upload; +} + +qlonglong SessionStatus::totalPayloadDownload() const +{ + return m_nativeStatus.total_payload_download; +} + +qlonglong SessionStatus::totalPayloadUpload() const +{ + return m_nativeStatus.total_payload_upload; +} + +qlonglong SessionStatus::totalWasted() const +{ + return (m_nativeStatus.total_redundant_bytes + m_nativeStatus.total_failed_bytes); +} + +int SessionStatus::diskReadQueue() const +{ + return m_nativeStatus.disk_read_queue; +} + +int SessionStatus::diskWriteQueue() const +{ + return m_nativeStatus.disk_write_queue; +} + +int SessionStatus::dhtNodes() const +{ + return m_nativeStatus.dht_nodes; +} + +int SessionStatus::peersCount() const +{ + return m_nativeStatus.num_peers; +} diff --git a/src/core/bittorrent/sessionstatus.h b/src/core/bittorrent/sessionstatus.h new file mode 100644 index 000000000..f0a18e2f5 --- /dev/null +++ b/src/core/bittorrent/sessionstatus.h @@ -0,0 +1,69 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * 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. + */ + +#ifndef BITTORRENT_SESSIONSTATUS_H +#define BITTORRENT_SESSIONSTATUS_H + +#include +#include + +namespace BitTorrent +{ + class SessionStatus + { + public: + SessionStatus(const libtorrent::session_status &nativeStatus); + + bool hasIncomingConnections() const; + + // Return current download rate for the BT + // session. Payload means that it only take into + // account "useful" part of the rate + int payloadDownloadRate() const; + + // Return current upload rate for the BT + // session. Payload means that it only take into + // account "useful" part of the rate + int payloadUploadRate() const; + + qlonglong totalDownload() const; + qlonglong totalUpload() const; + qlonglong totalPayloadDownload() const; + qlonglong totalPayloadUpload() const; + qlonglong totalWasted() const; + int diskReadQueue() const; + int diskWriteQueue() const; + int dhtNodes() const; + int peersCount() const; + + private: + libtorrent::session_status m_nativeStatus; + }; +} + +#endif // BITTORRENT_SESSIONSTATUS_H diff --git a/src/core/bittorrent/torrentcreatorthread.cpp b/src/core/bittorrent/torrentcreatorthread.cpp new file mode 100644 index 000000000..fba0ed5d2 --- /dev/null +++ b/src/core/bittorrent/torrentcreatorthread.cpp @@ -0,0 +1,166 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2010 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 +#include + +#include +#include +#include + +#include "core/fs_utils.h" +#include "core/misc.h" +#include "core/utils/string.h" +#include "torrentcreatorthread.h" + +namespace libt = libtorrent; +using namespace BitTorrent; + +// do not include files and folders whose +// name starts with a . +bool fileFilter(const std::string &f) +{ + return (libt::filename(f)[0] != '.'); +} + +TorrentCreatorThread::TorrentCreatorThread(QObject *parent) + : QThread(parent) +{ +} + +TorrentCreatorThread::~TorrentCreatorThread() +{ + m_abort = true; + wait(); +} + +void TorrentCreatorThread::create(const QString &inputPath, const QString &savePath, const QStringList &trackers, + const QStringList &urlSeeds, const QString &comment, bool isPrivate, int pieceSize) +{ + m_inputPath = fsutils::fromNativePath(inputPath); + m_savePath = fsutils::fromNativePath(savePath); + if (QFile(m_savePath).exists()) + fsutils::forceRemove(m_savePath); + m_trackers = trackers; + m_urlSeeds = urlSeeds; + m_comment = comment; + m_private = isPrivate; + m_pieceSize = pieceSize; + m_abort = false; + + start(); +} + +void TorrentCreatorThread::sendProgressSignal(int numHashes, int numPieces) +{ + emit updateProgress(static_cast((numHashes * 100.) / numPieces)); +} + +void TorrentCreatorThread::abortCreation() +{ + m_abort = true; +} + +void TorrentCreatorThread::run() +{ + emit updateProgress(0); + + QString creator_str("qBittorrent " VERSION); + try { + libt::file_storage fs; + // Adding files to the torrent + libt::add_files(fs, String::toStdString(fsutils::toNativePath(m_inputPath)), fileFilter); + if (m_abort) return; + + libt::create_torrent t(fs, m_pieceSize); + + // Add url seeds + foreach (const QString &seed, m_urlSeeds) + t.add_url_seed(String::toStdString(seed.trimmed())); + + int tier = 0; + bool newline = false; + foreach (const QString &tracker, m_trackers) { + if (tracker.isEmpty()) { + if (newline) + continue; + ++tier; + newline = true; + continue; + } + t.add_tracker(String::toStdString(tracker.trimmed()), tier); + newline = false; + } + if (m_abort) return; + + // calculate the hash for all pieces + const QString parentPath = fsutils::branchPath(m_inputPath) + "/"; + libt::set_piece_hashes(t, String::toStdString(fsutils::toNativePath(parentPath)), boost::bind(&TorrentCreatorThread::sendProgressSignal, this, _1, t.num_pieces())); + // Set qBittorrent as creator and add user comment to + // torrent_info structure + t.set_creator(creator_str.toUtf8().constData()); + t.set_comment(m_comment.toUtf8().constData()); + // Is private ? + t.set_priv(m_private); + if (m_abort) return; + + // create the torrent and print it to out + qDebug("Saving to %s", qPrintable(m_savePath)); +#ifdef _MSC_VER + wchar_t *savePathW = new wchar_t[m_savePath.length() + 1]; + int len = fsutils::toNativePath(m_savePath).toWCharArray(savePathW); + savePathW[len] = L'\0'; + std::ofstream outfile(savePathW, std::ios_base::out | std::ios_base::binary); + delete[] savePathW; +#else + std::ofstream outfile(fsutils::toNativePath(m_savePath).toLocal8Bit().constData(), std::ios_base::out | std::ios_base::binary); +#endif + if (outfile.fail()) + throw std::exception(); + + libt::bencode(std::ostream_iterator(outfile), t.generate()); + outfile.close(); + + emit updateProgress(100); + emit creationSuccess(m_savePath, parentPath); + } + catch (std::exception& e) { + emit creationFailure(String::fromStdString(e.what())); + } +} diff --git a/src/core/bittorrent/torrentcreatorthread.h b/src/core/bittorrent/torrentcreatorthread.h new file mode 100644 index 000000000..3816e3489 --- /dev/null +++ b/src/core/bittorrent/torrentcreatorthread.h @@ -0,0 +1,73 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2010 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 BITTORRENT_TORRENTCREATORTHREAD_H +#define BITTORRENT_TORRENTCREATORTHREAD_H + +#include +#include + +namespace BitTorrent +{ + class TorrentCreatorThread : public QThread + { + Q_OBJECT + + public: + TorrentCreatorThread(QObject *parent = 0); + ~TorrentCreatorThread(); + + void create(const QString &inputPath, const QString &savePath, const QStringList &trackers, + const QStringList &urlSeeds, const QString &comment, bool isPrivate, int pieceSize); + void abortCreation(); + + protected: + void run(); + + signals: + void creationFailure(const QString &msg); + void creationSuccess(const QString &path, const QString &branchPath); + void updateProgress(int progress); + + private: + void sendProgressSignal(int numHashes, int numPieces); + + QString m_inputPath; + QString m_savePath; + QStringList m_trackers; + QStringList m_urlSeeds; + QString m_comment; + bool m_private; + int m_pieceSize; + bool m_abort; + }; +} + +#endif // BITTORRENT_TORRENTCREATORTHREAD_H diff --git a/src/core/bittorrent/torrenthandle.cpp b/src/core/bittorrent/torrenthandle.cpp new file mode 100644 index 000000000..f47d09253 --- /dev/null +++ b/src/core/bittorrent/torrenthandle.cpp @@ -0,0 +1,1887 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_WIN +#include +#endif + +#include "core/fs_utils.h" +#include "core/misc.h" +#include "core/logger.h" +#include "core/preferences.h" +#include "core/utils/string.h" +#include "session.h" +#include "peerinfo.h" +#include "trackerentry.h" +#include "torrenthandle.h" + +static const char QB_EXT[] = ".!qB"; + +namespace libt = libtorrent; +using namespace BitTorrent; + +// TrackerInfo + +TrackerInfo::TrackerInfo() + : numPeers(0) +{ +} + +// TorrentState + +TorrentState::TorrentState(int value) + : m_value(value) +{ +} + +QString TorrentState::toString() const +{ + switch (m_value) { + case Error: + return QLatin1String("error"); + case Uploading: + return QLatin1String("uploading"); + case PausedUploading: + return QLatin1String("pausedUP"); + case QueuedUploading: + return QLatin1String("queuedUP"); + case StalledUploading: + return QLatin1String("stalledUP"); + case CheckingUploading: + return QLatin1String("checkingUP"); + case ForcedUploading: + return QLatin1String("forcedUP"); + case Allocating: + return QLatin1String("allocating"); + case Downloading: + return QLatin1String("downloading"); + case DownloadingMetadata: + return QLatin1String("metaDL"); + case PausedDownloading: + return QLatin1String("pausedDL"); + case QueuedDownloading: + return QLatin1String("queuedDL"); + case StalledDownloading: + return QLatin1String("stalledDL"); + case CheckingDownloading: + return QLatin1String("checkingDL"); + case ForcedDownloading: + return QLatin1String("forcedDL"); + default: + return QLatin1String("unknown"); + } +} + +TorrentState::operator int() const +{ + return m_value; +} + +// AddTorrentData + +AddTorrentData::AddTorrentData() {} + +AddTorrentData::AddTorrentData(const AddTorrentParams &in) + : resumed(false) + , name(in.name) + , label(in.label) + , savePath(in.savePath) + , disableTempPath(in.disableTempPath) + , sequential(in.sequential) + , hasSeedStatus(in.skipChecking) + , addPaused(in.addPaused) + , filePriorities(in.filePriorities) + , ratioLimit(TorrentHandle::USE_GLOBAL_RATIO) +{ +} + +// TorrentHandle + +#define SAFE_CALL(func, ...) \ + try { \ + m_nativeHandle.func(__VA_ARGS__); \ + } \ + catch (std::exception &exc) { \ + qDebug("torrent_handle::"#func"() throws exception: %s", exc.what()); \ + } + +#define SAFE_CALL_BOOL(func, ...) \ + try { \ + m_nativeHandle.func(__VA_ARGS__); \ + return true; \ + } \ + catch (std::exception &exc) { \ + qDebug("torrent_handle::"#func"() throws exception: %s", exc.what()); \ + return false; \ + } + +#define SAFE_RETURN(type, func, val) \ + type result = val; \ + try { \ + result = m_nativeHandle.func(); \ + } \ + catch (std::exception &exc) { \ + qDebug("torrent_handle::"#func"() throws exception: %s", exc.what()); \ + } \ + return result; + +#define SAFE_GET(var, func, ...) \ + try { \ + var = m_nativeHandle.func(__VA_ARGS__); \ + } \ + catch (std::exception &exc) { \ + qDebug("torrent_handle::"#func"() throws exception: %s", exc.what()); \ + } + +const qreal TorrentHandle::USE_GLOBAL_RATIO = -2.; +const qreal TorrentHandle::NO_RATIO_LIMIT = -1.; + +const qreal TorrentHandle::MAX_RATIO = 9999.; + +TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle &nativeHandle, + const AddTorrentData &data) + : QObject(session) + , m_session(session) + , m_nativeHandle(nativeHandle) + , m_state(TorrentState::Unknown) + , m_name(data.name) + , m_addedTime(data.resumed ? data.addedTime : QDateTime::currentDateTime()) + , m_savePath(fsutils::toNativePath(data.savePath)) + , m_label(data.label) + , m_hasSeedStatus(data.resumed ? data.hasSeedStatus : false) + , m_ratioLimit(data.resumed ? data.ratioLimit : (data.ignoreShareRatio ? NO_RATIO_LIMIT : USE_GLOBAL_RATIO)) + , m_tempPathDisabled(data.disableTempPath) + , m_hasMissingFiles(false) + , m_useDefaultSavePath(false) + , m_pauseAfterRecheck(false) + , m_needSaveResumeData(false) +{ + initialize(); + setSequentialDownload(data.sequential); + +#ifndef DISABLE_COUNTRIES_RESOLUTION + resolveCountries(m_session->isResolveCountriesEnabled()); +#endif + + if (!data.resumed) { + if (hasMetadata()) { + setFirstLastPiecePriority(data.sequential); + appendExtensionsToIncompleteFiles(); + } + } +} + +TorrentHandle::~TorrentHandle() {} + +bool TorrentHandle::isValid() const +{ + return m_nativeHandle.is_valid(); +} + +InfoHash TorrentHandle::hash() const +{ + return m_hash; +} + +QString TorrentHandle::name() const +{ + QString name = m_name; + if (name.isEmpty()) { +#if LIBTORRENT_VERSION_NUM < 10000 + name = m_nativeName; +#else + name = String::fromStdString(m_nativeStatus.name); +#endif + } + + if (name.isEmpty()) + name = m_hash; + + return name; +} + +QDateTime TorrentHandle::creationDate() const +{ + return m_torrentInfo.creationDate(); +} + +QString TorrentHandle::creator() const +{ + return m_torrentInfo.creator(); +} + +QString TorrentHandle::comment() const +{ + return m_torrentInfo.comment(); +} + +bool TorrentHandle::isPrivate() const +{ + return m_torrentInfo.isPrivate(); +} + +qlonglong TorrentHandle::totalSize() const +{ + return m_torrentInfo.totalSize(); +} + +// get the size of the torrent without the filtered files +qlonglong TorrentHandle::wantedSize() const +{ + return m_nativeStatus.total_wanted; +} + +qlonglong TorrentHandle::completedSize() const +{ + return m_nativeStatus.total_wanted_done; +} + +qlonglong TorrentHandle::incompletedSize() const +{ + return (m_nativeStatus.total_wanted - m_nativeStatus.total_wanted_done); +} + +qlonglong TorrentHandle::pieceLength() const +{ + return m_torrentInfo.pieceLength(); +} + +qlonglong TorrentHandle::wastedSize() const +{ + return (m_nativeStatus.total_failed_bytes + m_nativeStatus.total_redundant_bytes); +} + +QString TorrentHandle::currentTracker() const +{ + return String::fromStdString(m_nativeStatus.current_tracker); +} + +QString TorrentHandle::savePath() const +{ + return fsutils::fromNativePath(m_savePath); +} + +QString TorrentHandle::rootPath() const +{ + if (filesCount() > 1) { + QString first_filepath = filePath(0); + const int slashIndex = first_filepath.indexOf("/"); + if (slashIndex >= 0) + return QDir(actualSavePath()).absoluteFilePath(first_filepath.left(slashIndex)); + } + + return actualSavePath(); +} + +QString TorrentHandle::nativeActualSavePath() const +{ +#if LIBTORRENT_VERSION_NUM < 10000 + return m_nativeSavePath; +#else + return String::fromStdString(m_nativeStatus.save_path); +#endif +} + +QString TorrentHandle::actualSavePath() const +{ + return fsutils::fromNativePath(nativeActualSavePath()); +} + +QList TorrentHandle::trackers() const +{ + QList entries; + std::vector announces; + SAFE_GET(announces, trackers); + + foreach (const libt::announce_entry &tracker, announces) + entries << tracker; + + return entries; +} + +QHash TorrentHandle::trackerInfos() const +{ + return m_trackerInfos; +} + +void TorrentHandle::addTrackers(const QList &trackers) +{ + QList addedTrackers; + foreach (const TrackerEntry &tracker, trackers) { + if (addTracker(tracker)) + addedTrackers << tracker; + } + + if (!addedTrackers.isEmpty()) + m_session->handleTorrentTrackersAdded(this, addedTrackers); +} + +void TorrentHandle::replaceTrackers(QList trackers) +{ + QList existingTrackers = this->trackers(); + QList addedTrackers; + + std::vector announces; + foreach (const TrackerEntry &tracker, trackers) { + announces.push_back(tracker.nativeEntry()); + if (!existingTrackers.contains(tracker)) + addedTrackers << tracker; + else + existingTrackers.removeOne(tracker); + } + + try { + m_nativeHandle.replace_trackers(announces); + if (addedTrackers.isEmpty() && existingTrackers.isEmpty()) { + m_session->handleTorrentTrackersChanged(this); + } + else { + if (!existingTrackers.isEmpty()) + m_session->handleTorrentTrackersRemoved(this, existingTrackers); + if (!addedTrackers.isEmpty()) + m_session->handleTorrentTrackersAdded(this, addedTrackers); + } + + } + catch (std::exception &exc) { + qDebug("torrent_handle::replace_trackers() throws exception: %s", exc.what()); + } +} + +bool TorrentHandle::addTracker(const TrackerEntry &tracker) +{ + if (trackers().contains(tracker)) + return false; + + SAFE_CALL_BOOL(add_tracker, tracker.nativeEntry()); +} + +QList TorrentHandle::urlSeeds() const +{ + QList urlSeeds; + std::set seeds; + SAFE_GET(seeds, url_seeds); + + foreach (const std::string &urlSeed, seeds) + urlSeeds.append(QUrl(urlSeed.c_str())); + + return urlSeeds; +} + +void TorrentHandle::addUrlSeeds(const QList &urlSeeds) +{ + QList addedUrlSeeds; + foreach (const QUrl &urlSeed, urlSeeds) { + if (addUrlSeed(urlSeed)) + addedUrlSeeds << urlSeed; + } + + if (!addedUrlSeeds.isEmpty()) + m_session->handleTorrentUrlSeedsAdded(this, addedUrlSeeds); +} + +void TorrentHandle::removeUrlSeeds(const QList &urlSeeds) +{ + QList removedUrlSeeds; + foreach (const QUrl &urlSeed, urlSeeds) { + if (removeUrlSeed(urlSeed)) + removedUrlSeeds << urlSeed; + } + + if (!removedUrlSeeds.isEmpty()) + m_session->handleTorrentUrlSeedsRemoved(this, removedUrlSeeds); +} + +bool TorrentHandle::addUrlSeed(const QUrl &urlSeed) +{ + QList seeds = urlSeeds(); + if (seeds.contains(urlSeed)) return false; + + SAFE_CALL_BOOL(add_url_seed, String::toStdString(urlSeed.toString())); +} + +bool TorrentHandle::removeUrlSeed(const QUrl &urlSeed) +{ + QList seeds = urlSeeds(); + if (!seeds.contains(urlSeed)) return false; + + SAFE_CALL_BOOL(remove_url_seed, String::toStdString(urlSeed.toString())); +} + +bool TorrentHandle::connectPeer(const PeerAddress &peerAddress) +{ + libt::error_code ec; + libt::address addr = libt::address::from_string(String::toStdString(peerAddress.ip.toString()), ec); + if (ec) return false; + + libt::asio::ip::tcp::endpoint ep(addr, peerAddress.port); + SAFE_CALL_BOOL(connect_peer, ep); +} + +bool TorrentHandle::needSaveResumeData() const +{ + if (m_needSaveResumeData) return true; + + SAFE_RETURN(bool, need_save_resume_data, false); +} + +void TorrentHandle::saveResumeData() +{ + SAFE_CALL(save_resume_data); + m_needSaveResumeData = false; +} + +QString TorrentHandle::savePathParsed() const +{ + QString p; + if (hasMetadata() && (filesCount() == 1)) + p = firstFileSavePath(); + else + p = savePath(); + + return fsutils::toNativePath(p); +} + +int TorrentHandle::filesCount() const +{ + return m_torrentInfo.filesCount(); +} + +int TorrentHandle::piecesCount() const +{ + return m_torrentInfo.piecesCount(); +} + +qreal TorrentHandle::progress() const +{ + if (!m_nativeStatus.total_wanted) + return 0.; + + if (m_nativeStatus.total_wanted_done == m_nativeStatus.total_wanted) + return 1.; + + float progress = (float) m_nativeStatus.total_wanted_done / (float) m_nativeStatus.total_wanted; + Q_ASSERT((progress >= 0.f) && (progress <= 1.f)); + return progress; +} + +QString TorrentHandle::label() const +{ + return m_label; +} + +QDateTime TorrentHandle::addedTime() const +{ + return m_addedTime; +} + +qreal TorrentHandle::ratioLimit() const +{ + return m_ratioLimit; +} + + +QString TorrentHandle::firstFileSavePath() const +{ + Q_ASSERT(hasMetadata()); + + QString fSavePath = savePath(); + if (!fSavePath.endsWith("/")) + fSavePath += "/"; + fSavePath += filePath(0); + // Remove .!qB extension + if (fSavePath.endsWith(".!qB", Qt::CaseInsensitive)) + fSavePath.chop(4); + + return fSavePath; +} + +QString TorrentHandle::filePath(int index) const +{ + return m_torrentInfo.filePath(index); +} + +QString TorrentHandle::fileName(int index) const +{ + if (!hasMetadata()) return QString(); + return fsutils::fileName(filePath(index)); +} + +qlonglong TorrentHandle::fileSize(int index) const +{ + return m_torrentInfo.fileSize(index); +} + +// Return a list of absolute paths corresponding +// to all files in a torrent +QStringList TorrentHandle::absoluteFilePaths() const +{ + if (!hasMetadata()) return QStringList(); + + QDir saveDir(actualSavePath()); + QStringList res; + for (int i = 0; i < filesCount(); ++i) + res << fsutils::expandPathAbs(saveDir.absoluteFilePath(filePath(i))); + return res; +} + +QStringList TorrentHandle::absoluteFilePathsUnwanted() const +{ + if (!hasMetadata()) return QStringList(); + + QDir saveDir(actualSavePath()); + QStringList res; + std::vector fp; + SAFE_GET(fp, file_priorities); + + int count = static_cast(fp.size()); + for (int i = 0; i < count; ++i) { + if (fp[i] == 0) { + const QString path = fsutils::expandPathAbs(saveDir.absoluteFilePath(filePath(i))); + if (path.contains(".unwanted")) + res << path; + } + } + + return res; +} + +QPair TorrentHandle::fileExtremityPieces(int index) const +{ + if (!hasMetadata()) return qMakePair(-1, -1); + + const int numPieces = piecesCount(); + const qlonglong pieceSize = pieceLength(); + + // Determine the first and last piece of the file + int firstPiece = floor((m_torrentInfo.fileOffset(index) + 1) / (float) pieceSize); + Q_ASSERT((firstPiece >= 0) && (firstPiece < numPieces)); + + int numPiecesInFile = ceil(fileSize(index) / (float) pieceSize); + int lastPiece = firstPiece + numPiecesInFile - 1; + Q_ASSERT((lastPiece >= 0) && (lastPiece < numPieces)); + + Q_UNUSED(numPieces) + return qMakePair(firstPiece, lastPiece); +} + +QVector TorrentHandle::filePriorities() const +{ + std::vector fp; + SAFE_GET(fp, file_priorities); + + return QVector::fromStdVector(fp); +} + +TorrentInfo TorrentHandle::info() const +{ + return m_torrentInfo; +} + +bool TorrentHandle::isPaused() const +{ + return (m_nativeStatus.paused && !m_nativeStatus.auto_managed); +} + +bool TorrentHandle::isResumed() const +{ + return !isPaused(); +} + +bool TorrentHandle::isQueued() const +{ + return (m_nativeStatus.paused && m_nativeStatus.auto_managed); +} + +bool TorrentHandle::isChecking() const +{ + return ((m_nativeStatus.state == libt::torrent_status::queued_for_checking) + || (m_nativeStatus.state == libt::torrent_status::checking_files) + || (m_nativeStatus.state == libt::torrent_status::checking_resume_data)); +} + +bool TorrentHandle::isDownloading() const +{ + return m_state == TorrentState::Downloading + || m_state == TorrentState::DownloadingMetadata + || m_state == TorrentState::StalledDownloading + || m_state == TorrentState::CheckingDownloading + || m_state == TorrentState::PausedDownloading + || m_state == TorrentState::QueuedDownloading + || m_state == TorrentState::ForcedDownloading + || m_state == TorrentState::Error; +} + +bool TorrentHandle::isUploading() const +{ + return m_state == TorrentState::Uploading + || m_state == TorrentState::StalledUploading + || m_state == TorrentState::CheckingUploading + || m_state == TorrentState::QueuedUploading + || m_state == TorrentState::ForcedUploading; +} + +bool TorrentHandle::isCompleted() const +{ + return m_state == TorrentState::Uploading + || m_state == TorrentState::StalledUploading + || m_state == TorrentState::CheckingUploading + || m_state == TorrentState::PausedUploading + || m_state == TorrentState::QueuedUploading; +} + +bool TorrentHandle::isActive() const +{ + if (m_state == TorrentState::StalledDownloading) + return (uploadPayloadRate() > 0); + + return m_state == TorrentState::DownloadingMetadata + || m_state == TorrentState::Downloading + || m_state == TorrentState::ForcedDownloading + || m_state == TorrentState::Uploading + || m_state == TorrentState::ForcedUploading; +} + +bool TorrentHandle::isInactive() const +{ + return !isActive(); +} + +bool TorrentHandle::isSeed() const +{ + // Affected by bug http://code.rasterbar.com/libtorrent/ticket/402 + //SAFE_RETURN(bool, is_seed, false); + // May suffer from approximation problems + //return (progress() == 1.); + // This looks safe + return ((m_nativeStatus.state == libt::torrent_status::finished) + || (m_nativeStatus.state == libt::torrent_status::seeding)); +} + +bool TorrentHandle::isForced() const +{ + return (!m_nativeStatus.paused && !m_nativeStatus.auto_managed); +} + +bool TorrentHandle::isSequentialDownload() const +{ + return m_nativeStatus.sequential_download; +} + +bool TorrentHandle::hasFirstLastPiecePriority() const +{ + if (!hasMetadata()) return false; + + // Get int first media file + std::vector fp; + SAFE_GET(fp, file_priorities); + + QPair extremities; + bool found = false; + int count = static_cast(fp.size()); + for (int i = 0; i < count; ++i) { + const QString ext = fsutils::fileExtension(filePath(i)); + if (misc::isPreviewable(ext) && (fp[i] > 0)) { + extremities = fileExtremityPieces(i); + found = true; + break; + } + } + + if (!found) return false; // No media file + + int first = 0; + int last = 0; + SAFE_GET(first, piece_priority, extremities.first); + SAFE_GET(last, piece_priority, extremities.second); + + return ((first == 7) && (last == 7)); +} + +TorrentState TorrentHandle::state() const +{ + return m_state; +} + +void TorrentHandle::updateState() +{ + if (isPaused()) { + if (hasError() || hasMissingFiles()) + m_state = TorrentState::Error; + else + m_state = isSeed() ? TorrentState::PausedUploading : TorrentState::PausedDownloading; + } + else { + if (m_session->isQueueingEnabled() && isQueued() && !isChecking()) { + m_state = isSeed() ? TorrentState::QueuedUploading : TorrentState::QueuedDownloading; + } + else { + switch (m_nativeStatus.state) { + case libt::torrent_status::finished: + case libt::torrent_status::seeding: + if (isForced()) + m_state = TorrentState::ForcedUploading; + else + m_state = m_nativeStatus.upload_payload_rate > 0 ? TorrentState::Uploading : TorrentState::StalledUploading; + break; + case libt::torrent_status::allocating: + m_state = TorrentState::Allocating; + break; + case libt::torrent_status::checking_files: + case libt::torrent_status::queued_for_checking: + case libt::torrent_status::checking_resume_data: + m_state = m_hasSeedStatus ? TorrentState::CheckingUploading : TorrentState::CheckingDownloading; + break; + case libt::torrent_status::downloading_metadata: + m_state = TorrentState::DownloadingMetadata; + break; + case libt::torrent_status::downloading: + if (isForced()) + m_state = TorrentState::ForcedDownloading; + else + m_state = m_nativeStatus.download_payload_rate > 0 ? TorrentState::Downloading : TorrentState::StalledDownloading; + break; + default: + qWarning("Unrecognized torrent status, should not happen!!! status was %d", m_nativeStatus.state); + m_state = TorrentState::Unknown; + } + } + } +} + +bool TorrentHandle::hasMetadata() const +{ + return m_nativeStatus.has_metadata; +} + +bool TorrentHandle::hasMissingFiles() const +{ + return m_hasMissingFiles; +} + +bool TorrentHandle::hasError() const +{ + return (m_nativeStatus.paused && !m_nativeStatus.error.empty()); +} + +bool TorrentHandle::hasFilteredPieces() const +{ + std::vector pp; + SAFE_GET(pp, piece_priorities); + + foreach (const int priority, pp) + if (priority == 0) return true; + + return false; +} + +int TorrentHandle::queuePosition() const +{ + if (m_nativeStatus.queue_position < 0) return 0; + + return m_nativeStatus.queue_position + 1; +} + +QString TorrentHandle::error() const +{ + return String::fromStdString(m_nativeStatus.error); +} + +qlonglong TorrentHandle::totalDownload() const +{ + return m_nativeStatus.all_time_download; +} + +qlonglong TorrentHandle::totalUpload() const +{ + return m_nativeStatus.all_time_upload; +} + +int TorrentHandle::activeTime() const +{ + return m_nativeStatus.active_time; +} + +int TorrentHandle::seedingTime() const +{ + return m_nativeStatus.seeding_time; +} + +qulonglong TorrentHandle::eta() const +{ + if (isPaused()) return MAX_ETA; + + const SpeedSampleAvg speed_average = m_speedMonitor.average(); + + if (isSeed()) { + if (speed_average.upload == 0) return MAX_ETA; + + qreal max_ratio = maxRatio(); + if (max_ratio < 0) return MAX_ETA; + + qlonglong realDL = totalDownload(); + if (realDL <= 0) + realDL = wantedSize(); + + return ((realDL * max_ratio) - totalUpload()) / speed_average.upload; + } + + if (!speed_average.download) return MAX_ETA; + + return (wantedSize() - completedSize()) / speed_average.download; +} + +QVector TorrentHandle::filesProgress() const +{ + std::vector fp; + QVector result; + SAFE_CALL(file_progress, fp, libt::torrent_handle::piece_granularity); + + int count = static_cast(fp.size()); + for (int i = 0; i < count; ++i) { + qlonglong size = fileSize(i); + if ((size <= 0) || (fp[i] == size)) + result << 1; + else + result << (fp[i] / static_cast(size)); + } + + return result; +} + +int TorrentHandle::seedsCount() const +{ + return m_nativeStatus.num_seeds; +} + +int TorrentHandle::peersCount() const +{ + return m_nativeStatus.num_peers; +} + +int TorrentHandle::leechsCount() const +{ + return (m_nativeStatus.num_peers - m_nativeStatus.num_seeds); +} + +int TorrentHandle::completeCount() const +{ + return m_nativeStatus.num_complete; +} + +int TorrentHandle::incompleteCount() const +{ + return m_nativeStatus.num_incomplete; +} + +QDateTime TorrentHandle::lastSeenComplete() const +{ + return QDateTime::fromTime_t(m_nativeStatus.last_seen_complete); +} + +QDateTime TorrentHandle::completedTime() const +{ + return QDateTime::fromTime_t(m_nativeStatus.completed_time); +} + +int TorrentHandle::timeSinceUpload() const +{ + return m_nativeStatus.time_since_upload; +} + +int TorrentHandle::timeSinceDownload() const +{ + return m_nativeStatus.time_since_download; +} + +int TorrentHandle::downloadLimit() const +{ + SAFE_RETURN(int, download_limit, -1) +} + +int TorrentHandle::uploadLimit() const +{ + SAFE_RETURN(int, upload_limit, -1) +} + +bool TorrentHandle::superSeeding() const +{ + return m_nativeStatus.super_seeding; +} + +QList TorrentHandle::peers() const +{ + QList peers; + std::vector nativePeers; + + SAFE_CALL(get_peer_info, nativePeers); + + foreach (const libt::peer_info &peer, nativePeers) + peers << peer; + + return peers; +} + +QBitArray TorrentHandle::pieces() const +{ + QBitArray result(m_nativeStatus.pieces.size()); + +#if LIBTORRENT_VERSION_NUM < 10000 + typedef size_t pieces_size_t; +#else + typedef int pieces_size_t; +#endif + for (pieces_size_t i = 0; i < m_nativeStatus.pieces.size(); ++i) + result.setBit(i, m_nativeStatus.pieces.get_bit(i)); + + return result; +} + +QBitArray TorrentHandle::downloadingPieces() const +{ + QBitArray result(piecesCount()); + + std::vector queue; + SAFE_CALL(get_download_queue, queue); + + std::vector::const_iterator it = queue.begin(); + std::vector::const_iterator itend = queue.end(); + for (; it != itend; ++it) + result.setBit(it->piece_index); + + return result; +} + +QVector TorrentHandle::pieceAvailability() const +{ + std::vector avail; + SAFE_CALL(piece_availability, avail); + + return QVector::fromStdVector(avail); +} + +qreal TorrentHandle::distributedCopies() const +{ + return m_nativeStatus.distributed_copies; +} + +qreal TorrentHandle::maxRatio(bool *usesGlobalRatio) const +{ + qreal ratioLimit = m_ratioLimit; + + if (ratioLimit == USE_GLOBAL_RATIO) { + ratioLimit = m_session->globalMaxRatio(); + if (usesGlobalRatio) + *usesGlobalRatio = true; + } + else { + if (usesGlobalRatio) + *usesGlobalRatio = false; + } + + return ratioLimit; +} + +qreal TorrentHandle::realRatio() const +{ + libt::size_type all_time_upload = m_nativeStatus.all_time_upload; + libt::size_type all_time_download = m_nativeStatus.all_time_download; + libt::size_type total_done = m_nativeStatus.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; + else return MAX_RATIO + 1; + } + + qreal ratio = all_time_upload / static_cast(all_time_download); + Q_ASSERT(ratio >= 0.); + if (ratio > MAX_RATIO) + ratio = MAX_RATIO; + + return ratio; +} + +int TorrentHandle::uploadPayloadRate() const +{ + return m_nativeStatus.upload_payload_rate; +} + +int TorrentHandle::downloadPayloadRate() const +{ + return m_nativeStatus.download_payload_rate; +} + +int TorrentHandle::totalPayloadUpload() const +{ + return m_nativeStatus.total_payload_upload; +} + +int TorrentHandle::totalPayloadDownload() const +{ + return m_nativeStatus.total_payload_download; +} + +int TorrentHandle::connectionsCount() const +{ + return m_nativeStatus.num_connections; +} + +int TorrentHandle::connectionsLimit() const +{ + return m_nativeStatus.connections_limit; +} + +qlonglong TorrentHandle::nextAnnounce() const +{ + return m_nativeStatus.next_announce.total_seconds(); +} + +void TorrentHandle::setName(const QString &name) +{ + if (m_name != name) { + m_name = name; + m_needSaveResumeData = true; + } +} + +void TorrentHandle::setLabel(const QString &label) +{ + if (m_label != label) { + m_label = label; + m_needSaveResumeData = true; + adjustSavePath(); + } +} + +void TorrentHandle::setSequentialDownload(bool b) +{ + SAFE_CALL(set_sequential_download, b); +} + +void TorrentHandle::move(QString path) +{ + // now we use non-default save path + // even if new path same as default. + m_useDefaultSavePath = false; + + path = fsutils::toNativePath(path); + if (path == savePath()) return; + + if (!useTempPath()) { + moveStorage(path); + } + else { + m_savePath = path; + m_needSaveResumeData = true; + m_session->handleTorrentSavePathChanged(this); + } +} + +#if LIBTORRENT_VERSION_NUM < 10000 +void TorrentHandle::forceReannounce() +{ + SAFE_CALL(force_reannounce); +} + +#else + +void TorrentHandle::forceReannounce(int index) +{ + SAFE_CALL(force_reannounce, 0, index); +} +#endif + +void TorrentHandle::forceDHTAnnounce() +{ + SAFE_CALL(force_dht_announce); +} + +void TorrentHandle::forceRecheck() +{ + if (!hasMetadata()) return; + + if (isPaused()) { + m_pauseAfterRecheck = true; + resume(); + } + + SAFE_CALL(force_recheck); +} + +void TorrentHandle::toggleSequentialDownload() +{ + if (hasMetadata()) { + bool was_sequential = isSequentialDownload(); + SAFE_CALL(set_sequential_download, !was_sequential); + if (!was_sequential) + setFirstLastPiecePriority(true); + } +} + +void TorrentHandle::toggleFirstLastPiecePriority() +{ + if (hasMetadata()) + setFirstLastPiecePriority(!hasFirstLastPiecePriority()); +} + +void TorrentHandle::setFirstLastPiecePriority(bool b) +{ + std::vector fp; + SAFE_GET(fp, file_priorities); + + // Download first and last pieces first for all media files in the torrent + int nbfiles = static_cast(fp.size()); + for (int index = 0; index < nbfiles; ++index) { + const QString path = filePath(index); + const QString ext = fsutils::fileExtension(path); + if (misc::isPreviewable(ext) && (fp[index] > 0)) { + qDebug() << "File" << path << "is previewable, toggle downloading of first/last pieces first"; + // Determine the priority to set + int prio = b ? 7 : fp[index]; + + QPair extremities = fileExtremityPieces(index); + SAFE_CALL(piece_priority, extremities.first, prio); + SAFE_CALL(piece_priority, extremities.second, prio); + } + } +} + +void TorrentHandle::pause() +{ + if (isPaused()) return; + + try { + m_nativeHandle.auto_managed(false); + m_nativeHandle.pause(); + } + catch (std::exception &exc) { + qDebug("torrent_handle method inside TorrentHandleImpl::pause() throws exception: %s", exc.what()); + } +} + +void TorrentHandle::resume(bool forced) +{ + try { + if (hasError()) + m_nativeHandle.clear_error(); + m_nativeHandle.set_upload_mode(false); + m_nativeHandle.auto_managed(!forced); + m_nativeHandle.resume(); + } + catch (std::exception &exc) { + qDebug("torrent_handle method inside TorrentHandleImpl::resume() throws exception: %s", exc.what()); + } +} + +void TorrentHandle::moveStorage(const QString &newPath) +{ + if (isMoveInProgress()) { + qDebug("enqueue move storage to %s", qPrintable(newPath)); + m_queuedPath = newPath; + } + else { + QString oldPath = nativeActualSavePath(); + if (QDir(oldPath) == QDir(newPath)) return; + + qDebug("move storage: %s to %s", qPrintable(oldPath), qPrintable(newPath)); + // Create destination directory if necessary + // or move_storage() will fail... + QDir().mkpath(newPath); + + try { + // Actually move the storage + m_nativeHandle.move_storage(newPath.toUtf8().constData()); + m_oldPath = oldPath; + m_newPath = newPath; + } + catch (std::exception &exc) { + qDebug("torrent_handle::move_storage() throws exception: %s", exc.what()); + } + } +} + +void TorrentHandle::setTrackerLogin(const QString &username, const QString &password) +{ + SAFE_CALL(set_tracker_login, std::string(username.toLocal8Bit().constData()), std::string(password.toLocal8Bit().constData())); +} + +void TorrentHandle::renameFile(int index, const QString &name) +{ + qDebug() << Q_FUNC_INFO << index << name; + SAFE_CALL(rename_file, index, String::toStdString(fsutils::toNativePath(name))); +} + +bool TorrentHandle::saveTorrentFile(const QString &path) +{ + if (!m_torrentInfo.isValid()) return false; + + // TODO: Use libtorrent::create_torrent() here! + + libt::entry meta = libt::bdecode(m_torrentInfo.metadata().data(), + m_torrentInfo.metadata().data() + m_torrentInfo.metadata().size()); + libt::entry torrentEntry(libt::entry::dictionary_t); + torrentEntry["info"] = meta; + QList trackers = m_torrentInfo.trackers(); + if (!trackers.isEmpty()) + torrentEntry["announce"] = trackers.first().nativeEntry().url; + + QVector out; + libt::bencode(std::back_inserter(out), torrentEntry); + QFile torrentFile(path); + if (!out.empty() && torrentFile.open(QIODevice::WriteOnly)) + return (torrentFile.write(&out[0], out.size()) == out.size()); + + return false; +} + +void TorrentHandle::setFilePriority(int index, int priority) +{ + std::vector priorities; + SAFE_GET(priorities, file_priorities); + + if ((priorities.size() > static_cast(index)) && (priorities[index] != priority)) { + priorities[index] = priority; + prioritizeFiles(QVector::fromStdVector(priorities)); + } +} + +void TorrentHandle::handleStateUpdate(const libt::torrent_status &nativeStatus) +{ + updateStatus(nativeStatus); +} + +void TorrentHandle::handleStorageMovedAlert(libtorrent::storage_moved_alert *p) +{ + if (!isMoveInProgress()) { + qWarning("Unexpected TorrentHandleImpl::handleStorageMoved() call."); + return; + } + + QString newPath = String::fromStdString(p->path); + if (newPath != m_newPath) { + qWarning("TorrentHandleImpl::handleStorageMoved(): New path doesn't match a path in a queue."); + return; + } + + qDebug("Torrent is successfully moved from %s to %s", qPrintable(m_oldPath), qPrintable(m_newPath)); + updateStatus(); + + m_newPath.clear(); + if (!m_queuedPath.isEmpty()) { + moveStorage(m_queuedPath); + m_queuedPath.clear(); + } + + if (!useTempPath()) { + m_savePath = newPath; + m_session->handleTorrentSavePathChanged(this); + } + + // Attempt to remove old folder if empty + QDir oldSaveDir(fsutils::fromNativePath(m_oldPath)); + if ((oldSaveDir != QDir(m_session->defaultSavePath())) && (oldSaveDir != QDir(m_session->tempPath()))) { + qDebug("Attempting to remove %s", qPrintable(m_oldPath)); + QDir().rmpath(m_oldPath); + } + + if (!isMoveInProgress()) { + while (!m_moveStorageTriggers.isEmpty()) + m_moveStorageTriggers.takeFirst()(); + } +} + +void TorrentHandle::handleStorageMovedFailedAlert(libtorrent::storage_moved_failed_alert *p) +{ + if (!isMoveInProgress()) { + qWarning("Unexpected TorrentHandleImpl::handleStorageMovedFailed() call."); + return; + } + + Logger::instance()->addMessage(tr("Could not move torrent: '%1'. Reason: %2") + .arg(name()).arg(String::fromStdString(p->message())), Log::CRITICAL); + + m_newPath.clear(); + if (!m_queuedPath.isEmpty()) { + moveStorage(m_queuedPath); + m_queuedPath.clear(); + } + + if (!isMoveInProgress()) { + while (!m_moveStorageTriggers.isEmpty()) + m_moveStorageTriggers.takeFirst()(); + } +} + +void TorrentHandle::handleTrackerReplyAlert(libtorrent::tracker_reply_alert *p) +{ + QString trackerUrl = String::fromStdString(p->url); + qDebug("Received a tracker reply from %s (Num_peers = %d)", qPrintable(trackerUrl), p->num_peers); + // Connection was successful now. Remove possible old errors + m_trackerInfos[trackerUrl].lastMessage.clear(); // Reset error/warning message + m_trackerInfos[trackerUrl].numPeers = p->num_peers; + + m_session->handleTorrentTrackerReply(this, trackerUrl); +} + +void TorrentHandle::handleTrackerWarningAlert(libtorrent::tracker_warning_alert *p) +{ + QString trackerUrl = String::fromStdString(p->url); + QString message = String::fromStdString(p->msg); + qDebug("Received a tracker warning for %s: %s", qPrintable(trackerUrl), qPrintable(message)); + // Connection was successful now but there is a warning message + m_trackerInfos[trackerUrl].lastMessage = message; // Store warning message + + m_session->handleTorrentTrackerWarning(this, trackerUrl); +} + +void TorrentHandle::handleTrackerErrorAlert(libtorrent::tracker_error_alert *p) +{ + QString trackerUrl = String::fromStdString(p->url); + QString message = String::fromStdString(p->msg); + qDebug("Received a tracker error for %s: %s", qPrintable(trackerUrl), qPrintable(message)); + m_trackerInfos[trackerUrl].lastMessage = message; + + if (p->status_code == 401) + m_session->handleTorrentTrackerAuthenticationRequired(this, trackerUrl); + + m_session->handleTorrentTrackerError(this, trackerUrl); +} + +void TorrentHandle::handleTorrentCheckedAlert(libtorrent::torrent_checked_alert *p) +{ + Q_UNUSED(p); + qDebug("%s have just finished checking", qPrintable(hash())); + + updateStatus(); + adjustActualSavePath(); + + if (m_pauseAfterRecheck) { + m_pauseAfterRecheck = false; + pause(); + } + + m_session->handleTorrentChecked(this); +} + +void TorrentHandle::handleTorrentFinishedAlert(libtorrent::torrent_finished_alert *p) +{ + Q_UNUSED(p); + qDebug("Got a torrent finished alert for %s", qPrintable(name())); + qDebug("Torrent has seed status: %s", m_hasSeedStatus ? "yes" : "no"); + if (m_hasSeedStatus) return; + + updateStatus(); + m_hasMissingFiles = false; + m_hasSeedStatus = true; + + adjustActualSavePath(); + if (Preferences::instance()->recheckTorrentsOnCompletion()) + forceRecheck(); + + if (!isMoveInProgress()) + m_session->handleTorrentFinished(this); + else + m_moveStorageTriggers.append(boost::bind(&SessionPrivate::handleTorrentFinished, m_session, this)); +} + +void TorrentHandle::handleTorrentPausedAlert(libtorrent::torrent_paused_alert *p) +{ + Q_UNUSED(p); + updateStatus(); + m_speedMonitor.reset(); + m_session->handleTorrentPaused(this); +} + +void TorrentHandle::handleTorrentResumedAlert(libtorrent::torrent_resumed_alert *p) +{ + Q_UNUSED(p); + m_session->handleTorrentResumed(this); +} + +void TorrentHandle::handleSaveResumeDataAlert(libtorrent::save_resume_data_alert *p) +{ + if (p->resume_data) { + (*(p->resume_data))["qBt-addedTime"] = m_addedTime.toTime_t(); + (*(p->resume_data))["qBt-savePath"] = m_useDefaultSavePath ? "" : m_savePath.toUtf8().constData(); + (*(p->resume_data))["qBt-ratioLimit"] = QString::number(m_ratioLimit).toUtf8().constData(); + (*(p->resume_data))["qBt-label"] = m_label.toUtf8().constData(); + (*(p->resume_data))["qBt-name"] = m_name.toUtf8().constData(); + (*(p->resume_data))["qBt-seedStatus"] = m_hasSeedStatus; + (*(p->resume_data))["qBt-tempPathDisabled"] = m_tempPathDisabled; + + m_session->handleTorrentResumeDataReady(this, *(p->resume_data)); + } + else { + m_session->handleTorrentResumeDataFailed(this); + } +} + +void TorrentHandle::handleSaveResumeDataFailedAlert(libtorrent::save_resume_data_failed_alert *p) +{ + Q_UNUSED(p); + m_session->handleTorrentResumeDataFailed(this); +} + +void TorrentHandle::handleFastResumeRejectedAlert(libtorrent::fastresume_rejected_alert *p) +{ + qDebug("/!\\ Fast resume failed for %s, reason: %s", qPrintable(name()), p->message().c_str()); + Logger *const logger = Logger::instance(); + + updateStatus(); + if (p->error.value() == libt::errors::mismatching_file_size) { + // Mismatching file size (files were probably moved) + logger->addMessage(tr("File sizes mismatch for torrent %1, pausing it.").arg(name()), Log::CRITICAL); + m_hasMissingFiles = true; + if (!isPaused()) + pause(); + } + else { + logger->addMessage(tr("Fast resume data was rejected for torrent %1. Reason: %2. Checking again...") + .arg(name()).arg(String::fromStdString(p->message())), Log::CRITICAL); + } +} + +void TorrentHandle::handleFileRenamedAlert(libtorrent::file_renamed_alert *p) +{ + QString newName = fsutils::fromNativePath(String::fromStdString(p->name)); + + // TODO: Check this! + if (filesCount() > 1) { + // Check if folders were renamed + QStringList oldPathParts = m_torrentInfo.origFilePath(p->index).split("/"); + oldPathParts.removeLast(); + QString oldPath = oldPathParts.join("/"); + QStringList newPathParts = newName.split("/"); + newPathParts.removeLast(); + QString newPath = newPathParts.join("/"); + if (!newPathParts.isEmpty() && (oldPath != newPath)) { + qDebug("oldPath(%s) != newPath(%s)", qPrintable(oldPath), qPrintable(newPath)); + oldPath = QString("%1/%2").arg(actualSavePath()).arg(oldPath); + qDebug("Detected folder renaming, attempt to delete old folder: %s", qPrintable(oldPath)); + QDir().rmpath(oldPath); + } + } + + updateStatus(); + + if (filesCount() == 1) { + // Single-file torrent + // Renaming a file corresponds to changing the save path + m_session->handleTorrentSavePathChanged(this); + } +} + +void TorrentHandle::handleFileCompletedAlert(libtorrent::file_completed_alert *p) +{ + updateStatus(); + + qDebug("A file completed download in torrent \"%s\"", qPrintable(name())); + if (m_session->isAppendExtensionEnabled()) { + QString name = filePath(p->index); + if (name.endsWith(QB_EXT)) { + const QString oldName = name; + name.chop(QString(QB_EXT).size()); + qDebug("Renaming %s to %s", qPrintable(oldName), qPrintable(name)); + renameFile(p->index, name); + } + } +} + +void TorrentHandle::handleStatsAlert(libtorrent::stats_alert *p) +{ + Q_ASSERT(p->interval >= 1000); + SpeedSample transferred(p->transferred[libt::stats_alert::download_payload] * 1000LL / p->interval, + p->transferred[libt::stats_alert::upload_payload] * 1000LL / p->interval); + m_speedMonitor.addSample(transferred); +} + +void TorrentHandle::handleMetadataReceivedAlert(libt::metadata_received_alert *p) +{ + Q_UNUSED(p); + qDebug("Metadata received for torrent %s.", qPrintable(name())); + updateStatus(); + appendExtensionsToIncompleteFiles(); + m_session->handleTorrentMetadataReceived(this); + + if (isPaused()) { + // XXX: Unfortunately libtorrent-rasterbar does not send a torrent_paused_alert + // and the torrent can be paused when metadata is received + m_speedMonitor.reset(); + m_session->handleTorrentPaused(this); + } +} + +void TorrentHandle::handleDefaultSavePathChanged() +{ + adjustSavePath(); +} + +void TorrentHandle::adjustSavePath() +{ + // If we use default save path... + if (m_useDefaultSavePath) { + QString defaultSavePath = m_session->defaultSavePath(); + if (m_session->useAppendLabelToSavePath() && !m_label.isEmpty()) + defaultSavePath += QString("%1/").arg(m_label); + defaultSavePath = fsutils::toNativePath(defaultSavePath); + if (m_savePath != defaultSavePath) { + if (!useTempPath()) { + moveStorage(defaultSavePath); + } + else { + m_savePath = defaultSavePath; + m_needSaveResumeData = true; + m_session->handleTorrentSavePathChanged(this); + } + } + } +} + +void TorrentHandle::handleTempPathChanged() +{ + adjustActualSavePath(); +} + +void TorrentHandle::handleAppendExtensionToggled() +{ + if (!hasMetadata()) return; + + if (m_session->isAppendExtensionEnabled()) + appendExtensionsToIncompleteFiles(); + else + removeExtensionsFromIncompleteFiles(); +} + +#ifndef DISABLE_COUNTRIES_RESOLUTION +void TorrentHandle::handleResolveCountriesToggled() +{ + resolveCountries(m_session->isResolveCountriesEnabled()); +} +#endif + +void TorrentHandle::handleAlert(libtorrent::alert *a) +{ + switch (a->type()) { + case libt::stats_alert::alert_type: + handleStatsAlert(static_cast(a)); + break; + case libt::file_renamed_alert::alert_type: + handleFileRenamedAlert(static_cast(a)); + break; + case libt::file_completed_alert::alert_type: + handleFileCompletedAlert(static_cast(a)); + break; + case libt::torrent_finished_alert::alert_type: + handleTorrentFinishedAlert(static_cast(a)); + break; + case libt::save_resume_data_alert::alert_type: + handleSaveResumeDataAlert(static_cast(a)); + break; + case libt::save_resume_data_failed_alert::alert_type: + handleSaveResumeDataFailedAlert(static_cast(a)); + break; + case libt::storage_moved_alert::alert_type: + handleStorageMovedAlert(static_cast(a)); + break; + case libt::storage_moved_failed_alert::alert_type: + handleStorageMovedFailedAlert(static_cast(a)); + break; + case libt::torrent_paused_alert::alert_type: + handleTorrentPausedAlert(static_cast(a)); + break; + case libt::tracker_error_alert::alert_type: + handleTrackerErrorAlert(static_cast(a)); + break; + case libt::tracker_reply_alert::alert_type: + handleTrackerReplyAlert(static_cast(a)); + break; + case libt::tracker_warning_alert::alert_type: + handleTrackerWarningAlert(static_cast(a)); + break; + case libt::metadata_received_alert::alert_type: + handleMetadataReceivedAlert(static_cast(a)); + break; + case libt::fastresume_rejected_alert::alert_type: + handleFastResumeRejectedAlert(static_cast(a)); + break; + case libt::torrent_checked_alert::alert_type: + handleTorrentCheckedAlert(static_cast(a)); + break; + } +} + +void TorrentHandle::appendExtensionsToIncompleteFiles() +{ + QVector fp = filesProgress(); + for (int i = 0; i < filesCount(); ++i) { + if ((fileSize(i) > 0) && (fp[i] < 1)) { + const QString name = filePath(i); + if (!name.endsWith(QB_EXT)) { + const QString newName = name + QB_EXT; + qDebug("Renaming %s to %s", qPrintable(name), qPrintable(newName)); + renameFile(i, newName); + } + } + } +} + +void TorrentHandle::removeExtensionsFromIncompleteFiles() +{ + for (int i = 0; i < filesCount(); ++i) { + QString name = filePath(i); + if (name.endsWith(QB_EXT)) { + const QString oldName = name; + name.chop(QString(QB_EXT).size()); + qDebug("Renaming %s to %s", qPrintable(oldName), qPrintable(name)); + renameFile(i, name); + } + } +} + +void TorrentHandle::adjustActualSavePath() +{ + QString path; + if (!useTempPath()) { + // Disabling temp dir + // Moving all torrents to their destination folder + path = savePath(); + } + else { + // Moving all downloading torrents to temporary save path + path = m_session->tempPath(); + qDebug("Moving torrent to its temp save path: %s", qPrintable(path)); + } + + moveStorage(fsutils::toNativePath(path)); +} + +libtorrent::torrent_handle TorrentHandle::nativeHandle() const +{ + return m_nativeHandle; +} + +void TorrentHandle::updateTorrentInfo() +{ + if (!hasMetadata()) return; + +#if LIBTORRENT_VERSION_NUM < 10000 + try { + m_torrentInfo = TorrentInfo(&m_nativeHandle.get_torrent_info()); + } + catch (std::exception &exc) { + qDebug("torrent_handle::get_torrent_info() throws exception: %s", exc.what()); \ + } +#else + m_torrentInfo = TorrentInfo(m_nativeStatus.torrent_file); +#endif +} + +void TorrentHandle::initialize() +{ + updateStatus(); + + m_hash = InfoHash(m_nativeStatus.info_hash); + if (m_savePath.isEmpty()) { + // we use default save path + m_savePath = nativeActualSavePath(); + m_useDefaultSavePath = true; + } + + adjustSavePath(); + adjustActualSavePath(); +} + +bool TorrentHandle::isMoveInProgress() const +{ + return !m_newPath.isEmpty(); +} + +bool TorrentHandle::useTempPath() const +{ + return !m_tempPathDisabled && m_session->isTempPathEnabled() && !isSeed(); +} + +void TorrentHandle::updateStatus() +{ + libt::torrent_status status; + SAFE_GET(status, status); + + updateStatus(status); +} + +void TorrentHandle::updateStatus(const libtorrent::torrent_status &nativeStatus) +{ + m_nativeStatus = nativeStatus; +#if LIBTORRENT_VERSION_NUM < 10000 + try { + m_nativeName = String::fromStdString(m_nativeHandle.name()); + m_nativeSavePath = fsutils::fromNativePath(String::fromStdString(m_nativeHandle.save_path())); + } + catch (std::exception &exc) { + qDebug("torrent_handle method inside TorrentHandleImpl::updateStatus() throws exception: %s", exc.what()); + } +#endif + + updateState(); + updateTorrentInfo(); +} + +void TorrentHandle::setRatioLimit(qreal limit) +{ + if (limit < USE_GLOBAL_RATIO) + limit = NO_RATIO_LIMIT; + else if (limit > MAX_RATIO) + limit = MAX_RATIO; + + if (m_ratioLimit != limit) { + m_ratioLimit = limit; + m_needSaveResumeData = true; + m_session->handleTorrentRatioLimitChanged(this); + } +} + +void TorrentHandle::setUploadLimit(int limit) +{ + SAFE_CALL(set_upload_limit, limit) +} + +void TorrentHandle::setDownloadLimit(int limit) +{ + SAFE_CALL(set_download_limit, limit) +} + +void TorrentHandle::setSuperSeeding(bool enable) +{ + SAFE_CALL(super_seeding, enable) +} + +void TorrentHandle::flushCache() +{ + SAFE_CALL(flush_cache) +} + +QString TorrentHandle::toMagnetUri() const +{ + return m_torrentInfo.toMagnetUri(); +} + +void TorrentHandle::resolveCountries(bool b) +{ + SAFE_CALL(resolve_countries, b); +} + +void TorrentHandle::prioritizeFiles(const QVector &priorities) +{ + qDebug() << Q_FUNC_INFO; + if (priorities.size() != filesCount()) return; + + qDebug() << Q_FUNC_INFO << "Changing files priorities..."; + SAFE_CALL(prioritize_files, priorities.toStdVector()); + + qDebug() << Q_FUNC_INFO << "Moving unwanted files to .unwanted folder and conversely..."; + QString spath = actualSavePath(); + for (int i = 0; i < priorities.size(); ++i) { + QString filepath = filePath(i); + // Move unwanted files to a .unwanted subfolder + if (priorities[i] == 0) { + QString oldAbsPath = QDir(spath).absoluteFilePath(filepath); + QString parentAbsPath = fsutils::branchPath(oldAbsPath); + // Make sure the file does not already exists + if (QDir(parentAbsPath).dirName() != ".unwanted") { + QString unwantedAbsPath = parentAbsPath + "/.unwanted"; + QString newAbsPath = unwantedAbsPath + "/" + fsutils::fileName(filepath); + qDebug() << "Unwanted path is" << unwantedAbsPath; + if (QFile::exists(newAbsPath)) { + qWarning() << "File" << newAbsPath << "already exists at destination."; + continue; + } + + bool created = QDir().mkpath(unwantedAbsPath); + qDebug() << "unwanted folder was created:" << created; +#ifdef Q_OS_WIN + if (created) { + // Hide the folder on Windows + qDebug() << "Hiding folder (Windows)"; + std::wstring winPath = fsutils::toNativePath(unwantedAbsPath).toStdWString(); + DWORD dwAttrs = ::GetFileAttributesW(winPath.c_str()); + bool ret = ::SetFileAttributesW(winPath.c_str(), dwAttrs | FILE_ATTRIBUTE_HIDDEN); + Q_ASSERT(ret != 0); Q_UNUSED(ret); + } +#endif + QString parentPath = fsutils::branchPath(filepath); + if (!parentPath.isEmpty() && !parentPath.endsWith("/")) + parentPath += "/"; + renameFile(i, parentPath + ".unwanted/" + fsutils::fileName(filepath)); + } + } + + // Move wanted files back to their original folder + if (priorities[i] > 0) { + QString parentRelPath = fsutils::branchPath(filepath); + if (QDir(parentRelPath).dirName() == ".unwanted") { + QString oldName = fsutils::fileName(filepath); + QString newRelPath = fsutils::branchPath(parentRelPath); + if (newRelPath.isEmpty()) + renameFile(i, oldName); + else + renameFile(i, QDir(newRelPath).filePath(oldName)); + + // Remove .unwanted directory if empty + qDebug() << "Attempting to remove .unwanted folder at " << QDir(spath + "/" + newRelPath).absoluteFilePath(".unwanted"); + QDir(spath + "/" + newRelPath).rmdir(".unwanted"); + } + } + } + + updateStatus(); +} diff --git a/src/core/bittorrent/torrenthandle.h b/src/core/bittorrent/torrenthandle.h new file mode 100644 index 000000000..541ce96c4 --- /dev/null +++ b/src/core/bittorrent/torrenthandle.h @@ -0,0 +1,380 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * 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. + */ + +#ifndef BITTORRENT_TORRENTHANDLE_H +#define BITTORRENT_TORRENTHANDLE_H + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "core/tristatebool.h" +#include "private/speedmonitor.h" +#include "private/torrenthandleprivate.h" +#include "infohash.h" +#include "torrentinfo.h" + +class QBitArray; +class QStringList; +template struct QPair; + +namespace libtorrent +{ + class alert; + struct stats_alert; + struct torrent_checked_alert; + struct torrent_finished_alert; + struct torrent_paused_alert; + struct torrent_resumed_alert; + struct save_resume_data_alert; + struct save_resume_data_failed_alert; + struct file_renamed_alert; + struct storage_moved_alert; + struct storage_moved_failed_alert; + struct metadata_received_alert; + struct file_completed_alert; + struct tracker_error_alert; + struct tracker_reply_alert; + struct tracker_warning_alert; + struct fastresume_rejected_alert; +} + +struct SessionPrivate; + +namespace BitTorrent +{ + struct PeerAddress; + class Session; + class PeerInfo; + class TrackerEntry; + struct AddTorrentParams; + + struct AddTorrentData + { + bool resumed; + // for both new and resumed torrents + QString name; + QString label; + QString savePath; + bool disableTempPath; + bool sequential; + bool hasSeedStatus; + // for new torrents + TriStateBool addPaused; + QVector filePriorities; + bool ignoreShareRatio; + // for resumed torrents + QDateTime addedTime; + qreal ratioLimit; + + AddTorrentData(); + AddTorrentData(const AddTorrentParams &in); + }; + + struct TrackerInfo + { + QString lastMessage; + quint32 numPeers; + + TrackerInfo(); + }; + + class TorrentState + { + public: + enum + { + Unknown = -1, + + Error, + + Uploading, + PausedUploading, + QueuedUploading, + StalledUploading, + CheckingUploading, + ForcedUploading, + + Allocating, + + DownloadingMetadata, + Downloading, + PausedDownloading, + QueuedDownloading, + StalledDownloading, + CheckingDownloading, + ForcedDownloading + }; + + TorrentState(int value); + + operator int() const; + QString toString() const; + + private: + int m_value; + }; + + class TorrentHandle : public QObject, public TorrentHandlePrivate + { + Q_DISABLE_COPY(TorrentHandle) + + public: + static const qreal USE_GLOBAL_RATIO; + static const qreal NO_RATIO_LIMIT; + + static const qreal MAX_RATIO; + + TorrentHandle(Session *session, const libtorrent::torrent_handle &nativeHandle, + const AddTorrentData &data); + ~TorrentHandle(); + + bool isValid() const; + InfoHash hash() const; + QString name() const; + QDateTime creationDate() const; + QString creator() const; + QString comment() const; + bool isPrivate() const; + qlonglong totalSize() const; + qlonglong wantedSize() const; + qlonglong completedSize() const; + qlonglong incompletedSize() const; + qlonglong pieceLength() const; + qlonglong wastedSize() const; + QString currentTracker() const; + QString actualSavePath() const; + QString savePath() const; + QString rootPath() const; + QString savePathParsed() const; + int filesCount() const; + int piecesCount() const; + qreal progress() const; + QString label() const; + QDateTime addedTime() const; + qreal ratioLimit() const; + + QString firstFileSavePath() const; + QString filePath(int index) const; + QString fileName(int index) const; + qlonglong fileSize(int index) const; + QStringList absoluteFilePaths() const; + QStringList absoluteFilePathsUnwanted() const; + QPair fileExtremityPieces(int index) const; + QVector filePriorities() const; + + TorrentInfo info() const; + bool isSeed() const; + bool isPaused() const; + bool isResumed() const; + bool isQueued() const; + bool isForced() const; + bool isChecking() const; + bool isDownloading() const; + bool isUploading() const; + bool isCompleted() const; + bool isActive() const; + bool isInactive() const; + bool isSequentialDownload() const; + bool hasFirstLastPiecePriority() const; + TorrentState state() const; + bool hasMetadata() const; + bool hasMissingFiles() const; + bool hasError() const; + bool hasFilteredPieces() const; + int queuePosition() const; + QList trackers() const; + QHash trackerInfos() const; + QList urlSeeds() const; + QString error() const; + qlonglong totalDownload() const; + qlonglong totalUpload() const; + int activeTime() const; + int seedingTime() const; + qulonglong eta() const; + QVector filesProgress() const; + int seedsCount() const; + int peersCount() const; + int leechsCount() const; + int completeCount() const; + int incompleteCount() const; + QDateTime lastSeenComplete() const; + QDateTime completedTime() const; + int timeSinceUpload() const; + int timeSinceDownload() const; + int downloadLimit() const; + int uploadLimit() const; + bool superSeeding() const; + QList peers() const; + QBitArray pieces() const; + QBitArray downloadingPieces() const; + QVector pieceAvailability() const; + qreal distributedCopies() const; + qreal maxRatio(bool *usesGlobalRatio = 0) const; + qreal realRatio() const; + int uploadPayloadRate() const; + int downloadPayloadRate() const; + int totalPayloadUpload() const; + int totalPayloadDownload() const; + int connectionsCount() const; + int connectionsLimit() const; + qlonglong nextAnnounce() const; + + void setName(const QString &name); + void setLabel(const QString &label); + void setSequentialDownload(bool b); + void toggleSequentialDownload(); + void toggleFirstLastPiecePriority(); + void setFirstLastPiecePriority(bool b); + void pause(); + void resume(bool forced = false); + void move(QString path); + #if LIBTORRENT_VERSION_NUM < 10000 + void forceReannounce(); + #else + void forceReannounce(int index = -1); + #endif + void forceDHTAnnounce(); + void forceRecheck(); + void setTrackerLogin(const QString &username, const QString &password); + void renameFile(int index, const QString &name); + bool saveTorrentFile(const QString &path); + void prioritizeFiles(const QVector &priorities); + void setFilePriority(int index, int priority); + void setRatioLimit(qreal limit); + void setUploadLimit(int limit); + void setDownloadLimit(int limit); + void setSuperSeeding(bool enable); + void flushCache(); + void addTrackers(const QList &trackers); + void replaceTrackers(QList trackers); + void addUrlSeeds(const QList &urlSeeds); + void removeUrlSeeds(const QList &urlSeeds); + bool connectPeer(const PeerAddress &peerAddress); + + QString toMagnetUri() const; + + bool needSaveResumeData() const; + void saveResumeData(); + + libtorrent::torrent_handle nativeHandle() const; + + private: + typedef boost::function MoveStorageTrigger; + + void initialize(); + void updateStatus(); + void updateStatus(const libtorrent::torrent_status &nativeStatus); + void updateState(); + void updateTorrentInfo(); + + void handleAlert(libtorrent::alert *a); + void handleStateUpdate(const libtorrent::torrent_status &nativeStatus); + void handleDefaultSavePathChanged(); + void handleTempPathChanged(); + void handleAppendExtensionToggled(); + #ifndef DISABLE_COUNTRIES_RESOLUTION + void handleResolveCountriesToggled(); + #endif + + void handleStorageMovedAlert(libtorrent::storage_moved_alert *p); + void handleStorageMovedFailedAlert(libtorrent::storage_moved_failed_alert *p); + void handleTrackerReplyAlert(libtorrent::tracker_reply_alert *p); + void handleTrackerWarningAlert(libtorrent::tracker_warning_alert *p); + void handleTrackerErrorAlert(libtorrent::tracker_error_alert *p); + void handleTorrentCheckedAlert(libtorrent::torrent_checked_alert *p); + void handleTorrentFinishedAlert(libtorrent::torrent_finished_alert *p); + void handleTorrentPausedAlert(libtorrent::torrent_paused_alert *p); + void handleTorrentResumedAlert(libtorrent::torrent_resumed_alert *p); + void handleSaveResumeDataAlert(libtorrent::save_resume_data_alert *p); + void handleSaveResumeDataFailedAlert(libtorrent::save_resume_data_failed_alert *p); + void handleFastResumeRejectedAlert(libtorrent::fastresume_rejected_alert *p); + void handleFileRenamedAlert(libtorrent::file_renamed_alert *p); + void handleFileCompletedAlert(libtorrent::file_completed_alert *p); + void handleMetadataReceivedAlert(libtorrent::metadata_received_alert *p); + void handleStatsAlert(libtorrent::stats_alert *p); + + bool isMoveInProgress() const; + bool useTempPath() const; + QString nativeActualSavePath() const; + + void resolveCountries(bool b); + void adjustSavePath(); + void adjustActualSavePath(); + void moveStorage(const QString &newPath); + void appendExtensionsToIncompleteFiles(); + void removeExtensionsFromIncompleteFiles(); + bool addTracker(const TrackerEntry &tracker); + bool addUrlSeed(const QUrl &urlSeed); + bool removeUrlSeed(const QUrl &urlSeed); + + SessionPrivate *const m_session; + libtorrent::torrent_handle m_nativeHandle; + libtorrent::torrent_status m_nativeStatus; + TorrentState m_state; + TorrentInfo m_torrentInfo; + SpeedMonitor m_speedMonitor; + #if LIBTORRENT_VERSION_NUM < 10000 + QString m_nativeName; + QString m_nativeSavePath; + #endif + + InfoHash m_hash; + + QString m_oldPath; + QString m_newPath; + // m_queuedPath is where files should be moved to, + // when current moving is completed + QString m_queuedPath; + QQueue m_moveStorageTriggers; + + // Persistent data + QString m_name; + QDateTime m_addedTime; + QString m_savePath; + QString m_label; + bool m_hasSeedStatus; + qreal m_ratioLimit; + bool m_tempPathDisabled; + bool m_hasMissingFiles; + + bool m_useDefaultSavePath; + bool m_pauseAfterRecheck; + bool m_needSaveResumeData; + QHash m_trackerInfos; + }; +} + +#endif // BITTORRENT_TORRENTHANDLE_H diff --git a/src/core/bittorrent/torrentinfo.cpp b/src/core/bittorrent/torrentinfo.cpp new file mode 100644 index 000000000..2f00a96ba --- /dev/null +++ b/src/core/bittorrent/torrentinfo.cpp @@ -0,0 +1,230 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include +#include +#include +#include + +#include +#include + +#include "core/misc.h" +#include "core/fs_utils.h" +#include "core/utils/string.h" +#include "infohash.h" +#include "trackerentry.h" +#include "torrentinfo.h" + +namespace libt = libtorrent; +using namespace BitTorrent; + +TorrentInfo::TorrentInfo(boost::intrusive_ptr nativeInfo) + : m_nativeInfo(const_cast(nativeInfo.get())) +{ +} + +TorrentInfo::TorrentInfo(const TorrentInfo &other) + : m_nativeInfo(other.m_nativeInfo) +{ +} + +TorrentInfo &TorrentInfo::operator=(const TorrentInfo &other) +{ + m_nativeInfo = other.m_nativeInfo; + return *this; +} + +TorrentInfo TorrentInfo::loadFromFile(const QString &path, QString &error) +{ + error.clear(); + libt::error_code ec; + TorrentInfo info(new libt::torrent_info(String::toStdString(fsutils::toNativePath(path)), ec)); + if (ec) { + error = QString::fromUtf8(ec.message().c_str()); + qDebug("Cannot load .torrent file: %s", qPrintable(error)); + } + + return info; +} + +TorrentInfo TorrentInfo::loadFromFile(const QString &path) +{ + QString error; + return loadFromFile(path, error); +} + +bool TorrentInfo::isValid() const +{ + return (m_nativeInfo && m_nativeInfo->is_valid() && (m_nativeInfo->num_files() > 0)); +} + +InfoHash TorrentInfo::hash() const +{ + if (!isValid()) return InfoHash(); + return m_nativeInfo->info_hash(); +} + +QString TorrentInfo::name() const +{ + if (!isValid()) return QString(); + return String::fromStdString(m_nativeInfo->name()); +} + +QDateTime TorrentInfo::creationDate() const +{ + if (!isValid()) return QDateTime(); + boost::optional t = m_nativeInfo->creation_date(); + return t ? QDateTime::fromTime_t(*t) : QDateTime(); +} + +QString TorrentInfo::creator() const +{ + if (!isValid()) return QString(); + return String::fromStdString(m_nativeInfo->creator()); +} + +QString TorrentInfo::comment() const +{ + if (!isValid()) return QString(); + return String::fromStdString(m_nativeInfo->comment()); +} + +bool TorrentInfo::isPrivate() const +{ + if (!isValid()) return false; + return m_nativeInfo->priv(); +} + +qlonglong TorrentInfo::totalSize() const +{ + if (!isValid()) return -1; + return m_nativeInfo->total_size(); +} + +int TorrentInfo::filesCount() const +{ + if (!isValid()) return -1; + return m_nativeInfo->num_files(); +} + +int TorrentInfo::pieceLength() const +{ + if (!isValid()) return -1; + return m_nativeInfo->piece_length(); +} + +int TorrentInfo::piecesCount() const +{ + if (!isValid()) return -1; + return m_nativeInfo->num_pieces(); +} + +QString TorrentInfo::filePath(int index) const +{ + if (!isValid()) return QString(); + return fsutils::fromNativePath(String::fromStdString(m_nativeInfo->files().file_path(index))); +} + +QStringList TorrentInfo::filePaths() const +{ + QStringList list; + for (int i = 0; i < filesCount(); ++i) + list << filePath(i); + + return list; +} + +QString TorrentInfo::fileName(int index) const +{ + return fsutils::fileName(filePath(index)); +} + +QString TorrentInfo::origFilePath(int index) const +{ + if (!isValid()) return QString(); + return fsutils::fromNativePath(String::fromStdString(m_nativeInfo->orig_files().file_path(index))); +} + +qlonglong TorrentInfo::fileSize(int index) const +{ + if (!isValid()) return -1; + return m_nativeInfo->files().file_size(index); +} + +qlonglong TorrentInfo::fileOffset(int index) const +{ + if (!isValid()) return -1; + return m_nativeInfo->file_at(index).offset; +} + +QList TorrentInfo::trackers() const +{ + if (!isValid()) return QList(); + + QList trackers; + foreach (const libt::announce_entry &tracker, m_nativeInfo->trackers()) + trackers.append(tracker); + + return trackers; +} + +QList TorrentInfo::urlSeeds() const +{ + if (!isValid()) return QList(); + + QList urlSeeds; + foreach (const libt::web_seed_entry &webSeed, m_nativeInfo->web_seeds()) + if (webSeed.type == libt::web_seed_entry::url_seed) + urlSeeds.append(QUrl(webSeed.url.c_str())); + + return urlSeeds; +} + +QByteArray TorrentInfo::metadata() const +{ + if (!isValid()) return QByteArray(); + return QByteArray(m_nativeInfo->metadata().get(), m_nativeInfo->metadata_size()); +} + +QString TorrentInfo::toMagnetUri() const +{ + if (!isValid()) return QString(); + return String::fromStdString(libt::make_magnet_uri(*m_nativeInfo)); +} + +void TorrentInfo::renameFile(uint index, const QString &newPath) +{ + if (!isValid()) return; + m_nativeInfo->rename_file(index, String::toStdString(newPath)); +} + +boost::intrusive_ptr TorrentInfo::nativeInfo() const +{ + return m_nativeInfo; +} diff --git a/src/core/bittorrent/torrentinfo.h b/src/core/bittorrent/torrentinfo.h new file mode 100644 index 000000000..44fc4adb2 --- /dev/null +++ b/src/core/bittorrent/torrentinfo.h @@ -0,0 +1,88 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * 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. + */ + +#ifndef BITTORRENT_TORRENTINFO_H +#define BITTORRENT_TORRENTINFO_H + +#include +#include + +class QString; +class QUrl; +class QDateTime; +class QStringList; +class QByteArray; +template class QList; + +namespace BitTorrent +{ + class InfoHash; + class TrackerEntry; + + class TorrentInfo + { + public: + explicit TorrentInfo(boost::intrusive_ptr nativeInfo = boost::intrusive_ptr()); + TorrentInfo(const TorrentInfo &other); + + static TorrentInfo loadFromFile(const QString &path, QString &error); + static TorrentInfo loadFromFile(const QString &path); + + TorrentInfo &operator=(const TorrentInfo &other); + + bool isValid() const; + InfoHash hash() const; + QString name() const; + QDateTime creationDate() const; + QString creator() const; + QString comment() const; + bool isPrivate() const; + qlonglong totalSize() const; + int filesCount() const; + int pieceLength() const; + int piecesCount() const; + QString filePath(int index) const; + QStringList filePaths() const; + QString fileName(int index) const; + QString origFilePath(int index) const; + qlonglong fileSize(int index) const; + qlonglong fileOffset(int index) const; + QList trackers() const; + QList urlSeeds() const; + QByteArray metadata() const; + QString toMagnetUri() const; + + void renameFile(uint index, const QString &newPath); + boost::intrusive_ptr nativeInfo() const; + + private: + boost::intrusive_ptr m_nativeInfo; + }; +} + +#endif // BITTORRENT_TORRENTINFO_H diff --git a/src/core/qtracker.cpp b/src/core/bittorrent/tracker.cpp similarity index 54% rename from src/core/qtracker.cpp rename to src/core/bittorrent/tracker.cpp index cc7146861..9b6558813 100644 --- a/src/core/qtracker.cpp +++ b/src/core/bittorrent/tracker.cpp @@ -31,59 +31,68 @@ #include #include +#include -#include "preferences.h" -#include "http/server.h" -#include "qtracker.h" +#include "core/preferences.h" +#include "core/http/server.h" +#include "core/utils/string.h" +#include "tracker.h" -// QPeer -bool QPeer::operator!=(const QPeer &other) const +// static limits +static const int MAX_TORRENTS = 100; +static const int MAX_PEERS_PER_TORRENT = 1000; +static const int ANNOUNCE_INTERVAL = 1800; // 30min + +using namespace BitTorrent; + +// Peer +bool Peer::operator!=(const Peer &other) const { - return qhash() != other.qhash(); + return uid() != other.uid(); } -bool QPeer::operator==(const QPeer &other) const +bool Peer::operator==(const Peer &other) const { - return qhash() == other.qhash(); + return uid() == other.uid(); } -QString QPeer::qhash() const +QString Peer::uid() const { return ip + ":" + QString::number(port); } -libtorrent::entry QPeer::toEntry(bool no_peer_id) const +libtorrent::entry Peer::toEntry(bool noPeerId) const { - libtorrent::entry::dictionary_type peer_map; - if (!no_peer_id) - peer_map["id"] = libtorrent::entry(peer_id.toStdString()); - peer_map["ip"] = libtorrent::entry(ip.toStdString()); - peer_map["port"] = libtorrent::entry(port); + libtorrent::entry::dictionary_type peerMap; + if (!noPeerId) + peerMap["id"] = libtorrent::entry(String::toStdString(peerId)); + peerMap["ip"] = libtorrent::entry(String::toStdString(ip)); + peerMap["port"] = libtorrent::entry(port); - return libtorrent::entry(peer_map); + return libtorrent::entry(peerMap); } -// QTracker +// Tracker -QTracker::QTracker(QObject *parent) +Tracker::Tracker(QObject *parent) : Http::ResponseBuilder(parent) , m_server(new Http::Server(this, this)) { } -QTracker::~QTracker() +Tracker::~Tracker() { if (m_server->isListening()) qDebug("Shutting down the embedded tracker..."); // TODO: Store the torrent list } -bool QTracker::start() +bool Tracker::start() { - const int listen_port = Preferences::instance()->getTrackerPort(); + const int listenPort = Preferences::instance()->getTrackerPort(); if (m_server->isListening()) { - if (m_server->serverPort() == listen_port) { + if (m_server->serverPort() == listenPort) { // Already listening on the right port, just return return true; } @@ -93,21 +102,21 @@ bool QTracker::start() qDebug("Starting the embedded tracker..."); // Listen on the predefined port - return m_server->listen(QHostAddress::Any, listen_port); + return m_server->listen(QHostAddress::Any, listenPort); } -Http::Response QTracker::processRequest(const Http::Request &request, const Http::Environment &env) +Http::Response Tracker::processRequest(const Http::Request &request, const Http::Environment &env) { clear(); // clear response - //qDebug("QTracker received the following request:\n%s", qPrintable(parser.toString())); + //qDebug("Tracker received the following request:\n%s", qPrintable(parser.toString())); // Is request a GET request? if (request.method != "GET") { - qDebug("QTracker: Unsupported HTTP request: %s", qPrintable(request.method)); + qDebug("Tracker: Unsupported HTTP request: %s", qPrintable(request.method)); status(100, "Invalid request type"); } else if (!request.path.startsWith("/announce", Qt::CaseInsensitive)) { - qDebug("QTracker: Unrecognized path: %s", qPrintable(request.path)); + qDebug("Tracker: Unrecognized path: %s", qPrintable(request.path)); status(100, "Invalid request type"); } else { @@ -120,85 +129,85 @@ Http::Response QTracker::processRequest(const Http::Request &request, const Http return response(); } -void QTracker::respondToAnnounceRequest() +void Tracker::respondToAnnounceRequest() { const QStringMap &gets = m_request.gets; - TrackerAnnounceRequest annonce_req; + TrackerAnnounceRequest annonceReq; // IP - annonce_req.peer.ip = m_env.clientAddress.toString(); + annonceReq.peer.ip = m_env.clientAddress.toString(); // 1. Get info_hash if (!gets.contains("info_hash")) { - qDebug("QTracker: Missing info_hash"); + qDebug("Tracker: Missing info_hash"); status(101, "Missing info_hash"); return; } - annonce_req.info_hash = gets.value("info_hash"); + annonceReq.infoHash = gets.value("info_hash"); // info_hash cannot be longer than 20 bytes /*if (annonce_req.info_hash.toLatin1().length() > 20) { - qDebug("QTracker: Info_hash is not 20 byte long: %s (%d)", qPrintable(annonce_req.info_hash), annonce_req.info_hash.toLatin1().length()); + qDebug("Tracker: Info_hash is not 20 byte long: %s (%d)", qPrintable(annonce_req.info_hash), annonce_req.info_hash.toLatin1().length()); status(150, "Invalid infohash"); return; }*/ // 2. Get peer ID if (!gets.contains("peer_id")) { - qDebug("QTracker: Missing peer_id"); + qDebug("Tracker: Missing peer_id"); status(102, "Missing peer_id"); return; } - annonce_req.peer.peer_id = gets.value("peer_id"); + annonceReq.peer.peerId = gets.value("peer_id"); // peer_id cannot be longer than 20 bytes /*if (annonce_req.peer.peer_id.length() > 20) { - qDebug("QTracker: peer_id is not 20 byte long: %s", qPrintable(annonce_req.peer.peer_id)); + qDebug("Tracker: peer_id is not 20 byte long: %s", qPrintable(annonce_req.peer.peer_id)); status(151, "Invalid peerid"); return; }*/ // 3. Get port if (!gets.contains("port")) { - qDebug("QTracker: Missing port"); + qDebug("Tracker: Missing port"); status(103, "Missing port"); return; } bool ok = false; - annonce_req.peer.port = gets.value("port").toInt(&ok); - if (!ok || annonce_req.peer.port < 1 || annonce_req.peer.port > 65535) { - qDebug("QTracker: Invalid port number (%d)", annonce_req.peer.port); + annonceReq.peer.port = gets.value("port").toInt(&ok); + if (!ok || annonceReq.peer.port < 1 || annonceReq.peer.port > 65535) { + qDebug("Tracker: Invalid port number (%d)", annonceReq.peer.port); status(103, "Missing port"); return; } // 4. Get event - annonce_req.event = ""; + annonceReq.event = ""; if (gets.contains("event")) { - annonce_req.event = gets.value("event"); - qDebug("QTracker: event is %s", qPrintable(annonce_req.event)); + annonceReq.event = gets.value("event"); + qDebug("Tracker: event is %s", qPrintable(annonceReq.event)); } // 5. Get numwant - annonce_req.numwant = 50; + annonceReq.numwant = 50; if (gets.contains("numwant")) { int tmp = gets.value("numwant").toInt(); if (tmp > 0) { - qDebug("QTracker: numwant = %d", tmp); - annonce_req.numwant = tmp; + qDebug("Tracker: numwant = %d", tmp); + annonceReq.numwant = tmp; } } // 6. no_peer_id (extension) - annonce_req.no_peer_id = false; + annonceReq.noPeerId = false; if (gets.contains("no_peer_id")) - annonce_req.no_peer_id = true; + annonceReq.noPeerId = true; // 7. TODO: support "compact" extension // Done parsing, now let's reply - if (m_torrents.contains(annonce_req.info_hash)) { - if (annonce_req.event == "stopped") { - qDebug("QTracker: Peer stopped downloading, deleting it from the list"); - m_torrents[annonce_req.info_hash].remove(annonce_req.peer.qhash()); + if (m_torrents.contains(annonceReq.infoHash)) { + if (annonceReq.event == "stopped") { + qDebug("Tracker: Peer stopped downloading, deleting it from the list"); + m_torrents[annonceReq.infoHash].remove(annonceReq.peer.uid()); return; } } @@ -210,36 +219,36 @@ void QTracker::respondToAnnounceRequest() } } // Register the user - PeerList peers = m_torrents.value(annonce_req.info_hash); + PeerList peers = m_torrents.value(annonceReq.infoHash); if (peers.size() == MAX_PEERS_PER_TORRENT) { // Too many peers, remove a random one peers.erase(peers.begin()); } - peers[annonce_req.peer.qhash()] = annonce_req.peer; - m_torrents[annonce_req.info_hash] = peers; + peers[annonceReq.peer.uid()] = annonceReq.peer; + m_torrents[annonceReq.infoHash] = peers; // Reply - replyWithPeerList(annonce_req); + replyWithPeerList(annonceReq); } -void QTracker::replyWithPeerList(const TrackerAnnounceRequest &annonce_req) +void Tracker::replyWithPeerList(const TrackerAnnounceRequest &annonceReq) { // Prepare the entry for bencoding - libtorrent::entry::dictionary_type reply_dict; - reply_dict["interval"] = libtorrent::entry(ANNOUNCE_INTERVAL); - QList peers = m_torrents.value(annonce_req.info_hash).values(); - libtorrent::entry::list_type peer_list; - foreach (const QPeer &p, peers) { + libtorrent::entry::dictionary_type replyDict; + replyDict["interval"] = libtorrent::entry(ANNOUNCE_INTERVAL); + QList peers = m_torrents.value(annonceReq.infoHash).values(); + libtorrent::entry::list_type peerList; + foreach (const Peer &p, peers) { //if (p != annonce_req.peer) - peer_list.push_back(p.toEntry(annonce_req.no_peer_id)); + peerList.push_back(p.toEntry(annonceReq.noPeerId)); } - reply_dict["peers"] = libtorrent::entry(peer_list); - libtorrent::entry reply_entry(reply_dict); + replyDict["peers"] = libtorrent::entry(peerList); + libtorrent::entry replyEntry(replyDict); // bencode std::vector buf; - libtorrent::bencode(std::back_inserter(buf), reply_entry); + libtorrent::bencode(std::back_inserter(buf), replyEntry); QByteArray reply(&buf[0], static_cast(buf.size())); - qDebug("QTracker: reply with the following bencoded data:\n %s", reply.constData()); + qDebug("Tracker: reply with the following bencoded data:\n %s", reply.constData()); // HTTP reply print(reply, Http::CONTENT_TYPE_TXT); diff --git a/src/core/bittorrent/tracker.h b/src/core/bittorrent/tracker.h new file mode 100644 index 000000000..be96b4b1e --- /dev/null +++ b/src/core/bittorrent/tracker.h @@ -0,0 +1,103 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * Copyright (C) 2010 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 BITTORRENT_TRACKER_H +#define BITTORRENT_TRACKER_H + +#include +#include "core/http/types.h" +#include "core/http/responsebuilder.h" +#include "core/http/irequesthandler.h" + +namespace libtorrent +{ + class entry; +} + +namespace Http +{ + class Server; +} + +namespace BitTorrent +{ + struct Peer + { + QString ip; + QString peerId; + int port; + + bool operator!=(const Peer &other) const; + bool operator==(const Peer &other) const; + QString uid() const; + libtorrent::entry toEntry(bool noPeerId) const; + }; + + struct TrackerAnnounceRequest + { + QString infoHash; + QString event; + int numwant; + Peer peer; + // Extensions + bool noPeerId; + }; + + typedef QHash PeerList; + typedef QHash TorrentList; + + /* Basic Bittorrent tracker implementation in Qt */ + /* Following http://wiki.theory.org/BitTorrent_Tracker_Protocol */ + class Tracker : public Http::ResponseBuilder, public Http::IRequestHandler + { + Q_OBJECT + Q_DISABLE_COPY(Tracker) + + public: + explicit Tracker(QObject *parent = 0); + ~Tracker(); + + bool start(); + Http::Response processRequest(const Http::Request &request, const Http::Environment &env); + + private: + void respondToAnnounceRequest(); + void replyWithPeerList(const TrackerAnnounceRequest &annonceReq); + + Http::Server *m_server; + TorrentList m_torrents; + + Http::Request m_request; + Http::Environment m_env; + }; +} + +#endif // BITTORRENT_TRACKER_H diff --git a/src/core/bittorrent/trackerentry.cpp b/src/core/bittorrent/trackerentry.cpp new file mode 100644 index 000000000..444cc446a --- /dev/null +++ b/src/core/bittorrent/trackerentry.cpp @@ -0,0 +1,93 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include + +#include "core/misc.h" +#include "core/utils/string.h" +#include "trackerentry.h" + +using namespace BitTorrent; + +TrackerEntry::TrackerEntry(const QString &url) + : m_nativeEntry(libtorrent::announce_entry(String::toStdString(url))) +{ +} + +TrackerEntry::TrackerEntry(const libtorrent::announce_entry &nativeEntry) + : m_nativeEntry(nativeEntry) +{ +} + +TrackerEntry::TrackerEntry(const TrackerEntry &other) + : m_nativeEntry(other.m_nativeEntry) +{ +} + +QString TrackerEntry::url() const +{ + return String::fromStdString(m_nativeEntry.url); +} + +int TrackerEntry::tier() const +{ + return m_nativeEntry.tier; +} + +TrackerEntry::Status TrackerEntry::status() const +{ + if (m_nativeEntry.verified) + return Working; + else if ((m_nativeEntry.fails == 0) && m_nativeEntry.updating) + return Updating; + else if (m_nativeEntry.fails == 0) + return NotContacted; + else + return NotWorking; +} + +void TrackerEntry::setTier(int value) +{ + m_nativeEntry.tier = value; +} + +TrackerEntry &TrackerEntry::operator=(const TrackerEntry &other) +{ + this->m_nativeEntry = other.m_nativeEntry; + return *this; +} + +bool TrackerEntry::operator==(const TrackerEntry &other) +{ + return (QUrl(url()) == QUrl(other.url())); +} + +libtorrent::announce_entry TrackerEntry::nativeEntry() const +{ + return m_nativeEntry; +} diff --git a/src/core/bittorrent/trackerentry.h b/src/core/bittorrent/trackerentry.h new file mode 100644 index 000000000..edfcac434 --- /dev/null +++ b/src/core/bittorrent/trackerentry.h @@ -0,0 +1,68 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * 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. + */ + +#ifndef BITTORRENT_TRACKERENTRY_H +#define BITTORRENT_TRACKERENTRY_H + +#include + +class QString; + +namespace BitTorrent +{ + class TrackerEntry + { + public: + enum Status + { + NotContacted, + Working, + Updating, + NotWorking + }; + + TrackerEntry(const QString &url); + TrackerEntry(const libtorrent::announce_entry &nativeEntry); + TrackerEntry(const TrackerEntry &other); + + QString url() const; + int tier() const; + Status status() const; + + void setTier(int value); + TrackerEntry &operator=(const TrackerEntry &other); + bool operator==(const TrackerEntry &other); + + libtorrent::announce_entry nativeEntry() const; + + private: + libtorrent::announce_entry m_nativeEntry; + }; +} + +#endif // BITTORRENT_TRACKERENTRY_H diff --git a/src/core/core.pri b/src/core/core.pri index 6997ca78d..c6bb3cf34 100644 --- a/src/core/core.pri +++ b/src/core/core.pri @@ -1,16 +1,13 @@ -include(qtlibtorrent/qtlibtorrent.pri) - HEADERS += \ + $$PWD/types.h \ $$PWD/misc.h \ $$PWD/fs_utils.h \ - $$PWD/downloadthread.h \ - $$PWD/torrentpersistentdata.h \ + $$PWD/tristatebool.h \ $$PWD/filesystemwatcher.h \ - $$PWD/scannedfoldersmodel.h \ $$PWD/qinisettings.h \ $$PWD/logger.h \ $$PWD/preferences.h \ - $$PWD/qtracker.h \ + $$PWD/iconprovider.h \ $$PWD/http/irequesthandler.h \ $$PWD/http/connection.h \ $$PWD/http/requestparser.h \ @@ -19,24 +16,66 @@ HEADERS += \ $$PWD/http/types.h \ $$PWD/http/responsebuilder.h \ $$PWD/net/dnsupdater.h \ + $$PWD/net/downloadmanager.h \ + $$PWD/net/downloadhandler.h \ + $$PWD/net/portforwarder.h \ $$PWD/net/reverseresolution.h \ - $$PWD/net/smtp.h + $$PWD/net/smtp.h \ + $$PWD/bittorrent/infohash.h \ + $$PWD/bittorrent/session.h \ + $$PWD/bittorrent/sessionstatus.h \ + $$PWD/bittorrent/cachestatus.h \ + $$PWD/bittorrent/magneturi.h \ + $$PWD/bittorrent/torrentinfo.h \ + $$PWD/bittorrent/torrenthandle.h \ + $$PWD/bittorrent/peerinfo.h \ + $$PWD/bittorrent/trackerentry.h \ + $$PWD/bittorrent/tracker.h \ + $$PWD/bittorrent/torrentcreatorthread.h \ + $$PWD/bittorrent/private/sessionprivate.h \ + $$PWD/bittorrent/private/torrenthandleprivate.h \ + $$PWD/bittorrent/private/speedmonitor.h \ + $$PWD/bittorrent/private/bandwidthscheduler.h \ + $$PWD/bittorrent/private/filterparserthread.h \ + $$PWD/bittorrent/private/statistics.h \ + $$PWD/utils/string.h \ + $$PWD/torrentfilter.h \ + $$PWD/scanfoldersmodel.h SOURCES += \ - $$PWD/downloadthread.cpp \ - $$PWD/scannedfoldersmodel.cpp \ - $$PWD/torrentpersistentdata.cpp \ - $$PWD/filesystemwatcher.cpp \ $$PWD/misc.cpp \ $$PWD/fs_utils.cpp \ + $$PWD/tristatebool.cpp \ + $$PWD/filesystemwatcher.cpp \ $$PWD/logger.cpp \ $$PWD/preferences.cpp \ - $$PWD/qtracker.cpp \ + $$PWD/iconprovider.cpp \ $$PWD/http/connection.cpp \ $$PWD/http/requestparser.cpp \ $$PWD/http/responsegenerator.cpp \ $$PWD/http/server.cpp \ $$PWD/http/responsebuilder.cpp \ $$PWD/net/dnsupdater.cpp \ + $$PWD/net/downloadmanager.cpp \ + $$PWD/net/downloadhandler.cpp \ + $$PWD/net/portforwarder.cpp \ $$PWD/net/reverseresolution.cpp \ - $$PWD/net/smtp.cpp + $$PWD/net/smtp.cpp \ + $$PWD/bittorrent/infohash.cpp \ + $$PWD/bittorrent/session.cpp \ + $$PWD/bittorrent/sessionstatus.cpp \ + $$PWD/bittorrent/cachestatus.cpp \ + $$PWD/bittorrent/magneturi.cpp \ + $$PWD/bittorrent/torrentinfo.cpp \ + $$PWD/bittorrent/torrenthandle.cpp \ + $$PWD/bittorrent/peerinfo.cpp \ + $$PWD/bittorrent/trackerentry.cpp \ + $$PWD/bittorrent/tracker.cpp \ + $$PWD/bittorrent/torrentcreatorthread.cpp \ + $$PWD/bittorrent/private/speedmonitor.cpp \ + $$PWD/bittorrent/private/bandwidthscheduler.cpp \ + $$PWD/bittorrent/private/filterparserthread.cpp \ + $$PWD/bittorrent/private/statistics.cpp \ + $$PWD/utils/string.cpp \ + $$PWD/torrentfilter.cpp \ + $$PWD/scanfoldersmodel.cpp diff --git a/src/core/filesystemwatcher.cpp b/src/core/filesystemwatcher.cpp index fb109545e..0debaa06f 100644 --- a/src/core/filesystemwatcher.cpp +++ b/src/core/filesystemwatcher.cpp @@ -12,8 +12,10 @@ #endif #endif -#include "fs_utils.h" -#include "misc.h" +#include "core/preferences.h" +#include "core/bittorrent/torrentinfo.h" +#include "core/bittorrent/magneturi.h" +#include "filesystemwatcher.h" #ifndef CIFS_MAGIC_NUMBER #define CIFS_MAGIC_NUMBER 0xFF534D42 @@ -30,8 +32,6 @@ const int WATCH_INTERVAL = 10000; // 10 sec const int MAX_PARTIAL_RETRIES = 5; -#include "filesystemwatcher.h" - FileSystemWatcher::FileSystemWatcher(QObject *parent) : QFileSystemWatcher(parent) { @@ -149,7 +149,7 @@ void FileSystemWatcher::processPartialTorrents() if (!QFile::exists(torrentPath)) { m_partialTorrents.remove(torrentPath); } - else if (fsutils::isValidTorrentFile(torrentPath)) { + else if (BitTorrent::TorrentInfo::loadFromFile(torrentPath).isValid()) { noLongerPartial << torrentPath; m_partialTorrents.remove(torrentPath); } @@ -197,11 +197,11 @@ void FileSystemWatcher::addTorrentsFromDir(const QDir &dir, QStringList &torrent if (fileAbsPath.endsWith(".magnet")) { QFile f(fileAbsPath); if (f.open(QIODevice::ReadOnly) - && !misc::magnetUriToHash(QString::fromLocal8Bit(f.readAll())).isEmpty()) { + && !BitTorrent::MagnetUri(QString::fromLocal8Bit(f.readAll())).isValid()) { torrents << fileAbsPath; } } - else if (fsutils::isValidTorrentFile(fileAbsPath)) { + else if (BitTorrent::TorrentInfo::loadFromFile(fileAbsPath).isValid()) { torrents << fileAbsPath; } else if (!m_partialTorrents.contains(fileAbsPath)) { diff --git a/src/core/filesystemwatcher.h b/src/core/filesystemwatcher.h index a6eda5046..9e71958ef 100644 --- a/src/core/filesystemwatcher.h +++ b/src/core/filesystemwatcher.h @@ -25,7 +25,7 @@ public: void removePath(const QString &path); signals: - void torrentsAdded(QStringList &pathList); + void torrentsAdded(const QStringList &pathList); protected slots: void scanLocalFolder(QString path); diff --git a/src/core/fs_utils.cpp b/src/core/fs_utils.cpp index 9cb37e963..012d1e61f 100644 --- a/src/core/fs_utils.cpp +++ b/src/core/fs_utils.cpp @@ -36,11 +36,7 @@ #include #include #include -#ifdef DISABLE_GUI #include -#else -#include -#endif #include #ifdef Q_OS_MAC @@ -110,17 +106,6 @@ QString fsutils::folderName(const QString& file_path) { return path.left(slash_index); } -bool fsutils::isValidTorrentFile(const QString& torrent_path) { - try { - boost::intrusive_ptr t = new torrent_info(fsutils::toNativePath(torrent_path).toUtf8().constData()); - if (!t->is_valid() || t->num_files() == 0) - return false; - } catch(std::exception&) { - return false; - } - return true; -} - /** * Remove an empty folder tree. * @@ -233,38 +218,6 @@ bool fsutils::sameFiles(const QString& path1, const QString& path2) { return same; } -QString fsutils::updateLabelInSavePath(const QString& defaultSavePath, const QString& save_path, const QString& old_label, const QString& new_label) { - if (old_label == new_label) return fsutils::fromNativePath(save_path); - QString defaultPath = fsutils::fromNativePath(defaultSavePath); - QString path = fsutils::fromNativePath(save_path); - qDebug("UpdateLabelInSavePath(%s, %s, %s)", qPrintable(path), qPrintable(old_label), qPrintable(new_label)); - if (!path.startsWith(defaultPath)) return path; - QString new_save_path = path; - new_save_path.remove(defaultPath); - QStringList path_parts = new_save_path.split("/", QString::SkipEmptyParts); - if (path_parts.empty()) { - if (!new_label.isEmpty()) - path_parts << new_label; - } else { - if (old_label.isEmpty() || path_parts.first() != old_label) { - if (path_parts.first() != new_label) - path_parts.prepend(new_label); - } else { - if (new_label.isEmpty()) { - path_parts.removeAt(0); - } else { - if (path_parts.first() != new_label) - path_parts.replace(0, new_label); - } - } - } - new_save_path = defaultPath; - if (!new_save_path.endsWith("/")) new_save_path += "/"; - new_save_path += path_parts.join("/"); - qDebug("New save path is %s", qPrintable(new_save_path)); - return new_save_path; -} - QString fsutils::toValidFileSystemName(QString filename) { qDebug("toValidFSName: %s", qPrintable(filename)); const QRegExp regex("[\\\\/:?\"*<>|]"); @@ -513,15 +466,6 @@ QString fsutils::searchEngineLocation() { return location; } -QString fsutils::BTBackupLocation() { - const QString location = fsutils::expandPathAbs(QDesktopServicesDataLocation() - + "BT_backup"); - QDir locationDir(location); - if (!locationDir.exists()) - locationDir.mkpath(locationDir.absolutePath()); - return location; -} - QString fsutils::cacheLocation() { QString location = fsutils::expandPathAbs(QDesktopServicesCacheLocation()); QDir locationDir(location); diff --git a/src/core/fs_utils.h b/src/core/fs_utils.h index eeceec516..d5dceb94b 100644 --- a/src/core/fs_utils.h +++ b/src/core/fs_utils.h @@ -46,7 +46,6 @@ namespace fsutils QString folderName(const QString& file_path); qint64 computePathSize(const QString& path); bool sameFiles(const QString& path1, const QString& path2); - QString updateLabelInSavePath(const QString &defaultSavePath, const QString &save_path, const QString& old_label, const QString& new_label); QString toValidFileSystemName(QString filename); bool isValidFileSystemName(const QString& filename); long long freeDiskSpaceOnPath(QString path); @@ -54,7 +53,6 @@ namespace fsutils bool sameFileNames(const QString& first, const QString& second); QString expandPath(const QString& path); QString expandPathAbs(const QString& path); - bool isValidTorrentFile(const QString& path); bool smartRemoveEmptyFolderTree(const QString& dir_path); bool forceRemove(const QString& file_path); @@ -64,7 +62,6 @@ namespace fsutils QString QDesktopServicesDownloadLocation(); /* End of Qt4 code */ QString searchEngineLocation(); - QString BTBackupLocation(); QString cacheLocation(); } diff --git a/src/core/iconprovider.cpp b/src/core/iconprovider.cpp new file mode 100644 index 000000000..a7e6f4eb6 --- /dev/null +++ b/src/core/iconprovider.cpp @@ -0,0 +1,64 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * Copyright (C) 2011 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. + */ + +#include +#include "iconprovider.h" + +IconProvider::IconProvider(QObject *parent) + : QObject(parent) +{ +} + +IconProvider::~IconProvider() {} + +void IconProvider::initInstance() +{ + if (!m_instance) + m_instance = new IconProvider; +} + +void IconProvider::freeInstance() +{ + if (m_instance) { + delete m_instance; + m_instance = 0; + } +} + +IconProvider *IconProvider::instance() +{ + return m_instance; +} + +QString IconProvider::getIconPath(const QString &iconId) +{ + return ":/icons/oxygen/" + iconId + ".png"; +} + +IconProvider *IconProvider::m_instance = 0; diff --git a/src/core/iconprovider.h b/src/core/iconprovider.h new file mode 100644 index 000000000..f9c00d63c --- /dev/null +++ b/src/core/iconprovider.h @@ -0,0 +1,55 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * Copyright (C) 2011 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. + */ + +#ifndef ICONPROVIDER_H +#define ICONPROVIDER_H + +#include + +class QString; + +class IconProvider : public QObject +{ + Q_DISABLE_COPY(IconProvider) + +public: + static void initInstance(); + static void freeInstance(); + static IconProvider *instance(); + + virtual QString getIconPath(const QString &iconId); + +protected: + explicit IconProvider(QObject *parent = 0); + ~IconProvider(); + + static IconProvider *m_instance; +}; + +#endif // ICONPROVIDER_H diff --git a/src/core/logger.cpp b/src/core/logger.cpp index bdd4379dd..442ca9a35 100644 --- a/src/core/logger.cpp +++ b/src/core/logger.cpp @@ -44,15 +44,18 @@ Logger::Logger() Logger::~Logger() {} -Logger * Logger::instance() +Logger *Logger::instance() { - if (!m_instance) - m_instance = new Logger; - return m_instance; } -void Logger::drop() +void Logger::initInstance() +{ + if (!m_instance) + m_instance = new Logger; +} + +void Logger::freeInstance() { if (m_instance) { delete m_instance; diff --git a/src/core/logger.h b/src/core/logger.h index eb53dd023..a132e6b73 100644 --- a/src/core/logger.h +++ b/src/core/logger.h @@ -53,9 +53,9 @@ class Logger : public QObject Q_DISABLE_COPY(Logger) public: - static Logger* instance(); - static void drop(); - ~Logger(); + static void initInstance(); + static void freeInstance(); + static Logger *instance(); void addMessage(const QString &message, const Log::MsgType &type = Log::NORMAL); #if LIBTORRENT_VERSION_NUM < 10000 @@ -72,6 +72,8 @@ signals: private: Logger(); + ~Logger(); + static Logger* m_instance; QVector m_messages; QVector m_peers; diff --git a/src/core/misc.cpp b/src/core/misc.cpp index 57bcd8254..153035a1e 100644 --- a/src/core/misc.cpp +++ b/src/core/misc.cpp @@ -1,5 +1,5 @@ /* - * Bittorrent Client using Qt4 and libtorrent. + * Bittorrent Client using Qt and libtorrent. * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -28,8 +28,6 @@ * Contact : chris@qbittorrent.org */ -#include "misc.h" - #include #include @@ -71,14 +69,7 @@ const int UNLEN = 256; #endif #endif // DISABLE_GUI -#if LIBTORRENT_VERSION_NUM < 10000 -#include -#else -#include -#endif -#include - -using namespace libtorrent; +#include "misc.h" static struct { const char *source; const char *comment; } units[] = { QT_TRANSLATE_NOOP3("misc", "B", "bytes"), @@ -88,35 +79,8 @@ static struct { const char *source; const char *comment; } units[] = { QT_TRANSLATE_NOOP3("misc", "TiB", "tebibytes (1024 gibibytes)") }; -QString misc::toQString(const std::string &str) -{ - return QString::fromLocal8Bit(str.c_str()); -} - -QString misc::toQString(const char* str) -{ - return QString::fromLocal8Bit(str); -} - -QString misc::toQStringU(const std::string &str) -{ - return QString::fromUtf8(str.c_str()); -} - -QString misc::toQStringU(const char* str) -{ - return QString::fromUtf8(str); -} - -QString misc::toQString(const libtorrent::sha1_hash &hash) -{ - char out[41]; - libtorrent::to_hex((char const*)&hash[0], libtorrent::sha1_hash::size, out); - return QString(out); -} - #ifndef DISABLE_GUI -void misc::shutdownComputer(shutDownAction action) +void misc::shutdownComputer(ShutDownAction action) { #if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) && defined(QT_DBUS_LIB) // Use dbus to power off / suspend the system @@ -378,28 +342,6 @@ QString misc::bcLinkToMagnet(QString bc_link) return magnet; } -QString misc::magnetUriToName(const QString& magnet_uri) -{ - add_torrent_params p; - error_code ec; - parse_magnet_uri(magnet_uri.toUtf8().constData(), p, ec); - - if (ec) - return QString::null; - return toQStringU(p.name); -} - -QString misc::magnetUriToHash(const QString& magnet_uri) -{ - add_torrent_params p; - error_code ec; - parse_magnet_uri(magnet_uri.toUtf8().constData(), p, ec); - - if (ec) - return QString::null; - return toQString(p.info_hash); -} - // Take a number of seconds and return an user-friendly // time duration like "1d 2h 10m". QString misc::userFriendlyDuration(qlonglong seconds) @@ -530,11 +472,6 @@ QString misc::parseHtmlLinks(const QString &raw_text) return result; } -QString misc::toQString(time_t t) -{ - return QDateTime::fromTime_t(t).toString(Qt::DefaultLocaleLongDate); -} - #ifndef DISABLE_GUI bool misc::naturalSort(QString left, QString right, bool &result) // uses lessThan comparison { // Return value indicates if functions was successful @@ -624,18 +561,6 @@ bool misc::slowEquals(const QByteArray &a, const QByteArray &b) return (diff == 0); } -void misc::loadBencodedFile(const QString &filename, std::vector &buffer, libtorrent::lazy_entry &entry, libtorrent::error_code &ec) -{ - QFile file(filename); - if (!file.open(QIODevice::ReadOnly)) return; - const qint64 content_size = file.bytesAvailable(); - if (content_size <= 0) return; - buffer.resize(content_size); - file.read(&buffer[0], content_size); - // bdecode - lazy_bdecode(&buffer[0], &buffer[0] + buffer.size(), entry, ec); -} - namespace { // Trick to get a portable sleep() function class SleeperThread: public QThread { diff --git a/src/core/misc.h b/src/core/misc.h index b99d58ae7..70afc1cd6 100644 --- a/src/core/misc.h +++ b/src/core/misc.h @@ -1,5 +1,5 @@ /* - * Bittorrent Client using Qt4 and libtorrent. + * Bittorrent Client using Qt and libtorrent. * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -39,37 +39,18 @@ #include #include #include -#ifndef DISABLE_GUI -#include -#endif -#include -#include - -namespace libtorrent { -#if LIBTORRENT_VERSION_NUM < 10000 - class big_number; - typedef big_number sha1_hash; -#else - class sha1_hash; -#endif - struct lazy_entry; -} +namespace BitTorrent { class TorrentHandle; } const qlonglong MAX_ETA = 8640000; -enum shutDownAction { NO_SHUTDOWN, SHUTDOWN_COMPUTER, SUSPEND_COMPUTER, HIBERNATE_COMPUTER }; +enum ShutDownAction { NO_SHUTDOWN, SHUTDOWN_COMPUTER, SUSPEND_COMPUTER, HIBERNATE_COMPUTER }; /* Miscellaneaous functions that can be useful */ namespace misc { - QString toQString(const std::string &str); - QString toQString(const char* str); - QString toQStringU(const std::string &str); - QString toQStringU(const char* str); - QString toQString(const libtorrent::sha1_hash &hash); #ifndef DISABLE_GUI - void shutdownComputer(shutDownAction action = SHUTDOWN_COMPUTER); + void shutdownComputer(ShutDownAction action = SHUTDOWN_COMPUTER); #endif QString parseHtmlLinks(const QString &raw_text); @@ -87,8 +68,7 @@ namespace misc // value must be given in bytes QString friendlyUnit(qreal val, bool is_speed = false); bool isPreviewable(const QString& extension); - QString magnetUriToName(const QString& magnet_uri); - QString magnetUriToHash(const QString& magnet_uri); + QString bcLinkToMagnet(QString bc_link); // Take a number of seconds and return an user-friendly // time duration like "1d 2h 10m". @@ -100,7 +80,6 @@ namespace misc QList intListfromStringList(const QStringList &l); QList boolListfromStringList(const QStringList &l); - QString toQString(time_t t); QString accurateDoubleToString(const double &n, const int &precision); #ifndef DISABLE_GUI @@ -110,8 +89,6 @@ namespace misc // Implements constant-time comparison to protect against timing attacks // Taken from https://crackstation.net/hashing-security.htm bool slowEquals(const QByteArray &a, const QByteArray &b); - void loadBencodedFile(const QString &filename, std::vector &buffer, libtorrent::lazy_entry &entry, libtorrent::error_code &ec); - void msleep(unsigned long msecs); } diff --git a/src/core/net/downloadhandler.cpp b/src/core/net/downloadhandler.cpp index fe86a9de1..99bb88af5 100644 --- a/src/core/net/downloadhandler.cpp +++ b/src/core/net/downloadhandler.cpp @@ -33,12 +33,13 @@ #include #include #include +#include #include #include -#include "fs_utils.h" -#include "misc.h" +#include "core/fs_utils.h" +#include "core/misc.h" #include "downloadmanager.h" #include "downloadhandler.h" diff --git a/src/core/net/downloadhandler.h b/src/core/net/downloadhandler.h index d427eabad..3de172b81 100644 --- a/src/core/net/downloadhandler.h +++ b/src/core/net/downloadhandler.h @@ -35,6 +35,7 @@ QT_BEGIN_NAMESPACE class QNetworkAccessManager; class QNetworkReply; +class QUrl; QT_END_NAMESPACE namespace Net diff --git a/src/core/net/portforwarder.cpp b/src/core/net/portforwarder.cpp new file mode 100644 index 000000000..9e7a1ec5d --- /dev/null +++ b/src/core/net/portforwarder.cpp @@ -0,0 +1,160 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include + +#include +#if LIBTORRENT_VERSION_NUM < 10000 +#include +#include +#endif + +#include "core/logger.h" +#include "core/preferences.h" +#include "portforwarder.h" + +namespace libt = libtorrent; +using namespace Net; + +PortForwarder::PortForwarder(libtorrent::session *provider, QObject *parent) + : QObject(parent) + , m_active(false) + , m_provider(provider) +#if LIBTORRENT_VERSION_NUM < 10000 + , m_upnp(0) + , m_natpmp(0) +#endif +{ + configure(); + connect(Preferences::instance(), SIGNAL(changed()), SLOT(configure())); +} + +PortForwarder::~PortForwarder() +{ + stop(); +} + +void PortForwarder::initInstance(libtorrent::session *const provider) +{ + if (!m_instance) + m_instance = new PortForwarder(provider); +} + +void PortForwarder::freeInstance() +{ + if (m_instance) { + delete m_instance; + m_instance = 0; + } +} + +PortForwarder *PortForwarder::instance() +{ + return m_instance; +} + +void PortForwarder::addPort(qint16 port) +{ + if (!m_mappedPorts.contains(port)) { +#if LIBTORRENT_VERSION_NUM < 10000 + m_mappedPorts.insert(port, qMakePair(0, 0)); +#else + m_mappedPorts.insert(port, 0); +#endif + if (m_active) { +#if LIBTORRENT_VERSION_NUM < 10000 + m_mappedPorts[port].first = m_upnp->add_mapping(libt::upnp::tcp, port, port); + m_mappedPorts[port].second = m_natpmp->add_mapping(libt::natpmp::tcp, port, port); +#else + m_mappedPorts[port] = m_provider->add_port_mapping(libt::session::tcp, port, port); +#endif + } + } +} + +void PortForwarder::deletePort(qint16 port) +{ + if (m_mappedPorts.contains(port)) { + if (m_active) { +#if LIBTORRENT_VERSION_NUM < 10000 + m_upnp->delete_mapping(m_mappedPorts[port].first); + m_natpmp->delete_mapping(m_mappedPorts[port].second); +#else + m_provider->delete_port_mapping(m_mappedPorts[port]); +#endif + } + m_mappedPorts.remove(port); + } +} + +void PortForwarder::configure() +{ + bool enable = Preferences::instance()->isUPnPEnabled(); + if (m_active != enable) { + if (enable) + start(); + else + stop(); + } +} + +void PortForwarder::start() +{ + qDebug("Enabling UPnP / NAT-PMP"); +#if LIBTORRENT_VERSION_NUM < 10000 + m_upnp = m_provider->start_upnp(); + m_natpmp = m_provider->start_natpmp(); + foreach (qint16 port, m_mappedPorts.keys()) { + m_mappedPorts[port].first = m_upnp->add_mapping(libt::upnp::tcp, port, port); + m_mappedPorts[port].second = m_natpmp->add_mapping(libt::natpmp::tcp, port, port); + } +#else + m_provider->start_upnp(); + m_provider->start_natpmp(); + foreach (qint16 port, m_mappedPorts.keys()) + m_mappedPorts[port] = m_provider->add_port_mapping(libt::session::tcp, port, port); +#endif + m_active = true; + Logger::instance()->addMessage(tr("UPnP / NAT-PMP support [ON]"), Log::INFO); +} + +void PortForwarder::stop() +{ + qDebug("Disabling UPnP / NAT-PMP"); + m_provider->stop_upnp(); + m_provider->stop_natpmp(); + +#if LIBTORRENT_VERSION_NUM < 10000 + m_upnp = 0; + m_natpmp = 0; +#endif + m_active = false; + Logger::instance()->addMessage(tr("UPnP / NAT-PMP support [OFF]"), Log::INFO); +} + +PortForwarder *PortForwarder::m_instance = 0; diff --git a/src/core/net/portforwarder.h b/src/core/net/portforwarder.h new file mode 100644 index 000000000..c963a1f81 --- /dev/null +++ b/src/core/net/portforwarder.h @@ -0,0 +1,84 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * 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. + */ + +#ifndef NET_PORTFORWARDER_H +#define NET_PORTFORWARDER_H + +#include +#include +#include + +namespace libtorrent +{ +#if LIBTORRENT_VERSION_NUM < 10000 + class upnp; + class natpmp; +#endif + class session; +} + +namespace Net +{ + class PortForwarder : public QObject + { + Q_OBJECT + Q_DISABLE_COPY(PortForwarder) + + public: + static void initInstance(libtorrent::session *const provider); + static void freeInstance(); + static PortForwarder *instance(); + + void addPort(qint16 port); + void deletePort(qint16 port); + + private slots: + void configure(); + + private: + explicit PortForwarder(libtorrent::session *const provider, QObject *parent = 0); + ~PortForwarder(); + + void start(); + void stop(); + + bool m_active; + libtorrent::session *m_provider; +#if LIBTORRENT_VERSION_NUM < 10000 + libtorrent::upnp *m_upnp; + libtorrent::natpmp *m_natpmp; + QHash > m_mappedPorts; +#else + QHash m_mappedPorts; +#endif + + static PortForwarder *m_instance; + }; +} + +#endif // NET_PORTFORWARDER_H diff --git a/src/core/preferences.cpp b/src/core/preferences.cpp index 12a788227..d82739788 100644 --- a/src/core/preferences.cpp +++ b/src/core/preferences.cpp @@ -116,15 +116,18 @@ Preferences::~Preferences() save(); } -Preferences * Preferences::instance() +Preferences *Preferences::instance() { - if (!m_instance) - m_instance = new Preferences; - return m_instance; } -void Preferences::drop() +void Preferences::initInstance() +{ + if (!m_instance) + m_instance = new Preferences; +} + +void Preferences::freeInstance() { if (m_instance) { delete m_instance; @@ -132,12 +135,11 @@ void Preferences::drop() } } -void Preferences::save() +bool Preferences::save() { QReadLocker locker(&lock); - if (!dirty) - return; + if (!dirty) return false; #ifndef Q_OS_MAC // QSettings delete the file before writing it out. This can result in problems @@ -160,7 +162,7 @@ void Preferences::save() settings->sync(); // Important to get error status if (settings->status() == QSettings::AccessError) { delete settings; - return; + return false; } QString new_path = settings->fileName(); delete settings; @@ -173,7 +175,7 @@ void Preferences::save() delete settings; #endif - emit changed(); + return true; } const QVariant Preferences::value(const QString &key, const QVariant &defaultValue) const @@ -950,12 +952,12 @@ void Preferences::setGlobalMaxRatio(qreal ratio) setValue("Preferences/Bittorrent/MaxRatio", ratio); } -int Preferences::getMaxRatioAction() const +MaxRatioAction Preferences::getMaxRatioAction() const { - return value("Preferences/Bittorrent/MaxRatioAction", PAUSE_ACTION).toInt(); + return value("Preferences/Bittorrent/MaxRatioAction", MaxRatioAction::Pause).toInt(); } -void Preferences::setMaxRatioAction(int act) +void Preferences::setMaxRatioAction(MaxRatioAction act) { setValue("Preferences/Bittorrent/MaxRatioAction", act); } @@ -2477,3 +2479,9 @@ void Preferences::setHostNameCookies(const QString &host_name, const QList +#include "core/types.h" + enum scheduler_days { EVERY_DAY, @@ -57,12 +59,6 @@ enum scheduler_days SUN }; -enum maxRatioAction -{ - PAUSE_ACTION, - REMOVE_ACTION -}; - namespace Proxy { enum ProxyType @@ -101,7 +97,9 @@ class Preferences: public QObject Q_DISABLE_COPY(Preferences) private: - explicit Preferences(); + Preferences(); + ~Preferences(); + static Preferences* m_instance; QHash m_data; int m_randomPort; @@ -111,16 +109,16 @@ private: const QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; void setValue(const QString &key, const QVariant &value); +private slots: + bool save(); + signals: void changed(); -public slots: - void save(); - public: + static void initInstance(); + static void freeInstance(); static Preferences* instance(); - static void drop(); - ~Preferences(); // General options QString getLocale() const; @@ -276,8 +274,8 @@ public: void setEncryptionSetting(int val); qreal getGlobalMaxRatio() const; void setGlobalMaxRatio(qreal ratio); - int getMaxRatioAction() const; - void setMaxRatioAction(int act); + MaxRatioAction getMaxRatioAction() const; + void setMaxRatioAction(MaxRatioAction act); // IP Filter bool isFilteringEnabled() const; @@ -536,6 +534,8 @@ public slots: void setStatusFilterState(bool checked); void setLabelFilterState(bool checked); void setTrackerFilterState(bool checked); + + void apply(); }; #endif // PREFERENCES_H diff --git a/src/core/qtlibtorrent/bandwidthscheduler.h b/src/core/qtlibtorrent/bandwidthscheduler.h deleted file mode 100644 index 8794eebfb..000000000 --- a/src/core/qtlibtorrent/bandwidthscheduler.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef BANDWIDTHSCHEDULER_H -#define BANDWIDTHSCHEDULER_H - -#include -#include -#include -#include "core/preferences.h" -#include - - -class BandwidthScheduler: public QTimer { - Q_OBJECT - -public: - BandwidthScheduler(QObject *parent): QTimer(parent) { - Q_ASSERT(Preferences::instance()->isSchedulerEnabled()); - // Signal shot, we call start() again manually - setSingleShot(true); - // Connect Signals/Slots - connect(this, SIGNAL(timeout()), this, SLOT(start())); - } - -public slots: - void start() { - const Preferences* const pref = Preferences::instance(); - Q_ASSERT(pref->isSchedulerEnabled()); - bool alt_bw_enabled = pref->isAltBandwidthEnabled(); - - QTime start = pref->getSchedulerStartTime(); - QTime end = pref->getSchedulerEndTime(); - QTime now = QTime::currentTime(); - int sched_days = pref->getSchedulerDays(); - int day = QDateTime::currentDateTime().toLocalTime().date().dayOfWeek(); - bool new_mode = false; - bool reverse = false; - - if (start > end) { - QTime temp = start; - start = end; - end = temp; - reverse = true; - } - - if (start <= now && end >= now) { - switch(sched_days) { - case EVERY_DAY: - new_mode = true; - break; - case WEEK_ENDS: - if (day == 6 || day == 7) - new_mode = true; - break; - case WEEK_DAYS: - if (day != 6 && day != 7) - new_mode = true; - break; - default: - if (day == sched_days - 2) - new_mode = true; - break; - } - } - - if (reverse) - new_mode = !new_mode; - - if (new_mode != alt_bw_enabled) - emit switchToAlternativeMode(new_mode); - - // Timeout regularly to accomodate for external system clock changes - // eg from the user or from a timesync utility - QTimer::start(1500); - } - -signals: - void switchToAlternativeMode(bool alternative); -}; - -#endif // BANDWIDTHSCHEDULER_H diff --git a/src/core/qtlibtorrent/filterparserthread.cpp b/src/core/qtlibtorrent/filterparserthread.cpp deleted file mode 100644 index 41749b61f..000000000 --- a/src/core/qtlibtorrent/filterparserthread.cpp +++ /dev/null @@ -1,394 +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 "filterparserthread.h" - -#include -#include - -#include -#include - -FilterParserThread::FilterParserThread(QObject* parent, libtorrent::session *s) : QThread(parent), s(s), abort(false) { - -} - -FilterParserThread::~FilterParserThread() { - abort = true; - wait(); -} - -// Parser for eMule ip filter in DAT format -int FilterParserThread::parseDATFilterFile(QString filePath, libtorrent::ip_filter& filter) { - int ruleCount = 0; - QFile file(filePath); - if (file.exists()) { - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - std::cerr << "I/O Error: Could not open ip filer file in read mode." << std::endl; - return ruleCount; - } - unsigned int nbLine = 0; - while (!file.atEnd() && !abort) { - ++nbLine; - QByteArray line = file.readLine(); - // Ignoring empty lines - line = line.trimmed(); - if (line.isEmpty()) continue; - // Ignoring commented lines - if (line.startsWith('#') || line.startsWith("//")) continue; - - // Line should be splitted by commas - QList partsList = line.split(','); - const uint nbElem = partsList.size(); - - // IP Range should be splitted by a dash - QList IPs = partsList.first().split('-'); - if (IPs.size() != 2) { - qDebug("Ipfilter.dat: line %d is malformed.", nbLine); - qDebug("Line was %s", line.constData()); - continue; - } - - boost::system::error_code ec; - const QString strStartIP = cleanupIPAddress(IPs.at(0)); - if (strStartIP.isEmpty()) { - qDebug("Ipfilter.dat: line %d is malformed.", nbLine); - qDebug("Start IP of the range is malformated: %s", qPrintable(strStartIP)); - continue; - } - libtorrent::address startAddr = libtorrent::address::from_string(qPrintable(strStartIP), ec); - if (ec) { - qDebug("Ipfilter.dat: line %d is malformed.", nbLine); - qDebug("Start IP of the range is malformated: %s", qPrintable(strStartIP)); - continue; - } - const QString strEndIP = cleanupIPAddress(IPs.at(1)); - if (strEndIP.isEmpty()) { - qDebug("Ipfilter.dat: line %d is malformed.", nbLine); - qDebug("End IP of the range is malformated: %s", qPrintable(strEndIP)); - continue; - } - libtorrent::address endAddr = libtorrent::address::from_string(qPrintable(strEndIP), ec); - if (ec) { - qDebug("Ipfilter.dat: line %d is malformed.", nbLine); - qDebug("End IP of the range is malformated: %s", qPrintable(strEndIP)); - continue; - } - if (startAddr.is_v4() != endAddr.is_v4()) { - qDebug("Ipfilter.dat: line %d is malformed.", nbLine); - qDebug("One IP is IPv4 and the other is IPv6!"); - continue; - } - - // Check if there is an access value (apparently not mandatory) - int nbAccess = 0; - if (nbElem > 1) { - // There is possibly one - nbAccess = partsList.at(1).trimmed().toInt(); - } - - if (nbAccess > 127) { - // Ignoring this rule because access value is too high - continue; - } - // Now Add to the filter - try { - filter.add_rule(startAddr, endAddr, libtorrent::ip_filter::blocked); - ++ruleCount; - }catch(exception) { - qDebug("Bad line in filter file, avoided crash..."); - } - } - file.close(); - } - return ruleCount; -} - -// Parser for PeerGuardian ip filter in p2p format -int FilterParserThread::parseP2PFilterFile(QString filePath, libtorrent::ip_filter& filter) { - int ruleCount = 0; - QFile file(filePath); - if (file.exists()) { - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - std::cerr << "I/O Error: Could not open ip filer file in read mode." << std::endl; - return ruleCount; - } - unsigned int nbLine = 0; - while (!file.atEnd() && !abort) { - ++nbLine; - QByteArray line = file.readLine().trimmed(); - if (line.isEmpty()) continue; - // Ignoring commented lines - if (line.startsWith('#') || line.startsWith("//")) continue; - // Line is splitted by : - QList partsList = line.split(':'); - if (partsList.size() < 2) { - qDebug("p2p file: line %d is malformed.", nbLine); - continue; - } - // Get IP range - QList IPs = partsList.last().split('-'); - if (IPs.size() != 2) { - qDebug("p2p file: line %d is malformed.", nbLine); - qDebug("line was: %s", line.constData()); - continue; - } - boost::system::error_code ec; - QString strStartIP = cleanupIPAddress(IPs.at(0)); - if (strStartIP.isEmpty()) { - qDebug("p2p file: line %d is malformed.", nbLine); - qDebug("Start IP is invalid: %s", qPrintable(strStartIP)); - continue; - } - libtorrent::address startAddr = libtorrent::address::from_string(qPrintable(strStartIP), ec); - if (ec) { - qDebug("p2p file: line %d is malformed.", nbLine); - qDebug("Start IP is invalid: %s", qPrintable(strStartIP)); - continue; - } - QString strEndIP = cleanupIPAddress(IPs.at(1)); - if (strEndIP.isEmpty()) { - qDebug("p2p file: line %d is malformed.", nbLine); - qDebug("End IP is invalid: %s", qPrintable(strStartIP)); - continue; - } - libtorrent::address endAddr = libtorrent::address::from_string(qPrintable(strEndIP), ec); - if (ec) { - qDebug("p2p file: line %d is malformed.", nbLine); - qDebug("End IP is invalid: %s", qPrintable(strStartIP)); - continue; - } - if (startAddr.is_v4() != endAddr.is_v4()) { - qDebug("p2p file: line %d is malformed.", nbLine); - qDebug("Line was: %s", line.constData()); - continue; - } - try { - filter.add_rule(startAddr, endAddr, libtorrent::ip_filter::blocked); - ++ruleCount; - } catch(std::exception&) { - qDebug("p2p file: line %d is malformed.", nbLine); - qDebug("Line was: %s", line.constData()); - continue; - } - } - file.close(); - } - return ruleCount; -} - -int FilterParserThread::getlineInStream(QDataStream& stream, string& name, char delim) { - char c; - int total_read = 0; - int read; - do { - read = stream.readRawData(&c, 1); - total_read += read; - if (read > 0) { - if (c != delim) { - name += c; - } else { - // Delim found - return total_read; - } - } - } while(read > 0); - return total_read; -} - -// Parser for PeerGuardian ip filter in p2p format -int FilterParserThread::parseP2BFilterFile(QString filePath, libtorrent::ip_filter& filter) { - int ruleCount = 0; - QFile file(filePath); - if (file.exists()) { - if (!file.open(QIODevice::ReadOnly)) { - std::cerr << "I/O Error: Could not open ip filer file in read mode." << std::endl; - return ruleCount; - } - QDataStream stream(&file); - // Read header - char buf[7]; - unsigned char version; - if ( - !stream.readRawData(buf, sizeof(buf)) || - memcmp(buf, "\xFF\xFF\xFF\xFFP2B", 7) || - !stream.readRawData((char*)&version, sizeof(version)) - ) { - std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl; - return ruleCount; - } - - if (version==1 || version==2) { - qDebug ("p2b version 1 or 2"); - unsigned int start, end; - - string name; - while(getlineInStream(stream, name, '\0') && !abort) { - if ( - !stream.readRawData((char*)&start, sizeof(start)) || - !stream.readRawData((char*)&end, sizeof(end)) - ) { - std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl; - return ruleCount; - } - // Network byte order to Host byte order - // asio address_v4 contructor expects it - // that way - libtorrent::address_v4 first(ntohl(start)); - libtorrent::address_v4 last(ntohl(end)); - // Apply to bittorrent session - try { - filter.add_rule(first, last, libtorrent::ip_filter::blocked); - ++ruleCount; - } catch(std::exception&) {} - } - } - else if (version==3) { - qDebug ("p2b version 3"); - unsigned int namecount; - if (!stream.readRawData((char*)&namecount, sizeof(namecount))) { - std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl; - return ruleCount; - } - namecount=ntohl(namecount); - // Reading names although, we don't really care about them - for (unsigned int i=0; iget_ip_filter(); - foreach (const QString &ip, IPs) { - qDebug("Manual ban of peer %s", ip.toLocal8Bit().constData()); - boost::system::error_code ec; - libtorrent::address addr = libtorrent::address::from_string(ip.toLocal8Bit().constData(), ec); - Q_ASSERT(!ec); - if (!ec) - filter.add_rule(addr, addr, libtorrent::ip_filter::blocked); - } - s->set_ip_filter(filter); -} - -QString FilterParserThread::cleanupIPAddress(QString _ip) { - QHostAddress ip(_ip.trimmed()); - if (ip.isNull()) { - return QString(); - } - return ip.toString(); -} - -void FilterParserThread::run() { - qDebug("Processing filter file"); - libtorrent::ip_filter filter = s->get_ip_filter(); - int ruleCount = 0; - if (filePath.endsWith(".p2p", Qt::CaseInsensitive)) { - // PeerGuardian p2p file - ruleCount = parseP2PFilterFile(filePath, filter); - } else { - if (filePath.endsWith(".p2b", Qt::CaseInsensitive)) { - // PeerGuardian p2b file - ruleCount = parseP2BFilterFile(filePath, filter); - } else { - // Default: eMule DAT format - ruleCount = parseDATFilterFile(filePath, filter); - } - } - if (abort) - return; - try { - s->set_ip_filter(filter); - emit IPFilterParsed(ruleCount); - } catch(std::exception&) { - emit IPFilterError(); - } - qDebug("IP Filter thread: finished parsing, filter applied"); -} diff --git a/src/core/qtlibtorrent/qbtsession.cpp b/src/core/qtlibtorrent/qbtsession.cpp deleted file mode 100644 index 03b077719..000000000 --- a/src/core/qtlibtorrent/qbtsession.cpp +++ /dev/null @@ -1,3162 +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 "core/net/smtp.h" -#include "core/filesystemwatcher.h" -#include "torrentspeedmonitor.h" -#include "torrentstatistics.h" -#include "qbtsession.h" -#include "alertdispatcher.h" -#include "core/misc.h" -#include "core/fs_utils.h" -#include "core/downloadthread.h" -#include "filterparserthread.h" -#include "core/preferences.h" -#include "core/scannedfoldersmodel.h" -#include "core/qtracker.h" -#include "core/logger.h" -#ifndef DISABLE_GUI -#include "shutdownconfirm.h" -#include "geoipmanager.h" -#endif -#include "core/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(); - QList downloadInDirList = pref->getDownloadInScanDirs(); - while(scan_dirs.size() > downloadInDirList.size()) { - downloadInDirList << false; - } - int i = 0; - foreach (const QString &dir, scan_dirs) { - qDebug() << "Adding scan dir" << dir << downloadInDirList.at(i); - m_scanFolders->addPath(dir, downloadInDirList.at(i)); - ++i; - } - qDebug("Session configured"); -} - -void QBtSession::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, const bool force) { - QTorrentHandle h = getTorrentHandle(hash); - if (h.is_paused() || (h.is_forced() != force)) { - h.resume(force); - 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(const QTorrentHandle &h, const QString &magnet_uri) -{ - QStringList trackers; - QStringList urlSeeds; - add_torrent_params p; - boost::system::error_code ec; - - parse_magnet_uri(magnet_uri.toUtf8().constData(), p, ec); - - for (std::vector::const_iterator i = p.trackers.begin(), e = p.trackers.end(); i != e; ++i) - trackers.push_back(misc::toQStringU(*i)); - -#if LIBTORRENT_VERSION_NUM >= 10000 - for (std::vector::const_iterator i = p.url_seeds.begin(), e = p.url_seeds.end(); i != e; ++i) - urlSeeds.push_back(misc::toQStringU(*i)); -#endif - - mergeTorrents_impl(h, trackers, urlSeeds); -} - -void QBtSession::mergeTorrents(const QTorrentHandle &h, const boost::intrusive_ptr t) { - QStringList trackers; - QStringList urlSeeds; - - foreach (const announce_entry& newTracker, t->trackers()) - trackers.append(misc::toQStringU(newTracker.url)); - - foreach (const web_seed_entry& newUrlSeed, t->web_seeds()) - urlSeeds.append(misc::toQStringU(newUrlSeed.url)); - - mergeTorrents_impl(h, trackers, urlSeeds); -} - -void QBtSession::mergeTorrents_impl(const QTorrentHandle &h, const QStringList &trackers, const QStringList &urlSeeds) -{ - if (!h.is_valid()) - return; - - QString hash = h.hash(); - QString name = h.name(); - QStringList addedTrackers; - const std::vector existingTrackers = h.trackers(); - const QStringList existingUrlSeeds = h.url_seeds(); - - foreach (const QString &tracker, trackers) { - QUrl trackerUrl(tracker); - bool found = false; - - foreach (const announce_entry &existingTracker, existingTrackers) { - QUrl existingTrackerUrl(misc::toQStringU(existingTracker.url)); - if (trackerUrl == existingTrackerUrl) { - found = true; - break; - } - } - - if (!found) { - h.add_tracker(announce_entry(tracker.toUtf8().constData())); - addedTrackers.append(tracker); - Logger::instance()->addMessage(tr("Tracker '%1' was added to torrent '%2'").arg(tracker).arg(name)); - } - } - - if (!addedTrackers.empty()) - emit trackersAdded(addedTrackers, hash); - - if (existingTrackers.empty() && !h.trackers().empty()) - emit trackerlessChange(false, hash); - - foreach (const QString &urlSeed, urlSeeds) { - QUrl urlSeedUrl(urlSeed); - bool found = false; - - foreach (const QString &existingUrlSeed, existingUrlSeeds) { - QUrl existingUrlSeedUrl(existingUrlSeed); - if (urlSeedUrl == existingUrlSeedUrl) { - found = true; - break; - } - } - - if (!found) { - h.add_url_seed(urlSeed); - Logger::instance()->addMessage(tr("URL seed '%1' was added to torrent '%2'").arg(urlSeed).arg(name)); - } - } - - h.force_reannounce(); - emit reloadTrackersAndUrlSeeds(h); -} - -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; - } - } -} - -void QBtSession::addTorrentsFromScanFolder(QStringList &pathList) -{ - foreach (const QString &file, pathList) { - qDebug("File %s added", qPrintable(file)); - if (file.endsWith(".magnet")) { - QFile f(file); - if (!f.open(QIODevice::ReadOnly)) { - qDebug("Failed to open magnet file: %s", qPrintable(f.errorString())); - } else { - const QString link = QString::fromLocal8Bit(f.readAll()); - addMagnetUri(link, false, true, file); - f.remove(); - } - continue; - } - try { - std::vector 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 - Net::Smtp *sender = new Net::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()) { - const QString hash = h.hash(); - // 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(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[hash] = trackers_data; - } - else { - emit trackerAuthenticationRequired(h); - } - emit trackerError(hash, misc::toQStringU(p->url)); - } -} - -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 - const QString hash = h.hash(); - QHash trackers_data = trackersInfos.value(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[hash] = trackers_data; - emit trackerSuccess(hash, misc::toQStringU(p->url)); - } -} - -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 - const QString hash = h.hash(); - QHash trackers_data = trackersInfos.value(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[hash] = trackers_data; - qDebug("Received a tracker warning from %s: %s", p->url.c_str(), p->msg.c_str()); - emit trackerWarning(hash, misc::toQStringU(p->url)); - } -} - -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 = 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); -} - - -void QBtSession::addTrackersAndUrlSeeds(const QString &hash, const QStringList &trackers, const QStringList& urlSeeds) -{ - QTorrentHandle h = getTorrentHandle(hash); - if (h.is_valid()) - mergeTorrents_impl(h, trackers, urlSeeds); -} diff --git a/src/core/qtlibtorrent/qbtsession.h b/src/core/qtlibtorrent/qbtsession.h deleted file mode 100644 index 9c9a12fc8..000000000 --- a/src/core/qtlibtorrent/qbtsession.h +++ /dev/null @@ -1,352 +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 - */ -#ifndef __BITTORRENT_H__ -#define __BITTORRENT_H__ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "qtorrenthandle.h" -#include "trackerinfos.h" -#include "core/misc.h" - -#ifndef DISABLE_GUI -#include "rssdownloadrule.h" -#endif - -namespace libtorrent { -struct add_torrent_params; -struct pe_settings; -struct proxy_settings; -class session; -struct session_status; - -class alert; -struct torrent_finished_alert; -struct save_resume_data_alert; -struct file_renamed_alert; -struct torrent_deleted_alert; -struct storage_moved_alert; -struct storage_moved_failed_alert; -struct metadata_received_alert; -struct file_error_alert; -struct file_completed_alert; -struct torrent_paused_alert; -struct tracker_error_alert; -struct tracker_reply_alert; -struct tracker_warning_alert; -struct portmap_error_alert; -struct portmap_alert; -struct peer_blocked_alert; -struct peer_ban_alert; -struct fastresume_rejected_alert; -struct url_seed_alert; -struct listen_succeeded_alert; -struct listen_failed_alert; -struct torrent_checked_alert; -struct external_ip_alert; -struct state_update_alert; -struct stats_alert; - -#if LIBTORRENT_VERSION_NUM < 10000 -class upnp; -class natpmp; -#endif -} - -class DownloadThread; -class FilterParserThread; -class BandwidthScheduler; -class ScanFoldersModel; -class TorrentSpeedMonitor; -class TorrentStatistics; -class QAlertDispatcher; - -enum TorrentExportFolder { - RegularTorrentExportFolder, - FinishedTorrentExportFolder -}; - -class QTracker; - -class QBtSession : public QObject { - Q_OBJECT - Q_DISABLE_COPY(QBtSession) - -public: - static const qreal MAX_RATIO; - -private: - explicit QBtSession(); - static QBtSession* m_instance; - -public: - static QBtSession* instance(); - static void drop(); - ~QBtSession(); - QTorrentHandle getTorrentHandle(const QString &hash) const; - std::vector getTorrents() const; - qreal getPayloadDownloadRate() const; - qreal getPayloadUploadRate() const; - libtorrent::session_status getSessionStatus() const; - int getListenPort() const; - qreal getRealRatio(const libtorrent::torrent_status &status) const; - QHash getTrackersInfo(const QString &hash) const; - bool hasActiveTorrents() const; - bool hasDownloadingTorrents() const; - //int getMaximumActiveDownloads() const; - //int getMaximumActiveTorrents() const; - inline libtorrent::session* getSession() const { return s; } - inline bool useTemporaryFolder() const { return !defaultTempPath.isEmpty(); } - inline QString getDefaultSavePath() const { return defaultSavePath; } - inline ScanFoldersModel* getScanFoldersModel() const { return m_scanFolders; } - inline bool isDHTEnabled() const { return DHTEnabled; } - inline bool isLSDEnabled() const { return LSDEnabled; } - inline bool isPexEnabled() const { return PeXEnabled; } - inline bool isQueueingEnabled() const { return queueingEnabled; } - quint64 getAlltimeDL() const; - quint64 getAlltimeUL() const; - void postTorrentUpdate(); - -public slots: - QTorrentHandle addTorrent(QString path, bool fromScanDir = false, QString from_url = QString(), bool resumed = false, bool imported = false); - QTorrentHandle addMagnetUri(QString magnet_uri, bool resumed=false, bool fromScanDir=false, const QString &filePath=QString()); - void loadSessionState(); - void saveSessionState(); - void downloadFromUrl(const QString &url, const QList& cookies = QList()); - void deleteTorrent(const QString &hash, bool delete_local_files = false); - void startUpTorrents(); - void recheckTorrent(const QString &hash); - void useAlternativeSpeedsLimit(bool alternative); - qlonglong getETA(const QString& hash, const libtorrent::torrent_status &status) const; - /* Needed by Web UI */ - void pauseAllTorrents(); - void pauseTorrent(const QString &hash); - void resumeTorrent(const QString &hash, const bool force = false); - void resumeAllTorrents(); - /* End Web UI */ - void preAllocateAllFiles(bool b); - void saveFastResumeData(); - void enableIPFilter(const QString &filter_path, bool force=false); - void disableIPFilter(); - void setQueueingEnabled(bool enable); - void handleDownloadFailure(QString url, QString reason); - void handleMagnetRedirect(const QString &url_new, const QString &url_old); -#ifndef DISABLE_GUI - void downloadUrlAndSkipDialog(QString url, QString save_path=QString(), QString label=QString(), - const QList& cookies = QList(), - const RssDownloadRule::AddPausedState &aps = RssDownloadRule::USE_GLOBAL); -#else - void downloadUrlAndSkipDialog(QString url, QString save_path=QString(), QString label=QString(), - const QList& cookies = QList()); -#endif - // Session configuration - Setters - void setListeningPort(int port); - void setMaxConnectionsPerTorrent(int max); - void setMaxUploadsPerTorrent(int max); - void setDownloadRateLimit(long rate); - void setUploadRateLimit(long rate); - void setGlobalMaxRatio(qreal ratio); - qreal getGlobalMaxRatio() const { return global_ratio_limit; } - void setMaxRatioPerTorrent(const QString &hash, qreal ratio); - qreal getMaxRatioPerTorrent(const QString &hash, bool *usesGlobalRatio) const; - void removeRatioPerTorrent(const QString &hash); - void setDefaultSavePath(const QString &savepath); - void setDefaultTempPath(const QString &temppath); - void setAppendLabelToSavePath(bool append); - void appendLabelToTorrentSavePath(const QTorrentHandle &h); - void changeLabelInTorrentSavePath(const QTorrentHandle &h, QString old_label, QString new_label); - void appendqBextensionToTorrent(const QTorrentHandle &h, bool append); - void setAppendqBExtension(bool append); - void setDownloadLimit(QString hash, long val); - void setUploadLimit(QString hash, long val); - void enableUPnP(bool b); - void enableLSD(bool b); - void enableDHT(bool b); - void processDownloadedFile(QString, QString); -#ifndef DISABLE_GUI - void addMagnetSkipAddDlg(const QString& uri, const QString& save_path = QString(), const QString& label = QString(), - const RssDownloadRule::AddPausedState &aps = RssDownloadRule::USE_GLOBAL, const QString &uri_old = QString()); -#else - void addMagnetSkipAddDlg(const QString& uri, const QString& save_path = QString(), const QString& label = QString(), const QString &uri_old = QString()); -#endif - void addMagnetInteractive(const QString& uri); - void downloadFromURLList(const QStringList& urls); - void banIP(QString ip); - void recursiveTorrentDownload(const QTorrentHandle &h); - void unhideMagnet(const QString &hash); - void addTrackersAndUrlSeeds(const QString &hash, const QStringList &trackers, const QStringList& urlSeeds); - -private: - void applyEncryptionSettings(libtorrent::pe_settings se); - void setProxySettings(libtorrent::proxy_settings proxySettings); - void setSessionSettings(const libtorrent::session_settings &sessionSettings); - QString getSavePath(const QString &hash, bool fromScanDir = false, QString filePath = QString::null, bool imported = false); - bool loadFastResumeData(const QString &hash, std::vector &buf); - void loadTorrentSettings(QTorrentHandle &h); - void loadTorrentTempData(QTorrentHandle &h, QString savePath, bool magnet); - void initializeAddTorrentParams(const QString &hash, libtorrent::add_torrent_params &p); - void updateRatioTimer(); - void recoverPersistentData(const QString &hash, const std::vector &buf); - void backupPersistentData(const QString &hash, boost::shared_ptr data); - void handleAlert(libtorrent::alert* a); - void handleTorrentFinishedAlert(libtorrent::torrent_finished_alert* p); - void handleSaveResumeDataAlert(libtorrent::save_resume_data_alert* p); - void handleFileRenamedAlert(libtorrent::file_renamed_alert* p); - void handleTorrentDeletedAlert(libtorrent::torrent_deleted_alert* p); - void handleStorageMovedAlert(libtorrent::storage_moved_alert* p); - void handleStorageMovedFailedAlert(libtorrent::storage_moved_failed_alert* p); - void handleMetadataReceivedAlert(libtorrent::metadata_received_alert* p); - void handleFileErrorAlert(libtorrent::file_error_alert* p); - void handleFileCompletedAlert(libtorrent::file_completed_alert* p); - void handleTorrentPausedAlert(libtorrent::torrent_paused_alert* p); - void handleTrackerErrorAlert(libtorrent::tracker_error_alert* p); - void handleTrackerReplyAlert(libtorrent::tracker_reply_alert* p); - void handleTrackerWarningAlert(libtorrent::tracker_warning_alert* p); - void handlePortmapWarningAlert(libtorrent::portmap_error_alert* p); - void handlePortmapAlert(libtorrent::portmap_alert* p); - void handlePeerBlockedAlert(libtorrent::peer_blocked_alert* p); - void handlePeerBanAlert(libtorrent::peer_ban_alert* p); - void handleFastResumeRejectedAlert(libtorrent::fastresume_rejected_alert* p); - void handleUrlSeedAlert(libtorrent::url_seed_alert* p); - void handleListenSucceededAlert(libtorrent::listen_succeeded_alert *p); - void handleListenFailedAlert(libtorrent::listen_failed_alert *p); - void handleTorrentCheckedAlert(libtorrent::torrent_checked_alert* p); - void handleExternalIPAlert(libtorrent::external_ip_alert *p); - void handleStateUpdateAlert(libtorrent::state_update_alert *p); - void handleStatsAlert(libtorrent::stats_alert *p); - -private slots: - void addTorrentsFromScanFolder(QStringList&); - void readAlerts(); - void processBigRatios(); - void exportTorrentFiles(QString path); - void saveTempFastResumeData(); - void sendNotificationEmail(const QTorrentHandle &h); - void autoRunExternalProgram(const QTorrentHandle &h); - void mergeTorrents(const QTorrentHandle &h, const boost::intrusive_ptr t); - void mergeTorrents(const QTorrentHandle &h, const QString &magnet_uri); - void mergeTorrents_impl(const QTorrentHandle &h, const QStringList &trackers, const QStringList& urlSeeds); - void exportTorrentFile(const QTorrentHandle &h, TorrentExportFolder folder = RegularTorrentExportFolder); - void handleIPFilterParsed(int ruleCount); - void handleIPFilterError(); - void configureSession(); - -signals: - void addedTorrent(const QTorrentHandle& h); - void torrentAboutToBeRemoved(const QTorrentHandle &h); - void pausedTorrent(const QTorrentHandle& h); - void resumedTorrent(const QTorrentHandle& h); - void finishedTorrent(const QTorrentHandle& h); - void fullDiskError(const QTorrentHandle& h, QString msg); - void trackerSuccess(const QString &hash, const QString &tracker); - void trackerError(const QString &hash, const QString &tracker); - void trackerWarning(const QString &hash, const QString &tracker); - void trackerAuthenticationRequired(const QTorrentHandle& h); - void newDownloadedTorrent(QString path, QString url); - void newDownloadedTorrentFromRss(QString url); - void newMagnetLink(const QString& link); - void updateFileSize(const QString &hash); - void downloadFromUrlFailure(QString url, QString reason); - void torrentFinishedChecking(const QTorrentHandle& h); - void metadataReceived(const QTorrentHandle &h); - void savePathChanged(const QTorrentHandle &h); - void alternativeSpeedsModeChanged(bool alternative); - void recursiveTorrentDownloadPossible(const QTorrentHandle &h); - void ipFilterParsed(bool error, int ruleCount); - void metadataReceivedHidden(const QTorrentHandle &h); - void stateUpdate(const std::vector &statuses); - void statsReceived(const libtorrent::stats_alert&); - void trackersAdded(const QStringList &trackers, const QString &hash); - void trackerlessChange(bool trackerless, const QString &hash); - void reloadTrackersAndUrlSeeds(const QTorrentHandle &h); - -private: - // Bittorrent - libtorrent::session *s; - QPointer bd_scheduler; - QMap > savepathLabel_fromurl; // Use QMap for compatibility with Qt < 4.7: qHash(QUrl) -#ifndef DISABLE_GUI - QMap addpaused_fromurl; -#endif - QHash > trackersInfos; - QHash savePathsToRemove; - QStringList torrentsToPausedAfterChecking; - QTimer resumeDataTimer; - // Ratio - QPointer BigRatioTimer; - // HTTP - DownloadThread* downloader; - // File System - ScanFoldersModel *m_scanFolders; - // Settings - bool preAllocateAll; - qreal global_ratio_limit; - int high_ratio_action; - bool LSDEnabled; - bool DHTEnabled; - bool PeXEnabled; - bool queueingEnabled; - bool appendLabelToSavePath; - bool m_torrentExportEnabled; - bool m_finishedTorrentExportEnabled; - bool appendqBExtension; - QString defaultSavePath; - QString defaultTempPath; - // IP filtering - QPointer filterParser; - QString filterPath; - QList url_skippingDlg; - // GeoIP -#ifndef DISABLE_GUI - bool geoipDBLoaded; - bool resolve_countries; -#endif - // Tracker - QPointer m_tracker; - TorrentSpeedMonitor *m_speedMonitor; - shutDownAction m_shutdownAct; - // Port forwarding -#if LIBTORRENT_VERSION_NUM < 10000 - libtorrent::upnp *m_upnp; - libtorrent::natpmp *m_natpmp; -#endif - QAlertDispatcher* m_alertDispatcher; - TorrentStatistics* m_torrentStatistics; -}; - -#endif diff --git a/src/core/qtlibtorrent/qtlibtorrent.pri b/src/core/qtlibtorrent/qtlibtorrent.pri deleted file mode 100644 index 83cffd84b..000000000 --- a/src/core/qtlibtorrent/qtlibtorrent.pri +++ /dev/null @@ -1,17 +0,0 @@ -INCLUDEPATH += $$PWD - -HEADERS += $$PWD/qbtsession.h \ - $$PWD/qtorrenthandle.h \ - $$PWD/bandwidthscheduler.h \ - $$PWD/trackerinfos.h \ - $$PWD/torrentspeedmonitor.h \ - $$PWD/filterparserthread.h \ - $$PWD/alertdispatcher.h \ - $$PWD/torrentstatistics.h - -SOURCES += $$PWD/qbtsession.cpp \ - $$PWD/qtorrenthandle.cpp \ - $$PWD/torrentspeedmonitor.cpp \ - $$PWD/alertdispatcher.cpp \ - $$PWD/torrentstatistics.cpp \ - $$PWD/filterparserthread.cpp diff --git a/src/core/qtlibtorrent/qtorrenthandle.cpp b/src/core/qtlibtorrent/qtorrenthandle.cpp deleted file mode 100644 index 302118223..000000000 --- a/src/core/qtlibtorrent/qtorrenthandle.cpp +++ /dev/null @@ -1,856 +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 "core/fs_utils.h" -#include "core/misc.h" -#include "core/preferences.h" -#include "qtorrenthandle.h" -#include "core/torrentpersistentdata.h" -#include "qbtsession.h" -#include -#include -#include -#include -#include - -#ifdef Q_OS_WIN -#include -#endif - -using namespace libtorrent; -using namespace std; - -static QPair get_file_extremity_pieces(const torrent_info& t, int file_index) -{ - const int num_pieces = t.num_pieces(); - const int piece_size = t.piece_length(); - const file_entry& file = t.file_at(file_index); - - // Determine the first and last piece of the file - int first_piece = floor((file.offset + 1) / (float) piece_size); - Q_ASSERT(first_piece >= 0 && first_piece < num_pieces); - - int num_pieces_in_file = ceil(file.size / (float) piece_size); - int last_piece = first_piece + num_pieces_in_file - 1; - Q_ASSERT(last_piece >= 0 && last_piece < num_pieces); - - return qMakePair(first_piece, last_piece); -} - -QTorrentHandle::QTorrentHandle(const torrent_handle& h): torrent_handle(h) -{ -} - -// -// Getters -// - -QString QTorrentHandle::hash() const -{ - return misc::toQString(torrent_handle::info_hash()); -} - -QString QTorrentHandle::name() const -{ - QString name = TorrentPersistentData::instance()->getName(hash()); - if (name.isEmpty()) { -#if LIBTORRENT_VERSION_NUM < 10000 - name = misc::toQStringU(torrent_handle::name()); -#else - name = misc::toQStringU(status(query_name).name); -#endif - } - return name; -} - -QString QTorrentHandle::creation_date() const -{ -#if LIBTORRENT_VERSION_NUM < 10000 - boost::optional t = torrent_handle::get_torrent_info().creation_date(); -#else - boost::optional t = torrent_handle::torrent_file()->creation_date(); -#endif - return t ? misc::toQString(*t) : ""; -} - -qlonglong QTorrentHandle::creation_date_unix() const -{ -#if LIBTORRENT_VERSION_NUM < 10000 - boost::optional t = torrent_handle::get_torrent_info().creation_date(); -#else - boost::optional t = torrent_handle::torrent_file()->creation_date(); -#endif - return t ? *t : -1; -} - -QString QTorrentHandle::current_tracker() const -{ - return misc::toQString(status(0x0).current_tracker); -} - -bool QTorrentHandle::is_paused() const -{ - return is_paused(status(0x0)); -} - -bool QTorrentHandle::is_queued() const -{ - return is_queued(status(0x0)); -} - -size_type QTorrentHandle::total_size() const -{ -#if LIBTORRENT_VERSION_NUM < 10000 - return torrent_handle::get_torrent_info().total_size(); -#else - return torrent_handle::torrent_file()->total_size(); -#endif -} - -size_type QTorrentHandle::piece_length() const -{ -#if LIBTORRENT_VERSION_NUM < 10000 - return torrent_handle::get_torrent_info().piece_length(); -#else - return torrent_handle::torrent_file()->piece_length(); -#endif -} - -int QTorrentHandle::num_pieces() const -{ -#if LIBTORRENT_VERSION_NUM < 10000 - return torrent_handle::get_torrent_info().num_pieces(); -#else - return torrent_handle::torrent_file()->num_pieces(); -#endif -} - -bool QTorrentHandle::first_last_piece_first() const -{ -#if LIBTORRENT_VERSION_NUM < 10000 - torrent_info const* t = &get_torrent_info(); -#else - boost::intrusive_ptr t = torrent_file(); -#endif - - // Get int first media file - int index = 0; - for (index = 0; index < t->num_files(); ++index) { - QString path = misc::toQStringU(t->file_at(index).path); - const QString ext = fsutils::fileExtension(path); - if (misc::isPreviewable(ext) && torrent_handle::file_priority(index) > 0) - break; - } - - if (index >= t->num_files()) // No media file - return false; - - QPair extremities = get_file_extremity_pieces(*t, index); - - return (torrent_handle::piece_priority(extremities.first) == 7) - && (torrent_handle::piece_priority(extremities.second) == 7); -} - -QString QTorrentHandle::save_path() const -{ -#if LIBTORRENT_VERSION_NUM < 10000 - return fsutils::fromNativePath(misc::toQStringU(torrent_handle::save_path())); -#else - return fsutils::fromNativePath(misc::toQStringU(status(torrent_handle::query_save_path).save_path)); -#endif -} - -QString QTorrentHandle::save_path_parsed() const -{ - QString p; - if (has_metadata() && num_files() == 1) { - p = firstFileSavePath(); - } - else { - p = fsutils::fromNativePath(TorrentPersistentData::instance()->getSavePath(hash())); - if (p.isEmpty()) - p = save_path(); - } - return p; -} - -QStringList QTorrentHandle::url_seeds() const -{ - QStringList res; - try { - const std::set existing_seeds = torrent_handle::url_seeds(); - - std::set::const_iterator it = existing_seeds.begin(); - std::set::const_iterator itend = existing_seeds.end(); - for (; it != itend; ++it) { - qDebug("URL Seed: %s", it->c_str()); - res << misc::toQString(*it); - } - } catch(std::exception &e) { - std::cout << "ERROR: Failed to convert the URL seed" << std::endl; - } - return res; -} - -// get the size of the torrent without the filtered files -size_type QTorrentHandle::actual_size() const -{ - return status(query_accurate_download_counters).total_wanted; -} - -bool QTorrentHandle::has_filtered_pieces() const -{ - const std::vector piece_priorities = torrent_handle::piece_priorities(); - foreach (const int priority, piece_priorities) - if (priority == 0) - return true; - return false; -} - -int QTorrentHandle::num_files() const -{ - if (!has_metadata()) - return -1; -#if LIBTORRENT_VERSION_NUM < 10000 - return torrent_handle::get_torrent_info().num_files(); -#else - return torrent_handle::torrent_file()->num_files(); -#endif -} - -QString QTorrentHandle::filename_at(unsigned int index) const -{ -#if LIBTORRENT_VERSION_NUM < 10000 - Q_ASSERT(index < (unsigned int)torrent_handle::get_torrent_info().num_files()); -#else - Q_ASSERT(index < (unsigned int)torrent_handle::torrent_file()->num_files()); -#endif - return fsutils::fileName(filepath_at(index)); -} - -size_type QTorrentHandle::filesize_at(unsigned int index) const -{ -#if LIBTORRENT_VERSION_NUM < 10000 - Q_ASSERT(index < (unsigned int)torrent_handle::get_torrent_info().num_files()); - return torrent_handle::get_torrent_info().files().file_size(index); -#else - Q_ASSERT(index < (unsigned int)torrent_handle::torrent_file()->num_files()); - return torrent_handle::torrent_file()->files().file_size(index); -#endif -} - -QString QTorrentHandle::filepath_at(unsigned int index) const -{ -#if LIBTORRENT_VERSION_NUM < 10000 - return filepath_at(torrent_handle::get_torrent_info(), index); -#else - return filepath_at(*torrent_handle::torrent_file(), index); -#endif -} - -QString QTorrentHandle::orig_filepath_at(unsigned int index) const -{ -#if LIBTORRENT_VERSION_NUM < 10000 - return fsutils::fromNativePath(misc::toQStringU(torrent_handle::get_torrent_info().orig_files().file_path(index))); -#else - return fsutils::fromNativePath(misc::toQStringU(torrent_handle::torrent_file()->orig_files().file_path(index))); -#endif - -} - -torrent_status::state_t QTorrentHandle::state() const -{ - return status(0x0).state; -} - -QString QTorrentHandle::creator() const -{ -#if LIBTORRENT_VERSION_NUM < 10000 - return misc::toQStringU(torrent_handle::get_torrent_info().creator()); -#else - return misc::toQStringU(torrent_handle::torrent_file()->creator()); -#endif -} - -QString QTorrentHandle::comment() const -{ -#if LIBTORRENT_VERSION_NUM < 10000 - return misc::toQStringU(torrent_handle::get_torrent_info().comment()); -#else - return misc::toQStringU(torrent_handle::torrent_file()->comment()); -#endif -} - -bool QTorrentHandle::is_checking() const -{ - return is_checking(status(0x0)); -} - -// Return a list of absolute paths corresponding -// to all files in a torrent -QStringList QTorrentHandle::absolute_files_path() const -{ - QDir saveDir(save_path()); - QStringList res; - for (int i = 0; i fp = torrent_handle::file_priorities(); - for (uint i = 0; i < fp.size(); ++i) { - if (fp[i] == 0) { - const QString file_path = fsutils::expandPathAbs(saveDir.absoluteFilePath(filepath_at(i))); - if (file_path.contains(".unwanted")) - res << file_path; - } - } - return res; -} - -bool QTorrentHandle::has_missing_files() const -{ - const QStringList paths = absolute_files_path(); - foreach (const QString &path, paths) - if (!QFile::exists(path)) return true; - return false; -} - -int QTorrentHandle::queue_position() const -{ - return queue_position(status(0x0)); -} - -bool QTorrentHandle::is_seed() const -{ - // Affected by bug http://code.rasterbar.com/libtorrent/ticket/402 - //return torrent_handle::is_seed(); - // May suffer from approximation problems - //return (progress() == 1.); - // This looks safe - return is_seed(status(0x0)); -} - -bool QTorrentHandle::is_sequential_download() const -{ - return status(0x0).sequential_download; -} - -bool QTorrentHandle::priv() const -{ - if (!has_metadata()) - return false; -#if LIBTORRENT_VERSION_NUM < 10000 - return torrent_handle::get_torrent_info().priv(); -#else - return torrent_handle::torrent_file()->priv(); -#endif -} - -QString QTorrentHandle::firstFileSavePath() const -{ - Q_ASSERT(has_metadata()); - QString fsave_path = fsutils::fromNativePath(TorrentPersistentData::instance()->getSavePath(hash())); - if (fsave_path.isEmpty()) - fsave_path = save_path(); - if (!fsave_path.endsWith("/")) - fsave_path += "/"; - fsave_path += filepath_at(0); - // Remove .!qB extension - if (fsave_path.endsWith(".!qB", Qt::CaseInsensitive)) - fsave_path.chop(4); - return fsave_path; -} - -QString QTorrentHandle::root_path() const -{ - if (num_files() < 2) - return save_path(); - QString first_filepath = filepath_at(0); - const int slashIndex = first_filepath.indexOf("/"); - if (slashIndex >= 0) - return QDir(save_path()).absoluteFilePath(first_filepath.left(slashIndex)); - return save_path(); -} - -bool QTorrentHandle::has_error() const -{ - return has_error(status(0x0)); -} - -QString QTorrentHandle::error() const -{ - return misc::toQString(status(0x0).error); -} - -void QTorrentHandle::downloading_pieces(bitfield &bf) const -{ - std::vector queue; - torrent_handle::get_download_queue(queue); - - std::vector::const_iterator it = queue.begin(); - std::vector::const_iterator itend = queue.end(); - for (; it!= itend; ++it) - bf.set_bit(it->piece_index); - return; -} - -bool QTorrentHandle::has_metadata() const -{ - return status(0x0).has_metadata; -} - -void QTorrentHandle::file_progress(std::vector& fp) const -{ - torrent_handle::file_progress(fp, torrent_handle::piece_granularity); -} - -QTorrentState QTorrentHandle::torrentState() const -{ - QTorrentState state = QTorrentState::Unknown; - libtorrent::torrent_status s = status(torrent_handle::query_accurate_download_counters); - - if (is_paused(s)) { - if (has_error(s)) - state = QTorrentState::Error; - else - state = is_seed(s) ? QTorrentState::PausedUploading : QTorrentState::PausedDownloading; - } - else { - if (QBtSession::instance()->isQueueingEnabled() && is_queued(s)) { - state = is_seed(s) ? QTorrentState::QueuedUploading : QTorrentState::QueuedDownloading; - } - else { - switch (s.state) { - case torrent_status::finished: - case torrent_status::seeding: - state = s.upload_payload_rate > 0 ? QTorrentState::Uploading : QTorrentState::StalledUploading; - break; - case torrent_status::allocating: - case torrent_status::checking_files: - case torrent_status::queued_for_checking: - case torrent_status::checking_resume_data: - state = is_seed(s) ? QTorrentState::CheckingUploading : QTorrentState::CheckingDownloading; - break; - case torrent_status::downloading: - case torrent_status::downloading_metadata: - state = s.download_payload_rate > 0 ? QTorrentState::Downloading : QTorrentState::StalledDownloading; - break; - default: - qWarning("Unrecognized torrent status, should not happen!!! status was %d", this->state()); - } - } - } - - return state; -} - -qulonglong QTorrentHandle::eta() const -{ - libtorrent::torrent_status s = status(torrent_handle::query_accurate_download_counters); - return QBtSession::instance()->getETA(hash(), s); -} - -void QTorrentHandle::toggleSequentialDownload() -{ - if (is_valid() && has_metadata()) { - bool was_sequential = is_sequential_download(); - set_sequential_download(!was_sequential); - if (!was_sequential) - prioritize_first_last_piece(true); - } -} - -void QTorrentHandle::toggleFirstLastPiecePrio() -{ - if (is_valid() && has_metadata()) - prioritize_first_last_piece(!first_last_piece_first()); -} - -bool QTorrentHandle::is_forced() const -{ - return is_forced(status(0x0)); -} - -// -// Setters -// - -void QTorrentHandle::pause() const -{ - torrent_handle::auto_managed(false); - torrent_handle::pause(); - if (!TorrentPersistentData::instance()->getHasMissingFiles(this->hash())) - torrent_handle::save_resume_data(); -} - -void QTorrentHandle::resume(const bool force) const -{ - if (has_error()) - torrent_handle::clear_error(); - torrent_handle::set_upload_mode(false); - - const QString torrent_hash = hash(); - TorrentPersistentData* const TorPersistent = TorrentPersistentData::instance(); - bool has_persistant_error = TorPersistent->hasError(torrent_hash); - TorPersistent->setErrorState(torrent_hash, false); - bool temp_path_enabled = Preferences::instance()->isTempPathEnabled(); - if (has_persistant_error && temp_path_enabled) { - // Torrent was supposed to be seeding, checking again in final destination - qDebug("Resuming a torrent with error..."); - const QString final_save_path = TorPersistent->getSavePath(torrent_hash); - qDebug("Torrent final path is: %s", qPrintable(final_save_path)); - if (!final_save_path.isEmpty()) - move_storage(final_save_path); - } - torrent_handle::auto_managed(!force); - torrent_handle::resume(); - if (has_persistant_error && temp_path_enabled) - // Force recheck - torrent_handle::force_recheck(); -} - -void QTorrentHandle::remove_url_seed(const QString& seed) const -{ - torrent_handle::remove_url_seed(seed.toStdString()); -} - -void QTorrentHandle::add_url_seed(const QString& seed) const -{ - const std::string str_seed = seed.toStdString(); - qDebug("calling torrent_handle::add_url_seed(%s)", str_seed.c_str()); - torrent_handle::add_url_seed(str_seed); -} - -void QTorrentHandle::set_tracker_login(const QString& username, const QString& password) const -{ - torrent_handle::set_tracker_login(std::string(username.toLocal8Bit().constData()), std::string(password.toLocal8Bit().constData())); -} - -void QTorrentHandle::move_storage(const QString& new_path) const -{ - QString hashstr = hash(); - - if (TorrentTempData::isMoveInProgress(hashstr)) { - qDebug("enqueue move storage to %s", qPrintable(new_path)); - TorrentTempData::enqueueMove(hashstr, new_path); - } - else { - QString old_path = save_path(); - - qDebug("move storage: %s to %s", qPrintable(old_path), qPrintable(new_path)); - - if (QDir(old_path) == QDir(new_path)) - return; - - TorrentTempData::startMove(hashstr, old_path, new_path); - - // Create destination directory if necessary - // or move_storage() will fail... - QDir().mkpath(new_path); - // Actually move the storage - torrent_handle::move_storage(fsutils::toNativePath(new_path).toUtf8().constData()); - } -} - -bool QTorrentHandle::save_torrent_file(const QString& path) const -{ - if (!has_metadata()) return false; - -#if LIBTORRENT_VERSION_NUM < 10000 - torrent_info const* t = &get_torrent_info(); -#else - boost::intrusive_ptr t = torrent_file(); -#endif - - entry meta = bdecode(t->metadata().get(), - t->metadata().get() + t->metadata_size()); - entry torrent_entry(entry::dictionary_t); - torrent_entry["info"] = meta; - if (!torrent_handle::trackers().empty()) - torrent_entry["announce"] = torrent_handle::trackers().front().url; - - vector out; - bencode(back_inserter(out), torrent_entry); - QFile torrent_file(path); - if (!out.empty() && torrent_file.open(QIODevice::WriteOnly)) { - torrent_file.write(&out[0], out.size()); - torrent_file.close(); - return true; - } - - return false; -} - -void QTorrentHandle::file_priority(int index, int priority) const -{ - vector priorities = torrent_handle::file_priorities(); - if (priorities[index] != priority) { - priorities[index] = priority; - prioritize_files(priorities); - } -} - -void QTorrentHandle::prioritize_files(const vector &files) const -{ -#if LIBTORRENT_VERSION_NUM < 10000 - torrent_info const& info = torrent_handle::get_torrent_info(); -#else - boost::intrusive_ptr info_ptr = torrent_handle::torrent_file(); - torrent_info const& info = *info_ptr; -#endif - if ((int)files.size() != info.num_files()) return; - qDebug() << Q_FUNC_INFO; - bool was_seed = is_seed(); - qDebug() << Q_FUNC_INFO << "Changing files priorities..."; - torrent_handle::prioritize_files(files); - qDebug() << Q_FUNC_INFO << "Moving unwanted files to .unwanted folder and conversely..."; - - QString spath = save_path(); - - for (uint i = 0; i < files.size(); ++i) { - QString filepath = filepath_at(info, i); - // Move unwanted files to a .unwanted subfolder - if (files[i] == 0) { - QString old_abspath = QDir(spath).absoluteFilePath(filepath); - QString parent_abspath = fsutils::branchPath(old_abspath); - // Make sure the file does not already exists - if (QDir(parent_abspath).dirName() != ".unwanted") { - QString unwanted_abspath = parent_abspath + "/.unwanted"; - QString new_abspath = unwanted_abspath + "/" + fsutils::fileName(filepath); - qDebug() << "Unwanted path is" << unwanted_abspath; - if (QFile::exists(new_abspath)) { - qWarning() << "File" << new_abspath << "already exists at destination."; - continue; - } - bool created = QDir().mkpath(unwanted_abspath); -#ifdef Q_OS_WIN - qDebug() << "unwanted folder was created:" << created; - if (created) { - // Hide the folder on Windows - qDebug() << "Hiding folder (Windows)"; - wstring win_path = fsutils::toNativePath(unwanted_abspath).toStdWString(); - DWORD dwAttrs = GetFileAttributesW(win_path.c_str()); - bool ret = SetFileAttributesW(win_path.c_str(), dwAttrs | FILE_ATTRIBUTE_HIDDEN); - Q_ASSERT(ret != 0); Q_UNUSED(ret); - } -#else - Q_UNUSED(created); -#endif - QString parent_path = fsutils::branchPath(filepath); - if (!parent_path.isEmpty() && !parent_path.endsWith("/")) - parent_path += "/"; - rename_file(i, parent_path + ".unwanted/" + fsutils::fileName(filepath)); - } - } - // Move wanted files back to their original folder - if (files[i] > 0) { - QString parent_relpath = fsutils::branchPath(filepath); - if (QDir(parent_relpath).dirName() == ".unwanted") { - QString old_name = fsutils::fileName(filepath); - QString new_relpath = fsutils::branchPath(parent_relpath); - if (new_relpath.isEmpty()) - rename_file(i, old_name); - else - rename_file(i, QDir(new_relpath).filePath(old_name)); - // Remove .unwanted directory if empty - qDebug() << "Attempting to remove .unwanted folder at " << QDir(spath + "/" + new_relpath).absoluteFilePath(".unwanted"); - QDir(spath + "/" + new_relpath).rmdir(".unwanted"); - } - } - } - - if (was_seed && !is_seed()) { - qDebug() << "Torrent is no longer SEEDING"; - // Save seed status - TorrentPersistentData::instance()->saveSeedStatus(*this); - // Move to temp folder if necessary - const Preferences* const pref = Preferences::instance(); - if (pref->isTempPathEnabled()) { - QString tmp_path = pref->getTempPath(); - qDebug() << "tmp folder is enabled, move torrent to " << tmp_path << " from " << spath; - move_storage(tmp_path); - } - } -} - -void QTorrentHandle::prioritize_first_last_piece(int file_index, bool b) const -{ - // Determine the priority to set - int prio = b ? 7 : torrent_handle::file_priority(file_index); - -#if LIBTORRENT_VERSION_NUM < 10000 - torrent_info const* tf = &get_torrent_info(); -#else - boost::intrusive_ptr tf = torrent_file(); -#endif - - QPair extremities = get_file_extremity_pieces(*tf, file_index); - piece_priority(extremities.first, prio); - piece_priority(extremities.second, prio); -} - -void QTorrentHandle::prioritize_first_last_piece(bool b) const -{ - if (!has_metadata()) return; - // Download first and last pieces first for all media files in the torrent - const uint nbfiles = num_files(); - for (uint index = 0; index < nbfiles; ++index) { - const QString path = filepath_at(index); - const QString ext = fsutils::fileExtension(path); - if (misc::isPreviewable(ext) && torrent_handle::file_priority(index) > 0) { - qDebug() << "File" << path << "is previewable, toggle downloading of first/last pieces first"; - prioritize_first_last_piece(index, b); - } - } -} - -void QTorrentHandle::rename_file(int index, const QString& name) const -{ - qDebug() << Q_FUNC_INFO << index << name; - torrent_handle::rename_file(index, std::string(fsutils::toNativePath(name).toUtf8().constData())); -} - -// -// Operators -// - -bool QTorrentHandle::operator ==(const QTorrentHandle& new_h) const -{ - return info_hash() == new_h.info_hash(); -} - -bool QTorrentHandle::is_paused(const libtorrent::torrent_status &status) -{ - return status.paused && !status.auto_managed; -} - -int QTorrentHandle::queue_position(const libtorrent::torrent_status &status) -{ - if (status.queue_position < 0) - return -1; - return status.queue_position + 1; -} - -bool QTorrentHandle::is_queued(const libtorrent::torrent_status &status) -{ - return status.paused && status.auto_managed; -} - -bool QTorrentHandle::is_seed(const libtorrent::torrent_status &status) -{ - return status.state == torrent_status::finished - || status.state == torrent_status::seeding; -} - -bool QTorrentHandle::is_checking(const libtorrent::torrent_status &status) -{ - return status.state == torrent_status::checking_files - || status.state == torrent_status::checking_resume_data; -} - -bool QTorrentHandle::has_error(const libtorrent::torrent_status &status) -{ - return status.paused && !status.error.empty(); -} - -float QTorrentHandle::progress(const libtorrent::torrent_status &status) -{ - if (!status.total_wanted) - return 0.; - if (status.total_wanted_done == status.total_wanted) - return 1.; - float progress = (float) status.total_wanted_done / (float) status.total_wanted; - Q_ASSERT(progress >= 0.f && progress <= 1.f); - return progress; -} - -QString QTorrentHandle::filepath_at(const libtorrent::torrent_info &info, unsigned int index) -{ - return fsutils::fromNativePath(misc::toQStringU(info.files().file_path(index))); - -} - -bool QTorrentHandle::is_forced(const libtorrent::torrent_status &status) -{ - return !status.paused && !status.auto_managed; -} - - -QTorrentState::QTorrentState(int value) - : m_value(value) -{ -} - -QString QTorrentState::toString() const -{ - switch (m_value) { - case Error: - return "error"; - case Uploading: - return "uploading"; - case PausedUploading: - return "pausedUP"; - case QueuedUploading: - return "queuedUP"; - case StalledUploading: - return "stalledUP"; - case CheckingUploading: - return "checkingUP"; - case Downloading: - return "downloading"; - case PausedDownloading: - return "pausedDL"; - case QueuedDownloading: - return "queuedDL"; - case StalledDownloading: - return "stalledDL"; - case CheckingDownloading: - return "checkingDL"; - default: - return "unknown"; - } -} - -QTorrentState::operator int() const -{ - return m_value; -} diff --git a/src/core/qtlibtorrent/qtorrenthandle.h b/src/core/qtlibtorrent/qtorrenthandle.h deleted file mode 100644 index 51ebee4f9..000000000 --- a/src/core/qtlibtorrent/qtorrenthandle.h +++ /dev/null @@ -1,170 +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 - */ - -#ifndef QTORRENTHANDLE_H -#define QTORRENTHANDLE_H - -#include - -#include - -QT_BEGIN_NAMESPACE -class QStringList; -QT_END_NAMESPACE - -class QTorrentState -{ -public: - enum - { - Unknown = -1, - - Error, - - Uploading, - PausedUploading, - QueuedUploading, - StalledUploading, - CheckingUploading, - - Downloading, - PausedDownloading, - QueuedDownloading, - StalledDownloading, - CheckingDownloading - }; - - QTorrentState(int value); - - operator int() const; - QString toString() const; - -private: - int m_value; -}; - -// A wrapper for torrent_handle in libtorrent -// to interact well with Qt types -class QTorrentHandle: public libtorrent::torrent_handle -{ - -public: - - // - // Constructors - // - - QTorrentHandle() {} - explicit QTorrentHandle(const libtorrent::torrent_handle& h); - - // - // Getters - // - QString hash() const; - QString name() const; - QString current_tracker() const; - bool is_paused() const; - bool has_filtered_pieces() const; - libtorrent::size_type total_size() const; - libtorrent::size_type piece_length() const; - int num_pieces() const; - QString save_path() const; - QString save_path_parsed() const; - QStringList url_seeds() const; - libtorrent::size_type actual_size() const; - int num_files() const; - int queue_position() const; - bool is_queued() const; - QString filename_at(unsigned int index) const; - libtorrent::size_type filesize_at(unsigned int index) const; - QString filepath_at(unsigned int index) const; - QString orig_filepath_at(unsigned int index) const; - libtorrent::torrent_status::state_t state() const; - QString creator() const; - QString comment() const; - QStringList absolute_files_path() const; - QStringList absolute_files_path_uneeded() const; - bool has_missing_files() const; - bool is_seed() const; - bool is_checking() const; - bool is_sequential_download() const; - QString creation_date() const; - qlonglong creation_date_unix() const; - bool priv() const; - bool first_last_piece_first() const; - QString root_path() const; - QString firstFileSavePath() const; - bool has_error() const; - QString error() const; - void downloading_pieces(libtorrent::bitfield& bf) const; - bool has_metadata() const; - void file_progress(std::vector& fp) const; - QTorrentState torrentState() const; - qulonglong eta() const; - void toggleSequentialDownload(); - void toggleFirstLastPiecePrio(); - bool is_forced() const; - - // - // Setters - // - void pause() const; - void resume(const bool force = false) const; - void remove_url_seed(const QString& seed) const; - void add_url_seed(const QString& seed) const; - void set_tracker_login(const QString& username, const QString& password) const; - void move_storage(const QString& path) const; - void prioritize_first_last_piece(bool b) const; - void rename_file(int index, const QString& name) const; - bool save_torrent_file(const QString& path) const; - void prioritize_files(const std::vector& files) const; - void file_priority(int index, int priority) const; - - // - // Operators - // - bool operator ==(const QTorrentHandle& new_h) const; - - static bool is_paused(const libtorrent::torrent_status &status); - static int queue_position(const libtorrent::torrent_status &status); - static bool is_queued(const libtorrent::torrent_status &status); - static bool is_seed(const libtorrent::torrent_status &status); - static bool is_checking(const libtorrent::torrent_status &status); - static bool has_error(const libtorrent::torrent_status &status); - static float progress(const libtorrent::torrent_status &status); - static QString filepath_at(const libtorrent::torrent_info &info, unsigned int index); - static bool is_forced(const libtorrent::torrent_status &status); - -private: - void prioritize_first_last_piece(int file_index, bool b) const; - -}; - -#endif diff --git a/src/core/qtracker.h b/src/core/qtracker.h deleted file mode 100644 index 3c1484f7a..000000000 --- a/src/core/qtracker.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2015 Vladimir Golovnev - * Copyright (C) 2010 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 QTRACKER_H -#define QTRACKER_H - -#include -#include -#include "http/types.h" -#include "http/responsebuilder.h" -#include "http/irequesthandler.h" - -struct QPeer -{ - QString ip; - QString peer_id; - int port; - - bool operator!=(const QPeer &other) const; - bool operator==(const QPeer &other) const; - QString qhash() const; - libtorrent::entry toEntry(bool no_peer_id) const; -}; - -struct TrackerAnnounceRequest -{ - QString info_hash; - QString event; - int numwant; - QPeer peer; - // Extensions - bool no_peer_id; -}; - -// static limits -const int MAX_TORRENTS = 100; -const int MAX_PEERS_PER_TORRENT = 1000; -const int ANNOUNCE_INTERVAL = 1800; // 30min - -typedef QHash PeerList; -typedef QHash TorrentList; - -namespace Http { class Server; } - -/* Basic Bittorrent tracker implementation in Qt */ -/* Following http://wiki.theory.org/BitTorrent_Tracker_Protocol */ -class QTracker : public Http::ResponseBuilder, public Http::IRequestHandler -{ - Q_OBJECT - Q_DISABLE_COPY(QTracker) - -public: - explicit QTracker(QObject *parent = 0); - ~QTracker(); - - bool start(); - Http::Response processRequest(const Http::Request &request, const Http::Environment &env); - -private: - void respondToAnnounceRequest(); - void replyWithPeerList(const TrackerAnnounceRequest &annonce_req); - - Http::Server *m_server; - TorrentList m_torrents; - - Http::Request m_request; - Http::Environment m_env; -}; - -#endif // QTRACKER_H diff --git a/src/core/scannedfoldersmodel.cpp b/src/core/scanfoldersmodel.cpp similarity index 76% rename from src/core/scannedfoldersmodel.cpp rename to src/core/scanfoldersmodel.cpp index 261609634..a63071a38 100644 --- a/src/core/scannedfoldersmodel.cpp +++ b/src/core/scanfoldersmodel.cpp @@ -38,7 +38,8 @@ #include "fs_utils.h" #include "preferences.h" #include "filesystemwatcher.h" -#include "scannedfoldersmodel.h" +#include "bittorrent/session.h" +#include "scanfoldersmodel.h" namespace { @@ -67,11 +68,26 @@ public: ScanFoldersModel *ScanFoldersModel::m_instance = 0; -ScanFoldersModel *ScanFoldersModel::instance(QObject *parent) +bool ScanFoldersModel::initInstance(QObject *parent) { - //Q_ASSERT(!parent != !m_instance); - if (!m_instance) + if (!m_instance) { m_instance = new ScanFoldersModel(parent); + return true; + } + + return false; +} + +void ScanFoldersModel::freeInstance() +{ + if (m_instance) { + delete m_instance; + m_instance = 0; + } +} + +ScanFoldersModel *ScanFoldersModel::instance() +{ return m_instance; } @@ -79,6 +95,8 @@ ScanFoldersModel::ScanFoldersModel(QObject *parent) : QAbstractTableModel(parent) , m_fsWatcher(0) { + configure(); + connect(Preferences::instance(), SIGNAL(changed()), SLOT(configure())); } ScanFoldersModel::~ScanFoldersModel() @@ -153,7 +171,7 @@ ScanFoldersModel::PathStatus ScanFoldersModel::addPath(const QString &path, bool if (!m_fsWatcher) { m_fsWatcher = new FileSystemWatcher(this); - connect(m_fsWatcher, SIGNAL(torrentsAdded(QStringList&)), this, SIGNAL(torrentsAdded(QStringList&))); + connect(m_fsWatcher, SIGNAL(torrentsAdded(const QStringList &)), this, SLOT(addTorrentsToSession(const QStringList &))); } beginInsertRows(QModelIndex(), rowCount(), rowCount()); @@ -231,3 +249,47 @@ void ScanFoldersModel::makePersistent() pref->setScanDirs(paths); pref->setDownloadInScanDirs(downloadInFolderInfo); } + +void ScanFoldersModel::configure() +{ + Preferences *const pref = Preferences::instance(); + + int i = 0; + QList downloadInDirList = pref->getDownloadInScanDirs(); + foreach (const QString &dir, pref->getScanDirs()) { + bool downloadInDir = downloadInDirList.value(i, false); + addPath(dir, downloadInDir); + ++i; + } +} + +void ScanFoldersModel::addTorrentsToSession(const QStringList &pathList) +{ + foreach (const QString &file, pathList) { + qDebug("File %s added", qPrintable(file)); + if (file.endsWith(".magnet")) { + QFile f(file); + if (f.open(QIODevice::ReadOnly)) { + BitTorrent::Session::instance()->addTorrent(QString::fromLocal8Bit(f.readAll())); + f.remove(); + } + else { + qDebug("Failed to open magnet file: %s", qPrintable(f.errorString())); + } + } + else { + BitTorrent::AddTorrentParams params; + if (downloadInTorrentFolder(file)) + params.savePath = QFileInfo(file).dir().path(); + + BitTorrent::TorrentInfo torrentInfo = BitTorrent::TorrentInfo::loadFromFile(file); + if (torrentInfo.isValid()) { + BitTorrent::Session::instance()->addTorrent(torrentInfo, params); + fsutils::forceRemove(file); + } + else { + qDebug("Ignoring incomplete torrent file: %s", qPrintable(file)); + } + } + } +} diff --git a/src/core/scannedfoldersmodel.h b/src/core/scanfoldersmodel.h similarity index 78% rename from src/core/scannedfoldersmodel.h rename to src/core/scanfoldersmodel.h index ad17602b3..1b00e8ddf 100644 --- a/src/core/scannedfoldersmodel.h +++ b/src/core/scanfoldersmodel.h @@ -28,8 +28,8 @@ * Contact : chris@qbittorrent.org */ -#ifndef SCANNEDFOLDERSMODEL_H -#define SCANNEDFOLDERSMODEL_H +#ifndef SCANFOLDERSMODEL_H +#define SCANFOLDERSMODEL_H #include #include @@ -55,14 +55,15 @@ public: AlreadyInList }; - static ScanFoldersModel *instance(QObject *parent = 0); - virtual ~ScanFoldersModel(); + static bool initInstance(QObject *parent = 0); + static void freeInstance(); + static ScanFoldersModel *instance(); - 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; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + Qt::ItemFlags flags(const QModelIndex &index) const; // TODO: removePaths(); singular version becomes private helper functions; // also: remove functions should take modelindexes @@ -74,12 +75,14 @@ public: bool downloadInTorrentFolder(const QString &filePath) const; void makePersistent(); -signals: - // The absolute paths of new torrent files in the scanned directories. - void torrentsAdded(QStringList &pathList); +private slots: + void configure(); + void addTorrentsToSession(const QStringList &pathList); private: explicit ScanFoldersModel(QObject *parent = 0); + ~ScanFoldersModel(); + virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); static ScanFoldersModel *m_instance; class PathData; @@ -89,4 +92,4 @@ private: FileSystemWatcher *m_fsWatcher; }; -#endif // SCANNEDFOLDERSMODEL_H +#endif // SCANFOLDERSMODEL_H diff --git a/src/core/torrentfilter.cpp b/src/core/torrentfilter.cpp new file mode 100644 index 000000000..e514d15c8 --- /dev/null +++ b/src/core/torrentfilter.cpp @@ -0,0 +1,159 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2014 Vladimir Golovnev + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include "bittorrent/torrenthandle.h" +#include "torrentfilter.h" + +const QString TorrentFilter::AnyLabel; +const QStringSet TorrentFilter::AnyHash = (QStringSet() << QString()); + +const TorrentFilter TorrentFilter::DownloadingTorrent(TorrentFilter::Downloading); +const TorrentFilter TorrentFilter::SeedingTorrent(TorrentFilter::Seeding); +const TorrentFilter TorrentFilter::CompletedTorrent(TorrentFilter::Completed); +const TorrentFilter TorrentFilter::PausedTorrent(TorrentFilter::Paused); +const TorrentFilter TorrentFilter::ResumedTorrent(TorrentFilter::Resumed); +const TorrentFilter TorrentFilter::ActiveTorrent(TorrentFilter::Active); +const TorrentFilter TorrentFilter::InactiveTorrent(TorrentFilter::Inactive); + +using BitTorrent::TorrentHandle; +using BitTorrent::TorrentState; + +TorrentFilter::TorrentFilter() + : m_type(All) +{ +} + +TorrentFilter::TorrentFilter(Type type, QStringSet hashSet, QString label) + : m_type(type) + , m_label(label) + , m_hashSet(hashSet) +{ +} + +TorrentFilter::TorrentFilter(QString filter, QStringSet hashSet, QString label) + : m_type(All) + , m_label(label) + , m_hashSet(hashSet) +{ + setTypeByName(filter); +} + +bool TorrentFilter::setType(Type type) +{ + if (m_type != type) { + m_type = type; + return true; + } + + return false; +} + +bool TorrentFilter::setTypeByName(const QString &filter) +{ + Type type = All; + + if (filter == "downloading") + type = Downloading; + else if (filter == "seeding") + type = Seeding; + else if (filter == "completed") + type = Completed; + else if (filter == "paused") + type = Paused; + else if (filter == "resumed") + type = Resumed; + else if (filter == "active") + type = Active; + else if (filter == "inactive") + type = Inactive; + + return setType(type); +} + +bool TorrentFilter::setHashSet(const QStringSet &hashSet) +{ + if (m_hashSet != hashSet) { + m_hashSet = hashSet; + return true; + } + + return false; +} + +bool TorrentFilter::setLabel(const QString &label) +{ + if (m_label != label) { + m_label = label; + return true; + } + + return false; +} + +bool TorrentFilter::match(TorrentHandle *const torrent) const +{ + if (!torrent) return false; + + return (matchState(torrent) && matchHash(torrent) && matchLabel(torrent)); +} + +bool TorrentFilter::matchState(BitTorrent::TorrentHandle *const torrent) const +{ + switch (m_type) { + case All: + return true; + case Downloading: + return torrent->isDownloading(); + case Seeding: + return torrent->isUploading(); + case Completed: + return torrent->isCompleted(); + case Paused: + return torrent->isPaused(); + case Resumed: + return torrent->isResumed(); + case Active: + return torrent->isActive(); + case Inactive: + return torrent->isInactive(); + default: // All + return true; + } +} + +bool TorrentFilter::matchHash(BitTorrent::TorrentHandle *const torrent) const +{ + if (m_hashSet == AnyHash) return true; + else return m_hashSet.contains(torrent->hash()); +} + +bool TorrentFilter::matchLabel(BitTorrent::TorrentHandle *const torrent) const +{ + if (m_label == AnyLabel) return true; + else return (torrent->label() == m_label); +} diff --git a/src/core/torrentfilter.h b/src/core/torrentfilter.h new file mode 100644 index 000000000..88eea589a --- /dev/null +++ b/src/core/torrentfilter.h @@ -0,0 +1,93 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2014 Vladimir Golovnev + * + * 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. + */ + +#ifndef TORRENTFILTER_H +#define TORRENTFILTER_H + +#include +#include + +typedef QSet QStringSet; + +namespace BitTorrent +{ + +class TorrentHandle; +class TorrentState; + +} + +class TorrentFilter +{ +public: + enum Type + { + All, + Downloading, + Seeding, + Completed, + Resumed, + Paused, + Active, + Inactive + }; + + static const QString AnyLabel; + static const QStringSet AnyHash; + + static const TorrentFilter DownloadingTorrent; + static const TorrentFilter SeedingTorrent; + static const TorrentFilter CompletedTorrent; + static const TorrentFilter PausedTorrent; + static const TorrentFilter ResumedTorrent; + static const TorrentFilter ActiveTorrent; + static const TorrentFilter InactiveTorrent; + + TorrentFilter(); + // label: pass empty string for "no label" or null string (QString()) for "any label" + TorrentFilter(Type type, QStringSet hashSet = AnyHash, QString label = AnyLabel); + TorrentFilter(QString filter, QStringSet hashSet = AnyHash, QString label = AnyLabel); + + bool setType(Type type); + bool setTypeByName(const QString &filter); + bool setHashSet(const QStringSet &hashSet); + bool setLabel(const QString &label); + + bool match(BitTorrent::TorrentHandle *const torrent) const; + +private: + bool matchState(BitTorrent::TorrentHandle *const torrent) const; + bool matchHash(BitTorrent::TorrentHandle *const torrent) const; + bool matchLabel(BitTorrent::TorrentHandle *const torrent) const; + + Type m_type; + QString m_label; + QStringSet m_hashSet; +}; + +#endif // TORRENTFILTER_H diff --git a/src/core/torrentpersistentdata.cpp b/src/core/torrentpersistentdata.cpp deleted file mode 100644 index 179e87495..000000000 --- a/src/core/torrentpersistentdata.cpp +++ /dev/null @@ -1,527 +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 "torrentpersistentdata.h" - -#include -#include -#include - -#include "qinisettings.h" -#include "misc.h" -#include "qtorrenthandle.h" - -#include - -QHash TorrentTempData::data = QHash(); -QHash TorrentTempData::torrentMoveStates = QHash(); -QHash HiddenData::data = QHash(); -unsigned int HiddenData::metadata_counter = 0; -TorrentPersistentData* TorrentPersistentData::m_instance = 0; - -TorrentTempData::TorrentData::TorrentData() - : sequential(false) - , seed(false) - , add_paused(Preferences::instance()->addTorrentsInPause()) -{ -} - -TorrentTempData::TorrentMoveState::TorrentMoveState(QString oldPath, QString newPath) - : oldPath(oldPath) - , newPath(newPath) -{ -} - -bool TorrentTempData::hasTempData(const QString &hash) -{ - return data.contains(hash); -} - -void TorrentTempData::deleteTempData(const QString &hash) -{ - data.remove(hash); -} - -void TorrentTempData::setFilesPriority(const QString &hash, const std::vector &pp) -{ - data[hash].files_priority = pp; -} - -void TorrentTempData::setFilesPath(const QString &hash, const QStringList &path_list) -{ - data[hash].path_list = path_list; -} - -void TorrentTempData::setSavePath(const QString &hash, const QString &save_path) -{ - data[hash].save_path = save_path; -} - -void TorrentTempData::setLabel(const QString &hash, const QString &label) -{ - data[hash].label = label; -} - -void TorrentTempData::setSequential(const QString &hash, const bool &sequential) -{ - data[hash].sequential = sequential; -} - -bool TorrentTempData::isSequential(const QString &hash) -{ - return data.value(hash).sequential; -} - -void TorrentTempData::setSeedingMode(const QString &hash, const bool &seed) -{ - data[hash].seed = seed; -} - -bool TorrentTempData::isSeedingMode(const QString &hash) -{ - return data.value(hash).seed; -} - -QString TorrentTempData::getSavePath(const QString &hash) -{ - return data.value(hash).save_path; -} - -QStringList TorrentTempData::getFilesPath(const QString &hash) -{ - return data.value(hash).path_list; -} - -QString TorrentTempData::getLabel(const QString &hash) -{ - return data.value(hash).label; -} - -void TorrentTempData::getFilesPriority(const QString &hash, std::vector &fp) -{ - fp = data.value(hash).files_priority; -} - -bool TorrentTempData::isMoveInProgress(const QString &hash) -{ - return torrentMoveStates.find(hash) != torrentMoveStates.end(); -} - -void TorrentTempData::enqueueMove(const QString &hash, const QString &queuedPath) -{ - QHash::iterator i = torrentMoveStates.find(hash); - if (i == torrentMoveStates.end()) { - Q_ASSERT(false); - return; - } - i->queuedPath = queuedPath; -} - -void TorrentTempData::startMove(const QString &hash, const QString &oldPath, const QString& newPath) -{ - QHash::iterator i = torrentMoveStates.find(hash); - if (i != torrentMoveStates.end()) { - Q_ASSERT(false); - return; - } - - torrentMoveStates.insert(hash, TorrentMoveState(oldPath, newPath)); -} - -void TorrentTempData::finishMove(const QString &hash) -{ - QHash::iterator i = torrentMoveStates.find(hash); - if (i == torrentMoveStates.end()) { - Q_ASSERT(false); - return; - } - torrentMoveStates.erase(i); -} - -QString TorrentTempData::getOldPath(const QString &hash) -{ - QHash::iterator i = torrentMoveStates.find(hash); - if (i == torrentMoveStates.end()) { - Q_ASSERT(false); - return QString(); - } - return i->oldPath; -} - -QString TorrentTempData::getNewPath(const QString &hash) -{ - QHash::iterator i = torrentMoveStates.find(hash); - if (i == torrentMoveStates.end()) { - Q_ASSERT(false); - return QString(); - } - return i->newPath; -} - -QString TorrentTempData::getQueuedPath(const QString &hash) -{ - QHash::iterator i = torrentMoveStates.find(hash); - if (i == torrentMoveStates.end()) { - Q_ASSERT(false); - return QString(); - } - return i->queuedPath; -} - -void TorrentTempData::setAddPaused(const QString &hash, const bool &paused) -{ - data[hash].add_paused = paused; -} - -bool TorrentTempData::isAddPaused(const QString &hash) -{ - return data.value(hash).add_paused; -} - -void HiddenData::addData(const QString &hash) -{ - data[hash] = false; -} - -bool HiddenData::hasData(const QString &hash) -{ - return data.contains(hash); -} - -void HiddenData::deleteData(const QString &hash) -{ - if (data.value(hash, false)) - metadata_counter--; - data.remove(hash); -} - -int HiddenData::getSize() -{ - return data.size(); -} - -int HiddenData::getDownloadingSize() -{ - return data.size() - metadata_counter; -} - -void HiddenData::gotMetadata(const QString &hash) -{ - if (!data.contains(hash)) - return; - data[hash] = true; - metadata_counter++; -} - -TorrentPersistentData::TorrentPersistentData() - : m_data(QIniSettings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent-resume")).value("torrents").toHash()) - , dirty(false) -{ - timer.setSingleShot(true); - timer.setInterval(5 * 1000); - connect(&timer, SIGNAL(timeout()), SLOT(save())); -} - -TorrentPersistentData::~TorrentPersistentData() -{ - save(); -} - -void TorrentPersistentData::save() -{ - if (!dirty) - return; - - QIniSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent-resume")); - settings.setValue("torrents", m_data); - dirty = false; -} - -const QVariant TorrentPersistentData::value(const QString &key, const QVariant &defaultValue) const -{ - QReadLocker locker(&lock); - return m_data.value(key, defaultValue); -} - -void TorrentPersistentData::setValue(const QString &key, const QVariant &value) -{ - QWriteLocker locker(&lock); - if (m_data.value(key) == value) - return; - dirty = true; - timer.start(); - m_data.insert(key, value); -} - -TorrentPersistentData* TorrentPersistentData::instance() -{ - if (!m_instance) - m_instance = new TorrentPersistentData; - - return m_instance; -} - -void TorrentPersistentData::drop() -{ - if (m_instance) { - delete m_instance; - m_instance = 0; - } -} - -bool TorrentPersistentData::isKnownTorrent(QString hash) const -{ - QReadLocker locker(&lock); - return m_data.contains(hash); -} - -QStringList TorrentPersistentData::knownTorrents() const -{ - QReadLocker locker(&lock); - return m_data.keys(); -} - -void TorrentPersistentData::setRatioLimit(const QString &hash, const qreal &ratio) -{ - QHash torrent = value(hash).toHash(); - torrent["max_ratio"] = ratio; - setValue(hash, torrent); -} - -qreal TorrentPersistentData::getRatioLimit(const QString &hash) const -{ - const QHash torrent = value(hash).toHash(); - return torrent.value("max_ratio", USE_GLOBAL_RATIO).toReal(); -} - -bool TorrentPersistentData::hasPerTorrentRatioLimit() const -{ - QReadLocker locker(&lock); - QHash::ConstIterator it = m_data.constBegin(); - QHash::ConstIterator itend = m_data.constEnd(); - for (; it != itend; ++it) - if (it.value().toHash().value("max_ratio", USE_GLOBAL_RATIO).toReal() >= 0) - return true; - return false; -} - -void TorrentPersistentData::setAddedDate(const QString &hash, const QDateTime &time) -{ - QHash torrent = value(hash).toHash(); - if (!torrent.contains("add_date")) { - torrent["add_date"] = time; - setValue(hash, torrent); - } -} - -QDateTime TorrentPersistentData::getAddedDate(const QString &hash) const -{ - const QHash torrent = value(hash).toHash(); - QDateTime dt = torrent.value("add_date").toDateTime(); - if (!dt.isValid()) - dt = QDateTime::currentDateTime(); - return dt; -} - -void TorrentPersistentData::setErrorState(const QString &hash, const bool has_error) -{ - QHash torrent = value(hash).toHash(); - torrent["has_error"] = has_error; - setValue(hash, torrent); -} - -bool TorrentPersistentData::hasError(const QString &hash) const -{ - const QHash torrent = value(hash).toHash(); - return torrent.value("has_error", false).toBool(); -} - -QDateTime TorrentPersistentData::getSeedDate(const QString &hash) const -{ - const QHash torrent = value(hash).toHash(); - return torrent.value("seed_date").toDateTime(); -} - -void TorrentPersistentData::deletePersistentData(const QString &hash) -{ - QWriteLocker locker(&lock); - if (m_data.contains(hash)) { - m_data.remove(hash); - dirty = true; - timer.start(); - } -} - -void TorrentPersistentData::saveTorrentPersistentData(const QTorrentHandle &h, const QString &save_path, const bool is_magnet) -{ - Q_ASSERT(h.is_valid()); - QString hash = h.hash(); - qDebug("Saving persistent data for %s", qPrintable(hash)); - // Save persistent data - QHash torrent = value(hash).toHash(); - torrent["is_magnet"] = is_magnet; - if (is_magnet) - torrent["magnet_uri"] = misc::toQString(make_magnet_uri(h)); - torrent["seed"] = h.is_seed(); - torrent["priority"] = h.queue_position(); - if (save_path.isEmpty()) { - qDebug("TorrentPersistantData: save path is %s", qPrintable(h.save_path())); - torrent["save_path"] = h.save_path(); - } - else { - qDebug("TorrentPersistantData: overriding save path is %s", qPrintable(save_path)); - torrent["save_path"] = save_path; // Override torrent save path (e.g. because it is a temp dir) - } - // Label - torrent["label"] = TorrentTempData::getLabel(hash); - // Save data - setValue(hash, torrent); - qDebug("TorrentPersistentData: Saving save_path %s, hash: %s", qPrintable(h.save_path()), qPrintable(hash)); - // Set Added date - setAddedDate(hash, QDateTime::currentDateTime()); - // Finally, remove temp data - TorrentTempData::deleteTempData(hash); -} - -void TorrentPersistentData::saveSavePath(const QString &hash, const QString &save_path) -{ - Q_ASSERT(!hash.isEmpty()); - qDebug("TorrentPersistentData::saveSavePath(%s)", qPrintable(save_path)); - QHash torrent = value(hash).toHash(); - torrent["save_path"] = save_path; - setValue(hash, torrent); - qDebug("TorrentPersistentData: Saving save_path: %s, hash: %s", qPrintable(save_path), qPrintable(hash)); -} - -void TorrentPersistentData::saveLabel(const QString &hash, const QString &label) -{ - Q_ASSERT(!hash.isEmpty()); - QHash torrent = value(hash).toHash(); - torrent["label"] = label; - setValue(hash, torrent); -} - -void TorrentPersistentData::saveName(const QString &hash, const QString &name) -{ - Q_ASSERT(!hash.isEmpty()); - QHash torrent = value(hash).toHash(); - torrent["name"] = name; - setValue(hash, torrent); -} - -void TorrentPersistentData::savePriority(const QTorrentHandle &h) -{ - QString hash = h.hash(); - QHash torrent = value(hash).toHash(); - torrent["priority"] = h.queue_position(); - setValue(hash, torrent); -} - -void TorrentPersistentData::savePriority(const QString &hash, const int &queue_pos) -{ - QHash torrent = value(hash).toHash(); - torrent["priority"] = queue_pos; - setValue(hash, torrent); -} - -void TorrentPersistentData::saveSeedStatus(const QTorrentHandle &h) -{ - QString hash = h.hash(); - QHash torrent = value(hash).toHash(); - bool was_seed = torrent.value("seed", false).toBool(); - if (was_seed != h.is_seed()) { - torrent["seed"] = !was_seed; - setValue(hash, torrent); - } -} - -void TorrentPersistentData::saveSeedStatus(const QString &hash, const bool seedStatus) -{ - QHash torrent = value(hash).toHash(); - torrent["seed"] = seedStatus; - setValue(hash, torrent); -} - -void TorrentPersistentData::setHasMissingFiles(const QString& hash, bool missing) -{ - QHash torrent = value(hash).toHash(); - torrent["has_missing_files"] = missing; - setValue(hash, torrent); -} - -QString TorrentPersistentData::getSavePath(const QString &hash) const -{ - const QHash torrent = value(hash).toHash(); - //qDebug("TorrentPersistentData: getSavePath %s", data["save_path"].toString().toLocal8Bit().data()); - return torrent.value("save_path").toString(); -} - -QString TorrentPersistentData::getLabel(const QString &hash) const -{ - const QHash torrent = value(hash).toHash(); - return torrent.value("label", "").toString(); -} - -QString TorrentPersistentData::getName(const QString &hash) const -{ - const QHash torrent = value(hash).toHash(); - return torrent.value("name", "").toString(); -} - -int TorrentPersistentData::getPriority(const QString &hash) const -{ - const QHash torrent = value(hash).toHash(); - return torrent.value("priority", -1).toInt(); -} - -bool TorrentPersistentData::isSeed(const QString &hash) const -{ - const QHash torrent = value(hash).toHash(); - return torrent.value("seed", false).toBool(); -} - -bool TorrentPersistentData::isMagnet(const QString &hash) const -{ - const QHash torrent = value(hash).toHash(); - return torrent.value("is_magnet", false).toBool(); -} - -QString TorrentPersistentData::getMagnetUri(const QString &hash) const -{ - const QHash torrent = value(hash).toHash(); - Q_ASSERT(torrent.value("is_magnet", false).toBool()); - return torrent.value("magnet_uri").toString(); -} - -bool TorrentPersistentData::getHasMissingFiles(const QString& hash) -{ - QHash torrent = value(hash).toHash(); - return torrent.value("has_missing_files").toBool(); -} diff --git a/src/core/torrentpersistentdata.h b/src/core/torrentpersistentdata.h deleted file mode 100644 index b19f36acf..000000000 --- a/src/core/torrentpersistentdata.h +++ /dev/null @@ -1,181 +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 - */ - -#ifndef TORRENTPERSISTENTDATA_H -#define TORRENTPERSISTENTDATA_H - -#include -#include -#include -#include -#include -#include -#include "preferences.h" - -QT_BEGIN_NAMESPACE -class QDateTime; -QT_END_NAMESPACE - -class QTorrentHandle; - -class TorrentTempData -{ - // This class stores strings w/o modifying separators -public: - static bool hasTempData(const QString &hash); - static void deleteTempData(const QString &hash); - static void setFilesPriority(const QString &hash, const std::vector &pp); - static void setFilesPath(const QString &hash, const QStringList &path_list); - static void setSavePath(const QString &hash, const QString &save_path); - static void setLabel(const QString &hash, const QString &label); - static void setSequential(const QString &hash, const bool &sequential); - static bool isSequential(const QString &hash); - static void setSeedingMode(const QString &hash, const bool &seed); - static bool isSeedingMode(const QString &hash); - static QString getSavePath(const QString &hash); - static QStringList getFilesPath(const QString &hash); - static QString getLabel(const QString &hash); - static void getFilesPriority(const QString &hash, std::vector &fp); - static bool isMoveInProgress(const QString &hash); - static void enqueueMove(const QString &hash, const QString &queuedPath); - static void startMove(const QString &hash, const QString &oldPath, const QString& newPath); - static void finishMove(const QString &hash); - static QString getOldPath(const QString &hash); - static QString getNewPath(const QString &hash); - static QString getQueuedPath(const QString &hash); - static void setAddPaused(const QString &hash, const bool &paused); - static bool isAddPaused(const QString &hash); - -private: - struct TorrentData - { - TorrentData(); - std::vector files_priority; - QStringList path_list; - QString save_path; - QString label; - bool sequential; - bool seed; - bool add_paused; - }; - - struct TorrentMoveState - { - TorrentMoveState(QString oldPath, QString newPath); - // the moving occurs from oldPath to newPath - // queuedPath is where files should be moved to, when current moving is completed - QString oldPath; - QString newPath; - QString queuedPath; - }; - - static QHash data; - static QHash torrentMoveStates; -}; - -class HiddenData -{ -public: - static void addData(const QString &hash); - static bool hasData(const QString &hash); - static void deleteData(const QString &hash); - static int getSize(); - static int getDownloadingSize(); - static void gotMetadata(const QString &hash); - -private: - static QHash data; - static unsigned int metadata_counter; -}; - -class TorrentPersistentData: QObject -{ - Q_OBJECT - Q_DISABLE_COPY(TorrentPersistentData) - -public: - enum RatioLimit - { - USE_GLOBAL_RATIO = -2, - NO_RATIO_LIMIT = -1 - }; - - static TorrentPersistentData* instance(); - static void drop(); - ~TorrentPersistentData(); - - bool isKnownTorrent(QString hash) const; - QStringList knownTorrents() const; - void setRatioLimit(const QString &hash, const qreal &ratio); - qreal getRatioLimit(const QString &hash) const; - bool hasPerTorrentRatioLimit() const; - void setAddedDate(const QString &hash, const QDateTime &time); - QDateTime getAddedDate(const QString &hash) const; - void setErrorState(const QString &hash, const bool has_error); - bool hasError(const QString &hash) const; - QDateTime getSeedDate(const QString &hash) const; - void deletePersistentData(const QString &hash); - void saveTorrentPersistentData(const QTorrentHandle &h, const QString &save_path = QString::null, const bool is_magnet = false); - - // Setters - void saveSavePath(const QString &hash, const QString &save_path); - void saveLabel(const QString &hash, const QString &label); - void saveName(const QString &hash, const QString &name); - void savePriority(const QTorrentHandle &h); - void savePriority(const QString &hash, const int &queue_pos); - void saveSeedStatus(const QTorrentHandle &h); - void saveSeedStatus(const QString &hash, const bool seedStatus); - void setHasMissingFiles(const QString &hash, bool missing); - - // Getters - QString getSavePath(const QString &hash) const; - QString getLabel(const QString &hash) const; - QString getName(const QString &hash) const; - int getPriority(const QString &hash) const; - bool isSeed(const QString &hash) const; - bool isMagnet(const QString &hash) const; - QString getMagnetUri(const QString &hash) const; - bool getHasMissingFiles(const QString& hash); -public slots: - void save(); - -private: - TorrentPersistentData(); - static TorrentPersistentData* m_instance; - QHash m_data; - bool dirty; - QTimer timer; - mutable QReadWriteLock lock; - const QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; - void setValue(const QString &key, const QVariant &value); - -}; - -#endif // TORRENTPERSISTENTDATA_H diff --git a/src/core/tristatebool.cpp b/src/core/tristatebool.cpp new file mode 100644 index 000000000..1e5a5eed9 --- /dev/null +++ b/src/core/tristatebool.cpp @@ -0,0 +1,60 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include "tristatebool.h" + +TriStateBool::TriStateBool() + : m_value(Undefined) +{ +} + +TriStateBool::TriStateBool(bool b) +{ + m_value = (b ? True : False); +} + +TriStateBool::TriStateBool(TriStateBool::ValueType value) + : m_value(Undefined) +{ + switch (value) { + case Undefined: + case True: + case False: + m_value = value; + } +} + +TriStateBool::operator bool() const +{ + return (m_value == True); +} + +TriStateBool::operator ValueType() const +{ + return m_value; +} diff --git a/src/core/tristatebool.h b/src/core/tristatebool.h new file mode 100644 index 000000000..b57f3873c --- /dev/null +++ b/src/core/tristatebool.h @@ -0,0 +1,53 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * 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. + */ + +#ifndef TRISTATEBOOL_H +#define TRISTATEBOOL_H + +class TriStateBool +{ +public: + enum ValueType + { + Undefined = -1, + False = 0, + True = 1 + }; + + TriStateBool(); + TriStateBool(bool b); + TriStateBool(ValueType value); + + operator ValueType() const; + operator bool() const; + +private: + ValueType m_value; +}; + +#endif // TRISTATEBOOL_H diff --git a/src/core/types.h b/src/core/types.h new file mode 100644 index 000000000..5716ae34c --- /dev/null +++ b/src/core/types.h @@ -0,0 +1,63 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * 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. + */ + +#ifndef TYPES_H +#define TYPES_H + +#include + +#define BEGIN_SCOPED_ENUM(name) class name\ +{\ + int m_val;\ +\ +public:\ + name() {}\ + name(int val) : m_val(val) {}\ + operator int() const { return m_val; }\ + operator QVariant() const { return m_val; }\ +\ + enum + +#define END_SCOPED_ENUM ; }; + + +BEGIN_SCOPED_ENUM(MaxRatioAction) +{ + Pause, + Remove +} +END_SCOPED_ENUM + +BEGIN_SCOPED_ENUM(TorrentExportFolder) +{ + Regular, + Finished +} +END_SCOPED_ENUM + +#endif // TYPES_H diff --git a/src/core/utils/string.cpp b/src/core/utils/string.cpp new file mode 100644 index 000000000..4544138bd --- /dev/null +++ b/src/core/utils/string.cpp @@ -0,0 +1,43 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * 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. + */ + +#include +#include +#include "string.h" + +QString String::fromStdString(const std::string &str) +{ + return QString::fromUtf8(str.c_str()); +} + +std::string String::toStdString(const QString &str) +{ + QByteArray utf8 = str.toUtf8(); + return std::string(utf8.constData(), utf8.length()); +} diff --git a/src/core/utils/string.h b/src/core/utils/string.h new file mode 100644 index 000000000..b39ff654e --- /dev/null +++ b/src/core/utils/string.h @@ -0,0 +1,43 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * 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. + */ + +#ifndef UTILS_STRING_H +#define UTILS_STRING_H + +#include + +class QString; + +namespace String +{ + QString fromStdString(const std::string &str); + std::string toStdString(const QString &str); +} + +#endif // UTILS_STRING_H diff --git a/src/gui/addnewtorrentdialog.cpp b/src/gui/addnewtorrentdialog.cpp index e9a06b92a..aa39758ca 100644 --- a/src/gui/addnewtorrentdialog.cpp +++ b/src/gui/addnewtorrentdialog.cpp @@ -1,5 +1,5 @@ /* - * Bittorrent Client using Qt4 and libtorrent. + * Bittorrent Client using Qt and libtorrent. * Copyright (C) 2012 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -34,9 +34,12 @@ #include "torrentcontentmodel.h" #include "torrentcontentfiltermodel.h" #include "core/preferences.h" -#include "core/torrentpersistentdata.h" -#include "qbtsession.h" -#include "iconprovider.h" +#include "core/net/downloadmanager.h" +#include "core/net/downloadhandler.h" +#include "core/bittorrent/session.h" +#include "core/bittorrent/magneturi.h" +#include "core/bittorrent/torrentinfo.h" +#include "guiiconprovider.h" #include "core/fs_utils.h" #include "autoexpandabledialog.h" #include "messageboxraised.h" @@ -47,18 +50,13 @@ #include #include #include -#include - -using namespace libtorrent; AddNewTorrentDialog::AddNewTorrentDialog(QWidget *parent) : QDialog(parent) , ui(new Ui::AddNewTorrentDialog) , m_contentModel(0) , m_contentDelegate(0) - , m_isMagnet(false) , m_hasMetadata(false) - , m_hasRenamedFile(false) , m_oldIndex(0) { ui->setupUi(this); @@ -123,22 +121,115 @@ void AddNewTorrentDialog::saveState() pref->setAddNewTorrentDialogExpanded(ui->adv_button->isChecked()); } -void AddNewTorrentDialog::showTorrent(const QString &torrent_path, const QString& from_url, QWidget *parent) +void AddNewTorrentDialog::show(QString source, QWidget *parent) { + if (source.startsWith("bc://bt/", Qt::CaseInsensitive)) { + qDebug("Converting bc link to magnet link"); + source = misc::bcLinkToMagnet(source); + } + AddNewTorrentDialog *dlg = new AddNewTorrentDialog(parent); - if (dlg->loadTorrent(torrent_path, from_url)) - dlg->open(); - else - delete dlg; + + if (misc::isUrl(source)) { + // Launch downloader + Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(source, 10485760 /* 10MB */); + connect(handler, SIGNAL(downloadFinished(QString, QString)), dlg, SLOT(handleDownloadFinished(QString, QString))); + connect(handler, SIGNAL(downloadFailed(QString, QString)), dlg, SLOT(handleDownloadFailed(QString, QString))); + connect(handler, SIGNAL(redirectedToMagnet(QString, QString)), dlg, SLOT(handleRedirectedToMagnet(QString, QString))); + } + else { + bool ok = false; + if (source.startsWith("magnet:", Qt::CaseInsensitive)) + ok = dlg->loadMagnet(source); + else + ok = dlg->loadTorrent(source); + + if (ok) + dlg->open(); + else + delete dlg; + } } -void AddNewTorrentDialog::showMagnet(const QString& link, QWidget *parent) +bool AddNewTorrentDialog::loadTorrent(const QString& torrent_path) { - AddNewTorrentDialog *dlg = new AddNewTorrentDialog(parent); - if (dlg->loadMagnet(link)) - dlg->open(); + if (torrent_path.startsWith("file://", Qt::CaseInsensitive)) + m_filePath = QUrl::fromEncoded(torrent_path.toLocal8Bit()).toLocalFile(); else - delete dlg; + m_filePath = torrent_path; + + if (!QFile::exists(m_filePath)) { + MessageBoxRaised::critical(0, tr("I/O Error"), tr("The torrent file does not exist.")); + return false; + } + + m_hasMetadata = true; + QString error; + m_torrentInfo = BitTorrent::TorrentInfo::loadFromFile(m_filePath, error); + if (!m_torrentInfo.isValid()) { + MessageBoxRaised::critical(0, tr("Invalid torrent"), tr("Failed to load the torrent: %1").arg(error)); + return false; + } + + m_hash = m_torrentInfo.hash(); + + // Prevent showing the dialog if download is already present + if (BitTorrent::Session::instance()->isKnownTorrent(m_hash)) { + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(m_hash); + if (torrent) { + torrent->addTrackers(m_torrentInfo.trackers()); + torrent->addUrlSeeds(m_torrentInfo.urlSeeds()); + MessageBoxRaised::information(0, tr("Already in download list"), tr("Torrent is already in download list. Trackers were merged."), QMessageBox::Ok); + } + else { + MessageBoxRaised::critical(0, tr("Cannot add torrent"), tr("Cannot add this torrent. Perhaps it is already in adding state."), QMessageBox::Ok); + } + return false; + } + + ui->lblhash->setText(m_hash); + setupTreeview(); + return true; +} + +bool AddNewTorrentDialog::loadMagnet(const QString &magnet_uri) +{ + BitTorrent::MagnetUri magnet(magnet_uri); + if (!magnet.isValid()) { + MessageBoxRaised::critical(0, tr("Invalid magnet link"), tr("This magnet link was not recognized")); + return false; + } + + m_hash = magnet.hash(); + // Prevent showing the dialog if download is already present + if (BitTorrent::Session::instance()->isKnownTorrent(m_hash)) { + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(m_hash); + if (torrent) { + torrent->addTrackers(magnet.trackers()); + torrent->addUrlSeeds(magnet.urlSeeds()); + MessageBoxRaised::information(0, tr("Already in download list"), tr("Magnet link is already in download list. Trackers were merged."), QMessageBox::Ok); + } + else { + MessageBoxRaised::critical(0, tr("Cannot add torrent"), tr("Cannot add this torrent. Perhaps it is already in adding."), QMessageBox::Ok); + } + return false; + } + + connect(BitTorrent::Session::instance(), SIGNAL(metadataLoaded(BitTorrent::TorrentInfo)), SLOT(updateMetadata(BitTorrent::TorrentInfo))); + + // Set dialog title + QString torrent_name = magnet.name(); + setWindowTitle(torrent_name.isEmpty() ? tr("Magnet link") : torrent_name); + + setupTreeview(); + // Set dialog position + setdialogPosition(); + + BitTorrent::Session::instance()->loadMetadata(magnet_uri); + setMetadataProgressIndicator(true, tr("Retrieving metadata...")); + ui->lblhash->setText(m_hash); + + return true; } void AddNewTorrentDialog::showEvent(QShowEvent *event) @@ -158,7 +249,7 @@ void AddNewTorrentDialog::showAdvancedSettings(bool show) ui->adv_button->setText(QString::fromUtf8("▲")); ui->settings_group->setVisible(true); ui->info_group->setVisible(true); - if (m_hasMetadata && (m_torrentInfo->num_files() > 1)) { + if (m_hasMetadata && (m_torrentInfo.filesCount() > 1)) { ui->content_tree->setVisible(true); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); } @@ -178,83 +269,6 @@ void AddNewTorrentDialog::showAdvancedSettings(bool show) relayout(); } -bool AddNewTorrentDialog::loadTorrent(const QString& torrent_path, const QString& from_url) -{ - m_isMagnet = false; - m_url = from_url; - if (torrent_path.startsWith("file://", Qt::CaseInsensitive)) - m_filePath = QUrl::fromEncoded(torrent_path.toLocal8Bit()).toLocalFile(); - else - m_filePath = torrent_path; - - if (!QFile::exists(m_filePath)) { - MessageBoxRaised::critical(0, tr("I/O Error"), tr("The torrent file does not exist.")); - return false; - } - - m_hasMetadata = true; - - try { - std::vector buffer; - lazy_entry entry; - libtorrent::error_code ec; - misc::loadBencodedFile(m_filePath, buffer, entry, ec); - m_torrentInfo = new torrent_info(entry); - m_hash = misc::toQString(m_torrentInfo->info_hash()); - } - catch(const std::exception& e) { - MessageBoxRaised::critical(0, tr("Invalid torrent"), tr("Failed to load the torrent: %1").arg(misc::toQStringU(e.what()))); - return false; - } - - // Prevent showing the dialog if download is already present - if (QBtSession::instance()->getTorrentHandle(m_hash).is_valid()) { - MessageBoxRaised::information(0, tr("Already in download list"), tr("Torrent is already in download list. Merging trackers."), QMessageBox::Ok); - QBtSession::instance()->addTorrent(m_filePath, false, m_url);; - return false; - } - - ui->lblhash->setText(m_hash); - setupTreeview(); - return true; -} - -bool AddNewTorrentDialog::loadMagnet(const QString &magnet_uri) -{ - connect(QBtSession::instance(), SIGNAL(metadataReceivedHidden(const QTorrentHandle &)), SLOT(updateMetadata(const QTorrentHandle &))); - m_isMagnet = true; - m_url = magnet_uri; - m_hash = misc::magnetUriToHash(m_url); - if (m_hash.isEmpty()) { - MessageBoxRaised::critical(0, tr("Invalid magnet link"), tr("This magnet link was not recognized")); - return false; - } - - // Prevent showing the dialog if download is already present - if (QBtSession::instance()->getTorrentHandle(m_hash).is_valid()) { - MessageBoxRaised::information(0, tr("Already in download list"), tr("Magnet link is already in download list. Merging trackers."), QMessageBox::Ok); - QBtSession::instance()->addMagnetUri(m_url, false); - return false; - } - - // Set dialog title - QString torrent_name = misc::magnetUriToName(m_url); - setWindowTitle(torrent_name.isEmpty() ? tr("Magnet link") : torrent_name); - - setupTreeview(); - // Set dialog position - setdialogPosition(); - - // Override save path - TorrentTempData::setSavePath(m_hash, QString(QDir::tempPath() + "/" + m_hash)); - HiddenData::addData(m_hash); - QBtSession::instance()->addMagnetUri(m_url, false); - setMetadataProgressIndicator(true, tr("Retrieving metadata...")); - ui->lblhash->setText(m_hash); - - return true; -} - void AddNewTorrentDialog::saveSavePathHistory() const { QDir selected_save_path(ui->save_path_combo->itemData(ui->save_path_combo->currentIndex()).toString()); @@ -279,7 +293,7 @@ void AddNewTorrentDialog::saveSavePathHistory() const int AddNewTorrentDialog::indexOfSavePath(const QString &save_path) { QDir saveDir(save_path); - for(int i = 0; isave_path_combo->count() - 1; ++i) + for(int i = 0; i < ui->save_path_combo->count(); ++i) if (QDir(ui->save_path_combo->itemData(i).toString()) == saveDir) return i; return -1; @@ -287,7 +301,7 @@ int AddNewTorrentDialog::indexOfSavePath(const QString &save_path) void AddNewTorrentDialog::updateFileNameInSavePaths(const QString &new_filename) { - for(int i = 0; isave_path_combo->count() - 1; ++i) { + for(int i = 0; i < ui->save_path_combo->count(); ++i) { const QDir folder(ui->save_path_combo->itemData(i).toString()); ui->save_path_combo->setItemText(i, fsutils::toNativePath(folder.absoluteFilePath(new_filename))); } @@ -300,14 +314,14 @@ void AddNewTorrentDialog::updateDiskSpaceLabel() if (m_hasMetadata) { if (m_contentModel) { - const std::vector priorities = m_contentModel->model()->getFilesPriorities(); - Q_ASSERT(priorities.size() == (uint) m_torrentInfo->num_files()); - for (uint i = 0; i priorities = m_contentModel->model()->getFilePriorities(); + Q_ASSERT(priorities.size() == m_torrentInfo.filesCount()); + for (int i = 0; i < priorities.size(); ++i) if (priorities[i] > 0) - torrent_size += m_torrentInfo->files().file_size(i); + torrent_size += m_torrentInfo.fileSize(i); } else { - torrent_size = m_torrentInfo->total_size(); + torrent_size = m_torrentInfo.totalSize(); } } @@ -338,7 +352,7 @@ void AddNewTorrentDialog::browseButton_clicked() QString cur_save_path = ui->save_path_combo->itemText(m_oldIndex); QString new_path, old_filename, new_filename; - if (m_torrentInfo && m_torrentInfo->num_files() == 1) { + if (m_torrentInfo.isValid() && (m_torrentInfo.filesCount() == 1)) { old_filename = fsutils::fileName(cur_save_path); new_path = QFileDialog::getSaveFileName(this, tr("Choose save path"), cur_save_path, QString(), 0, QFileDialog::DontConfirmOverwrite); if (!new_path.isEmpty()) @@ -367,8 +381,7 @@ void AddNewTorrentDialog::browseButton_clicked() } // Update file name in all save_paths if (!new_filename.isEmpty() && !fsutils::sameFileNames(old_filename, new_filename)) { - m_hasRenamedFile = true; - m_filesPath[0] = new_filename; + m_torrentInfo.renameFile(0, new_filename); updateFileNameInSavePaths(new_filename); } @@ -415,7 +428,7 @@ void AddNewTorrentDialog::renameSelectedFile() if (m_contentModel->itemType(index) == TorrentContentModelItem::FileType) { // File renaming const int file_index = m_contentModel->getFileIndex(index); - QString old_name = fsutils::fromNativePath(m_filesPath.at(file_index)); + QString old_name = fsutils::fromNativePath(m_torrentInfo.filePath(file_index)); qDebug("Old name: %s", qPrintable(old_name)); QStringList path_items = old_name.split("/"); path_items.removeLast(); @@ -428,9 +441,9 @@ void AddNewTorrentDialog::renameSelectedFile() new_name = fsutils::expandPath(new_name); qDebug("New name: %s", qPrintable(new_name)); // Check if that name is already used - for (int i = 0; inum_files(); ++i) { + for (int i = 0; i < m_torrentInfo.filesCount(); ++i) { if (i == file_index) continue; - if (fsutils::sameFileNames(m_filesPath.at(i), new_name)) { + if (fsutils::sameFileNames(m_torrentInfo.filePath(i), new_name)) { // Display error message MessageBoxRaised::warning(this, tr("The file could not be renamed"), tr("This name is already in use in this folder. Please use a different name."), @@ -439,9 +452,7 @@ void AddNewTorrentDialog::renameSelectedFile() } } qDebug("Renaming %s to %s", qPrintable(old_name), qPrintable(new_name)); - // Rename file in files_path - m_filesPath.replace(file_index, new_name); - m_hasRenamedFile = true; + m_torrentInfo.renameFile(file_index, new_name); // Rename in torrent files model too m_contentModel->setData(index, new_name_last); } @@ -460,8 +471,8 @@ void AddNewTorrentDialog::renameSelectedFile() QString new_path = path_items.join("/"); if (!new_path.endsWith("/")) new_path += "/"; // Check for overwriting - for (int i = 0; inum_files(); ++i) { - const QString ¤t_name = m_filesPath.at(i); + for (int i = 0; i < m_torrentInfo.filesCount(); ++i) { + const QString ¤t_name = m_torrentInfo.filePath(i); #if defined(Q_OS_UNIX) || defined(Q_WS_QWS) if (current_name.startsWith(new_path, Qt::CaseSensitive)) { #else @@ -474,18 +485,17 @@ void AddNewTorrentDialog::renameSelectedFile() } } // Replace path in all files - for (int i = 0; inum_files(); ++i) { - const QString ¤t_name = m_filesPath.at(i); + for (int i = 0; i < m_torrentInfo.filesCount(); ++i) { + const QString ¤t_name = m_torrentInfo.filePath(i); if (current_name.startsWith(old_path)) { QString new_name = current_name; new_name.replace(0, old_path.length(), new_path); new_name = fsutils::expandPath(new_name); qDebug("Rename %s to %s", qPrintable(current_name), qPrintable(new_name)); - // Rename in files_path - m_filesPath.replace(i, new_name); + m_torrentInfo.renameFile(i, new_name); } } - m_hasRenamedFile = true; + // Rename folder in torrent files model too m_contentModel->setData(index, new_name_last); } @@ -524,8 +534,8 @@ void AddNewTorrentDialog::displayContentTreeMenu(const QPoint&) QMenu myFilesLlistMenu; const QModelIndexList selectedRows = ui->content_tree->selectionModel()->selectedRows(0); QAction *actRename = 0; - if (selectedRows.size() == 1 && m_torrentInfo->num_files() > 1) { - actRename = myFilesLlistMenu.addAction(IconProvider::instance()->getIcon("edit-rename"), tr("Rename...")); + if ((selectedRows.size() == 1) && (m_torrentInfo.filesCount() > 1)) { + actRename = myFilesLlistMenu.addAction(GuiIconProvider::instance()->getIcon("edit-rename"), tr("Rename...")); myFilesLlistMenu.addSeparator(); } QMenu subMenu; @@ -564,85 +574,78 @@ void AddNewTorrentDialog::displayContentTreeMenu(const QPoint&) void AddNewTorrentDialog::accept() { - if (m_isMagnet) - disconnect(this, SLOT(updateMetadata(const QTorrentHandle &))); + if (!m_hasMetadata) + disconnect(this, SLOT(updateMetadata(const BitTorrent::TorrentInfo &))); + + Preferences *const pref = Preferences::instance(); + BitTorrent::AddTorrentParams params; - Preferences* const pref = Preferences::instance(); - // Save Temporary data about torrent - QString save_path = ui->save_path_combo->itemData(ui->save_path_combo->currentIndex()).toString(); - TorrentTempData::setSavePath(m_hash, save_path); if (ui->skip_check_cb->isChecked()) // TODO: Check if destination actually exists - TorrentTempData::setSeedingMode(m_hash, true); + params.skipChecking = true; // Label - const QString label = ui->label_combo->currentText(); - if (!label.isEmpty()) - TorrentTempData::setLabel(m_hash, label); + params.label = ui->label_combo->currentText(); // Save file priorities if (m_contentModel) - TorrentTempData::setFilesPriority(m_hash, m_contentModel->model()->getFilesPriorities()); + params.filePriorities = m_contentModel->model()->getFilePriorities(); - // Rename files if necessary - if (m_hasRenamedFile) - TorrentTempData::setFilesPath(m_hash, m_filesPath); - - TorrentTempData::setAddPaused(m_hash, !ui->start_torrent_cb->isChecked()); - - // Add torrent - if (m_isMagnet) - QBtSession::instance()->unhideMagnet(m_hash); - else - QBtSession::instance()->addTorrent(m_filePath, false, m_url); + params.addPaused = !ui->start_torrent_cb->isChecked(); saveSavePathHistory(); - // Save settings pref->useAdditionDialog(!ui->never_show_cb->isChecked()); + + QString savePath = ui->save_path_combo->itemData(ui->save_path_combo->currentIndex()).toString(); if (ui->default_save_path_cb->isChecked()) { - pref->setSavePath(ui->save_path_combo->itemData(ui->save_path_combo->currentIndex()).toString()); - QBtSession::instance()->setDefaultSavePath(pref->getSavePath()); + pref->setSavePath(savePath); + pref->apply(); } + else { + // if we don't use default save path... + if (QDir(savePath) != QDir(pref->getSavePath())) + params.savePath = savePath; + } + + // Add torrent + if (!m_hasMetadata) + BitTorrent::Session::instance()->addTorrent(m_hash, params); + else + BitTorrent::Session::instance()->addTorrent(m_torrentInfo, params); + QDialog::accept(); } void AddNewTorrentDialog::reject() { - if (m_isMagnet) { - disconnect(this, SLOT(updateMetadata(const QTorrentHandle &))); + if (!m_hasMetadata) { + disconnect(this, SLOT(updateMetadata(BitTorrent::TorrentInfo))); setMetadataProgressIndicator(false); - QBtSession::instance()->deleteTorrent(m_hash, true); + BitTorrent::Session::instance()->cancelLoadMetadata(m_hash); } + QDialog::reject(); } -void AddNewTorrentDialog::updateMetadata(const QTorrentHandle &h) +void AddNewTorrentDialog::updateMetadata(const BitTorrent::TorrentInfo &info) { - try { - if (h.hash() != m_hash) - return; + if (info.hash() != m_hash) return; - disconnect(this, SLOT(updateMetadata(const QTorrentHandle &))); - Q_ASSERT(h.has_metadata()); - -#if LIBTORRENT_VERSION_NUM < 10000 - m_torrentInfo = new torrent_info(h.get_torrent_info()); -#else - m_torrentInfo = new torrent_info(*h.torrent_file()); -#endif - - // Good to go - m_hasMetadata = true; - setMetadataProgressIndicator(true, tr("Parsing metadata...")); - - // Update UI - setupTreeview(); - setMetadataProgressIndicator(false, tr("Metadata retrieval complete")); - } catch (invalid_handle&) { - MessageBoxRaised::critical(0, tr("I/O Error"), ("Unknown error.")); - setMetadataProgressIndicator(false, tr("Unknown error")); + disconnect(this, SLOT(updateMetadata(BitTorrent::TorrentInfo))); + if (!info.isValid()) { + MessageBoxRaised::critical(0, tr("I/O Error"), ("Invalid metadata.")); + setMetadataProgressIndicator(false, tr("Invalid metadata")); return; } + + // Good to go + m_torrentInfo = info; + m_hasMetadata = true; + setMetadataProgressIndicator(true, tr("Parsing metadata...")); + + // Update UI + setupTreeview(); + setMetadataProgressIndicator(false, tr("Metadata retrieval complete")); } void AddNewTorrentDialog::setMetadataProgressIndicator(bool visibleIndicator, const QString &labelText) @@ -661,21 +664,15 @@ void AddNewTorrentDialog::setupTreeview() } else { // Set dialog title - setWindowTitle(misc::toQStringU(m_torrentInfo->name())); + setWindowTitle(m_torrentInfo.name()); // Set torrent information - QString comment = misc::toQString(m_torrentInfo->comment()); + QString comment = m_torrentInfo.comment(); ui->comment_lbl->setText(comment.replace('\n', ' ')); - ui->date_lbl->setText(m_torrentInfo->creation_date() ? misc::toQString(*m_torrentInfo->creation_date()) : tr("Not available")); - - file_storage const& fs = m_torrentInfo->files(); - - // Populate m_filesList - for (int i = 0; i < fs.num_files(); ++i) - m_filesPath << misc::toQStringU(fs.file_path(i)); + ui->date_lbl->setText(!m_torrentInfo.creationDate().isNull() ? m_torrentInfo.creationDate().toString(Qt::DefaultLocaleLongDate) : tr("Not available")); // Prepare content tree - if (fs.num_files() > 1) { + if (m_torrentInfo.filesCount() > 1) { m_contentModel = new TorrentContentFilterModel(this); connect(m_contentModel->model(), SIGNAL(filteredFilesChanged()), SLOT(updateDiskSpaceLabel())); ui->content_tree->setModel(m_contentModel); @@ -686,7 +683,7 @@ void AddNewTorrentDialog::setupTreeview() connect(ui->content_tree, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(displayContentTreeMenu(const QPoint &))); // List files in torrent - m_contentModel->model()->setupModelData(*m_torrentInfo); + m_contentModel->model()->setupModelData(m_torrentInfo); if (!m_headerState.isEmpty()) ui->content_tree->header()->restoreState(m_headerState); @@ -695,8 +692,8 @@ void AddNewTorrentDialog::setupTreeview() } else { // Update save paths (append file name to them) - QString single_file_relpath = misc::toQStringU(fs.file_path(0)); - for (int i = 0; isave_path_combo->count() - 1; ++i) + QString single_file_relpath = m_torrentInfo.filePath(0); + for (int i = 0; i < ui->save_path_combo->count(); ++i) ui->save_path_combo->setItemText(i, fsutils::toNativePath(QDir(ui->save_path_combo->itemText(i)).absoluteFilePath(single_file_relpath))); } } @@ -706,3 +703,27 @@ void AddNewTorrentDialog::setupTreeview() // Set dialog position setdialogPosition(); } + +void AddNewTorrentDialog::handleDownloadFailed(const QString &url, const QString &reason) +{ + MessageBoxRaised::critical(0, tr("Download Error"), QString("Cannot download %1: %2").arg(url).arg(reason)); + this->deleteLater(); +} + +void AddNewTorrentDialog::handleRedirectedToMagnet(const QString &url, const QString &magnetUri) +{ + Q_UNUSED(url) + if (loadMagnet(magnetUri)) + open(); + else + this->deleteLater(); +} + +void AddNewTorrentDialog::handleDownloadFinished(const QString &url, const QString &filePath) +{ + Q_UNUSED(url) + if (loadTorrent(filePath)) + open(); + else + this->deleteLater(); +} diff --git a/src/gui/addnewtorrentdialog.h b/src/gui/addnewtorrentdialog.h index 8b9d0ee79..e12f8758b 100644 --- a/src/gui/addnewtorrentdialog.h +++ b/src/gui/addnewtorrentdialog.h @@ -35,8 +35,8 @@ #include #include -#include -#include "qtorrenthandle.h" +#include "core/bittorrent/infohash.h" +#include "core/bittorrent/torrentinfo.h" QT_BEGIN_NAMESPACE namespace Ui { @@ -54,8 +54,7 @@ class AddNewTorrentDialog: public QDialog public: ~AddNewTorrentDialog(); - static void showTorrent(const QString& torrent_path, const QString& from_url, QWidget *parent = 0); - static void showMagnet(const QString& torrent_link, QWidget *parent = 0); + static void show(QString source, QWidget *parent = 0); protected: void showEvent(QShowEvent *event); @@ -68,8 +67,11 @@ private slots: void relayout(); void renameSelectedFile(); void setdialogPosition(); - void updateMetadata(const QTorrentHandle& h); + void updateMetadata(const BitTorrent::TorrentInfo &info); void browseButton_clicked(); + void handleDownloadFailed(const QString &url, const QString &reason); + void handleRedirectedToMagnet(const QString &url, const QString &magnetUri); + void handleDownloadFinished(const QString &url, const QString &filePath); protected slots: virtual void accept(); @@ -77,7 +79,7 @@ protected slots: private: explicit AddNewTorrentDialog(QWidget *parent = 0); - bool loadTorrent(const QString& torrent_path, const QString& from_url); + bool loadTorrent(const QString& torrent_path); bool loadMagnet(const QString& magnet_uri); void loadSavePathHistory(); void saveSavePathHistory() const; @@ -92,14 +94,10 @@ private: Ui::AddNewTorrentDialog *ui; TorrentContentFilterModel *m_contentModel; PropListDelegate *m_contentDelegate; - bool m_isMagnet; bool m_hasMetadata; QString m_filePath; - QString m_url; - QString m_hash; - boost::intrusive_ptr m_torrentInfo; - QStringList m_filesPath; - bool m_hasRenamedFile; + BitTorrent::InfoHash m_hash; + BitTorrent::TorrentInfo m_torrentInfo; QShortcut *editHotkey; QByteArray m_headerState; int m_oldIndex; diff --git a/src/gui/advancedsettings.h b/src/gui/advancedsettings.h index 7c6a0e6bb..1e8ad0618 100644 --- a/src/gui/advancedsettings.h +++ b/src/gui/advancedsettings.h @@ -9,7 +9,7 @@ #include #include #include -#include + #include "core/preferences.h" enum AdvSettingsCols {PROPERTY, VALUE}; diff --git a/src/gui/torrentcreator/createtorrent.ui b/src/gui/createtorrent.ui similarity index 100% rename from src/gui/torrentcreator/createtorrent.ui rename to src/gui/createtorrent.ui diff --git a/src/gui/deletionconfirmationdlg.h b/src/gui/deletionconfirmationdlg.h index 8af23a64e..09e061ebf 100644 --- a/src/gui/deletionconfirmationdlg.h +++ b/src/gui/deletionconfirmationdlg.h @@ -35,7 +35,7 @@ #include #include "ui_confirmdeletiondlg.h" #include "core/preferences.h" -#include "iconprovider.h" +#include "guiiconprovider.h" #include "core/misc.h" class DeletionConfirmationDlg : public QDialog, private Ui::confirmDeletionDlg { @@ -49,9 +49,9 @@ class DeletionConfirmationDlg : public QDialog, private Ui::confirmDeletionDlg { else label->setText(tr("Are you sure you want to delete these %1 torrents from the transfer list?", "Are you sure you want to delete these 5 torrents from the transfer list?").arg(QString::number(size))); // Icons - lbl_warn->setPixmap(IconProvider::instance()->getIcon("dialog-warning").pixmap(lbl_warn->height())); + lbl_warn->setPixmap(GuiIconProvider::instance()->getIcon("dialog-warning").pixmap(lbl_warn->height())); lbl_warn->setFixedWidth(lbl_warn->height()); - rememberBtn->setIcon(IconProvider::instance()->getIcon("object-locked")); + rememberBtn->setIcon(GuiIconProvider::instance()->getIcon("object-locked")); move(misc::screenCenter(this)); checkPermDelete->setChecked(Preferences::instance()->deleteTorrentFilesAsDefault()); diff --git a/src/gui/executionlog.cpp b/src/gui/executionlog.cpp index 2498191aa..20ae706d1 100644 --- a/src/gui/executionlog.cpp +++ b/src/gui/executionlog.cpp @@ -36,7 +36,7 @@ #include "executionlog.h" #include "ui_executionlog.h" #include "core/logger.h" -#include "iconprovider.h" +#include "guiiconprovider.h" #include "loglistwidget.h" ExecutionLog::ExecutionLog(QWidget *parent) @@ -47,8 +47,8 @@ ExecutionLog::ExecutionLog(QWidget *parent) { ui->setupUi(this); - ui->tabConsole->setTabIcon(0, IconProvider::instance()->getIcon("view-calendar-journal")); - ui->tabConsole->setTabIcon(1, IconProvider::instance()->getIcon("view-filter")); + ui->tabConsole->setTabIcon(0, GuiIconProvider::instance()->getIcon("view-calendar-journal")); + ui->tabConsole->setTabIcon(1, GuiIconProvider::instance()->getIcon("view-filter")); ui->tabGeneral->layout()->addWidget(m_msgList); ui->tabBan->layout()->addWidget(m_peerList); diff --git a/src/gui/geoip/geoipmanager.cpp b/src/gui/geoip/geoipmanager.cpp index 5559545b8..ef6edeb1e 100644 --- a/src/gui/geoip/geoipmanager.cpp +++ b/src/gui/geoip/geoipmanager.cpp @@ -183,21 +183,21 @@ const char * country_name[253] = "Zambia","Montenegro","Zimbabwe","Anonymous Proxy","Satellite Provider","Other","Aland Islands","Guernsey","Isle of Man","Jersey", "Saint Barthelemy","Saint Martin"}; -QString GeoIPManager::CountryISOCodeToName(const char* iso) { - if (iso[0] == 0) return "N/A"; +QString GeoIPManager::CountryISOCodeToName(const QString &iso) { + if (iso.isEmpty()) return "N/A"; + for (uint i = 0; i < num_countries; ++i) { - if (iso[0] == country_code[i][0] && iso[1] == country_code[i][1]) { + if (iso == country_code[i]) { return QLatin1String(country_name[i]); } } - qDebug("GeoIPManager: Country name resolution failed for: %c%c", iso[0], iso[1]); + qDebug("GeoIPManager: Country name resolution failed for: %s", qPrintable(iso)); return "N/A"; } // http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm -QIcon GeoIPManager::CountryISOCodeToIcon(const char* iso) { - if (iso[0] == 0 || iso[0] == '!') return QIcon(); - const QString isoStr = QString(QByteArray(iso, 2)).toLower(); - return QIcon(":/icons/flags/"+isoStr+".png"); +QIcon GeoIPManager::CountryISOCodeToIcon(const QString &iso) { + if (iso.isEmpty() || (iso[0] == '!')) return QIcon(); + return QIcon(":/icons/flags/" + iso.toLower() + ".png"); } diff --git a/src/gui/geoip/geoipmanager.h b/src/gui/geoip/geoipmanager.h index 7ea828e57..36a115029 100644 --- a/src/gui/geoip/geoipmanager.h +++ b/src/gui/geoip/geoipmanager.h @@ -43,8 +43,8 @@ class GeoIPManager : public QObject { public: static void loadDatabase(libtorrent::session *s); - static QIcon CountryISOCodeToIcon(const char* iso); - static QString CountryISOCodeToName(const char* iso); + static QIcon CountryISOCodeToIcon(const QString &iso); + static QString CountryISOCodeToName(const QString &iso); private: static QString geoipFolder(bool embedded=false); diff --git a/src/gui/gui.pri b/src/gui/gui.pri index 9665fe4f2..f3c72769d 100644 --- a/src/gui/gui.pri +++ b/src/gui/gui.pri @@ -3,7 +3,6 @@ INCLUDEPATH += $$PWD include(lineedit/lineedit.pri) include(properties/properties.pri) include(rss/rss.pri) -include(torrentcreator/torrentcreator.pri) include(geoip/geoip.pri) include(powermanagement/powermanagement.pri) unix:!macx:dbus: include(qtnotify/qtnotify.pri) @@ -32,18 +31,18 @@ HEADERS += \ $$PWD/hidabletabwidget.h \ $$PWD/torrentimportdlg.h \ $$PWD/executionlog.h \ - $$PWD/iconprovider.h \ + $$PWD/guiiconprovider.h \ $$PWD/updownratiodlg.h \ $$PWD/loglistwidget.h \ $$PWD/addnewtorrentdialog.h \ $$PWD/autoexpandabledialog.h \ $$PWD/statsdialog.h \ $$PWD/messageboxraised.h \ - $$PWD/torrentfilterenum.h \ $$PWD/options_imp.h \ $$PWD/advancedsettings.h \ $$PWD/shutdownconfirm.h \ - $$PWD/torrentmodel.h + $$PWD/torrentmodel.h \ + $$PWD/torrentcreatordlg.h SOURCES += \ $$PWD/mainwindow.cpp \ @@ -62,7 +61,7 @@ SOURCES += \ $$PWD/executionlog.cpp \ $$PWD/speedlimitdlg.cpp \ $$PWD/previewselect.cpp \ - $$PWD/iconprovider.cpp \ + $$PWD/guiiconprovider.cpp \ $$PWD/updownratiodlg.cpp \ $$PWD/loglistwidget.cpp \ $$PWD/addnewtorrentdialog.cpp \ @@ -73,7 +72,8 @@ SOURCES += \ $$PWD/trackerlogin.cpp \ $$PWD/options_imp.cpp \ $$PWD/shutdownconfirm.cpp \ - $$PWD/torrentmodel.cpp + $$PWD/torrentmodel.cpp \ + $$PWD/torrentcreatordlg.cpp win32|macx { HEADERS += $$PWD/programupdater.h @@ -94,6 +94,7 @@ FORMS += \ $$PWD/addnewtorrentdialog.ui \ $$PWD/autoexpandabledialog.ui \ $$PWD/statsdialog.ui \ - $$PWD/options.ui + $$PWD/options.ui \ + $$PWD/createtorrent.ui RESOURCES += $$PWD/about.qrc diff --git a/src/gui/guiiconprovider.cpp b/src/gui/guiiconprovider.cpp new file mode 100644 index 000000000..c042f2d7f --- /dev/null +++ b/src/gui/guiiconprovider.cpp @@ -0,0 +1,125 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * Copyright (C) 2011 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. + */ + +#include "guiiconprovider.h" +#include "core/preferences.h" + +#include +#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) +#include +#include +#endif + +GuiIconProvider::GuiIconProvider(QObject *parent) + : IconProvider(parent) +{ + configure(); + connect(Preferences::instance(), SIGNAL(changed()), SLOT(configure())); +} + +GuiIconProvider::~GuiIconProvider() {} + +void GuiIconProvider::initInstance() +{ + if (!m_instance) + m_instance = new GuiIconProvider; +} + +GuiIconProvider *GuiIconProvider::instance() +{ + return static_cast(m_instance); +} + +QIcon GuiIconProvider::getIcon(const QString &iconId) +{ +#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) + if (m_useSystemTheme) { + QIcon icon = QIcon::fromTheme(iconId, QIcon(IconProvider::getIconPath(iconId))); + icon = generateDifferentSizes(icon); + return icon; + } +#endif + return QIcon(IconProvider::getIconPath(iconId)); +} + +// Makes sure the icon is at least available in 16px and 24px size +// It scales the icon from the theme if necessary +// Otherwise, the UI looks broken if the icon is not available +// in the correct size. +#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) +QIcon GuiIconProvider::generateDifferentSizes(const QIcon &icon) +{ + QIcon newIcon; + QList requiredSizes; + requiredSizes << QSize(16, 16) << QSize(24, 24) << QSize(32, 32); + QList modes; + modes << QIcon::Normal << QIcon::Active << QIcon::Selected << QIcon::Disabled; + foreach (const QSize &size, requiredSizes) { + foreach (QIcon::Mode mode, modes) { + QPixmap pixoff = icon.pixmap(size, mode, QIcon::Off); + if (pixoff.height() > size.height()) + pixoff = pixoff.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation); + newIcon.addPixmap(pixoff, mode, QIcon::Off); + QPixmap pixon = icon.pixmap(size, mode, QIcon::On); + if (pixon.height() > size.height()) + pixon = pixoff.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation); + newIcon.addPixmap(pixon, mode, QIcon::On); + } + } + + return newIcon; +} +#endif + +QString GuiIconProvider::getIconPath(const QString &iconId) +{ +#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) + if (m_useSystemTheme) { + QString path = QDir::temp().absoluteFilePath(iconId + ".png"); + if (!QFile::exists(path)) { + const QIcon icon = QIcon::fromTheme(iconId); + if (!icon.isNull()) + icon.pixmap(32).save(path); + else + path = IconProvider::getIconPath(iconId); + } + + return path; + } +#endif + return IconProvider::getIconPath(iconId); +} + + +void GuiIconProvider::configure() +{ +#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) + m_useSystemTheme = Preferences::instance()->useSystemIconTheme(); +#endif +} diff --git a/src/gui/guiiconprovider.h b/src/gui/guiiconprovider.h new file mode 100644 index 000000000..1b7beca1a --- /dev/null +++ b/src/gui/guiiconprovider.h @@ -0,0 +1,62 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * Copyright (C) 2011 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. + */ + +#ifndef GUIICONPROVIDER_H +#define GUIICONPROVIDER_H + +#include "core/iconprovider.h" + +class QIcon; + +class GuiIconProvider : public IconProvider +{ + Q_DISABLE_COPY(GuiIconProvider) + Q_OBJECT + +public: + static void initInstance(); + static GuiIconProvider *instance(); + + QIcon getIcon(const QString &iconId); + QString getIconPath(const QString &iconId); + +private slots: + void configure(); + +private: + explicit GuiIconProvider(QObject *parent = 0); + ~GuiIconProvider(); +#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) + QIcon generateDifferentSizes(const QIcon &icon); + + bool m_useSystemTheme; +#endif +}; + +#endif // GUIICONPROVIDER_H diff --git a/src/gui/loglistwidget.cpp b/src/gui/loglistwidget.cpp index a8dbb8900..ecab80e7a 100644 --- a/src/gui/loglistwidget.cpp +++ b/src/gui/loglistwidget.cpp @@ -35,7 +35,7 @@ #include #include #include "loglistwidget.h" -#include "iconprovider.h" +#include "guiiconprovider.h" LogListWidget::LogListWidget(int max_lines, QWidget *parent) : QListWidget(parent), @@ -44,8 +44,8 @@ LogListWidget::LogListWidget(int max_lines, QWidget *parent) : // Allow multiple selections setSelectionMode(QAbstractItemView::ExtendedSelection); // Context menu - QAction *copyAct = new QAction(IconProvider::instance()->getIcon("edit-copy"), tr("Copy"), this); - QAction *clearAct = new QAction(IconProvider::instance()->getIcon("edit-clear"), tr("Clear"), this); + QAction *copyAct = new QAction(GuiIconProvider::instance()->getIcon("edit-copy"), tr("Copy"), this); + QAction *clearAct = new QAction(GuiIconProvider::instance()->getIcon("edit-clear"), tr("Clear"), this); connect(copyAct, SIGNAL(triggered()), SLOT(copySelection())); connect(clearAct, SIGNAL(triggered()), SLOT(clearLog())); addAction(copyAct); diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index f210ffbbf..cf8fab575 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -55,7 +55,9 @@ #include "addnewtorrentdialog.h" #include "searchengine.h" #include "rss_imp.h" -#include "qbtsession.h" +#include "core/bittorrent/session.h" +#include "core/bittorrent/sessionstatus.h" +#include "core/bittorrent/torrenthandle.h" #include "about_imp.h" #include "trackerlogin.h" #include "options_imp.h" @@ -63,7 +65,6 @@ #include "core/preferences.h" #include "trackerlist.h" #include "peerlistwidget.h" -#include "core/torrentpersistentdata.h" #include "transferlistfilterswidget.h" #include "propertieswidget.h" #include "statusbar.h" @@ -71,11 +72,9 @@ #include "torrentimportdlg.h" #include "torrentmodel.h" #include "executionlog.h" -#include "iconprovider.h" +#include "guiiconprovider.h" #include "core/logger.h" -#ifndef DISABLE_GUI #include "autoexpandabledialog.h" -#endif #ifdef Q_OS_MAC void qt_mac_set_dock_menu(QMenu *menu); #endif @@ -86,13 +85,10 @@ void qt_mac_set_dock_menu(QMenu *menu); #endif #include "powermanagement.h" #ifdef Q_OS_WIN -#include "core/downloadthread.h" +#include "core/net/downloadmanager.h" +#include "core/net/downloadhandler.h" #endif -#include - -using namespace libtorrent; - #define TIME_TRAY_BALLOON 5000 #define PREVENT_SUSPEND_INTERVAL 60000 @@ -128,32 +124,32 @@ MainWindow::MainWindow(QWidget *parent) addToolbarContextMenu(); - actionOpen->setIcon(IconProvider::instance()->getIcon("list-add")); - actionDownload_from_URL->setIcon(IconProvider::instance()->getIcon("insert-link")); + actionOpen->setIcon(GuiIconProvider::instance()->getIcon("list-add")); + actionDownload_from_URL->setIcon(GuiIconProvider::instance()->getIcon("insert-link")); actionSet_upload_limit->setIcon(QIcon(QString::fromUtf8(":/icons/skin/seeding.png"))); actionSet_download_limit->setIcon(QIcon(QString::fromUtf8(":/icons/skin/download.png"))); actionSet_global_upload_limit->setIcon(QIcon(QString::fromUtf8(":/icons/skin/seeding.png"))); actionSet_global_download_limit->setIcon(QIcon(QString::fromUtf8(":/icons/skin/download.png"))); - actionCreate_torrent->setIcon(IconProvider::instance()->getIcon("document-edit")); - actionAbout->setIcon(IconProvider::instance()->getIcon("help-about")); - actionStatistics->setIcon(IconProvider::instance()->getIcon("view-statistics")); - actionBugReport->setIcon(IconProvider::instance()->getIcon("tools-report-bug")); - actionDecreasePriority->setIcon(IconProvider::instance()->getIcon("go-down")); - actionBottomPriority->setIcon(IconProvider::instance()->getIcon("go-bottom")); - actionDelete->setIcon(IconProvider::instance()->getIcon("list-remove")); - actionDocumentation->setIcon(IconProvider::instance()->getIcon("help-contents")); - actionDonate_money->setIcon(IconProvider::instance()->getIcon("wallet-open")); - actionExit->setIcon(IconProvider::instance()->getIcon("application-exit")); - actionIncreasePriority->setIcon(IconProvider::instance()->getIcon("go-up")); - actionTopPriority->setIcon(IconProvider::instance()->getIcon("go-top")); - actionLock_qBittorrent->setIcon(IconProvider::instance()->getIcon("object-locked")); - actionOptions->setIcon(IconProvider::instance()->getIcon("preferences-system")); - actionPause->setIcon(IconProvider::instance()->getIcon("media-playback-pause")); - actionPause_All->setIcon(IconProvider::instance()->getIcon("media-playback-pause")); - actionStart->setIcon(IconProvider::instance()->getIcon("media-playback-start")); - actionStart_All->setIcon(IconProvider::instance()->getIcon("media-playback-start")); - action_Import_Torrent->setIcon(IconProvider::instance()->getIcon("document-import")); - menuAuto_Shutdown_on_downloads_completion->setIcon(IconProvider::instance()->getIcon("application-exit")); + actionCreate_torrent->setIcon(GuiIconProvider::instance()->getIcon("document-edit")); + actionAbout->setIcon(GuiIconProvider::instance()->getIcon("help-about")); + actionStatistics->setIcon(GuiIconProvider::instance()->getIcon("view-statistics")); + actionBugReport->setIcon(GuiIconProvider::instance()->getIcon("tools-report-bug")); + actionDecreasePriority->setIcon(GuiIconProvider::instance()->getIcon("go-down")); + actionBottomPriority->setIcon(GuiIconProvider::instance()->getIcon("go-bottom")); + actionDelete->setIcon(GuiIconProvider::instance()->getIcon("list-remove")); + actionDocumentation->setIcon(GuiIconProvider::instance()->getIcon("help-contents")); + actionDonate_money->setIcon(GuiIconProvider::instance()->getIcon("wallet-open")); + actionExit->setIcon(GuiIconProvider::instance()->getIcon("application-exit")); + actionIncreasePriority->setIcon(GuiIconProvider::instance()->getIcon("go-up")); + actionTopPriority->setIcon(GuiIconProvider::instance()->getIcon("go-top")); + actionLock_qBittorrent->setIcon(GuiIconProvider::instance()->getIcon("object-locked")); + actionOptions->setIcon(GuiIconProvider::instance()->getIcon("preferences-system")); + actionPause->setIcon(GuiIconProvider::instance()->getIcon("media-playback-pause")); + actionPause_All->setIcon(GuiIconProvider::instance()->getIcon("media-playback-pause")); + actionStart->setIcon(GuiIconProvider::instance()->getIcon("media-playback-start")); + actionStart_All->setIcon(GuiIconProvider::instance()->getIcon("media-playback-start")); + action_Import_Torrent->setIcon(GuiIconProvider::instance()->getIcon("document-import")); + menuAuto_Shutdown_on_downloads_completion->setIcon(GuiIconProvider::instance()->getIcon("application-exit")); QMenu *startAllMenu = new QMenu(this); startAllMenu->addAction(actionStart_All); @@ -169,14 +165,13 @@ MainWindow::MainWindow(QWidget *parent) actionLock_qBittorrent->setMenu(lockMenu); // Creating Bittorrent session - connect(QBtSession::instance(), SIGNAL(fullDiskError(QTorrentHandle, QString)), this, SLOT(fullDiskError(QTorrentHandle, QString))); - connect(QBtSession::instance(), SIGNAL(finishedTorrent(QTorrentHandle)), this, SLOT(finishedTorrent(QTorrentHandle))); - connect(QBtSession::instance(), SIGNAL(trackerAuthenticationRequired(QTorrentHandle)), this, SLOT(trackerAuthenticationRequired(QTorrentHandle))); - connect(QBtSession::instance(), SIGNAL(newDownloadedTorrent(QString, QString)), this, SLOT(processDownloadedFiles(QString, QString))); - connect(QBtSession::instance(), SIGNAL(newMagnetLink(QString)), this, SLOT(processNewMagnetLink(QString))); - connect(QBtSession::instance(), SIGNAL(downloadFromUrlFailure(QString, QString)), this, SLOT(handleDownloadFromUrlFailure(QString, QString))); - connect(QBtSession::instance(), SIGNAL(alternativeSpeedsModeChanged(bool)), this, SLOT(updateAltSpeedsBtn(bool))); - connect(QBtSession::instance(), SIGNAL(recursiveTorrentDownloadPossible(QTorrentHandle)), this, SLOT(askRecursiveTorrentDownloadConfirmation(QTorrentHandle))); + connect(BitTorrent::Session::instance(), SIGNAL(fullDiskError(BitTorrent::TorrentHandle *const, QString)), this, SLOT(fullDiskError(BitTorrent::TorrentHandle *const, QString))); + connect(BitTorrent::Session::instance(), SIGNAL(addTorrentFailed(const QString &)), this, SLOT(addTorrentFailed(const QString &))); + connect(BitTorrent::Session::instance(), SIGNAL(torrentFinished(BitTorrent::TorrentHandle *const)), this, SLOT(finishedTorrent(BitTorrent::TorrentHandle *const))); + connect(BitTorrent::Session::instance(), SIGNAL(trackerAuthenticationRequired(BitTorrent::TorrentHandle *const)), this, SLOT(trackerAuthenticationRequired(BitTorrent::TorrentHandle *const))); + connect(BitTorrent::Session::instance(), SIGNAL(downloadFromUrlFailed(QString, QString)), this, SLOT(handleDownloadFromUrlFailure(QString, QString))); + connect(BitTorrent::Session::instance(), SIGNAL(speedLimitModeChanged(bool)), this, SLOT(updateAltSpeedsBtn(bool))); + connect(BitTorrent::Session::instance(), SIGNAL(recursiveTorrentDownloadPossible(BitTorrent::TorrentHandle *const)), this, SLOT(askRecursiveTorrentDownloadConfirmation(BitTorrent::TorrentHandle *const))); qDebug("create tabWidget"); tabs = new HidableTabWidget(this); @@ -200,7 +195,7 @@ MainWindow::MainWindow(QWidget *parent) toolBar->insertWidget(searchFilterAct, spacer); // Transfer List tab - transferList = new TransferListWidget(hSplitter, this, QBtSession::instance()); + transferList = new TransferListWidget(hSplitter, this); properties = new PropertiesWidget(hSplitter, this, transferList); transferListFilters = new TransferListFiltersWidget(vSplitter, transferList); hSplitter->addWidget(transferList); @@ -209,20 +204,18 @@ MainWindow::MainWindow(QWidget *parent) vSplitter->addWidget(hSplitter); vSplitter->setCollapsible(0, true); vSplitter->setCollapsible(1, false); - tabs->addTab(vSplitter, IconProvider::instance()->getIcon("folder-remote"), tr("Transfers")); + tabs->addTab(vSplitter, GuiIconProvider::instance()->getIcon("folder-remote"), tr("Transfers")); connect(search_filter, SIGNAL(textChanged(QString)), transferList, SLOT(applyNameFilter(QString))); connect(hSplitter, SIGNAL(splitterMoved(int, int)), this, SLOT(writeSettings())); connect(vSplitter, SIGNAL(splitterMoved(int, int)), this, SLOT(writeSettings())); - connect(properties, SIGNAL(trackersAdded(const QStringList &, const QString &)), transferListFilters, SLOT(addTrackers(const QStringList &, const QString &))); - connect(properties, SIGNAL(trackersRemoved(const QStringList &, const QString &)), transferListFilters, SLOT(removeTrackers(const QStringList &, const QString &))); - connect(properties, SIGNAL(trackerlessChange(bool, const QString &)), transferListFilters, SLOT(changeTrackerless(bool, const QString &))); - connect(QBtSession::instance(), SIGNAL(trackersAdded(const QStringList &, const QString &)), transferListFilters, SLOT(addTrackers(const QStringList &, const QString &))); - connect(QBtSession::instance(), SIGNAL(trackerlessChange(bool, const QString &)), transferListFilters, SLOT(changeTrackerless(bool, const QString &))); - connect(QBtSession::instance(), SIGNAL(reloadTrackersAndUrlSeeds(const QTorrentHandle &)), properties, SLOT(loadTrackers(const QTorrentHandle &))); - connect(QBtSession::instance(), SIGNAL(trackerSuccess(const QString &, const QString &)), transferListFilters, SIGNAL(trackerSuccess(const QString &, const QString &))); - connect(QBtSession::instance(), SIGNAL(trackerError(const QString &, const QString &)), transferListFilters, SIGNAL(trackerError(const QString &, const QString &))); - connect(QBtSession::instance(), SIGNAL(trackerWarning(const QString &, const QString &)), transferListFilters, SIGNAL(trackerWarning(const QString &, const QString &))); + connect(BitTorrent::Session::instance(), SIGNAL(trackersChanged(BitTorrent::TorrentHandle *const)), properties, SLOT(loadTrackers(BitTorrent::TorrentHandle *const))); + connect(BitTorrent::Session::instance(), SIGNAL(trackersAdded(BitTorrent::TorrentHandle *const, const QList &)), transferListFilters, SLOT(addTrackers(BitTorrent::TorrentHandle *const, const QList &))); + connect(BitTorrent::Session::instance(), SIGNAL(trackersRemoved(BitTorrent::TorrentHandle *const, const QList &)), transferListFilters, SLOT(removeTrackers(BitTorrent::TorrentHandle *const, const QList &))); + connect(BitTorrent::Session::instance(), SIGNAL(trackerlessStateChanged(BitTorrent::TorrentHandle *const, bool)), transferListFilters, SLOT(changeTrackerless(BitTorrent::TorrentHandle *const, bool))); + connect(BitTorrent::Session::instance(), SIGNAL(trackerSuccess(BitTorrent::TorrentHandle *const, const QString &)), transferListFilters, SLOT(trackerSuccess(BitTorrent::TorrentHandle *const, const QString &))); + connect(BitTorrent::Session::instance(), SIGNAL(trackerError(BitTorrent::TorrentHandle *const, const QString &)), transferListFilters, SLOT(trackerError(BitTorrent::TorrentHandle *const, const QString &))); + connect(BitTorrent::Session::instance(), SIGNAL(trackerWarning(BitTorrent::TorrentHandle *const, const QString &)), transferListFilters, SLOT(trackerWarning(BitTorrent::TorrentHandle *const, const QString &))); vboxLayout->addWidget(tabs); @@ -231,9 +224,9 @@ MainWindow::MainWindow(QWidget *parent) // Transfer list slots connect(actionStart, SIGNAL(triggered()), transferList, SLOT(startSelectedTorrents())); - connect(actionStart_All, SIGNAL(triggered()), QBtSession::instance(), SLOT(resumeAllTorrents())); + connect(actionStart_All, SIGNAL(triggered()), transferList, SLOT(resumeAllTorrents())); connect(actionPause, SIGNAL(triggered()), transferList, SLOT(pauseSelectedTorrents())); - connect(actionPause_All, SIGNAL(triggered()), QBtSession::instance(), SLOT(pauseAllTorrents())); + connect(actionPause_All, SIGNAL(triggered()), transferList, SLOT(pauseAllTorrents())); connect(actionDelete, SIGNAL(triggered()), transferList, SLOT(deleteSelectedTorrents())); connect(actionTopPriority, SIGNAL(triggered()), transferList, SLOT(topPrioSelectedTorrents())); connect(actionIncreasePriority, SIGNAL(triggered()), transferList, SLOT(increasePrioSelectedTorrents())); @@ -258,10 +251,8 @@ MainWindow::MainWindow(QWidget *parent) // Configure BT session according to options loadPreferences(false); - // Start connection checking timer - guiUpdater = new QTimer(this); - connect(guiUpdater, SIGNAL(timeout()), this, SLOT(updateGUI())); - guiUpdater->start(2000); + connect(BitTorrent::Session::instance(), SIGNAL(torrentsUpdated()), this, SLOT(updateGUI())); + // Accept drag 'n drops setAcceptDrops(true); createKeyboardShortcuts(); @@ -516,7 +507,7 @@ void MainWindow::displayRSSTab(bool enable) if (!rssWidget) { rssWidget = new RSSImp(tabs); int index_tab = tabs->addTab(rssWidget, tr("RSS")); - tabs->setTabIcon(index_tab, IconProvider::instance()->getIcon("application-rss+xml")); + tabs->setTabIcon(index_tab, GuiIconProvider::instance()->getIcon("application-rss+xml")); } } else if (rssWidget) { @@ -532,7 +523,7 @@ void MainWindow::displaySearchTab(bool enable) // RSS tab if (!searchEngine) { searchEngine = new SearchEngine(this); - tabs->insertTab(1, searchEngine, IconProvider::instance()->getIcon("edit-find"), tr("Search")); + tabs->insertTab(1, searchEngine, GuiIconProvider::instance()->getIcon("edit-find"), tr("Search")); } } else if (searchEngine) { @@ -595,7 +586,6 @@ void MainWindow::cleanup() writeSettings(); delete executable_watcher; - guiUpdater->stop(); if (systrayCreator) systrayCreator->stop(); if (preventTimer) @@ -643,18 +633,21 @@ void MainWindow::balloonClicked() activateWindow(); } -// called when a torrent has finished -void MainWindow::finishedTorrent(const QTorrentHandle& h) const +void MainWindow::addTorrentFailed(const QString &error) const { - if (TorrentPersistentData::instance()->isSeed(h.hash())) - showNotificationBaloon(tr("Download completion"), tr("%1 has finished downloading.", "e.g: xxx.avi has finished downloading.").arg(h.name())); + showNotificationBaloon(tr("Error"), tr("Failed to add torrent: %1").arg(error)); +} + +// called when a torrent has finished +void MainWindow::finishedTorrent(BitTorrent::TorrentHandle *const torrent) const +{ + showNotificationBaloon(tr("Download completion"), tr("%1 has finished downloading.", "e.g: xxx.avi has finished downloading.").arg(torrent->name())); } // Notification when disk is full -void MainWindow::fullDiskError(const QTorrentHandle& h, QString msg) const +void MainWindow::fullDiskError(BitTorrent::TorrentHandle *const torrent, QString msg) const { - if (!h.is_valid()) return; - showNotificationBaloon(tr("I/O Error", "i.e: Input/Output Error"), tr("An I/O error occurred for torrent %1.\n Reason: %2", "e.g: An error occurred for torrent xxx.avi.\n Reason: disk is full.").arg(h.name()).arg(msg)); + showNotificationBaloon(tr("I/O Error", "i.e: Input/Output Error"), tr("An I/O error occurred for torrent %1.\n Reason: %2", "e.g: An error occurred for torrent xxx.avi.\n Reason: disk is full.").arg(torrent->name()).arg(msg)); } void MainWindow::createKeyboardShortcuts() @@ -709,28 +702,21 @@ void MainWindow::displayRSSTab() const // End of keyboard shortcuts slots -void MainWindow::askRecursiveTorrentDownloadConfirmation(const QTorrentHandle &h) +void MainWindow::askRecursiveTorrentDownloadConfirmation(BitTorrent::TorrentHandle *const torrent) { Preferences* const pref = Preferences::instance(); if (pref->recursiveDownloadDisabled()) return; // Get Torrent name - QString torrent_name; - try { - torrent_name = h.name(); - } catch(invalid_handle&) { - return; - } + QString torrent_name = torrent->name(); QMessageBox confirmBox(QMessageBox::Question, tr("Recursive download confirmation"), tr("The torrent %1 contains torrent files, do you want to proceed with their download?").arg(torrent_name)); QPushButton *yes = confirmBox.addButton(tr("Yes"), QMessageBox::YesRole); /*QPushButton *no = */ confirmBox.addButton(tr("No"), QMessageBox::NoRole); QPushButton *never = confirmBox.addButton(tr("Never"), QMessageBox::NoRole); confirmBox.exec(); - if (confirmBox.clickedButton() == 0) return; - if (confirmBox.clickedButton() == yes) { - QBtSession::instance()->recursiveTorrentDownload(h); - return; - } - if (confirmBox.clickedButton() == never) + + if (confirmBox.clickedButton() == yes) + BitTorrent::Session::instance()->recursiveTorrentDownload(torrent->hash()); + else if (confirmBox.clickedButton() == never) pref->disableRecursiveDownload(); } @@ -744,11 +730,11 @@ void MainWindow::on_actionSet_global_upload_limit_triggered() { qDebug("actionSet_global_upload_limit_triggered"); bool ok; - int cur_limit = QBtSession::instance()->getSession()->settings().upload_rate_limit; + int cur_limit = BitTorrent::Session::instance()->uploadRateLimit(); const long new_limit = SpeedLimitDialog::askSpeedLimit(&ok, tr("Global Upload Speed Limit"), cur_limit); if (ok) { qDebug("Setting global upload rate limit to %.1fKb/s", new_limit / 1024.); - QBtSession::instance()->setUploadRateLimit(new_limit); + BitTorrent::Session::instance()->setUploadRateLimit(new_limit); if (new_limit <= 0) Preferences::instance()->setGlobalUploadLimit(-1); else @@ -760,11 +746,11 @@ void MainWindow::on_actionSet_global_download_limit_triggered() { qDebug("actionSet_global_download_limit_triggered"); bool ok; - int cur_limit = QBtSession::instance()->getSession()->settings().download_rate_limit; + int cur_limit = BitTorrent::Session::instance()->downloadRateLimit(); const long new_limit = SpeedLimitDialog::askSpeedLimit(&ok, tr("Global Download Speed Limit"), cur_limit); if (ok) { qDebug("Setting global download rate limit to %.1fKb/s", new_limit / 1024.); - QBtSession::instance()->setDownloadRateLimit(new_limit); + BitTorrent::Session::instance()->setDownloadRateLimit(new_limit); if (new_limit <= 0) Preferences::instance()->setGlobalDownloadLimit(-1); else @@ -904,7 +890,7 @@ void MainWindow::closeEvent(QCloseEvent *e) return; } - if (pref->confirmOnExit() && QBtSession::instance()->hasActiveTorrents()) { + if (pref->confirmOnExit() && BitTorrent::Session::instance()->hasActiveTorrents()) { if (e->spontaneous() || force_exit) { if (!isVisible()) show(); @@ -941,13 +927,10 @@ void MainWindow::closeEvent(QCloseEvent *e) // Display window to create a torrent void MainWindow::on_actionCreate_torrent_triggered() { - if (createTorrentDlg) { + if (createTorrentDlg) createTorrentDlg->setFocus(); - } - else { + else createTorrentDlg = new TorrentCreatorDlg(this); - connect(createTorrentDlg, SIGNAL(torrent_to_seed(QString)), this, SLOT(addTorrent(QString))); - } } bool MainWindow::event(QEvent * e) @@ -1015,33 +998,15 @@ void MainWindow::dropEvent(QDropEvent *event) else { files = event->mimeData()->text().split(QString::fromUtf8("\n")); } + // Add file to download list - Preferences* const pref = Preferences::instance(); - const bool useTorrentAdditionDialog = pref->useAdditionDialog(); + const bool useTorrentAdditionDialog = Preferences::instance()->useAdditionDialog(); foreach (QString file, files) { qDebug("Dropped file %s on download list", qPrintable(file)); - if (misc::isUrl(file)) { - QBtSession::instance()->downloadFromUrl(file); - continue; - } - // Bitcomet or Magnet link - if (file.startsWith("bc://bt/", Qt::CaseInsensitive)) { - qDebug("Converting bc link to magnet link"); - file = misc::bcLinkToMagnet(file); - } - if (file.startsWith("magnet:", Qt::CaseInsensitive)) { - if (useTorrentAdditionDialog) - AddNewTorrentDialog::showMagnet(file, this); - else - QBtSession::instance()->addMagnetUri(file); - } - else { - // Local file - if (useTorrentAdditionDialog) - AddNewTorrentDialog::showTorrent(file, QString(), this); - else - QBtSession::instance()->addTorrent(file); - } + if (useTorrentAdditionDialog) + AddNewTorrentDialog::show(file, this); + else + BitTorrent::Session::instance()->addTorrent(file); } } @@ -1067,22 +1032,22 @@ void MainWindow::on_actionOpen_triggered() Preferences* const pref = Preferences::instance(); // Open File Open Dialog // Note: it is possible to select more than one file - const QStringList pathsList = QFileDialog::getOpenFileNames(0, - tr("Open Torrent Files"), pref->getMainLastDir(), - tr("Torrent Files") + QString::fromUtf8(" (*.torrent)")); - if (!pathsList.empty()) { - const uint listSize = pathsList.size(); - for (uint i = 0; iuseAdditionDialog()) - AddNewTorrentDialog::showTorrent(pathsList.at(i), QString(), this); - else - QBtSession::instance()->addTorrent(pathsList.at(i)); - } - // Save last dir to remember it - QStringList top_dir = fsutils::fromNativePath(pathsList.at(0)).split("/"); - top_dir.removeLast(); - pref->setMainLastDir(fsutils::fromNativePath(top_dir.join("/"))); + const QStringList pathsList = + QFileDialog::getOpenFileNames(0, tr("Open Torrent Files"), pref->getMainLastDir(), + tr("Torrent Files") + QString::fromUtf8(" (*.torrent)")); + const bool useTorrentAdditionDialog = Preferences::instance()->useAdditionDialog(); + foreach (QString file, pathsList) { + qDebug("Dropped file %s on download list", qPrintable(file)); + if (useTorrentAdditionDialog) + AddNewTorrentDialog::show(file, this); + else + BitTorrent::Session::instance()->addTorrent(file); } + + // Save last dir to remember it + QStringList top_dir = fsutils::fromNativePath(pathsList.at(0)).split("/"); + top_dir.removeLast(); + pref->setMainLastDir(fsutils::fromNativePath(top_dir.join("/"))); } void MainWindow::activate() @@ -1094,29 +1059,6 @@ void MainWindow::activate() } } -void MainWindow::addTorrent(QString path) -{ - QBtSession::instance()->addTorrent(path); -} - -void MainWindow::processDownloadedFiles(QString path, QString url) -{ - Preferences* const pref = Preferences::instance(); - if (pref->useAdditionDialog()) - AddNewTorrentDialog::showTorrent(path, url, this); - else - QBtSession::instance()->addTorrent(path, false, url); -} - -void MainWindow::processNewMagnetLink(const QString& link) -{ - Preferences* const pref = Preferences::instance(); - if (pref->useAdditionDialog()) - AddNewTorrentDialog::showMagnet(link, this); - else - QBtSession::instance()->addMagnetUri(link); -} - void MainWindow::optionsSaved() { loadPreferences(); @@ -1175,8 +1117,6 @@ void MainWindow::loadPreferences(bool configure_session) m_pwr->setActivityState(false); } - const uint new_refreshInterval = pref->getRefreshInterval(); - transferList->setRefreshInterval(new_refreshInterval); transferList->setAlternatingRowColors(pref->useAlternatingRowColors()); properties->getFilesList()->setAlternatingRowColors(pref->useAlternatingRowColors()); properties->getTrackerList()->setAlternatingRowColors(pref->useAlternatingRowColors()); @@ -1209,11 +1149,6 @@ void MainWindow::loadPreferences(bool configure_session) // Torrent properties properties->reloadPreferences(); - // Icon provider -#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) - IconProvider::instance()->useSystemIconTheme(pref->useSystemIconTheme()); -#endif - #if defined(Q_OS_WIN) || defined(Q_OS_MAC) if (pref->isUpdateCheckEnabled()) checkProgramUpdate(); @@ -1224,7 +1159,7 @@ void MainWindow::loadPreferences(bool configure_session) qDebug("GUI settings loaded"); } -void MainWindow::addUnauthenticatedTracker(const QPair &tracker) +void MainWindow::addUnauthenticatedTracker(const QPair &tracker) { // Trackers whose authentication was cancelled if (unauthenticated_trackers.indexOf(tracker) < 0) @@ -1232,16 +1167,18 @@ void MainWindow::addUnauthenticatedTracker(const QPair & } // Called when a tracker requires authentication -void MainWindow::trackerAuthenticationRequired(const QTorrentHandle& h) +void MainWindow::trackerAuthenticationRequired(BitTorrent::TorrentHandle *const torrent) { - if (unauthenticated_trackers.indexOf(QPair(h, h.current_tracker())) < 0) + if (unauthenticated_trackers.indexOf(QPair(torrent, torrent->currentTracker())) < 0) // Tracker login - new trackerLogin(this, h); + new trackerLogin(this, torrent); } // Check connection status and display right icon void MainWindow::updateGUI() { + BitTorrent::SessionStatus status = BitTorrent::Session::instance()->status(); + // update global informations if (systrayIcon) { #if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) @@ -1249,21 +1186,21 @@ void MainWindow::updateGUI() html += "qBittorrent"; html += ""; html += "
"; - html += " " + tr("DL speed: %1", "e.g: Download speed: 10 KiB/s").arg(misc::friendlyUnit(QBtSession::instance()->getPayloadDownloadRate(), true)); + html += " " + tr("DL speed: %1", "e.g: Download speed: 10 KiB/s").arg(misc::friendlyUnit(status.payloadDownloadRate(), true)); html += "
"; html += "
"; - html += " " + tr("UP speed: %1", "e.g: Upload speed: 10 KiB/s").arg(misc::friendlyUnit(QBtSession::instance()->getPayloadUploadRate(), true)); + html += " " + tr("UP speed: %1", "e.g: Upload speed: 10 KiB/s").arg(misc::friendlyUnit(status.payloadUploadRate(), true)); html += "
"; #else // OSes such as Windows do not support html here - QString html = tr("DL speed: %1", "e.g: Download speed: 10 KiB/s").arg(misc::friendlyUnit(QBtSession::instance()->getPayloadDownloadRate(), true)); + QString html = tr("DL speed: %1", "e.g: Download speed: 10 KiB/s").arg(misc::friendlyUnit(status.payloadDownloadRate(), true)); html += "\n"; - html += tr("UP speed: %1", "e.g: Upload speed: 10 KiB/s").arg(misc::friendlyUnit(QBtSession::instance()->getPayloadUploadRate(), true)); + html += tr("UP speed: %1", "e.g: Upload speed: 10 KiB/s").arg(misc::friendlyUnit(status.payloadUploadRate(), true)); #endif systrayIcon->setToolTip(html); // tray icon } if (displaySpeedInTitle) - setWindowTitle(tr("[D: %1, U: %2] qBittorrent %3", "D = Download; U = Upload; %3 is qBittorrent version").arg(misc::friendlyUnit(QBtSession::instance()->getPayloadDownloadRate(), true)).arg(misc::friendlyUnit(QBtSession::instance()->getPayloadUploadRate(), true)).arg(QString::fromUtf8(VERSION))); + setWindowTitle(tr("[D: %1, U: %2] qBittorrent %3", "D = Download; U = Upload; %3 is qBittorrent version").arg(misc::friendlyUnit(status.payloadDownloadRate(), true)).arg(misc::friendlyUnit(status.payloadUploadRate(), true)).arg(QString::fromUtf8(VERSION))); } void MainWindow::showNotificationBaloon(QString title, QString msg) const @@ -1302,26 +1239,16 @@ void MainWindow::showNotificationBaloon(QString title, QString msg) const void MainWindow::downloadFromURLList(const QStringList& url_list) { - Preferences* const pref = Preferences::instance(); - const bool useTorrentAdditionDialog = pref->useAdditionDialog(); + const bool useTorrentAdditionDialog = Preferences::instance()->useAdditionDialog(); foreach (QString url, url_list) { - if (url.startsWith("bc://bt/", Qt::CaseInsensitive)) { - qDebug("Converting bc link to magnet link"); - url = misc::bcLinkToMagnet(url); - } if ((url.size() == 40 && !url.contains(QRegExp("[^0-9A-Fa-f]"))) || (url.size() == 32 && !url.contains(QRegExp("[^2-7A-Za-z]")))) url = "magnet:?xt=urn:btih:" + url; - if (url.startsWith("magnet:", Qt::CaseInsensitive)) { - if (useTorrentAdditionDialog) - AddNewTorrentDialog::showMagnet(url, this); - else - QBtSession::instance()->addMagnetUri(url); - } - else if (url.startsWith("http://", Qt::CaseInsensitive) || url.startsWith("https://", Qt::CaseInsensitive) - || url.startsWith("ftp://", Qt::CaseInsensitive)) { - QBtSession::instance()->downloadFromUrl(url); - } + + if (useTorrentAdditionDialog) + AddNewTorrentDialog::show(url, this); + else + BitTorrent::Session::instance()->addTorrent(url); } } @@ -1549,7 +1476,7 @@ void MainWindow::on_actionExecution_Logs_triggered(bool checked) Q_ASSERT(!m_executionLog); m_executionLog = new ExecutionLog(tabs); int index_tab = tabs->addTab(m_executionLog, tr("Execution Log")); - tabs->setTabIcon(index_tab, IconProvider::instance()->getIcon("view-calendar-journal")); + tabs->setTabIcon(index_tab, GuiIconProvider::instance()->getIcon("view-calendar-journal")); } else if (m_executionLog) { delete m_executionLog; @@ -1583,7 +1510,7 @@ void MainWindow::on_actionAutoShutdown_system_toggled(bool enabled) void MainWindow::checkForActiveTorrents() { - m_pwr->setActivityState(transferList->getSourceModel()->inhibitSystem()); + m_pwr->setActivityState(BitTorrent::Session::instance()->hasActiveTorrents()); } QIcon MainWindow::getSystrayIcon() const @@ -1650,21 +1577,20 @@ void MainWindow::installPython() { setCursor(QCursor(Qt::WaitCursor)); // Download python - DownloadThread *pydownloader = new DownloadThread(this); - connect(pydownloader, SIGNAL(downloadFinished(QString,QString)), this, SLOT(pythonDownloadSuccess(QString,QString))); - connect(pydownloader, SIGNAL(downloadFailure(QString,QString)), this, SLOT(pythonDownloadFailure(QString,QString))); - pydownloader->downloadUrl("http://python.org/ftp/python/2.7.3/python-2.7.3.msi"); + Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl("http://python.org/ftp/python/2.7.3/python-2.7.3.msi"); + connect(handler, SIGNAL(downloadFinished(QString, QString)), this, SLOT(pythonDownloadSuccess(QString, QString))); + connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(pythonDownloadFailure(QString, QString))); } -void MainWindow::pythonDownloadSuccess(QString url, QString file_path) +void MainWindow::pythonDownloadSuccess(const QString &url, const QString &filePath) { + Q_UNUSED(url) setCursor(QCursor(Qt::ArrowCursor)); - Q_UNUSED(url); - QFile::rename(file_path, file_path + ".msi"); + QFile::rename(filePath, filePath + ".msi"); QProcess installer; qDebug("Launching Python installer in passive mode..."); - installer.start("msiexec.exe /passive /i " + fsutils::toNativePath(file_path) + ".msi"); + installer.start("msiexec.exe /passive /i " + fsutils::toNativePath(filePath) + ".msi"); // Wait for setup to complete installer.waitForFinished(); @@ -1672,21 +1598,19 @@ void MainWindow::pythonDownloadSuccess(QString url, QString file_path) qDebug("Installer stderr: %s", installer.readAllStandardError().data()); qDebug("Setup should be complete!"); // Delete temp file - fsutils::forceRemove(file_path); + fsutils::forceRemove(filePath); // Reload search engine has_python = addPythonPathToEnv(); if (has_python) { actionSearch_engine->setChecked(true); displaySearchTab(true); } - sender()->deleteLater(); } -void MainWindow::pythonDownloadFailure(QString url, QString error) +void MainWindow::pythonDownloadFailure(const QString &url, const QString &error) { - Q_UNUSED(url); + Q_UNUSED(url) setCursor(QCursor(Qt::ArrowCursor)); QMessageBox::warning(this, tr("Download error"), tr("Python setup could not be downloaded, reason: %1.\nPlease install it manually.").arg(error)); - sender()->deleteLater(); } #endif diff --git a/src/gui/mainwindow.h b/src/gui/mainwindow.h index c3ebdd3a9..7adcb0a87 100644 --- a/src/gui/mainwindow.h +++ b/src/gui/mainwindow.h @@ -35,10 +35,8 @@ #include #include #include "ui_mainwindow.h" -#include "qtorrenthandle.h" #include "statsdialog.h" -class QBtSession; class downloadFromURL; class SearchEngine; class RSSImp; @@ -64,6 +62,11 @@ class QTabWidget; class QTimer; QT_END_NAMESPACE +namespace BitTorrent +{ + class TorrentHandle; +} + class MainWindow: public QMainWindow, private Ui::MainWindow { Q_OBJECT @@ -79,7 +82,7 @@ public: PropertiesWidget *getProperties() const { return properties; } public slots: - void trackerAuthenticationRequired(const QTorrentHandle& h); + void trackerAuthenticationRequired(BitTorrent::TorrentHandle *const torrent); void setTabText(int index, QString text) const; void showNotificationBaloon(QString title, QString msg) const; void downloadFromURLList(const QStringList& urls); @@ -101,7 +104,7 @@ protected slots: void readSettings(); void on_actionExit_triggered(); void createTrayIcon(); - void fullDiskError(const QTorrentHandle& h, QString msg) const; + void fullDiskError(BitTorrent::TorrentHandle *const torrent, QString msg) const; void handleDownloadFromUrlFailure(QString, QString) const; void createSystrayDelayed(); void tab_changed(int); @@ -125,12 +128,10 @@ protected slots: void on_actionOpen_triggered(); void updateGUI(); void loadPreferences(bool configure_session = true); - void addTorrent(QString path); - void addUnauthenticatedTracker(const QPair &tracker); - void processDownloadedFiles(QString path, QString url); - void processNewMagnetLink(const QString& link); - void finishedTorrent(const QTorrentHandle& h) const; - void askRecursiveTorrentDownloadConfirmation(const QTorrentHandle &h); + void addUnauthenticatedTracker(const QPair &tracker); + void addTorrentFailed(const QString &error) const; + void finishedTorrent(BitTorrent::TorrentHandle *const torrent) const; + void askRecursiveTorrentDownloadConfirmation(BitTorrent::TorrentHandle *const torrent); // Options slots void on_actionOptions_triggered(); void optionsSaved(); @@ -156,18 +157,17 @@ private: void installPython(); private slots: - void pythonDownloadSuccess(QString url, QString file_path); - void pythonDownloadFailure(QString url, QString error); + void pythonDownloadSuccess(const QString &url, const QString &filePath); + void pythonDownloadFailure(const QString &url, const QString &error); #endif void addToolbarContextMenu(); private: QFileSystemWatcher *executable_watcher; // Bittorrent - QList > unauthenticated_trackers; // Still needed? + QList > unauthenticated_trackers; // Still needed? // GUI related bool m_posInitialized; - QTimer *guiUpdater; QTabWidget *tabs; StatusBar *status_bar; QPointer options; diff --git a/src/gui/options_imp.cpp b/src/gui/options_imp.cpp index d2da8e3df..17c6492ea 100644 --- a/src/gui/options_imp.cpp +++ b/src/gui/options_imp.cpp @@ -45,9 +45,9 @@ #include "core/preferences.h" #include "core/fs_utils.h" #include "advancedsettings.h" -#include "core/scannedfoldersmodel.h" -#include "qbtsession.h" -#include "iconprovider.h" +#include "core/scanfoldersmodel.h" +#include "core/bittorrent/session.h" +#include "guiiconprovider.h" #include "core/net/dnsupdater.h" #ifndef QT_NO_OPENSSL @@ -55,8 +55,6 @@ #include #endif -using namespace libtorrent; - // Constructor options_imp::options_imp(QWidget *parent): QDialog(parent), m_refreshingIpFilter(false) { @@ -65,18 +63,18 @@ options_imp::options_imp(QWidget *parent): setAttribute(Qt::WA_DeleteOnClose); setModal(true); // Icons - tabSelection->item(TAB_UI)->setIcon(IconProvider::instance()->getIcon("preferences-desktop")); - tabSelection->item(TAB_BITTORRENT)->setIcon(IconProvider::instance()->getIcon("preferences-system-network")); - tabSelection->item(TAB_CONNECTION)->setIcon(IconProvider::instance()->getIcon("network-wired")); - tabSelection->item(TAB_DOWNLOADS)->setIcon(IconProvider::instance()->getIcon("download")); - tabSelection->item(TAB_SPEED)->setIcon(IconProvider::instance()->getIcon("chronometer")); + tabSelection->item(TAB_UI)->setIcon(GuiIconProvider::instance()->getIcon("preferences-desktop")); + tabSelection->item(TAB_BITTORRENT)->setIcon(GuiIconProvider::instance()->getIcon("preferences-system-network")); + tabSelection->item(TAB_CONNECTION)->setIcon(GuiIconProvider::instance()->getIcon("network-wired")); + tabSelection->item(TAB_DOWNLOADS)->setIcon(GuiIconProvider::instance()->getIcon("download")); + tabSelection->item(TAB_SPEED)->setIcon(GuiIconProvider::instance()->getIcon("chronometer")); #ifndef DISABLE_WEBUI - tabSelection->item(TAB_WEBUI)->setIcon(IconProvider::instance()->getIcon("network-server")); + tabSelection->item(TAB_WEBUI)->setIcon(GuiIconProvider::instance()->getIcon("network-server")); #else tabSelection->item(TAB_WEBUI)->setHidden(true); #endif - tabSelection->item(TAB_ADVANCED)->setIcon(IconProvider::instance()->getIcon("preferences-other")); - IpFilterRefreshBtn->setIcon(IconProvider::instance()->getIcon("view-refresh")); + tabSelection->item(TAB_ADVANCED)->setIcon(GuiIconProvider::instance()->getIcon("preferences-other")); + IpFilterRefreshBtn->setIcon(GuiIconProvider::instance()->getIcon("view-refresh")); hsplitter->setCollapsible(0, false); hsplitter->setCollapsible(1, false); @@ -494,7 +492,7 @@ void options_imp::saveOptions() { advancedSettings->saveAdvancedSettings(); // Assume that user changed multiple settings // so it's best to save immediately - pref->save(); + pref->apply(); } bool options_imp::isFilteringEnabled() const { @@ -1250,9 +1248,9 @@ void options_imp::on_IpFilterRefreshBtn_clicked() { pref->setFilteringEnabled(true); pref->setFilter(getFilter()); // Force refresh - connect(QBtSession::instance(), SIGNAL(ipFilterParsed(bool, int)), SLOT(handleIPFilterParsed(bool, int))); + connect(BitTorrent::Session::instance(), SIGNAL(ipFilterParsed(bool, int)), SLOT(handleIPFilterParsed(bool, int))); setCursor(QCursor(Qt::WaitCursor)); - QBtSession::instance()->enableIPFilter(getFilter(), true); + BitTorrent::Session::instance()->enableIPFilter(getFilter(), true); } void options_imp::handleIPFilterParsed(bool error, int ruleCount) @@ -1264,7 +1262,7 @@ void options_imp::handleIPFilterParsed(bool error, int ruleCount) QMessageBox::information(this, tr("Successfully refreshed"), tr("Successfully parsed the provided IP filter: %1 rules were applied.", "%1 is a number").arg(ruleCount)); } m_refreshingIpFilter = false; - disconnect(QBtSession::instance(), SIGNAL(ipFilterParsed(bool, int)), this, SLOT(handleIPFilterParsed(bool, int))); + disconnect(BitTorrent::Session::instance(), SIGNAL(ipFilterParsed(bool, int)), this, SLOT(handleIPFilterParsed(bool, int))); } QString options_imp::languageToLocalizedString(const QLocale &locale) diff --git a/src/gui/previewselect.cpp b/src/gui/previewselect.cpp index 42c01fbf4..e6d0f7b5f 100644 --- a/src/gui/previewselect.cpp +++ b/src/gui/previewselect.cpp @@ -33,16 +33,16 @@ #include #include -#include -#include - #include "core/misc.h" #include "previewlistdelegate.h" #include "previewselect.h" #include "core/fs_utils.h" #include "core/preferences.h" -PreviewSelect::PreviewSelect(QWidget* parent, QTorrentHandle h): QDialog(parent), h(h) { +PreviewSelect::PreviewSelect(QWidget* parent, BitTorrent::TorrentHandle *const torrent) + : QDialog(parent) + , m_torrent(torrent) +{ setupUi(this); setAttribute(Qt::WA_DeleteOnClose); Preferences* const pref = Preferences::instance(); @@ -58,11 +58,10 @@ PreviewSelect::PreviewSelect(QWidget* parent, QTorrentHandle h): QDialog(parent) previewList->header()->resizeSection(0, 200); previewList->setAlternatingRowColors(pref->useAlternatingRowColors()); // Fill list in - std::vector fp; - h.file_progress(fp); - unsigned int nbFiles = h.num_files(); - for (unsigned int i=0; i fp = torrent->filesProgress(); + uint nbFiles = torrent->filesCount(); + for (uint i = 0; i < nbFiles; ++i) { + QString fileName = torrent->fileName(i); if (fileName.endsWith(".!qB")) fileName.chop(4); QString extension = fsutils::fileExtension(fileName).toUpper(); @@ -70,8 +69,8 @@ PreviewSelect::PreviewSelect(QWidget* parent, QTorrentHandle h): QDialog(parent) int row = previewListModel->rowCount(); previewListModel->insertRow(row); previewListModel->setData(previewListModel->index(row, NAME), QVariant(fileName)); - previewListModel->setData(previewListModel->index(row, SIZE), QVariant((qlonglong)h.filesize_at(i))); - previewListModel->setData(previewListModel->index(row, PROGRESS), QVariant((double)fp[i]/h.filesize_at(i))); + previewListModel->setData(previewListModel->index(row, SIZE), QVariant(torrent->fileSize(i))); + previewListModel->setData(previewListModel->index(row, PROGRESS), QVariant(fp[i])); previewListModel->setData(previewListModel->index(row, FILE_INDEX), QVariant(i)); } } @@ -105,9 +104,9 @@ void PreviewSelect::on_previewButton_clicked() { QModelIndexList selectedIndexes = previewList->selectionModel()->selectedRows(FILE_INDEX); if (selectedIndexes.size() == 0) return; // Flush data - h.flush_cache(); + m_torrent->flushCache(); - QStringList absolute_paths(h.absolute_files_path()); + QStringList absolute_paths(m_torrent->absoluteFilePaths()); //only one file should be selected QString path = absolute_paths.at(selectedIndexes.at(0).data().toInt()); // File diff --git a/src/gui/previewselect.h b/src/gui/previewselect.h index 71bbca654..023eef08e 100644 --- a/src/gui/previewselect.h +++ b/src/gui/previewselect.h @@ -34,7 +34,7 @@ #include #include #include "ui_preview.h" -#include "qtorrenthandle.h" +#include "core/bittorrent/torrenthandle.h" class PreviewListDelegate; @@ -49,7 +49,7 @@ public: enum PreviewColumn { NAME, SIZE, PROGRESS, FILE_INDEX, NB_COLUMNS }; public: - PreviewSelect(QWidget* parent, QTorrentHandle h); + PreviewSelect(QWidget* parent, BitTorrent::TorrentHandle *const torrent); ~PreviewSelect(); signals: @@ -62,8 +62,7 @@ protected slots: private: QStandardItemModel *previewListModel; PreviewListDelegate *listDelegate; - QTorrentHandle h; - + BitTorrent::TorrentHandle *const m_torrent; }; #endif diff --git a/src/gui/properties/downloadedpiecesbar.cpp b/src/gui/properties/downloadedpiecesbar.cpp index fad66e870..e0764507e 100644 --- a/src/gui/properties/downloadedpiecesbar.cpp +++ b/src/gui/properties/downloadedpiecesbar.cpp @@ -28,28 +28,25 @@ * Contact : chris@qbittorrent.org */ +#include #include "downloadedpiecesbar.h" -//#include - DownloadedPiecesBar::DownloadedPiecesBar(QWidget *parent): QWidget(parent) { setFixedHeight(BAR_HEIGHT); - bg_color = 0xffffff; - border_color = palette().color(QPalette::Dark).rgb(); - piece_color = 0x0000ff; - piece_color_dl = 0x00d000; + m_bgColor = 0xffffff; + m_borderColor = palette().color(QPalette::Dark).rgb(); + m_pieceColor = 0x0000ff; + m_dlPieceColor = 0x00d000; updatePieceColors(); } -std::vector DownloadedPiecesBar::bitfieldToFloatVector(const libtorrent::bitfield &vecin, int reqSize) +QVector DownloadedPiecesBar::bitfieldToFloatVector(const QBitArray &vecin, int reqSize) { - std::vector result(reqSize, 0.0); - - if (vecin.empty()) - return result; + QVector result(reqSize, 0.0); + if (vecin.isEmpty()) return result; const float ratio = vecin.size() / (float)reqSize; @@ -154,18 +151,18 @@ void DownloadedPiecesBar::updateImage() // qDebug() << "updateImage"; QImage image2(width() - 2, 1, QImage::Format_RGB888); - if (pieces.empty()) { + if (m_pieces.isEmpty()) { image2.fill(0xffffff); - image = image2; + m_image = image2; update(); return; } - std::vector scaled_pieces = bitfieldToFloatVector(pieces, image2.width()); - std::vector scaled_pieces_dl = bitfieldToFloatVector(pieces_dl, image2.width()); + QVector scaled_pieces = bitfieldToFloatVector(m_pieces, image2.width()); + QVector scaled_pieces_dl = bitfieldToFloatVector(m_downloadedPieces, image2.width()); // filling image - for (unsigned int x = 0; x < scaled_pieces.size(); ++x) + for (int x = 0; x < scaled_pieces.size(); ++x) { float pieces2_val = scaled_pieces.at(x); float pieces2_val_dl = scaled_pieces_dl.at(x); @@ -174,23 +171,23 @@ void DownloadedPiecesBar::updateImage() float fill_ratio = pieces2_val + pieces2_val_dl; float ratio = pieces2_val_dl / fill_ratio; - int mixedColor = mixTwoColors(piece_color, piece_color_dl, ratio); - mixedColor = mixTwoColors(bg_color, mixedColor, fill_ratio); + int mixedColor = mixTwoColors(m_pieceColor, m_dlPieceColor, ratio); + mixedColor = mixTwoColors(m_bgColor, mixedColor, fill_ratio); image2.setPixel(x, 0, mixedColor); } else { - image2.setPixel(x, 0, piece_colors[pieces2_val * 255]); + image2.setPixel(x, 0, m_pieceColors[pieces2_val * 255]); } } - image = image2; + m_image = image2; } -void DownloadedPiecesBar::setProgress(const libtorrent::bitfield &bf, const libtorrent::bitfield &bf_dl) +void DownloadedPiecesBar::setProgress(const QBitArray &pieces, const QBitArray &downloadedPieces) { - pieces = libtorrent::bitfield(bf); - pieces_dl = libtorrent::bitfield(bf_dl); + m_pieces = pieces; + m_downloadedPieces = downloadedPieces; updateImage(); update(); @@ -198,16 +195,16 @@ void DownloadedPiecesBar::setProgress(const libtorrent::bitfield &bf, const libt void DownloadedPiecesBar::updatePieceColors() { - piece_colors = std::vector(256); + m_pieceColors = QVector(256); for (int i = 0; i < 256; ++i) { float ratio = (i / 255.0); - piece_colors[i] = mixTwoColors(bg_color, piece_color, ratio); + m_pieceColors[i] = mixTwoColors(m_bgColor, m_pieceColor, ratio); } } void DownloadedPiecesBar::clear() { - image = QImage(); + m_image = QImage(); update(); } @@ -215,30 +212,30 @@ void DownloadedPiecesBar::paintEvent(QPaintEvent *) { QPainter painter(this); QRect imageRect(1, 1, width() - 2, height() - 2); - if (image.isNull()) + if (m_image.isNull()) { painter.setBrush(Qt::white); painter.drawRect(imageRect); } else { - if (image.width() != imageRect.width()) + if (m_image.width() != imageRect.width()) updateImage(); - painter.drawImage(imageRect, image); + painter.drawImage(imageRect, m_image); } QPainterPath border; border.addRect(0, 0, width() - 1, height() - 1); - painter.setPen(border_color); + painter.setPen(m_borderColor); painter.drawPath(border); } void DownloadedPiecesBar::setColors(int background, int border, int complete, int incomplete) { - bg_color = background; - border_color = border; - piece_color = complete; - piece_color_dl = incomplete; + m_bgColor = background; + m_borderColor = border; + m_pieceColor = complete; + m_dlPieceColor = incomplete; updatePieceColors(); updateImage(); diff --git a/src/gui/properties/downloadedpiecesbar.h b/src/gui/properties/downloadedpiecesbar.h index e4f659fca..052134e3f 100644 --- a/src/gui/properties/downloadedpiecesbar.h +++ b/src/gui/properties/downloadedpiecesbar.h @@ -34,8 +34,8 @@ #include #include #include -#include -#include +#include +#include #define BAR_HEIGHT 18 @@ -44,28 +44,28 @@ class DownloadedPiecesBar: public QWidget { Q_DISABLE_COPY(DownloadedPiecesBar) private: - QImage image; + QImage m_image; // I used values, bacause it should be possible to change colors in runtime // background color - int bg_color; + int m_bgColor; // border color - int border_color; + int m_borderColor; // complete piece color - int piece_color; + int m_pieceColor; // incomplete piece color - int piece_color_dl; + int m_dlPieceColor; // buffered 256 levels gradient from bg_color to piece_color - std::vector piece_colors; + QVector m_pieceColors; // last used bitfields, uses to better resize redraw // TODO: make a diff pieces to new pieces and update only changed pixels, speedup when update > 20x faster - libtorrent::bitfield pieces; - libtorrent::bitfield pieces_dl; + QBitArray m_pieces; + QBitArray m_downloadedPieces; // scale bitfield vector to float vector - std::vector bitfieldToFloatVector(const libtorrent::bitfield &vecin, int reqSize); + QVector bitfieldToFloatVector(const QBitArray &vecin, int reqSize); // mix two colors by light model, ratio <0, 1> int mixTwoColors(int &rgb1, int &rgb2, float ratio); // draw new image and replace actual image @@ -74,7 +74,7 @@ private: public: DownloadedPiecesBar(QWidget *parent); - void setProgress(const libtorrent::bitfield &bf, const libtorrent::bitfield &bf_dl); + void setProgress(const QBitArray &m_pieces, const QBitArray &downloadedPieces); void updatePieceColors(); void clear(); diff --git a/src/gui/properties/peeraddition.h b/src/gui/properties/peeraddition.h index b4f778f40..c415268c5 100644 --- a/src/gui/properties/peeraddition.h +++ b/src/gui/properties/peeraddition.h @@ -1,5 +1,5 @@ /* - * Bittorrent Client using Qt4 and libtorrent. + * Bittorrent Client using Qt and libtorrent. * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -34,72 +34,62 @@ #include #include #include -#include + +#include "core/bittorrent/peerinfo.h" #include "ui_peer.h" -#include -#include - -#include -#if BOOST_VERSION < 103500 -#include -#else -#include -#endif - -class PeerAdditionDlg: public QDialog, private Ui::addPeerDialog { - Q_OBJECT +class PeerAdditionDlg: public QDialog, private Ui::addPeerDialog +{ + Q_OBJECT public: - PeerAdditionDlg(QWidget *parent=0): QDialog(parent) { - setupUi(this); - connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); - connect(buttonBox, SIGNAL(accepted()), this, SLOT(validateInput())); - } - - ~PeerAdditionDlg() {} - - QString getIP() const { - QHostAddress ip(lineIP->text()); - if (!ip.isNull()) { - // QHostAddress::toString() cleans up the IP for libtorrent - return ip.toString(); + PeerAdditionDlg(QWidget *parent=0) + : QDialog(parent) + { + setupUi(this); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + connect(buttonBox, SIGNAL(accepted()), this, SLOT(validateInput())); } - return QString(); - } - unsigned short getPort() const { - return spinPort->value(); - } - - static libtorrent::asio::ip::tcp::endpoint askForPeerEndpoint() { - libtorrent::asio::ip::tcp::endpoint ep; - PeerAdditionDlg dlg; - if (dlg.exec() == QDialog::Accepted) { - QString ip = dlg.getIP(); - boost::system::error_code ec; - libtorrent::address addr = libtorrent::address::from_string(qPrintable(ip), ec); - if (ec) { - qDebug("Unable to parse the provided IP: %s", qPrintable(ip)); - return ep; - } - qDebug("Provided IP is correct"); - ep = libtorrent::asio::ip::tcp::endpoint(addr, dlg.getPort()); + ~PeerAdditionDlg() + { + } + + QHostAddress getAddress() const + { + return QHostAddress(lineIP->text()); + } + + ushort getPort() const + { + return spinPort->value(); + } + + static BitTorrent::PeerAddress askForPeerAddress() + { + BitTorrent::PeerAddress addr; + + PeerAdditionDlg dlg; + if (dlg.exec() == QDialog::Accepted) { + addr.ip = dlg.getAddress(); + if (addr.ip.isNull()) + qDebug("Unable to parse the provided IP."); + else + qDebug("Provided IP is correct"); + addr.port = dlg.getPort(); + } + + return addr; } - return ep; - } protected slots: - void validateInput() { - if (getIP().isEmpty()) { - QMessageBox::warning(this, tr("Invalid IP"), - tr("The IP you provided is invalid."), - QMessageBox::Ok); - } else { - accept(); + void validateInput() { + if (getAddress().isNull()) + QMessageBox::warning(this, tr("Invalid IP"), tr("The IP you provided is invalid."), QMessageBox::Ok); + else + accept(); } - } }; #endif // PEERADDITION_H diff --git a/src/gui/properties/peerlistwidget.cpp b/src/gui/properties/peerlistwidget.cpp index aa7346448..e45102634 100644 --- a/src/gui/properties/peerlistwidget.cpp +++ b/src/gui/properties/peerlistwidget.cpp @@ -37,19 +37,17 @@ #include "geoipmanager.h" #include "peeraddition.h" #include "speedlimitdlg.h" -#include "iconprovider.h" -#include "qtorrenthandle.h" +#include "guiiconprovider.h" +#include "core/bittorrent/torrenthandle.h" +#include "core/bittorrent/peerinfo.h" #include "core/logger.h" + #include #include #include #include #include #include -#include -#include - -using namespace libtorrent; PeerListWidget::PeerListWidget(PropertiesWidget *parent): QTreeView(parent), m_properties(parent), m_displayFlags(false) @@ -149,8 +147,9 @@ void PeerListWidget::showPeerListMenu(const QPoint&) { QMenu menu; bool empty_menu = true; - QTorrentHandle h = m_properties->getCurrentTorrent(); - if (!h.is_valid()) return; + BitTorrent::TorrentHandle *const torrent = m_properties->getCurrentTorrent(); + if (!torrent) return; + QModelIndexList selectedIndexes = selectionModel()->selectedRows(); QStringList selectedPeerIPs; QStringList selectedPeerIPPort; @@ -163,55 +162,34 @@ void PeerListWidget::showPeerListMenu(const QPoint&) } // Add Peer Action QAction *addPeerAct = 0; - if (!h.is_queued() && !h.is_checking()) { - addPeerAct = menu.addAction(IconProvider::instance()->getIcon("user-group-new"), tr("Add a new peer...")); + if (!torrent->isQueued() && !torrent->isChecking()) { + addPeerAct = menu.addAction(GuiIconProvider::instance()->getIcon("user-group-new"), tr("Add a new peer...")); empty_menu = false; } - // Per Peer Speed limiting actions -#if LIBTORRENT_VERSION_NUM < 10000 - QAction *upLimitAct = 0; - QAction *dlLimitAct = 0; -#endif QAction *banAct = 0; QAction *copyPeerAct = 0; if (!selectedPeerIPs.isEmpty()) { - copyPeerAct = menu.addAction(IconProvider::instance()->getIcon("edit-copy"), tr("Copy selected")); + copyPeerAct = menu.addAction(GuiIconProvider::instance()->getIcon("edit-copy"), tr("Copy selected")); menu.addSeparator(); -#if LIBTORRENT_VERSION_NUM < 10000 - dlLimitAct = menu.addAction(QIcon(":/icons/skin/download.png"), tr("Limit download rate...")); - upLimitAct = menu.addAction(QIcon(":/icons/skin/seeding.png"), tr("Limit upload rate...")); - menu.addSeparator(); -#endif - banAct = menu.addAction(IconProvider::instance()->getIcon("user-group-delete"), tr("Ban peer permanently")); + banAct = menu.addAction(GuiIconProvider::instance()->getIcon("user-group-delete"), tr("Ban peer permanently")); empty_menu = false; } if (empty_menu) return; QAction *act = menu.exec(QCursor::pos()); if (act == 0) return; if (act == addPeerAct) { - boost::asio::ip::tcp::endpoint ep = PeerAdditionDlg::askForPeerEndpoint(); - if (ep != boost::asio::ip::tcp::endpoint()) { - try { - h.connect_peer(ep); + BitTorrent::PeerAddress addr = PeerAdditionDlg::askForPeerAddress(); + if (!addr.ip.isNull()) { + if (torrent->connectPeer(addr)) QMessageBox::information(0, tr("Peer addition"), tr("The peer was added to this torrent.")); - } catch(std::exception) { + else QMessageBox::critical(0, tr("Peer addition"), tr("The peer could not be added to this torrent.")); - } - } else { + } + else { qDebug("No peer was added"); } return; } -#if LIBTORRENT_VERSION_NUM < 10000 - if (act == upLimitAct) { - limitUpRateSelectedPeers(selectedPeerIPs); - return; - } - if (act == dlLimitAct) { - limitDlRateSelectedPeers(selectedPeerIPs); - return; - } -#endif if (act == banAct) { banSelectedPeers(selectedPeerIPs); return; @@ -237,84 +215,16 @@ void PeerListWidget::banSelectedPeers(const QStringList& peer_ips) foreach (const QString &ip, peer_ips) { qDebug("Banning peer %s...", ip.toLocal8Bit().data()); Logger::instance()->addMessage(tr("Manually banning peer %1...").arg(ip)); - QBtSession::instance()->banIP(ip); + BitTorrent::Session::instance()->banIP(ip); } // Refresh list loadPeers(m_properties->getCurrentTorrent()); } -#if LIBTORRENT_VERSION_NUM < 10000 -void PeerListWidget::limitUpRateSelectedPeers(const QStringList& peer_ips) -{ - if (peer_ips.empty()) - return; - QTorrentHandle h = m_properties->getCurrentTorrent(); - if (!h.is_valid()) - return; - - bool ok = false; - int cur_limit = -1; - boost::asio::ip::tcp::endpoint first_ep = m_peerEndpoints.value(peer_ips.first(), - boost::asio::ip::tcp::endpoint()); - if (first_ep != boost::asio::ip::tcp::endpoint()) - cur_limit = h.get_peer_upload_limit(first_ep); - long limit = SpeedLimitDialog::askSpeedLimit(&ok, - tr("Upload rate limiting"), - cur_limit, - Preferences::instance()->getGlobalUploadLimit()*1024.); - if (!ok) - return; - - foreach (const QString &ip, peer_ips) { - boost::asio::ip::tcp::endpoint ep = m_peerEndpoints.value(ip, boost::asio::ip::tcp::endpoint()); - if (ep != boost::asio::ip::tcp::endpoint()) { - qDebug("Settings Upload limit of %.1f Kb/s to peer %s", limit/1024., ip.toLocal8Bit().data()); - try { - h.set_peer_upload_limit(ep, limit); - } catch(std::exception) { - std::cerr << "Impossible to apply upload limit to peer" << std::endl; - } - } else { - qDebug("The selected peer no longer exists..."); - } - } -} - -void PeerListWidget::limitDlRateSelectedPeers(const QStringList& peer_ips) -{ - QTorrentHandle h = m_properties->getCurrentTorrent(); - if (!h.is_valid()) - return; - bool ok = false; - int cur_limit = -1; - boost::asio::ip::tcp::endpoint first_ep = m_peerEndpoints.value(peer_ips.first(), - boost::asio::ip::tcp::endpoint()); - if (first_ep != boost::asio::ip::tcp::endpoint()) - cur_limit = h.get_peer_download_limit(first_ep); - long limit = SpeedLimitDialog::askSpeedLimit(&ok, tr("Download rate limiting"), cur_limit, Preferences::instance()->getGlobalDownloadLimit()*1024.); - if (!ok) - return; - - foreach (const QString &ip, peer_ips) { - boost::asio::ip::tcp::endpoint ep = m_peerEndpoints.value(ip, boost::asio::ip::tcp::endpoint()); - if (ep != boost::asio::ip::tcp::endpoint()) { - qDebug("Settings Download limit of %.1f Kb/s to peer %s", limit/1024., ip.toLocal8Bit().data()); - try { - h.set_peer_download_limit(ep, limit); - }catch(std::exception) { - std::cerr << "Impossible to apply download limit to peer" << std::endl; - } - } else { - qDebug("The selected peer no longer exists..."); - } - } -} -#endif - void PeerListWidget::clear() { qDebug("clearing peer list"); m_peerItems.clear(); - m_peerEndpoints.clear(); + m_peerAddresses.clear(); m_missingFlags.clear(); int nbrows = m_listModel->rowCount(); if (nbrows > 0) { @@ -331,34 +241,28 @@ void PeerListWidget::saveSettings() const { Preferences::instance()->setPeerListState(header()->saveState()); } -void PeerListWidget::loadPeers(const QTorrentHandle &h, bool force_hostname_resolution) { - if (!h.is_valid()) - return; - boost::system::error_code ec; - libtorrent::torrent_status status = h.status(torrent_handle::query_pieces); - std::vector peers; - h.get_peer_info(peers); +void PeerListWidget::loadPeers(BitTorrent::TorrentHandle *const torrent, bool force_hostname_resolution) { + if (!torrent) return; + + QList peers = torrent->peers(); QSet old_peers_set = m_peerItems.keys().toSet(); - std::vector::const_iterator itr = peers.begin(); - std::vector::const_iterator itrend = peers.end(); - for ( ; itr != itrend; ++itr) { - peer_info peer = *itr; - std::string ip_str = peer.ip.address().to_string(ec); - if (ec || ip_str.empty()) - continue; - QString peer_ip = misc::toQString(ip_str); + foreach (const BitTorrent::PeerInfo &peer, peers) { + BitTorrent::PeerAddress addr = peer.address(); + if (addr.ip.isNull()) continue; + + QString peer_ip = addr.ip.toString(); if (m_peerItems.contains(peer_ip)) { // Update existing peer - updatePeer(peer_ip, status, peer); + updatePeer(peer_ip, torrent, peer); old_peers_set.remove(peer_ip); if (force_hostname_resolution && m_resolver) { m_resolver->resolve(peer_ip); } } else { // Add new peer - m_peerItems[peer_ip] = addPeer(peer_ip, status, peer); - m_peerEndpoints[peer_ip] = peer.ip; + m_peerItems[peer_ip] = addPeer(peer_ip, torrent, peer); + m_peerAddresses[peer_ip] = addr; // Resolve peer host name is asked if (m_resolver) m_resolver->resolve(peer_ip); @@ -369,70 +273,70 @@ void PeerListWidget::loadPeers(const QTorrentHandle &h, bool force_hostname_reso while(it.hasNext()) { const QString& ip = it.next(); m_missingFlags.remove(ip); - m_peerEndpoints.remove(ip); + m_peerAddresses.remove(ip); QStandardItem *item = m_peerItems.take(ip); m_listModel->removeRow(item->row()); } } -QStandardItem* PeerListWidget::addPeer(const QString& ip, const libtorrent::torrent_status &status, const peer_info& peer) { +QStandardItem* PeerListWidget::addPeer(const QString& ip, BitTorrent::TorrentHandle *const torrent, const BitTorrent::PeerInfo &peer) { int row = m_listModel->rowCount(); // Adding Peer to peer list m_listModel->insertRow(row); m_listModel->setData(m_listModel->index(row, PeerListDelegate::IP), ip); m_listModel->setData(m_listModel->index(row, PeerListDelegate::IP), ip, Qt::ToolTipRole); - m_listModel->setData(m_listModel->index(row, PeerListDelegate::PORT), peer.ip.port()); + m_listModel->setData(m_listModel->index(row, PeerListDelegate::PORT), peer.address().port); m_listModel->setData(m_listModel->index(row, PeerListDelegate::IP_HIDDEN), ip); if (m_displayFlags) { - const QIcon ico = GeoIPManager::CountryISOCodeToIcon(peer.country); + const QIcon ico = GeoIPManager::CountryISOCodeToIcon(peer.country()); if (!ico.isNull()) { m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), ico, Qt::DecorationRole); - const QString country_name = GeoIPManager::CountryISOCodeToName(peer.country); + const QString country_name = GeoIPManager::CountryISOCodeToName(peer.country()); m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), country_name, Qt::ToolTipRole); } else { m_missingFlags.insert(ip); } } - m_listModel->setData(m_listModel->index(row, PeerListDelegate::CONNECTION), getConnectionString(peer)); + m_listModel->setData(m_listModel->index(row, PeerListDelegate::CONNECTION), peer.connectionType()); QString flags, tooltip; getFlags(peer, flags, tooltip); m_listModel->setData(m_listModel->index(row, PeerListDelegate::FLAGS), flags); m_listModel->setData(m_listModel->index(row, PeerListDelegate::FLAGS), tooltip, Qt::ToolTipRole); - m_listModel->setData(m_listModel->index(row, PeerListDelegate::CLIENT), misc::toQStringU(peer.client)); - m_listModel->setData(m_listModel->index(row, PeerListDelegate::PROGRESS), peer.progress); - m_listModel->setData(m_listModel->index(row, PeerListDelegate::DOWN_SPEED), peer.payload_down_speed); - m_listModel->setData(m_listModel->index(row, PeerListDelegate::UP_SPEED), peer.payload_up_speed); - m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_DOWN), (qulonglong)peer.total_download); - m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_UP), (qulonglong)peer.total_upload); - m_listModel->setData(m_listModel->index(row, PeerListDelegate::RELEVANCE), getPeerRelevance(status, peer)); + m_listModel->setData(m_listModel->index(row, PeerListDelegate::CLIENT), peer.client()); + m_listModel->setData(m_listModel->index(row, PeerListDelegate::PROGRESS), peer.progress()); + m_listModel->setData(m_listModel->index(row, PeerListDelegate::DOWN_SPEED), peer.payloadDownSpeed()); + m_listModel->setData(m_listModel->index(row, PeerListDelegate::UP_SPEED), peer.payloadUpSpeed()); + m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_DOWN), peer.totalDownload()); + m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_UP), peer.totalUpload()); + m_listModel->setData(m_listModel->index(row, PeerListDelegate::RELEVANCE), getPeerRelevance(torrent->pieces(), peer.pieces())); return m_listModel->item(row, PeerListDelegate::IP); } -void PeerListWidget::updatePeer(const QString& ip, const libtorrent::torrent_status &status, const peer_info& peer) { +void PeerListWidget::updatePeer(const QString &ip, BitTorrent::TorrentHandle *const torrent, const BitTorrent::PeerInfo &peer) { QStandardItem *item = m_peerItems.value(ip); int row = item->row(); if (m_displayFlags) { - const QIcon ico = GeoIPManager::CountryISOCodeToIcon(peer.country); + const QIcon ico = GeoIPManager::CountryISOCodeToIcon(peer.country()); if (!ico.isNull()) { m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), ico, Qt::DecorationRole); - const QString country_name = GeoIPManager::CountryISOCodeToName(peer.country); + const QString country_name = GeoIPManager::CountryISOCodeToName(peer.country()); m_listModel->setData(m_listModel->index(row, PeerListDelegate::COUNTRY), country_name, Qt::ToolTipRole); m_missingFlags.remove(ip); } } - m_listModel->setData(m_listModel->index(row, PeerListDelegate::CONNECTION), getConnectionString(peer)); + m_listModel->setData(m_listModel->index(row, PeerListDelegate::CONNECTION), peer.connectionType()); QString flags, tooltip; getFlags(peer, flags, tooltip); - m_listModel->setData(m_listModel->index(row, PeerListDelegate::PORT), peer.ip.port()); + m_listModel->setData(m_listModel->index(row, PeerListDelegate::PORT), peer.address().port); m_listModel->setData(m_listModel->index(row, PeerListDelegate::FLAGS), flags); m_listModel->setData(m_listModel->index(row, PeerListDelegate::FLAGS), tooltip, Qt::ToolTipRole); - m_listModel->setData(m_listModel->index(row, PeerListDelegate::CLIENT), misc::toQStringU(peer.client)); - m_listModel->setData(m_listModel->index(row, PeerListDelegate::PROGRESS), peer.progress); - m_listModel->setData(m_listModel->index(row, PeerListDelegate::DOWN_SPEED), peer.payload_down_speed); - m_listModel->setData(m_listModel->index(row, PeerListDelegate::UP_SPEED), peer.payload_up_speed); - m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_DOWN), (qulonglong)peer.total_download); - m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_UP), (qulonglong)peer.total_upload); - m_listModel->setData(m_listModel->index(row, PeerListDelegate::RELEVANCE), getPeerRelevance(status, peer)); + m_listModel->setData(m_listModel->index(row, PeerListDelegate::CLIENT), peer.client()); + m_listModel->setData(m_listModel->index(row, PeerListDelegate::PROGRESS), peer.progress()); + m_listModel->setData(m_listModel->index(row, PeerListDelegate::DOWN_SPEED), peer.payloadDownSpeed()); + m_listModel->setData(m_listModel->index(row, PeerListDelegate::UP_SPEED), peer.payloadUpSpeed()); + m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_DOWN), peer.totalDownload()); + m_listModel->setData(m_listModel->index(row, PeerListDelegate::TOT_UP), peer.totalUpload()); + m_listModel->setData(m_listModel->index(row, PeerListDelegate::RELEVANCE), getPeerRelevance(torrent->pieces(), peer.pieces())); } void PeerListWidget::handleResolved(const QString &ip, const QString &hostname) { @@ -453,34 +357,11 @@ void PeerListWidget::handleSortColumnChanged(int col) } } -QString PeerListWidget::getConnectionString(const peer_info& peer) -{ -#if LIBTORRENT_VERSION_NUM < 10000 - if (peer.connection_type & peer_info::bittorrent_utp) { -#else - if (peer.flags & peer_info::utp_socket) { -#endif - return QString::fromUtf8("μTP"); - } - - QString connection; - switch(peer.connection_type) { - case peer_info::http_seed: - case peer_info::web_seed: - connection = "Web"; - break; - default: - connection = "BT"; - break; - } - return connection; -} - -void PeerListWidget::getFlags(const peer_info& peer, QString& flags, QString& tooltip) +void PeerListWidget::getFlags(const BitTorrent::PeerInfo &peer, QString& flags, QString& tooltip) { - if (peer.flags & peer_info::interesting) { + if (peer.isInteresting()) { //d = Your client wants to download, but peer doesn't want to send (interested and choked) - if (peer.flags & peer_info::remote_choked) { + if (peer.isRemoteChocked()) { flags += "d "; tooltip += tr("interested(local) and choked(peer)"); tooltip += ", "; @@ -493,9 +374,9 @@ void PeerListWidget::getFlags(const peer_info& peer, QString& flags, QString& to } } - if (peer.flags & peer_info::remote_interested) { + if (peer.isRemoteInterested()) { //u = Peer wants your client to upload, but your client doesn't want to (interested and choked) - if (peer.flags & peer_info::choked) { + if (peer.isChocked()) { flags += "u "; tooltip += tr("interested(peer) and choked(local)"); tooltip += ", "; @@ -509,81 +390,78 @@ void PeerListWidget::getFlags(const peer_info& peer, QString& flags, QString& to } //O = Optimistic unchoke - if (peer.flags & peer_info::optimistic_unchoke) { + if (peer.optimisticUnchoke()) { flags += "O "; tooltip += tr("optimistic unchoke"); tooltip += ", "; } //S = Peer is snubbed - if (peer.flags & peer_info::snubbed) { + if (peer.isSnubbed()) { flags += "S "; tooltip += tr("peer snubbed"); tooltip += ", "; } //I = Peer is an incoming connection - if ((peer.flags & peer_info::local_connection) == 0 ) { + if (!peer.isLocalConnection()) { flags += "I "; tooltip += tr("incoming connection"); tooltip += ", "; } //K = Peer is unchoking your client, but your client is not interested - if (((peer.flags & peer_info::remote_choked) == 0) && ((peer.flags & peer_info::interesting) == 0)) { + if (!peer.isRemoteChocked() && !peer.isInteresting()) { flags += "K "; tooltip += tr("not interested(local) and unchoked(peer)"); tooltip += ", "; } //? = Your client unchoked the peer but the peer is not interested - if (((peer.flags & peer_info::choked) == 0) && ((peer.flags & peer_info::remote_interested) == 0)) { + if (!peer.isChocked() && !peer.isRemoteInterested()) { flags += "? "; tooltip += tr("not interested(peer) and unchoked(local)"); tooltip += ", "; } //X = Peer was included in peerlists obtained through Peer Exchange (PEX) - if (peer.source & peer_info::pex) { + if (peer.fromPeX()) { flags += "X "; tooltip += tr("peer from PEX"); tooltip += ", "; } //H = Peer was obtained through DHT - if (peer.source & peer_info::dht) { + if (peer.fromDHT()) { flags += "H "; tooltip += tr("peer from DHT"); tooltip += ", "; } //E = Peer is using Protocol Encryption (all traffic) - if (peer.flags & peer_info::rc4_encrypted) { + if (peer.isRC4Encrypted()) { flags += "E "; tooltip += tr("encrypted traffic"); tooltip += ", "; } //e = Peer is using Protocol Encryption (handshake) - if (peer.flags & peer_info::plaintext_encrypted) { + if (peer.isPlaintextEncrypted()) { flags += "e "; tooltip += tr("encrypted handshake"); tooltip += ", "; } //P = Peer is using uTorrent uTP -#if LIBTORRENT_VERSION_NUM < 10000 - if (peer.connection_type & peer_info::bittorrent_utp) { -#else - if (peer.flags & peer_info::utp_socket) { -#endif + + if (peer.useUTPSocket()) { flags += "P "; tooltip += QString::fromUtf8("μTP"); tooltip += ", "; } //L = Peer is local - if (peer.source & peer_info::lsd) { + if (peer.fromLSD()) { flags += "L"; tooltip += tr("peer from LSD"); } @@ -594,17 +472,15 @@ void PeerListWidget::getFlags(const peer_info& peer, QString& flags, QString& to tooltip.chop(1); } -double PeerListWidget::getPeerRelevance(const torrent_status& status, const libtorrent::peer_info &peer) +qreal PeerListWidget::getPeerRelevance(const QBitArray &allPieces, const QBitArray &peerPieces) { int localMissing = 0; int remoteHaves = 0; - libtorrent::bitfield local = status.pieces; - libtorrent::bitfield remote = peer.pieces; - for (int i=0; i(remoteHaves) / localMissing; } diff --git a/src/gui/properties/peerlistwidget.h b/src/gui/properties/peerlistwidget.h index fa0652563..e6f792f76 100644 --- a/src/gui/properties/peerlistwidget.h +++ b/src/gui/properties/peerlistwidget.h @@ -35,7 +35,6 @@ #include #include #include -#include namespace Net { @@ -45,7 +44,6 @@ namespace Net class PeerListDelegate; class PeerListSortModel; class PropertiesWidget; -class QTorrentHandle; QT_BEGIN_NAMESPACE class QSortFilterProxyModel; @@ -53,18 +51,14 @@ class QStandardItem; class QStandardItemModel; QT_END_NAMESPACE -namespace libtorrent +namespace BitTorrent { - struct peer_info; - struct torrent_status; -} -#include -#if BOOST_VERSION < 103500 -#include -#else -#include -#endif +class TorrentHandle; +class PeerInfo; +struct PeerAddress; + +} class PeerListWidget : public QTreeView { Q_OBJECT @@ -74,9 +68,9 @@ public: ~PeerListWidget(); public slots: - void loadPeers(const QTorrentHandle &h, bool force_hostname_resolution = false); - QStandardItem* addPeer(const QString& ip, const libtorrent::torrent_status &status, const libtorrent::peer_info& peer); - void updatePeer(const QString& ip, const libtorrent::torrent_status &status, const libtorrent::peer_info& peer); + void loadPeers(BitTorrent::TorrentHandle *const torrent, bool force_hostname_resolution = false); + QStandardItem *addPeer(const QString &ip, BitTorrent::TorrentHandle *const torrent, const BitTorrent::PeerInfo &peer); + void updatePeer(const QString &ip, BitTorrent::TorrentHandle *const torrent, const BitTorrent::PeerInfo &peer); void handleResolved(const QString &ip, const QString &hostname); void updatePeerHostNameResolutionState(); void updatePeerCountryResolutionState(); @@ -86,26 +80,19 @@ protected slots: void loadSettings(); void saveSettings() const; void showPeerListMenu(const QPoint&); - -#if LIBTORRENT_VERSION_NUM < 10000 - void limitUpRateSelectedPeers(const QStringList& peer_ips); - void limitDlRateSelectedPeers(const QStringList& peer_ips); -#endif - void banSelectedPeers(const QStringList& peer_ips); void handleSortColumnChanged(int col); private: - static QString getConnectionString(const libtorrent::peer_info &peer); - static void getFlags(const libtorrent::peer_info& peer, QString& flags, QString& tooltip); - double getPeerRelevance(const libtorrent::torrent_status &status, const libtorrent::peer_info &peer); + static void getFlags(const BitTorrent::PeerInfo &peer, QString &flags, QString &tooltip); + qreal getPeerRelevance(const QBitArray &allPieces, const QBitArray &peerPieces); private: QStandardItemModel *m_listModel; PeerListDelegate *m_listDelegate; PeerListSortModel *m_proxyModel; QHash m_peerItems; - QHash m_peerEndpoints; + QHash m_peerAddresses; QSet m_missingFlags; QPointer m_resolver; PropertiesWidget *m_properties; diff --git a/src/gui/properties/pieceavailabilitybar.cpp b/src/gui/properties/pieceavailabilitybar.cpp index 89d8b0075..4cc6f2fa2 100644 --- a/src/gui/properties/pieceavailabilitybar.cpp +++ b/src/gui/properties/pieceavailabilitybar.cpp @@ -28,28 +28,25 @@ * Contact : chris@qbittorrent.org */ +#include #include "pieceavailabilitybar.h" -//#include - PieceAvailabilityBar::PieceAvailabilityBar(QWidget *parent) : QWidget(parent) { setFixedHeight(BAR_HEIGHT); - bg_color = 0xffffff; - border_color = palette().color(QPalette::Dark).rgb(); - piece_color = 0x0000ff; + m_bgColor = 0xffffff; + m_borderColor = palette().color(QPalette::Dark).rgb(); + m_pieceColor = 0x0000ff; updatePieceColors(); } -std::vector PieceAvailabilityBar::intToFloatVector(const std::vector &vecin, int reqSize) +QVector PieceAvailabilityBar::intToFloatVector(const QVector &vecin, int reqSize) { - std::vector result(reqSize, 0.0); - - if (vecin.empty()) - return result; + QVector result(reqSize, 0.0); + if (vecin.isEmpty()) return result; const float ratio = vecin.size() / (float)reqSize; @@ -162,27 +159,27 @@ void PieceAvailabilityBar::updateImage() // qDebug() << "updateImageAv"; QImage image2(width() - 2, 1, QImage::Format_RGB888); - if (pieces.empty()) { + if (m_pieces.empty()) { image2.fill(0xffffff); - image = image2; + m_image = image2; update(); return; } - std::vector scaled_pieces = intToFloatVector(pieces, image2.width()); + QVector scaled_pieces = intToFloatVector(m_pieces, image2.width()); // filling image - for (unsigned int x = 0; x < scaled_pieces.size(); ++x) + for (int x = 0; x < scaled_pieces.size(); ++x) { float pieces2_val = scaled_pieces.at(x); - image2.setPixel(x, 0, piece_colors[pieces2_val * 255]); + image2.setPixel(x, 0, m_pieceColors[pieces2_val * 255]); } - image = image2; + m_image = image2; } -void PieceAvailabilityBar::setAvailability(const std::vector& avail) +void PieceAvailabilityBar::setAvailability(const QVector &avail) { - pieces = std::vector(avail); + m_pieces = avail; updateImage(); update(); @@ -190,16 +187,16 @@ void PieceAvailabilityBar::setAvailability(const std::vector& avail) void PieceAvailabilityBar::updatePieceColors() { - piece_colors = std::vector(256); + m_pieceColors = QVector(256); for (int i = 0; i < 256; ++i) { float ratio = (i / 255.0); - piece_colors[i] = mixTwoColors(bg_color, piece_color, ratio); + m_pieceColors[i] = mixTwoColors(m_bgColor, m_pieceColor, ratio); } } void PieceAvailabilityBar::clear() { - image = QImage(); + m_image = QImage(); update(); } @@ -207,29 +204,29 @@ void PieceAvailabilityBar::paintEvent(QPaintEvent *) { QPainter painter(this); QRect imageRect(1, 1, width() - 2, height() - 2); - if (image.isNull()) + if (m_image.isNull()) { painter.setBrush(Qt::white); painter.drawRect(imageRect); } else { - if (image.width() != imageRect.width()) + if (m_image.width() != imageRect.width()) updateImage(); - painter.drawImage(imageRect, image); + painter.drawImage(imageRect, m_image); } QPainterPath border; border.addRect(0, 0, width() - 1, height() - 1); - painter.setPen(border_color); + painter.setPen(m_borderColor); painter.drawPath(border); } void PieceAvailabilityBar::setColors(int background, int border, int available) { - bg_color = background; - border_color = border; - piece_color = available; + m_bgColor = background; + m_borderColor = border; + m_pieceColor = available; updatePieceColors(); updateImage(); diff --git a/src/gui/properties/pieceavailabilitybar.h b/src/gui/properties/pieceavailabilitybar.h index 93e063b28..fa332bf27 100644 --- a/src/gui/properties/pieceavailabilitybar.h +++ b/src/gui/properties/pieceavailabilitybar.h @@ -34,8 +34,6 @@ #include #include #include -#include -#include #define BAR_HEIGHT 18 @@ -44,25 +42,25 @@ class PieceAvailabilityBar: public QWidget { Q_DISABLE_COPY(PieceAvailabilityBar) private: - QImage image; + QImage m_image; // I used values, bacause it should be possible to change colors in runtime // background color - int bg_color; + int m_bgColor; // border color - int border_color; + int m_borderColor; // complete piece color - int piece_color; + int m_pieceColor; // buffered 256 levels gradient from bg_color to piece_color - std::vector piece_colors; + QVector m_pieceColors; // last used int vector, uses to better resize redraw // TODO: make a diff pieces to new pieces and update only changed pixels, speedup when update > 20x faster - std::vector pieces; + QVector m_pieces; // scale int vector to float vector - std::vector intToFloatVector(const std::vector &vecin, int reqSize); + QVector intToFloatVector(const QVector &vecin, int reqSize); // mix two colors by light model, ratio <0, 1> int mixTwoColors(int &rgb1, int &rgb2, float ratio); @@ -72,7 +70,7 @@ private: public: PieceAvailabilityBar(QWidget *parent); - void setAvailability(const std::vector& avail); + void setAvailability(const QVector &avail); void updatePieceColors(); void clear(); diff --git a/src/gui/properties/propertieswidget.cpp b/src/gui/properties/propertieswidget.cpp index 53bca79ec..f7e1fee0d 100644 --- a/src/gui/properties/propertieswidget.cpp +++ b/src/gui/properties/propertieswidget.cpp @@ -40,11 +40,11 @@ #include #include #include -#include +#include + #include "propertieswidget.h" #include "transferlistwidget.h" -#include "core/torrentpersistentdata.h" -#include "qbtsession.h" +#include "core/bittorrent/session.h" #include "proplistdelegate.h" #include "torrentcontentfiltermodel.h" #include "torrentcontentmodel.h" @@ -55,20 +55,18 @@ #include "pieceavailabilitybar.h" #include "core/preferences.h" #include "proptabbar.h" -#include "iconprovider.h" +#include "guiiconprovider.h" #include "lineedit.h" #include "core/fs_utils.h" #include "autoexpandabledialog.h" -using namespace libtorrent; - PropertiesWidget::PropertiesWidget(QWidget *parent, MainWindow* main_window, TransferListWidget *transferList): - QWidget(parent), transferList(transferList), main_window(main_window) { + QWidget(parent), transferList(transferList), main_window(main_window), m_torrent(0) { setupUi(this); // Icons - trackerUpButton->setIcon(IconProvider::instance()->getIcon("go-up")); - trackerDownButton->setIcon(IconProvider::instance()->getIcon("go-down")); + trackerUpButton->setIcon(GuiIconProvider::instance()->getIcon("go-up")); + trackerDownButton->setIcon(GuiIconProvider::instance()->getIcon("go-down")); state = VISIBLE; @@ -92,11 +90,11 @@ PropertiesWidget::PropertiesWidget(QWidget *parent, MainWindow* main_window, Tra connect(filesList, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(openDoubleClickedFile(const QModelIndex &))); connect(PropListModel, SIGNAL(filteredFilesChanged()), this, SLOT(filteredFilesChanged())); connect(listWebSeeds, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayWebSeedListMenu(const QPoint&))); - connect(transferList, SIGNAL(currentTorrentChanged(QTorrentHandle)), this, SLOT(loadTorrentInfos(QTorrentHandle))); + connect(transferList, SIGNAL(currentTorrentChanged(BitTorrent::TorrentHandle *const)), this, SLOT(loadTorrentInfos(BitTorrent::TorrentHandle *const))); connect(PropDelegate, SIGNAL(filteredFilesChanged()), this, SLOT(filteredFilesChanged())); connect(stackedProperties, SIGNAL(currentChanged(int)), this, SLOT(loadDynamicData())); - connect(QBtSession::instance(), SIGNAL(savePathChanged(QTorrentHandle)), this, SLOT(updateSavePath(QTorrentHandle))); - connect(QBtSession::instance(), SIGNAL(metadataReceived(QTorrentHandle)), this, SLOT(updateTorrentInfos(QTorrentHandle))); + connect(BitTorrent::Session::instance(), SIGNAL(torrentSavePathChanged(BitTorrent::TorrentHandle *const)), this, SLOT(updateSavePath(BitTorrent::TorrentHandle *const))); + connect(BitTorrent::Session::instance(), SIGNAL(torrentMetadataLoaded(BitTorrent::TorrentHandle *const)), this, SLOT(updateTorrentInfos(BitTorrent::TorrentHandle *const))); connect(filesList->header(), SIGNAL(sectionMoved(int, int, int)), this, SLOT(saveSettings())); connect(filesList->header(), SIGNAL(sectionResized(int, int, int)), this, SLOT(saveSettings())); connect(filesList->header(), SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)), this, SLOT(saveSettings())); @@ -111,9 +109,6 @@ PropertiesWidget::PropertiesWidget(QWidget *parent, MainWindow* main_window, Tra trackerList = new TrackerList(this); connect(trackerUpButton, SIGNAL(clicked()), trackerList, SLOT(moveSelectionUp())); connect(trackerDownButton, SIGNAL(clicked()), trackerList, SLOT(moveSelectionDown())); - connect(trackerList, SIGNAL(trackersAdded(const QStringList &, const QString &)), this, SIGNAL(trackersAdded(const QStringList &, const QString &))); - connect(trackerList, SIGNAL(trackersRemoved(const QStringList &, const QString &)), this, SIGNAL(trackersRemoved(const QStringList &, const QString &))); - connect(trackerList, SIGNAL(trackerlessChange(bool, const QString &)), this, SIGNAL(trackerlessChange(bool, const QString &))); horizontalLayout_trackers->insertWidget(0, trackerList); connect(trackerList->header(), SIGNAL(sectionMoved(int, int, int)), trackerList, SLOT(saveSettings())); connect(trackerList->header(), SIGNAL(sectionResized(int, int, int)), trackerList, SLOT(saveSettings())); @@ -230,61 +225,56 @@ void PropertiesWidget::clear() { showPiecesDownloaded(false); } -QTorrentHandle PropertiesWidget::getCurrentTorrent() const { - return h; +BitTorrent::TorrentHandle *PropertiesWidget::getCurrentTorrent() const +{ + return m_torrent; } -void PropertiesWidget::updateSavePath(const QTorrentHandle& _h) { - if (h.is_valid() && h == _h) { - save_path->setText(fsutils::toNativePath(h.save_path_parsed())); +void PropertiesWidget::updateSavePath(BitTorrent::TorrentHandle *const torrent) +{ + if (m_torrent == torrent) { + save_path->setText(m_torrent->savePathParsed()); } } -void PropertiesWidget::loadTrackers(const QTorrentHandle &handle) +void PropertiesWidget::loadTrackers(BitTorrent::TorrentHandle *const torrent) { - if (handle == h) + if (torrent == m_torrent) trackerList->loadTrackers(); } -void PropertiesWidget::updateTorrentInfos(const QTorrentHandle& _h) { - if (h.is_valid() && h == _h) { - loadTorrentInfos(h); - } +void PropertiesWidget::updateTorrentInfos(BitTorrent::TorrentHandle *const torrent) +{ + if (m_torrent == torrent) + loadTorrentInfos(m_torrent); } -void PropertiesWidget::loadTorrentInfos(const QTorrentHandle& _h) +void PropertiesWidget::loadTorrentInfos(BitTorrent::TorrentHandle *const torrent) { clear(); - h = _h; - if (!h.is_valid()) - return; + m_torrent = torrent; + if (!m_torrent) return; - try { // Save path - updateSavePath(h); + updateSavePath(m_torrent); // Hash - hash_lbl->setText(h.hash()); + hash_lbl->setText(m_torrent->hash()); PropListModel->model()->clear(); - if (h.has_metadata()) { + if (m_torrent->hasMetadata()) { // Creation date - lbl_creationDate->setText(h.creation_date()); + lbl_creationDate->setText(m_torrent->creationDate().toString()); // Piece size - pieceSize_lbl->setText(misc::friendlyUnit(h.piece_length())); + pieceSize_lbl->setText(misc::friendlyUnit(m_torrent->pieceLength())); // Comment - comment_text->setHtml(misc::parseHtmlLinks(h.comment())); + comment_text->setHtml(misc::parseHtmlLinks(m_torrent->comment())); // URL seeds loadUrlSeeds(); // List files in torrent -#if LIBTORRENT_VERSION_NUM < 10000 - PropListModel->model()->setupModelData(h.get_torrent_info()); -#else - PropListModel->model()->setupModelData(*h.torrent_file()); -#endif + PropListModel->model()->setupModelData(m_torrent->info()); filesList->setExpanded(PropListModel->index(0, 0), true); // Load file priorities - PropListModel->model()->updateFilesPriorities(h.file_priorities()); + PropListModel->model()->updateFilesPriorities(m_torrent->filePriorities()); } - } catch(const invalid_handle& e) { } // Load dynamic data loadDynamicData(); } @@ -338,55 +328,44 @@ void PropertiesWidget::reloadPreferences() { void PropertiesWidget::loadDynamicData() { // Refresh only if the torrent handle is valid and if visible - if (!h.is_valid() || main_window->getCurrentTabWidget() != transferList || state != VISIBLE) return; - try { + if (!m_torrent || (main_window->getCurrentTabWidget() != transferList) || (state != VISIBLE)) return; + // Transfer infos if (stackedProperties->currentIndex() == PropTabBar::MAIN_TAB) { - libtorrent::torrent_status status = h.status(torrent_handle::query_accurate_download_counters - | torrent_handle::query_distributed_copies - | torrent_handle::query_pieces); - wasted->setText(misc::friendlyUnit(status.total_failed_bytes+status.total_redundant_bytes)); - upTotal->setText(misc::friendlyUnit(status.all_time_upload) + " ("+misc::friendlyUnit(status.total_payload_upload)+" "+tr("this session")+")"); - dlTotal->setText(misc::friendlyUnit(status.all_time_download) + " ("+misc::friendlyUnit(status.total_payload_download)+" "+tr("this session")+")"); - lbl_uplimit->setText(h.upload_limit() <= 0 ? QString::fromUtf8("∞") : misc::friendlyUnit(h.upload_limit())+tr("/s", "/second (i.e. per second)")); - lbl_dllimit->setText(h.download_limit() <= 0 ? QString::fromUtf8("∞") : misc::friendlyUnit(h.download_limit())+tr("/s", "/second (i.e. per second)")); - QString elapsed_txt = misc::userFriendlyDuration(status.active_time); - if (h.is_seed(status)) { - elapsed_txt += " ("+tr("Seeded for %1", "e.g. Seeded for 3m10s").arg(misc::userFriendlyDuration(status.seeding_time))+")"; + wasted->setText(misc::friendlyUnit(m_torrent->wastedSize())); + upTotal->setText(misc::friendlyUnit(m_torrent->totalUpload()) + " ("+misc::friendlyUnit(m_torrent->totalPayloadUpload())+" "+tr("this session")+")"); + dlTotal->setText(misc::friendlyUnit(m_torrent->totalDownload()) + " ("+misc::friendlyUnit(m_torrent->totalPayloadDownload())+" "+tr("this session")+")"); + lbl_uplimit->setText(m_torrent->uploadLimit() <= 0 ? QString::fromUtf8("∞") : misc::friendlyUnit(m_torrent->uploadLimit())+tr("/s", "/second (i.e. per second)")); + lbl_dllimit->setText(m_torrent->downloadLimit() <= 0 ? QString::fromUtf8("∞") : misc::friendlyUnit(m_torrent->downloadLimit())+tr("/s", "/second (i.e. per second)")); + QString elapsed_txt = misc::userFriendlyDuration(m_torrent->activeTime()); + if (m_torrent->isSeed()) { + elapsed_txt += " ("+tr("Seeded for %1", "e.g. Seeded for 3m10s").arg(misc::userFriendlyDuration(m_torrent->seedingTime()))+")"; } lbl_elapsed->setText(elapsed_txt); - if (status.connections_limit > 0) - lbl_connections->setText(QString::number(status.num_connections)+" ("+tr("%1 max", "e.g. 10 max").arg(QString::number(status.connections_limit))+")"); + if (m_torrent->connectionsLimit() > 0) + lbl_connections->setText(QString::number(m_torrent->connectionsCount())+" ("+tr("%1 max", "e.g. 10 max").arg(QString::number(m_torrent->connectionsLimit()))+")"); else - lbl_connections->setText(QString::number(status.num_connections)); + lbl_connections->setText(QString::number(m_torrent->connectionsLimit())); // Update next announce time - reannounce_lbl->setText(misc::userFriendlyDuration(status.next_announce.total_seconds())); + reannounce_lbl->setText(misc::userFriendlyDuration(m_torrent->nextAnnounce())); // Update ratio info - const qreal ratio = QBtSession::instance()->getRealRatio(status); - shareRatio->setText(ratio > QBtSession::MAX_RATIO ? QString::fromUtf8("∞") : misc::accurateDoubleToString(ratio, 2)); - if (!h.is_seed(status) && status.has_metadata) { + const qreal ratio = m_torrent->realRatio(); + shareRatio->setText(ratio > BitTorrent::TorrentHandle::MAX_RATIO ? QString::fromUtf8("∞") : misc::accurateDoubleToString(ratio, 2)); + if (!m_torrent->isSeed() && m_torrent->hasMetadata()) { showPiecesDownloaded(true); // Downloaded pieces -#if LIBTORRENT_VERSION_NUM < 10000 - bitfield bf(h.get_torrent_info().num_pieces(), 0); -#else - bitfield bf(h.torrent_file()->num_pieces(), 0); -#endif - h.downloading_pieces(bf); - downloaded_pieces->setProgress(status.pieces, bf); + downloaded_pieces->setProgress(m_torrent->pieces(), m_torrent->downloadingPieces()); // Pieces availability - if (!h.is_paused(status) && !h.is_queued(status) && !h.is_checking(status)) { + if (!m_torrent->isPaused() && !m_torrent->isQueued() && !m_torrent->isChecking()) { showPiecesAvailability(true); - std::vector avail; - h.piece_availability(avail); - pieces_availability->setAvailability(avail); - avail_average_lbl->setText(misc::accurateDoubleToString(status.distributed_copies, 3)); + pieces_availability->setAvailability(m_torrent->pieceAvailability()); + avail_average_lbl->setText(misc::accurateDoubleToString(m_torrent->distributedCopies(), 3)); } else { showPiecesAvailability(false); } // Progress - qreal progress = h.progress(status)*100.; + qreal progress = m_torrent->progress() * 100.; progress_lbl->setText(misc::accurateDoubleToString(progress, 1)+"%"); } else { showPiecesAvailability(false); @@ -401,20 +380,15 @@ void PropertiesWidget::loadDynamicData() { } if (stackedProperties->currentIndex() == PropTabBar::PEERS_TAB) { // Load peers - peersList->loadPeers(h); + peersList->loadPeers(m_torrent); return; } if (stackedProperties->currentIndex() == PropTabBar::FILES_TAB) { - libtorrent::torrent_status status = h.status(torrent_handle::query_accurate_download_counters - | torrent_handle::query_distributed_copies - | torrent_handle::query_pieces); // Files progress - if (h.is_valid() && status.has_metadata) { + if (m_torrent->hasMetadata()) { qDebug("Updating priorities in files tab"); filesList->setUpdatesEnabled(false); - std::vector fp; - h.file_progress(fp); - PropListModel->model()->updateFilesProgress(fp); + PropListModel->model()->updateFilesProgress(m_torrent->filesProgress()); // XXX: We don't update file priorities regularly for performance // reasons. This means that priorities will not be updated if // set from the Web UI. @@ -422,25 +396,22 @@ void PropertiesWidget::loadDynamicData() { filesList->setUpdatesEnabled(true); } } - } catch(const invalid_handle& e) { - qWarning() << "Caught exception in PropertiesWidget::loadDynamicData(): " << misc::toQStringU(e.what()); - } } void PropertiesWidget::loadUrlSeeds() { listWebSeeds->clear(); qDebug("Loading URL seeds"); - const QStringList hc_seeds = h.url_seeds(); + const QList hc_seeds = m_torrent->urlSeeds(); // Add url seeds - foreach (const QString &hc_seed, hc_seeds) { - qDebug("Loading URL seed: %s", qPrintable(hc_seed)); - new QListWidgetItem(hc_seed, listWebSeeds); + foreach (const QUrl &hc_seed, hc_seeds) { + qDebug("Loading URL seed: %s", qPrintable(hc_seed.toString())); + new QListWidgetItem(hc_seed.toString(), listWebSeeds); } } void PropertiesWidget::openDoubleClickedFile(const QModelIndex &index) { if (!index.isValid()) return; - if (!h.is_valid() || !h.has_metadata()) return; + if (!m_torrent || !m_torrent->hasMetadata()) return; if (PropListModel->itemType(index) == TorrentContentModelItem::FileType) openFile(index); else @@ -449,12 +420,12 @@ void PropertiesWidget::openDoubleClickedFile(const QModelIndex &index) { void PropertiesWidget::openFile(const QModelIndex &index) { int i = PropListModel->getFileIndex(index); - const QDir saveDir(h.save_path()); - const QString filename = h.filepath_at(i); + const QDir saveDir(m_torrent->actualSavePath()); + const QString filename = m_torrent->filePath(i); const QString file_path = fsutils::expandPath(saveDir.absoluteFilePath(filename)); qDebug("Trying to open file at %s", qPrintable(file_path)); // Flush data - h.flush_cache(); + m_torrent->flushCache(); if (QFile::exists(file_path)) { if (file_path.startsWith("//")) QDesktopServices::openUrl(fsutils::toNativePath("file:" + file_path)); @@ -484,24 +455,24 @@ void PropertiesWidget::openFolder(const QModelIndex &index, bool containing_fold if (containing_folder) path_items.removeLast(); #endif - const QDir saveDir(h.save_path()); + const QDir saveDir(m_torrent->actualSavePath()); const QString relative_path = path_items.join("/"); absolute_path = fsutils::expandPath(saveDir.absoluteFilePath(relative_path)); } else { - int i = PropListModel->getFileIndex(index); - const QDir saveDir(h.save_path()); - const QString relative_path = h.filepath_at(i); - absolute_path = fsutils::expandPath(saveDir.absoluteFilePath(relative_path)); + int i = PropListModel->getFileIndex(index); + const QDir saveDir(m_torrent->actualSavePath()); + const QString relative_path = m_torrent->filePath(i); + absolute_path = fsutils::expandPath(saveDir.absoluteFilePath(relative_path)); #if !(defined(Q_OS_WIN) || (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))) - if (containing_folder) - absolute_path = fsutils::folderName(absolute_path); + if (containing_folder) + absolute_path = fsutils::folderName(absolute_path); #endif } // Flush data - h.flush_cache(); + m_torrent->flushCache(); if (!QFile::exists(absolute_path)) return; qDebug("Trying to open folder at %s", qPrintable(absolute_path)); @@ -544,8 +515,8 @@ void PropertiesWidget::openFolder(const QModelIndex &index, bool containing_fold } void PropertiesWidget::displayFilesListMenu(const QPoint&) { - if (!h.is_valid()) - return; + if (!m_torrent) return; + QModelIndexList selectedRows = filesList->selectionModel()->selectedRows(0); if (selectedRows.empty()) return; @@ -554,13 +525,13 @@ void PropertiesWidget::displayFilesListMenu(const QPoint&) { QAction *actOpenContainingFolder = 0; QAction *actRename = 0; if (selectedRows.size() == 1) { - actOpen = myFilesLlistMenu.addAction(IconProvider::instance()->getIcon("folder-documents"), tr("Open")); - actOpenContainingFolder = myFilesLlistMenu.addAction(IconProvider::instance()->getIcon("inode-directory"), tr("Open Containing Folder")); - actRename = myFilesLlistMenu.addAction(IconProvider::instance()->getIcon("edit-rename"), tr("Rename...")); + actOpen = myFilesLlistMenu.addAction(GuiIconProvider::instance()->getIcon("folder-documents"), tr("Open")); + actOpenContainingFolder = myFilesLlistMenu.addAction(GuiIconProvider::instance()->getIcon("inode-directory"), tr("Open Containing Folder")); + actRename = myFilesLlistMenu.addAction(GuiIconProvider::instance()->getIcon("edit-rename"), tr("Rename...")); myFilesLlistMenu.addSeparator(); } QMenu subMenu; - if (!h.status(0x0).is_seeding) { + if (!m_torrent->isSeed()) { subMenu.setTitle(tr("Priority")); subMenu.addAction(actionNot_downloaded); subMenu.addAction(actionNormal); @@ -603,20 +574,20 @@ void PropertiesWidget::displayFilesListMenu(const QPoint&) { } void PropertiesWidget::displayWebSeedListMenu(const QPoint&) { - if (!h.is_valid()) - return; + if (!m_torrent) return; + QMenu seedMenu; QModelIndexList rows = listWebSeeds->selectionModel()->selectedRows(); - QAction *actAdd = seedMenu.addAction(IconProvider::instance()->getIcon("list-add"), tr("New Web seed")); + QAction *actAdd = seedMenu.addAction(GuiIconProvider::instance()->getIcon("list-add"), tr("New Web seed")); QAction *actDel = 0; QAction *actCpy = 0; QAction *actEdit = 0; if (rows.size()) { - actDel = seedMenu.addAction(IconProvider::instance()->getIcon("list-remove"), tr("Remove Web seed")); + actDel = seedMenu.addAction(GuiIconProvider::instance()->getIcon("list-remove"), tr("Remove Web seed")); seedMenu.addSeparator(); - actCpy = seedMenu.addAction(IconProvider::instance()->getIcon("edit-copy"), tr("Copy Web seed URL")); - actEdit = seedMenu.addAction(IconProvider::instance()->getIcon("edit-rename"), tr("Edit Web seed URL")); + actCpy = seedMenu.addAction(GuiIconProvider::instance()->getIcon("edit-copy"), tr("Copy Web seed URL")); + actEdit = seedMenu.addAction(GuiIconProvider::instance()->getIcon("edit-rename"), tr("Edit Web seed URL")); } const QAction *act = seedMenu.exec(QCursor::pos()); @@ -654,8 +625,8 @@ void PropertiesWidget::renameSelectedFile() { if (PropListModel->itemType(index) == TorrentContentModelItem::FileType) { // File renaming const int file_index = PropListModel->getFileIndex(index); - if (!h.is_valid() || !h.has_metadata()) return; - QString old_name = h.filepath_at(file_index); + if (!m_torrent || !m_torrent->hasMetadata()) return; + QString old_name = m_torrent->filePath(file_index); if (old_name.endsWith(".!qB") && !new_name_last.endsWith(".!qB")) { new_name_last += ".!qB"; } @@ -669,12 +640,12 @@ void PropertiesWidget::renameSelectedFile() { } new_name = fsutils::expandPath(new_name); // Check if that name is already used - for (int i=0; ifilesCount(); ++i) { if (i == file_index) continue; #if defined(Q_OS_UNIX) || defined(Q_WS_QWS) - if (h.filepath_at(i).compare(new_name, Qt::CaseSensitive) == 0) { + if (m_torrent->filePath(i).compare(new_name, Qt::CaseSensitive) == 0) { #else - if (h.filepath_at(i).compare(new_name, Qt::CaseInsensitive) == 0) { + if (m_torrent->filePath(i).compare(new_name, Qt::CaseInsensitive) == 0) { #endif // Display error message QMessageBox::warning(this, tr("The file could not be renamed"), @@ -683,11 +654,11 @@ void PropertiesWidget::renameSelectedFile() { return; } } - const bool force_recheck = QFile::exists(h.save_path()+"/"+new_name); + const bool force_recheck = QFile::exists(m_torrent->actualSavePath() + "/" + new_name); qDebug("Renaming %s to %s", qPrintable(old_name), qPrintable(new_name)); - h.rename_file(file_index, new_name); + m_torrent->renameFile(file_index, new_name); // Force recheck - if (force_recheck) h.force_recheck(); + if (force_recheck) m_torrent->forceRecheck(); // Rename if torrent files model too if (new_name_last.endsWith(".!qB")) new_name_last.chop(4); @@ -707,9 +678,9 @@ void PropertiesWidget::renameSelectedFile() { QString new_path = path_items.join("/"); if (!new_path.endsWith("/")) new_path += "/"; // Check for overwriting - const int num_files = h.num_files(); + const int num_files = m_torrent->filesCount(); for (int i=0; ifilePath(i); #if defined(Q_OS_UNIX) || defined(Q_WS_QWS) if (current_name.startsWith(new_path, Qt::CaseSensitive)) { #else @@ -724,23 +695,23 @@ void PropertiesWidget::renameSelectedFile() { bool force_recheck = false; // Replace path in all files for (int i=0; ifilePath(i); if (current_name.startsWith(old_path)) { QString new_name = current_name; new_name.replace(0, old_path.length(), new_path); - if (!force_recheck && QDir(h.save_path()).exists(new_name)) + if (!force_recheck && QDir(m_torrent->actualSavePath()).exists(new_name)) force_recheck = true; new_name = fsutils::expandPath(new_name); qDebug("Rename %s to %s", qPrintable(current_name), qPrintable(new_name)); - h.rename_file(i, new_name); + m_torrent->renameFile(i, new_name); } } // Force recheck - if (force_recheck) h.force_recheck(); + if (force_recheck) m_torrent->forceRecheck(); // Rename folder in torrent files model too PropListModel->setData(index, new_name_last); // Remove old folder - const QDir old_folder(h.save_path()+"/"+old_path); + const QDir old_folder(m_torrent->actualSavePath() + "/" + old_path); int timeout = 10; while(!QDir().rmpath(old_folder.absolutePath()) && timeout > 0) { // XXX: We should not sleep here (freezes the UI for 1 second) @@ -765,28 +736,23 @@ void PropertiesWidget::askWebSeed() { QMessageBox::Ok); return; } - if (h.is_valid()) - h.add_url_seed(url_seed); + if (m_torrent) + m_torrent->addUrlSeeds(QList() << url_seed); // Refresh the seeds list loadUrlSeeds(); } void PropertiesWidget::deleteSelectedUrlSeeds() { const QList selectedItems = listWebSeeds->selectedItems(); - if (selectedItems.isEmpty()) - return; - bool change = false; - foreach (const QListWidgetItem *item, selectedItems) { - QString url_seed = item->text(); - try { - h.remove_url_seed(url_seed); - change = true; - } catch (invalid_handle&) {} - } - if (change) { - // Refresh list - loadUrlSeeds(); - } + if (selectedItems.isEmpty()) return; + + QList urlSeeds; + foreach (const QListWidgetItem *item, selectedItems) + urlSeeds << item->text(); + + m_torrent->removeUrlSeeds(urlSeeds); + // Refresh list + loadUrlSeeds(); } void PropertiesWidget::copySelectedWebSeedsToClipboard() const { @@ -822,31 +788,28 @@ void PropertiesWidget::editWebSeed() { return; } - try { - h.remove_url_seed(old_seed); - h.add_url_seed(new_seed); - loadUrlSeeds(); - } catch (invalid_handle&) {} + m_torrent->removeUrlSeeds(QList() << old_seed); + m_torrent->addUrlSeeds(QList() << new_seed); + loadUrlSeeds(); } bool PropertiesWidget::applyPriorities() { qDebug("Saving files priorities"); - const std::vector priorities = PropListModel->model()->getFilesPriorities(); + const QVector priorities = PropListModel->model()->getFilePriorities(); // Save first/last piece first option state - bool first_last_piece_first = h.first_last_piece_first(); + bool first_last_piece_first = m_torrent->hasFirstLastPiecePriority(); // Prioritize the files qDebug("prioritize files: %d", priorities[0]); - h.prioritize_files(priorities); + m_torrent->prioritizeFiles(priorities); // Restore first/last piece first option if necessary if (first_last_piece_first) - h.prioritize_first_last_piece(true); + m_torrent->setFirstLastPiecePriority(true); return true; } void PropertiesWidget::filteredFilesChanged() { - if (h.is_valid()) { + if (m_torrent) applyPriorities(); - } } void PropertiesWidget::filterText(const QString& filter) { diff --git a/src/gui/properties/propertieswidget.h b/src/gui/properties/propertieswidget.h index 23cb8c46d..98a144d8b 100644 --- a/src/gui/properties/propertieswidget.h +++ b/src/gui/properties/propertieswidget.h @@ -34,7 +34,7 @@ #include #include #include "ui_propertieswidget.h" -#include "qtorrenthandle.h" +#include "core/bittorrent/torrenthandle.h" class TransferListWidget; @@ -64,23 +64,18 @@ public: public: PropertiesWidget(QWidget *parent, MainWindow* main_window, TransferListWidget *transferList); ~PropertiesWidget(); - QTorrentHandle getCurrentTorrent() const; + BitTorrent::TorrentHandle *getCurrentTorrent() const; TrackerList* getTrackerList() const { return trackerList; } PeerListWidget* getPeerList() const { return peersList; } QTreeView* getFilesList() const { return filesList; } -signals: - void trackersAdded(const QStringList &trackers, const QString &hash); - void trackersRemoved(const QStringList &trackers, const QString &hash); - void trackerlessChange(bool trackerless, const QString &hash); - protected: QPushButton* getButtonFromIndex(int index); bool applyPriorities(); protected slots: - void loadTorrentInfos(const QTorrentHandle &h); - void updateTorrentInfos(const QTorrentHandle &h); + void loadTorrentInfos(BitTorrent::TorrentHandle *const torrent); + void updateTorrentInfos(BitTorrent::TorrentHandle *const torrent); void loadUrlSeeds(); void askWebSeed(); void deleteSelectedUrlSeeds(); @@ -101,8 +96,8 @@ public slots: void saveSettings(); void reloadPreferences(); void openDoubleClickedFile(const QModelIndex &); - void updateSavePath(const QTorrentHandle& h); - void loadTrackers(const QTorrentHandle &handle); + void updateSavePath(BitTorrent::TorrentHandle *const torrent); + void loadTrackers(BitTorrent::TorrentHandle *const torrent); private: void openFile(const QModelIndex &index); @@ -111,7 +106,7 @@ private: private: TransferListWidget *transferList; MainWindow *main_window; - QTorrentHandle h; + BitTorrent::TorrentHandle *m_torrent; QTimer *refreshTimer; SlideState state; TorrentContentFilterModel *PropListModel; diff --git a/src/gui/properties/proplistdelegate.h b/src/gui/properties/proplistdelegate.h index 9a5c3109f..fb228899f 100644 --- a/src/gui/properties/proplistdelegate.h +++ b/src/gui/properties/proplistdelegate.h @@ -154,8 +154,8 @@ public: QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &index) const { if (index.column() != PRIORITY) return 0; if (properties) { - QTorrentHandle h = properties->getCurrentTorrent(); - if (!h.is_valid() || !h.has_metadata() || h.status(0x0).is_seeding) + BitTorrent::TorrentHandle *const torrent = properties->getCurrentTorrent(); + if (!torrent || !torrent->hasMetadata() || torrent->isSeed()) return 0; } if (index.data().toInt() <= 0) { diff --git a/src/gui/properties/proptabbar.cpp b/src/gui/properties/proptabbar.cpp index 4b34eb962..e2defb738 100644 --- a/src/gui/properties/proptabbar.cpp +++ b/src/gui/properties/proptabbar.cpp @@ -34,7 +34,7 @@ #include #include "proptabbar.h" -#include "iconprovider.h" +#include "guiiconprovider.h" PropTabBar::PropTabBar(QWidget *parent) : QHBoxLayout(parent), m_currentIndex(-1) @@ -42,24 +42,24 @@ PropTabBar::PropTabBar(QWidget *parent) : setSpacing(2); m_btnGroup = new QButtonGroup(this); // General tab - QPushButton *main_infos_button = new QPushButton(IconProvider::instance()->getIcon("document-properties"), tr("General"), parent); + QPushButton *main_infos_button = new QPushButton(GuiIconProvider::instance()->getIcon("document-properties"), tr("General"), parent); main_infos_button->setShortcut(QKeySequence(QString::fromUtf8("Alt+P"))); addWidget(main_infos_button); m_btnGroup->addButton(main_infos_button, MAIN_TAB); // Trackers tab - QPushButton *trackers_button = new QPushButton(IconProvider::instance()->getIcon("network-server"), tr("Trackers"), parent); + QPushButton *trackers_button = new QPushButton(GuiIconProvider::instance()->getIcon("network-server"), tr("Trackers"), parent); addWidget(trackers_button); m_btnGroup->addButton(trackers_button, TRACKERS_TAB); // Peers tab - QPushButton *peers_button = new QPushButton(IconProvider::instance()->getIcon("edit-find-user"), tr("Peers"), parent); + QPushButton *peers_button = new QPushButton(GuiIconProvider::instance()->getIcon("edit-find-user"), tr("Peers"), parent); addWidget(peers_button); m_btnGroup->addButton(peers_button, PEERS_TAB); // URL seeds tab - QPushButton *urlseeds_button = new QPushButton(IconProvider::instance()->getIcon("network-server"), tr("HTTP Sources"), parent); + QPushButton *urlseeds_button = new QPushButton(GuiIconProvider::instance()->getIcon("network-server"), tr("HTTP Sources"), parent); addWidget(urlseeds_button); m_btnGroup->addButton(urlseeds_button, URLSEEDS_TAB); // Files tab - QPushButton *files_button = new QPushButton(IconProvider::instance()->getIcon("inode-directory"), tr("Content"), parent); + QPushButton *files_button = new QPushButton(GuiIconProvider::instance()->getIcon("inode-directory"), tr("Content"), parent); addWidget(files_button); m_btnGroup->addButton(files_button, FILES_TAB); // Spacer diff --git a/src/gui/properties/trackerlist.cpp b/src/gui/properties/trackerlist.cpp index 22a3a2272..8b3ceb7b1 100644 --- a/src/gui/properties/trackerlist.cpp +++ b/src/gui/properties/trackerlist.cpp @@ -36,19 +36,18 @@ #include #include #include -#include -#include #include "trackerlist.h" #include "propertieswidget.h" #include "trackersadditiondlg.h" -#include "iconprovider.h" -#include "qbtsession.h" +#include "guiiconprovider.h" +#include "core/bittorrent/session.h" +#include "core/bittorrent/torrenthandle.h" +#include "core/bittorrent/peerinfo.h" +#include "core/bittorrent/trackerentry.h" #include "core/preferences.h" #include "core/misc.h" #include "autoexpandabledialog.h" -using namespace libtorrent; - TrackerList::TrackerList(PropertiesWidget *properties): QTreeWidget(), properties(properties) { // Graphical settings setRootIsDecorated(false); @@ -108,8 +107,8 @@ void TrackerList::setRowColor(int row, QColor color) { } void TrackerList::moveSelectionUp() { - QTorrentHandle h = properties->getCurrentTorrent(); - if (!h.is_valid()) { + BitTorrent::TorrentHandle *const torrent = properties->getCurrentTorrent(); + if (!torrent) { clear(); return; } @@ -131,23 +130,23 @@ void TrackerList::moveSelectionUp() { } setSelectionModel(selection); // Update torrent trackers - std::vector trackers; - for (int i=NB_STICKY_ITEM; i trackers; + for (int i = NB_STICKY_ITEM; i < topLevelItemCount(); ++i) { QString tracker_url = topLevelItem(i)->data(COL_URL, Qt::DisplayRole).toString(); - announce_entry e(tracker_url.toStdString()); - e.tier = i-NB_STICKY_ITEM; - trackers.push_back(e); + BitTorrent::TrackerEntry e(tracker_url); + e.setTier(i - NB_STICKY_ITEM); + trackers.append(e); } - h.replace_trackers(trackers); + + torrent->replaceTrackers(trackers); // Reannounce - if (!h.is_paused()) - h.force_reannounce(); - loadTrackers(); + if (!torrent->isPaused()) + torrent->forceReannounce(); } void TrackerList::moveSelectionDown() { - QTorrentHandle h = properties->getCurrentTorrent(); - if (!h.is_valid()) { + BitTorrent::TorrentHandle *const torrent = properties->getCurrentTorrent(); + if (!torrent) { clear(); return; } @@ -169,18 +168,18 @@ void TrackerList::moveSelectionDown() { } setSelectionModel(selection); // Update torrent trackers - std::vector trackers; - for (int i=NB_STICKY_ITEM; i trackers; + for (int i = NB_STICKY_ITEM; i < topLevelItemCount(); ++i) { QString tracker_url = topLevelItem(i)->data(COL_URL, Qt::DisplayRole).toString(); - announce_entry e(tracker_url.toStdString()); - e.tier = i-NB_STICKY_ITEM; - trackers.push_back(e); + BitTorrent::TrackerEntry e(tracker_url); + e.setTier(i - NB_STICKY_ITEM); + trackers.append(e); } - h.replace_trackers(trackers); + + torrent->replaceTrackers(trackers); // Reannounce - if (!h.is_paused()) - h.force_reannounce(); - loadTrackers(); + if (!torrent->isPaused()) + torrent->forceReannounce(); } void TrackerList::clear() { @@ -197,29 +196,29 @@ void TrackerList::clear() { lsd_item->setText(COL_MSG, ""); } -void TrackerList::loadStickyItems(const QTorrentHandle &h) { +void TrackerList::loadStickyItems(BitTorrent::TorrentHandle *const torrent) { QString working = tr("Working"); QString disabled = tr("Disabled"); // load DHT information - if (QBtSession::instance()->isDHTEnabled() && !h.priv()) + if (BitTorrent::Session::instance()->isDHTEnabled() && !torrent->isPrivate()) dht_item->setText(COL_STATUS, working); else dht_item->setText(COL_STATUS, disabled); // Load PeX Information - if (QBtSession::instance()->isPexEnabled() && !h.priv()) + if (BitTorrent::Session::instance()->isPexEnabled() && !torrent->isPrivate()) pex_item->setText(COL_STATUS, working); else pex_item->setText(COL_STATUS, disabled); // Load LSD Information - if (QBtSession::instance()->isLSDEnabled() && !h.priv()) + if (BitTorrent::Session::instance()->isLSDEnabled() && !torrent->isPrivate()) lsd_item->setText(COL_STATUS, working); else lsd_item->setText(COL_STATUS, disabled); - if (h.priv()) { + if (torrent->isPrivate()) { QString privateMsg = tr("This torrent is private"); dht_item->setText(COL_MSG, privateMsg); pex_item->setText(COL_MSG, privateMsg); @@ -229,16 +228,12 @@ void TrackerList::loadStickyItems(const QTorrentHandle &h) { // XXX: libtorrent should provide this info... // Count peers from DHT, LSD, PeX uint nb_dht = 0, nb_lsd = 0, nb_pex = 0; - std::vector peers; - h.get_peer_info(peers); - std::vector::iterator it = peers.begin(); - std::vector::iterator end = peers.end(); - for ( ; it != end; ++it) { - if (it->source & peer_info::dht) + foreach (const BitTorrent::PeerInfo &peer, torrent->peers()) { + if (peer.fromDHT()) ++nb_dht; - if (it->source & peer_info::lsd) + if (peer.fromLSD()) ++nb_lsd; - if (it->source & peer_info::pex) + if (peer.fromPeX()) ++nb_pex; } dht_item->setText(COL_PEERS, QString::number(nb_dht)); @@ -248,47 +243,47 @@ void TrackerList::loadStickyItems(const QTorrentHandle &h) { void TrackerList::loadTrackers() { // Load trackers from torrent handle - QTorrentHandle h = properties->getCurrentTorrent(); - if (!h.is_valid()) return; - loadStickyItems(h); + BitTorrent::TorrentHandle *const torrent = properties->getCurrentTorrent(); + if (!torrent) return; + + loadStickyItems(torrent); // Load actual trackers information - QHash trackers_data = QBtSession::instance()->getTrackersInfo(h.hash()); + QHash trackers_data = torrent->trackerInfos(); QStringList old_trackers_urls = tracker_items.keys(); - const std::vector trackers = h.trackers(); - std::vector::const_iterator it = trackers.begin(); - std::vector::const_iterator end = trackers.end(); - for ( ; it != end; ++it) { - QString tracker_url = misc::toQString(it->url); - QTreeWidgetItem *item = tracker_items.value(tracker_url, 0); + foreach (const BitTorrent::TrackerEntry &entry, torrent->trackers()) { + QString trackerUrl = entry.url(); + QTreeWidgetItem *item = tracker_items.value(trackerUrl, 0); if (!item) { item = new QTreeWidgetItem(); - item->setText(COL_URL, tracker_url); + item->setText(COL_URL, trackerUrl); addTopLevelItem(item); - tracker_items[tracker_url] = item; + tracker_items[trackerUrl] = item; } else { - old_trackers_urls.removeOne(tracker_url); + old_trackers_urls.removeOne(trackerUrl); } - item->setText(COL_TIER, QString::number(it->tier)); - TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url)); - QString error_message = data.last_message.trimmed(); - if (it->verified) { - item->setText(COL_STATUS, tr("Working")); - item->setText(COL_MSG, ""); - } else { - if (it->updating && it->fails == 0) { + item->setText(COL_TIER, QString::number(entry.tier())); + BitTorrent::TrackerInfo data = trackers_data.value(trackerUrl); + QString error_message = data.lastMessage.trimmed(); + switch (entry.status()) { + case BitTorrent::TrackerEntry::Working: + item->setText(COL_STATUS, tr("Working")); + item->setText(COL_MSG, ""); + break; + case BitTorrent::TrackerEntry::Updating: item->setText(COL_STATUS, tr("Updating...")); item->setText(COL_MSG, ""); - } else { - if (it->fails > 0) { - item->setText(COL_STATUS, tr("Not working")); - item->setText(COL_MSG, error_message); - } else { - item->setText(COL_STATUS, tr("Not contacted yet")); - item->setText(COL_MSG, ""); - } - } + break; + case BitTorrent::TrackerEntry::NotWorking: + item->setText(COL_STATUS, tr("Not working")); + item->setText(COL_MSG, error_message); + break; + case BitTorrent::TrackerEntry::NotContacted: + item->setText(COL_STATUS, tr("Not contacted yet")); + item->setText(COL_MSG, ""); + break; } - item->setText(COL_PEERS, QString::number(trackers_data.value(tracker_url, TrackerInfos(tracker_url)).num_peers)); + + item->setText(COL_PEERS, QString::number(trackers_data.value(trackerUrl).numPeers)); } // Remove old trackers foreach (const QString &tracker, old_trackers_urls) { @@ -298,12 +293,13 @@ void TrackerList::loadTrackers() { // Ask the user for new trackers and add them to the torrent void TrackerList::askForTrackers() { - QTorrentHandle h = properties->getCurrentTorrent(); - if (!h.is_valid()) return; - QString hash = h.hash(); - QStringList trackers = TrackersAdditionDlg::askForTrackers(h); - QBtSession::instance()->addTrackersAndUrlSeeds(hash, trackers, QStringList()); - loadTrackers(); + BitTorrent::TorrentHandle *const torrent = properties->getCurrentTorrent(); + if (!torrent) return; + + QList trackers; + foreach (const QString &tracker, TrackersAdditionDlg::askForTrackers(torrent)) + trackers << tracker; + torrent->addTrackers(trackers); } void TrackerList::copyTrackerUrl() { @@ -320,14 +316,15 @@ void TrackerList::copyTrackerUrl() { void TrackerList::deleteSelectedTrackers() { - QTorrentHandle h = properties->getCurrentTorrent(); - if (!h.is_valid()) { + BitTorrent::TorrentHandle *const torrent = properties->getCurrentTorrent(); + if (!torrent) { clear(); return; } - QString hash = h.hash(); + QList selected_items = getSelectedTrackerItems(); if (selected_items.isEmpty()) return; + QStringList urls_to_remove; foreach (QTreeWidgetItem *item, selected_items) { QString tracker_url = item->data(COL_URL, Qt::DisplayRole).toString(); @@ -335,32 +332,26 @@ void TrackerList::deleteSelectedTrackers() { tracker_items.remove(tracker_url); delete item; } - // Iterate of trackers and remove selected ones - std::vector remaining_trackers; - std::vector trackers = h.trackers(); - std::vector::iterator it = trackers.begin(); - std::vector::iterator itend = trackers.end(); - for ( ; it != itend; ++it) { - if (!urls_to_remove.contains(misc::toQString((*it).url))) { - remaining_trackers.push_back(*it); + // Iterate of trackers and remove selected ones + QList remaining_trackers; + QList trackers = torrent->trackers(); + foreach (const BitTorrent::TrackerEntry &entry, trackers) { + if (!urls_to_remove.contains(entry.url())) { + remaining_trackers.push_back(entry); } } - h.replace_trackers(remaining_trackers); - if (!urls_to_remove.empty()) - emit trackersRemoved(urls_to_remove, hash); - if (remaining_trackers.empty()) - emit trackerlessChange(true, hash); - if (!h.is_paused()) - h.force_reannounce(); - // Reload Trackers - loadTrackers(); + + torrent->replaceTrackers(remaining_trackers); + if (!torrent->isPaused()) + torrent->forceReannounce(); } void TrackerList::editSelectedTracker() { - try { - QTorrentHandle h = properties->getCurrentTorrent(); - QString hash = h.hash(); + BitTorrent::TorrentHandle *const torrent = properties->getCurrentTorrent(); + if (!torrent) return; + + QString hash = torrent->hash(); QList selected_items = getSelectedTrackerItems(); if (selected_items.isEmpty()) @@ -381,91 +372,78 @@ void TrackerList::editSelectedTracker() { if (new_tracker_url == tracker_url) return; - std::vector trackers = h.trackers(); - std::vector::iterator it = trackers.begin(); - std::vector::iterator itend = trackers.end(); + QList trackers = torrent->trackers(); bool match = false; - - for ( ; it != itend; ++it) { - if (new_tracker_url == QUrl(misc::toQString(it->url))) { + for (int i = 0; i < trackers.size(); ++i) { + BitTorrent::TrackerEntry &entry = trackers[i]; + if (new_tracker_url == QUrl(entry.url())) { QMessageBox::warning(this, tr("Tracker editing failed"), tr("The tracker URL already exists.")); return; } - if (tracker_url == QUrl(misc::toQStringU(it->url)) && !match) { - announce_entry new_entry(new_tracker_url.toString().toUtf8().constData()); - new_entry.tier = it->tier; + if (tracker_url == QUrl(entry.url()) && !match) { + BitTorrent::TrackerEntry new_entry(new_tracker_url.toString()); + new_entry.setTier(entry.tier()); match = true; - *it = new_entry; - emit trackersRemoved(QStringList(tracker_url.toString()), hash); - emit trackersAdded(QStringList(new_tracker_url.toString()), hash); + entry = new_entry; } } - h.replace_trackers(trackers); - if (!h.is_paused()) { - h.force_reannounce(); - h.force_dht_announce(); + torrent->replaceTrackers(trackers); + if (!torrent->isPaused()) { + torrent->forceReannounce(); + torrent->forceDHTAnnounce(); } - } catch(invalid_handle&) { - return; - } - - loadTrackers(); } #if LIBTORRENT_VERSION_NUM >= 10000 void TrackerList::reannounceSelected() { - try { - QTorrentHandle h = properties->getCurrentTorrent(); + BitTorrent::TorrentHandle *const torrent = properties->getCurrentTorrent(); + if (!torrent) return; QList selected_items = getSelectedTrackerItems(); - if (selected_items.isEmpty()) - return; + if (selected_items.isEmpty()) return; - std::vector trackers = h.trackers(); - for (size_t i = 0; i < trackers.size(); ++i) { + QList trackers = torrent->trackers(); + for (int i = 0; i < trackers.size(); ++i) { foreach (QTreeWidgetItem* w, selected_items) { - if (w->text(COL_URL) == misc::toQString(trackers[i].url)) { - h.force_reannounce(0, i); + if (w->text(COL_URL) == trackers[i].url()) { + torrent->forceReannounce(i); break; } } } - } catch(invalid_handle&) { - return; - } - loadTrackers(); + loadTrackers(); } #endif void TrackerList::showTrackerListMenu(QPoint) { - QTorrentHandle h = properties->getCurrentTorrent(); - if (!h.is_valid()) return; + BitTorrent::TorrentHandle *const torrent = properties->getCurrentTorrent(); + if (!torrent) return; //QList selected_items = getSelectedTrackerItems(); QMenu menu; // Add actions - QAction *addAct = menu.addAction(IconProvider::instance()->getIcon("list-add"), tr("Add a new tracker...")); + QAction *addAct = menu.addAction(GuiIconProvider::instance()->getIcon("list-add"), tr("Add a new tracker...")); QAction *copyAct = 0; QAction *delAct = 0; QAction *editAct = 0; if (!getSelectedTrackerItems().isEmpty()) { - delAct = menu.addAction(IconProvider::instance()->getIcon("list-remove"), tr("Remove tracker")); - copyAct = menu.addAction(IconProvider::instance()->getIcon("edit-copy"), tr("Copy tracker url")); - editAct = menu.addAction(IconProvider::instance()->getIcon("edit-rename"),tr("Edit selected tracker URL")); + delAct = menu.addAction(GuiIconProvider::instance()->getIcon("list-remove"), tr("Remove tracker")); + copyAct = menu.addAction(GuiIconProvider::instance()->getIcon("edit-copy"), tr("Copy tracker url")); + editAct = menu.addAction(GuiIconProvider::instance()->getIcon("edit-rename"),tr("Edit selected tracker URL")); } #if LIBTORRENT_VERSION_NUM >= 10000 QAction *reannounceSelAct = NULL; #endif QAction *reannounceAct = NULL; - if (!h.is_paused()) { + if (!torrent->isPaused()) { #if LIBTORRENT_VERSION_NUM >= 10000 - reannounceSelAct = menu.addAction(IconProvider::instance()->getIcon("view-refresh"), tr("Force reannounce to selected trackers")); + reannounceSelAct = menu.addAction(GuiIconProvider::instance()->getIcon("view-refresh"), tr("Force reannounce to selected trackers")); #endif menu.addSeparator(); - reannounceAct = menu.addAction(IconProvider::instance()->getIcon("view-refresh"), tr("Force reannounce to all trackers")); + reannounceAct = menu.addAction(GuiIconProvider::instance()->getIcon("view-refresh"), tr("Force reannounce to all trackers")); } QAction *act = menu.exec(QCursor::pos()); if (act == 0) return; @@ -488,7 +466,7 @@ void TrackerList::showTrackerListMenu(QPoint) { } #endif if (act == reannounceAct) { - properties->getCurrentTorrent().force_reannounce(); + properties->getCurrentTorrent()->forceReannounce(); return; } if (act == editAct) { diff --git a/src/gui/properties/trackerlist.h b/src/gui/properties/trackerlist.h index d1b01b92e..a02ddbb10 100644 --- a/src/gui/properties/trackerlist.h +++ b/src/gui/properties/trackerlist.h @@ -37,12 +37,16 @@ #include #include -#include "qtorrenthandle.h" #include "propertieswidget.h" enum TrackerListColumn {COL_TIER, COL_URL, COL_STATUS, COL_PEERS, COL_MSG}; #define NB_STICKY_ITEM 3 +namespace BitTorrent +{ + class TorrentHandle; +} + class TrackerList: public QTreeWidget { Q_OBJECT Q_DISABLE_COPY(TrackerList) @@ -60,11 +64,6 @@ public: TrackerList(PropertiesWidget *properties); ~TrackerList(); -signals: - void trackersAdded(const QStringList &trackers, const QString &hash); - void trackersRemoved(const QStringList &trackers, const QString &hash); - void trackerlessChange(bool trackerless, const QString &hash); - protected: QList getSelectedTrackerItems() const; @@ -75,7 +74,7 @@ public slots: void moveSelectionDown(); void clear(); - void loadStickyItems(const QTorrentHandle &h); + void loadStickyItems(BitTorrent::TorrentHandle *const torrent); void loadTrackers(); void askForTrackers(); void copyTrackerUrl(); diff --git a/src/gui/properties/trackersadditiondlg.h b/src/gui/properties/trackersadditiondlg.h index 11efe3761..81fb6b469 100644 --- a/src/gui/properties/trackersadditiondlg.h +++ b/src/gui/properties/trackersadditiondlg.h @@ -36,24 +36,26 @@ #include #include #include -#include "iconprovider.h" +#include "guiiconprovider.h" #include "core/misc.h" #include "ui_trackersadditiondlg.h" -#include "core/downloadthread.h" -#include "qtorrenthandle.h" +#include "core/net/downloadmanager.h" +#include "core/net/downloadhandler.h" +#include "core/bittorrent/trackerentry.h" +#include "core/bittorrent/torrenthandle.h" #include "core/fs_utils.h" class TrackersAdditionDlg : public QDialog, private Ui::TrackersAdditionDlg{ Q_OBJECT private: - QTorrentHandle h; + BitTorrent::TorrentHandle *const m_torrent; public: - TrackersAdditionDlg(QTorrentHandle h, QWidget *parent=0): QDialog(parent), h(h) { + TrackersAdditionDlg(BitTorrent::TorrentHandle *const torrent, QWidget *parent = 0): QDialog(parent), m_torrent(torrent) { setupUi(this); // Icons - uTorrentListButton->setIcon(IconProvider::instance()->getIcon("download")); + uTorrentListButton->setIcon(GuiIconProvider::instance()->getIcon("download")); } ~TrackersAdditionDlg() {} @@ -65,41 +67,33 @@ public: public slots: void on_uTorrentListButton_clicked() { uTorrentListButton->setEnabled(false); - DownloadThread *d = new DownloadThread(this); - connect(d, SIGNAL(downloadFinished(QString,QString)), SLOT(parseUTorrentList(QString,QString))); - connect(d, SIGNAL(downloadFailure(QString,QString)), SLOT(getTrackerError(QString,QString))); + Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(QString("http://www.torrentz.com/announce_%1").arg(m_torrent->hash())); + connect(handler, SIGNAL(downloadFinished(QString, QString)), this, SLOT(parseUTorrentList(QString, QString))); + connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(getTrackerError(QString, QString))); //Just to show that it takes times setCursor(Qt::WaitCursor); - d->downloadUrl("http://www.torrentz.com/announce_"+h.hash()); } - void parseUTorrentList(QString, QString path) { + void parseUTorrentList(const QString &, const QString &path) { QFile list_file(path); if (!list_file.open(QFile::ReadOnly)) { QMessageBox::warning(this, tr("I/O Error"), tr("Error while trying to open the downloaded file."), QMessageBox::Ok); setCursor(Qt::ArrowCursor); uTorrentListButton->setEnabled(true); - sender()->deleteLater(); fsutils::forceRemove(path); return; } - QList existingTrackers; - // Load from torrent handle - std::vector tor_trackers = h.trackers(); - std::vector::iterator itr = tor_trackers.begin(); - std::vector::iterator itrend = tor_trackers.end(); - while(itr != itrend) { - existingTrackers << QUrl(misc::toQString(itr->url)); - ++itr; - } + // Load from torrent handle + QList existingTrackers = m_torrent->trackers(); // Load from current user list QStringList tmp = trackers_list->toPlainText().split("\n"); - foreach (const QString &user_url_str, tmp) { - QUrl user_url(user_url_str); - if (!existingTrackers.contains(user_url)) - existingTrackers << user_url; + foreach (const QString &user_url, tmp) { + BitTorrent::TrackerEntry userTracker(user_url); + if (!existingTrackers.contains(userTracker)) + existingTrackers << userTracker; } + // Add new trackers to the list if (!trackers_list->toPlainText().isEmpty() && !trackers_list->toPlainText().endsWith("\n")) trackers_list->insertPlainText("\n"); @@ -107,8 +101,8 @@ public slots: while (!list_file.atEnd()) { const QByteArray line = list_file.readLine().trimmed(); if (line.isEmpty()) continue; - QUrl url(line); - if (!existingTrackers.contains(url)) { + BitTorrent::TrackerEntry newTracker(line); + if (!existingTrackers.contains(newTracker)) { trackers_list->insertPlainText(line + "\n"); ++nb; } @@ -123,22 +117,20 @@ public slots: if (nb == 0) { QMessageBox::information(this, tr("No change"), tr("No additional trackers were found."), QMessageBox::Ok); } - sender()->deleteLater(); } - void getTrackerError(const QString&, const QString &error) { + void getTrackerError(const QString &, const QString &error) { //To restore the cursor ... setCursor(Qt::ArrowCursor); uTorrentListButton->setEnabled(true); QMessageBox::warning(this, tr("Download error"), tr("The trackers list could not be downloaded, reason: %1").arg(error), QMessageBox::Ok); - sender()->deleteLater(); } public: - static QStringList askForTrackers(QTorrentHandle h) { + static QStringList askForTrackers(BitTorrent::TorrentHandle *const torrent) { QStringList trackers; - TrackersAdditionDlg dlg(h); + TrackersAdditionDlg dlg(torrent); if (dlg.exec() == QDialog::Accepted) { return dlg.newTrackers(); } diff --git a/src/gui/rss/automatedrssdownloader.cpp b/src/gui/rss/automatedrssdownloader.cpp index 496329f0a..75ff98421 100644 --- a/src/gui/rss/automatedrssdownloader.cpp +++ b/src/gui/rss/automatedrssdownloader.cpp @@ -40,7 +40,7 @@ #include "core/preferences.h" #include "rssmanager.h" #include "rssfeed.h" -#include "iconprovider.h" +#include "guiiconprovider.h" #include "autoexpandabledialog.h" #include "core/fs_utils.h" @@ -51,8 +51,8 @@ AutomatedRssDownloader::AutomatedRssDownloader(const QWeakPointer& m { ui->setupUi(this); // Icons - ui->removeRuleBtn->setIcon(IconProvider::instance()->getIcon("list-remove")); - ui->addRuleBtn->setIcon(IconProvider::instance()->getIcon("list-add")); + ui->removeRuleBtn->setIcon(GuiIconProvider::instance()->getIcon("list-remove")); + ui->addRuleBtn->setIcon(GuiIconProvider::instance()->getIcon("list-add")); // Ui Settings ui->listRules->setSortingEnabled(true); @@ -436,17 +436,17 @@ void AutomatedRssDownloader::displayRulesListMenu(const QPoint &pos) { Q_UNUSED(pos); QMenu menu; - QAction *addAct = menu.addAction(IconProvider::instance()->getIcon("list-add"), tr("Add new rule...")); + QAction *addAct = menu.addAction(GuiIconProvider::instance()->getIcon("list-add"), tr("Add new rule...")); QAction *delAct = 0; QAction *renameAct = 0; const QList selection = ui->listRules->selectedItems(); if (!selection.isEmpty()) { if (selection.count() == 1) { - delAct = menu.addAction(IconProvider::instance()->getIcon("list-remove"), tr("Delete rule")); + delAct = menu.addAction(GuiIconProvider::instance()->getIcon("list-remove"), tr("Delete rule")); menu.addSeparator(); - renameAct = menu.addAction(IconProvider::instance()->getIcon("edit-rename"), tr("Rename rule...")); + renameAct = menu.addAction(GuiIconProvider::instance()->getIcon("edit-rename"), tr("Rename rule...")); } else { - delAct = menu.addAction(IconProvider::instance()->getIcon("list-remove"), tr("Delete selected rules")); + delAct = menu.addAction(GuiIconProvider::instance()->getIcon("list-remove"), tr("Delete selected rules")); } } QAction *act = menu.exec(QCursor::pos()); @@ -557,7 +557,7 @@ void AutomatedRssDownloader::addFeedArticlesToTree(const RssFeedPtr& feed, const QFont f = treeFeedItem->font(0); f.setBold(true); treeFeedItem->setFont(0, f); - treeFeedItem->setData(0, Qt::DecorationRole, IconProvider::instance()->getIcon("inode-directory")); + treeFeedItem->setData(0, Qt::DecorationRole, GuiIconProvider::instance()->getIcon("inode-directory")); treeFeedItem->setData(0, Qt::UserRole, feed->url()); ui->treeMatchingArticles->addTopLevelItem(treeFeedItem); } @@ -606,7 +606,7 @@ void AutomatedRssDownloader::updateMustLineValidity() ui->lbl_must_stat->setPixmap(QPixmap()); } else { ui->lineContains->setStyleSheet("QLineEdit { color: #ff0000; }"); - ui->lbl_must_stat->setPixmap(IconProvider::instance()->getIcon("task-attention").pixmap(16, 16)); + ui->lbl_must_stat->setPixmap(GuiIconProvider::instance()->getIcon("task-attention").pixmap(16, 16)); } } @@ -631,7 +631,7 @@ void AutomatedRssDownloader::updateMustNotLineValidity() ui->lbl_mustnot_stat->setPixmap(QPixmap()); } else { ui->lineNotContains->setStyleSheet("QLineEdit { color: #ff0000; }"); - ui->lbl_mustnot_stat->setPixmap(IconProvider::instance()->getIcon("task-attention").pixmap(16, 16)); + ui->lbl_mustnot_stat->setPixmap(GuiIconProvider::instance()->getIcon("task-attention").pixmap(16, 16)); } } diff --git a/src/gui/rss/cookiesdlg.cpp b/src/gui/rss/cookiesdlg.cpp index 7890550d9..4782821f3 100644 --- a/src/gui/rss/cookiesdlg.cpp +++ b/src/gui/rss/cookiesdlg.cpp @@ -30,7 +30,7 @@ #include "cookiesdlg.h" #include "ui_cookiesdlg.h" -#include "iconprovider.h" +#include "guiiconprovider.h" #include @@ -42,8 +42,8 @@ CookiesDlg::CookiesDlg(QWidget *parent, const QList &raw_cookies) : { ui->setupUi(this); // Icons - ui->add_btn->setIcon(IconProvider::instance()->getIcon("list-add")); - ui->del_btn->setIcon(IconProvider::instance()->getIcon("list-remove")); + ui->add_btn->setIcon(GuiIconProvider::instance()->getIcon("list-add")); + ui->del_btn->setIcon(GuiIconProvider::instance()->getIcon("list-remove")); ui->infos_lbl->setText(tr("Common keys for cookies are : '%1', '%2'.\nYou should get this information from your Web browser preferences.").arg("uid").arg("pass")); foreach (const QByteArray &raw_cookie, raw_cookies) { diff --git a/src/gui/rss/feedlistwidget.cpp b/src/gui/rss/feedlistwidget.cpp index 259502593..f86ac9864 100644 --- a/src/gui/rss/feedlistwidget.cpp +++ b/src/gui/rss/feedlistwidget.cpp @@ -31,7 +31,7 @@ #include "feedlistwidget.h" #include "rssmanager.h" #include "rssfeed.h" -#include "iconprovider.h" +#include "guiiconprovider.h" FeedListWidget::FeedListWidget(QWidget *parent, const RssManagerPtr& rssmanager): QTreeWidget(parent), m_rssManager(rssmanager) { setContextMenuPolicy(Qt::CustomContextMenu); @@ -41,7 +41,7 @@ FeedListWidget::FeedListWidget(QWidget *parent, const RssManagerPtr& rssmanager) headerItem()->setText(0, tr("RSS feeds")); m_unreadStickyItem = new QTreeWidgetItem(this); m_unreadStickyItem->setText(0, tr("Unread") + QString::fromUtf8(" (") + QString::number(rssmanager->unreadCount())+ QString(")")); - m_unreadStickyItem->setData(0,Qt::DecorationRole, IconProvider::instance()->getIcon("mail-folder-inbox")); + m_unreadStickyItem->setData(0,Qt::DecorationRole, GuiIconProvider::instance()->getIcon("mail-folder-inbox")); itemAdded(m_unreadStickyItem, rssmanager); connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), SLOT(updateCurrentFeed(QTreeWidgetItem*))); setCurrentItem(m_unreadStickyItem); diff --git a/src/gui/rss/rss_imp.cpp b/src/gui/rss/rss_imp.cpp index f5a1de187..1c055ce23 100644 --- a/src/gui/rss/rss_imp.cpp +++ b/src/gui/rss/rss_imp.cpp @@ -39,7 +39,8 @@ #include "rss_imp.h" #include "feedlistwidget.h" -#include "qbtsession.h" +#include "core/bittorrent/session.h" +#include "core/net/downloadmanager.h" #include "cookiesdlg.h" #include "core/preferences.h" #include "rsssettingsdlg.h" @@ -49,8 +50,9 @@ #include "rssparser.h" #include "rssfeed.h" #include "automatedrssdownloader.h" -#include "iconprovider.h" +#include "guiiconprovider.h" #include "autoexpandabledialog.h" +#include "addnewtorrentdialog.h" namespace Article { @@ -140,6 +142,7 @@ void RSSImp::on_actionManage_cookies_triggered() if (ok) { qDebug() << "Settings cookies for host name: " << feed_hostname; pref->setHostNameCookies(feed_hostname, raw_cookies); + Net::DownloadManager::instance()->setCookiesFromUrl(pref->getHostNameQNetworkCookies(feed_hostname), feed_hostname); } } @@ -343,17 +346,10 @@ void RSSImp::downloadSelectedTorrents() QString torrentLink = article->torrentUrl(); // Check if it is a magnet link - if (torrentLink.startsWith("magnet:", Qt::CaseInsensitive)) { - QBtSession::instance()->addMagnetInteractive(torrentLink); - } - else { - // Load possible cookies - QString feed_url = m_feedList->getItemID(m_feedList->selectedItems().first()); - QString feed_hostname = QUrl::fromEncoded(feed_url.toUtf8()).host(); - QList cookies = Preferences::instance()->getHostNameQNetworkCookies(feed_hostname); - qDebug("Loaded %d cookies for RSS item\n", cookies.size()); - QBtSession::instance()->downloadFromUrl(torrentLink, cookies); - } + if (torrentLink.startsWith("magnet:", Qt::CaseInsensitive) && Preferences::instance()->useAdditionDialog()) + AddNewTorrentDialog::show(torrentLink); + else + BitTorrent::Session::instance()->addTorrent(torrentLink); } } @@ -700,22 +696,22 @@ RSSImp::RSSImp(QWidget *parent): { setupUi(this); // Icons - actionCopy_feed_URL->setIcon(IconProvider::instance()->getIcon("edit-copy")); - actionDelete->setIcon(IconProvider::instance()->getIcon("edit-delete")); - actionDownload_torrent->setIcon(IconProvider::instance()->getIcon("download")); - actionManage_cookies->setIcon(IconProvider::instance()->getIcon("preferences-web-browser-cookies")); - actionMark_items_read->setIcon(IconProvider::instance()->getIcon("mail-mark-read")); - actionNew_folder->setIcon(IconProvider::instance()->getIcon("folder-new")); - actionNew_subscription->setIcon(IconProvider::instance()->getIcon("list-add")); - actionOpen_news_URL->setIcon(IconProvider::instance()->getIcon("application-x-mswinurl")); - actionRename->setIcon(IconProvider::instance()->getIcon("edit-rename")); - actionUpdate->setIcon(IconProvider::instance()->getIcon("view-refresh")); - actionUpdate_all_feeds->setIcon(IconProvider::instance()->getIcon("view-refresh")); - newFeedButton->setIcon(IconProvider::instance()->getIcon("list-add")); - markReadButton->setIcon(IconProvider::instance()->getIcon("mail-mark-read")); - updateAllButton->setIcon(IconProvider::instance()->getIcon("view-refresh")); - rssDownloaderBtn->setIcon(IconProvider::instance()->getIcon("download")); - settingsButton->setIcon(IconProvider::instance()->getIcon("preferences-system")); + actionCopy_feed_URL->setIcon(GuiIconProvider::instance()->getIcon("edit-copy")); + actionDelete->setIcon(GuiIconProvider::instance()->getIcon("edit-delete")); + actionDownload_torrent->setIcon(GuiIconProvider::instance()->getIcon("download")); + actionManage_cookies->setIcon(GuiIconProvider::instance()->getIcon("preferences-web-browser-cookies")); + actionMark_items_read->setIcon(GuiIconProvider::instance()->getIcon("mail-mark-read")); + actionNew_folder->setIcon(GuiIconProvider::instance()->getIcon("folder-new")); + actionNew_subscription->setIcon(GuiIconProvider::instance()->getIcon("list-add")); + actionOpen_news_URL->setIcon(GuiIconProvider::instance()->getIcon("application-x-mswinurl")); + actionRename->setIcon(GuiIconProvider::instance()->getIcon("edit-rename")); + actionUpdate->setIcon(GuiIconProvider::instance()->getIcon("view-refresh")); + actionUpdate_all_feeds->setIcon(GuiIconProvider::instance()->getIcon("view-refresh")); + newFeedButton->setIcon(GuiIconProvider::instance()->getIcon("list-add")); + markReadButton->setIcon(GuiIconProvider::instance()->getIcon("mail-mark-read")); + updateAllButton->setIcon(GuiIconProvider::instance()->getIcon("view-refresh")); + rssDownloaderBtn->setIcon(GuiIconProvider::instance()->getIcon("download")); + settingsButton->setIcon(GuiIconProvider::instance()->getIcon("preferences-system")); m_feedList = new FeedListWidget(splitter_h, m_rssManager); splitter_h->insertWidget(0, m_feedList); diff --git a/src/gui/rss/rssfeed.cpp b/src/gui/rss/rssfeed.cpp index 36b8f67b9..91b8751c1 100644 --- a/src/gui/rss/rssfeed.cpp +++ b/src/gui/rss/rssfeed.cpp @@ -31,7 +31,7 @@ #include #include "rssfeed.h" #include "rssmanager.h" -#include "qbtsession.h" +#include "core/bittorrent/session.h" #include "rssfolder.h" #include "core/preferences.h" #include "core/qinisettings.h" @@ -39,7 +39,8 @@ #include "rssparser.h" #include "core/misc.h" #include "rssdownloadrulelist.h" -#include "core/downloadthread.h" +#include "core/net/downloadmanager.h" +#include "core/net/downloadhandler.h" #include "core/fs_utils.h" #include "core/logger.h" @@ -60,15 +61,15 @@ RssFeed::RssFeed(RssManager* manager, RssFolder* parent, const QString& url): { qDebug() << Q_FUNC_INFO << m_url; // Listen for new RSS downloads - connect(manager->rssDownloader(), SIGNAL(downloadFinished(QString,QString)), SLOT(handleFinishedDownload(QString,QString))); - connect(manager->rssDownloader(), SIGNAL(downloadFailure(QString,QString)), SLOT(handleDownloadFailure(QString,QString))); connect(manager->rssParser(), SIGNAL(feedTitle(QString,QString)), SLOT(handleFeedTitle(QString,QString))); connect(manager->rssParser(), SIGNAL(newArticle(QString,QVariantHash)), SLOT(handleNewArticle(QString,QVariantHash))); connect(manager->rssParser(), SIGNAL(feedParsingFinished(QString,QString)), SLOT(handleFeedParsingFinished(QString,QString))); // Download the RSS Feed icon - m_iconUrl = iconUrl(); - manager->rssDownloader()->downloadUrl(m_iconUrl); + Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(iconUrl()); + connect(handler, SIGNAL(downloadFinished(QString, QString)), this, SLOT(handleFinishedDownload(QString, QString))); + connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString))); + m_iconUrl = handler->url(); // Load old RSS articles loadItemsFromDisk(); @@ -159,12 +160,6 @@ void RssFeed::addArticle(const RssArticlePtr& article) { } } -QList RssFeed::feedCookies() const -{ - QString feed_hostname = QUrl::fromEncoded(m_url.toUtf8()).host(); - return Preferences::instance()->getHostNameQNetworkCookies(feed_hostname); -} - bool RssFeed::refresh() { if (m_loading) { @@ -173,7 +168,10 @@ bool RssFeed::refresh() } m_loading = true; // Download the RSS again - m_manager->rssDownloader()->downloadUrl(m_url, feedCookies()); + Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(m_url); + connect(handler, SIGNAL(downloadFinished(QString, QString)), this, SLOT(handleFinishedDownload(QString, QString))); + connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString))); + m_url = handler->url(); // sync URL encoding return true; } @@ -307,7 +305,7 @@ QString RssFeed::iconUrl() const } // read and store the downloaded rss' informations -void RssFeed::handleFinishedDownload(const QString& url, const QString& filePath) +void RssFeed::handleFinishedDownload(const QString &url, const QString &filePath) { if (url == m_url) { qDebug() << Q_FUNC_INFO << "Successfully downloaded RSS feed at" << url; @@ -320,10 +318,9 @@ void RssFeed::handleFinishedDownload(const QString& url, const QString& filePath } } -void RssFeed::handleDownloadFailure(const QString& url, const QString& error) +void RssFeed::handleDownloadFailure(const QString &url, const QString &error) { - if (url != m_url) - return; + if (url != m_url) return; m_inErrorState = true; m_loading = false; @@ -369,12 +366,17 @@ void RssFeed::downloadArticleTorrentIfMatching(RssDownloadRuleList* rules, const // Download the torrent const QString& torrent_url = article->torrentUrl(); Logger::instance()->addMessage(tr("Automatically downloading %1 torrent from %2 RSS feed...").arg(article->title()).arg(displayName())); - connect(QBtSession::instance(), SIGNAL(newDownloadedTorrentFromRss(QString)), article.data(), SLOT(handleTorrentDownloadSuccess(const QString&)), Qt::UniqueConnection); + connect(BitTorrent::Session::instance(), SIGNAL(downloadFromUrlFinished(QString)), article.data(), SLOT(handleTorrentDownloadSuccess(const QString&)), Qt::UniqueConnection); connect(article.data(), SIGNAL(articleWasRead()), SLOT(handleArticleStateChanged()), Qt::UniqueConnection); - if (torrent_url.startsWith("magnet:", Qt::CaseInsensitive)) - QBtSession::instance()->addMagnetSkipAddDlg(torrent_url, matching_rule->savePath(), matching_rule->label(), matching_rule->addPaused()); - else - QBtSession::instance()->downloadUrlAndSkipDialog(torrent_url, matching_rule->savePath(), matching_rule->label(), feedCookies(), matching_rule->addPaused()); + + BitTorrent::AddTorrentParams params; + params.savePath = matching_rule->savePath(); + params.label = matching_rule->label(); + if (matching_rule->addPaused() == RssDownloadRule::ALWAYS_PAUSED) + params.addPaused = TriStateBool::True; + else if (matching_rule->addPaused() == RssDownloadRule::NEVER_PAUSED) + params.addPaused = TriStateBool::False; + BitTorrent::Session::instance()->addTorrent(torrent_url, params); } void RssFeed::recheckRssItemsForDownload() @@ -426,7 +428,8 @@ void RssFeed::handleFeedParsingFinished(const QString& feedUrl, const QString& e saveItemsToDisk(); } -void RssFeed::handleArticleStateChanged() { +void RssFeed::handleArticleStateChanged() +{ m_manager->forwardFeedInfosChanged(m_url, displayName(), m_unreadCount); } diff --git a/src/gui/rss/rssfeed.h b/src/gui/rss/rssfeed.h index e0d42befe..81e53a5b2 100644 --- a/src/gui/rss/rssfeed.h +++ b/src/gui/rss/rssfeed.h @@ -81,8 +81,8 @@ public: void recheckRssItemsForDownload(); private slots: - void handleFinishedDownload(const QString& url, const QString &file_path); - void handleDownloadFailure(const QString &url, const QString& error); + void handleFinishedDownload(const QString &url, const QString &filePath); + void handleDownloadFailure(const QString &url, const QString &error); void handleFeedTitle(const QString& feedUrl, const QString& title); void handleNewArticle(const QString& feedUrl, const QVariantHash& article); void handleFeedParsingFinished(const QString& feedUrl, const QString& error); @@ -93,7 +93,6 @@ private: void loadItemsFromDisk(); void addArticle(const RssArticlePtr& article); void downloadArticleTorrentIfMatching(RssDownloadRuleList* rules, const RssArticlePtr& article); - QList feedCookies() const; private: RssManager* m_manager; diff --git a/src/gui/rss/rssfolder.cpp b/src/gui/rss/rssfolder.cpp index d6b73d8d6..cb5c9cae0 100644 --- a/src/gui/rss/rssfolder.cpp +++ b/src/gui/rss/rssfolder.cpp @@ -30,10 +30,10 @@ #include -#include "iconprovider.h" +#include "guiiconprovider.h" #include "rssfolder.h" #include "rssarticle.h" -#include "qbtsession.h" +#include "core/bittorrent/session.h" #include "rssmanager.h" #include "rssfeed.h" @@ -233,7 +233,7 @@ QString RssFolder::id() const QIcon RssFolder::icon() const { - return IconProvider::instance()->getIcon("inode-directory"); + return GuiIconProvider::instance()->getIcon("inode-directory"); } bool RssFolder::hasChild(const QString &childId) { diff --git a/src/gui/rss/rssmanager.cpp b/src/gui/rss/rssmanager.cpp index 42e59f3a3..5ec47edbe 100644 --- a/src/gui/rss/rssmanager.cpp +++ b/src/gui/rss/rssmanager.cpp @@ -31,17 +31,15 @@ #include #include "rssmanager.h" #include "core/preferences.h" -#include "qbtsession.h" +#include "core/bittorrent/session.h" #include "rssfeed.h" #include "rssarticle.h" #include "rssdownloadrulelist.h" #include "rssparser.h" -#include "core/downloadthread.h" static const int MSECS_PER_MIN = 60000; RssManager::RssManager(): - m_rssDownloader(new DownloadThread(this)), m_downloadRules(new RssDownloadRuleList), m_rssParser(new RssParser(this)) { @@ -60,11 +58,6 @@ RssManager::~RssManager() qDebug("RSSManager deleted"); } -DownloadThread* RssManager::rssDownloader() const -{ - return m_rssDownloader; -} - RssParser* RssManager::rssParser() const { return m_rssParser; diff --git a/src/gui/rss/rssmanager.h b/src/gui/rss/rssmanager.h index f539a1801..4a8252ccb 100644 --- a/src/gui/rss/rssmanager.h +++ b/src/gui/rss/rssmanager.h @@ -36,7 +36,6 @@ #include "rssfolder.h" -class DownloadThread; class RssDownloadRuleList; class RssParser; @@ -50,7 +49,6 @@ public: RssManager(); virtual ~RssManager(); - DownloadThread* rssDownloader() const; RssParser* rssParser() const; RssDownloadRuleList* downloadRules() const; @@ -71,7 +69,6 @@ signals: private: QTimer m_refreshTimer; uint m_refreshInterval; - DownloadThread* m_rssDownloader; RssDownloadRuleList* m_downloadRules; RssParser* m_rssParser; }; diff --git a/src/gui/rss/rssparser.cpp b/src/gui/rss/rssparser.cpp index d0e85a64a..2cd6b43b9 100644 --- a/src/gui/rss/rssparser.cpp +++ b/src/gui/rss/rssparser.cpp @@ -29,8 +29,8 @@ */ #include "rssparser.h" -#include "core/downloadthread.h" #include "core/fs_utils.h" + #include #include #include diff --git a/src/gui/shutdownconfirm.cpp b/src/gui/shutdownconfirm.cpp index 07dcfd549..636340f38 100644 --- a/src/gui/shutdownconfirm.cpp +++ b/src/gui/shutdownconfirm.cpp @@ -34,7 +34,7 @@ #include -ShutdownConfirmDlg::ShutdownConfirmDlg(const shutDownAction &action) +ShutdownConfirmDlg::ShutdownConfirmDlg(const ShutDownAction &action) : m_exitNow(0) , m_timeout(15) , m_action(action) @@ -71,7 +71,7 @@ void ShutdownConfirmDlg::showEvent(QShowEvent *event) m_timer.start(); } -bool ShutdownConfirmDlg::askForConfirmation(const shutDownAction &action) +bool ShutdownConfirmDlg::askForConfirmation(const ShutDownAction &action) { ShutdownConfirmDlg dlg(action); dlg.exec(); diff --git a/src/gui/shutdownconfirm.h b/src/gui/shutdownconfirm.h index a8013f846..9495f41a8 100644 --- a/src/gui/shutdownconfirm.h +++ b/src/gui/shutdownconfirm.h @@ -40,10 +40,10 @@ class ShutdownConfirmDlg : public QMessageBox Q_OBJECT public: - ShutdownConfirmDlg(const shutDownAction &action); + ShutdownConfirmDlg(const ShutDownAction &action); bool shutdown() const; - static bool askForConfirmation(const shutDownAction &action); + static bool askForConfirmation(const ShutDownAction &action); QAbstractButton *getExit_now() const; void setExit_now(QAbstractButton *value); @@ -62,7 +62,7 @@ private: QAbstractButton *m_exitNow; QTimer m_timer; int m_timeout; - shutDownAction m_action; + ShutDownAction m_action; }; #endif // SHUTDOWNCONFIRM_H diff --git a/src/gui/speedlimitdlg.h b/src/gui/speedlimitdlg.h index f084d330b..fd3c36026 100644 --- a/src/gui/speedlimitdlg.h +++ b/src/gui/speedlimitdlg.h @@ -35,7 +35,7 @@ #include #include "ui_bandwidth_limit.h" #include "core/misc.h" -#include "qbtsession.h" +#include "core/bittorrent/session.h" class SpeedLimitDialog : public QDialog, private Ui_bandwidth_dlg { Q_OBJECT diff --git a/src/gui/statsdialog.cpp b/src/gui/statsdialog.cpp index b093c8595..01f957f60 100644 --- a/src/gui/statsdialog.cpp +++ b/src/gui/statsdialog.cpp @@ -32,14 +32,18 @@ #include "ui_statsdialog.h" #include "core/misc.h" -#include -#include +#include "core/bittorrent/session.h" +#include "core/bittorrent/sessionstatus.h" +#include "core/bittorrent/cachestatus.h" +#include "core/bittorrent/torrenthandle.h" -StatsDialog::StatsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::StatsDialog) { +StatsDialog::StatsDialog(QWidget *parent) + : QDialog(parent) + , ui(new Ui::StatsDialog) +{ ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); connect(ui->buttonOK, SIGNAL(clicked()), SLOT(close())); - session = QBtSession::instance(); updateUI(); t = new QTimer(this); t->setInterval(1500); @@ -55,17 +59,16 @@ StatsDialog::~StatsDialog() { } void StatsDialog::updateUI() { - libtorrent::session* s = session->getSession(); - libtorrent::cache_status cache = s->get_cache_status(); - libtorrent::session_status ss = s->status(); + BitTorrent::SessionStatus ss = BitTorrent::Session::instance()->status(); + BitTorrent::CacheStatus cs = BitTorrent::Session::instance()->cacheStatus(); // Alltime DL/UL - quint64 atd = session->getAlltimeDL(); - quint64 atu = session->getAlltimeUL(); + quint64 atd = BitTorrent::Session::instance()->getAlltimeDL(); + quint64 atu = BitTorrent::Session::instance()->getAlltimeUL(); ui->labelAlltimeDL->setText(misc::friendlyUnit(atd)); ui->labelAlltimeUL->setText(misc::friendlyUnit(atu)); // Total waste (this session) - ui->labelWaste->setText(misc::friendlyUnit(ss.total_redundant_bytes + ss.total_failed_bytes)); + ui->labelWaste->setText(misc::friendlyUnit(ss.totalWasted())); // Global ratio ui->labelGlobalRatio->setText( ( atd > 0 && atu > 0 ) ? @@ -73,39 +76,30 @@ void StatsDialog::updateUI() { "-" ); // Cache hits - ui->labelCacheHits->setText( - ( cache.blocks_read > 0 && cache.blocks_read_hit > 0 ) ? - misc::accurateDoubleToString(100. * (qreal)cache.blocks_read_hit / (qreal)cache.blocks_read, 2) : - "-" - ); + qreal readRatio = cs.readRatio(); + ui->labelCacheHits->setText((readRatio >= 0) ? misc::accurateDoubleToString(100 * readRatio, 2) : "-"); // Buffers size - ui->labelTotalBuf->setText(misc::friendlyUnit(cache.total_used_buffers * 16 * 1024)); + ui->labelTotalBuf->setText(misc::friendlyUnit(cs.totalUsedBuffers() * 16 * 1024)); // Disk overload (100%) equivalent // From lt manual: disk_write_queue and disk_read_queue are the number of peers currently waiting on a disk write or disk read // to complete before it receives or sends any more data on the socket. It'a a metric of how disk bound you are. // num_peers is not reliable (adds up peers, which didn't even overcome tcp handshake) - const std::vector torrents = session->getTorrents(); - std::vector::const_iterator iBegin = torrents.begin(); - std::vector::const_iterator iEnd = torrents.end(); quint32 peers = 0; - for ( ; iBegin < iEnd ; ++iBegin) - peers += (*iBegin).status().num_peers; - ui->labelWriteStarve->setText( - ( ss.disk_write_queue > 0 && peers > 0 ) ? - misc::accurateDoubleToString(100. * (qreal)ss.disk_write_queue / (qreal)peers, 2) + "%" : - QString("0\%") - ); - ui->labelReadStarve->setText( - ( ss.disk_read_queue > 0 && peers > 0 ) ? - misc::accurateDoubleToString(100. * (qreal)ss.disk_read_queue / (qreal)peers, 2) + "%" : - QString("0\%") - ); + foreach (BitTorrent::TorrentHandle *const torrent, BitTorrent::Session::instance()->torrents()) + peers += torrent->peersCount(); + + ui->labelWriteStarve->setText(QString("%1%").arg(((ss.diskWriteQueue() > 0) && (peers > 0)) + ? misc::accurateDoubleToString((100. * ss.diskWriteQueue()) / peers, 2) + : "0")); + ui->labelReadStarve->setText(QString("%1%").arg(((ss.diskReadQueue() > 0) && (peers > 0)) + ? misc::accurateDoubleToString((100. * ss.diskReadQueue()) / peers, 2) + : "0")); // Disk queues - ui->labelQueuedJobs->setText(QString::number(cache.job_queue_length)); - ui->labelJobsTime->setText(QString::number(cache.average_job_time)); - ui->labelQueuedBytes->setText(misc::friendlyUnit(cache.queued_bytes)); + ui->labelQueuedJobs->setText(QString::number(cs.jobQueueLength())); + ui->labelJobsTime->setText(QString::number(cs.averageJobTime())); + ui->labelQueuedBytes->setText(misc::friendlyUnit(cs.queuedBytes())); // Total connected peers - ui->labelPeers->setText(QString::number(ss.num_peers)); + ui->labelPeers->setText(QString::number(ss.peersCount())); } diff --git a/src/gui/statsdialog.h b/src/gui/statsdialog.h index 004fde022..af41a5999 100644 --- a/src/gui/statsdialog.h +++ b/src/gui/statsdialog.h @@ -33,7 +33,6 @@ #include #include -#include "qbtsession.h" namespace Ui { class StatsDialog; @@ -51,7 +50,6 @@ private slots: private: Ui::StatsDialog *ui; - QBtSession* session; QTimer* t; }; diff --git a/src/gui/statusbar.cpp b/src/gui/statusbar.cpp index aee363506..7d13b1a7c 100644 --- a/src/gui/statusbar.cpp +++ b/src/gui/statusbar.cpp @@ -33,21 +33,19 @@ #include #include -#include "qbtsession.h" +#include "core/bittorrent/session.h" +#include "core/bittorrent/sessionstatus.h" #include "speedlimitdlg.h" -#include "iconprovider.h" +#include "guiiconprovider.h" #include "core/preferences.h" #include "core/misc.h" #include "core/logger.h" -#include -#include - StatusBar::StatusBar(QStatusBar *bar) : m_bar(bar) { Preferences* const pref = Preferences::instance(); - connect(QBtSession::instance(), SIGNAL(alternativeSpeedsModeChanged(bool)), this, SLOT(updateAltSpeedsBtn(bool))); + connect(BitTorrent::Session::instance(), SIGNAL(speedLimitModeChanged(bool)), this, SLOT(updateAltSpeedsBtn(bool))); container = new QWidget(bar); layout = new QHBoxLayout(container); layout->setContentsMargins(0,0,0,0); @@ -159,12 +157,12 @@ void StatusBar::stopTimer() { void StatusBar::refreshStatusBar() { // Update connection status - const libtorrent::session_status sessionStatus = QBtSession::instance()->getSessionStatus(); - if (!QBtSession::instance()->getSession()->is_listening()) { + const BitTorrent::SessionStatus sessionStatus = BitTorrent::Session::instance()->status(); + if (!BitTorrent::Session::instance()->isListening()) { connecStatusLblIcon->setIcon(QIcon(QString::fromUtf8(":/icons/skin/disconnected.png"))); connecStatusLblIcon->setToolTip(QString::fromUtf8("")+tr("Connection Status:")+QString::fromUtf8("
")+tr("Offline. This usually means that qBittorrent failed to listen on the selected port for incoming connections.")); } else { - if (sessionStatus.has_incoming_connections) { + if (sessionStatus.hasIncomingConnections()) { // Connection OK connecStatusLblIcon->setIcon(QIcon(QString::fromUtf8(":/icons/skin/connected.png"))); connecStatusLblIcon->setToolTip(QString::fromUtf8("")+tr("Connection Status:")+QString::fromUtf8("
")+tr("Online")); @@ -174,22 +172,22 @@ void StatusBar::refreshStatusBar() { } } // Update Number of DHT nodes - if (QBtSession::instance()->isDHTEnabled()) { + if (BitTorrent::Session::instance()->isDHTEnabled()) { DHTLbl->setVisible(true); //statusSep1->setVisible(true); - DHTLbl->setText(tr("DHT: %1 nodes").arg(QString::number(sessionStatus.dht_nodes))); + DHTLbl->setText(tr("DHT: %1 nodes").arg(QString::number(sessionStatus.dhtNodes()))); } else { DHTLbl->setVisible(false); //statusSep1->setVisible(false); } // Update speed labels - QString speedLbl = misc::friendlyUnit(sessionStatus.payload_download_rate, true)+" ("+misc::friendlyUnit(sessionStatus.total_payload_download)+")"; - int speedLimit = QBtSession::instance()->getSession()->settings().download_rate_limit; + QString speedLbl = misc::friendlyUnit(sessionStatus.payloadDownloadRate(), true)+" ("+misc::friendlyUnit(sessionStatus.totalPayloadDownload())+")"; + int speedLimit = BitTorrent::Session::instance()->downloadRateLimit(); if (speedLimit) speedLbl = "["+misc::friendlyUnit(speedLimit, true)+"] " + speedLbl; dlSpeedLbl->setText(speedLbl); - speedLimit = QBtSession::instance()->getSession()->settings().upload_rate_limit; - speedLbl = misc::friendlyUnit(sessionStatus.payload_upload_rate, true)+" ("+misc::friendlyUnit(sessionStatus.total_payload_upload)+")"; + speedLimit = BitTorrent::Session::instance()->uploadRateLimit(); + speedLbl = misc::friendlyUnit(sessionStatus.payloadUploadRate(), true)+" ("+misc::friendlyUnit(sessionStatus.totalPayloadUpload())+")"; if (speedLimit) speedLbl = "["+misc::friendlyUnit(speedLimit, true)+"] " + speedLbl; upSpeedLbl->setText(speedLbl); @@ -214,26 +212,26 @@ void StatusBar::toggleAlternativeSpeeds() { pref->setSchedulerEnabled(false); m_bar->showMessage(tr("Manual change of rate limits mode. The scheduler is disabled."), 5000); } - QBtSession::instance()->useAlternativeSpeedsLimit(!pref->isAltBandwidthEnabled()); + BitTorrent::Session::instance()->changeSpeedLimitMode(!pref->isAltBandwidthEnabled()); } void StatusBar::capDownloadSpeed() { bool ok = false; - int cur_limit = QBtSession::instance()->getSession()->settings().download_rate_limit; + int cur_limit = BitTorrent::Session::instance()->downloadRateLimit(); long new_limit = SpeedLimitDialog::askSpeedLimit(&ok, tr("Global Download Speed Limit"), cur_limit); if (ok) { Preferences* const pref = Preferences::instance(); bool alt = pref->isAltBandwidthEnabled(); if (new_limit <= 0) { qDebug("Setting global download rate limit to Unlimited"); - QBtSession::instance()->setDownloadRateLimit(-1); + BitTorrent::Session::instance()->setDownloadRateLimit(-1); if (!alt) pref->setGlobalDownloadLimit(-1); else pref->setAltGlobalDownloadLimit(-1); } else { qDebug("Setting global download rate limit to %.1fKb/s", new_limit/1024.); - QBtSession::instance()->setDownloadRateLimit(new_limit); + BitTorrent::Session::instance()->setDownloadRateLimit(new_limit); if (!alt) pref->setGlobalDownloadLimit(new_limit/1024.); else @@ -245,21 +243,21 @@ void StatusBar::capDownloadSpeed() { void StatusBar::capUploadSpeed() { bool ok = false; - int cur_limit = QBtSession::instance()->getSession()->settings().upload_rate_limit; + int cur_limit = BitTorrent::Session::instance()->uploadRateLimit(); long new_limit = SpeedLimitDialog::askSpeedLimit(&ok, tr("Global Upload Speed Limit"), cur_limit); if (ok) { Preferences* const pref = Preferences::instance(); bool alt = pref->isAltBandwidthEnabled(); if (new_limit <= 0) { qDebug("Setting global upload rate limit to Unlimited"); - QBtSession::instance()->setUploadRateLimit(-1); + BitTorrent::Session::instance()->setUploadRateLimit(-1); if (!alt) Preferences::instance()->setGlobalUploadLimit(-1); else Preferences::instance()->setAltGlobalUploadLimit(-1); } else { qDebug("Setting global upload rate limit to %.1fKb/s", new_limit/1024.); - QBtSession::instance()->setUploadRateLimit(new_limit); + BitTorrent::Session::instance()->setUploadRateLimit(new_limit); if (!alt) Preferences::instance()->setGlobalUploadLimit(new_limit/1024.); else diff --git a/src/gui/torrentcontentmodel.cpp b/src/gui/torrentcontentmodel.cpp index 83f775075..94f0f3228 100644 --- a/src/gui/torrentcontentmodel.cpp +++ b/src/gui/torrentcontentmodel.cpp @@ -28,23 +28,25 @@ * Contact : chris@qbittorrent.org */ -#include "iconprovider.h" +#include +#include + +#include "guiiconprovider.h" #include "core/misc.h" #include "core/fs_utils.h" #include "torrentcontentmodel.h" #include "torrentcontentmodelitem.h" #include "torrentcontentmodelfolder.h" #include "torrentcontentmodelfile.h" -#include namespace { QIcon get_directory_icon() { - static QIcon cached = IconProvider::instance()->getIcon("inode-directory"); + static QIcon cached = GuiIconProvider::instance()->getIcon("inode-directory"); return cached; } QIcon get_file_icon() { - static QIcon cached = IconProvider::instance()->getIcon("text-plain"); + static QIcon cached = GuiIconProvider::instance()->getIcon("text-plain"); return cached; } } @@ -61,15 +63,14 @@ TorrentContentModel::~TorrentContentModel() delete m_rootItem; } -void TorrentContentModel::updateFilesProgress(const std::vector& fp) +void TorrentContentModel::updateFilesProgress(const QVector &fp) { - Q_ASSERT(m_filesIndex.size() == (int)fp.size()); + Q_ASSERT(m_filesIndex.size() == fp.size()); // XXX: Why is this necessary? - if (m_filesIndex.size() != (int)fp.size()) - return; + if (m_filesIndex.size() != fp.size()) return; emit layoutAboutToBeChanged(); - for (uint i = 0; i < fp.size(); ++i) { + for (int i = 0; i < fp.size(); ++i) { m_filesIndex[i]->setProgress(fp[i]); } // Update folders progress in the tree @@ -77,7 +78,7 @@ void TorrentContentModel::updateFilesProgress(const std::vector& fprio) +void TorrentContentModel::updateFilesPriorities(const QVector &fprio) { Q_ASSERT(m_filesIndex.size() == (int)fprio.size()); // XXX: Why is this necessary? @@ -85,15 +86,15 @@ void TorrentContentModel::updateFilesPriorities(const std::vector& fprio) return; emit layoutAboutToBeChanged(); - for (uint i = 0; i < fprio.size(); ++i) { + for (int i = 0; i < fprio.size(); ++i) { m_filesIndex[i]->setPriority(fprio[i]); } emit dataChanged(index(0, 0), index(rowCount(), columnCount())); } -std::vector TorrentContentModel::getFilesPriorities() const +QVector TorrentContentModel::getFilePriorities() const { - std::vector prio; + QVector prio; prio.reserve(m_filesIndex.size()); foreach (const TorrentContentModelFile* file, m_filesIndex) { prio.push_back(file->priority()); @@ -280,23 +281,22 @@ void TorrentContentModel::clear() endResetModel(); } -void TorrentContentModel::setupModelData(const libtorrent::torrent_info& t) +void TorrentContentModel::setupModelData(const BitTorrent::TorrentInfo &info) { qDebug("setup model data called"); - if (t.num_files() == 0) + if (info.filesCount() == 0) return; emit layoutAboutToBeChanged(); // Initialize files_index array - qDebug("Torrent contains %d files", t.num_files()); - m_filesIndex.reserve(t.num_files()); + qDebug("Torrent contains %d files", info.filesCount()); + m_filesIndex.reserve(info.filesCount()); TorrentContentModelFolder* current_parent; // Iterate over files - for (int i = 0; i < t.num_files(); ++i) { - const libtorrent::file_entry& fentry = t.file_at(i); + for (int i = 0; i < info.filesCount(); ++i) { current_parent = m_rootItem; - QString path = fsutils::fromNativePath(misc::toQStringU(fentry.path)); + QString path = fsutils::fromNativePath(info.filePath(i)); // Iterate of parts of the path to create necessary folders QStringList pathFolders = path.split("/", QString::SkipEmptyParts); pathFolders.removeLast(); @@ -311,7 +311,7 @@ void TorrentContentModel::setupModelData(const libtorrent::torrent_info& t) current_parent = new_parent; } // Actually create the file - TorrentContentModelFile* fileItem = new TorrentContentModelFile(fentry, current_parent, i); + TorrentContentModelFile* fileItem = new TorrentContentModelFile(info.fileName(i), info.fileSize(i), current_parent, i); current_parent->appendChild(fileItem); m_filesIndex.push_back(fileItem); } diff --git a/src/gui/torrentcontentmodel.h b/src/gui/torrentcontentmodel.h index 04c1c1628..25448190a 100644 --- a/src/gui/torrentcontentmodel.h +++ b/src/gui/torrentcontentmodel.h @@ -36,8 +36,7 @@ #include #include -#include - +#include "core/bittorrent/torrentinfo.h" #include "torrentcontentmodelitem.h" class TorrentContentModelFile; @@ -49,9 +48,9 @@ public: TorrentContentModel(QObject *parent = 0); ~TorrentContentModel(); - void updateFilesProgress(const std::vector& fp); - void updateFilesPriorities(const std::vector &fprio); - std::vector getFilesPriorities() const; + void updateFilesProgress(const QVector &fp); + void updateFilesPriorities(const QVector &fprio); + QVector getFilePriorities() const; bool allFiltered() const; virtual int columnCount(const QModelIndex &parent=QModelIndex()) const; virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); @@ -64,7 +63,7 @@ public: virtual QModelIndex parent(const QModelIndex& index) const; virtual int rowCount(const QModelIndex& parent = QModelIndex()) const; void clear(); - void setupModelData(const libtorrent::torrent_info& t); + void setupModelData(const BitTorrent::TorrentInfo &info); signals: void filteredFilesChanged(); diff --git a/src/gui/torrentcontentmodelfile.cpp b/src/gui/torrentcontentmodelfile.cpp index e9c2f8701..1d8d1dd0a 100644 --- a/src/gui/torrentcontentmodelfile.cpp +++ b/src/gui/torrentcontentmodelfile.cpp @@ -30,24 +30,21 @@ #include "torrentcontentmodelfile.h" #include "torrentcontentmodelfolder.h" -#include "core/fs_utils.h" -#include "core/misc.h" -TorrentContentModelFile::TorrentContentModelFile(const libtorrent::file_entry& f, - TorrentContentModelFolder* parent, - int file_index) +TorrentContentModelFile::TorrentContentModelFile(const QString &fileName, qulonglong fileSize, + TorrentContentModelFolder* parent, int file_index) : TorrentContentModelItem(parent) , m_fileIndex(file_index) { Q_ASSERT(parent); - m_name = fsutils::fileName(misc::toQStringU(f.path.c_str())); + m_name = fileName; // Do not display incomplete extensions if (m_name.endsWith(".!qB")) m_name.chop(4); - m_size = (qulonglong)f.size; + m_size = fileSize; } int TorrentContentModelFile::fileIndex() const @@ -69,8 +66,8 @@ void TorrentContentModelFile::setPriority(int new_prio, bool update_parent) m_parentItem->updatePriority(); } -void TorrentContentModelFile::setProgress(qulonglong done) +void TorrentContentModelFile::setProgress(qreal progress) { - m_totalDone = done; - Q_ASSERT(m_totalDone <= m_size); + m_progress = progress; + Q_ASSERT(m_progress <= 1.); } diff --git a/src/gui/torrentcontentmodelfile.h b/src/gui/torrentcontentmodelfile.h index 594333e7c..d3861b717 100644 --- a/src/gui/torrentcontentmodelfile.h +++ b/src/gui/torrentcontentmodelfile.h @@ -36,13 +36,12 @@ class TorrentContentModelFile : public TorrentContentModelItem { public: - TorrentContentModelFile(const libtorrent::file_entry& f, - TorrentContentModelFolder* parent, - int file_index); + TorrentContentModelFile(const QString &fileName, qulonglong fileSize, + TorrentContentModelFolder* parent, int file_index); int fileIndex() const; void setPriority(int new_prio, bool update_parent = true); - void setProgress(qulonglong done); + void setProgress(qreal progress); ItemType itemType() const { return FileType; } private: diff --git a/src/gui/torrentcontentmodelfolder.cpp b/src/gui/torrentcontentmodelfolder.cpp index 4d89396ac..82e038ad7 100644 --- a/src/gui/torrentcontentmodelfolder.cpp +++ b/src/gui/torrentcontentmodelfolder.cpp @@ -28,6 +28,7 @@ * Contact : chris@qbittorrent.org */ +#include #include "torrentcontentmodelfolder.h" TorrentContentModelFolder::TorrentContentModelFolder(const QString& name, TorrentContentModelFolder* parent) @@ -135,18 +136,20 @@ void TorrentContentModelFolder::setPriority(int new_prio, bool update_parent) void TorrentContentModelFolder::recalculateProgress() { - qulonglong totalDone = 0; + qreal progress = 0; + int count = 0; foreach (TorrentContentModelItem* child, m_childItems) { if (child->priority() != prio::IGNORED) { if (child->itemType() == FolderType) static_cast(child)->recalculateProgress(); - totalDone += child->totalDone(); + progress += child->progress(); + ++count; } } - if (!isRootItem()) { - m_totalDone = totalDone; - Q_ASSERT(m_totalDone <= m_size); + if (!isRootItem() && (count > 0)) { + m_progress = progress / count; + Q_ASSERT(m_progress <= 1.); } } diff --git a/src/gui/torrentcontentmodelitem.cpp b/src/gui/torrentcontentmodelitem.cpp index 03385389d..7b9c8dbed 100644 --- a/src/gui/torrentcontentmodelitem.cpp +++ b/src/gui/torrentcontentmodelitem.cpp @@ -38,7 +38,7 @@ TorrentContentModelItem::TorrentContentModelItem(TorrentContentModelFolder* pare : m_parentItem(parent) , m_size(0) , m_priority(prio::NORMAL) - , m_totalDone(0) + , m_progress(0) { } @@ -65,21 +65,14 @@ qulonglong TorrentContentModelItem::size() const return m_size; } -qulonglong TorrentContentModelItem::totalDone() const +qreal TorrentContentModelItem::progress() const { - Q_ASSERT(!isRootItem()); - return m_totalDone; -} + Q_ASSERT(!isRootItem()); + if (m_priority == prio::IGNORED) return 0; -float TorrentContentModelItem::progress() const -{ - Q_ASSERT(!isRootItem()); - if (m_priority == prio::IGNORED) - return 0; + if (m_size > 0) return m_progress; - if (m_size > 0) - return m_totalDone / (double) m_size; - return 1; + return 1; } int TorrentContentModelItem::priority() const diff --git a/src/gui/torrentcontentmodelitem.h b/src/gui/torrentcontentmodelitem.h index df9399275..e05825c9f 100644 --- a/src/gui/torrentcontentmodelitem.h +++ b/src/gui/torrentcontentmodelitem.h @@ -34,8 +34,6 @@ #include #include -#include - namespace prio { enum FilePriority {IGNORED=0, NORMAL=1, HIGH=2, MAXIMUM=7, MIXED=-1}; } @@ -58,9 +56,7 @@ public: void setName(const QString& name); qulonglong size() const; - qulonglong totalDone() const; - - float progress() const; + qreal progress() const; int priority() const; virtual void setPriority(int new_prio, bool update_parent = true) = 0; @@ -77,7 +73,7 @@ protected: QString m_name; qulonglong m_size; int m_priority; - qulonglong m_totalDone; + qreal m_progress; }; #endif // TORRENTCONTENTMODELITEM_H diff --git a/src/gui/torrentcreator/torrentcreator.pri b/src/gui/torrentcreator/torrentcreator.pri deleted file mode 100644 index 9be087937..000000000 --- a/src/gui/torrentcreator/torrentcreator.pri +++ /dev/null @@ -1,10 +0,0 @@ -INCLUDEPATH += $$PWD - -FORMS += $$PWD/createtorrent.ui - -HEADERS += $$PWD/torrentcreatordlg.h \ - $$PWD/torrentcreatorthread.h - -SOURCES += $$PWD/torrentcreatordlg.cpp \ - $$PWD/torrentcreatorthread.cpp - diff --git a/src/gui/torrentcreator/torrentcreatordlg.cpp b/src/gui/torrentcreatordlg.cpp similarity index 80% rename from src/gui/torrentcreator/torrentcreatordlg.cpp rename to src/gui/torrentcreatordlg.cpp index 0092b2905..9dd7d5f9a 100644 --- a/src/gui/torrentcreator/torrentcreatordlg.cpp +++ b/src/gui/torrentcreatordlg.cpp @@ -32,41 +32,39 @@ #include #include -#include "core/torrentpersistentdata.h" #include "torrentcreatordlg.h" #include "core/fs_utils.h" #include "core/misc.h" #include "core/preferences.h" -#include "torrentcreatorthread.h" -#include "iconprovider.h" -#include "qbtsession.h" +#include "guiiconprovider.h" +#include "core/bittorrent/session.h" +#include "core/bittorrent/torrentinfo.h" +#include "core/bittorrent/torrentcreatorthread.h" const uint NB_PIECES_MIN = 1200; const uint NB_PIECES_MAX = 2200; -using namespace libtorrent; - -TorrentCreatorDlg::TorrentCreatorDlg(QWidget *parent): QDialog(parent), creatorThread(0) { +TorrentCreatorDlg::TorrentCreatorDlg(QWidget *parent): QDialog(parent), m_creatorThread(0) { setupUi(this); // Icons - addFile_button->setIcon(IconProvider::instance()->getIcon("document-new")); - addFolder_button->setIcon(IconProvider::instance()->getIcon("folder-new")); - createButton->setIcon(IconProvider::instance()->getIcon("document-save")); - cancelButton->setIcon(IconProvider::instance()->getIcon("dialog-cancel")); + addFile_button->setIcon(GuiIconProvider::instance()->getIcon("document-new")); + addFolder_button->setIcon(GuiIconProvider::instance()->getIcon("folder-new")); + createButton->setIcon(GuiIconProvider::instance()->getIcon("document-save")); + cancelButton->setIcon(GuiIconProvider::instance()->getIcon("dialog-cancel")); setAttribute(Qt::WA_DeleteOnClose); setModal(true); showProgressBar(false); loadTrackerList(); // Piece sizes - m_piece_sizes << 32 << 64 << 128 << 256 << 512 << 1024 << 2048 << 4096; + m_pieceSizes << 32 << 64 << 128 << 256 << 512 << 1024 << 2048 << 4096; loadSettings(); show(); } TorrentCreatorDlg::~TorrentCreatorDlg() { - if (creatorThread) - delete creatorThread; + if (m_creatorThread) + delete m_creatorThread; } void TorrentCreatorDlg::on_addFolder_button_clicked() { @@ -96,7 +94,7 @@ void TorrentCreatorDlg::on_addFile_button_clicked() { } int TorrentCreatorDlg::getPieceSize() const { - return m_piece_sizes.at(comboPieceSize->currentIndex())*1024; + return m_pieceSizes.at(comboPieceSize->currentIndex())*1024; } // Main function that create a .torrent file @@ -132,11 +130,11 @@ void TorrentCreatorDlg::on_createButton_clicked() { QStringList url_seeds = URLSeeds_list->toPlainText().split("\n"); QString comment = txt_comment->toPlainText(); // Create the creator thread - creatorThread = new TorrentCreatorThread(this); - connect(creatorThread, SIGNAL(creationSuccess(QString, QString)), this, SLOT(handleCreationSuccess(QString, QString))); - connect(creatorThread, SIGNAL(creationFailure(QString)), this, SLOT(handleCreationFailure(QString))); - connect(creatorThread, SIGNAL(updateProgress(int)), this, SLOT(updateProgressBar(int))); - creatorThread->create(input, destination, trackers, url_seeds, comment, check_private->isChecked(), getPieceSize()); + m_creatorThread = new BitTorrent::TorrentCreatorThread(this); + connect(m_creatorThread, SIGNAL(creationSuccess(QString, QString)), this, SLOT(handleCreationSuccess(QString, QString))); + connect(m_creatorThread, SIGNAL(creationFailure(QString)), this, SLOT(handleCreationFailure(QString))); + connect(m_creatorThread, SIGNAL(updateProgress(int)), this, SLOT(updateProgressBar(int))); + m_creatorThread->create(input, destination, trackers, url_seeds, comment, check_private->isChecked(), getPieceSize()); } void TorrentCreatorDlg::handleCreationFailure(QString msg) { @@ -152,33 +150,31 @@ void TorrentCreatorDlg::handleCreationSuccess(QString path, QString branch_path) setCursor(QCursor(Qt::ArrowCursor)); if (checkStartSeeding->isChecked()) { // Create save path temp data - boost::intrusive_ptr t; - try { - t = new torrent_info(fsutils::toNativePath(path).toUtf8().data()); - } catch(std::exception&) { + BitTorrent::TorrentInfo t = BitTorrent::TorrentInfo::loadFromFile(fsutils::toNativePath(path)); + if (!t.isValid()) { QMessageBox::critical(0, tr("Torrent creation"), tr("Created torrent file is invalid. It won't be added to download list.")); return; } - QString hash = misc::toQString(t->info_hash()); - QString save_path = branch_path; - TorrentTempData::setSavePath(hash, save_path); - // Enable seeding mode (do not recheck the files) - TorrentTempData::setSeedingMode(hash, true); - emit torrent_to_seed(path); - if (checkIgnoreShareLimits->isChecked()) - QBtSession::instance()->setMaxRatioPerTorrent(hash, -1); + + BitTorrent::AddTorrentParams params; + params.savePath = branch_path; + params.skipChecking = true; + params.ignoreShareRatio = checkIgnoreShareLimits->isChecked(); + + BitTorrent::Session::instance()->addTorrent(t, params); } + QMessageBox::information(0, tr("Torrent creation"), tr("Torrent was created successfully:")+" "+fsutils::toNativePath(path)); close(); } void TorrentCreatorDlg::on_cancelButton_clicked() { // End torrent creation thread - if (creatorThread && creatorThread->isRunning()) { - creatorThread->abortCreation(); - creatorThread->terminate(); + if (m_creatorThread && m_creatorThread->isRunning()) { + m_creatorThread->abortCreation(); + m_creatorThread->terminate(); // Wait for termination - creatorThread->wait(); + m_creatorThread->wait(); } // Close the dialog close(); @@ -227,7 +223,7 @@ void TorrentCreatorDlg::updateOptimalPieceSize() int i = 0; qulonglong nb_pieces = 0; do { - nb_pieces = (double)torrent_size/(m_piece_sizes.at(i)*1024.); + nb_pieces = (double)torrent_size/(m_pieceSizes.at(i)*1024.); qDebug("nb_pieces=%lld with piece_size=%s", nb_pieces, qPrintable(comboPieceSize->itemText(i))); if (nb_pieces <= NB_PIECES_MIN) { if (i > 1) @@ -239,7 +235,7 @@ void TorrentCreatorDlg::updateOptimalPieceSize() break; } ++i; - }while(i<(m_piece_sizes.size()-1)); + }while(i<(m_pieceSizes.size()-1)); comboPieceSize->setCurrentIndex(i); } diff --git a/src/gui/torrentcreator/torrentcreatordlg.h b/src/gui/torrentcreatordlg.h similarity index 94% rename from src/gui/torrentcreator/torrentcreatordlg.h rename to src/gui/torrentcreatordlg.h index cda947d52..44a7ecab0 100644 --- a/src/gui/torrentcreator/torrentcreatordlg.h +++ b/src/gui/torrentcreatordlg.h @@ -33,7 +33,10 @@ #include "ui_createtorrent.h" -class TorrentCreatorThread; +namespace BitTorrent +{ + class TorrentCreatorThread; +} class TorrentCreatorDlg : public QDialog, private Ui::createTorrentDialog{ Q_OBJECT @@ -43,9 +46,6 @@ public: ~TorrentCreatorDlg(); int getPieceSize() const; -signals: - void torrent_to_seed(QString path); - public slots: void updateProgressBar(int progress); void on_cancelButton_clicked(); @@ -71,8 +71,8 @@ private: void loadSettings(); private: - TorrentCreatorThread *creatorThread; - QList m_piece_sizes; + BitTorrent::TorrentCreatorThread *m_creatorThread; + QList m_pieceSizes; }; #endif diff --git a/src/gui/torrentimportdlg.cpp b/src/gui/torrentimportdlg.cpp index 12db5eadc..b76cf214b 100644 --- a/src/gui/torrentimportdlg.cpp +++ b/src/gui/torrentimportdlg.cpp @@ -35,22 +35,20 @@ #include "torrentimportdlg.h" #include "ui_torrentimportdlg.h" #include "core/preferences.h" -#include "qbtsession.h" -#include "core/torrentpersistentdata.h" -#include "iconprovider.h" +#include "core/bittorrent/infohash.h" +#include "core/bittorrent/session.h" +#include "guiiconprovider.h" #include "core/fs_utils.h" -using namespace libtorrent; - TorrentImportDlg::TorrentImportDlg(QWidget *parent): QDialog(parent), ui(new Ui::TorrentImportDlg) { ui->setupUi(this); // Icons - ui->lbl_info->setPixmap(IconProvider::instance()->getIcon("dialog-information").pixmap(ui->lbl_info->height())); + ui->lbl_info->setPixmap(GuiIconProvider::instance()->getIcon("dialog-information").pixmap(ui->lbl_info->height())); ui->lbl_info->setFixedWidth(ui->lbl_info->height()); - ui->importBtn->setIcon(IconProvider::instance()->getIcon("document-import")); + ui->importBtn->setIcon(GuiIconProvider::instance()->getIcon("document-import")); // Libtorrent < 0.15 does not support skipping file checking loadSettings(); } @@ -74,13 +72,14 @@ void TorrentImportDlg::on_browseTorrentBtn_clicked() void TorrentImportDlg::on_browseContentBtn_clicked() { const QString default_dir = Preferences::instance()->getTorImportLastContentDir(); - bool multifile = t->num_files() > 1; - if (!multifile && (fsutils::fromNativePath(misc::toQStringU(t->file_at(0).path)).indexOf('/') != -1)) + bool multifile = (m_torrentInfo.filesCount() > 1); + QString filePath = fsutils::fromNativePath(m_torrentInfo.filePath(0)); + if (!multifile && (filePath.indexOf('/') != -1)) multifile = true; if (!multifile) { // Single file torrent - const QString file_name = fsutils::fileName(misc::toQStringU(t->file_at(0).path)); + const QString file_name = fsutils::fileName(filePath); qDebug("Torrent has only one file: %s", qPrintable(file_name)); QString extension = fsutils::fileExtension(file_name); qDebug("File extension is : %s", qPrintable(extension)); @@ -100,7 +99,7 @@ void TorrentImportDlg::on_browseContentBtn_clicked() ui->lineContent->setText(fsutils::toNativePath(m_contentPath)); // Check file size const qint64 file_size = QFile(m_contentPath).size(); - if (t->file_at(0).size == file_size) { + if (m_torrentInfo.fileSize(0) == file_size) { qDebug("The file size matches, allowing fast seeding..."); ui->checkSkipCheck->setEnabled(true); } @@ -122,7 +121,7 @@ void TorrentImportDlg::on_browseContentBtn_clicked() } else { // multiple files torrent - m_contentPath = QFileDialog::getExistingDirectory(this, tr("Please point to the location of the torrent: %1").arg(misc::toQStringU(t->name())), + m_contentPath = QFileDialog::getExistingDirectory(this, tr("Please point to the location of the torrent: %1").arg(m_torrentInfo.name()), default_dir); if (m_contentPath.isEmpty() || !QDir(m_contentPath).exists()) { m_contentPath = QString::null; @@ -136,13 +135,13 @@ void TorrentImportDlg::on_browseContentBtn_clicked() QDir content_dir(m_contentPath); content_dir.cdUp(); // Check file sizes - for (int i = 0; inum_files(); ++i) { - const QString rel_path = misc::toQStringU(t->file_at(i).path); - if (QFile(fsutils::expandPath(content_dir.absoluteFilePath(rel_path))).size() != t->file_at(i).size) { + for (int i = 0; i < m_torrentInfo.filesCount(); ++i) { + const QString rel_path = m_torrentInfo.filePath(i); + if (QFile(fsutils::expandPath(content_dir.absoluteFilePath(rel_path))).size() != m_torrentInfo.fileSize(i)) { qDebug("%s is %lld", qPrintable(fsutils::expandPath(content_dir.absoluteFilePath(rel_path))), (long long int) QFile(fsutils::expandPath(content_dir.absoluteFilePath(rel_path))).size()); qDebug("%s is %lld", - qPrintable(rel_path), (long long int)t->file_at(i).size); + qPrintable(rel_path), (long long int)m_torrentInfo.fileSize(i)); size_mismatch = true; break; } @@ -184,63 +183,57 @@ void TorrentImportDlg::importTorrent() qDebug() << Q_FUNC_INFO << "ENTER"; TorrentImportDlg dlg; if (dlg.exec()) { + BitTorrent::AddTorrentParams params; qDebug() << "Loading the torrent file..."; - boost::intrusive_ptr t = dlg.torrent(); - if (!t->is_valid()) - return; - QString torrent_path = dlg.getTorrentPath(); - QString content_path = dlg.getContentPath(); - if (torrent_path.isEmpty() || content_path.isEmpty() || !QFile(torrent_path).exists()) { - qWarning() << "Incorrect input, aborting." << torrent_path << content_path; + BitTorrent::TorrentInfo torrentInfo = dlg.torrent(); + if (!torrentInfo.isValid()) return; + + QString torrentPath = dlg.getTorrentPath(); + QString contentPath = dlg.getContentPath(); + if (torrentPath.isEmpty() || contentPath.isEmpty() || !QFile(torrentPath).exists()) { + qWarning() << "Incorrect input, aborting." << torrentPath << contentPath; return; } - const QString hash = misc::toQString(t->info_hash()); + + const QString hash = torrentInfo.hash(); qDebug() << "Torrent hash is" << hash; - TorrentTempData::setSavePath(hash, content_path); - TorrentTempData::setSeedingMode(hash, dlg.skipFileChecking()); + params.savePath = contentPath; + params.skipChecking = dlg.skipFileChecking(); + params.disableTempPath = true; qDebug("Adding the torrent to the session..."); - QBtSession::instance()->addTorrent(torrent_path, false, QString(), false, true); + BitTorrent::Session::instance()->addTorrent(torrentInfo, params); // Remember the last opened folder Preferences* const pref = Preferences::instance(); - pref->setMainLastDir(fsutils::fromNativePath(torrent_path)); - pref->setTorImportLastContentDir(fsutils::fromNativePath(content_path)); + pref->setMainLastDir(fsutils::fromNativePath(torrentPath)); + pref->setTorImportLastContentDir(fsutils::fromNativePath(contentPath)); return; } qDebug() << Q_FUNC_INFO << "EXIT"; return; } -void TorrentImportDlg::loadTorrent(const QString &torrent_path) +void TorrentImportDlg::loadTorrent(const QString &torrentPath) { // Load the torrent file - try { - std::vector buffer; - lazy_entry entry; - libtorrent::error_code ec; - misc::loadBencodedFile(torrent_path, buffer, entry, ec); - t = new torrent_info(entry); - if (!t->is_valid() || t->num_files() == 0) - throw std::exception(); - } - catch(std::exception&) { + m_torrentInfo = BitTorrent::TorrentInfo::loadFromFile(torrentPath); + if (!m_torrentInfo.isValid()) { ui->browseContentBtn->setEnabled(false); ui->lineTorrent->clear(); QMessageBox::warning(this, tr("Invalid torrent file"), tr("This is not a valid torrent file.")); - return; } - // Update display - ui->lineTorrent->setText(fsutils::toNativePath(torrent_path)); - ui->browseContentBtn->setEnabled(true); - // Load the file names - initializeFilesPath(); + else { + // Update display + ui->lineTorrent->setText(fsutils::toNativePath(torrentPath)); + ui->browseContentBtn->setEnabled(true); + // Load the file names + initializeFilesPath(); + } } void TorrentImportDlg::initializeFilesPath() { - m_filesPath.clear(); // Loads files path in the torrent - for (int i = 0; inum_files(); ++i) - m_filesPath << fsutils::fromNativePath(misc::toQStringU(t->file_at(i).path)); + m_filesPath = m_torrentInfo.filePaths(); } bool TorrentImportDlg::fileRenamed() const @@ -249,9 +242,9 @@ bool TorrentImportDlg::fileRenamed() const } -boost::intrusive_ptr TorrentImportDlg::torrent() const +BitTorrent::TorrentInfo TorrentImportDlg::torrent() const { - return t; + return m_torrentInfo; } bool TorrentImportDlg::skipFileChecking() const diff --git a/src/gui/torrentimportdlg.h b/src/gui/torrentimportdlg.h index 4e0ff7640..568bb2811 100644 --- a/src/gui/torrentimportdlg.h +++ b/src/gui/torrentimportdlg.h @@ -34,16 +34,12 @@ #include #include -#include -#include +#include "core/bittorrent/torrentinfo.h" -QT_BEGIN_NAMESPACE -namespace Ui { +namespace Ui +{ class TorrentImportDlg; } -QT_END_NAMESPACE - -class QBtSession; class TorrentImportDlg: public QDialog { @@ -52,22 +48,22 @@ class TorrentImportDlg: public QDialog public: explicit TorrentImportDlg(QWidget *parent = 0); ~TorrentImportDlg(); + static void importTorrent(); + QString getTorrentPath() const; QString getContentPath() const; bool fileRenamed() const; - boost::intrusive_ptr torrent() const; + BitTorrent::TorrentInfo torrent() const; bool skipFileChecking() const; protected slots: - void loadTorrent(const QString &torrent_path); + void loadTorrent(const QString &torrentPath); void initializeFilesPath(); private slots: void on_browseTorrentBtn_clicked(); - void on_browseContentBtn_clicked(); - void on_importBtn_clicked(); protected: @@ -79,7 +75,8 @@ private: private: Ui::TorrentImportDlg *ui; - boost::intrusive_ptr t; + BitTorrent::TorrentInfo m_torrentInfo; + // NOTE: Where do we use it? QStringList m_filesPath; QString m_contentPath; QString m_torrentPath; diff --git a/src/gui/torrentmodel.cpp b/src/gui/torrentmodel.cpp index c680206e6..b82cb3bd8 100644 --- a/src/gui/torrentmodel.cpp +++ b/src/gui/torrentmodel.cpp @@ -31,15 +31,12 @@ #include #include #include +#include -#include "torrentmodel.h" -#include "core/torrentpersistentdata.h" -#include "qbtsession.h" +#include "core/bittorrent/session.h" +#include "core/torrentfilter.h" #include "core/fs_utils.h" - -#include - -using namespace libtorrent; +#include "torrentmodel.h" namespace { QIcon get_paused_icon() { @@ -96,110 +93,43 @@ bool isDarkTheme() } } -TorrentStatusReport::TorrentStatusReport() - : nb_downloading(0) - , nb_seeding(0) - , nb_completed(0) - , nb_active(0) - , nb_inactive(0) - , nb_paused(0) +TorrentModelItem::TorrentModelItem(BitTorrent::TorrentHandle *torrent) + : m_torrent(torrent) { } -TorrentModelItem::TorrentModelItem(const QTorrentHandle &h) - : m_torrent(h) - , m_lastStatus(h.status(torrent_handle::query_accurate_download_counters)) - , m_addedTime(TorrentPersistentData::instance()->getAddedDate(h.hash())) - , m_label(TorrentPersistentData::instance()->getLabel(h.hash())) - , m_name(TorrentPersistentData::instance()->getName(h.hash())) - , m_hash(h.hash()) +BitTorrent::TorrentState TorrentModelItem::state() const { - if (m_name.isEmpty()) - m_name = h.name(); - // If name is empty show the hash. This happens when magnet isn't retrieved. - if (m_name.isEmpty()) - m_name = h.hash(); + return m_torrent->state(); } -void TorrentModelItem::refreshStatus(libtorrent::torrent_status const& status) { - m_lastStatus = status; -} - -TorrentModelItem::State TorrentModelItem::state() const { - try { - // Pause or Queued - if (m_torrent.is_paused(m_lastStatus)) { - if (TorrentPersistentData::instance()->getHasMissingFiles(misc::toQString(m_lastStatus.info_hash))) - return STATE_PAUSED_MISSING; - else - return m_torrent.is_seed(m_lastStatus) ? STATE_PAUSED_UP : STATE_PAUSED_DL; - } - - if (m_torrent.is_queued(m_lastStatus) - && m_lastStatus.state != torrent_status::queued_for_checking - && m_lastStatus.state != torrent_status::checking_resume_data - && m_lastStatus.state != torrent_status::checking_files) - return m_torrent.is_seed(m_lastStatus) ? STATE_QUEUED_UP : STATE_QUEUED_DL; - - // Other states - switch(m_lastStatus.state) { - case torrent_status::allocating: - return STATE_ALLOCATING; - case torrent_status::downloading_metadata: - return STATE_DOWNLOADING_META; - case torrent_status::downloading: - if (!m_torrent.is_forced(m_lastStatus)) - return m_lastStatus.download_payload_rate > 0 ? STATE_DOWNLOADING : STATE_STALLED_DL; - else - return STATE_FORCED_DL; - case torrent_status::finished: - case torrent_status::seeding: - if (!m_torrent.is_forced(m_lastStatus)) - return m_lastStatus.upload_payload_rate > 0 ? STATE_SEEDING : STATE_STALLED_UP; - else - return STATE_FORCED_UP; - case torrent_status::queued_for_checking: - return STATE_QUEUED_CHECK; - case torrent_status::checking_resume_data: - return STATE_QUEUED_FASTCHECK; - case torrent_status::checking_files: - return m_torrent.is_seed(m_lastStatus) ? STATE_CHECKING_UP : STATE_CHECKING_DL; - default: - return STATE_INVALID; - } - } catch(invalid_handle&) { - return STATE_INVALID; - } -} - -QIcon TorrentModelItem::getIconByState(State state) { +QIcon TorrentModelItem::getIconByState(BitTorrent::TorrentState state) +{ switch (state) { - case STATE_DOWNLOADING: - case STATE_DOWNLOADING_META: - case STATE_FORCED_DL: + case BitTorrent::TorrentState::Downloading: + case BitTorrent::TorrentState::ForcedDownloading: + case BitTorrent::TorrentState::DownloadingMetadata: return get_downloading_icon(); - case STATE_ALLOCATING: - case STATE_STALLED_DL: + case BitTorrent::TorrentState::Allocating: + case BitTorrent::TorrentState::StalledDownloading: return get_stalled_downloading_icon(); - case STATE_STALLED_UP: + case BitTorrent::TorrentState::StalledUploading: return get_stalled_uploading_icon(); - case STATE_SEEDING: - case STATE_FORCED_UP: + case BitTorrent::TorrentState::Uploading: + case BitTorrent::TorrentState::ForcedUploading: return get_uploading_icon(); - case STATE_PAUSED_DL: + case BitTorrent::TorrentState::PausedDownloading: return get_paused_icon(); - case STATE_PAUSED_UP: + case BitTorrent::TorrentState::PausedUploading: return get_completed_icon(); - case STATE_QUEUED_DL: - case STATE_QUEUED_UP: + case BitTorrent::TorrentState::QueuedDownloading: + case BitTorrent::TorrentState::QueuedUploading: return get_queued_icon(); - case STATE_CHECKING_UP: - case STATE_CHECKING_DL: - case STATE_QUEUED_CHECK: - case STATE_QUEUED_FASTCHECK: + case BitTorrent::TorrentState::CheckingDownloading: + case BitTorrent::TorrentState::CheckingUploading: return get_checking_icon(); - case STATE_INVALID: - case STATE_PAUSED_MISSING: + case BitTorrent::TorrentState::Unknown: + case BitTorrent::TorrentState::Error: return get_error_icon(); default: Q_ASSERT(false); @@ -207,43 +137,43 @@ QIcon TorrentModelItem::getIconByState(State state) { } } -QColor TorrentModelItem::getColorByState(State state) { +QColor TorrentModelItem::getColorByState(BitTorrent::TorrentState state) +{ bool dark = isDarkTheme(); + switch (state) { - case STATE_DOWNLOADING: - case STATE_DOWNLOADING_META: - case STATE_FORCED_DL: + case BitTorrent::TorrentState::Downloading: + case BitTorrent::TorrentState::ForcedDownloading: + case BitTorrent::TorrentState::DownloadingMetadata: return QColor(34, 139, 34); // Forest Green - case STATE_ALLOCATING: - case STATE_STALLED_DL: - case STATE_STALLED_UP: + case BitTorrent::TorrentState::Allocating: + case BitTorrent::TorrentState::StalledDownloading: + case BitTorrent::TorrentState::StalledUploading: if (!dark) return QColor(0, 0, 0); // Black else return QColor(255, 255, 255); // White - case STATE_SEEDING: - case STATE_FORCED_UP: + case BitTorrent::TorrentState::Uploading: + case BitTorrent::TorrentState::ForcedUploading: if (!dark) return QColor(65, 105, 225); // Royal Blue else return QColor(100, 149, 237); // Cornflower Blue - case STATE_PAUSED_DL: + case BitTorrent::TorrentState::PausedDownloading: return QColor(250, 128, 114); // Salmon - case STATE_PAUSED_UP: + case BitTorrent::TorrentState::PausedUploading: if (!dark) return QColor(0, 0, 139); // Dark Blue else return QColor(65, 105, 225); // Royal Blue - case STATE_PAUSED_MISSING: + case BitTorrent::TorrentState::Error: return QColor(255, 0, 0); // red - case STATE_QUEUED_DL: - case STATE_QUEUED_UP: - case STATE_CHECKING_UP: - case STATE_CHECKING_DL: - case STATE_QUEUED_CHECK: - case STATE_QUEUED_FASTCHECK: + case BitTorrent::TorrentState::QueuedDownloading: + case BitTorrent::TorrentState::QueuedUploading: + case BitTorrent::TorrentState::CheckingDownloading: + case BitTorrent::TorrentState::CheckingUploading: return QColor(0, 128, 128); // Teal - case STATE_INVALID: + case BitTorrent::TorrentState::Unknown: return QColor(255, 0, 0); // red default: Q_ASSERT(false); @@ -255,18 +185,17 @@ bool TorrentModelItem::setData(int column, const QVariant &value, int role) { qDebug() << Q_FUNC_INFO << column << value; if (role != Qt::DisplayRole) return false; + // Label, seed date and Name columns can be edited switch(column) { case TR_NAME: - m_name = value.toString(); - TorrentPersistentData::instance()->saveName(m_torrent.hash(), m_name); + m_torrent->setName(value.toString()); return true; case TR_LABEL: { QString new_label = value.toString(); - if (m_label != new_label) { - QString old_label = m_label; - m_label = new_label; - TorrentPersistentData::instance()->saveLabel(m_torrent.hash(), new_label); + if (m_torrent->label() != new_label) { + QString old_label = m_torrent->label(); + m_torrent->setLabel(new_label); emit labelChanged(old_label, new_label); } return true; @@ -279,136 +208,118 @@ bool TorrentModelItem::setData(int column, const QVariant &value, int role) QVariant TorrentModelItem::data(int column, int role) const { - if (role == Qt::DecorationRole && column == TR_NAME) { + if ((role == Qt::DecorationRole) && (column == TR_NAME)) return getIconByState(state()); - } - if (role == Qt::ForegroundRole) { + + if (role == Qt::ForegroundRole) return getColorByState(state()); - } - if (role != Qt::DisplayRole && role != Qt::UserRole) return QVariant(); + + if ((role != Qt::DisplayRole) && (role != Qt::UserRole)) + return QVariant(); + switch(column) { case TR_NAME: - return m_name.isEmpty() ? m_torrent.name() : m_name; - case TR_PRIORITY: { - int pos = m_torrent.queue_position(m_lastStatus); - if (pos > -1) - return pos - HiddenData::getSize(); - else - return pos; - } + return m_torrent->name(); + case TR_PRIORITY: + return m_torrent->queuePosition(); case TR_SIZE: - return m_lastStatus.has_metadata ? static_cast(m_lastStatus.total_wanted) : -1; + return m_torrent->hasMetadata() ? m_torrent->wantedSize() : -1; case TR_PROGRESS: - return m_torrent.progress(m_lastStatus); + return m_torrent->progress(); case TR_STATUS: - return state(); - case TR_SEEDS: { - return (role == Qt::DisplayRole) ? m_lastStatus.num_seeds : m_lastStatus.num_complete; - } - case TR_PEERS: { - return (role == Qt::DisplayRole) ? (m_lastStatus.num_peers-m_lastStatus.num_seeds) : m_lastStatus.num_incomplete; - } + return static_cast(m_torrent->state()); + case TR_SEEDS: + return (role == Qt::DisplayRole) ? m_torrent->seedsCount() : m_torrent->completeCount(); + case TR_PEERS: + return (role == Qt::DisplayRole) ? (m_torrent->peersCount() - m_torrent->seedsCount()) : m_torrent->incompleteCount(); case TR_DLSPEED: - return m_lastStatus.download_payload_rate; + return m_torrent->downloadPayloadRate(); case TR_UPSPEED: - return m_lastStatus.upload_payload_rate; - case TR_ETA: { - // XXX: Is this correct? - if (m_torrent.is_paused(m_lastStatus) || m_torrent.is_queued(m_lastStatus)) return MAX_ETA; - return QBtSession::instance()->getETA(m_hash, m_lastStatus); - } + return m_torrent->uploadPayloadRate(); + case TR_ETA: + return m_torrent->eta(); case TR_RATIO: - return QBtSession::instance()->getRealRatio(m_lastStatus); + return m_torrent->realRatio(); case TR_LABEL: - return m_label; + return m_torrent->label(); case TR_ADD_DATE: - return m_addedTime; + return m_torrent->addedTime(); case TR_SEED_DATE: - return m_lastStatus.completed_time ? QDateTime::fromTime_t(m_lastStatus.completed_time) : QDateTime(); + return m_torrent->completedTime(); case TR_TRACKER: - return misc::toQString(m_lastStatus.current_tracker); + return m_torrent->currentTracker(); case TR_DLLIMIT: - return m_torrent.download_limit(); + return m_torrent->downloadLimit(); case TR_UPLIMIT: - return m_torrent.upload_limit(); + return m_torrent->uploadLimit(); case TR_AMOUNT_DOWNLOADED: - return static_cast(m_lastStatus.all_time_download); + return m_torrent->totalDownload(); case TR_AMOUNT_UPLOADED: - return static_cast(m_lastStatus.all_time_upload); + return m_torrent->totalUpload(); case TR_AMOUNT_DOWNLOADED_SESSION: - return static_cast(m_lastStatus.total_payload_download); + return m_torrent->totalPayloadDownload(); case TR_AMOUNT_UPLOADED_SESSION: - return static_cast(m_lastStatus.total_payload_upload); + return m_torrent->totalPayloadUpload(); case TR_AMOUNT_LEFT: - return static_cast(m_lastStatus.total_wanted - m_lastStatus.total_wanted_done); + return m_torrent->incompletedSize(); case TR_TIME_ELAPSED: - return (role == Qt::DisplayRole) ? m_lastStatus.active_time : m_lastStatus.seeding_time; + return (role == Qt::DisplayRole) ? m_torrent->activeTime() : m_torrent->seedingTime(); case TR_SAVE_PATH: - return fsutils::toNativePath(m_torrent.save_path_parsed()); + return m_torrent->savePathParsed(); case TR_COMPLETED: - return static_cast(m_lastStatus.total_wanted_done); - case TR_RATIO_LIMIT: { - QString hash = misc::toQString(m_lastStatus.info_hash); - return QBtSession::instance()->getMaxRatioPerTorrent(hash, NULL); - } + return m_torrent->completedSize(); + case TR_RATIO_LIMIT: + return m_torrent->maxRatio(); case TR_SEEN_COMPLETE_DATE: - return m_lastStatus.last_seen_complete ? QDateTime::fromTime_t(m_lastStatus.last_seen_complete) : QDateTime(); + return m_torrent->lastSeenComplete(); case TR_LAST_ACTIVITY: - if (m_torrent.is_paused(m_lastStatus) || m_torrent.is_checking(m_lastStatus)) + if (m_torrent->isPaused() || m_torrent->isChecking()) return -1; - if (m_lastStatus.time_since_upload < m_lastStatus.time_since_download) - return m_lastStatus.time_since_upload; + if (m_torrent->timeSinceUpload() < m_torrent->timeSinceDownload()) + return m_torrent->timeSinceUpload(); else - return m_lastStatus.time_since_download; + return m_torrent->timeSinceDownload(); case TR_TOTAL_SIZE: - return m_lastStatus.has_metadata ? static_cast(m_torrent.total_size()) : -1; + return m_torrent->hasMetadata() ? m_torrent->totalSize() : -1; default: return QVariant(); } } -QTorrentHandle TorrentModelItem::torrentHandle() const +BitTorrent::TorrentHandle *TorrentModelItem::torrentHandle() const { return m_torrent; } // TORRENT MODEL -TorrentModel::TorrentModel(QObject *parent) : - QAbstractListModel(parent), m_refreshInterval(2000) +TorrentModel::TorrentModel(QObject *parent) + : QAbstractListModel(parent) { } -void TorrentModel::populate() { +void TorrentModel::populate() +{ // Load the torrents - std::vector torrents = QBtSession::instance()->getSession()->get_torrents(); + foreach (BitTorrent::TorrentHandle *const torrent, BitTorrent::Session::instance()->torrents()) + addTorrent(torrent); - std::vector::const_iterator it = torrents.begin(); - std::vector::const_iterator itend = torrents.end(); - for ( ; it != itend; ++it) { - const QTorrentHandle h(*it); - if (HiddenData::hasData(h.hash())) - continue; - addTorrent(h); - } - // Refresh timer - connect(&m_refreshTimer, SIGNAL(timeout()), SLOT(forceModelRefresh())); - m_refreshTimer.start(m_refreshInterval); // Listen for torrent changes - connect(QBtSession::instance(), SIGNAL(addedTorrent(QTorrentHandle)), SLOT(addTorrent(QTorrentHandle))); - connect(QBtSession::instance(), SIGNAL(torrentAboutToBeRemoved(QTorrentHandle)), SLOT(handleTorrentAboutToBeRemoved(QTorrentHandle))); - connect(QBtSession::instance(), SIGNAL(finishedTorrent(QTorrentHandle)), SLOT(handleFinishedTorrent(QTorrentHandle))); - connect(QBtSession::instance(), SIGNAL(metadataReceived(QTorrentHandle)), SLOT(handleTorrentUpdate(QTorrentHandle))); - connect(QBtSession::instance(), SIGNAL(resumedTorrent(QTorrentHandle)), SLOT(handleTorrentUpdate(QTorrentHandle))); - connect(QBtSession::instance(), SIGNAL(pausedTorrent(QTorrentHandle)), SLOT(handleTorrentUpdate(QTorrentHandle))); - connect(QBtSession::instance(), SIGNAL(torrentFinishedChecking(QTorrentHandle)), SLOT(handleTorrentUpdate(QTorrentHandle))); - connect(QBtSession::instance(), SIGNAL(stateUpdate(std::vector)), SLOT(stateUpdated(std::vector))); + connect(BitTorrent::Session::instance(), SIGNAL(torrentAdded(BitTorrent::TorrentHandle *const)), SLOT(addTorrent(BitTorrent::TorrentHandle *const))); + connect(BitTorrent::Session::instance(), SIGNAL(torrentAboutToBeRemoved(BitTorrent::TorrentHandle *const)), SLOT(handleTorrentAboutToBeRemoved(BitTorrent::TorrentHandle *const))); + connect(BitTorrent::Session::instance(), SIGNAL(torrentStatusUpdated(BitTorrent::TorrentHandle *const)), this, SLOT(handleTorrentStatusUpdated(BitTorrent::TorrentHandle *const))); + + connect(BitTorrent::Session::instance(), SIGNAL(torrentFinished(BitTorrent::TorrentHandle *const)), SLOT(handleTorrentStatusUpdated(BitTorrent::TorrentHandle *const))); + connect(BitTorrent::Session::instance(), SIGNAL(torrentMetadataLoaded(BitTorrent::TorrentHandle *const)), SLOT(handleTorrentStatusUpdated(BitTorrent::TorrentHandle *const))); + connect(BitTorrent::Session::instance(), SIGNAL(torrentResumed(BitTorrent::TorrentHandle *const)), SLOT(handleTorrentStatusUpdated(BitTorrent::TorrentHandle *const))); + connect(BitTorrent::Session::instance(), SIGNAL(torrentPaused(BitTorrent::TorrentHandle *const)), SLOT(handleTorrentStatusUpdated(BitTorrent::TorrentHandle *const))); + connect(BitTorrent::Session::instance(), SIGNAL(torrentFinishedChecking(BitTorrent::TorrentHandle *const)), SLOT(handleTorrentStatusUpdated(BitTorrent::TorrentHandle *const))); } TorrentModel::~TorrentModel() { qDebug() << Q_FUNC_INFO << "ENTER"; - qDeleteAll(m_torrents); - m_torrents.clear(); + qDeleteAll(m_items); + m_items.clear(); qDebug() << Q_FUNC_INFO << "EXIT"; } @@ -485,49 +396,47 @@ QVariant TorrentModel::headerData(int section, Qt::Orientation orientation, QVariant TorrentModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); - try { - if (index.row() >= 0 && index.row() < rowCount() && index.column() >= 0 && index.column() < columnCount()) - return m_torrents[index.row()]->data(index.column(), role); - } catch(invalid_handle&) {} + + if (index.row() >= 0 && index.row() < rowCount() && index.column() >= 0 && index.column() < columnCount()) + return m_items[index.row()]->data(index.column(), role); + return QVariant(); } bool TorrentModel::setData(const QModelIndex &index, const QVariant &value, int role) { qDebug() << Q_FUNC_INFO << value; - if (!index.isValid() || role != Qt::DisplayRole) return false; + if (!index.isValid() || (role != Qt::DisplayRole)) return false; + qDebug("Index is valid and role is DisplayRole"); - try { - if (index.row() >= 0 && index.row() < rowCount() && index.column() >= 0 && index.column() < columnCount()) { - bool change = m_torrents[index.row()]->setData(index.column(), value, role); - if (change) - notifyTorrentChanged(index.row()); - return change; - } - } catch(invalid_handle&) {} + if ((index.row() >= 0) && (index.row() < rowCount()) && (index.column() >= 0) && (index.column() < columnCount())) { + bool change = m_items[index.row()]->setData(index.column(), value, role); + if (change) + notifyTorrentChanged(index.row()); + return change; + } + return false; } -int TorrentModel::torrentRow(const QString &hash) const +int TorrentModel::torrentRow(const BitTorrent::InfoHash &hash) const { int row = 0; - QList::const_iterator it = m_torrents.constBegin(); - QList::const_iterator itend = m_torrents.constEnd(); - for ( ; it != itend; ++it) { - if ((*it)->hash() == hash) return row; + foreach (TorrentModelItem *const item, m_items) { + if (item->hash() == hash) return row; ++row; } return -1; } -void TorrentModel::addTorrent(const QTorrentHandle &h) +void TorrentModel::addTorrent(BitTorrent::TorrentHandle *const torrent) { - if (torrentRow(h.hash()) < 0) { - beginInsertTorrent(m_torrents.size()); - TorrentModelItem *item = new TorrentModelItem(h); - connect(item, SIGNAL(labelChanged(QString,QString)), SLOT(handleTorrentLabelChange(QString,QString))); - m_torrents << item; + if (torrentRow(torrent->hash()) < 0) { + beginInsertTorrent(m_items.size()); + TorrentModelItem *item = new TorrentModelItem(torrent); + connect(item, SIGNAL(labelChanged(QString, QString)), SLOT(handleTorrentLabelChange(QString, QString))); + m_items << item; emit torrentAdded(item); endInsertTorrent(); } @@ -553,95 +462,9 @@ void TorrentModel::endRemoveTorrent() endRemoveRows(); } -void TorrentModel::handleTorrentUpdate(const QTorrentHandle &h) -{ - const int row = torrentRow(h.hash()); - if (row >= 0) { - // This line changes the torrent name when magnet is retrieved. - // When magnet link is added, "dn" parameter is used as name, but when metadata is retrieved - // we change the name with the retrieved torrent name. - m_torrents[row]->setData(TorrentModelItem::TR_NAME, h.name(), Qt::DisplayRole); - - m_torrents[row]->refreshStatus(h.status(torrent_handle::query_accurate_download_counters)); - notifyTorrentChanged(row); - } -} - -void TorrentModel::handleFinishedTorrent(const QTorrentHandle& h) -{ - const int row = torrentRow(h.hash()); - if (row < 0) - return; - - // Update completion date - m_torrents[row]->refreshStatus(h.status(torrent_handle::query_accurate_download_counters)); - notifyTorrentChanged(row); -} - void TorrentModel::notifyTorrentChanged(int row) { - emit dataChanged(index(row, 0), index(row, columnCount()-1)); -} - -void TorrentModel::setRefreshInterval(int refreshInterval) -{ - if (m_refreshInterval != refreshInterval) { - m_refreshInterval = refreshInterval; - m_refreshTimer.stop(); - m_refreshTimer.start(m_refreshInterval); - } -} - -void TorrentModel::forceModelRefresh() -{ - QBtSession::instance()->postTorrentUpdate(); -} - -TorrentStatusReport TorrentModel::getTorrentStatusReport() const -{ - TorrentStatusReport report; - - QList::const_iterator it = m_torrents.constBegin(); - QList::const_iterator itend = m_torrents.constEnd(); - for ( ; it != itend; ++it) { - switch((*it)->state()) { - case TorrentModelItem::STATE_DOWNLOADING: - case TorrentModelItem::STATE_FORCED_DL: - ++report.nb_active; - ++report.nb_downloading; - break; - case TorrentModelItem::STATE_DOWNLOADING_META: - ++report.nb_downloading; - break; - case TorrentModelItem::STATE_PAUSED_DL: - case TorrentModelItem::STATE_PAUSED_MISSING: - ++report.nb_paused; - case TorrentModelItem::STATE_STALLED_DL: - case TorrentModelItem::STATE_CHECKING_DL: - case TorrentModelItem::STATE_QUEUED_DL: { - ++report.nb_inactive; - ++report.nb_downloading; - break; - } - case TorrentModelItem::STATE_SEEDING: - case TorrentModelItem::STATE_FORCED_UP: - ++report.nb_active; - ++report.nb_seeding; - ++report.nb_completed; - break; - case TorrentModelItem::STATE_STALLED_UP: - case TorrentModelItem::STATE_CHECKING_UP: - case TorrentModelItem::STATE_QUEUED_UP: - ++report.nb_seeding; - case TorrentModelItem::STATE_PAUSED_UP: - ++report.nb_completed; - ++report.nb_inactive; - break; - default: - break; - } - } - return report; + emit dataChanged(index(row, 0), index(row, columnCount() - 1)); } Qt::ItemFlags TorrentModel::flags(const QModelIndex &index) const @@ -660,57 +483,34 @@ void TorrentModel::handleTorrentLabelChange(QString previous, QString current) QString TorrentModel::torrentHash(int row) const { if (row >= 0 && row < rowCount()) - return m_torrents.at(row)->hash(); + return m_items.at(row)->hash(); return QString(); } -void TorrentModel::handleTorrentAboutToBeRemoved(const QTorrentHandle &h) +BitTorrent::TorrentHandle *TorrentModel::torrentHandle(const QModelIndex &index) const { - const int row = torrentRow(h.hash()); + if (index.isValid() && (index.row() >= 0) && (index.row() < rowCount())) + return m_items[index.row()]->torrentHandle(); + + return 0; +} + +void TorrentModel::handleTorrentAboutToBeRemoved(BitTorrent::TorrentHandle *const torrent) +{ + const int row = torrentRow(torrent->hash()); qDebug() << Q_FUNC_INFO << row; if (row >= 0) { - emit torrentAboutToBeRemoved(m_torrents.at(row)); + emit torrentAboutToBeRemoved(m_items.at(row)); beginRemoveTorrent(row); - delete m_torrents[row]; - m_torrents.removeAt(row); + delete m_items.takeAt(row); endRemoveTorrent(); } } -void TorrentModel::stateUpdated(const std::vector &statuses) { - typedef std::vector statuses_t; - - for (statuses_t::const_iterator i = statuses.begin(), end = statuses.end(); i != end; ++i) { - libtorrent::torrent_status const& status = *i; - - const int row = torrentRow(misc::toQString(status.info_hash)); - if (row >= 0) { - m_torrents[row]->refreshStatus(status); - notifyTorrentChanged(row); - } - } - - emit modelRefreshed(); -} - -bool TorrentModel::inhibitSystem() +void TorrentModel::handleTorrentStatusUpdated(BitTorrent::TorrentHandle *const torrent) { - QList::const_iterator it = m_torrents.constBegin(); - QList::const_iterator itend = m_torrents.constEnd(); - for ( ; it != itend; ++it) { - switch((*it)->data(TorrentModelItem::TR_STATUS).toInt()) { - case TorrentModelItem::STATE_DOWNLOADING: - case TorrentModelItem::STATE_DOWNLOADING_META: - case TorrentModelItem::STATE_FORCED_DL: - case TorrentModelItem::STATE_STALLED_DL: - case TorrentModelItem::STATE_SEEDING: - case TorrentModelItem::STATE_FORCED_UP: - case TorrentModelItem::STATE_STALLED_UP: - return true; - default: - break; - } - } - return false; + const int row = torrentRow(torrent->hash()); + if (row >= 0) + notifyTorrentChanged(row); } diff --git a/src/gui/torrentmodel.h b/src/gui/torrentmodel.h index 9284feb0b..2a328198d 100644 --- a/src/gui/torrentmodel.h +++ b/src/gui/torrentmodel.h @@ -33,53 +33,35 @@ #include #include -#include -#include -#include -#include "qtorrenthandle.h" +#include "core/bittorrent/torrenthandle.h" -struct TorrentStatusReport { - TorrentStatusReport(); - uint nb_downloading; - uint nb_seeding; - uint nb_completed; - uint nb_active; - uint nb_inactive; - uint nb_paused; -}; +class QIcon; class TorrentModelItem : public QObject { Q_OBJECT public: - enum State {STATE_DOWNLOADING, STATE_DOWNLOADING_META, STATE_ALLOCATING, STATE_STALLED_DL, STATE_SEEDING, STATE_STALLED_UP, STATE_QUEUED_DL, STATE_QUEUED_UP, STATE_CHECKING_UP, STATE_CHECKING_DL, STATE_QUEUED_CHECK, STATE_QUEUED_FASTCHECK, STATE_PAUSED_DL, STATE_PAUSED_UP, STATE_PAUSED_MISSING, STATE_FORCED_DL, STATE_FORCED_UP, STATE_INVALID}; enum Column {TR_NAME, TR_PRIORITY, TR_SIZE, TR_TOTAL_SIZE, TR_PROGRESS, TR_STATUS, TR_SEEDS, TR_PEERS, TR_DLSPEED, TR_UPSPEED, TR_ETA, TR_RATIO, TR_LABEL, TR_ADD_DATE, TR_SEED_DATE, TR_TRACKER, TR_DLLIMIT, TR_UPLIMIT, TR_AMOUNT_DOWNLOADED, TR_AMOUNT_UPLOADED, TR_AMOUNT_LEFT, TR_TIME_ELAPSED, TR_SAVE_PATH, TR_COMPLETED, TR_RATIO_LIMIT, TR_SEEN_COMPLETE_DATE, TR_LAST_ACTIVITY, TR_AMOUNT_DOWNLOADED_SESSION, TR_AMOUNT_UPLOADED_SESSION, NB_COLUMNS}; public: - TorrentModelItem(const QTorrentHandle& h); - void refreshStatus(libtorrent::torrent_status const& status); + TorrentModelItem(BitTorrent::TorrentHandle *torrent); inline int columnCount() const { return NB_COLUMNS; } QVariant data(int column, int role = Qt::DisplayRole) const; bool setData(int column, const QVariant &value, int role = Qt::DisplayRole); - inline QString const& hash() const { return m_hash; } - State state() const; - QTorrentHandle torrentHandle() const; + inline BitTorrent::InfoHash hash() const { return m_torrent->hash(); } + BitTorrent::TorrentState state() const; + BitTorrent::TorrentHandle *torrentHandle() const; signals: void labelChanged(QString previous, QString current); private: - static QIcon getIconByState(State state); - static QColor getColorByState(State state); + static QIcon getIconByState(BitTorrent::TorrentState state); + static QColor getColorByState(BitTorrent::TorrentState state); private: - QTorrentHandle m_torrent; - libtorrent::torrent_status m_lastStatus; - QDateTime m_addedTime; - QString m_label; - QString m_name; - QString m_hash; // Cached for safety reasons + BitTorrent::TorrentHandle *m_torrent; }; class TorrentModel : public QAbstractListModel @@ -90,34 +72,28 @@ class TorrentModel : public QAbstractListModel public: explicit TorrentModel(QObject *parent = 0); ~TorrentModel(); - inline int rowCount(const QModelIndex& index = QModelIndex()) const { Q_UNUSED(index); return m_torrents.size(); } + inline int rowCount(const QModelIndex& index = QModelIndex()) const { Q_UNUSED(index); return m_items.size(); } int columnCount(const QModelIndex &parent=QModelIndex()) const { Q_UNUSED(parent); return TorrentModelItem::NB_COLUMNS; } QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::DisplayRole); QVariant headerData(int section, Qt::Orientation orientation, int role) const; - int torrentRow(const QString &hash) const; + int torrentRow(const BitTorrent::InfoHash &hash) const; QString torrentHash(int row) const; - void setRefreshInterval(int refreshInterval); - TorrentStatusReport getTorrentStatusReport() const; + BitTorrent::TorrentHandle *torrentHandle(const QModelIndex &index) const; Qt::ItemFlags flags(const QModelIndex &index) const; void populate(); - bool inhibitSystem(); signals: void torrentAdded(TorrentModelItem *torrentItem); void torrentAboutToBeRemoved(TorrentModelItem *torrentItem); void torrentChangedLabel(TorrentModelItem *torrentItem, QString previous, QString current); - void modelRefreshed(); private slots: - void addTorrent(const QTorrentHandle& h); - void handleTorrentUpdate(const QTorrentHandle &h); - void handleFinishedTorrent(const QTorrentHandle& h); + void addTorrent(BitTorrent::TorrentHandle *const torrent); void notifyTorrentChanged(int row); - void forceModelRefresh(); void handleTorrentLabelChange(QString previous, QString current); - void handleTorrentAboutToBeRemoved(const QTorrentHandle & h); - void stateUpdated(const std::vector &statuses); + void handleTorrentAboutToBeRemoved(BitTorrent::TorrentHandle *const torrent); + void handleTorrentStatusUpdated(BitTorrent::TorrentHandle *const torrent); private: void beginInsertTorrent(int row); @@ -126,9 +102,7 @@ private: void endRemoveTorrent(); private: - QList m_torrents; - int m_refreshInterval; - QTimer m_refreshTimer; + QList m_items; }; #endif // TORRENTMODEL_H diff --git a/src/gui/trackerlogin.cpp b/src/gui/trackerlogin.cpp index fabcc9572..ecc88355c 100644 --- a/src/gui/trackerlogin.cpp +++ b/src/gui/trackerlogin.cpp @@ -28,17 +28,18 @@ * Contact : chris@qbittorrent.org */ +#include "core/bittorrent/torrenthandle.h" #include "trackerlogin.h" -trackerLogin::trackerLogin(QWidget *parent, QTorrentHandle h) +trackerLogin::trackerLogin(QWidget *parent, BitTorrent::TorrentHandle *const torrent) : QDialog(parent) - , h(h) + , m_torrent(torrent) { setupUi(this); setAttribute(Qt::WA_DeleteOnClose); login_logo->setPixmap(QPixmap(QString::fromUtf8(":/icons/oxygen/encrypted.png"))); - tracker_url->setText(h.current_tracker()); - connect(this, SIGNAL(trackerLoginCancelled(QPair)), parent, SLOT(addUnauthenticatedTracker(QPair))); + tracker_url->setText(torrent->currentTracker()); + connect(this, SIGNAL(trackerLoginCancelled(QPair)), parent, SLOT(addUnauthenticatedTracker(QPair))); show(); } @@ -46,12 +47,12 @@ trackerLogin::~trackerLogin() {} void trackerLogin::on_loginButton_clicked() { // login - h.set_tracker_login(lineUsername->text(), linePasswd->text()); + m_torrent->setTrackerLogin(lineUsername->text(), linePasswd->text()); close(); } void trackerLogin::on_cancelButton_clicked() { // Emit a signal to GUI to stop asking for authentication - emit trackerLoginCancelled(QPair(h, h.current_tracker())); + emit trackerLoginCancelled(QPair(m_torrent, m_torrent->currentTracker())); close(); } diff --git a/src/gui/trackerlogin.h b/src/gui/trackerlogin.h index f9679308d..906011803 100644 --- a/src/gui/trackerlogin.h +++ b/src/gui/trackerlogin.h @@ -34,20 +34,24 @@ #include #include "ui_login.h" -#include "qtorrenthandle.h" + +namespace BitTorrent +{ + class TorrentHandle; +} class trackerLogin : public QDialog, private Ui::authentication{ Q_OBJECT private: - QTorrentHandle h; + BitTorrent::TorrentHandle *const m_torrent; public: - trackerLogin(QWidget *parent, QTorrentHandle h); + trackerLogin(QWidget *parent, BitTorrent::TorrentHandle *const torrent); ~trackerLogin(); signals: - void trackerLoginCancelled(QPair tracker); + void trackerLoginCancelled(QPair tracker); public slots: void on_loginButton_clicked(); diff --git a/src/gui/transferlistdelegate.cpp b/src/gui/transferlistdelegate.cpp index 020a60355..f0239a0a9 100644 --- a/src/gui/transferlistdelegate.cpp +++ b/src/gui/transferlistdelegate.cpp @@ -36,7 +36,8 @@ #include #include "core/misc.h" #include "torrentmodel.h" -#include "qbtsession.h" +#include "core/bittorrent/session.h" +#include "core/bittorrent/torrenthandle.h" #ifdef Q_OS_WIN #if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) @@ -90,49 +91,43 @@ void TransferListDelegate::paint(QPainter * painter, const QStyleOptionViewItem const int state = index.data().toInt(); QString display; switch(state) { - case TorrentModelItem::STATE_DOWNLOADING: + case BitTorrent::TorrentState::Downloading: display = tr("Downloading"); break; - case TorrentModelItem::STATE_DOWNLOADING_META: - display = tr("Downloading metadata", "used when loading a magnet link"); - break; - case TorrentModelItem::STATE_FORCED_DL: - display = tr("[F] Downloading", "used when the torrent is forced started. You probably shouldn't translate the F."); - break; - case TorrentModelItem::STATE_ALLOCATING: - display = tr("Allocating", "qBittorrent is allocating the files on disk"); - break; - case TorrentModelItem::STATE_STALLED_DL: + case BitTorrent::TorrentState::StalledDownloading: display = tr("Stalled", "Torrent is waiting for download to begin"); break; - case TorrentModelItem::STATE_SEEDING: - case TorrentModelItem::STATE_STALLED_UP: + case BitTorrent::TorrentState::DownloadingMetadata: + display = tr("Downloading metadata", "used when loading a magnet link"); + break; + case BitTorrent::TorrentState::ForcedDownloading: + display = tr("[F] Downloading", "used when the torrent is forced started. You probably shouldn't translate the F."); + break; + case BitTorrent::TorrentState::Allocating: + display = tr("Allocating", "qBittorrent is allocating the files on disk"); + break; + case BitTorrent::TorrentState::Uploading: + case BitTorrent::TorrentState::StalledUploading: display = tr("Seeding", "Torrent is complete and in upload-only mode"); break; - case TorrentModelItem::STATE_FORCED_UP: + case BitTorrent::TorrentState::ForcedUploading: display = tr("[F] Seeding", "used when the torrent is forced started. You probably shouldn't translate the F."); break; - case TorrentModelItem::STATE_QUEUED_DL: - case TorrentModelItem::STATE_QUEUED_UP: + case BitTorrent::TorrentState::QueuedDownloading: + case BitTorrent::TorrentState::QueuedUploading: display = tr("Queued", "i.e. torrent is queued"); break; - case TorrentModelItem::STATE_CHECKING_DL: - case TorrentModelItem::STATE_CHECKING_UP: + case BitTorrent::TorrentState::CheckingDownloading: + case BitTorrent::TorrentState::CheckingUploading: display = tr("Checking", "Torrent local data is being checked"); break; - case TorrentModelItem::STATE_QUEUED_CHECK: - display = tr("Queued for checking", "i.e. torrent is queued for hash checking"); - break; - case TorrentModelItem::STATE_QUEUED_FASTCHECK: - display = tr("Checking resume data", "used when loading the torrents from disk after qbt is launched. It checks the correctness of the .fastresume file. Normally it is completed in a fraction of a second, unless loading many many torrents."); - break; - case TorrentModelItem::STATE_PAUSED_DL: + case BitTorrent::TorrentState::PausedDownloading: display = tr("Paused"); break; - case TorrentModelItem::STATE_PAUSED_UP: + case BitTorrent::TorrentState::PausedUploading: display = tr("Completed"); break; - case TorrentModelItem::STATE_PAUSED_MISSING: + case BitTorrent::TorrentState::Error: display = tr("Missing Files"); break; default: @@ -178,13 +173,13 @@ void TransferListDelegate::paint(QPainter * painter, const QStyleOptionViewItem opt.displayAlignment = Qt::AlignRight | Qt::AlignVCenter; const qreal ratio = index.data().toDouble(); QItemDelegate::drawDisplay(painter, opt, opt.rect, - (ratio == -1 || ratio > QBtSession::MAX_RATIO) ? QString::fromUtf8("∞") : misc::accurateDoubleToString(ratio, 2)); + ((ratio == -1) || (ratio > BitTorrent::TorrentHandle::MAX_RATIO)) ? QString::fromUtf8("∞") : misc::accurateDoubleToString(ratio, 2)); break; } case TorrentModelItem::TR_PRIORITY: { const int priority = index.data().toInt(); opt.displayAlignment = Qt::AlignRight | Qt::AlignVCenter; - if (priority >= 0) + if (priority > 0) QItemDelegate::paint(painter, opt, index); else { QItemDelegate::drawBackground(painter, opt, index); diff --git a/src/gui/transferlistfilterswidget.cpp b/src/gui/transferlistfilterswidget.cpp index 1000668d5..19429554d 100644 --- a/src/gui/transferlistfilterswidget.cpp +++ b/src/gui/transferlistfilterswidget.cpp @@ -43,12 +43,15 @@ #include "transferlistwidget.h" #include "core/preferences.h" #include "torrentmodel.h" -#include "iconprovider.h" +#include "guiiconprovider.h" #include "core/fs_utils.h" #include "autoexpandabledialog.h" -#include "torrentfilterenum.h" +#include "core/torrentfilter.h" +#include "core/bittorrent/trackerentry.h" +#include "core/bittorrent/session.h" +#include "core/net/downloadmanager.h" +#include "core/net/downloadhandler.h" #include "core/misc.h" -#include "core/downloadthread.h" #include "core/logger.h" FiltersBase::FiltersBase(QWidget *parent, TransferListWidget *transferList) @@ -104,7 +107,7 @@ StatusFiltersWidget::StatusFiltersWidget(QWidget *parent, TransferListWidget *tr // Height is fixed (sizeHint().height() is used) setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); setSpacing(0); - connect(transferList->getSourceModel(), SIGNAL(modelRefreshed()), SLOT(updateTorrentNumbers())); + connect(BitTorrent::Session::instance(), SIGNAL(torrentsUpdated(const BitTorrent::TorrentStatusReport &)), SLOT(updateTorrentNumbers(const BitTorrent::TorrentStatusReport &))); // Add status filters QListWidgetItem *all = new QListWidgetItem(this); @@ -142,17 +145,16 @@ StatusFiltersWidget::~StatusFiltersWidget() Preferences::instance()->setTransSelFilter(currentRow()); } -void StatusFiltersWidget::updateTorrentNumbers() +void StatusFiltersWidget::updateTorrentNumbers(const BitTorrent::TorrentStatusReport &report) { - const TorrentStatusReport report = transferList->getSourceModel()->getTorrentStatusReport(); - item(TorrentFilter::ALL)->setData(Qt::DisplayRole, QVariant(tr("All (%1)").arg(report.nb_active + report.nb_inactive))); - item(TorrentFilter::DOWNLOADING)->setData(Qt::DisplayRole, QVariant(tr("Downloading (%1)").arg(report.nb_downloading))); - item(TorrentFilter::SEEDING)->setData(Qt::DisplayRole, QVariant(tr("Seeding (%1)").arg(report.nb_seeding))); - item(TorrentFilter::COMPLETED)->setData(Qt::DisplayRole, QVariant(tr("Completed (%1)").arg(report.nb_completed))); - item(TorrentFilter::PAUSED)->setData(Qt::DisplayRole, QVariant(tr("Paused (%1)").arg(report.nb_paused))); - item(TorrentFilter::RESUMED)->setData(Qt::DisplayRole, QVariant(tr("Resumed (%1)").arg(report.nb_downloading + report.nb_seeding - report.nb_paused))); - item(TorrentFilter::ACTIVE)->setData(Qt::DisplayRole, QVariant(tr("Active (%1)").arg(report.nb_active))); - item(TorrentFilter::INACTIVE)->setData(Qt::DisplayRole, QVariant(tr("Inactive (%1)").arg(report.nb_inactive))); + item(TorrentFilter::All)->setData(Qt::DisplayRole, QVariant(tr("All (%1)").arg(report.nbActive + report.nbInactive))); + item(TorrentFilter::Downloading)->setData(Qt::DisplayRole, QVariant(tr("Downloading (%1)").arg(report.nbDownloading))); + item(TorrentFilter::Seeding)->setData(Qt::DisplayRole, QVariant(tr("Seeding (%1)").arg(report.nbSeeding))); + item(TorrentFilter::Completed)->setData(Qt::DisplayRole, QVariant(tr("Completed (%1)").arg(report.nbCompleted))); + item(TorrentFilter::Paused)->setData(Qt::DisplayRole, QVariant(tr("Paused (%1)").arg(report.nbPaused))); + item(TorrentFilter::Resumed)->setData(Qt::DisplayRole, QVariant(tr("Resumed (%1)").arg(report.nbResumed))); + item(TorrentFilter::Active)->setData(Qt::DisplayRole, QVariant(tr("Active (%1)").arg(report.nbActive))); + item(TorrentFilter::Inactive)->setData(Qt::DisplayRole, QVariant(tr("Inactive (%1)").arg(report.nbInactive))); } void StatusFiltersWidget::showMenu(QPoint) {} @@ -177,10 +179,10 @@ LabelFiltersList::LabelFiltersList(QWidget *parent, TransferListWidget *transfer // Add Label filters QListWidgetItem *allLabels = new QListWidgetItem(this); allLabels->setData(Qt::DisplayRole, QVariant(tr("All (0)", "this is for the label filter"))); - allLabels->setData(Qt::DecorationRole, IconProvider::instance()->getIcon("inode-directory")); + allLabels->setData(Qt::DecorationRole, GuiIconProvider::instance()->getIcon("inode-directory")); QListWidgetItem *noLabel = new QListWidgetItem(this); noLabel->setData(Qt::DisplayRole, QVariant(tr("Unlabeled (0)"))); - noLabel->setData(Qt::DecorationRole, IconProvider::instance()->getIcon("inode-directory")); + noLabel->setData(Qt::DecorationRole, GuiIconProvider::instance()->getIcon("inode-directory")); const Preferences* const pref = Preferences::instance(); QStringList labelList = pref->getTorrentLabels(); @@ -215,7 +217,7 @@ void LabelFiltersList::addItem(QString &label, bool hasTorrent) } else { labelItem = new QListWidgetItem(); - labelItem->setData(Qt::DecorationRole, IconProvider::instance()->getIcon("inode-directory")); + labelItem->setData(Qt::DecorationRole, GuiIconProvider::instance()->getIcon("inode-directory")); } if (hasTorrent) { @@ -269,9 +271,12 @@ void LabelFiltersList::removeItem(const QString &label) void LabelFiltersList::removeSelectedLabel() { - const int labelRow = row(selectedItems().first()); - if (labelRow < 2) - return; + QList items = selectedItems(); + if (items.size() == 0) return; + + const int labelRow = row(items.first()); + if (labelRow < 2) return; + const QString &label = labelFromRow(labelRow); Q_ASSERT(m_labels.contains(label)); m_labels.remove(label); @@ -314,16 +319,16 @@ void LabelFiltersList::torrentChangedLabel(TorrentModelItem *torrentItem, QStrin void LabelFiltersList::showMenu(QPoint) { QMenu menu(this); - QAction *addAct = menu.addAction(IconProvider::instance()->getIcon("list-add"), tr("Add label...")); + QAction *addAct = menu.addAction(GuiIconProvider::instance()->getIcon("list-add"), tr("Add label...")); QAction *removeAct = 0; QAction *removeUnusedAct = 0; if (!selectedItems().empty() && row(selectedItems().first()) > 1) - removeAct = menu.addAction(IconProvider::instance()->getIcon("list-remove"), tr("Remove label")); - removeUnusedAct = menu.addAction(IconProvider::instance()->getIcon("list-remove"), tr("Remove unused labels")); + removeAct = menu.addAction(GuiIconProvider::instance()->getIcon("list-remove"), tr("Remove label")); + removeUnusedAct = menu.addAction(GuiIconProvider::instance()->getIcon("list-remove"), tr("Remove unused labels")); menu.addSeparator(); - QAction *startAct = menu.addAction(IconProvider::instance()->getIcon("media-playback-start"), tr("Resume torrents")); - QAction *pauseAct = menu.addAction(IconProvider::instance()->getIcon("media-playback-pause"), tr("Pause torrents")); - QAction *deleteTorrentsAct = menu.addAction(IconProvider::instance()->getIcon("edit-delete"), tr("Delete torrents")); + QAction *startAct = menu.addAction(GuiIconProvider::instance()->getIcon("media-playback-start"), tr("Resume torrents")); + QAction *pauseAct = menu.addAction(GuiIconProvider::instance()->getIcon("media-playback-pause"), tr("Pause torrents")); + QAction *deleteTorrentsAct = menu.addAction(GuiIconProvider::instance()->getIcon("edit-delete"), tr("Delete torrents")); QAction *act = 0; act = menu.exec(QCursor::pos()); if (!act) @@ -415,17 +420,16 @@ int LabelFiltersList::rowFromLabel(const QString &label) const TrackerFiltersList::TrackerFiltersList(QWidget *parent, TransferListWidget *transferList) : FiltersBase(parent, transferList) - , m_downloader(new DownloadThread(this)) , m_totalTorrents(0) { setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); QListWidgetItem *allTrackers = new QListWidgetItem(this); allTrackers->setData(Qt::DisplayRole, QVariant(tr("All (0)", "this is for the label filter"))); - allTrackers->setData(Qt::DecorationRole, IconProvider::instance()->getIcon("network-server")); + allTrackers->setData(Qt::DecorationRole, GuiIconProvider::instance()->getIcon("network-server")); QListWidgetItem *noTracker = new QListWidgetItem(this); noTracker->setData(Qt::DisplayRole, QVariant(tr("Trackerless (0)"))); - noTracker->setData(Qt::DecorationRole, IconProvider::instance()->getIcon("network-server")); + noTracker->setData(Qt::DecorationRole, GuiIconProvider::instance()->getIcon("network-server")); QListWidgetItem *errorTracker = new QListWidgetItem(this); errorTracker->setData(Qt::DisplayRole, QVariant(tr("Error (0)"))); errorTracker->setData(Qt::DecorationRole, style()->standardIcon(QStyle::SP_MessageBoxCritical)); @@ -435,14 +439,11 @@ TrackerFiltersList::TrackerFiltersList(QWidget *parent, TransferListWidget *tran m_trackers.insert("", QStringList()); setCurrentRow(0, QItemSelectionModel::SelectCurrent); - connect(m_downloader, SIGNAL(downloadFinished(QString, QString)), SLOT(handleFavicoDownload(QString, QString))); - connect(m_downloader, SIGNAL(downloadFailure(QString, QString)), SLOT(handleFavicoFailure(QString, QString))); toggleFilter(Preferences::instance()->getTrackerFilterState()); } TrackerFiltersList::~TrackerFiltersList() { - delete m_downloader; foreach (const QString &iconPath, m_iconPaths) fsutils::forceRemove(iconPath); } @@ -466,8 +467,11 @@ void TrackerFiltersList::addItem(const QString &tracker, const QString &hash) } else { trackerItem = new QListWidgetItem(); - trackerItem->setData(Qt::DecorationRole, IconProvider::instance()->getIcon("network-server")); - m_downloader->downloadUrl(QString("http://") + host + QString("/favicon.ico")); + trackerItem->setData(Qt::DecorationRole, GuiIconProvider::instance()->getIcon("network-server")); + + Net::DownloadHandler *h = Net::DownloadManager::instance()->downloadUrl(QString("http://%1/favicon.ico").arg(host)); + connect(h, SIGNAL(downloadFinished(QString, QString)), this, SLOT(handleFavicoDownload(QString, QString))); + connect(h, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleFavicoFailure(QString, QString))); } tmp.append(hash); @@ -611,18 +615,20 @@ void TrackerFiltersList::trackerWarning(const QString &hash, const QString &trac void TrackerFiltersList::handleFavicoDownload(const QString& url, const QString& filePath) { QString host = getHost(url); - if (!m_trackers.contains(host)) - return; + if (!m_trackers.contains(host)) return; QListWidgetItem *trackerItem = item(rowFromTracker(host)); QIcon icon(filePath); //Detect a non-decodable icon - bool invalid = icon.pixmap(icon.availableSizes().first()).isNull(); + QList sizes = icon.availableSizes(); + bool invalid = (sizes.size() > 0 ? icon.pixmap(sizes.first()).isNull() : true); if (invalid) { if (url.endsWith(".ico", Qt::CaseInsensitive)) { Logger::instance()->addMessage(tr("Couldn't decode favico for url `%1`. Trying to download favico in PNG format.").arg(url), Log::WARNING); - m_downloader->downloadUrl(url.left(url.size() - 4) + ".png"); + Net::DownloadHandler *h = Net::DownloadManager::instance()->downloadUrl(url.left(url.size() - 4) + ".png"); + connect(h, SIGNAL(downloadFinished(QString, QString)), this, SLOT(handleFavicoDownload(QString, QString))); + connect(h, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleFavicoFailure(QString, QString))); } else { Logger::instance()->addMessage(tr("Couldn't decode favico for url `%1`.").arg(url), Log::WARNING); @@ -646,9 +652,9 @@ void TrackerFiltersList::handleFavicoFailure(const QString& url, const QString& void TrackerFiltersList::showMenu(QPoint) { QMenu menu(this); - QAction *startAct = menu.addAction(IconProvider::instance()->getIcon("media-playback-start"), tr("Resume torrents")); - QAction *pauseAct = menu.addAction(IconProvider::instance()->getIcon("media-playback-pause"), tr("Pause torrents")); - QAction *deleteTorrentsAct = menu.addAction(IconProvider::instance()->getIcon("edit-delete"), tr("Delete torrents")); + QAction *startAct = menu.addAction(GuiIconProvider::instance()->getIcon("media-playback-start"), tr("Resume torrents")); + QAction *pauseAct = menu.addAction(GuiIconProvider::instance()->getIcon("media-playback-pause"), tr("Pause torrents")); + QAction *deleteTorrentsAct = menu.addAction(GuiIconProvider::instance()->getIcon("edit-delete"), tr("Delete torrents")); QAction *act = 0; act = menu.exec(QCursor::pos()); @@ -673,11 +679,11 @@ void TrackerFiltersList::applyFilter(int row) void TrackerFiltersList::handleNewTorrent(TorrentModelItem* torrentItem) { - QTorrentHandle handle = torrentItem->torrentHandle(); - QString hash = handle.hash(); - std::vector trackers = handle.trackers(); - for (std::vector::iterator i = trackers.begin(), e = trackers.end(); i != e; ++i) - addItem(misc::toQStringU(i->url), hash); + BitTorrent::TorrentHandle *const handle = torrentItem->torrentHandle(); + QString hash = handle->hash(); + QList trackers = handle->trackers(); + foreach (const BitTorrent::TrackerEntry &tracker, trackers) + addItem(tracker.url(), hash); //Check for trackerless torrent if (trackers.size() == 0) @@ -688,11 +694,11 @@ void TrackerFiltersList::handleNewTorrent(TorrentModelItem* torrentItem) void TrackerFiltersList::torrentAboutToBeDeleted(TorrentModelItem* torrentItem) { - QTorrentHandle handle = torrentItem->torrentHandle(); - QString hash = handle.hash(); - std::vector trackers = handle.trackers(); - for (std::vector::iterator i = trackers.begin(), e = trackers.end(); i != e; ++i) - removeItem(misc::toQStringU(i->url), hash); + BitTorrent::TorrentHandle *const handle = torrentItem->torrentHandle(); + QString hash = handle->hash(); + QList trackers = handle->trackers(); + foreach (const BitTorrent::TrackerEntry &tracker, trackers) + removeItem(tracker.url(), hash); //Check for trackerless torrent if (trackers.size() == 0) @@ -817,19 +823,34 @@ TransferListFiltersWidget::TransferListFiltersWidget(QWidget *parent, TransferLi connect(this, SIGNAL(trackerWarning(const QString &, const QString &)), trackerFilters, SLOT(trackerWarning(const QString &, const QString &))); } -void TransferListFiltersWidget::addTrackers(const QStringList &trackers, const QString &hash) +void TransferListFiltersWidget::addTrackers(BitTorrent::TorrentHandle *const torrent, const QList &trackers) { - foreach (const QString &tracker, trackers) - trackerFilters->addItem(tracker, hash); + foreach (const BitTorrent::TrackerEntry &tracker, trackers) + trackerFilters->addItem(tracker.url(), torrent->hash()); } -void TransferListFiltersWidget::removeTrackers(const QStringList &trackers, const QString &hash) +void TransferListFiltersWidget::removeTrackers(BitTorrent::TorrentHandle *const torrent, const QList &trackers) { - foreach (const QString &tracker, trackers) - trackerFilters->removeItem(tracker, hash); + foreach (const BitTorrent::TrackerEntry &tracker, trackers) + trackerFilters->removeItem(tracker.url(), torrent->hash()); } -void TransferListFiltersWidget::changeTrackerless(bool trackerless, const QString &hash) +void TransferListFiltersWidget::changeTrackerless(BitTorrent::TorrentHandle *const torrent, bool trackerless) { - trackerFilters->changeTrackerless(trackerless, hash); + trackerFilters->changeTrackerless(trackerless, torrent->hash()); +} + +void TransferListFiltersWidget::trackerSuccess(BitTorrent::TorrentHandle *const torrent, const QString &tracker) +{ + emit trackerSuccess(torrent->hash(), tracker); +} + +void TransferListFiltersWidget::trackerWarning(BitTorrent::TorrentHandle *const torrent, const QString &tracker) +{ + emit trackerWarning(torrent->hash(), tracker); +} + +void TransferListFiltersWidget::trackerError(BitTorrent::TorrentHandle *const torrent, const QString &tracker) +{ + emit trackerError(torrent->hash(), tracker); } diff --git a/src/gui/transferlistfilterswidget.h b/src/gui/transferlistfilterswidget.h index 316320f83..4e670b2fc 100644 --- a/src/gui/transferlistfilterswidget.h +++ b/src/gui/transferlistfilterswidget.h @@ -41,8 +41,13 @@ QT_END_NAMESPACE class TransferListWidget; class TorrentModelItem; -class QTorrentHandle; -class DownloadThread; + +namespace BitTorrent +{ + class TorrentHandle; + class TrackerEntry; + struct TorrentStatusReport; +} class FiltersBase: public QListWidget { @@ -76,7 +81,7 @@ public: ~StatusFiltersWidget(); private slots: - void updateTorrentNumbers(); + void updateTorrentNumbers(const BitTorrent::TorrentStatusReport &report); private: // These 4 methods are virtual slots in the base class. @@ -158,7 +163,6 @@ private: QHash m_trackers; QHash m_errors; QHash m_warnings; - DownloadThread *m_downloader; QStringList m_iconPaths; int m_totalTorrents; }; @@ -171,9 +175,12 @@ public: TransferListFiltersWidget(QWidget *parent, TransferListWidget *transferList); public slots: - void addTrackers(const QStringList &trackers, const QString &hash); - void removeTrackers(const QStringList &trackers, const QString &hash); - void changeTrackerless(bool trackerless, const QString &hash); + void addTrackers(BitTorrent::TorrentHandle *const torrent, const QList &trackers); + void removeTrackers(BitTorrent::TorrentHandle *const torrent, const QList &trackers); + void changeTrackerless(BitTorrent::TorrentHandle *const torrent, bool trackerless); + void trackerSuccess(BitTorrent::TorrentHandle *const torrent, const QString &tracker); + void trackerWarning(BitTorrent::TorrentHandle *const torrent, const QString &tracker); + void trackerError(BitTorrent::TorrentHandle *const torrent, const QString &tracker); signals: void trackerSuccess(const QString &hash, const QString &tracker); diff --git a/src/gui/transferlistsortmodel.cpp b/src/gui/transferlistsortmodel.cpp index ebced81e1..12bafcca3 100644 --- a/src/gui/transferlistsortmodel.cpp +++ b/src/gui/transferlistsortmodel.cpp @@ -28,61 +28,46 @@ * Contact : daymansmail@gmail.com */ -#include "transferlistsortmodel.h" +#include -#include "torrentmodel.h" #include "core/misc.h" +#include "core/bittorrent/torrenthandle.h" +#include "torrentmodel.h" +#include "transferlistsortmodel.h" TransferListSortModel::TransferListSortModel(QObject *parent) : QSortFilterProxyModel(parent) - , filter0(TorrentFilter::ALL) - , labelFilterEnabled(false) - , trackerFilterEnabled(false) { } -void TransferListSortModel::setStatusFilter(const TorrentFilter::TorrentFilter &filter) +void TransferListSortModel::setStatusFilter(TorrentFilter::Type filter) { - if (filter != filter0) { - filter0 = filter; + if (m_filter.setType(filter)) invalidateFilter(); - } } void TransferListSortModel::setLabelFilter(const QString &label) { - if (!labelFilterEnabled || labelFilter != label) { - labelFilterEnabled = true; - labelFilter = label; + if (m_filter.setLabel(label)) invalidateFilter(); - } } void TransferListSortModel::disableLabelFilter() { - if (labelFilterEnabled) { - labelFilterEnabled = false; - labelFilter = QString(); + if (m_filter.setLabel(TorrentFilter::AnyLabel)) invalidateFilter(); - } } void TransferListSortModel::setTrackerFilter(const QStringList &hashes) { - if (!trackerFilterEnabled || trackerFilter != hashes) { - trackerFilterEnabled = true; - trackerFilter = hashes; + if (m_filter.setHashSet(hashes.toSet())) invalidateFilter(); - } } void TransferListSortModel::disableTrackerFilter() { - if (trackerFilterEnabled) { - trackerFilterEnabled = false; - trackerFilter = QStringList(); + if (m_filter.setHashSet(TorrentFilter::AnyHash)) invalidateFilter(); - } } bool TransferListSortModel::lessThan(const QModelIndex &left, const QModelIndex &right) const @@ -92,12 +77,7 @@ bool TransferListSortModel::lessThan(const QModelIndex &left, const QModelIndex if (column == TorrentModelItem::TR_NAME) { QVariant vL = left.data(); QVariant vR = right.data(); - if (!(vL.isValid() && vR.isValid())) - return lowerPositionThan(left, right); - Q_ASSERT(vL.isValid()); - Q_ASSERT(vR.isValid()); - - if (vL == vR) + if (!vL.isValid() || !vR.isValid() || (vL == vR)) return lowerPositionThan(left, right); bool res = false; @@ -136,7 +116,7 @@ bool TransferListSortModel::lessThan(const QModelIndex &left, const QModelIndex } } else if (column == TorrentModelItem::TR_ETA) { - const QAbstractItemModel *model = sourceModel(); + TorrentModel *model = qobject_cast(sourceModel()); const int prioL = model->data(model->index(left.row(), TorrentModelItem::TR_PRIORITY)).toInt(); const int prioR = model->data(model->index(right.row(), TorrentModelItem::TR_PRIORITY)).toInt(); const qlonglong etaL = left.data().toLongLong(); @@ -146,36 +126,9 @@ bool TransferListSortModel::lessThan(const QModelIndex &left, const QModelIndex const bool invalidR = (etaR < 0 || etaR >= MAX_ETA); const bool seedingL = (prioL < 0); const bool seedingR = (prioR < 0); - bool activeL; - bool activeR; - switch (model->data(model->index(left.row(), TorrentModelItem::TR_STATUS)).toInt()) { - case TorrentModelItem::STATE_DOWNLOADING: - case TorrentModelItem::STATE_DOWNLOADING_META: - case TorrentModelItem::STATE_FORCED_DL: - case TorrentModelItem::STATE_STALLED_DL: - case TorrentModelItem::STATE_SEEDING: - case TorrentModelItem::STATE_FORCED_UP: - case TorrentModelItem::STATE_STALLED_UP: - activeL = true; - break; - default: - activeL = false; - } - - switch (model->data(model->index(right.row(), TorrentModelItem::TR_STATUS)).toInt()) { - case TorrentModelItem::STATE_DOWNLOADING: - case TorrentModelItem::STATE_DOWNLOADING_META: - case TorrentModelItem::STATE_FORCED_DL: - case TorrentModelItem::STATE_STALLED_DL: - case TorrentModelItem::STATE_SEEDING: - case TorrentModelItem::STATE_FORCED_UP: - case TorrentModelItem::STATE_STALLED_UP: - activeR = true; - break; - default: - activeR = false; - } + bool activeR = TorrentFilter::ActiveTorrent.match(model->torrentHandle(model->index(right.row()))); + bool activeL = TorrentFilter::ActiveTorrent.match(model->torrentHandle(model->index(right.row()))); // Sorting rules prioritized. // 1. Active torrents at the top @@ -189,7 +142,6 @@ bool TransferListSortModel::lessThan(const QModelIndex &left, const QModelIndex } if (invalidL && invalidR) { - if (seedingL) { //Both seeding QDateTime dateL = model->data(model->index(left.row(), TorrentModelItem::TR_SEED_DATE)).toDateTime(); QDateTime dateR = model->data(model->index(right.row(), TorrentModelItem::TR_SEED_DATE)).toDateTime(); @@ -204,7 +156,7 @@ bool TransferListSortModel::lessThan(const QModelIndex &left, const QModelIndex return prioL < prioR; } } - else if ((invalidL == false) && (invalidR == false)) { + else if (!invalidL && !invalidR) { return lowerPositionThan(left, right); } else { @@ -243,14 +195,8 @@ bool TransferListSortModel::lowerPositionThan(const QModelIndex &left, const QMo // Sort according to TR_PRIORITY const int queueL = model->data(model->index(left.row(), TorrentModelItem::TR_PRIORITY)).toInt(); const int queueR = model->data(model->index(right.row(), TorrentModelItem::TR_PRIORITY)).toInt(); - if (!(queueL < 0 && queueR < 0)) { - if (queueL > 0 && queueR > 0) - return queueL < queueR; - else if (queueL < 0) - return false; - else - return true; - } + if ((queueL > 0) || (queueR > 0)) + return queueL < queueR; // Sort according to TR_SEED_DATE const QDateTime dateL = model->data(model->index(left.row(), TorrentModelItem::TR_SEED_DATE)).toDateTime(); @@ -272,88 +218,17 @@ bool TransferListSortModel::lowerPositionThan(const QModelIndex &left, const QMo bool TransferListSortModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { - return matchStatusFilter(sourceRow, sourceParent) - && matchLabelFilter(sourceRow, sourceParent) - && matchTrackerFilter(sourceRow, sourceParent) - && QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent); + return matchFilter(sourceRow, sourceParent) + && QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent); } -bool TransferListSortModel::matchStatusFilter(int sourceRow, const QModelIndex &sourceParent) const +bool TransferListSortModel::matchFilter(int sourceRow, const QModelIndex &sourceParent) const { - if (filter0 == TorrentFilter::ALL) - return true; - QAbstractItemModel *model = sourceModel(); - if (!model) return false; - QModelIndex index = model->index(sourceRow, TorrentModelItem::TR_STATUS, sourceParent); - TorrentModelItem::State state = (TorrentModelItem::State)index.data().toInt(); - - switch (filter0) { - case TorrentFilter::DOWNLOADING: - return (state == TorrentModelItem::STATE_DOWNLOADING || state == TorrentModelItem::STATE_STALLED_DL - || state == TorrentModelItem::STATE_PAUSED_DL || state == TorrentModelItem::STATE_CHECKING_DL - || state == TorrentModelItem::STATE_QUEUED_DL || state == TorrentModelItem::STATE_DOWNLOADING_META - || state == TorrentModelItem::STATE_PAUSED_MISSING || state == TorrentModelItem::STATE_FORCED_DL); - - case TorrentFilter::SEEDING: - return (state == TorrentModelItem::STATE_SEEDING || state == TorrentModelItem::STATE_STALLED_UP - || state == TorrentModelItem::STATE_CHECKING_UP || state == TorrentModelItem::STATE_QUEUED_UP - || state == TorrentModelItem::STATE_FORCED_UP); - - case TorrentFilter::COMPLETED: - return (state == TorrentModelItem::STATE_SEEDING || state == TorrentModelItem::STATE_STALLED_UP - || state == TorrentModelItem::STATE_PAUSED_UP || state == TorrentModelItem::STATE_CHECKING_UP - || state == TorrentModelItem::STATE_QUEUED_UP || state == TorrentModelItem::STATE_FORCED_UP); - - case TorrentFilter::PAUSED: - return (state == TorrentModelItem::STATE_PAUSED_DL || state == TorrentModelItem::STATE_PAUSED_MISSING); - - case TorrentFilter::RESUMED: - return (state != TorrentModelItem::STATE_PAUSED_UP && state != TorrentModelItem::STATE_PAUSED_DL - && state != TorrentModelItem::STATE_PAUSED_MISSING); - - case TorrentFilter::ACTIVE: - if (state == TorrentModelItem::STATE_STALLED_DL) { - const qulonglong up_speed = model->index(sourceRow, TorrentModelItem::TR_UPSPEED, sourceParent).data().toULongLong(); - return (up_speed > 0); - } - - return (state == TorrentModelItem::STATE_DOWNLOADING || state == TorrentModelItem::STATE_SEEDING - || state == TorrentModelItem::STATE_FORCED_DL || state == TorrentModelItem::STATE_FORCED_UP); - - case TorrentFilter::INACTIVE: - if (state == TorrentModelItem::STATE_STALLED_DL) { - const qulonglong up_speed = model->index(sourceRow, TorrentModelItem::TR_UPSPEED, sourceParent).data().toULongLong(); - return !(up_speed > 0); - } - - return (state != TorrentModelItem::STATE_DOWNLOADING && state != TorrentModelItem::STATE_SEEDING - && state != TorrentModelItem::STATE_FORCED_DL && state != TorrentModelItem::STATE_FORCED_UP); - - default: - return false; - } -} - -bool TransferListSortModel::matchLabelFilter(int sourceRow, const QModelIndex &sourceParent) const -{ - if (!labelFilterEnabled) - return true; - - QAbstractItemModel *model = sourceModel(); - if (!model) - return false; - - return model->index(sourceRow, TorrentModelItem::TR_LABEL, sourceParent).data().toString() == labelFilter; -} - -bool TransferListSortModel::matchTrackerFilter(int sourceRow, const QModelIndex &sourceParent) const -{ - if (!trackerFilterEnabled) - return true; - TorrentModel *model = qobject_cast(sourceModel()); - if (!model) - return false; + if (!model) return false; - return trackerFilter.contains(model->torrentHash(sourceRow)); + BitTorrent::TorrentHandle *const torrent = model->torrentHandle(model->index(sourceRow, 0, sourceParent)); + if (!torrent) return false; + + return m_filter.match(torrent); } diff --git a/src/gui/transferlistsortmodel.h b/src/gui/transferlistsortmodel.h index ec59587a6..793d630cc 100644 --- a/src/gui/transferlistsortmodel.h +++ b/src/gui/transferlistsortmodel.h @@ -32,8 +32,9 @@ #define TRANSFERLISTSORTMODEL_H #include -#include -#include "torrentfilterenum.h" +#include "core/torrentfilter.h" + +class QStringList; class TransferListSortModel: public QSortFilterProxyModel { @@ -42,7 +43,7 @@ class TransferListSortModel: public QSortFilterProxyModel public: TransferListSortModel(QObject *parent = 0); - void setStatusFilter(const TorrentFilter::TorrentFilter &filter); + void setStatusFilter(TorrentFilter::Type filter); void setLabelFilter(const QString &label); void disableLabelFilter(); void setTrackerFilter(const QStringList &hashes); @@ -52,18 +53,10 @@ private: bool lessThan(const QModelIndex &left, const QModelIndex &right) const; bool lowerPositionThan(const QModelIndex &left, const QModelIndex &right) const; bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; - - bool matchStatusFilter(int sourceRow, const QModelIndex &sourceParent) const; - bool matchLabelFilter(int sourceRow, const QModelIndex &sourceParent) const; - bool matchTrackerFilter(int sourceRow, const QModelIndex &sourceParent) const; + bool matchFilter(int sourceRow, const QModelIndex &sourceParent) const; private: - TorrentFilter::TorrentFilter filter0; - - bool labelFilterEnabled; - QString labelFilter; - bool trackerFilterEnabled; - QStringList trackerFilter; + TorrentFilter m_filter; }; #endif // TRANSFERLISTSORTMODEL_H diff --git a/src/gui/transferlistwidget.cpp b/src/gui/transferlistwidget.cpp index 80c7fcfc9..13a51ba61 100644 --- a/src/gui/transferlistwidget.cpp +++ b/src/gui/transferlistwidget.cpp @@ -42,14 +42,10 @@ #include #include -#include -#include -#include -#include - #include "transferlistwidget.h" -#include "qbtsession.h" -#include "core/torrentpersistentdata.h" +#include "core/bittorrent/session.h" +#include "core/bittorrent/torrenthandle.h" +#include "core/torrentfilter.h" #include "transferlistdelegate.h" #include "previewselect.h" #include "speedlimitdlg.h" @@ -60,15 +56,14 @@ #include "torrentmodel.h" #include "deletionconfirmationdlg.h" #include "propertieswidget.h" -#include "iconprovider.h" +#include "guiiconprovider.h" #include "core/fs_utils.h" #include "autoexpandabledialog.h" #include "transferlistsortmodel.h" -using namespace libtorrent; - -TransferListWidget::TransferListWidget(QWidget *parent, MainWindow *main_window, QBtSession *_BTSession): - QTreeView(parent), BTSession(_BTSession), main_window(main_window) +TransferListWidget::TransferListWidget(QWidget *parent, MainWindow *main_window) + : QTreeView(parent) + , main_window(main_window) { setUniformRowHeights(true); @@ -181,12 +176,6 @@ void TransferListWidget::previewFile(QString filePath) openUrl(filePath); } -void TransferListWidget::setRefreshInterval(int t) -{ - qDebug("Settings transfer list refresh interval to %dms", t); - listModel->setRefreshInterval(t); -} - int TransferListWidget::getRowFromHash(QString hash) const { return listModel->torrentRow(hash); @@ -216,24 +205,24 @@ void TransferListWidget::torrentDoubleClicked(const QModelIndex& index) { const int row = mapToSource(index).row(); const QString hash = getHashFromRow(row); - QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (!h.is_valid()) return; + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (!torrent) return; + int action; - if (h.is_seed()) + if (torrent->isSeed()) action = Preferences::instance()->getActionOnDblClOnTorrentFn(); else action = Preferences::instance()->getActionOnDblClOnTorrentDl(); switch(action) { case TOGGLE_PAUSE: - if (h.is_paused()) - h.resume(); + if (torrent->isPaused()) + torrent->resume(); else - h.pause(); + torrent->pause(); break; case OPEN_DEST: - const QString path = h.root_path(); - openUrl(path); + openUrl(torrent->rootPath()); } } @@ -250,72 +239,85 @@ void TransferListWidget::setSelectedTorrentsLocation() { const QStringList hashes = getSelectedTorrentsHashes(); if (hashes.isEmpty()) return; + + BitTorrent::TorrentHandle *const firstTorrent = BitTorrent::Session::instance()->findTorrent(hashes.first()); + if (!firstTorrent) return; + QString dir; - const QDir saveDir(TorrentPersistentData::instance()->getSavePath(hashes.first())); + const QDir saveDir(firstTorrent->savePath()); qDebug("Old save path is %s", qPrintable(saveDir.absolutePath())); dir = QFileDialog::getExistingDirectory(this, tr("Choose save path"), saveDir.absolutePath(), QFileDialog::DontConfirmOverwrite | QFileDialog::ShowDirsOnly | QFileDialog::HideNameFilterDetails); if (!dir.isNull()) { qDebug("New path is %s", qPrintable(dir)); - // Check if savePath exists - QDir savePath(fsutils::expandPathAbs(dir)); - qDebug("New path after clean up is %s", qPrintable(savePath.absolutePath())); - foreach (const QString & hash, hashes) { + foreach (const QString &hash, hashes) { // Actually move storage - QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (!BTSession->useTemporaryFolder() || h.is_seed()) { - if (!savePath.exists()) savePath.mkpath(savePath.absolutePath()); - h.move_storage(savePath.absolutePath()); - } - else { - TorrentPersistentData::instance()->saveSavePath(h.hash(), savePath.absolutePath()); - main_window->getProperties()->updateSavePath(h); - } + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (!torrent) continue; + + torrent->move(fsutils::expandPathAbs(dir)); + main_window->getProperties()->updateSavePath(torrent); } } } +void TransferListWidget::pauseAllTorrents() +{ + foreach (BitTorrent::TorrentHandle *const torrent, BitTorrent::Session::instance()->torrents()) + torrent->pause(); +} + +void TransferListWidget::resumeAllTorrents() +{ + foreach (BitTorrent::TorrentHandle *const torrent, BitTorrent::Session::instance()->torrents()) + torrent->resume(); +} + void TransferListWidget::startSelectedTorrents() { - const QStringList hashes = getSelectedTorrentsHashes(); - foreach (const QString &hash, hashes) - BTSession->resumeTorrent(hash); + foreach (const QString &hash, getSelectedTorrentsHashes()) { + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (torrent) + torrent->resume(); + } } void TransferListWidget::forceStartSelectedTorrents() { - const QStringList hashes = getSelectedTorrentsHashes(); - foreach (const QString &hash, hashes) - BTSession->resumeTorrent(hash, true); + foreach (const QString &hash, getSelectedTorrentsHashes()) { + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (torrent) + torrent->resume(true); + } } void TransferListWidget::startVisibleTorrents() { - QStringList hashes; - for (int i = 0; irowCount(); ++i) { + for (int i = 0; i < nameFilterModel->rowCount(); ++i) { const int row = mapToSource(nameFilterModel->index(i, 0)).row(); - hashes << getHashFromRow(row); + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(getHashFromRow(row)); + if (torrent) + torrent->resume(); } - foreach (const QString &hash, hashes) - BTSession->resumeTorrent(hash); } void TransferListWidget::pauseSelectedTorrents() { - const QStringList hashes = getSelectedTorrentsHashes(); - foreach (const QString &hash, hashes) - BTSession->pauseTorrent(hash); + foreach (const QString &hash, getSelectedTorrentsHashes()) { + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (torrent) + torrent->pause(); + } } void TransferListWidget::pauseVisibleTorrents() { - QStringList hashes; - for (int i = 0; irowCount(); ++i) { + for (int i = 0; i < nameFilterModel->rowCount(); ++i) { const int row = mapToSource(nameFilterModel->index(i, 0)).row(); - hashes << getHashFromRow(row); + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(getHashFromRow(row)); + if (torrent) + torrent->pause(); } - foreach (const QString &hash, hashes) - BTSession->pauseTorrent(hash); } void TransferListWidget::deleteSelectedTorrents() @@ -323,13 +325,14 @@ void TransferListWidget::deleteSelectedTorrents() if (main_window->getCurrentTabWidget() != this) return; const QStringList& hashes = getSelectedTorrentsHashes(); if (hashes.empty()) return; - QTorrentHandle torrent = BTSession->getTorrentHandle(hashes[0]); + + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hashes[0]); bool delete_local_files = false; if (Preferences::instance()->confirmTorrentDeletion() && - !DeletionConfirmationDlg::askForDeletionConfirmation(delete_local_files, hashes.size(), torrent.name())) + !DeletionConfirmationDlg::askForDeletionConfirmation(delete_local_files, hashes.size(), torrent->name())) return; foreach (const QString &hash, hashes) - BTSession->deleteTorrent(hash, delete_local_files); + BitTorrent::Session::instance()->deleteTorrent(hash, delete_local_files); } void TransferListWidget::deleteVisibleTorrents() @@ -340,83 +343,40 @@ void TransferListWidget::deleteVisibleTorrents() const int row = mapToSource(nameFilterModel->index(i, 0)).row(); hashes << getHashFromRow(row); } - QTorrentHandle torrent = BTSession->getTorrentHandle(hashes[0]); + + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hashes[0]); bool delete_local_files = false; if (Preferences::instance()->confirmTorrentDeletion() && - !DeletionConfirmationDlg::askForDeletionConfirmation(delete_local_files, hashes.size(), torrent.name())) + !DeletionConfirmationDlg::askForDeletionConfirmation(delete_local_files, hashes.size(), torrent->name())) return; foreach (const QString &hash, hashes) - BTSession->deleteTorrent(hash, delete_local_files); + BitTorrent::Session::instance()->deleteTorrent(hash, delete_local_files); } void TransferListWidget::increasePrioSelectedTorrents() { qDebug() << Q_FUNC_INFO; - if (main_window->getCurrentTabWidget() != this) return; - const QStringList hashes = getSelectedTorrentsHashes(); - std::priority_queue, std::vector >, std::greater > > torrent_queue; - // Sort torrents by priority - foreach (const QString &hash, hashes) { - try { - QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (!h.is_seed()) - torrent_queue.push(qMakePair(h.queue_position(), h)); - }catch(invalid_handle&) {} - } - // Increase torrents priority (starting with the ones with highest priority) - while(!torrent_queue.empty()) { - QTorrentHandle h = torrent_queue.top().second; - try { - h.queue_position_up(); - } catch(invalid_handle& h) {} - torrent_queue.pop(); - } + if (main_window->getCurrentTabWidget() == this) + BitTorrent::Session::instance()->increaseTorrentsPriority(getSelectedTorrentsHashes()); } void TransferListWidget::decreasePrioSelectedTorrents() { qDebug() << Q_FUNC_INFO; - if (main_window->getCurrentTabWidget() != this) return; - const QStringList hashes = getSelectedTorrentsHashes(); - std::priority_queue, std::vector >, std::less > > torrent_queue; - // Sort torrents by priority - foreach (const QString &hash, hashes) { - try { - QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (!h.is_seed()) - torrent_queue.push(qMakePair(h.queue_position(), h)); - }catch(invalid_handle&) {} - } - // Decrease torrents priority (starting with the ones with lowest priority) - while(!torrent_queue.empty()) { - QTorrentHandle h = torrent_queue.top().second; - try { - h.queue_position_down(); - } catch(invalid_handle& h) {} - torrent_queue.pop(); - } + if (main_window->getCurrentTabWidget() == this) + BitTorrent::Session::instance()->decreaseTorrentsPriority(getSelectedTorrentsHashes()); } void TransferListWidget::topPrioSelectedTorrents() { - if (main_window->getCurrentTabWidget() != this) return; - const QStringList hashes = getSelectedTorrentsHashes(); - foreach (const QString &hash, hashes) { - QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (h.is_valid() && !h.is_seed()) - h.queue_position_top(); - } + if (main_window->getCurrentTabWidget() == this) + BitTorrent::Session::instance()->topTorrentsPriority(getSelectedTorrentsHashes()); } void TransferListWidget::bottomPrioSelectedTorrents() { - if (main_window->getCurrentTabWidget() != this) return; - const QStringList hashes = getSelectedTorrentsHashes(); - foreach (const QString &hash, hashes) { - QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (h.is_valid() && !h.is_seed()) - h.queue_position_bottom(); - } + if (main_window->getCurrentTabWidget() == this) + BitTorrent::Session::instance()->bottomTorrentsPriority(getSelectedTorrentsHashes()); } void TransferListWidget::copySelectedMagnetURIs() const @@ -424,9 +384,9 @@ void TransferListWidget::copySelectedMagnetURIs() const QStringList magnet_uris; const QStringList hashes = getSelectedTorrentsHashes(); foreach (const QString &hash, hashes) { - const QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (h.is_valid()) - magnet_uris << misc::toQString(make_magnet_uri(h)); + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (torrent) + magnet_uris << torrent->toMagnetUri(); } qApp->clipboard()->setText(magnet_uris.join("\n")); } @@ -436,9 +396,9 @@ void TransferListWidget::copySelectedNames() const QStringList torrent_names; const QStringList hashes = getSelectedTorrentsHashes(); foreach (const QString &hash, hashes) { - const QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (h.is_valid()) - torrent_names << h.name(); + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (torrent) + torrent_names << torrent->name(); } qApp->clipboard()->setText(torrent_names.join("\n")); } @@ -456,9 +416,9 @@ void TransferListWidget::openSelectedTorrentsFolder() const QSet pathsList; const QStringList hashes = getSelectedTorrentsHashes(); foreach (const QString &hash, hashes) { - const QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (h.is_valid()) { - QString rootFolder = h.root_path(); + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (torrent) { + QString rootFolder = torrent->rootPath(); qDebug("Opening path at %s", qPrintable(rootFolder)); if (!pathsList.contains(rootFolder)) { pathsList.insert(rootFolder); @@ -472,27 +432,27 @@ void TransferListWidget::previewSelectedTorrents() { const QStringList hashes = getSelectedTorrentsHashes(); foreach (const QString &hash, hashes) { - const QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (h.is_valid() && h.has_metadata()) - new PreviewSelect(this, h); + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (torrent && torrent->hasMetadata()) + new PreviewSelect(this, torrent); } } void TransferListWidget::setDlLimitSelectedTorrents() { - QList selected_torrents; + QList selected_torrents; bool first = true; bool all_same_limit = true; const QStringList hashes = getSelectedTorrentsHashes(); foreach (const QString &hash, hashes) { - const QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (h.is_valid() && !h.is_seed()) { - selected_torrents << h; + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (torrent && !torrent->isSeed()) { + selected_torrents << torrent; // Determine current limit for selected torrents if (first) first = false; else - if (all_same_limit && h.download_limit() != selected_torrents.first().download_limit()) + if (all_same_limit && (torrent->downloadLimit() != selected_torrents.first()->downloadLimit())) all_same_limit = false; } } @@ -501,31 +461,31 @@ void TransferListWidget::setDlLimitSelectedTorrents() bool ok = false; int default_limit = -1; if (all_same_limit) - default_limit = selected_torrents.first().download_limit(); + default_limit = selected_torrents.first()->downloadLimit(); const long new_limit = SpeedLimitDialog::askSpeedLimit(&ok, tr("Torrent Download Speed Limiting"), default_limit, Preferences::instance()->getGlobalDownloadLimit() * 1024.); if (ok) { - foreach (const QTorrentHandle &h, selected_torrents) { - qDebug("Applying download speed limit of %ld Kb/s to torrent %s", (long)(new_limit / 1024.), qPrintable(h.hash())); - BTSession->setDownloadLimit(h.hash(), new_limit); + foreach (BitTorrent::TorrentHandle *const torrent, selected_torrents) { + qDebug("Applying download speed limit of %ld Kb/s to torrent %s", (long)(new_limit / 1024.), qPrintable(torrent->hash())); + torrent->setDownloadLimit(new_limit); } } } void TransferListWidget::setUpLimitSelectedTorrents() { - QList selected_torrents; + QList selected_torrents; bool first = true; bool all_same_limit = true; const QStringList hashes = getSelectedTorrentsHashes(); foreach (const QString &hash, hashes) { - const QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (h.is_valid()) { - selected_torrents << h; + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (torrent) { + selected_torrents << torrent; // Determine current limit for selected torrents if (first) first = false; else - if (all_same_limit && h.upload_limit() != selected_torrents.first().upload_limit()) + if (all_same_limit && (torrent->uploadLimit() != selected_torrents.first()->uploadLimit())) all_same_limit = false; } } @@ -534,12 +494,12 @@ void TransferListWidget::setUpLimitSelectedTorrents() bool ok = false; int default_limit = -1; if (all_same_limit) - default_limit = selected_torrents.first().upload_limit(); + default_limit = selected_torrents.first()->uploadLimit(); const long new_limit = SpeedLimitDialog::askSpeedLimit(&ok, tr("Torrent Upload Speed Limiting"), default_limit, Preferences::instance()->getGlobalUploadLimit() * 1024.); if (ok) { - foreach (const QTorrentHandle &h, selected_torrents) { - qDebug("Applying upload speed limit of %ld Kb/s to torrent %s", (long)(new_limit / 1024.), qPrintable(h.hash())); - BTSession->setUploadLimit(h.hash(), new_limit); + foreach (BitTorrent::TorrentHandle *const torrent, selected_torrents) { + qDebug("Applying upload speed limit of %ld Kb/s to torrent %s", (long)(new_limit / 1024.), qPrintable(torrent->hash())); + torrent->setUploadLimit(new_limit); } } } @@ -549,34 +509,37 @@ void TransferListWidget::setMaxRatioSelectedTorrents() const QStringList hashes = getSelectedTorrentsHashes(); if (hashes.isEmpty()) return; - bool useGlobalValue; - qreal currentMaxRatio; + bool useGlobalValue = true; + qreal currentMaxRatio = BitTorrent::Session::instance()->globalMaxRatio();; if (hashes.count() == 1) { - currentMaxRatio = BTSession->getMaxRatioPerTorrent(hashes.first(), &useGlobalValue); + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hashes.first()); + if (torrent) + currentMaxRatio = torrent->maxRatio(&useGlobalValue); } - else { - useGlobalValue = true; - currentMaxRatio = BTSession->getGlobalMaxRatio(); - } - UpDownRatioDlg dlg(useGlobalValue, currentMaxRatio, QBtSession::MAX_RATIO, this); - if (dlg.exec() != QDialog::Accepted) - return; + + UpDownRatioDlg dlg(useGlobalValue, currentMaxRatio, BitTorrent::TorrentHandle::MAX_RATIO, this); + if (dlg.exec() != QDialog::Accepted) return; + foreach (const QString &hash, hashes) { - if (dlg.useDefault()) - BTSession->removeRatioPerTorrent(hash); - else - BTSession->setMaxRatioPerTorrent(hash, dlg.ratio()); + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (torrent) { + qreal ratio = (dlg.useDefault() ? BitTorrent::TorrentHandle::USE_GLOBAL_RATIO : dlg.ratio()); + torrent->setRatioLimit(ratio); + } } } void TransferListWidget::recheckSelectedTorrents() { QMessageBox::StandardButton ret = QMessageBox::question(this, tr("Recheck confirmation"), tr("Are you sure you want to recheck the selected torrent(s)?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); - if (ret != QMessageBox::Yes) - return; + if (ret != QMessageBox::Yes) return; + const QStringList hashes = getSelectedTorrentsHashes(); - foreach (const QString &hash, hashes) - BTSession->recheckTorrent(hash); + foreach (const QString &hash, hashes) { + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (torrent) + torrent->forceRecheck(); + } } // hide/show columns menu @@ -586,7 +549,7 @@ void TransferListWidget::displayDLHoSMenu(const QPoint&) hideshowColumn.setTitle(tr("Column visibility")); QList actions; for (int i = 0; i < listModel->columnCount(); ++i) { - if (!BTSession->isQueueingEnabled() && i == TorrentModelItem::TR_PRIORITY) { + if (!BitTorrent::Session::instance()->isQueueingEnabled() && i == TorrentModelItem::TR_PRIORITY) { actions.append(0); continue; } @@ -624,9 +587,9 @@ void TransferListWidget::toggleSelectedTorrentsSuperSeeding() const { const QStringList hashes = getSelectedTorrentsHashes(); foreach (const QString &hash, hashes) { - QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (h.is_valid() && h.has_metadata()) - h.super_seeding(!h.status(0).super_seeding); + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (torrent && torrent->hasMetadata()) + torrent->setSuperSeeding(!torrent->superSeeding()); } } @@ -634,8 +597,9 @@ void TransferListWidget::toggleSelectedTorrentsSequentialDownload() const { const QStringList hashes = getSelectedTorrentsHashes(); foreach (const QString &hash, hashes) { - QTorrentHandle h = BTSession->getTorrentHandle(hash); - h.toggleSequentialDownload(); + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (torrent) + torrent->toggleSequentialDownload(); } } @@ -643,8 +607,9 @@ void TransferListWidget::toggleSelectedFirstLastPiecePrio() const { QStringList hashes = getSelectedTorrentsHashes(); foreach (const QString &hash, hashes) { - QTorrentHandle h = BTSession->getTorrentHandle(hash); - h.toggleFirstLastPiecePrio(); + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (torrent) + torrent->setFirstLastPiecePriority(!torrent->hasFirstLastPiecePriority()); } } @@ -685,11 +650,12 @@ void TransferListWidget::renameSelectedTorrent() if (!selectedIndexes.first().isValid()) return; QModelIndex mi = mapToSource(selectedIndexes.first()); const QString hash = getHashFromRow(mi.row()); - const QTorrentHandle h = BTSession->getTorrentHandle(hash); - if (!h.is_valid()) return; + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (!torrent) return; + // Ask for a new Name bool ok; - QString name = AutoExpandableDialog::getText(this, tr("Rename"), tr("New name:"), QLineEdit::Normal, h.name(), &ok); + QString name = AutoExpandableDialog::getText(this, tr("Rename"), tr("New name:"), QLineEdit::Normal, torrent->name(), &ok); if (ok && !name.isEmpty()) { name.replace(QRegExp("\r?\n|\r"), " "); // Rename the torrent @@ -703,23 +669,15 @@ void TransferListWidget::setSelectionLabel(QString label) foreach (const QString &hash, hashes) { Q_ASSERT(!hash.isEmpty()); const int row = getRowFromHash(hash); - const QString old_label = listModel->data(listModel->index(row, TorrentModelItem::TR_LABEL)).toString(); listModel->setData(listModel->index(row, TorrentModelItem::TR_LABEL), QVariant(label), Qt::DisplayRole); - // Update save path if necessary - QTorrentHandle h = BTSession->getTorrentHandle(hash); - BTSession->changeLabelInTorrentSavePath(h, old_label, label); } } void TransferListWidget::removeLabelFromRows(QString label) { - for (int i = 0; irowCount(); ++i) { + for (int i = 0; i < listModel->rowCount(); ++i) { if (listModel->data(listModel->index(i, TorrentModelItem::TR_LABEL)) == label) { - const QString hash = getHashFromRow(i); listModel->setData(listModel->index(i, TorrentModelItem::TR_LABEL), "", Qt::DisplayRole); - // Update save path if necessary - QTorrentHandle h = BTSession->getTorrentHandle(hash); - BTSession->changeLabelInTorrentSavePath(h, label, ""); } } } @@ -730,15 +688,15 @@ void TransferListWidget::displayListMenu(const QPoint&) if (selectedIndexes.size() == 0) return; // Create actions - QAction actionStart(IconProvider::instance()->getIcon("media-playback-start"), tr("Resume", "Resume/start the torrent"), 0); + QAction actionStart(GuiIconProvider::instance()->getIcon("media-playback-start"), tr("Resume", "Resume/start the torrent"), 0); connect(&actionStart, SIGNAL(triggered()), this, SLOT(startSelectedTorrents())); QAction actionForceStart(tr("Force Resume", "Force Resume/start the torrent"), 0); connect(&actionForceStart, SIGNAL(triggered()), this, SLOT(forceStartSelectedTorrents())); - QAction actionPause(IconProvider::instance()->getIcon("media-playback-pause"), tr("Pause", "Pause the torrent"), 0); + QAction actionPause(GuiIconProvider::instance()->getIcon("media-playback-pause"), tr("Pause", "Pause the torrent"), 0); connect(&actionPause, SIGNAL(triggered()), this, SLOT(pauseSelectedTorrents())); - QAction actionDelete(IconProvider::instance()->getIcon("edit-delete"), tr("Delete", "Delete the torrent"), 0); + QAction actionDelete(GuiIconProvider::instance()->getIcon("edit-delete"), tr("Delete", "Delete the torrent"), 0); connect(&actionDelete, SIGNAL(triggered()), this, SLOT(deleteSelectedTorrents())); - QAction actionPreview_file(IconProvider::instance()->getIcon("view-preview"), tr("Preview file..."), 0); + QAction actionPreview_file(GuiIconProvider::instance()->getIcon("view-preview"), tr("Preview file..."), 0); connect(&actionPreview_file, SIGNAL(triggered()), this, SLOT(previewSelectedTorrents())); QAction actionSet_max_ratio(QIcon(QString::fromUtf8(":/icons/skin/ratio.png")), tr("Limit share ratio..."), 0); connect(&actionSet_max_ratio, SIGNAL(triggered()), this, SLOT(setMaxRatioSelectedTorrents())); @@ -746,28 +704,28 @@ void TransferListWidget::displayListMenu(const QPoint&) connect(&actionSet_upload_limit, SIGNAL(triggered()), this, SLOT(setUpLimitSelectedTorrents())); QAction actionSet_download_limit(QIcon(QString::fromUtf8(":/icons/skin/download.png")), tr("Limit download rate..."), 0); connect(&actionSet_download_limit, SIGNAL(triggered()), this, SLOT(setDlLimitSelectedTorrents())); - QAction actionOpen_destination_folder(IconProvider::instance()->getIcon("inode-directory"), tr("Open destination folder"), 0); + QAction actionOpen_destination_folder(GuiIconProvider::instance()->getIcon("inode-directory"), tr("Open destination folder"), 0); connect(&actionOpen_destination_folder, SIGNAL(triggered()), this, SLOT(openSelectedTorrentsFolder())); - QAction actionIncreasePriority(IconProvider::instance()->getIcon("go-up"), tr("Move up", "i.e. move up in the queue"), 0); + QAction actionIncreasePriority(GuiIconProvider::instance()->getIcon("go-up"), tr("Move up", "i.e. move up in the queue"), 0); connect(&actionIncreasePriority, SIGNAL(triggered()), this, SLOT(increasePrioSelectedTorrents())); - QAction actionDecreasePriority(IconProvider::instance()->getIcon("go-down"), tr("Move down", "i.e. Move down in the queue"), 0); + QAction actionDecreasePriority(GuiIconProvider::instance()->getIcon("go-down"), tr("Move down", "i.e. Move down in the queue"), 0); connect(&actionDecreasePriority, SIGNAL(triggered()), this, SLOT(decreasePrioSelectedTorrents())); - QAction actionTopPriority(IconProvider::instance()->getIcon("go-top"), tr("Move to top", "i.e. Move to top of the queue"), 0); + QAction actionTopPriority(GuiIconProvider::instance()->getIcon("go-top"), tr("Move to top", "i.e. Move to top of the queue"), 0); connect(&actionTopPriority, SIGNAL(triggered()), this, SLOT(topPrioSelectedTorrents())); - QAction actionBottomPriority(IconProvider::instance()->getIcon("go-bottom"), tr("Move to bottom", "i.e. Move to bottom of the queue"), 0); + QAction actionBottomPriority(GuiIconProvider::instance()->getIcon("go-bottom"), tr("Move to bottom", "i.e. Move to bottom of the queue"), 0); connect(&actionBottomPriority, SIGNAL(triggered()), this, SLOT(bottomPrioSelectedTorrents())); - QAction actionSetTorrentPath(IconProvider::instance()->getIcon("inode-directory"), tr("Set location..."), 0); + QAction actionSetTorrentPath(GuiIconProvider::instance()->getIcon("inode-directory"), tr("Set location..."), 0); connect(&actionSetTorrentPath, SIGNAL(triggered()), this, SLOT(setSelectedTorrentsLocation())); - QAction actionForce_recheck(IconProvider::instance()->getIcon("document-edit-verify"), tr("Force recheck"), 0); + QAction actionForce_recheck(GuiIconProvider::instance()->getIcon("document-edit-verify"), tr("Force recheck"), 0); connect(&actionForce_recheck, SIGNAL(triggered()), this, SLOT(recheckSelectedTorrents())); QAction actionCopy_magnet_link(QIcon(":/icons/magnet.png"), tr("Copy magnet link"), 0); connect(&actionCopy_magnet_link, SIGNAL(triggered()), this, SLOT(copySelectedMagnetURIs())); - QAction actionCopy_name(IconProvider::instance()->getIcon("edit-copy"), tr("Copy name"), 0); + QAction actionCopy_name(GuiIconProvider::instance()->getIcon("edit-copy"), tr("Copy name"), 0); connect(&actionCopy_name, SIGNAL(triggered()), this, SLOT(copySelectedNames())); QAction actionSuper_seeding_mode(tr("Super seeding mode"), 0); actionSuper_seeding_mode.setCheckable(true); connect(&actionSuper_seeding_mode, SIGNAL(triggered()), this, SLOT(toggleSelectedTorrentsSuperSeeding())); - QAction actionRename(IconProvider::instance()->getIcon("edit-rename"), tr("Rename..."), 0); + QAction actionRename(GuiIconProvider::instance()->getIcon("edit-rename"), tr("Rename..."), 0); connect(&actionRename, SIGNAL(triggered()), this, SLOT(renameSelectedTorrent())); QAction actionSequential_download(tr("Download in sequential order"), 0); actionSequential_download.setCheckable(true); @@ -786,43 +744,45 @@ void TransferListWidget::displayListMenu(const QPoint&) bool one_has_metadata = false, one_not_seed = false; bool first = true; bool forced = false; - QTorrentHandle h; + + BitTorrent::TorrentHandle *torrent; qDebug("Displaying menu"); foreach (const QModelIndex &index, selectedIndexes) { // Get the file name QString hash = getHashFromRow(mapToSource(index).row()); // Get handle and pause the torrent - h = BTSession->getTorrentHandle(hash); - if (!h.is_valid()) continue; - if (h.has_metadata()) + torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (!torrent) continue; + + if (torrent->hasMetadata()) one_has_metadata = true; - forced = h.is_forced(); - if (!h.is_seed()) { + forced = torrent->isForced(); + if (!torrent->isSeed()) { one_not_seed = true; - if (h.has_metadata()) { + if (torrent->hasMetadata()) { if (first) { - sequential_download_mode = h.is_sequential_download(); - prioritize_first_last = h.first_last_piece_first(); + sequential_download_mode = torrent->isSequentialDownload(); + prioritize_first_last = torrent->hasFirstLastPiecePriority(); } else { - if (sequential_download_mode != h.is_sequential_download()) + if (sequential_download_mode != torrent->isSequentialDownload()) all_same_sequential_download_mode = false; - if (prioritize_first_last != h.first_last_piece_first()) + if (prioritize_first_last != torrent->hasFirstLastPiecePriority()) all_same_prio_firstlast = false; } } } else { - if (!one_not_seed && all_same_super_seeding && h.has_metadata()) { + if (!one_not_seed && all_same_super_seeding && torrent->hasMetadata()) { if (first) { - super_seeding_mode = h.status(0).super_seeding; + super_seeding_mode = torrent->superSeeding(); } - else if (super_seeding_mode != h.status(0).super_seeding) + else if (super_seeding_mode != torrent->superSeeding()) all_same_super_seeding = false; } } - if (h.is_paused()) { + if (torrent->isPaused()) { if (!has_start) { listMenu.addAction(&actionStart); has_start = true; @@ -852,7 +812,7 @@ void TransferListWidget::displayListMenu(const QPoint&) has_pause = true; } } - if (h.has_metadata() && !has_preview) + if (torrent->hasMetadata() && !has_preview) has_preview = true; first = false; if (has_pause && has_start && has_force && has_preview && one_not_seed) break; @@ -867,12 +827,12 @@ void TransferListWidget::displayListMenu(const QPoint&) QStringList customLabels = Preferences::instance()->getTorrentLabels(); customLabels.sort(); QList labelActions; - QMenu *labelMenu = listMenu.addMenu(IconProvider::instance()->getIcon("view-categories"), tr("Label")); - labelActions << labelMenu->addAction(IconProvider::instance()->getIcon("list-add"), tr("New...", "New label...")); - labelActions << labelMenu->addAction(IconProvider::instance()->getIcon("edit-clear"), tr("Reset", "Reset label")); + QMenu *labelMenu = listMenu.addMenu(GuiIconProvider::instance()->getIcon("view-categories"), tr("Label")); + labelActions << labelMenu->addAction(GuiIconProvider::instance()->getIcon("list-add"), tr("New...", "New label...")); + labelActions << labelMenu->addAction(GuiIconProvider::instance()->getIcon("edit-clear"), tr("Reset", "Reset label")); labelMenu->addSeparator(); foreach (const QString &label, customLabels) - labelActions << labelMenu->addAction(IconProvider::instance()->getIcon("inode-directory"), label); + labelActions << labelMenu->addAction(GuiIconProvider::instance()->getIcon("inode-directory"), label); listMenu.addSeparator(); if (one_not_seed) listMenu.addAction(&actionSet_download_limit); @@ -907,7 +867,7 @@ void TransferListWidget::displayListMenu(const QPoint&) listMenu.addSeparator(); } listMenu.addAction(&actionOpen_destination_folder); - if (BTSession->isQueueingEnabled() && one_not_seed) { + if (BitTorrent::Session::instance()->isQueueingEnabled() && one_not_seed) { listMenu.addSeparator(); QMenu *prioMenu = listMenu.addMenu(tr("Priority")); prioMenu->addAction(&actionTopPriority); @@ -944,14 +904,14 @@ void TransferListWidget::displayListMenu(const QPoint&) void TransferListWidget::currentChanged(const QModelIndex& current, const QModelIndex&) { qDebug("CURRENT CHANGED"); - QTorrentHandle h; + BitTorrent::TorrentHandle *torrent = 0; if (current.isValid()) { const int row = mapToSource(current).row(); - h = BTSession->getTorrentHandle(getHashFromRow(row)); + torrent = BitTorrent::Session::instance()->findTorrent(getHashFromRow(row)); // Scroll Fix scrollTo(current); } - emit currentTorrentChanged(h); + emit currentTorrentChanged(torrent); } void TransferListWidget::applyLabelFilterAll() @@ -982,7 +942,7 @@ void TransferListWidget::applyNameFilter(const QString& name) void TransferListWidget::applyStatusFilter(int f) { - nameFilterModel->setStatusFilter((TorrentFilter::TorrentFilter)f); + nameFilterModel->setStatusFilter(static_cast(f)); // Select first item if nothing is selected if (selectionModel()->selectedRows(0).empty() && nameFilterModel->rowCount() > 0) { qDebug("Nothing is selected, selecting first row: %s", qPrintable(nameFilterModel->index(0, TorrentModelItem::TR_NAME).data().toString())); diff --git a/src/gui/transferlistwidget.h b/src/gui/transferlistwidget.h index 5077ca1cc..da1469702 100644 --- a/src/gui/transferlistwidget.h +++ b/src/gui/transferlistwidget.h @@ -32,10 +32,12 @@ #define TRANSFERLISTWIDGET_H #include -#include -class QBtSession; -class QTorrentHandle; +namespace BitTorrent +{ + class TorrentHandle; +} + class MainWindow; class TransferListDelegate; class TransferListSortModel; @@ -52,14 +54,15 @@ class TransferListWidget: public QTreeView Q_OBJECT public: - TransferListWidget(QWidget *parent, MainWindow *main_window, QBtSession* BTSession); + TransferListWidget(QWidget *parent, MainWindow *main_window); ~TransferListWidget(); TorrentModel* getSourceModel() const; public slots: void setSelectionLabel(QString label); - void setRefreshInterval(int t); void setSelectedTorrentsLocation(); + void pauseAllTorrents(); + void resumeAllTorrents(); void startSelectedTorrents(); void forceStartSelectedTorrents(); void startVisibleTorrents(); @@ -113,13 +116,12 @@ private: bool openUrl(const QString& _path) const; signals: - void currentTorrentChanged(const QTorrentHandle &h); + void currentTorrentChanged(BitTorrent::TorrentHandle *const torrent); private: TransferListDelegate *listDelegate; TorrentModel *listModel; TransferListSortModel *nameFilterModel; - QBtSession* BTSession; MainWindow *main_window; QShortcut *editHotkey; QShortcut *deleteHotkey; diff --git a/src/searchengine/engineselectdlg.cpp b/src/searchengine/engineselectdlg.cpp index 5ededabb0..b189d0732 100644 --- a/src/searchengine/engineselectdlg.cpp +++ b/src/searchengine/engineselectdlg.cpp @@ -29,13 +29,14 @@ */ #include "engineselectdlg.h" -#include "core/downloadthread.h" +#include "core/net/downloadmanager.h" +#include "core/net/downloadhandler.h" #include "core/fs_utils.h" #include "core/misc.h" #include "ico.h" #include "searchengine.h" #include "pluginsource.h" -#include "iconprovider.h" +#include "guiiconprovider.h" #include "autoexpandabledialog.h" #include #include @@ -55,12 +56,9 @@ engineSelectDlg::engineSelectDlg(QWidget *parent, SupportedEngines *supported_en pluginsTree->header()->resizeSection(0, 170); pluginsTree->header()->resizeSection(1, 220); pluginsTree->hideColumn(ENGINE_ID); - actionUninstall->setIcon(IconProvider::instance()->getIcon("list-remove")); + actionUninstall->setIcon(GuiIconProvider::instance()->getIcon("list-remove")); connect(actionEnable, SIGNAL(toggled(bool)), this, SLOT(enableSelection(bool))); connect(pluginsTree, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayContextMenu(const QPoint&))); - 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))); loadSupportedSearchEngines(); connect(supported_engines, SIGNAL(newSupportedEngine(QString)), this, SLOT(addNewEngine(QString))); connect(pluginsTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(toggleEngineState(QTreeWidgetItem*, int))); @@ -70,8 +68,6 @@ engineSelectDlg::engineSelectDlg(QWidget *parent, SupportedEngines *supported_en engineSelectDlg::~engineSelectDlg() { qDebug("Destroying engineSelectDlg"); emit enginesChanged(); - qDebug("Before deleting downloader"); - delete downloader; qDebug("Engine plugins dialog destroyed"); } @@ -82,7 +78,7 @@ void engineSelectDlg::dropEvent(QDropEvent *event) { qDebug("dropped %s", qPrintable(file)); if (misc::isUrl(file)) { setCursor(QCursor(Qt::WaitCursor)); - downloader->downloadUrl(file); + downloadFromUrl(file); continue; } if (file.endsWith(".py", Qt::CaseInsensitive)) { @@ -109,7 +105,7 @@ void engineSelectDlg::dragEnterEvent(QDragEnterEvent *event) { void engineSelectDlg::on_updateButton_clicked() { // Download version file from update server on sourceforge setCursor(QCursor(Qt::WaitCursor)); - downloader->downloadUrl(QString(UPDATE_URL)+"versions.txt"); + downloadFromUrl(QString(UPDATE_URL) + "versions.txt"); } void engineSelectDlg::toggleEngineState(QTreeWidgetItem *item, int) { @@ -315,7 +311,7 @@ void engineSelectDlg::addNewEngine(QString engine_name) { item->setData(ENGINE_NAME, Qt::DecorationRole, QVariant(QIcon(iconPath))); } else { // Icon is missing, we must download it - downloader->downloadUrl(engine->getUrl()+"/favicon.ico"); + downloadFromUrl(engine->getUrl() + "/favicon.ico"); } } } @@ -346,7 +342,7 @@ void engineSelectDlg::askForPluginUrl() { } setCursor(QCursor(Qt::WaitCursor)); - downloader->downloadUrl(url); + downloadFromUrl(url); } void engineSelectDlg::askForLocalPlugin() { @@ -392,8 +388,8 @@ bool engineSelectDlg::parseVersionsFile(QString versions_file) { qDebug("Plugin: %s is outdated", qPrintable(plugin_name)); // Downloading update setCursor(QCursor(Qt::WaitCursor)); - downloader->downloadUrl(UPDATE_URL+plugin_name+".py"); - //downloader->downloadUrl(UPDATE_URL+plugin_name+".png"); + downloadFromUrl(UPDATE_URL + plugin_name + ".py"); + //downloadFromUrl(UPDATE_URL + plugin_name + ".png"); updated = true; }else { qDebug("Plugin: %s is up to date", qPrintable(plugin_name)); @@ -409,7 +405,14 @@ bool engineSelectDlg::parseVersionsFile(QString versions_file) { return file_correct; } -void engineSelectDlg::processDownloadedFile(QString url, QString filePath) { +void engineSelectDlg::downloadFromUrl(const QString &url) +{ + Net::DownloadHandler *handler = Net::DownloadManager::instance()->downloadUrl(url); + connect(handler, SIGNAL(downloadFinished(QString, QString)), this, SLOT(processDownloadedFile(QString, QString))); + connect(handler, SIGNAL(downloadFailed(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString))); +} + +void engineSelectDlg::processDownloadedFile(const QString &url, QString filePath) { filePath = fsutils::fromNativePath(filePath); setCursor(QCursor(Qt::ArrowCursor)); qDebug("engineSelectDlg received %s", qPrintable(url)); @@ -452,7 +455,7 @@ void engineSelectDlg::processDownloadedFile(QString url, QString filePath) { } } -void engineSelectDlg::handleDownloadFailure(QString url, QString reason) { +void engineSelectDlg::handleDownloadFailure(const QString &url, const QString &reason) { setCursor(QCursor(Qt::ArrowCursor)); if (url.endsWith("favicon.ico", Qt::CaseInsensitive)) { qDebug("Could not download favicon: %s, reason: %s", qPrintable(url), qPrintable(reason)); diff --git a/src/searchengine/engineselectdlg.h b/src/searchengine/engineselectdlg.h index 31acdcd47..555e7aec9 100644 --- a/src/searchengine/engineselectdlg.h +++ b/src/searchengine/engineselectdlg.h @@ -34,8 +34,6 @@ #include "ui_engineselect.h" #include "supportedengines.h" -class DownloadThread; - QT_BEGIN_NAMESPACE class QDropEvent; QT_END_NAMESPACE @@ -44,7 +42,8 @@ class engineSelectDlg : public QDialog, public Ui::engineSelect{ Q_OBJECT private: - DownloadThread *downloader; + void downloadFromUrl(const QString &url); + SupportedEngines *supported_engines; public: @@ -66,8 +65,8 @@ class engineSelectDlg : public QDialog, public Ui::engineSelect{ void addNewEngine(QString engine_name); void toggleEngineState(QTreeWidgetItem*, int); void setRowColor(int row, QString color); - void processDownloadedFile(QString url, QString filePath); - void handleDownloadFailure(QString url, QString reason); + void processDownloadedFile(const QString &url, QString filePath); + void handleDownloadFailure(const QString &url, const QString &reason); void displayContextMenu(const QPoint& pos); void enableSelection(bool enable); void on_actionUninstall_triggered(); diff --git a/src/searchengine/searchengine.cpp b/src/searchengine/searchengine.cpp index 40543eb4a..72f285be5 100644 --- a/src/searchengine/searchengine.cpp +++ b/src/searchengine/searchengine.cpp @@ -48,13 +48,14 @@ #endif #include "searchengine.h" -#include "qbtsession.h" +#include "core/bittorrent/session.h" #include "core/fs_utils.h" #include "core/misc.h" #include "core/preferences.h" #include "searchlistdelegate.h" #include "mainwindow.h" -#include "iconprovider.h" +#include "addnewtorrentdialog.h" +#include "guiiconprovider.h" #include "lineedit.h" #define SEARCHHISTORY_MAXSIZE 50 @@ -69,10 +70,10 @@ SearchEngine::SearchEngine(MainWindow* parent) searchBarLayout->insertWidget(0, search_pattern); connect(search_pattern, SIGNAL(returnPressed()), search_button, SLOT(click())); // Icons - search_button->setIcon(IconProvider::instance()->getIcon("edit-find")); - download_button->setIcon(IconProvider::instance()->getIcon("download")); - goToDescBtn->setIcon(IconProvider::instance()->getIcon("application-x-mswinurl")); - enginesButton->setIcon(IconProvider::instance()->getIcon("preferences-system-network")); + search_button->setIcon(GuiIconProvider::instance()->getIcon("edit-find")); + download_button->setIcon(GuiIconProvider::instance()->getIcon("download")); + goToDescBtn->setIcon(GuiIconProvider::instance()->getIcon("application-x-mswinurl")); + enginesButton->setIcon(GuiIconProvider::instance()->getIcon("preferences-system-network")); tabWidget->setTabsClosable(true); connect(tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int))); // Boolean initialization @@ -308,8 +309,11 @@ void SearchEngine::downloadFinished(int exitcode, QProcess::ExitStatus) { QStringList parts = line.split(' '); if (parts.size() == 2) { QString path = parts[0]; - QString url = parts[1]; - QBtSession::instance()->processDownloadedFile(url, path); + + if (Preferences::instance()->useAdditionDialog()) + AddNewTorrentDialog::show(path, mp_mainWindow); + else + BitTorrent::Session::instance()->addTorrent(path); } } qDebug("Deleting downloadProcess"); diff --git a/src/src.pro b/src/src.pro index e210e755a..2a1e35021 100644 --- a/src/src.pro +++ b/src/src.pro @@ -16,7 +16,7 @@ os2: include(../os2conf.pri) nogui { QT -= gui - DEFINES += DISABLE_GUI + DEFINES += DISABLE_GUI DISABLE_COUNTRIES_RESOLUTION TARGET = qbittorrent-nox } else { QT += xml @@ -56,6 +56,8 @@ DEFINES += BOOST_FILESYSTEM_VERSION=2 win32: DEFINES += NOMINMAX +INCLUDEPATH += $$PWD + include(app/app.pri) include(core/core.pri) !nowebui: include(webui/webui.pri) diff --git a/src/webui/btjson.cpp b/src/webui/btjson.cpp index f682a4af2..9eaee5535 100644 --- a/src/webui/btjson.cpp +++ b/src/webui/btjson.cpp @@ -29,10 +29,14 @@ */ #include "btjson.h" +#include "core/misc.h" #include "core/fs_utils.h" -#include "qbtsession.h" -#include "core/torrentpersistentdata.h" -#include "qtorrentfilter.h" +#include "core/preferences.h" +#include "core/bittorrent/session.h" +#include "core/bittorrent/sessionstatus.h" +#include "core/bittorrent/torrenthandle.h" +#include "core/bittorrent/trackerentry.h" +#include "core/torrentfilter.h" #include "jsonutils.h" #include @@ -44,11 +48,6 @@ #include #endif -#include -#include - -using namespace libtorrent; - #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) #define CACHED_VARIABLE(VARTYPE, VAR, DUR) \ @@ -156,7 +155,7 @@ static const char KEY_RESPONSE_ID[] = "rid"; static const char KEY_SUFFIX_REMOVED[] = "_removed"; QVariantMap getTranserInfoMap(); -QVariantMap toMap(const QTorrentHandle& h); +QVariantMap toMap(BitTorrent::TorrentHandle *const torrent); void processMap(QVariantMap prevData, QVariantMap data, QVariantMap &syncData); void processHash(QVariantHash prevData, QVariantHash data, QVariantMap &syncData, QVariantList &removedItems); void processList(QVariantList prevData, QVariantList data, QVariantList &syncData, QVariantList &removedItems); @@ -241,16 +240,9 @@ QByteArray btjson::getTorrents(QString filter, QString label, QString sortedColumn, bool reverse, int limit, int offset) { QVariantList torrent_list; - - std::vector torrents = QBtSession::instance()->getTorrents(); - std::vector::const_iterator it = torrents.begin(); - std::vector::const_iterator end = torrents.end(); - - QTorrentFilter torrentFilter(filter, label); - for (; it != end; ++it) { - QTorrentHandle torrent = QTorrentHandle(*it); - - if (torrentFilter.apply(torrent)) + TorrentFilter torrentFilter(filter, TorrentFilter::AnyHash, label); + foreach (BitTorrent::TorrentHandle *const torrent, BitTorrent::Session::instance()->torrents()) { + if (torrentFilter.match(torrent)) torrent_list.append(toMap(torrent)); } @@ -315,18 +307,12 @@ QByteArray btjson::getTorrents(QString filter, QString label, QByteArray btjson::getSyncMainData(int acceptedResponseId, QVariantMap &lastData, QVariantMap &lastAcceptedData) { QVariantMap data; - QVariantHash torrents; - std::vector torrentsList = QBtSession::instance()->getTorrents(); - std::vector::const_iterator it = torrentsList.begin(); - std::vector::const_iterator end = torrentsList.end(); - - for (; it != end; ++it) { - QTorrentHandle torrent = QTorrentHandle(*it); + foreach (BitTorrent::TorrentHandle *const torrent, BitTorrent::Session::instance()->torrents()) { QVariantMap map = toMap(torrent); map.remove(KEY_TORRENT_HASH); - torrents[torrent.hash()] = map; + torrents[torrent->hash()] = map; } data["torrents"] = torrents; @@ -338,7 +324,7 @@ QByteArray btjson::getSyncMainData(int acceptedResponseId, QVariantMap &lastData data["labels"] = labels; QVariantMap serverState = getTranserInfoMap(); - serverState[KEY_SYNC_MAINDATA_QUEUEING] = QBtSession::instance()->isQueueingEnabled(); + serverState[KEY_SYNC_MAINDATA_QUEUEING] = BitTorrent::Session::instance()->isQueueingEnabled(); serverState[KEY_SYNC_MAINDATA_USE_ALT_SPEED_LIMITS] = Preferences::instance()->isAltBandwidthEnabled(); serverState[KEY_SYNC_MAINDATA_REFRESH_INTERVAL] = Preferences::instance()->getRefreshInterval(); data["server_state"] = serverState; @@ -359,39 +345,35 @@ QByteArray btjson::getSyncMainData(int acceptedResponseId, QVariantMap &lastData QByteArray btjson::getTrackersForTorrent(const QString& hash) { CACHED_VARIABLE_FOR_HASH(QVariantList, tracker_list, CACHE_DURATION_MS, hash); - try { - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - QHash trackers_data = QBtSession::instance()->getTrackersInfo(hash); - std::vector vect_trackers = h.trackers(); - std::vector::const_iterator it = vect_trackers.begin(); - std::vector::const_iterator end = vect_trackers.end(); - for (; it != end; ++it) { - QVariantMap tracker_dict; - const QString tracker_url = misc::toQString(it->url); - tracker_dict[KEY_TRACKER_URL] = tracker_url; - const TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url)); - QString status; - if (it->verified) { - status = tr("Working"); - } - else { - if (it->updating && it->fails == 0) - status = tr("Updating..."); - else - status = it->fails > 0 ? tr("Not working") : tr("Not contacted yet"); - } - tracker_dict[KEY_TRACKER_STATUS] = status; - tracker_dict[KEY_TRACKER_PEERS] = static_cast(trackers_data.value(tracker_url, TrackerInfos(tracker_url)).num_peers); - tracker_dict[KEY_TRACKER_MSG] = data.last_message.trimmed(); - - tracker_list.append(tracker_dict); - } - } - catch (const std::exception& e) { - qWarning() << Q_FUNC_INFO << "Invalid torrent: " << misc::toQStringU(e.what()); + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (!torrent) { + qWarning() << Q_FUNC_INFO << "Invalid torrent " << qPrintable(hash); return QByteArray(); } + QHash trackers_data = torrent->trackerInfos(); + foreach (const BitTorrent::TrackerEntry &tracker, torrent->trackers()) { + QVariantMap tracker_dict; + tracker_dict[KEY_TRACKER_URL] = tracker.url(); + const BitTorrent::TrackerInfo data = trackers_data.value(tracker.url()); + QString status; + switch (tracker.status()) { + case BitTorrent::TrackerEntry::NotContacted: + status = tr("Not contacted yet"); break; + case BitTorrent::TrackerEntry::Updating: + status = tr("Updating..."); break; + case BitTorrent::TrackerEntry::Working: + status = tr("Working"); break; + case BitTorrent::TrackerEntry::NotWorking: + status = tr("Not working"); break; + } + tracker_dict[KEY_TRACKER_STATUS] = status; + tracker_dict[KEY_TRACKER_PEERS] = data.numPeers; + tracker_dict[KEY_TRACKER_MSG] = data.lastMessage.trimmed(); + + tracker_list.append(tracker_dict); + } + return json::toJson(tracker_list); } @@ -420,40 +402,33 @@ QByteArray btjson::getTrackersForTorrent(const QString& hash) QByteArray btjson::getPropertiesForTorrent(const QString& hash) { CACHED_VARIABLE_FOR_HASH(QVariantMap, data, CACHE_DURATION_MS, hash); - try { - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - - libtorrent::torrent_status status = h.status(torrent_handle::query_accurate_download_counters); - if (!status.has_metadata) - return QByteArray(); - - // Save path - QString save_path = fsutils::toNativePath(TorrentPersistentData::instance()->getSavePath(hash)); - if (save_path.isEmpty()) - save_path = fsutils::toNativePath(h.save_path()); - data[KEY_PROP_SAVE_PATH] = save_path; - data[KEY_PROP_CREATION_DATE] = h.creation_date_unix(); - data[KEY_PROP_PIECE_SIZE] = static_cast(h.piece_length()); - data[KEY_PROP_COMMENT] = h.comment(); - data[KEY_PROP_WASTED] = static_cast(status.total_failed_bytes + status.total_redundant_bytes); - data[KEY_PROP_UPLOADED] = static_cast(status.all_time_upload); - data[KEY_PROP_UPLOADED_SESSION] = static_cast(status.total_payload_upload); - data[KEY_PROP_DOWNLOADED] = static_cast(status.all_time_download); - data[KEY_PROP_DOWNLOADED_SESSION] = static_cast(status.total_payload_download); - data[KEY_PROP_UP_LIMIT] = h.upload_limit() <= 0 ? -1 : h.upload_limit(); - data[KEY_PROP_DL_LIMIT] = h.download_limit() <= 0 ? -1 : h.download_limit(); - data[KEY_PROP_TIME_ELAPSED] = status.active_time; - data[KEY_PROP_SEEDING_TIME] = status.seeding_time; - data[KEY_PROP_CONNECT_COUNT] = status.num_connections; - data[KEY_PROP_CONNECT_COUNT_LIMIT] = status.connections_limit; - const qreal ratio = QBtSession::instance()->getRealRatio(status); - data[KEY_PROP_RATIO] = ratio > QBtSession::MAX_RATIO ? -1 : ratio; - } - catch (const std::exception& e) { - qWarning() << Q_FUNC_INFO << "Invalid torrent: " << misc::toQStringU(e.what()); + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (!torrent) { + qWarning() << Q_FUNC_INFO << "Invalid torrent " << qPrintable(hash); return QByteArray(); } + if (!torrent->hasMetadata()) + return QByteArray(); + + data[KEY_PROP_SAVE_PATH] = fsutils::toNativePath(torrent->savePath()); + data[KEY_PROP_CREATION_DATE] = torrent->creationDate().toTime_t(); + data[KEY_PROP_PIECE_SIZE] = torrent->pieceLength(); + data[KEY_PROP_COMMENT] = torrent->comment(); + data[KEY_PROP_WASTED] = torrent->wastedSize(); + data[KEY_PROP_UPLOADED] = torrent->totalUpload(); + data[KEY_PROP_UPLOADED_SESSION] = torrent->totalPayloadUpload(); + data[KEY_PROP_DOWNLOADED] = torrent->totalDownload(); + data[KEY_PROP_DOWNLOADED_SESSION] = torrent->totalPayloadDownload(); + data[KEY_PROP_UP_LIMIT] = torrent->uploadLimit() <= 0 ? -1 : torrent->uploadLimit(); + data[KEY_PROP_DL_LIMIT] = torrent->downloadLimit() <= 0 ? -1 : torrent->downloadLimit(); + data[KEY_PROP_TIME_ELAPSED] = torrent->activeTime(); + data[KEY_PROP_SEEDING_TIME] = torrent->seedingTime(); + data[KEY_PROP_CONNECT_COUNT] = torrent->connectionsCount(); + data[KEY_PROP_CONNECT_COUNT_LIMIT] = torrent->connectionsLimit(); + const qreal ratio = torrent->realRatio(); + data[KEY_PROP_RATIO] = ratio > BitTorrent::TorrentHandle::MAX_RATIO ? -1 : ratio; + return json::toJson(data); } @@ -471,35 +446,33 @@ QByteArray btjson::getPropertiesForTorrent(const QString& hash) QByteArray btjson::getFilesForTorrent(const QString& hash) { CACHED_VARIABLE_FOR_HASH(QVariantList, file_list, CACHE_DURATION_MS, hash); - try { - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - if (!h.has_metadata()) - return QByteArray(); - - const std::vector priorities = h.file_priorities(); - std::vector fp; - h.file_progress(fp); - for (int i = 0; i < h.num_files(); ++i) { - QVariantMap file_dict; - QString fileName = h.filepath_at(i); - if (fileName.endsWith(".!qB", Qt::CaseInsensitive)) - fileName.chop(4); - file_dict[KEY_FILE_NAME] = fsutils::toNativePath(fileName); - const size_type size = h.filesize_at(i); - file_dict[KEY_FILE_SIZE] = static_cast(size); - file_dict[KEY_FILE_PROGRESS] = (size > 0) ? (fp[i] / (double) size) : 1.; - file_dict[KEY_FILE_PRIORITY] = priorities[i]; - if (i == 0) - file_dict[KEY_FILE_IS_SEED] = h.is_seed(); - - file_list.append(file_dict); - } - } - catch (const std::exception& e) { - qWarning() << Q_FUNC_INFO << "Invalid torrent: " << misc::toQStringU(e.what()); + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (!torrent) { + qWarning() << Q_FUNC_INFO << "Invalid torrent " << qPrintable(hash); return QByteArray(); } + if (!torrent->hasMetadata()) + return QByteArray(); + + const QVector priorities = torrent->filePriorities(); + QVector fp = torrent->filesProgress(); + for (int i = 0; i < torrent->filesCount(); ++i) { + QVariantMap file_dict; + QString fileName = torrent->filePath(i); + if (fileName.endsWith(".!qB", Qt::CaseInsensitive)) + fileName.chop(4); + file_dict[KEY_FILE_NAME] = fsutils::toNativePath(fileName); + const qlonglong size = torrent->fileSize(i); + file_dict[KEY_FILE_SIZE] = size; + file_dict[KEY_FILE_PROGRESS] = fp[i]; + file_dict[KEY_FILE_PRIORITY] = priorities[i]; + if (i == 0) + file_dict[KEY_FILE_IS_SEED] = torrent->isSeed(); + + file_list.append(file_dict); + } + return json::toJson(file_list); } @@ -525,19 +498,18 @@ QByteArray btjson::getTransferInfo() QVariantMap getTranserInfoMap() { QVariantMap map; - session_status sessionStatus = QBtSession::instance()->getSessionStatus(); - session_settings sessionSettings = QBtSession::instance()->getSession()->settings(); - map[KEY_TRANSFER_DLSPEED] = sessionStatus.payload_download_rate; - map[KEY_TRANSFER_DLDATA] = static_cast(sessionStatus.total_payload_download); - map[KEY_TRANSFER_UPSPEED] = sessionStatus.payload_upload_rate; - map[KEY_TRANSFER_UPDATA] = static_cast(sessionStatus.total_payload_upload); - map[KEY_TRANSFER_DLRATELIMIT] = sessionSettings.download_rate_limit; - map[KEY_TRANSFER_UPRATELIMIT] = sessionSettings.upload_rate_limit; - map[KEY_TRANSFER_DHT_NODES] = sessionStatus.dht_nodes; - if (!QBtSession::instance()->getSession()->is_listening()) + BitTorrent::SessionStatus sessionStatus = BitTorrent::Session::instance()->status(); + map[KEY_TRANSFER_DLSPEED] = sessionStatus.payloadDownloadRate(); + map[KEY_TRANSFER_DLDATA] = sessionStatus.totalPayloadDownload(); + map[KEY_TRANSFER_UPSPEED] = sessionStatus.payloadUploadRate(); + map[KEY_TRANSFER_UPDATA] = sessionStatus.totalPayloadUpload(); + map[KEY_TRANSFER_DLRATELIMIT] = BitTorrent::Session::instance()->downloadRateLimit(); + map[KEY_TRANSFER_UPRATELIMIT] = BitTorrent::Session::instance()->uploadRateLimit(); + map[KEY_TRANSFER_DHT_NODES] = sessionStatus.dhtNodes(); + if (!BitTorrent::Session::instance()->isListening()) map[KEY_TRANSFER_CONNECTION_STATUS] = "disconnected"; else - map[KEY_TRANSFER_CONNECTION_STATUS] = sessionStatus.has_incoming_connections ? "connected" : "firewalled"; + map[KEY_TRANSFER_CONNECTION_STATUS] = sessionStatus.hasIncomingConnections() ? "connected" : "firewalled"; return map; } @@ -547,44 +519,39 @@ QByteArray btjson::getTorrentsRatesLimits(QStringList &hashes, bool downloadLimi foreach (const QString &hash, hashes) { int limit = -1; - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - if (h.is_valid()) - limit = downloadLimits ? h.download_limit() : h.upload_limit(); + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (torrent) + limit = downloadLimits ? torrent->downloadLimit() : torrent->uploadLimit(); map[hash] = limit; } return json::toJson(map); } -QVariantMap toMap(const QTorrentHandle& h) +QVariantMap toMap(BitTorrent::TorrentHandle *const torrent) { - libtorrent::torrent_status status = h.status(torrent_handle::query_accurate_download_counters); - QVariantMap ret; - ret[KEY_TORRENT_HASH] = h.hash(); - ret[KEY_TORRENT_NAME] = h.name(); - ret[KEY_TORRENT_SIZE] = static_cast(status.total_wanted); - ret[KEY_TORRENT_PROGRESS] = h.progress(status); - ret[KEY_TORRENT_DLSPEED] = status.download_payload_rate; - ret[KEY_TORRENT_UPSPEED] = status.upload_payload_rate; - if (QBtSession::instance()->isQueueingEnabled() && h.queue_position(status) >= 0) - ret[KEY_TORRENT_PRIORITY] = h.queue_position(status); - else - ret[KEY_TORRENT_PRIORITY] = -1; - ret[KEY_TORRENT_SEEDS] = status.num_seeds; - ret[KEY_TORRENT_NUM_COMPLETE] = status.num_complete; - ret[KEY_TORRENT_LEECHS] = status.num_peers - status.num_seeds; - ret[KEY_TORRENT_NUM_INCOMPLETE] = status.num_incomplete; - const qreal ratio = QBtSession::instance()->getRealRatio(status); - ret[KEY_TORRENT_RATIO] = (ratio > QBtSession::MAX_RATIO) ? -1 : ratio; - ret[KEY_TORRENT_STATE] = h.torrentState().toString(); - ret[KEY_TORRENT_ETA] = h.eta(); - ret[KEY_TORRENT_SEQUENTIAL_DOWNLOAD] = status.sequential_download; - if (h.has_metadata()) - ret[KEY_TORRENT_FIRST_LAST_PIECE_PRIO] = h.first_last_piece_first(); - ret[KEY_TORRENT_LABEL] = TorrentPersistentData::instance()->getLabel(h.hash()); - ret[KEY_TORRENT_SUPER_SEEDING] = status.super_seeding; - ret[KEY_TORRENT_FORCE_START] = h.is_forced(status); + ret[KEY_TORRENT_HASH] = QString(torrent->hash()); + ret[KEY_TORRENT_NAME] = torrent->name(); + ret[KEY_TORRENT_SIZE] = torrent->wantedSize(); + ret[KEY_TORRENT_PROGRESS] = torrent->progress(); + ret[KEY_TORRENT_DLSPEED] = torrent->downloadPayloadRate(); + ret[KEY_TORRENT_UPSPEED] = torrent->uploadPayloadRate(); + ret[KEY_TORRENT_PRIORITY] = torrent->queuePosition(); + ret[KEY_TORRENT_SEEDS] = torrent->seedsCount(); + ret[KEY_TORRENT_NUM_COMPLETE] = torrent->completeCount(); + ret[KEY_TORRENT_LEECHS] = torrent->leechsCount(); + ret[KEY_TORRENT_NUM_INCOMPLETE] = torrent->incompleteCount(); + const qreal ratio = torrent->realRatio(); + ret[KEY_TORRENT_RATIO] = (ratio > BitTorrent::TorrentHandle::MAX_RATIO) ? -1 : ratio; + ret[KEY_TORRENT_STATE] = torrent->state().toString(); + ret[KEY_TORRENT_ETA] = torrent->eta(); + ret[KEY_TORRENT_SEQUENTIAL_DOWNLOAD] = torrent->isSequentialDownload(); + if (torrent->hasMetadata()) + ret[KEY_TORRENT_FIRST_LAST_PIECE_PRIO] = torrent->hasFirstLastPiecePriority(); + ret[KEY_TORRENT_LABEL] = torrent->label(); + ret[KEY_TORRENT_SUPER_SEEDING] = torrent->superSeeding(); + ret[KEY_TORRENT_FORCE_START] = torrent->isForced(); return ret; } diff --git a/src/webui/btjson.h b/src/webui/btjson.h index fb9faf434..e919c6531 100644 --- a/src/webui/btjson.h +++ b/src/webui/btjson.h @@ -35,8 +35,6 @@ #include #include -class QTorrentHandle; - class btjson { Q_DECLARE_TR_FUNCTIONS(misc) diff --git a/src/webui/prefjson.cpp b/src/webui/prefjson.cpp index 02ac99d87..5fb2d59cd 100644 --- a/src/webui/prefjson.cpp +++ b/src/webui/prefjson.cpp @@ -30,15 +30,14 @@ #include "prefjson.h" #include "core/preferences.h" -#include "qbtsession.h" -#include "core/scannedfoldersmodel.h" +#include "core/scanfoldersmodel.h" #include "core/fs_utils.h" -#include #ifndef QT_NO_OPENSSL #include #include #endif +#include #include #include #include "jsonutils.h" @@ -183,7 +182,7 @@ void prefjson::setPreferences(const QString& json) foreach (const QString &old_folder, old_folders) { // Update deleted folders if (!new_folders.contains(old_folder)) { - QBtSession::instance()->getScanFoldersModel()->removePath(old_folder); + ScanFoldersModel::instance()->removePath(old_folder); } } int i = 0; @@ -191,7 +190,7 @@ void prefjson::setPreferences(const QString& json) qDebug("New watched folder: %s", qPrintable(new_folder)); // Update new folders if (!old_folders.contains(fsutils::fromNativePath(new_folder))) { - QBtSession::instance()->getScanFoldersModel()->addPath(new_folder, download_at_path.at(i)); + ScanFoldersModel::instance()->addPath(new_folder, download_at_path.at(i)); } ++i; } @@ -337,5 +336,5 @@ void prefjson::setPreferences(const QString& json) if (m.contains("dyndns_domain")) pref->setDynDomainName(m["dyndns_domain"].toString()); // Save preferences - pref->save(); + pref->apply(); } diff --git a/src/webui/webapplication.cpp b/src/webui/webapplication.cpp index 92a55f179..12fa7034f 100644 --- a/src/webui/webapplication.cpp +++ b/src/webui/webapplication.cpp @@ -32,22 +32,19 @@ #include #include #include -#include -#ifndef DISABLE_GUI -// TODO: Drop GUI dependency! -#include "iconprovider.h" -#endif +#include "core/iconprovider.h" #include "core/misc.h" #include "core/fs_utils.h" #include "core/preferences.h" #include "btjson.h" #include "prefjson.h" -#include "qbtsession.h" +#include "core/bittorrent/session.h" +#include "core/bittorrent/trackerentry.h" +#include "core/bittorrent/torrentinfo.h" +#include "core/bittorrent/torrenthandle.h" #include "websessiondata.h" #include "webapplication.h" -using namespace libtorrent; - static const int API_VERSION = 2; static const int API_VERSION_MIN = 2; @@ -195,11 +192,7 @@ void WebApplication::action_public_theme() return; } -#ifdef DISABLE_GUI - QString url = ":/icons/oxygen/" + args_.front() + ".png"; -#else QString url = IconProvider::instance()->getIconPath(args_.front()); -#endif qDebug() << Q_FUNC_INFO << "There icon:" << url; printFile(url); @@ -312,13 +305,8 @@ void WebApplication::action_command_download() qDebug("Converting bc link to magnet link"); url = misc::bcLinkToMagnet(url); } - else if (url.startsWith("magnet:", Qt::CaseInsensitive)) { - QBtSession::instance()->addMagnetSkipAddDlg(url); - } - else { - qDebug("Downloading url: %s", qPrintable(url)); - QBtSession::instance()->downloadUrlAndSkipDialog(url); - } + + BitTorrent::Session::instance()->addTorrent(url); } } } @@ -332,11 +320,17 @@ void WebApplication::action_command_upload() QString filePath = saveTmpFile(torrent.data); if (!filePath.isEmpty()) { - QTorrentHandle h = QBtSession::instance()->addTorrent(filePath); - if (!h.is_valid()) { - status(415, "Internal Server Error"); + BitTorrent::TorrentInfo torrentInfo = BitTorrent::TorrentInfo::loadFromFile(filePath); + if (!torrentInfo.isValid()) { + status(415, "Unsupported Media Type"); print(QObject::tr("Error: '%1' is not a valid torrent file.\n").arg(torrent.filename), Http::CONTENT_TYPE_TXT); } + else { + if (!BitTorrent::Session::instance()->addTorrent(torrentInfo)) { + status(500, "Internal Server Error"); + print(QObject::tr("Error: Could not add torrent to session."), Http::CONTENT_TYPE_TXT); + } + } // Clean up fsutils::forceRemove(filePath); } @@ -354,37 +348,49 @@ void WebApplication::action_command_addTrackers() CHECK_PARAMETERS("hash" << "urls"); QString hash = request().posts["hash"]; - if (!hash.isEmpty()) { - QString urls = request().posts["urls"]; - QStringList list = urls.split('\n'); - QBtSession::instance()->addTrackersAndUrlSeeds(hash, list, QStringList()); + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (torrent) { + QList trackers; + foreach (const QString &url, request().posts["urls"].split('\n')) + trackers << url; + torrent->addTrackers(trackers); } } void WebApplication::action_command_resumeAll() { CHECK_URI(0); - QBtSession::instance()->resumeAllTorrents(); + + foreach (BitTorrent::TorrentHandle *const torrent, BitTorrent::Session::instance()->torrents()) + torrent->resume(); } void WebApplication::action_command_pauseAll() { CHECK_URI(0); - QBtSession::instance()->pauseAllTorrents(); + + foreach (BitTorrent::TorrentHandle *const torrent, BitTorrent::Session::instance()->torrents()) + torrent->pause(); } void WebApplication::action_command_resume() { CHECK_URI(0); CHECK_PARAMETERS("hash"); - QBtSession::instance()->resumeTorrent(request().posts["hash"]); + + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(request().posts["hash"]); + if (torrent) + torrent->resume(); } void WebApplication::action_command_pause() { CHECK_URI(0); CHECK_PARAMETERS("hash"); - QBtSession::instance()->pauseTorrent(request().posts["hash"]); + + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(request().posts["hash"]); + if (torrent) + torrent->pause(); } void WebApplication::action_command_setPreferences() @@ -401,22 +407,22 @@ void WebApplication::action_command_setFilePrio() QString hash = request().posts["hash"]; int file_id = request().posts["id"].toInt(); int priority = request().posts["priority"].toInt(); - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); - if (h.is_valid() && h.has_metadata()) - h.file_priority(file_id, priority); + if (torrent && torrent->hasMetadata()) + torrent->setFilePriority(file_id, priority); } void WebApplication::action_command_getGlobalUpLimit() { CHECK_URI(0); - print(QByteArray::number(QBtSession::instance()->getSession()->settings().upload_rate_limit)); + print(QByteArray::number(BitTorrent::Session::instance()->uploadRateLimit())); } void WebApplication::action_command_getGlobalDlLimit() { CHECK_URI(0); - print(QByteArray::number(QBtSession::instance()->getSession()->settings().download_rate_limit)); + print(QByteArray::number(BitTorrent::Session::instance()->downloadRateLimit())); } void WebApplication::action_command_setGlobalUpLimit() @@ -426,7 +432,7 @@ void WebApplication::action_command_setGlobalUpLimit() qlonglong limit = request().posts["limit"].toLongLong(); if (limit == 0) limit = -1; - QBtSession::instance()->setUploadRateLimit(limit); + BitTorrent::Session::instance()->setUploadRateLimit(limit); if (Preferences::instance()->isAltBandwidthEnabled()) Preferences::instance()->setAltGlobalUploadLimit(limit / 1024.); else @@ -440,7 +446,7 @@ void WebApplication::action_command_setGlobalDlLimit() qlonglong limit = request().posts["limit"].toLongLong(); if (limit == 0) limit = -1; - QBtSession::instance()->setDownloadRateLimit(limit); + BitTorrent::Session::instance()->setDownloadRateLimit(limit); if (Preferences::instance()->isAltBandwidthEnabled()) Preferences::instance()->setAltGlobalDownloadLimit(limit / 1024.); else @@ -474,9 +480,9 @@ void WebApplication::action_command_setTorrentsUpLimit() QStringList hashes = request().posts["hashes"].split("|"); foreach (const QString &hash, hashes) { - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - if (h.is_valid()) - h.set_upload_limit(limit); + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (torrent) + torrent->setUploadLimit(limit); } } @@ -491,16 +497,16 @@ void WebApplication::action_command_setTorrentsDlLimit() QStringList hashes = request().posts["hashes"].split("|"); foreach (const QString &hash, hashes) { - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - if (h.is_valid()) - h.set_download_limit(limit); + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (torrent) + torrent->setDownloadLimit(limit); } } void WebApplication::action_command_toggleAlternativeSpeedLimits() { CHECK_URI(0); - QBtSession::instance()->useAlternativeSpeedsLimit(!Preferences::instance()->isAltBandwidthEnabled()); + BitTorrent::Session::instance()->changeSpeedLimitMode(!Preferences::instance()->isAltBandwidthEnabled()); } void WebApplication::action_command_alternativeSpeedLimitsEnabled() @@ -515,11 +521,9 @@ void WebApplication::action_command_toggleSequentialDownload() CHECK_PARAMETERS("hashes"); QStringList hashes = request().posts["hashes"].split("|"); foreach (const QString &hash, hashes) { - try { - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - h.toggleSequentialDownload(); - } - catch(invalid_handle&) {} + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (torrent) + torrent->toggleSequentialDownload(); } } @@ -529,11 +533,9 @@ void WebApplication::action_command_toggleFirstLastPiecePrio() CHECK_PARAMETERS("hashes"); QStringList hashes = request().posts["hashes"].split("|"); foreach (const QString &hash, hashes) { - try { - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - h.toggleFirstLastPiecePrio(); - } - catch(invalid_handle&) {} + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (torrent) + torrent->toggleFirstLastPiecePriority(); } } @@ -544,11 +546,9 @@ void WebApplication::action_command_setSuperSeeding() bool value = request().posts["value"] == "true"; QStringList hashes = request().posts["hashes"].split("|"); foreach (const QString &hash, hashes) { - try { - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - h.super_seeding(value); - } - catch(invalid_handle&) {} + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (torrent) + torrent->setSuperSeeding(value); } } @@ -559,9 +559,9 @@ void WebApplication::action_command_setForceStart() bool value = request().posts["value"] == "true"; QStringList hashes = request().posts["hashes"].split("|"); foreach (const QString &hash, hashes) { - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - if (h.is_valid()) - QBtSession::instance()->resumeTorrent(hash, value); + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); + if (torrent) + torrent->resume(value); } } @@ -571,7 +571,7 @@ void WebApplication::action_command_delete() CHECK_PARAMETERS("hashes"); QStringList hashes = request().posts["hashes"].split("|"); foreach (const QString &hash, hashes) - QBtSession::instance()->deleteTorrent(hash, false); + BitTorrent::Session::instance()->deleteTorrent(hash, false); } void WebApplication::action_command_deletePerm() @@ -580,7 +580,7 @@ void WebApplication::action_command_deletePerm() CHECK_PARAMETERS("hashes"); QStringList hashes = request().posts["hashes"].split("|"); foreach (const QString &hash, hashes) - QBtSession::instance()->deleteTorrent(hash, true); + BitTorrent::Session::instance()->deleteTorrent(hash, true); } void WebApplication::action_command_increasePrio() @@ -594,32 +594,7 @@ void WebApplication::action_command_increasePrio() } QStringList hashes = request().posts["hashes"].split("|"); - - std::priority_queue, - std::vector >, - std::greater > > torrent_queue; - - // Sort torrents by priority - foreach (const QString &hash, hashes) { - try { - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - if (!h.is_seed()) - torrent_queue.push(qMakePair(h.queue_position(), h)); - } - catch(invalid_handle&) {} - } - - // Increase torrents priority (starting with the ones with highest priority) - while(!torrent_queue.empty()) { - QTorrentHandle h = torrent_queue.top().second; - - try { - h.queue_position_up(); - } - catch(invalid_handle&) {} - - torrent_queue.pop(); - } + BitTorrent::Session::instance()->increaseTorrentsPriority(hashes); } void WebApplication::action_command_decreasePrio() @@ -633,33 +608,7 @@ void WebApplication::action_command_decreasePrio() } QStringList hashes = request().posts["hashes"].split("|"); - - std::priority_queue, - std::vector >, - std::less > > torrent_queue; - - // Sort torrents by priority - foreach (const QString &hash, hashes) { - try { - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - - if (!h.is_seed()) - torrent_queue.push(qMakePair(h.queue_position(), h)); - } - catch(invalid_handle&) {} - } - - // Decrease torrents priority (starting with the ones with lowest priority) - while(!torrent_queue.empty()) { - QTorrentHandle h = torrent_queue.top().second; - - try { - h.queue_position_down(); - } - catch(invalid_handle&) {} - - torrent_queue.pop(); - } + BitTorrent::Session::instance()->decreaseTorrentsPriority(hashes); } void WebApplication::action_command_topPrio() @@ -672,10 +621,8 @@ void WebApplication::action_command_topPrio() return; } - foreach (const QString &hash, request().posts["hashes"].split("|")) { - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - if (h.is_valid()) h.queue_position_top(); - } + QStringList hashes = request().posts["hashes"].split("|"); + BitTorrent::Session::instance()->topTorrentsPriority(hashes); } void WebApplication::action_command_bottomPrio() @@ -688,17 +635,18 @@ void WebApplication::action_command_bottomPrio() return; } - foreach (const QString &hash, request().posts["hashes"].split("|")) { - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - if (h.is_valid()) h.queue_position_bottom(); - } + QStringList hashes = request().posts["hashes"].split("|"); + BitTorrent::Session::instance()->bottomTorrentsPriority(hashes); } void WebApplication::action_command_recheck() { CHECK_URI(0); CHECK_PARAMETERS("hash"); - QBtSession::instance()->recheckTorrent(request().posts["hash"]); + + BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(request().posts["hash"]); + if (torrent) + torrent->forceRecheck(); } bool WebApplication::isPublicScope() diff --git a/src/webui/webui.cpp b/src/webui/webui.cpp index 244f345d8..2f91e34b4 100644 --- a/src/webui/webui.cpp +++ b/src/webui/webui.cpp @@ -26,14 +26,17 @@ * exception statement from your version. */ -#include "webui.h" -#include "core/http/server.h" -#include "webapplication.h" -#include "core/net/dnsupdater.h" #include "core/preferences.h" #include "core/logger.h" +#include "core/http/server.h" +#include "core/net/dnsupdater.h" +#include "core/net/portforwarder.h" +#include "webapplication.h" +#include "webui.h" -WebUI::WebUI(QObject *parent) : QObject(parent) +WebUI::WebUI(QObject *parent) + : QObject(parent) + , m_port(0) { init(); connect(Preferences::instance(), SIGNAL(changed()), SLOT(init())); @@ -46,9 +49,13 @@ void WebUI::init() if (pref->isWebUiEnabled()) { const quint16 port = pref->getWebUiPort(); + if (m_port != port) { + Net::PortForwarder::instance()->deletePort(port); + m_port = port; + } if (httpServer_) { - if (httpServer_->serverPort() != port) + if (httpServer_->serverPort() != m_port) httpServer_->close(); } else { @@ -72,11 +79,11 @@ void WebUI::init() #endif if (!httpServer_->isListening()) { - bool success = httpServer_->listen(QHostAddress::Any, port); + bool success = httpServer_->listen(QHostAddress::Any, m_port); if (success) - logger->addMessage(tr("The Web UI is listening on port %1").arg(port)); + logger->addMessage(tr("The Web UI is listening on port %1").arg(m_port)); else - logger->addMessage(tr("Web User Interface Error - Unable to bind Web UI to port %1").arg(port), Log::CRITICAL); + logger->addMessage(tr("Web User Interface Error - Unable to bind Web UI to port %1").arg(m_port), Log::CRITICAL); } // DynDNS @@ -90,6 +97,12 @@ void WebUI::init() if (dynDNSUpdater_) delete dynDNSUpdater_; } + + // Use UPnP/NAT-PMP for Web UI + if (pref->useUPnPForWebUIPort()) + Net::PortForwarder::instance()->addPort(m_port); + else + Net::PortForwarder::instance()->deletePort(m_port); } else { if (httpServer_) @@ -98,5 +111,6 @@ void WebUI::init() delete webapp_; if (dynDNSUpdater_) delete dynDNSUpdater_; + Net::PortForwarder::instance()->deletePort(m_port); } } diff --git a/src/webui/webui.h b/src/webui/webui.h index 9e7801af0..891e79084 100644 --- a/src/webui/webui.h +++ b/src/webui/webui.h @@ -58,6 +58,7 @@ private: QPointer httpServer_; QPointer dynDNSUpdater_; QPointer webapp_; + qint16 m_port; }; #endif // WEBUI_H diff --git a/src/webui/webui.pri b/src/webui/webui.pri index 8aa1a2887..b6e207e45 100644 --- a/src/webui/webui.pri +++ b/src/webui/webui.pri @@ -5,7 +5,6 @@ HEADERS += \ $$PWD/jsonutils.h \ $$PWD/extra_translations.h \ $$PWD/webapplication.h \ - $$PWD/qtorrentfilter.h \ $$PWD/websessiondata.h \ $$PWD/abstractwebapplication.h @@ -14,7 +13,6 @@ SOURCES += \ $$PWD/btjson.cpp \ $$PWD/prefjson.cpp \ $$PWD/webapplication.cpp \ - $$PWD/qtorrentfilter.cpp \ $$PWD/abstractwebapplication.cpp # QJson JSON parser/serializer for using with Qt4