From d87a9bf7ec7a377fdef22a4e68b6fbfa1c1f7c42 Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Tue, 31 Mar 2015 14:52:39 +0300 Subject: [PATCH 01/12] Move GUI-related sources into gui subdir. --- src/core/core.pri | 2 -- src/core/qtlibtorrent/qtlibtorrent.pri | 8 -------- src/gui/gui.pri | 9 +++++++-- src/{core => gui}/qtnotify/notifications.cpp | 0 src/{core => gui}/qtnotify/notifications.h | 0 src/{core => gui}/qtnotify/notifications.xml | 0 src/{core => gui}/qtnotify/qtnotify.pri | 0 src/{core/qtlibtorrent => gui}/shutdownconfirm.cpp | 0 src/{core/qtlibtorrent => gui}/shutdownconfirm.h | 0 src/{core/qtlibtorrent => gui}/torrentmodel.cpp | 0 src/{core/qtlibtorrent => gui}/torrentmodel.h | 0 11 files changed, 7 insertions(+), 12 deletions(-) rename src/{core => gui}/qtnotify/notifications.cpp (100%) rename src/{core => gui}/qtnotify/notifications.h (100%) rename src/{core => gui}/qtnotify/notifications.xml (100%) rename src/{core => gui}/qtnotify/qtnotify.pri (100%) rename src/{core/qtlibtorrent => gui}/shutdownconfirm.cpp (100%) rename src/{core/qtlibtorrent => gui}/shutdownconfirm.h (100%) rename src/{core/qtlibtorrent => gui}/torrentmodel.cpp (100%) rename src/{core/qtlibtorrent => gui}/torrentmodel.h (100%) diff --git a/src/core/core.pri b/src/core/core.pri index 19707a885..8e02c50c2 100644 --- a/src/core/core.pri +++ b/src/core/core.pri @@ -1,7 +1,5 @@ INCLUDEPATH += $$PWD -unix:!macx:dbus: include(qtnotify/qtnotify.pri) - include(qtlibtorrent/qtlibtorrent.pri) HEADERS += \ diff --git a/src/core/qtlibtorrent/qtlibtorrent.pri b/src/core/qtlibtorrent/qtlibtorrent.pri index 25acd8f73..83cffd84b 100644 --- a/src/core/qtlibtorrent/qtlibtorrent.pri +++ b/src/core/qtlibtorrent/qtlibtorrent.pri @@ -15,11 +15,3 @@ SOURCES += $$PWD/qbtsession.cpp \ $$PWD/alertdispatcher.cpp \ $$PWD/torrentstatistics.cpp \ $$PWD/filterparserthread.cpp - -!contains(DEFINES, DISABLE_GUI) { - HEADERS += $$PWD/torrentmodel.h \ - $$PWD/shutdownconfirm.h - - SOURCES += $$PWD/torrentmodel.cpp \ - $$PWD/shutdownconfirm.cpp -} diff --git a/src/gui/gui.pri b/src/gui/gui.pri index cc9afe1a6..3ba11cd54 100644 --- a/src/gui/gui.pri +++ b/src/gui/gui.pri @@ -6,6 +6,7 @@ include(rss/rss.pri) include(torrentcreator/torrentcreator.pri) include(geoip/geoip.pri) include(powermanagement/powermanagement.pri) +unix:!macx:dbus: include(qtnotify/qtnotify.pri) HEADERS += \ $$PWD/mainwindow.h \ @@ -41,7 +42,9 @@ HEADERS += \ $$PWD/messageboxraised.h \ $$PWD/torrentfilterenum.h \ $$PWD/options_imp.h \ - $$PWD/advancedsettings.h + $$PWD/advancedsettings.h \ + $$PWD/shutdownconfirm.h \ + $$PWD/torrentmodel.h SOURCES += \ $$PWD/mainwindow.cpp \ @@ -69,7 +72,9 @@ SOURCES += \ $$PWD/messageboxraised.cpp \ $$PWD/statusbar.cpp \ $$PWD/trackerlogin.cpp \ - $$PWD/options_imp.cpp + $$PWD/options_imp.cpp \ + $$PWD/shutdownconfirm.cpp \ + $$PWD/torrentmodel.cpp win32|macx { HEADERS += $$PWD/programupdater.h diff --git a/src/core/qtnotify/notifications.cpp b/src/gui/qtnotify/notifications.cpp similarity index 100% rename from src/core/qtnotify/notifications.cpp rename to src/gui/qtnotify/notifications.cpp diff --git a/src/core/qtnotify/notifications.h b/src/gui/qtnotify/notifications.h similarity index 100% rename from src/core/qtnotify/notifications.h rename to src/gui/qtnotify/notifications.h diff --git a/src/core/qtnotify/notifications.xml b/src/gui/qtnotify/notifications.xml similarity index 100% rename from src/core/qtnotify/notifications.xml rename to src/gui/qtnotify/notifications.xml diff --git a/src/core/qtnotify/qtnotify.pri b/src/gui/qtnotify/qtnotify.pri similarity index 100% rename from src/core/qtnotify/qtnotify.pri rename to src/gui/qtnotify/qtnotify.pri diff --git a/src/core/qtlibtorrent/shutdownconfirm.cpp b/src/gui/shutdownconfirm.cpp similarity index 100% rename from src/core/qtlibtorrent/shutdownconfirm.cpp rename to src/gui/shutdownconfirm.cpp diff --git a/src/core/qtlibtorrent/shutdownconfirm.h b/src/gui/shutdownconfirm.h similarity index 100% rename from src/core/qtlibtorrent/shutdownconfirm.h rename to src/gui/shutdownconfirm.h diff --git a/src/core/qtlibtorrent/torrentmodel.cpp b/src/gui/torrentmodel.cpp similarity index 100% rename from src/core/qtlibtorrent/torrentmodel.cpp rename to src/gui/torrentmodel.cpp diff --git a/src/core/qtlibtorrent/torrentmodel.h b/src/gui/torrentmodel.h similarity index 100% rename from src/core/qtlibtorrent/torrentmodel.h rename to src/gui/torrentmodel.h From 98dfb6302dc2fb2dd9e29b511d97e2b85f63aadb Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Tue, 31 Mar 2015 14:59:26 +0300 Subject: [PATCH 02/12] Fix shutdownconfirm.* coding style (Issue #2192). --- src/gui/shutdownconfirm.cpp | 62 ++++++++++++++++++++++++------------- src/gui/shutdownconfirm.h | 16 ++++++---- 2 files changed, 50 insertions(+), 28 deletions(-) diff --git a/src/gui/shutdownconfirm.cpp b/src/gui/shutdownconfirm.cpp index c10d2ed24..07dcfd549 100644 --- a/src/gui/shutdownconfirm.cpp +++ b/src/gui/shutdownconfirm.cpp @@ -1,5 +1,5 @@ /* - * Bittorrent Client using Qt4 and libtorrent. + * Bittorrent Client using Qt and libtorrent. * Copyright (C) 2011 Christophe Dumez * Copyright (C) 2014 sledgehammer999 * @@ -34,15 +34,19 @@ #include -ShutdownConfirmDlg::ShutdownConfirmDlg(const shutDownAction &action): exit_now(NULL), timeout(15), action0(action) { +ShutdownConfirmDlg::ShutdownConfirmDlg(const shutDownAction &action) + : m_exitNow(0) + , m_timeout(15) + , m_action(action) +{ // Title and button - if (action0 == NO_SHUTDOWN) { + if (m_action == NO_SHUTDOWN) { setWindowTitle(tr("Exit confirmation")); - exit_now = addButton(tr("Exit now"), QMessageBox::AcceptRole); + m_exitNow = addButton(tr("Exit now"), QMessageBox::AcceptRole); } else { setWindowTitle(tr("Shutdown confirmation")); - exit_now = addButton(tr("Shutdown now"), QMessageBox::AcceptRole); + m_exitNow = addButton(tr("Shutdown now"), QMessageBox::AcceptRole); } // Cancel Button addButton(QMessageBox::Cancel); @@ -54,61 +58,75 @@ ShutdownConfirmDlg::ShutdownConfirmDlg(const shutDownAction &action): exit_now(N setWindowFlags(windowFlags()|Qt::WindowStaysOnTopHint); // Set 'Cancel' as default button. setDefaultButton(QMessageBox::Cancel); - timer.setInterval(1000); // 1sec - connect(&timer, SIGNAL(timeout()), this, SLOT(updateSeconds())); + m_timer.setInterval(1000); // 1sec + connect(&m_timer, SIGNAL(m_timeout()), this, SLOT(updateSeconds())); show(); // Move to center move(misc::screenCenter(this)); } -void ShutdownConfirmDlg::showEvent(QShowEvent *event) { +void ShutdownConfirmDlg::showEvent(QShowEvent *event) +{ QMessageBox::showEvent(event); - timer.start(); + m_timer.start(); } -bool ShutdownConfirmDlg::askForConfirmation(const shutDownAction &action) { +bool ShutdownConfirmDlg::askForConfirmation(const shutDownAction &action) +{ ShutdownConfirmDlg dlg(action); dlg.exec(); return dlg.shutdown(); } -void ShutdownConfirmDlg::updateSeconds() { - --timeout; +void ShutdownConfirmDlg::updateSeconds() +{ + --m_timeout; updateText(); - if (timeout == 0) { - timer.stop(); + if (m_timeout == 0) { + m_timer.stop(); accept(); } } -bool ShutdownConfirmDlg::shutdown() const { +bool ShutdownConfirmDlg::shutdown() const +{ // This is necessary because result() in the case of QMessageBox // returns a type of StandardButton, but since we use a custom button // it will return 0 instead, even though we set the 'accept' role on it. if (result() != QDialog::Accepted) - return (clickedButton() == exit_now); + return (clickedButton() == m_exitNow); else return true; } -void ShutdownConfirmDlg::updateText() { +void ShutdownConfirmDlg::updateText() +{ QString text; - switch (action0) { + switch (m_action) { case NO_SHUTDOWN: - text = tr("qBittorrent will now exit unless you cancel within the next %1 seconds.").arg(QString::number(timeout)); + text = tr("qBittorrent will now exit unless you cancel within the next %1 seconds.").arg(QString::number(m_timeout)); break; case SHUTDOWN_COMPUTER: - text = tr("The computer will now be switched off unless you cancel within the next %1 seconds.").arg(QString::number(timeout)); + text = tr("The computer will now be switched off unless you cancel within the next %1 seconds.").arg(QString::number(m_timeout)); break; case SUSPEND_COMPUTER: - text = tr("The computer will now go to sleep mode unless you cancel within the next %1 seconds.").arg(QString::number(timeout)); + text = tr("The computer will now go to sleep mode unless you cancel within the next %1 seconds.").arg(QString::number(m_timeout)); break; case HIBERNATE_COMPUTER: - text = tr("The computer will now go to hibernation mode unless you cancel within the next %1 seconds.").arg(QString::number(timeout)); + text = tr("The computer will now go to hibernation mode unless you cancel within the next %1 seconds.").arg(QString::number(m_timeout)); break; } setText(text); } +QAbstractButton *ShutdownConfirmDlg::getExit_now() const +{ + return m_exitNow; +} + +void ShutdownConfirmDlg::setExit_now(QAbstractButton *value) +{ + m_exitNow = value; +} diff --git a/src/gui/shutdownconfirm.h b/src/gui/shutdownconfirm.h index 983e37092..774d9bc5e 100644 --- a/src/gui/shutdownconfirm.h +++ b/src/gui/shutdownconfirm.h @@ -1,5 +1,5 @@ /* - * Bittorrent Client using Qt4 and libtorrent. + * Bittorrent Client using Qt and libtorrent. * Copyright (C) 2011 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -35,7 +35,8 @@ #include #include "misc.h" -class ShutdownConfirmDlg : public QMessageBox { +class ShutdownConfirmDlg : public QMessageBox +{ Q_OBJECT public: @@ -44,6 +45,9 @@ public: static bool askForConfirmation(const shutDownAction &action); + QAbstractButton *getExit_now() const; + void setExit_now(QAbstractButton *value); + protected: void showEvent(QShowEvent *event); @@ -55,10 +59,10 @@ private: void updateText(); // Vars - QAbstractButton *exit_now; - QTimer timer; - int timeout; - shutDownAction action0; + QAbstractButton *m_exitNow; + QTimer m_timer; + int m_timeout; + shutDownAction m_action; }; #endif // SHUTDOWNCONFIRM_H From f1bce0b8e0c7889e00e8fb573f070cd4cf8bea04 Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Sat, 28 Mar 2015 18:31:44 +0300 Subject: [PATCH 03/12] Fix downloadthread.* coding style (Issue #2192). --- src/core/downloadthread.cpp | 507 +++++++++++++++++++----------------- src/core/downloadthread.h | 35 ++- 2 files changed, 285 insertions(+), 257 deletions(-) diff --git a/src/core/downloadthread.cpp b/src/core/downloadthread.cpp index e4dfd314b..5bd6414e7 100644 --- a/src/core/downloadthread.cpp +++ b/src/core/downloadthread.cpp @@ -43,270 +43,299 @@ /** Download Thread **/ -DownloadThread::DownloadThread(QObject* parent) : QObject(parent) { - connect(&m_networkManager, SIGNAL(finished (QNetworkReply*)), this, SLOT(processDlFinished(QNetworkReply*))); +DownloadThread::DownloadThread(QObject* parent) + : QObject(parent) +{ + connect(&m_networkManager, SIGNAL(finished (QNetworkReply*)), this, SLOT(processDlFinished(QNetworkReply*))); #ifndef QT_NO_OPENSSL - connect(&m_networkManager, SIGNAL(sslErrors(QNetworkReply*,QList)), this, SLOT(ignoreSslErrors(QNetworkReply*,QList))); + connect(&m_networkManager, SIGNAL(sslErrors(QNetworkReply*, QList)), this, SLOT(ignoreSslErrors(QNetworkReply*, QList))); #endif } -QByteArray DownloadThread::gUncompress(Bytef *inData, size_t len) { - if (len <= 4) { - qWarning("gUncompress: Input data is truncated"); - return QByteArray(); - } - - QByteArray result; - - z_stream strm; - static const int CHUNK_SIZE = 1024; - char out[CHUNK_SIZE]; - - /* allocate inflate state */ - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - strm.avail_in = len; - strm.next_in = inData; - - const int windowBits = 15; - const int ENABLE_ZLIB_GZIP = 32; - - int ret = inflateInit2(&strm, windowBits|ENABLE_ZLIB_GZIP ); // gzip decoding - if (ret != Z_OK) - return QByteArray(); - - // run inflate() - do { - strm.avail_out = CHUNK_SIZE; - strm.next_out = reinterpret_cast(out); - - ret = inflate(&strm, Z_NO_FLUSH); - Q_ASSERT(ret != Z_STREAM_ERROR); // state not clobbered - - switch (ret) { - case Z_NEED_DICT: - case Z_DATA_ERROR: - case Z_MEM_ERROR: - (void) inflateEnd(&strm); +QByteArray DownloadThread::gUncompress(Bytef *inData, size_t len) +{ + if (len <= 4) { + qWarning("gUncompress: Input data is truncated"); return QByteArray(); } - result.append(out, CHUNK_SIZE - strm.avail_out); - } while (!strm.avail_out); + QByteArray result; - // clean up and return - inflateEnd(&strm); - return result; + z_stream strm; + static const int CHUNK_SIZE = 1024; + char out[CHUNK_SIZE]; + + /* allocate inflate state */ + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = len; + strm.next_in = inData; + + const int windowBits = 15; + const int ENABLE_ZLIB_GZIP = 32; + + int ret = inflateInit2(&strm, windowBits | ENABLE_ZLIB_GZIP); // gzip decoding + if (ret != Z_OK) + return QByteArray(); + + // run inflate() + do { + strm.avail_out = CHUNK_SIZE; + strm.next_out = reinterpret_cast(out); + + ret = inflate(&strm, Z_NO_FLUSH); + Q_ASSERT(ret != Z_STREAM_ERROR); // state not clobbered + + switch (ret) { + case Z_NEED_DICT: + case Z_DATA_ERROR: + case Z_MEM_ERROR: + (void) inflateEnd(&strm); + return QByteArray(); + } + + result.append(out, CHUNK_SIZE - strm.avail_out); + } + while (!strm.avail_out); + + // clean up and return + inflateEnd(&strm); + return result; } -void DownloadThread::processDlFinished(QNetworkReply* reply) { - QString url = reply->url().toString(); - qDebug("Download finished: %s", qPrintable(url)); - // Check if the request was successful - if (reply->error() != QNetworkReply::NoError) { - // Failure - qDebug("Download failure (%s), reason: %s", qPrintable(url), qPrintable(errorCodeToString(reply->error()))); - emit downloadFailure(url, errorCodeToString(reply->error())); - reply->deleteLater(); - return; - } - // Check if the server ask us to redirect somewhere lese - const QVariant redirection = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); - if (redirection.isValid()) { - // We should redirect - QUrl newUrl = redirection.toUrl(); - // Resolve relative urls - if (newUrl.isRelative()) - newUrl = reply->url().resolved(newUrl); - const QString newUrlString = newUrl.toString(); - qDebug("Redirecting from %s to %s", qPrintable(url), qPrintable(newUrlString)); - // Redirect to magnet workaround - if (newUrlString.startsWith("magnet:", Qt::CaseInsensitive)) { - qDebug("Magnet redirect detected."); - reply->abort(); - emit magnetRedirect(newUrlString, url); - reply->deleteLater(); - return; - } - m_redirectMapping.insert(newUrlString, url); - // redirecting with first cookies - downloadUrl(newUrlString, m_networkManager.cookieJar()->cookiesForUrl(url)); - reply->deleteLater(); - return; - } - // Checking if it was redirected, restoring initial URL - if (m_redirectMapping.contains(url)) { - url = m_redirectMapping.take(url); - } - // Success - QTemporaryFile *tmpfile = new QTemporaryFile; - if (tmpfile->open()) { - tmpfile->setAutoRemove(false); - QString filePath = tmpfile->fileName(); - qDebug("Temporary filename is: %s", qPrintable(filePath)); - if (reply->isOpen() || reply->open(QIODevice::ReadOnly)) { - QByteArray replyData = reply->readAll(); - if (reply->rawHeader("Content-Encoding") == "gzip") { - // uncompress gzip reply - replyData = gUncompress(reinterpret_cast(replyData.data()), replyData.length()); - } - tmpfile->write(replyData); - tmpfile->close(); - // XXX: tmpfile needs to be deleted on Windows before using the file - // or it will complain that the file is used by another process. - delete tmpfile; - // Send finished signal - emit downloadFinished(url, filePath); - } else { - delete tmpfile; - fsutils::forceRemove(filePath); - // Error when reading the request - emit downloadFailure(url, tr("I/O Error")); - } - } else { - delete tmpfile; - emit downloadFailure(url, tr("I/O Error")); - } - // Clean up - reply->deleteLater(); -} - -void DownloadThread::downloadTorrentUrl(const QString &url, const QList& cookies) +void DownloadThread::processDlFinished(QNetworkReply *reply) { - // Process request - QNetworkReply *reply = downloadUrl(url, cookies); - connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(checkDownloadSize(qint64,qint64))); + QString url = reply->url().toString(); + qDebug("Download finished: %s", qPrintable(url)); + // Check if the request was successful + if (reply->error() != QNetworkReply::NoError) { + // Failure + qDebug("Download failure (%s), reason: %s", qPrintable(url), qPrintable(errorCodeToString(reply->error()))); + emit downloadFailure(url, errorCodeToString(reply->error())); + reply->deleteLater(); + return; + } + + // Check if the server ask us to redirect somewhere else + const QVariant redirection = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirection.isValid()) { + // We should redirect + QUrl newUrl = redirection.toUrl(); + // Resolve relative urls + if (newUrl.isRelative()) + newUrl = reply->url().resolved(newUrl); + const QString newUrlString = newUrl.toString(); + qDebug("Redirecting from %s to %s", qPrintable(url), qPrintable(newUrlString)); + + // Redirect to magnet workaround + if (newUrlString.startsWith("magnet:", Qt::CaseInsensitive)) { + qDebug("Magnet redirect detected."); + reply->abort(); + emit magnetRedirect(newUrlString, url); + reply->deleteLater(); + return; + } + + m_redirectMapping.insert(newUrlString, url); + // redirecting with first cookies + downloadUrl(newUrlString, m_networkManager.cookieJar()->cookiesForUrl(url)); + reply->deleteLater(); + return; + } + + // Checking if it was redirected, restoring initial URL + if (m_redirectMapping.contains(url)) + url = m_redirectMapping.take(url); + + // Success + QTemporaryFile *tmpfile = new QTemporaryFile; + if (tmpfile->open()) { + tmpfile->setAutoRemove(false); + QString filePath = tmpfile->fileName(); + qDebug("Temporary filename is: %s", qPrintable(filePath)); + if (reply->isOpen() || reply->open(QIODevice::ReadOnly)) { + QByteArray replyData = reply->readAll(); + if (reply->rawHeader("Content-Encoding") == "gzip") { + // uncompress gzip reply + replyData = gUncompress(reinterpret_cast(replyData.data()), replyData.length()); + } + tmpfile->write(replyData); + tmpfile->close(); + // XXX: tmpfile needs to be deleted on Windows before using the file + // or it will complain that the file is used by another process. + delete tmpfile; + // Send finished signal + emit downloadFinished(url, filePath); + } + else { + delete tmpfile; + fsutils::forceRemove(filePath); + // Error when reading the request + emit downloadFailure(url, tr("I/O Error")); + } + } + else { + delete tmpfile; + emit downloadFailure(url, tr("I/O Error")); + } + + // Clean up + reply->deleteLater(); } -QNetworkReply* DownloadThread::downloadUrl(const QString &url, const QList& cookies) { - // Update proxy settings - applyProxySettings(); - // Set cookies - if (!cookies.empty()) { - qDebug("Setting %d cookies for url: %s", cookies.size(), qPrintable(url)); - m_networkManager.cookieJar()->setCookiesFromUrl(cookies, url); - } - // Process download request - qDebug("url is %s", qPrintable(url)); - const QUrl qurl = QUrl::fromEncoded(url.toUtf8()); - QNetworkRequest request(qurl); - // Spoof Firefox 3.5 user agent to avoid - // Web server banning - request.setRawHeader("User-Agent", "Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5"); - qDebug("Downloading %s...", request.url().toEncoded().data()); - qDebug("%d cookies for this URL", m_networkManager.cookieJar()->cookiesForUrl(url).size()); - for (int i=0; icookiesForUrl(url).size(); ++i) { - qDebug("%s=%s", m_networkManager.cookieJar()->cookiesForUrl(url).at(i).name().data(), m_networkManager.cookieJar()->cookiesForUrl(url).at(i).value().data()); - qDebug("Domain: %s, Path: %s", qPrintable(m_networkManager.cookieJar()->cookiesForUrl(url).at(i).domain()), qPrintable(m_networkManager.cookieJar()->cookiesForUrl(url).at(i).path())); - } - // accept gzip - request.setRawHeader("Accept-Encoding", "gzip"); - return m_networkManager.get(request); +void DownloadThread::downloadTorrentUrl(const QString &url, const QList &cookies) +{ + // Process request + QNetworkReply *reply = downloadUrl(url, cookies); + connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(checkDownloadSize(qint64, qint64))); } -void DownloadThread::checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal) { - QNetworkReply *reply = qobject_cast(sender()); - if (!reply) return; - if (bytesTotal > 0) { - // Total number of bytes is available - if (bytesTotal > 1048576*10) { - // More than 10MB, this is probably not a torrent file, aborting... - reply->abort(); - reply->deleteLater(); - } else { - disconnect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(checkDownloadSize(qint64,qint64))); +QNetworkReply *DownloadThread::downloadUrl(const QString &url, const QList &cookies) +{ + // Update proxy settings + applyProxySettings(); + + // Set cookies + if (!cookies.empty()) { + qDebug("Setting %d cookies for url: %s", cookies.size(), qPrintable(url)); + m_networkManager.cookieJar()->setCookiesFromUrl(cookies, url); } - } else { - if (bytesReceived > 1048576*10) { - // More than 10MB, this is probably not a torrent file, aborting... - reply->abort(); - reply->deleteLater(); + + // Process download request + qDebug("url is %s", qPrintable(url)); + const QUrl qurl = QUrl::fromEncoded(url.toUtf8()); + QNetworkRequest request(qurl); + + // Spoof Firefox 3.5 user agent to avoid + // Web server banning + request.setRawHeader("User-Agent", "Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5"); + + qDebug("Downloading %s...", request.url().toEncoded().data()); + qDebug("%d cookies for this URL", m_networkManager.cookieJar()->cookiesForUrl(url).size()); + for (int i = 0; i < m_networkManager.cookieJar()->cookiesForUrl(url).size(); ++i) { + qDebug("%s=%s", m_networkManager.cookieJar()->cookiesForUrl(url).at(i).name().data(), m_networkManager.cookieJar()->cookiesForUrl(url).at(i).value().data()); + qDebug("Domain: %s, Path: %s", qPrintable(m_networkManager.cookieJar()->cookiesForUrl(url).at(i).domain()), qPrintable(m_networkManager.cookieJar()->cookiesForUrl(url).at(i).path())); } - } + + // accept gzip + request.setRawHeader("Accept-Encoding", "gzip"); + return m_networkManager.get(request); } -void DownloadThread::applyProxySettings() { - QNetworkProxy proxy; - const Preferences* const pref = Preferences::instance(); - if (pref->isProxyEnabled()) { - // Proxy enabled - proxy.setHostName(pref->getProxyIp()); - proxy.setPort(pref->getProxyPort()); - // Default proxy type is HTTP, we must change if it is SOCKS5 - const int proxy_type = pref->getProxyType(); - if (proxy_type == Proxy::SOCKS5 || proxy_type == Proxy::SOCKS5_PW) { - qDebug() << Q_FUNC_INFO << "using SOCKS proxy"; - proxy.setType(QNetworkProxy::Socks5Proxy); - } else { - qDebug() << Q_FUNC_INFO << "using HTTP proxy"; - proxy.setType(QNetworkProxy::HttpProxy); +void DownloadThread::checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal) +{ + QNetworkReply *reply = qobject_cast(sender()); + if (!reply) return; + + if (bytesTotal > 0) { + // Total number of bytes is available + if (bytesTotal > 10485760) { + // More than 10MB, this is probably not a torrent file, aborting... + reply->abort(); + reply->deleteLater(); + } + else { + disconnect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(checkDownloadSize(qint64, qint64))); + } } - // Authentication? - if (pref->isProxyAuthEnabled()) { - qDebug("Proxy requires authentication, authenticating"); - proxy.setUser(pref->getProxyUsername()); - proxy.setPassword(pref->getProxyPassword()); + else { + if (bytesReceived > 10485760) { + // More than 10MB, this is probably not a torrent file, aborting... + reply->abort(); + reply->deleteLater(); + } } - } else { - proxy.setType(QNetworkProxy::NoProxy); - } - m_networkManager.setProxy(proxy); } -QString DownloadThread::errorCodeToString(QNetworkReply::NetworkError status) { - switch(status) { - case QNetworkReply::HostNotFoundError: - return tr("The remote host name was not found (invalid hostname)"); - case QNetworkReply::OperationCanceledError: - return tr("The operation was canceled"); - case QNetworkReply::RemoteHostClosedError: - return tr("The remote server closed the connection prematurely, before the entire reply was received and processed"); - case QNetworkReply::TimeoutError: - return tr("The connection to the remote server timed out"); - case QNetworkReply::SslHandshakeFailedError: - return tr("SSL/TLS handshake failed"); - case QNetworkReply::ConnectionRefusedError: - return tr("The remote server refused the connection"); - case QNetworkReply::ProxyConnectionRefusedError: - return tr("The connection to the proxy server was refused"); - case QNetworkReply::ProxyConnectionClosedError: - return tr("The proxy server closed the connection prematurely"); - case QNetworkReply::ProxyNotFoundError: - return tr("The proxy host name was not found"); - case QNetworkReply::ProxyTimeoutError: - return tr("The connection to the proxy timed out or the proxy did not reply in time to the request sent"); - case QNetworkReply::ProxyAuthenticationRequiredError: - return tr("The proxy requires authentication in order to honour the request but did not accept any credentials offered"); - case QNetworkReply::ContentAccessDenied: - return tr("The access to the remote content was denied (401)"); - case QNetworkReply::ContentOperationNotPermittedError: - return tr("The operation requested on the remote content is not permitted"); - case QNetworkReply::ContentNotFoundError: - return tr("The remote content was not found at the server (404)"); - case QNetworkReply::AuthenticationRequiredError: - return tr("The remote server requires authentication to serve the content but the credentials provided were not accepted"); - case QNetworkReply::ProtocolUnknownError: - return tr("The Network Access API cannot honor the request because the protocol is not known"); - case QNetworkReply::ProtocolInvalidOperationError: - return tr("The requested operation is invalid for this protocol"); - case QNetworkReply::UnknownNetworkError: - return tr("An unknown network-related error was detected"); - case QNetworkReply::UnknownProxyError: - return tr("An unknown proxy-related error was detected"); - case QNetworkReply::UnknownContentError: - return tr("An unknown error related to the remote content was detected"); - case QNetworkReply::ProtocolFailure: - return tr("A breakdown in protocol was detected"); - default: - return tr("Unknown error"); - } +void DownloadThread::applyProxySettings() +{ + QNetworkProxy proxy; + const Preferences* const pref = Preferences::instance(); + + if (pref->isProxyEnabled()) { + // Proxy enabled + proxy.setHostName(pref->getProxyIp()); + proxy.setPort(pref->getProxyPort()); + // Default proxy type is HTTP, we must change if it is SOCKS5 + const int proxyType = pref->getProxyType(); + if ((proxyType == Proxy::SOCKS5) || (proxyType == Proxy::SOCKS5_PW)) { + qDebug() << Q_FUNC_INFO << "using SOCKS proxy"; + proxy.setType(QNetworkProxy::Socks5Proxy); + } + else { + qDebug() << Q_FUNC_INFO << "using HTTP proxy"; + proxy.setType(QNetworkProxy::HttpProxy); + } + // Authentication? + if (pref->isProxyAuthEnabled()) { + qDebug("Proxy requires authentication, authenticating"); + proxy.setUser(pref->getProxyUsername()); + proxy.setPassword(pref->getProxyPassword()); + } + } + else { + proxy.setType(QNetworkProxy::NoProxy); + } + + m_networkManager.setProxy(proxy); +} + +QString DownloadThread::errorCodeToString(QNetworkReply::NetworkError status) +{ + switch(status) { + case QNetworkReply::HostNotFoundError: + return tr("The remote host name was not found (invalid hostname)"); + case QNetworkReply::OperationCanceledError: + return tr("The operation was canceled"); + case QNetworkReply::RemoteHostClosedError: + return tr("The remote server closed the connection prematurely, before the entire reply was received and processed"); + case QNetworkReply::TimeoutError: + return tr("The connection to the remote server timed out"); + case QNetworkReply::SslHandshakeFailedError: + return tr("SSL/TLS handshake failed"); + case QNetworkReply::ConnectionRefusedError: + return tr("The remote server refused the connection"); + case QNetworkReply::ProxyConnectionRefusedError: + return tr("The connection to the proxy server was refused"); + case QNetworkReply::ProxyConnectionClosedError: + return tr("The proxy server closed the connection prematurely"); + case QNetworkReply::ProxyNotFoundError: + return tr("The proxy host name was not found"); + case QNetworkReply::ProxyTimeoutError: + return tr("The connection to the proxy timed out or the proxy did not reply in time to the request sent"); + case QNetworkReply::ProxyAuthenticationRequiredError: + return tr("The proxy requires authentication in order to honour the request but did not accept any credentials offered"); + case QNetworkReply::ContentAccessDenied: + return tr("The access to the remote content was denied (401)"); + case QNetworkReply::ContentOperationNotPermittedError: + return tr("The operation requested on the remote content is not permitted"); + case QNetworkReply::ContentNotFoundError: + return tr("The remote content was not found at the server (404)"); + case QNetworkReply::AuthenticationRequiredError: + return tr("The remote server requires authentication to serve the content but the credentials provided were not accepted"); + case QNetworkReply::ProtocolUnknownError: + return tr("The Network Access API cannot honor the request because the protocol is not known"); + case QNetworkReply::ProtocolInvalidOperationError: + return tr("The requested operation is invalid for this protocol"); + case QNetworkReply::UnknownNetworkError: + return tr("An unknown network-related error was detected"); + case QNetworkReply::UnknownProxyError: + return tr("An unknown proxy-related error was detected"); + case QNetworkReply::UnknownContentError: + return tr("An unknown error related to the remote content was detected"); + case QNetworkReply::ProtocolFailure: + return tr("A breakdown in protocol was detected"); + default: + return tr("Unknown error"); + } } #ifndef QT_NO_OPENSSL -void DownloadThread::ignoreSslErrors(QNetworkReply* reply, const QList &errors) { - Q_UNUSED(errors) - // Ignore all SSL errors - reply->ignoreSslErrors(); +void DownloadThread::ignoreSslErrors(QNetworkReply *reply, const QList &errors) +{ + Q_UNUSED(errors) + // Ignore all SSL errors + reply->ignoreSslErrors(); } #endif diff --git a/src/core/downloadthread.h b/src/core/downloadthread.h index c9c2a94bd..8eeb61849 100644 --- a/src/core/downloadthread.h +++ b/src/core/downloadthread.h @@ -42,35 +42,34 @@ QT_BEGIN_NAMESPACE class QNetworkAccessManager; QT_END_NAMESPACE -class DownloadThread : public QObject { - Q_OBJECT +class DownloadThread : public QObject +{ + Q_OBJECT public: - DownloadThread(QObject* parent = 0); - QNetworkReply* downloadUrl(const QString &url, const QList& cookies = QList()); - void downloadTorrentUrl(const QString &url, const QList& cookies = QList()); - //void setProxy(QString IP, int port, QString username, QString password); + DownloadThread(QObject *parent = 0); + QNetworkReply *downloadUrl(const QString &url, const QList &cookies = QList()); + void downloadTorrentUrl(const QString &url, const QList &cookies = QList()); signals: - void downloadFinished(const QString &url, const QString &file_path); - void downloadFailure(const QString &url, const QString &reason); - void magnetRedirect(const QString &url_new, const QString &url_old); + void downloadFinished(const QString &url, const QString &file_path); + void downloadFailure(const QString &url, const QString &reason); + void magnetRedirect(const QString &url_new, const QString &url_old); private slots: - void processDlFinished(QNetworkReply* reply); - void checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal); + void processDlFinished(QNetworkReply *reply); + void checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal); #ifndef QT_NO_OPENSSL - void ignoreSslErrors(QNetworkReply*,const QList&); + void ignoreSslErrors(QNetworkReply *,const QList &); #endif private: - static QByteArray gUncompress(Bytef *inData, size_t len); - QString errorCodeToString(QNetworkReply::NetworkError status); - void applyProxySettings(); + static QByteArray gUncompress(Bytef *inData, size_t len); + QString errorCodeToString(QNetworkReply::NetworkError status); + void applyProxySettings(); -private: - QNetworkAccessManager m_networkManager; - QHash m_redirectMapping; + QNetworkAccessManager m_networkManager; + QHash m_redirectMapping; }; From 3eeed813d64081e333ee941f329fae9ca8d379a6 Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Sat, 28 Mar 2015 20:22:22 +0300 Subject: [PATCH 04/12] Implement new download manager. --- src/core/net/downloadhandler.cpp | 289 +++++++++++++++++++++++++++++++ src/core/net/downloadhandler.h | 75 ++++++++ src/core/net/downloadmanager.cpp | 149 ++++++++++++++++ src/core/net/downloadmanager.h | 76 ++++++++ 4 files changed, 589 insertions(+) create mode 100644 src/core/net/downloadhandler.cpp create mode 100644 src/core/net/downloadhandler.h create mode 100644 src/core/net/downloadmanager.cpp create mode 100644 src/core/net/downloadmanager.h diff --git a/src/core/net/downloadhandler.cpp b/src/core/net/downloadhandler.cpp new file mode 100644 index 000000000..fe86a9de1 --- /dev/null +++ b/src/core/net/downloadhandler.cpp @@ -0,0 +1,289 @@ +/* + * 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 "fs_utils.h" +#include "misc.h" +#include "downloadmanager.h" +#include "downloadhandler.h" + +static QString errorCodeToString(QNetworkReply::NetworkError status); +static QByteArray gUncompress(Bytef *inData, uInt len); + +using namespace Net; + +DownloadHandler::DownloadHandler(QNetworkReply *reply, DownloadManager *manager, qint64 limit) + : QObject(manager) + , m_reply(reply) + , m_manager(manager) + , m_sizeLimit(limit) + , m_url(reply->url().toString()) +{ + init(); +} + +DownloadHandler::~DownloadHandler() +{ + if (m_reply) + delete m_reply; +} + +// Returns original url +QString DownloadHandler::url() const +{ + return m_url; +} + +void DownloadHandler::processFinishedDownload() +{ + QString url = m_reply->url().toString(); + qDebug("Download finished: %s", qPrintable(url)); + // Check if the request was successful + if (m_reply->error() != QNetworkReply::NoError) { + // Failure + qDebug("Download failure (%s), reason: %s", qPrintable(url), qPrintable(errorCodeToString(m_reply->error()))); + emit downloadFailed(m_url, errorCodeToString(m_reply->error())); + this->deleteLater(); + } + else { + // Check if the server ask us to redirect somewhere else + const QVariant redirection = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirection.isValid()) { + // We should redirect + handleRedirection(redirection.toUrl()); + } + else { + // Success + QString filePath; + if (saveToFile(filePath)) + emit downloadFinished(m_url, filePath); + else + emit downloadFailed(m_url, tr("I/O Error")); + this->deleteLater(); + } + } +} + +void DownloadHandler::checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal) +{ + QString msg = tr("The file size is %1. It exceeds the download limit of %2."); + + if (bytesTotal > 0) { + // Total number of bytes is available + if (bytesTotal > m_sizeLimit) { + m_reply->abort(); + emit downloadFailed(m_url, msg.arg(misc::friendlyUnit(bytesTotal)).arg(misc::friendlyUnit(m_sizeLimit))); + } + else { + disconnect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(checkDownloadSize(qint64, qint64))); + } + } + else if (bytesReceived > m_sizeLimit) { + m_reply->abort(); + emit downloadFailed(m_url, msg.arg(misc::friendlyUnit(bytesReceived)).arg(misc::friendlyUnit(m_sizeLimit))); + } +} + +void DownloadHandler::init() +{ + m_reply->setParent(this); + if (m_sizeLimit > 0) + connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(checkDownloadSize(qint64, qint64))); + connect(m_reply, SIGNAL(finished()), this, SLOT(processFinishedDownload())); +} + +bool DownloadHandler::saveToFile(QString &filePath) +{ + QTemporaryFile *tmpfile = new QTemporaryFile; + if (!tmpfile->open()) { + delete tmpfile; + return false; + } + + tmpfile->setAutoRemove(false); + filePath = tmpfile->fileName(); + qDebug("Temporary filename is: %s", qPrintable(filePath)); + if (m_reply->isOpen() || m_reply->open(QIODevice::ReadOnly)) { + QByteArray replyData = m_reply->readAll(); + if (m_reply->rawHeader("Content-Encoding") == "gzip") { + // uncompress gzip reply + replyData = gUncompress(reinterpret_cast(replyData.data()), static_cast(replyData.length())); + } + tmpfile->write(replyData); + tmpfile->close(); + // XXX: tmpfile needs to be deleted on Windows before using the file + // or it will complain that the file is used by another process. + delete tmpfile; + return true; + } + else { + delete tmpfile; + fsutils::forceRemove(filePath); + } + + return false; +} + +void DownloadHandler::handleRedirection(QUrl newUrl) +{ + // Resolve relative urls + if (newUrl.isRelative()) + newUrl = m_reply->url().resolved(newUrl); + + const QString newUrlString = newUrl.toString(); + qDebug("Redirecting from %s to %s", qPrintable(m_reply->url().toString()), qPrintable(newUrlString)); + + // Redirect to magnet workaround + if (newUrlString.startsWith("magnet:", Qt::CaseInsensitive)) { + qDebug("Magnet redirect detected."); + m_reply->abort(); + emit redirectedToMagnet(m_url, newUrlString); + this->deleteLater(); + } + else { + DownloadHandler *tmp = m_manager->downloadUrl(newUrlString, m_sizeLimit); + m_reply->deleteLater(); + m_reply = tmp->m_reply; + m_sizeLimit = tmp->m_sizeLimit; + init(); + tmp->m_reply = 0; + delete tmp; + } +} + +QString errorCodeToString(QNetworkReply::NetworkError status) +{ + switch(status) { + case QNetworkReply::HostNotFoundError: + return QObject::tr("The remote host name was not found (invalid hostname)"); + case QNetworkReply::OperationCanceledError: + return QObject::tr("The operation was canceled"); + case QNetworkReply::RemoteHostClosedError: + return QObject::tr("The remote server closed the connection prematurely, before the entire reply was received and processed"); + case QNetworkReply::TimeoutError: + return QObject::tr("The connection to the remote server timed out"); + case QNetworkReply::SslHandshakeFailedError: + return QObject::tr("SSL/TLS handshake failed"); + case QNetworkReply::ConnectionRefusedError: + return QObject::tr("The remote server refused the connection"); + case QNetworkReply::ProxyConnectionRefusedError: + return QObject::tr("The connection to the proxy server was refused"); + case QNetworkReply::ProxyConnectionClosedError: + return QObject::tr("The proxy server closed the connection prematurely"); + case QNetworkReply::ProxyNotFoundError: + return QObject::tr("The proxy host name was not found"); + case QNetworkReply::ProxyTimeoutError: + return QObject::tr("The connection to the proxy timed out or the proxy did not reply in time to the request sent"); + case QNetworkReply::ProxyAuthenticationRequiredError: + return QObject::tr("The proxy requires authentication in order to honour the request but did not accept any credentials offered"); + case QNetworkReply::ContentAccessDenied: + return QObject::tr("The access to the remote content was denied (401)"); + case QNetworkReply::ContentOperationNotPermittedError: + return QObject::tr("The operation requested on the remote content is not permitted"); + case QNetworkReply::ContentNotFoundError: + return QObject::tr("The remote content was not found at the server (404)"); + case QNetworkReply::AuthenticationRequiredError: + return QObject::tr("The remote server requires authentication to serve the content but the credentials provided were not accepted"); + case QNetworkReply::ProtocolUnknownError: + return QObject::tr("The Network Access API cannot honor the request because the protocol is not known"); + case QNetworkReply::ProtocolInvalidOperationError: + return QObject::tr("The requested operation is invalid for this protocol"); + case QNetworkReply::UnknownNetworkError: + return QObject::tr("An unknown network-related error was detected"); + case QNetworkReply::UnknownProxyError: + return QObject::tr("An unknown proxy-related error was detected"); + case QNetworkReply::UnknownContentError: + return QObject::tr("An unknown error related to the remote content was detected"); + case QNetworkReply::ProtocolFailure: + return QObject::tr("A breakdown in protocol was detected"); + default: + return QObject::tr("Unknown error"); + } +} + +QByteArray gUncompress(Bytef *inData, uInt len) +{ + if (len <= 4) { + qWarning("gUncompress: Input data is truncated"); + return QByteArray(); + } + + QByteArray result; + + z_stream strm; + static const int CHUNK_SIZE = 1024; + char out[CHUNK_SIZE]; + + /* allocate inflate state */ + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = len; + strm.next_in = inData; + + const int windowBits = 15; + const int ENABLE_ZLIB_GZIP = 32; + + int ret = inflateInit2(&strm, windowBits | ENABLE_ZLIB_GZIP); // gzip decoding + if (ret != Z_OK) + return QByteArray(); + + // run inflate() + do { + strm.avail_out = CHUNK_SIZE; + strm.next_out = reinterpret_cast(out); + + ret = inflate(&strm, Z_NO_FLUSH); + Q_ASSERT(ret != Z_STREAM_ERROR); // state not clobbered + + switch (ret) { + case Z_NEED_DICT: + case Z_DATA_ERROR: + case Z_MEM_ERROR: + (void) inflateEnd(&strm); + return QByteArray(); + } + + result.append(out, CHUNK_SIZE - strm.avail_out); + } + while (!strm.avail_out); + + // clean up and return + inflateEnd(&strm); + return result; +} diff --git a/src/core/net/downloadhandler.h b/src/core/net/downloadhandler.h new file mode 100644 index 000000000..d427eabad --- /dev/null +++ b/src/core/net/downloadhandler.h @@ -0,0 +1,75 @@ +/* + * 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 NET_DOWNLOADHANDLER_H +#define NET_DOWNLOADHANDLER_H + +#include + +QT_BEGIN_NAMESPACE +class QNetworkAccessManager; +class QNetworkReply; +QT_END_NAMESPACE + +namespace Net +{ + class DownloadManager; + + class DownloadHandler : public QObject + { + Q_OBJECT + + public: + DownloadHandler(QNetworkReply *reply, DownloadManager *manager, qint64 limit = 0); + ~DownloadHandler(); + + QString url() const; + + signals: + void downloadFinished(const QString &url, const QString &filePath); + void downloadFailed(const QString &url, const QString &reason); + void redirectedToMagnet(const QString &url, const QString &magnetUri); + + private slots: + void processFinishedDownload(); + void checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal); + + private: + void init(); + bool saveToFile(QString &filePath); + void handleRedirection(QUrl newUrl); + + QNetworkReply *m_reply; + DownloadManager *m_manager; + qint64 m_sizeLimit; + QString m_url; + }; +} + +#endif // NET_DOWNLOADHANDLER_H diff --git a/src/core/net/downloadmanager.cpp b/src/core/net/downloadmanager.cpp new file mode 100644 index 000000000..1d04d56ee --- /dev/null +++ b/src/core/net/downloadmanager.cpp @@ -0,0 +1,149 @@ +/* + * 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 "core/preferences.h" +#include "downloadhandler.h" +#include "downloadmanager.h" + +using namespace Net; + +DownloadManager *DownloadManager::m_instance = 0; + +DownloadManager::DownloadManager(QObject *parent) + : QObject(parent) +{ +#ifndef QT_NO_OPENSSL + connect(&m_networkManager, SIGNAL(sslErrors(QNetworkReply *, QList)), this, SLOT(ignoreSslErrors(QNetworkReply *, QList))); +#endif +} + +DownloadManager::~DownloadManager() +{ +} + +void DownloadManager::initInstance() +{ + if (!m_instance) + m_instance = new DownloadManager; +} + +void DownloadManager::freeInstance() +{ + if (m_instance) { + delete m_instance; + m_instance = 0; + } +} + +DownloadManager *DownloadManager::instance() +{ + return m_instance; +} + +DownloadHandler *DownloadManager::downloadUrl(const QString &url, qint64 limit) +{ + // Update proxy settings + applyProxySettings(); + + // Process download request + qDebug("url is %s", qPrintable(url)); + const QUrl qurl = QUrl::fromEncoded(url.toUtf8()); + QNetworkRequest request(qurl); + + // Spoof Firefox 3.5 user agent to avoid + // Web server banning + request.setRawHeader("User-Agent", "Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5"); + + qDebug("Downloading %s...", request.url().toEncoded().data()); + // accept gzip + request.setRawHeader("Accept-Encoding", "gzip"); + return new DownloadHandler(m_networkManager.get(request), this, limit); +} + +QList DownloadManager::cookiesForUrl(const QString &url) const +{ + return m_networkManager.cookieJar()->cookiesForUrl(url); +} + +bool DownloadManager::setCookiesFromUrl(const QList &cookieList, const QUrl &url) +{ + qDebug("Setting %d cookies for url: %s", cookieList.size(), qPrintable(url.toString())); + return m_networkManager.cookieJar()->setCookiesFromUrl(cookieList, url); +} + +void DownloadManager::applyProxySettings() +{ + QNetworkProxy proxy; + const Preferences* const pref = Preferences::instance(); + + if (pref->isProxyEnabled()) { + // Proxy enabled + proxy.setHostName(pref->getProxyIp()); + proxy.setPort(pref->getProxyPort()); + // Default proxy type is HTTP, we must change if it is SOCKS5 + const int proxyType = pref->getProxyType(); + if ((proxyType == Proxy::SOCKS5) || (proxyType == Proxy::SOCKS5_PW)) { + qDebug() << Q_FUNC_INFO << "using SOCKS proxy"; + proxy.setType(QNetworkProxy::Socks5Proxy); + } + else { + qDebug() << Q_FUNC_INFO << "using HTTP proxy"; + proxy.setType(QNetworkProxy::HttpProxy); + } + // Authentication? + if (pref->isProxyAuthEnabled()) { + qDebug("Proxy requires authentication, authenticating"); + proxy.setUser(pref->getProxyUsername()); + proxy.setPassword(pref->getProxyPassword()); + } + } + else { + proxy.setType(QNetworkProxy::NoProxy); + } + + m_networkManager.setProxy(proxy); +} + +#ifndef QT_NO_OPENSSL +void DownloadManager::ignoreSslErrors(QNetworkReply *reply, const QList &errors) +{ + Q_UNUSED(errors) + // Ignore all SSL errors + reply->ignoreSslErrors(); +} +#endif diff --git a/src/core/net/downloadmanager.h b/src/core/net/downloadmanager.h new file mode 100644 index 000000000..ab3556acc --- /dev/null +++ b/src/core/net/downloadmanager.h @@ -0,0 +1,76 @@ +/* + * 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 NET_DOWNLOADMANAGER_H +#define NET_DOWNLOADMANAGER_H + +#include +#include + +QT_BEGIN_NAMESPACE +class QNetworkReply; +class QNetworkCookie; +class QSslError; +class QUrl; +QT_END_NAMESPACE + +namespace Net +{ + class DownloadHandler; + + class DownloadManager : public QObject + { + Q_OBJECT + + public: + static void initInstance(); + static void freeInstance(); + static DownloadManager *instance(); + + DownloadHandler *downloadUrl(const QString &url, qint64 limit = 0); + QList cookiesForUrl(const QString &url) const; + bool setCookiesFromUrl(const QList &cookieList, const QUrl &url); + + private slots: + #ifndef QT_NO_OPENSSL + void ignoreSslErrors(QNetworkReply *,const QList &); + #endif + + private: + DownloadManager(QObject *parent = 0); + ~DownloadManager(); + + void applyProxySettings(); + + static DownloadManager *m_instance; + QNetworkAccessManager m_networkManager; + }; +} + +#endif // NET_DOWNLOADMANAGER_H From 4b5e7e61680952bccef49acdd8bb931236e1c664 Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Mon, 13 Apr 2015 19:02:48 +0300 Subject: [PATCH 05/12] Move network related code to core/net. --- src/core/core.pri | 14 ++-- src/core/{ => net}/dnsupdater.cpp | 2 + src/core/{ => net}/dnsupdater.h | 5 ++ .../net/reverseresolution.cpp} | 83 ++++++++----------- src/core/net/reverseresolution.h | 67 +++++++++++++++ src/core/{ => net}/smtp.cpp | 4 +- src/core/{ => net}/smtp.h | 8 +- src/core/qtlibtorrent/qbtsession.cpp | 4 +- src/gui/gui.pri | 1 - src/gui/options_imp.cpp | 4 +- src/gui/properties/peerlistwidget.cpp | 6 +- src/gui/properties/peerlistwidget.h | 8 +- src/webui/webui.cpp | 4 +- src/webui/webui.h | 14 +++- 14 files changed, 154 insertions(+), 70 deletions(-) rename src/core/{ => net}/dnsupdater.cpp (99%) rename src/core/{ => net}/dnsupdater.h (99%) rename src/{gui/reverseresolution.h => core/net/reverseresolution.cpp} (59%) create mode 100644 src/core/net/reverseresolution.h rename src/core/{ => net}/smtp.cpp (99%) rename src/core/{ => net}/smtp.h (97%) diff --git a/src/core/core.pri b/src/core/core.pri index 8e02c50c2..74dd529ec 100644 --- a/src/core/core.pri +++ b/src/core/core.pri @@ -10,8 +10,6 @@ HEADERS += \ $$PWD/filesystemwatcher.h \ $$PWD/scannedfoldersmodel.h \ $$PWD/qinisettings.h \ - $$PWD/smtp.h \ - $$PWD/dnsupdater.h \ $$PWD/logger.h \ $$PWD/preferences.h \ $$PWD/qtracker.h \ @@ -21,7 +19,10 @@ HEADERS += \ $$PWD/http/responsegenerator.h \ $$PWD/http/server.h \ $$PWD/http/types.h \ - $$PWD/http/responsebuilder.h + $$PWD/http/responsebuilder.h \ + $$PWD/net/dnsupdater.h \ + $$PWD/net/reverseresolution.h \ + $$PWD/net/smtp.h SOURCES += \ $$PWD/downloadthread.cpp \ @@ -29,8 +30,6 @@ SOURCES += \ $$PWD/torrentpersistentdata.cpp \ $$PWD/misc.cpp \ $$PWD/fs_utils.cpp \ - $$PWD/smtp.cpp \ - $$PWD/dnsupdater.cpp \ $$PWD/logger.cpp \ $$PWD/preferences.cpp \ $$PWD/qtracker.cpp \ @@ -38,4 +37,7 @@ SOURCES += \ $$PWD/http/requestparser.cpp \ $$PWD/http/responsegenerator.cpp \ $$PWD/http/server.cpp \ - $$PWD/http/responsebuilder.cpp + $$PWD/http/responsebuilder.cpp \ + $$PWD/net/dnsupdater.cpp \ + $$PWD/net/reverseresolution.cpp \ + $$PWD/net/smtp.cpp diff --git a/src/core/dnsupdater.cpp b/src/core/net/dnsupdater.cpp similarity index 99% rename from src/core/dnsupdater.cpp rename to src/core/net/dnsupdater.cpp index b7d1a0d9c..5d176e733 100644 --- a/src/core/dnsupdater.cpp +++ b/src/core/net/dnsupdater.cpp @@ -38,6 +38,8 @@ #include "dnsupdater.h" #include "logger.h" +using namespace Net; + DNSUpdater::DNSUpdater(QObject *parent) : QObject(parent), m_state(OK), m_service(DNS::NONE) { diff --git a/src/core/dnsupdater.h b/src/core/net/dnsupdater.h similarity index 99% rename from src/core/dnsupdater.h rename to src/core/net/dnsupdater.h index 3ea9a5cd3..a130ef423 100644 --- a/src/core/dnsupdater.h +++ b/src/core/net/dnsupdater.h @@ -38,6 +38,9 @@ #include #include "preferences.h" +namespace Net +{ + /*! * Based on http://www.dyndns.com/developers/specs/ */ @@ -78,4 +81,6 @@ private: enum State { OK, INVALID_CREDS, FATAL }; }; +} + #endif // DNSUPDATER_H diff --git a/src/gui/reverseresolution.h b/src/core/net/reverseresolution.cpp similarity index 59% rename from src/gui/reverseresolution.h rename to src/core/net/reverseresolution.cpp index b7971adc2..d41280f8e 100644 --- a/src/gui/reverseresolution.h +++ b/src/core/net/reverseresolution.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,14 +28,9 @@ * Contact : chris@qbittorrent.org */ -#ifndef REVERSERESOLUTION_H -#define REVERSERESOLUTION_H - -#include -#include #include #include -#include "misc.h" +#include #include #if BOOST_VERSION < 103500 @@ -44,62 +39,56 @@ #include #endif +#include "reverseresolution.h" + const int CACHE_SIZE = 500; -class ReverseResolution: public QObject { - Q_OBJECT - Q_DISABLE_COPY(ReverseResolution) +using namespace Net; -public: - explicit ReverseResolution(QObject* parent): QObject(parent) { +static inline bool isUsefulHostName(const QString &hostname, const QString &ip) +{ + return (!hostname.isEmpty() && hostname != ip); +} + +ReverseResolution::ReverseResolution(QObject *parent) + : QObject(parent) +{ m_cache.setMaxCost(CACHE_SIZE); - } +} - ~ReverseResolution() { +ReverseResolution::~ReverseResolution() +{ qDebug("Deleting host name resolver..."); - } +} - void resolve(const QString &ip) { +void ReverseResolution::resolve(const QString &ip) +{ if (m_cache.contains(ip)) { - const QString& hostname = *m_cache.object(ip); - qDebug() << "Resolved host name using cache: " << ip << " -> " << hostname; - if (isUsefulHostName(hostname, ip)) - emit ip_resolved(ip, hostname); - return; + const QString &hostname = *m_cache.object(ip); + qDebug() << "Resolved host name using cache: " << ip << " -> " << hostname; + if (isUsefulHostName(hostname, ip)) + emit ipResolved(ip, hostname); } - // Actually resolve the ip - m_lookups.insert(QHostInfo::lookupHost(ip, this, SLOT(hostResolved(QHostInfo))), ip); - } + else { + // Actually resolve the ip + m_lookups.insert(QHostInfo::lookupHost(ip, this, SLOT(hostResolved(QHostInfo))), ip); + } +} -signals: - void ip_resolved(const QString &ip, const QString &hostname); - -private slots: - void hostResolved(const QHostInfo& host) { - const QString& ip = m_lookups.take(host.lookupId()); +void ReverseResolution::hostResolved(const QHostInfo &host) +{ + const QString &ip = m_lookups.take(host.lookupId()); Q_ASSERT(!ip.isNull()); if (host.error() != QHostInfo::NoError) { - qDebug() << "DNS Reverse resolution error: " << host.errorString(); - return; + qDebug() << "DNS Reverse resolution error: " << host.errorString(); + return; } - const QString& hostname = host.hostName(); + const QString &hostname = host.hostName(); qDebug() << Q_FUNC_INFO << ip << QString("->") << hostname; m_cache.insert(ip, new QString(hostname)); if (isUsefulHostName(hostname, ip)) - emit ip_resolved(ip, hostname); - } - -private: - static inline bool isUsefulHostName(const QString& hostname, const QString& ip) { - return (!hostname.isEmpty() && hostname != ip); - } - - QHash m_lookups; - QCache m_cache; -}; - - -#endif // REVERSERESOLUTION_H + emit ipResolved(ip, hostname); +} diff --git a/src/core/net/reverseresolution.h b/src/core/net/reverseresolution.h new file mode 100644 index 000000000..ef181762a --- /dev/null +++ b/src/core/net/reverseresolution.h @@ -0,0 +1,67 @@ +/* + * Bittorrent Client using Qt 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 NET_REVERSERESOLUTION_H +#define NET_REVERSERESOLUTION_H + +#include +#include + +QT_BEGIN_NAMESPACE +class QHostInfo; +class QString; +QT_END_NAMESPACE + +namespace Net +{ + class ReverseResolution : public QObject + { + Q_OBJECT + Q_DISABLE_COPY(ReverseResolution) + + public: + explicit ReverseResolution(QObject *parent = 0); + ~ReverseResolution(); + + void resolve(const QString &ip); + + signals: + void ipResolved(const QString &ip, const QString &hostname); + + private slots: + void hostResolved(const QHostInfo &host); + + private: + QHash m_lookups; + QCache m_cache; + }; +} + +#endif // NET_REVERSERESOLUTION_H diff --git a/src/core/smtp.cpp b/src/core/net/smtp.cpp similarity index 99% rename from src/core/smtp.cpp rename to src/core/net/smtp.cpp index f90fcc5a7..f895c1924 100644 --- a/src/core/smtp.cpp +++ b/src/core/net/smtp.cpp @@ -1,5 +1,5 @@ /* - * Bittorrent Client using Qt4 and libtorrent. + * Bittorrent Client using Qt and libtorrent. * Copyright (C) 2011 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -89,6 +89,8 @@ QByteArray determineFQDN() } } // namespace +using namespace Net; + Smtp::Smtp(QObject *parent): QObject(parent), state(Init), use_ssl(false) { #ifndef QT_NO_OPENSSL diff --git a/src/core/smtp.h b/src/core/net/smtp.h similarity index 97% rename from src/core/smtp.h rename to src/core/net/smtp.h index bb80a3a85..9b332416d 100644 --- a/src/core/smtp.h +++ b/src/core/net/smtp.h @@ -1,5 +1,5 @@ /* - * Bittorrent Client using Qt4 and libtorrent. + * Bittorrent Client using Qt and libtorrent. * Copyright (C) 2011 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -50,6 +50,9 @@ class QTcpSocket; class QTextCodec; QT_END_NAMESPACE +namespace Net +{ + class Smtp : public QObject { Q_OBJECT @@ -96,4 +99,7 @@ private: QString username; QString password; }; + +} + #endif diff --git a/src/core/qtlibtorrent/qbtsession.cpp b/src/core/qtlibtorrent/qbtsession.cpp index fc42aa6da..01ee5076e 100644 --- a/src/core/qtlibtorrent/qbtsession.cpp +++ b/src/core/qtlibtorrent/qbtsession.cpp @@ -38,7 +38,7 @@ #include #include -#include "smtp.h" +#include "core/net/smtp.h" #include "filesystemwatcher.h" #include "torrentspeedmonitor.h" #include "torrentstatistics.h" @@ -2046,7 +2046,7 @@ void QBtSession::sendNotificationEmail(const QTorrentHandle &h) { content += tr("The torrent was downloaded in %1.", "The torrent was downloaded in 1 hour and 20 seconds").arg(misc::userFriendlyDuration(status.active_time)) + "\n\n\n"; content += tr("Thank you for using qBittorrent.") + "\n"; // Send the notification email - Smtp *sender = new Smtp(this); + 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); } diff --git a/src/gui/gui.pri b/src/gui/gui.pri index 3ba11cd54..9665fe4f2 100644 --- a/src/gui/gui.pri +++ b/src/gui/gui.pri @@ -22,7 +22,6 @@ HEADERS += \ $$PWD/torrentcontenttreeview.h \ $$PWD/deletionconfirmationdlg.h \ $$PWD/statusbar.h \ - $$PWD/reverseresolution.h \ $$PWD/ico.h \ $$PWD/speedlimitdlg.h \ $$PWD/about_imp.h \ diff --git a/src/gui/options_imp.cpp b/src/gui/options_imp.cpp index fdc85d96f..e125ee2dc 100644 --- a/src/gui/options_imp.cpp +++ b/src/gui/options_imp.cpp @@ -48,7 +48,7 @@ #include "scannedfoldersmodel.h" #include "qbtsession.h" #include "iconprovider.h" -#include "dnsupdater.h" +#include "core/net/dnsupdater.h" #ifndef QT_NO_OPENSSL #include @@ -1239,7 +1239,7 @@ void options_imp::on_btnWebUiKey_clicked() { } void options_imp::on_registerDNSBtn_clicked() { - QDesktopServices::openUrl(DNSUpdater::getRegistrationUrl(comboDNSService->currentIndex())); + QDesktopServices::openUrl(Net::DNSUpdater::getRegistrationUrl(comboDNSService->currentIndex())); } void options_imp::on_IpFilterRefreshBtn_clicked() { diff --git a/src/gui/properties/peerlistwidget.cpp b/src/gui/properties/peerlistwidget.cpp index 69ebfd355..ea4c787b0 100644 --- a/src/gui/properties/peerlistwidget.cpp +++ b/src/gui/properties/peerlistwidget.cpp @@ -31,7 +31,7 @@ #include "peerlistwidget.h" #include "peerlistdelegate.h" #include "peerlistsortmodel.h" -#include "reverseresolution.h" +#include "core/net/reverseresolution.h" #include "preferences.h" #include "propertieswidget.h" #include "geoipmanager.h" @@ -126,8 +126,8 @@ void PeerListWidget::updatePeerHostNameResolutionState() { if (Preferences::instance()->resolvePeerHostNames()) { if (!m_resolver) { - m_resolver = new ReverseResolution(this); - connect(m_resolver, SIGNAL(ip_resolved(QString,QString)), SLOT(handleResolved(QString,QString))); + m_resolver = new Net::ReverseResolution(this); + connect(m_resolver, SIGNAL(ipResolved(QString,QString)), SLOT(handleResolved(QString,QString))); loadPeers(m_properties->getCurrentTorrent(), true); } } else { diff --git a/src/gui/properties/peerlistwidget.h b/src/gui/properties/peerlistwidget.h index 0771555c1..fa0652563 100644 --- a/src/gui/properties/peerlistwidget.h +++ b/src/gui/properties/peerlistwidget.h @@ -37,9 +37,13 @@ #include #include +namespace Net +{ + class ReverseResolution; +} + class PeerListDelegate; class PeerListSortModel; -class ReverseResolution; class PropertiesWidget; class QTorrentHandle; @@ -103,7 +107,7 @@ private: QHash m_peerItems; QHash m_peerEndpoints; QSet m_missingFlags; - QPointer m_resolver; + QPointer m_resolver; PropertiesWidget *m_properties; bool m_displayFlags; }; diff --git a/src/webui/webui.cpp b/src/webui/webui.cpp index 6cafe0b4b..723675ab2 100644 --- a/src/webui/webui.cpp +++ b/src/webui/webui.cpp @@ -29,7 +29,7 @@ #include "webui.h" #include "http/server.h" #include "webapplication.h" -#include "dnsupdater.h" +#include "core/net/dnsupdater.h" #include "preferences.h" #include "logger.h" @@ -82,7 +82,7 @@ void WebUI::init() // DynDNS if (pref->isDynDNSEnabled()) { if (!dynDNSUpdater_) - dynDNSUpdater_ = new DNSUpdater(this); + dynDNSUpdater_ = new Net::DNSUpdater(this); else dynDNSUpdater_->updateCredentials(); } diff --git a/src/webui/webui.h b/src/webui/webui.h index 2abc6b924..9e7801af0 100644 --- a/src/webui/webui.h +++ b/src/webui/webui.h @@ -32,8 +32,16 @@ #include #include -namespace Http { class Server; } -class DNSUpdater; +namespace Http +{ + class Server; +} + +namespace Net +{ + class DNSUpdater; +} + class AbstractWebApplication; class WebUI : public QObject @@ -48,7 +56,7 @@ private slots: private: QPointer httpServer_; - QPointer dynDNSUpdater_; + QPointer dynDNSUpdater_; QPointer webapp_; }; From d32bb52390c41cccb6b6964dd30324ea78f090fa Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Mon, 13 Apr 2015 19:19:53 +0300 Subject: [PATCH 06/12] Don't add core to INCLUDEPATH. --- src/app/application.cpp | 6 +++--- src/app/main.cpp | 6 +++--- src/core/core.pri | 2 -- src/core/net/dnsupdater.cpp | 2 +- src/core/net/dnsupdater.h | 2 +- src/core/net/smtp.cpp | 4 ++-- src/core/qtlibtorrent/bandwidthscheduler.h | 2 +- src/core/qtlibtorrent/qbtsession.cpp | 18 +++++++++--------- src/core/qtlibtorrent/qbtsession.h | 2 +- src/core/qtlibtorrent/qtorrenthandle.cpp | 8 ++++---- src/core/qtlibtorrent/torrentspeedmonitor.cpp | 2 +- src/core/qtlibtorrent/torrentstatistics.cpp | 6 +++--- src/gui/addnewtorrentdialog.cpp | 6 +++--- src/gui/advancedsettings.h | 2 +- src/gui/deletionconfirmationdlg.h | 4 ++-- src/gui/executionlog.cpp | 2 +- src/gui/geoip/geoipmanager.cpp | 2 +- src/gui/iconprovider.cpp | 2 +- src/gui/mainwindow.cpp | 10 +++++----- src/gui/options_imp.cpp | 6 +++--- src/gui/previewlistdelegate.h | 2 +- src/gui/previewselect.cpp | 6 +++--- src/gui/programupdater.cpp | 4 ++-- src/gui/properties/peerlistdelegate.h | 2 +- src/gui/properties/peerlistwidget.cpp | 4 ++-- src/gui/properties/propertieswidget.cpp | 6 +++--- src/gui/properties/proplistdelegate.h | 2 +- src/gui/properties/trackerlist.cpp | 4 ++-- src/gui/properties/trackersadditiondlg.h | 6 +++--- src/gui/rss/automatedrssdownloader.cpp | 4 ++-- src/gui/rss/htmlbrowser.cpp | 2 +- src/gui/rss/rss_imp.cpp | 2 +- src/gui/rss/rssdownloadrule.cpp | 4 ++-- src/gui/rss/rssdownloadrulelist.cpp | 4 ++-- src/gui/rss/rssfeed.cpp | 12 ++++++------ src/gui/rss/rssmanager.cpp | 4 ++-- src/gui/rss/rssparser.cpp | 4 ++-- src/gui/rss/rsssettingsdlg.cpp | 2 +- src/gui/shutdownconfirm.h | 2 +- src/gui/speedlimitdlg.h | 2 +- src/gui/statsdialog.cpp | 2 +- src/gui/statusbar.cpp | 6 +++--- src/gui/torrentcontentmodel.cpp | 4 ++-- src/gui/torrentcontentmodelfile.cpp | 4 ++-- src/gui/torrentcontentmodelitem.cpp | 4 ++-- src/gui/torrentcreator/torrentcreatordlg.cpp | 8 ++++---- .../torrentcreator/torrentcreatorthread.cpp | 4 ++-- src/gui/torrentimportdlg.cpp | 6 +++--- src/gui/torrentmodel.cpp | 4 ++-- src/gui/transferlistdelegate.cpp | 2 +- src/gui/transferlistfilterswidget.cpp | 10 +++++----- src/gui/transferlistsortmodel.cpp | 2 +- src/gui/transferlistwidget.cpp | 6 +++--- src/gui/updownratiodlg.cpp | 2 +- src/searchengine/engineselectdlg.cpp | 6 +++--- src/searchengine/searchengine.cpp | 6 +++--- src/searchengine/searchlistdelegate.h | 2 +- src/searchengine/searchsortmodel.h | 2 +- src/searchengine/searchtab.cpp | 4 ++-- src/searchengine/supportedengines.h | 4 ++-- src/webui/abstractwebapplication.cpp | 2 +- src/webui/abstractwebapplication.h | 6 +++--- src/webui/btjson.cpp | 4 ++-- src/webui/prefjson.cpp | 6 +++--- src/webui/qtorrentfilter.cpp | 2 +- src/webui/webapplication.cpp | 6 +++--- src/webui/webui.cpp | 6 +++--- 67 files changed, 145 insertions(+), 147 deletions(-) diff --git a/src/app/application.cpp b/src/app/application.cpp index fa4a7f62b..79e814c06 100644 --- a/src/app/application.cpp +++ b/src/app/application.cpp @@ -55,10 +55,10 @@ #endif #include "application.h" -#include "logger.h" -#include "preferences.h" +#include "core/logger.h" +#include "core/preferences.h" #include "qbtsession.h" -#include "torrentpersistentdata.h" +#include "core/torrentpersistentdata.h" static const char PARAMS_SEPARATOR[] = "|"; diff --git a/src/app/main.cpp b/src/app/main.cpp index 26605d8a7..03a010d9e 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -66,9 +66,9 @@ Q_IMPORT_PLUGIN(qico) #include #include #include "application.h" -#include "misc.h" -#include "preferences.h" -#include "logger.h" +#include "core/misc.h" +#include "core/preferences.h" +#include "core/logger.h" // Signal handlers #if defined(Q_OS_UNIX) || defined(STACKTRACE_WIN) diff --git a/src/core/core.pri b/src/core/core.pri index 74dd529ec..43d5ecfd2 100644 --- a/src/core/core.pri +++ b/src/core/core.pri @@ -1,5 +1,3 @@ -INCLUDEPATH += $$PWD - include(qtlibtorrent/qtlibtorrent.pri) HEADERS += \ diff --git a/src/core/net/dnsupdater.cpp b/src/core/net/dnsupdater.cpp index 5d176e733..bb47528b8 100644 --- a/src/core/net/dnsupdater.cpp +++ b/src/core/net/dnsupdater.cpp @@ -36,7 +36,7 @@ #include #endif #include "dnsupdater.h" -#include "logger.h" +#include "core/logger.h" using namespace Net; diff --git a/src/core/net/dnsupdater.h b/src/core/net/dnsupdater.h index a130ef423..9a252405a 100644 --- a/src/core/net/dnsupdater.h +++ b/src/core/net/dnsupdater.h @@ -36,7 +36,7 @@ #include #include #include -#include "preferences.h" +#include "core/preferences.h" namespace Net { diff --git a/src/core/net/smtp.cpp b/src/core/net/smtp.cpp index f895c1924..1a685b48b 100644 --- a/src/core/net/smtp.cpp +++ b/src/core/net/smtp.cpp @@ -33,8 +33,8 @@ */ #include "smtp.h" -#include "preferences.h" -#include "logger.h" +#include "core/preferences.h" +#include "core/logger.h" #include #ifndef QT_NO_OPENSSL diff --git a/src/core/qtlibtorrent/bandwidthscheduler.h b/src/core/qtlibtorrent/bandwidthscheduler.h index 318f8e767..8794eebfb 100644 --- a/src/core/qtlibtorrent/bandwidthscheduler.h +++ b/src/core/qtlibtorrent/bandwidthscheduler.h @@ -4,7 +4,7 @@ #include #include #include -#include "preferences.h" +#include "core/preferences.h" #include diff --git a/src/core/qtlibtorrent/qbtsession.cpp b/src/core/qtlibtorrent/qbtsession.cpp index 01ee5076e..03b077719 100644 --- a/src/core/qtlibtorrent/qbtsession.cpp +++ b/src/core/qtlibtorrent/qbtsession.cpp @@ -39,24 +39,24 @@ #include #include "core/net/smtp.h" -#include "filesystemwatcher.h" +#include "core/filesystemwatcher.h" #include "torrentspeedmonitor.h" #include "torrentstatistics.h" #include "qbtsession.h" #include "alertdispatcher.h" -#include "misc.h" -#include "fs_utils.h" -#include "downloadthread.h" +#include "core/misc.h" +#include "core/fs_utils.h" +#include "core/downloadthread.h" #include "filterparserthread.h" -#include "preferences.h" -#include "scannedfoldersmodel.h" -#include "qtracker.h" -#include "logger.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 "torrentpersistentdata.h" +#include "core/torrentpersistentdata.h" #include "bandwidthscheduler.h" #include #include diff --git a/src/core/qtlibtorrent/qbtsession.h b/src/core/qtlibtorrent/qbtsession.h index aac6ae7ac..9c9a12fc8 100644 --- a/src/core/qtlibtorrent/qbtsession.h +++ b/src/core/qtlibtorrent/qbtsession.h @@ -42,7 +42,7 @@ #include "qtorrenthandle.h" #include "trackerinfos.h" -#include "misc.h" +#include "core/misc.h" #ifndef DISABLE_GUI #include "rssdownloadrule.h" diff --git a/src/core/qtlibtorrent/qtorrenthandle.cpp b/src/core/qtlibtorrent/qtorrenthandle.cpp index 79c923e1d..302118223 100644 --- a/src/core/qtlibtorrent/qtorrenthandle.cpp +++ b/src/core/qtlibtorrent/qtorrenthandle.cpp @@ -35,11 +35,11 @@ #include #include #include -#include "fs_utils.h" -#include "misc.h" -#include "preferences.h" +#include "core/fs_utils.h" +#include "core/misc.h" +#include "core/preferences.h" #include "qtorrenthandle.h" -#include "torrentpersistentdata.h" +#include "core/torrentpersistentdata.h" #include "qbtsession.h" #include #include diff --git a/src/core/qtlibtorrent/torrentspeedmonitor.cpp b/src/core/qtlibtorrent/torrentspeedmonitor.cpp index 1cb821dde..1cb6640e2 100644 --- a/src/core/qtlibtorrent/torrentspeedmonitor.cpp +++ b/src/core/qtlibtorrent/torrentspeedmonitor.cpp @@ -31,7 +31,7 @@ #include #include "qbtsession.h" -#include "misc.h" +#include "core/misc.h" #include "torrentspeedmonitor.h" using namespace libtorrent; diff --git a/src/core/qtlibtorrent/torrentstatistics.cpp b/src/core/qtlibtorrent/torrentstatistics.cpp index b143d6ccf..436c9c37c 100644 --- a/src/core/qtlibtorrent/torrentstatistics.cpp +++ b/src/core/qtlibtorrent/torrentstatistics.cpp @@ -5,8 +5,8 @@ #include #include "qbtsession.h" -#include "qinisettings.h" -#include "preferences.h" +#include "core/qinisettings.h" +#include "core/preferences.h" TorrentStatistics::TorrentStatistics(QBtSession* session, QObject* parent) : QObject(parent) @@ -69,7 +69,7 @@ void TorrentStatistics::loadStats() { // Don't forget to remove: // 1. Preferences::getStats() // 2. Preferences::removeStats() - // 3. #include "preferences.h" + // 3. #include "core/preferences.h" Preferences* const pref = Preferences::instance(); QIniSettings s("qBittorrent", "qBittorrent-data"); QVariantHash v = pref->getStats(); diff --git a/src/gui/addnewtorrentdialog.cpp b/src/gui/addnewtorrentdialog.cpp index 2bd8d633b..e9a06b92a 100644 --- a/src/gui/addnewtorrentdialog.cpp +++ b/src/gui/addnewtorrentdialog.cpp @@ -33,11 +33,11 @@ #include "proplistdelegate.h" #include "torrentcontentmodel.h" #include "torrentcontentfiltermodel.h" -#include "preferences.h" -#include "torrentpersistentdata.h" +#include "core/preferences.h" +#include "core/torrentpersistentdata.h" #include "qbtsession.h" #include "iconprovider.h" -#include "fs_utils.h" +#include "core/fs_utils.h" #include "autoexpandabledialog.h" #include "messageboxraised.h" diff --git a/src/gui/advancedsettings.h b/src/gui/advancedsettings.h index 289091163..7c6a0e6bb 100644 --- a/src/gui/advancedsettings.h +++ b/src/gui/advancedsettings.h @@ -10,7 +10,7 @@ #include #include #include -#include "preferences.h" +#include "core/preferences.h" enum AdvSettingsCols {PROPERTY, VALUE}; enum AdvSettingsRows {DISK_CACHE, DISK_CACHE_TTL, OS_CACHE, SAVE_RESUME_DATA_INTERVAL, OUTGOING_PORT_MIN, OUTGOING_PORT_MAX, IGNORE_LIMIT_LAN, RECHECK_COMPLETED, LIST_REFRESH, RESOLVE_COUNTRIES, RESOLVE_HOSTS, MAX_HALF_OPEN, SUPER_SEEDING, NETWORK_IFACE, NETWORK_LISTEN_IPV6, NETWORK_ADDRESS, PROGRAM_NOTIFICATIONS, TRACKER_STATUS, TRACKER_PORT, diff --git a/src/gui/deletionconfirmationdlg.h b/src/gui/deletionconfirmationdlg.h index 4b778d28b..8af23a64e 100644 --- a/src/gui/deletionconfirmationdlg.h +++ b/src/gui/deletionconfirmationdlg.h @@ -34,9 +34,9 @@ #include #include #include "ui_confirmdeletiondlg.h" -#include "preferences.h" +#include "core/preferences.h" #include "iconprovider.h" -#include "misc.h" +#include "core/misc.h" class DeletionConfirmationDlg : public QDialog, private Ui::confirmDeletionDlg { Q_OBJECT diff --git a/src/gui/executionlog.cpp b/src/gui/executionlog.cpp index 7c2be8f4f..2498191aa 100644 --- a/src/gui/executionlog.cpp +++ b/src/gui/executionlog.cpp @@ -35,7 +35,7 @@ #include #include "executionlog.h" #include "ui_executionlog.h" -#include "logger.h" +#include "core/logger.h" #include "iconprovider.h" #include "loglistwidget.h" diff --git a/src/gui/geoip/geoipmanager.cpp b/src/gui/geoip/geoipmanager.cpp index 1e0c58c55..5559545b8 100644 --- a/src/gui/geoip/geoipmanager.cpp +++ b/src/gui/geoip/geoipmanager.cpp @@ -64,7 +64,7 @@ #include #include -#include "fs_utils.h" +#include "core/fs_utils.h" #include using namespace libtorrent; diff --git a/src/gui/iconprovider.cpp b/src/gui/iconprovider.cpp index 2213993a4..3d79e2787 100644 --- a/src/gui/iconprovider.cpp +++ b/src/gui/iconprovider.cpp @@ -29,7 +29,7 @@ */ #include "iconprovider.h" -#include "preferences.h" +#include "core/preferences.h" #if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) #include diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index 49091e4f0..f210ffbbf 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -49,7 +49,7 @@ #include "mainwindow.h" #include "transferlistwidget.h" -#include "misc.h" +#include "core/misc.h" #include "torrentcreatordlg.h" #include "downloadfromurldlg.h" #include "addnewtorrentdialog.h" @@ -60,10 +60,10 @@ #include "trackerlogin.h" #include "options_imp.h" #include "speedlimitdlg.h" -#include "preferences.h" +#include "core/preferences.h" #include "trackerlist.h" #include "peerlistwidget.h" -#include "torrentpersistentdata.h" +#include "core/torrentpersistentdata.h" #include "transferlistfilterswidget.h" #include "propertieswidget.h" #include "statusbar.h" @@ -72,7 +72,7 @@ #include "torrentmodel.h" #include "executionlog.h" #include "iconprovider.h" -#include "logger.h" +#include "core/logger.h" #ifndef DISABLE_GUI #include "autoexpandabledialog.h" #endif @@ -86,7 +86,7 @@ void qt_mac_set_dock_menu(QMenu *menu); #endif #include "powermanagement.h" #ifdef Q_OS_WIN -#include "downloadthread.h" +#include "core/downloadthread.h" #endif #include diff --git a/src/gui/options_imp.cpp b/src/gui/options_imp.cpp index e125ee2dc..d2da8e3df 100644 --- a/src/gui/options_imp.cpp +++ b/src/gui/options_imp.cpp @@ -42,10 +42,10 @@ #include #include "options_imp.h" -#include "preferences.h" -#include "fs_utils.h" +#include "core/preferences.h" +#include "core/fs_utils.h" #include "advancedsettings.h" -#include "scannedfoldersmodel.h" +#include "core/scannedfoldersmodel.h" #include "qbtsession.h" #include "iconprovider.h" #include "core/net/dnsupdater.h" diff --git a/src/gui/previewlistdelegate.h b/src/gui/previewlistdelegate.h index 7b7be6871..37951901c 100644 --- a/src/gui/previewlistdelegate.h +++ b/src/gui/previewlistdelegate.h @@ -37,7 +37,7 @@ #include #include #include -#include "misc.h" +#include "core/misc.h" #include "previewselect.h" #ifdef Q_OS_WIN diff --git a/src/gui/previewselect.cpp b/src/gui/previewselect.cpp index d52177994..42c01fbf4 100644 --- a/src/gui/previewselect.cpp +++ b/src/gui/previewselect.cpp @@ -36,11 +36,11 @@ #include #include -#include "misc.h" +#include "core/misc.h" #include "previewlistdelegate.h" #include "previewselect.h" -#include "fs_utils.h" -#include "preferences.h" +#include "core/fs_utils.h" +#include "core/preferences.h" PreviewSelect::PreviewSelect(QWidget* parent, QTorrentHandle h): QDialog(parent), h(h) { setupUi(this); diff --git a/src/gui/programupdater.cpp b/src/gui/programupdater.cpp index b5350431d..80191acf1 100644 --- a/src/gui/programupdater.cpp +++ b/src/gui/programupdater.cpp @@ -39,8 +39,8 @@ #include #include "programupdater.h" -#include "fs_utils.h" -#include "preferences.h" +#include "core/fs_utils.h" +#include "core/preferences.h" #ifdef Q_OS_MAC const QUrl RSS_URL("http://sourceforge.net/projects/qbittorrent/rss?path=/qbittorrent-mac"); diff --git a/src/gui/properties/peerlistdelegate.h b/src/gui/properties/peerlistdelegate.h index a71ed3f8c..aca206768 100644 --- a/src/gui/properties/peerlistdelegate.h +++ b/src/gui/properties/peerlistdelegate.h @@ -33,7 +33,7 @@ #include #include -#include "misc.h" +#include "core/misc.h" class PeerListDelegate: public QItemDelegate { Q_OBJECT diff --git a/src/gui/properties/peerlistwidget.cpp b/src/gui/properties/peerlistwidget.cpp index ea4c787b0..aa7346448 100644 --- a/src/gui/properties/peerlistwidget.cpp +++ b/src/gui/properties/peerlistwidget.cpp @@ -32,14 +32,14 @@ #include "peerlistdelegate.h" #include "peerlistsortmodel.h" #include "core/net/reverseresolution.h" -#include "preferences.h" +#include "core/preferences.h" #include "propertieswidget.h" #include "geoipmanager.h" #include "peeraddition.h" #include "speedlimitdlg.h" #include "iconprovider.h" #include "qtorrenthandle.h" -#include "logger.h" +#include "core/logger.h" #include #include #include diff --git a/src/gui/properties/propertieswidget.cpp b/src/gui/properties/propertieswidget.cpp index c34fb0102..53bca79ec 100644 --- a/src/gui/properties/propertieswidget.cpp +++ b/src/gui/properties/propertieswidget.cpp @@ -43,7 +43,7 @@ #include #include "propertieswidget.h" #include "transferlistwidget.h" -#include "torrentpersistentdata.h" +#include "core/torrentpersistentdata.h" #include "qbtsession.h" #include "proplistdelegate.h" #include "torrentcontentfiltermodel.h" @@ -53,11 +53,11 @@ #include "mainwindow.h" #include "downloadedpiecesbar.h" #include "pieceavailabilitybar.h" -#include "preferences.h" +#include "core/preferences.h" #include "proptabbar.h" #include "iconprovider.h" #include "lineedit.h" -#include "fs_utils.h" +#include "core/fs_utils.h" #include "autoexpandabledialog.h" using namespace libtorrent; diff --git a/src/gui/properties/proplistdelegate.h b/src/gui/properties/proplistdelegate.h index 791cbe481..9a5c3109f 100644 --- a/src/gui/properties/proplistdelegate.h +++ b/src/gui/properties/proplistdelegate.h @@ -40,7 +40,7 @@ #include #include #include -#include "misc.h" +#include "core/misc.h" #include "propertieswidget.h" #ifdef Q_OS_WIN diff --git a/src/gui/properties/trackerlist.cpp b/src/gui/properties/trackerlist.cpp index 3910c0258..22a3a2272 100644 --- a/src/gui/properties/trackerlist.cpp +++ b/src/gui/properties/trackerlist.cpp @@ -43,8 +43,8 @@ #include "trackersadditiondlg.h" #include "iconprovider.h" #include "qbtsession.h" -#include "preferences.h" -#include "misc.h" +#include "core/preferences.h" +#include "core/misc.h" #include "autoexpandabledialog.h" using namespace libtorrent; diff --git a/src/gui/properties/trackersadditiondlg.h b/src/gui/properties/trackersadditiondlg.h index 874a497fc..11efe3761 100644 --- a/src/gui/properties/trackersadditiondlg.h +++ b/src/gui/properties/trackersadditiondlg.h @@ -37,11 +37,11 @@ #include #include #include "iconprovider.h" -#include "misc.h" +#include "core/misc.h" #include "ui_trackersadditiondlg.h" -#include "downloadthread.h" +#include "core/downloadthread.h" #include "qtorrenthandle.h" -#include "fs_utils.h" +#include "core/fs_utils.h" class TrackersAdditionDlg : public QDialog, private Ui::TrackersAdditionDlg{ Q_OBJECT diff --git a/src/gui/rss/automatedrssdownloader.cpp b/src/gui/rss/automatedrssdownloader.cpp index 910a04a13..496329f0a 100644 --- a/src/gui/rss/automatedrssdownloader.cpp +++ b/src/gui/rss/automatedrssdownloader.cpp @@ -37,12 +37,12 @@ #include "automatedrssdownloader.h" #include "ui_automatedrssdownloader.h" #include "rssdownloadrulelist.h" -#include "preferences.h" +#include "core/preferences.h" #include "rssmanager.h" #include "rssfeed.h" #include "iconprovider.h" #include "autoexpandabledialog.h" -#include "fs_utils.h" +#include "core/fs_utils.h" AutomatedRssDownloader::AutomatedRssDownloader(const QWeakPointer& manager, QWidget *parent) : QDialog(parent), diff --git a/src/gui/rss/htmlbrowser.cpp b/src/gui/rss/htmlbrowser.cpp index 3350c9450..07455fb3f 100644 --- a/src/gui/rss/htmlbrowser.cpp +++ b/src/gui/rss/htmlbrowser.cpp @@ -10,7 +10,7 @@ #include #include -#include "fs_utils.h" +#include "core/fs_utils.h" HtmlBrowser::HtmlBrowser(QWidget* parent) : QTextBrowser(parent) diff --git a/src/gui/rss/rss_imp.cpp b/src/gui/rss/rss_imp.cpp index 65565b5e2..f5a1de187 100644 --- a/src/gui/rss/rss_imp.cpp +++ b/src/gui/rss/rss_imp.cpp @@ -41,7 +41,7 @@ #include "feedlistwidget.h" #include "qbtsession.h" #include "cookiesdlg.h" -#include "preferences.h" +#include "core/preferences.h" #include "rsssettingsdlg.h" #include "rssmanager.h" #include "rssfolder.h" diff --git a/src/gui/rss/rssdownloadrule.cpp b/src/gui/rss/rssdownloadrule.cpp index 23ba95993..2127dcecf 100644 --- a/src/gui/rss/rssdownloadrule.cpp +++ b/src/gui/rss/rssdownloadrule.cpp @@ -33,10 +33,10 @@ #include #include "rssdownloadrule.h" -#include "preferences.h" +#include "core/preferences.h" #include "rssfeed.h" #include "rssarticle.h" -#include "fs_utils.h" +#include "core/fs_utils.h" RssDownloadRule::RssDownloadRule(): m_enabled(false), m_useRegex(false), m_apstate(USE_GLOBAL) { diff --git a/src/gui/rss/rssdownloadrulelist.cpp b/src/gui/rss/rssdownloadrulelist.cpp index 295cb6a6c..33ae84ea4 100644 --- a/src/gui/rss/rssdownloadrulelist.cpp +++ b/src/gui/rss/rssdownloadrulelist.cpp @@ -33,8 +33,8 @@ #include #include "rssdownloadrulelist.h" -#include "preferences.h" -#include "qinisettings.h" +#include "core/preferences.h" +#include "core/qinisettings.h" RssDownloadRuleList::RssDownloadRuleList() { diff --git a/src/gui/rss/rssfeed.cpp b/src/gui/rss/rssfeed.cpp index 63b23e92f..36b8f67b9 100644 --- a/src/gui/rss/rssfeed.cpp +++ b/src/gui/rss/rssfeed.cpp @@ -33,15 +33,15 @@ #include "rssmanager.h" #include "qbtsession.h" #include "rssfolder.h" -#include "preferences.h" -#include "qinisettings.h" +#include "core/preferences.h" +#include "core/qinisettings.h" #include "rssarticle.h" #include "rssparser.h" -#include "misc.h" +#include "core/misc.h" #include "rssdownloadrulelist.h" -#include "downloadthread.h" -#include "fs_utils.h" -#include "logger.h" +#include "core/downloadthread.h" +#include "core/fs_utils.h" +#include "core/logger.h" bool rssArticleDateRecentThan(const RssArticlePtr& left, const RssArticlePtr& right) { diff --git a/src/gui/rss/rssmanager.cpp b/src/gui/rss/rssmanager.cpp index 853de3c5c..42e59f3a3 100644 --- a/src/gui/rss/rssmanager.cpp +++ b/src/gui/rss/rssmanager.cpp @@ -30,13 +30,13 @@ #include #include "rssmanager.h" -#include "preferences.h" +#include "core/preferences.h" #include "qbtsession.h" #include "rssfeed.h" #include "rssarticle.h" #include "rssdownloadrulelist.h" #include "rssparser.h" -#include "downloadthread.h" +#include "core/downloadthread.h" static const int MSECS_PER_MIN = 60000; diff --git a/src/gui/rss/rssparser.cpp b/src/gui/rss/rssparser.cpp index 50989cbbe..d0e85a64a 100644 --- a/src/gui/rss/rssparser.cpp +++ b/src/gui/rss/rssparser.cpp @@ -29,8 +29,8 @@ */ #include "rssparser.h" -#include "downloadthread.h" -#include "fs_utils.h" +#include "core/downloadthread.h" +#include "core/fs_utils.h" #include #include #include diff --git a/src/gui/rss/rsssettingsdlg.cpp b/src/gui/rss/rsssettingsdlg.cpp index 195df9ac5..627a30a89 100644 --- a/src/gui/rss/rsssettingsdlg.cpp +++ b/src/gui/rss/rsssettingsdlg.cpp @@ -30,7 +30,7 @@ #include "rsssettingsdlg.h" #include "ui_rsssettingsdlg.h" -#include "preferences.h" +#include "core/preferences.h" RssSettingsDlg::RssSettingsDlg(QWidget *parent) : QDialog(parent), diff --git a/src/gui/shutdownconfirm.h b/src/gui/shutdownconfirm.h index 774d9bc5e..a8013f846 100644 --- a/src/gui/shutdownconfirm.h +++ b/src/gui/shutdownconfirm.h @@ -33,7 +33,7 @@ #include #include -#include "misc.h" +#include "core/misc.h" class ShutdownConfirmDlg : public QMessageBox { diff --git a/src/gui/speedlimitdlg.h b/src/gui/speedlimitdlg.h index 96a36171d..f084d330b 100644 --- a/src/gui/speedlimitdlg.h +++ b/src/gui/speedlimitdlg.h @@ -34,7 +34,7 @@ #include #include #include "ui_bandwidth_limit.h" -#include "misc.h" +#include "core/misc.h" #include "qbtsession.h" class SpeedLimitDialog : public QDialog, private Ui_bandwidth_dlg { diff --git a/src/gui/statsdialog.cpp b/src/gui/statsdialog.cpp index f90e38716..b093c8595 100644 --- a/src/gui/statsdialog.cpp +++ b/src/gui/statsdialog.cpp @@ -31,7 +31,7 @@ #include "statsdialog.h" #include "ui_statsdialog.h" -#include "misc.h" +#include "core/misc.h" #include #include diff --git a/src/gui/statusbar.cpp b/src/gui/statusbar.cpp index d775d068a..aee363506 100644 --- a/src/gui/statusbar.cpp +++ b/src/gui/statusbar.cpp @@ -36,9 +36,9 @@ #include "qbtsession.h" #include "speedlimitdlg.h" #include "iconprovider.h" -#include "preferences.h" -#include "misc.h" -#include "logger.h" +#include "core/preferences.h" +#include "core/misc.h" +#include "core/logger.h" #include #include diff --git a/src/gui/torrentcontentmodel.cpp b/src/gui/torrentcontentmodel.cpp index 97e8242b3..83f775075 100644 --- a/src/gui/torrentcontentmodel.cpp +++ b/src/gui/torrentcontentmodel.cpp @@ -29,8 +29,8 @@ */ #include "iconprovider.h" -#include "misc.h" -#include "fs_utils.h" +#include "core/misc.h" +#include "core/fs_utils.h" #include "torrentcontentmodel.h" #include "torrentcontentmodelitem.h" #include "torrentcontentmodelfolder.h" diff --git a/src/gui/torrentcontentmodelfile.cpp b/src/gui/torrentcontentmodelfile.cpp index d8312e8c7..e9c2f8701 100644 --- a/src/gui/torrentcontentmodelfile.cpp +++ b/src/gui/torrentcontentmodelfile.cpp @@ -30,8 +30,8 @@ #include "torrentcontentmodelfile.h" #include "torrentcontentmodelfolder.h" -#include "fs_utils.h" -#include "misc.h" +#include "core/fs_utils.h" +#include "core/misc.h" TorrentContentModelFile::TorrentContentModelFile(const libtorrent::file_entry& f, TorrentContentModelFolder* parent, diff --git a/src/gui/torrentcontentmodelitem.cpp b/src/gui/torrentcontentmodelitem.cpp index 429d8eddb..03385389d 100644 --- a/src/gui/torrentcontentmodelitem.cpp +++ b/src/gui/torrentcontentmodelitem.cpp @@ -28,8 +28,8 @@ * Contact : chris@qbittorrent.org */ -#include "misc.h" -#include "fs_utils.h" +#include "core/misc.h" +#include "core/fs_utils.h" #include "torrentcontentmodelitem.h" #include "torrentcontentmodelfolder.h" #include diff --git a/src/gui/torrentcreator/torrentcreatordlg.cpp b/src/gui/torrentcreator/torrentcreatordlg.cpp index 882192ce6..0092b2905 100644 --- a/src/gui/torrentcreator/torrentcreatordlg.cpp +++ b/src/gui/torrentcreator/torrentcreatordlg.cpp @@ -32,11 +32,11 @@ #include #include -#include "torrentpersistentdata.h" +#include "core/torrentpersistentdata.h" #include "torrentcreatordlg.h" -#include "fs_utils.h" -#include "misc.h" -#include "preferences.h" +#include "core/fs_utils.h" +#include "core/misc.h" +#include "core/preferences.h" #include "torrentcreatorthread.h" #include "iconprovider.h" #include "qbtsession.h" diff --git a/src/gui/torrentcreator/torrentcreatorthread.cpp b/src/gui/torrentcreator/torrentcreatorthread.cpp index 88f9666d1..43853befd 100644 --- a/src/gui/torrentcreator/torrentcreatorthread.cpp +++ b/src/gui/torrentcreator/torrentcreatorthread.cpp @@ -41,8 +41,8 @@ #include #include "torrentcreatorthread.h" -#include "fs_utils.h" -#include "misc.h" +#include "core/fs_utils.h" +#include "core/misc.h" #include #include diff --git a/src/gui/torrentimportdlg.cpp b/src/gui/torrentimportdlg.cpp index ed3a71993..12db5eadc 100644 --- a/src/gui/torrentimportdlg.cpp +++ b/src/gui/torrentimportdlg.cpp @@ -34,11 +34,11 @@ #include "torrentimportdlg.h" #include "ui_torrentimportdlg.h" -#include "preferences.h" +#include "core/preferences.h" #include "qbtsession.h" -#include "torrentpersistentdata.h" +#include "core/torrentpersistentdata.h" #include "iconprovider.h" -#include "fs_utils.h" +#include "core/fs_utils.h" using namespace libtorrent; diff --git a/src/gui/torrentmodel.cpp b/src/gui/torrentmodel.cpp index e694a551e..c680206e6 100644 --- a/src/gui/torrentmodel.cpp +++ b/src/gui/torrentmodel.cpp @@ -33,9 +33,9 @@ #include #include "torrentmodel.h" -#include "torrentpersistentdata.h" +#include "core/torrentpersistentdata.h" #include "qbtsession.h" -#include "fs_utils.h" +#include "core/fs_utils.h" #include diff --git a/src/gui/transferlistdelegate.cpp b/src/gui/transferlistdelegate.cpp index eb660401b..020a60355 100644 --- a/src/gui/transferlistdelegate.cpp +++ b/src/gui/transferlistdelegate.cpp @@ -34,7 +34,7 @@ #include #include #include -#include "misc.h" +#include "core/misc.h" #include "torrentmodel.h" #include "qbtsession.h" diff --git a/src/gui/transferlistfilterswidget.cpp b/src/gui/transferlistfilterswidget.cpp index 5886c498d..1000668d5 100644 --- a/src/gui/transferlistfilterswidget.cpp +++ b/src/gui/transferlistfilterswidget.cpp @@ -41,15 +41,15 @@ #include "transferlistdelegate.h" #include "transferlistwidget.h" -#include "preferences.h" +#include "core/preferences.h" #include "torrentmodel.h" #include "iconprovider.h" -#include "fs_utils.h" +#include "core/fs_utils.h" #include "autoexpandabledialog.h" #include "torrentfilterenum.h" -#include "misc.h" -#include "downloadthread.h" -#include "logger.h" +#include "core/misc.h" +#include "core/downloadthread.h" +#include "core/logger.h" FiltersBase::FiltersBase(QWidget *parent, TransferListWidget *transferList) : QListWidget(parent) diff --git a/src/gui/transferlistsortmodel.cpp b/src/gui/transferlistsortmodel.cpp index 0574e44c4..ebced81e1 100644 --- a/src/gui/transferlistsortmodel.cpp +++ b/src/gui/transferlistsortmodel.cpp @@ -31,7 +31,7 @@ #include "transferlistsortmodel.h" #include "torrentmodel.h" -#include "misc.h" +#include "core/misc.h" TransferListSortModel::TransferListSortModel(QObject *parent) : QSortFilterProxyModel(parent) diff --git a/src/gui/transferlistwidget.cpp b/src/gui/transferlistwidget.cpp index db5fbc65f..80c7fcfc9 100644 --- a/src/gui/transferlistwidget.cpp +++ b/src/gui/transferlistwidget.cpp @@ -49,19 +49,19 @@ #include "transferlistwidget.h" #include "qbtsession.h" -#include "torrentpersistentdata.h" +#include "core/torrentpersistentdata.h" #include "transferlistdelegate.h" #include "previewselect.h" #include "speedlimitdlg.h" #include "updownratiodlg.h" #include "options_imp.h" #include "mainwindow.h" -#include "preferences.h" +#include "core/preferences.h" #include "torrentmodel.h" #include "deletionconfirmationdlg.h" #include "propertieswidget.h" #include "iconprovider.h" -#include "fs_utils.h" +#include "core/fs_utils.h" #include "autoexpandabledialog.h" #include "transferlistsortmodel.h" diff --git a/src/gui/updownratiodlg.cpp b/src/gui/updownratiodlg.cpp index 78686544f..f5705fc39 100644 --- a/src/gui/updownratiodlg.cpp +++ b/src/gui/updownratiodlg.cpp @@ -31,7 +31,7 @@ #include "updownratiodlg.h" #include "ui_updownratiodlg.h" -#include "preferences.h" +#include "core/preferences.h" UpDownRatioDlg::UpDownRatioDlg(bool useDefault, qreal initialValue, qreal maxValue, QWidget *parent) diff --git a/src/searchengine/engineselectdlg.cpp b/src/searchengine/engineselectdlg.cpp index e8736fac8..5ededabb0 100644 --- a/src/searchengine/engineselectdlg.cpp +++ b/src/searchengine/engineselectdlg.cpp @@ -29,9 +29,9 @@ */ #include "engineselectdlg.h" -#include "downloadthread.h" -#include "fs_utils.h" -#include "misc.h" +#include "core/downloadthread.h" +#include "core/fs_utils.h" +#include "core/misc.h" #include "ico.h" #include "searchengine.h" #include "pluginsource.h" diff --git a/src/searchengine/searchengine.cpp b/src/searchengine/searchengine.cpp index 4293a7d11..40543eb4a 100644 --- a/src/searchengine/searchengine.cpp +++ b/src/searchengine/searchengine.cpp @@ -49,9 +49,9 @@ #include "searchengine.h" #include "qbtsession.h" -#include "fs_utils.h" -#include "misc.h" -#include "preferences.h" +#include "core/fs_utils.h" +#include "core/misc.h" +#include "core/preferences.h" #include "searchlistdelegate.h" #include "mainwindow.h" #include "iconprovider.h" diff --git a/src/searchengine/searchlistdelegate.h b/src/searchengine/searchlistdelegate.h index 98a55d085..ad07963c0 100644 --- a/src/searchengine/searchlistdelegate.h +++ b/src/searchengine/searchlistdelegate.h @@ -36,7 +36,7 @@ #include #include #include -#include "misc.h" +#include "core/misc.h" #include "searchengine.h" class SearchListDelegate: public QItemDelegate { diff --git a/src/searchengine/searchsortmodel.h b/src/searchengine/searchsortmodel.h index 7692128f3..bb5817b2a 100644 --- a/src/searchengine/searchsortmodel.h +++ b/src/searchengine/searchsortmodel.h @@ -2,7 +2,7 @@ #define SEARCHSORTMODEL_H #include -#include "misc.h" +#include "core/misc.h" class SearchSortModel : public QSortFilterProxyModel { Q_OBJECT diff --git a/src/searchengine/searchtab.cpp b/src/searchengine/searchtab.cpp index 558ee03d6..63ac09fc3 100644 --- a/src/searchengine/searchtab.cpp +++ b/src/searchengine/searchtab.cpp @@ -36,9 +36,9 @@ #include "searchtab.h" #include "searchlistdelegate.h" -#include "misc.h" +#include "core/misc.h" #include "searchengine.h" -#include "preferences.h" +#include "core/preferences.h" SearchTab::SearchTab(SearchEngine *parent) : QWidget(), parent(parent) { diff --git a/src/searchengine/supportedengines.h b/src/searchengine/supportedengines.h index 62c568d3b..e453f6b5a 100644 --- a/src/searchengine/supportedengines.h +++ b/src/searchengine/supportedengines.h @@ -41,8 +41,8 @@ #include #include -#include "fs_utils.h" -#include "preferences.h" +#include "core/fs_utils.h" +#include "core/preferences.h" class SearchCategories: public QObject, public QHash { Q_OBJECT diff --git a/src/webui/abstractwebapplication.cpp b/src/webui/abstractwebapplication.cpp index b325b7d8c..0adefc450 100644 --- a/src/webui/abstractwebapplication.cpp +++ b/src/webui/abstractwebapplication.cpp @@ -35,7 +35,7 @@ #include #include -#include "preferences.h" +#include "core/preferences.h" #include "websessiondata.h" #include "abstractwebapplication.h" diff --git a/src/webui/abstractwebapplication.h b/src/webui/abstractwebapplication.h index bac977fca..152a39f8a 100644 --- a/src/webui/abstractwebapplication.h +++ b/src/webui/abstractwebapplication.h @@ -32,9 +32,9 @@ #include #include #include -#include "http/types.h" -#include "http/responsebuilder.h" -#include "http/irequesthandler.h" +#include "core/http/types.h" +#include "core/http/responsebuilder.h" +#include "core/http/irequesthandler.h" struct WebSession; struct WebSessionData; diff --git a/src/webui/btjson.cpp b/src/webui/btjson.cpp index d74f97033..f682a4af2 100644 --- a/src/webui/btjson.cpp +++ b/src/webui/btjson.cpp @@ -29,9 +29,9 @@ */ #include "btjson.h" -#include "fs_utils.h" +#include "core/fs_utils.h" #include "qbtsession.h" -#include "torrentpersistentdata.h" +#include "core/torrentpersistentdata.h" #include "qtorrentfilter.h" #include "jsonutils.h" diff --git a/src/webui/prefjson.cpp b/src/webui/prefjson.cpp index e5e0709a3..02ac99d87 100644 --- a/src/webui/prefjson.cpp +++ b/src/webui/prefjson.cpp @@ -29,10 +29,10 @@ */ #include "prefjson.h" -#include "preferences.h" +#include "core/preferences.h" #include "qbtsession.h" -#include "scannedfoldersmodel.h" -#include "fs_utils.h" +#include "core/scannedfoldersmodel.h" +#include "core/fs_utils.h" #include #ifndef QT_NO_OPENSSL diff --git a/src/webui/qtorrentfilter.cpp b/src/webui/qtorrentfilter.cpp index 8a661bb6f..5cd62d531 100644 --- a/src/webui/qtorrentfilter.cpp +++ b/src/webui/qtorrentfilter.cpp @@ -26,7 +26,7 @@ * exception statement from your version. */ -#include "torrentpersistentdata.h" +#include "core/torrentpersistentdata.h" #include "qtorrentfilter.h" QTorrentFilter::QTorrentFilter(QString filter, QString label) diff --git a/src/webui/webapplication.cpp b/src/webui/webapplication.cpp index 90adb1519..92a55f179 100644 --- a/src/webui/webapplication.cpp +++ b/src/webui/webapplication.cpp @@ -37,9 +37,9 @@ // TODO: Drop GUI dependency! #include "iconprovider.h" #endif -#include "misc.h" -#include "fs_utils.h" -#include "preferences.h" +#include "core/misc.h" +#include "core/fs_utils.h" +#include "core/preferences.h" #include "btjson.h" #include "prefjson.h" #include "qbtsession.h" diff --git a/src/webui/webui.cpp b/src/webui/webui.cpp index 723675ab2..244f345d8 100644 --- a/src/webui/webui.cpp +++ b/src/webui/webui.cpp @@ -27,11 +27,11 @@ */ #include "webui.h" -#include "http/server.h" +#include "core/http/server.h" #include "webapplication.h" #include "core/net/dnsupdater.h" -#include "preferences.h" -#include "logger.h" +#include "core/preferences.h" +#include "core/logger.h" WebUI::WebUI(QObject *parent) : QObject(parent) { From 60c0939e05c898cdb00877d7cf79b99d14fb7438 Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Thu, 16 Apr 2015 20:08:30 +0300 Subject: [PATCH 07/12] Fix coding style (Issue #2192). Also split filesystemwatcher.h into .h/.cpp files. --- src/core/core.pri | 1 + src/core/filesystemwatcher.cpp | 278 ++++++++++++++++++++++++ src/core/filesystemwatcher.h | 299 +++----------------------- src/core/scannedfoldersmodel.cpp | 351 +++++++++++++++++-------------- src/core/scannedfoldersmodel.h | 72 ++++--- 5 files changed, 541 insertions(+), 460 deletions(-) create mode 100644 src/core/filesystemwatcher.cpp diff --git a/src/core/core.pri b/src/core/core.pri index 43d5ecfd2..6997ca78d 100644 --- a/src/core/core.pri +++ b/src/core/core.pri @@ -26,6 +26,7 @@ SOURCES += \ $$PWD/downloadthread.cpp \ $$PWD/scannedfoldersmodel.cpp \ $$PWD/torrentpersistentdata.cpp \ + $$PWD/filesystemwatcher.cpp \ $$PWD/misc.cpp \ $$PWD/fs_utils.cpp \ $$PWD/logger.cpp \ diff --git a/src/core/filesystemwatcher.cpp b/src/core/filesystemwatcher.cpp new file mode 100644 index 000000000..fb109545e --- /dev/null +++ b/src/core/filesystemwatcher.cpp @@ -0,0 +1,278 @@ +#include +#ifndef Q_OS_WIN +#include +#include +#include +#if defined(Q_OS_MAC) || defined(Q_OS_FREEBSD) +#include +#include +#include +#elif !defined Q_OS_HAIKU +#include +#endif +#endif + +#include "fs_utils.h" +#include "misc.h" + +#ifndef CIFS_MAGIC_NUMBER +#define CIFS_MAGIC_NUMBER 0xFF534D42 +#endif + +#ifndef NFS_SUPER_MAGIC +#define NFS_SUPER_MAGIC 0x6969 +#endif + +#ifndef SMB_SUPER_MAGIC +#define SMB_SUPER_MAGIC 0x517B +#endif + +const int WATCH_INTERVAL = 10000; // 10 sec +const int MAX_PARTIAL_RETRIES = 5; + +#include "filesystemwatcher.h" + +FileSystemWatcher::FileSystemWatcher(QObject *parent) + : QFileSystemWatcher(parent) +{ + m_filters << "*.torrent" << "*.magnet"; + connect(this, SIGNAL(directoryChanged(QString)), SLOT(scanLocalFolder(QString))); +} + +FileSystemWatcher::~FileSystemWatcher() +{ +#ifndef Q_OS_WIN + if (m_watchTimer) + delete m_watchTimer; +#endif + if (m_partialTorrentTimer) + delete m_partialTorrentTimer; +} + +QStringList FileSystemWatcher::directories() const +{ + QStringList dirs; +#ifndef Q_OS_WIN + if (m_watchTimer) { + foreach (const QDir &dir, m_watchedFolders) + dirs << dir.canonicalPath(); + } +#endif + dirs << QFileSystemWatcher::directories(); + return dirs; +} + +void FileSystemWatcher::addPath(const QString &path) +{ +#if !defined Q_OS_WIN && !defined Q_OS_HAIKU + QDir dir(path); + if (!dir.exists()) return; + + // Check if the path points to a network file system or not + if (isNetworkFileSystem(path)) { + // Network mode + qDebug("Network folder detected: %s", qPrintable(path)); + qDebug("Using file polling mode instead of inotify..."); + m_watchedFolders << dir; + // Set up the watch timer + if (!m_watchTimer) { + m_watchTimer = new QTimer(this); + connect(m_watchTimer, SIGNAL(timeout()), SLOT(scanNetworkFolders())); + m_watchTimer->start(WATCH_INTERVAL); // 5 sec + } + } + else { +#endif + // Normal mode + qDebug("FS Watching is watching %s in normal mode", qPrintable(path)); + QFileSystemWatcher::addPath(path); + scanLocalFolder(path); +#if !defined Q_OS_WIN && !defined Q_OS_HAIKU + } +#endif +} + +void FileSystemWatcher::removePath(const QString &path) +{ +#ifndef Q_OS_WIN + QDir dir(path); + for (int i = 0; i < m_watchedFolders.count(); ++i) { + if (QDir(m_watchedFolders.at(i)) == dir) { + m_watchedFolders.removeAt(i); + if (m_watchedFolders.isEmpty()) + delete m_watchTimer; + return; + } + } +#endif + // Normal mode + QFileSystemWatcher::removePath(path); +} + +void FileSystemWatcher::scanLocalFolder(QString path) +{ + qDebug("scanLocalFolder(%s) called", qPrintable(path)); + QStringList torrents; + // Local folders scan + addTorrentsFromDir(QDir(path), torrents); + // Report detected torrent files + if (!torrents.empty()) { + qDebug("The following files are being reported: %s", qPrintable(torrents.join("\n"))); + emit torrentsAdded(torrents); + } +} + +void FileSystemWatcher::scanNetworkFolders() +{ +#ifndef Q_OS_WIN + qDebug("scanNetworkFolders() called"); + QStringList torrents; + // Network folders scan + foreach (const QDir &dir, m_watchedFolders) { + //qDebug("FSWatcher: Polling manually folder %s", qPrintable(dir.path())); + addTorrentsFromDir(dir, torrents); + } + // Report detected torrent files + if (!torrents.empty()) { + qDebug("The following files are being reported: %s", qPrintable(torrents.join("\n"))); + emit torrentsAdded(torrents); + } +#endif +} + +void FileSystemWatcher::processPartialTorrents() +{ + QStringList noLongerPartial; + + // Check which torrents are still partial + foreach (const QString &torrentPath, m_partialTorrents.keys()) { + if (!QFile::exists(torrentPath)) { + m_partialTorrents.remove(torrentPath); + } + else if (fsutils::isValidTorrentFile(torrentPath)) { + noLongerPartial << torrentPath; + m_partialTorrents.remove(torrentPath); + } + else if (m_partialTorrents[torrentPath] >= MAX_PARTIAL_RETRIES) { + m_partialTorrents.remove(torrentPath); + QFile::rename(torrentPath, torrentPath + ".invalid"); + } + else { + ++m_partialTorrents[torrentPath]; + } + } + + // Stop the partial timer if necessary + if (m_partialTorrents.empty()) { + m_partialTorrentTimer->stop(); + m_partialTorrentTimer->deleteLater(); + qDebug("No longer any partial torrent."); + } + else { + qDebug("Still %d partial torrents after delayed processing.", m_partialTorrents.count()); + m_partialTorrentTimer->start(WATCH_INTERVAL); + } + + // Notify of new torrents + if (!noLongerPartial.isEmpty()) + emit torrentsAdded(noLongerPartial); +} + +void FileSystemWatcher::startPartialTorrentTimer() +{ + Q_ASSERT(!m_partialTorrents.isEmpty()); + if (!m_partialTorrentTimer) { + m_partialTorrentTimer = new QTimer(); + connect(m_partialTorrentTimer, SIGNAL(timeout()), SLOT(processPartialTorrents())); + m_partialTorrentTimer->setSingleShot(true); + m_partialTorrentTimer->start(WATCH_INTERVAL); + } +} + +void FileSystemWatcher::addTorrentsFromDir(const QDir &dir, QStringList &torrents) +{ + const QStringList files = dir.entryList(m_filters, QDir::Files, QDir::Unsorted); + foreach (const QString &file, files) { + const QString fileAbsPath = dir.absoluteFilePath(file); + if (fileAbsPath.endsWith(".magnet")) { + QFile f(fileAbsPath); + if (f.open(QIODevice::ReadOnly) + && !misc::magnetUriToHash(QString::fromLocal8Bit(f.readAll())).isEmpty()) { + torrents << fileAbsPath; + } + } + else if (fsutils::isValidTorrentFile(fileAbsPath)) { + torrents << fileAbsPath; + } + else if (!m_partialTorrents.contains(fileAbsPath)) { + qDebug("Partial torrent detected at: %s", qPrintable(fileAbsPath)); + qDebug("Delay the file's processing..."); + m_partialTorrents.insert(fileAbsPath, 0); + } + } + + if (!m_partialTorrents.empty()) + startPartialTorrentTimer(); +} + +#if !defined Q_OS_WIN && !defined Q_OS_HAIKU +bool FileSystemWatcher::isNetworkFileSystem(QString path) +{ + QString file = path; + if (!file.endsWith("/")) + file += "/"; + file += "."; + struct statfs buf; + if (!statfs(file.toLocal8Bit().constData(), &buf)) { +#ifdef Q_OS_MAC + // XXX: should we make sure HAVE_STRUCT_FSSTAT_F_FSTYPENAME is defined? + return ((strcmp(buf.f_fstypename, "nfs") == 0) || (strcmp(buf.f_fstypename, "cifs") == 0) || (strcmp(buf.f_fstypename, "smbfs") == 0)); +#else + return ((buf.f_type == (long)CIFS_MAGIC_NUMBER) || (buf.f_type == (long)NFS_SUPER_MAGIC) || (buf.f_type == (long)SMB_SUPER_MAGIC)); +#endif + } + else { + std::cerr << "Error: statfs() call failed for " << qPrintable(file) << ". Supposing it is a local folder..." << std::endl; + switch(errno) { + case EACCES: + std::cerr << "Search permission is denied for a component of the path prefix of the path" << std::endl; + break; + case EFAULT: + std::cerr << "Buf or path points to an invalid address" << std::endl; + break; + case EINTR: + std::cerr << "This call was interrupted by a signal" << std::endl; + break; + case EIO: + std::cerr << "I/O Error" << std::endl; + break; + case ELOOP: + std::cerr << "Too many symlinks" << std::endl; + break; + case ENAMETOOLONG: + std::cerr << "path is too long" << std::endl; + break; + case ENOENT: + std::cerr << "The file referred by path does not exist" << std::endl; + break; + case ENOMEM: + std::cerr << "Insufficient kernel memory" << std::endl; + break; + case ENOSYS: + std::cerr << "The file system does not detect this call" << std::endl; + break; + case ENOTDIR: + std::cerr << "A component of the path is not a directory" << std::endl; + break; + case EOVERFLOW: + std::cerr << "Some values were too large to be represented in the struct" << std::endl; + break; + default: + std::cerr << "Unknown error" << std::endl; + } + + std::cerr << "Errno: " << errno << std::endl; + return false; + } +} +#endif diff --git a/src/core/filesystemwatcher.h b/src/core/filesystemwatcher.h index 2a821d2e3..a6eda5046 100644 --- a/src/core/filesystemwatcher.h +++ b/src/core/filesystemwatcher.h @@ -8,290 +8,45 @@ #include #include -#ifndef Q_OS_WIN -#include -#include -#include -#if defined(Q_OS_MAC) || defined(Q_OS_FREEBSD) -#include -#include -#include -#elif !defined Q_OS_HAIKU -#include -#endif -#endif - -#include "fs_utils.h" -#include "misc.h" - -#ifndef CIFS_MAGIC_NUMBER -#define CIFS_MAGIC_NUMBER 0xFF534D42 -#endif - -#ifndef NFS_SUPER_MAGIC -#define NFS_SUPER_MAGIC 0x6969 -#endif - -#ifndef SMB_SUPER_MAGIC -#define SMB_SUPER_MAGIC 0x517B -#endif - -const int WATCH_INTERVAL = 10000; // 10 sec -const int MAX_PARTIAL_RETRIES = 5; - /* * Subclassing QFileSystemWatcher in order to support Network File * System watching (NFS, CIFS) on Linux and Mac OS. */ -class FileSystemWatcher: public QFileSystemWatcher { - Q_OBJECT - -private: -#ifndef Q_OS_WIN - QList watched_folders; - QPointer watch_timer; -#endif - QStringList m_filters; - // Partial torrents - QHash m_partialTorrents; - QPointer m_partialTorrentTimer; - -#if !defined Q_OS_WIN && !defined Q_OS_HAIKU -private: - static bool isNetworkFileSystem(QString path) { - QString file = path; - if (!file.endsWith("/")) - file += "/"; - file += "."; - struct statfs buf; - if (!statfs(file.toLocal8Bit().constData(), &buf)) { -#ifdef Q_OS_MAC - // XXX: should we make sure HAVE_STRUCT_FSSTAT_F_FSTYPENAME is defined? - return (strcmp(buf.f_fstypename, "nfs") == 0 || strcmp(buf.f_fstypename, "cifs") == 0 || strcmp(buf.f_fstypename, "smbfs") == 0); -#else - return (buf.f_type == (long)CIFS_MAGIC_NUMBER || buf.f_type == (long)NFS_SUPER_MAGIC || buf.f_type == (long)SMB_SUPER_MAGIC); -#endif - } else { - std::cerr << "Error: statfs() call failed for " << qPrintable(file) << ". Supposing it is a local folder..." << std::endl; - switch(errno) { - case EACCES: - std::cerr << "Search permission is denied for a component of the path prefix of the path" << std::endl; - break; - case EFAULT: - std::cerr << "Buf or path points to an invalid address" << std::endl; - break; - case EINTR: - std::cerr << "This call was interrupted by a signal" << std::endl; - break; - case EIO: - std::cerr << "I/O Error" << std::endl; - break; - case ELOOP: - std::cerr << "Too many symlinks" << std::endl; - break; - case ENAMETOOLONG: - std::cerr << "path is too long" << std::endl; - break; - case ENOENT: - std::cerr << "The file referred by path does not exist" << std::endl; - break; - case ENOMEM: - std::cerr << "Insufficient kernel memory" << std::endl; - break; - case ENOSYS: - std::cerr << "The file system does not detect this call" << std::endl; - break; - case ENOTDIR: - std::cerr << "A component of the path is not a directory" << std::endl; - break; - case EOVERFLOW: - std::cerr << "Some values were too large to be represented in the struct" << std::endl; - break; - default: - std::cerr << "Unknown error" << std::endl; - } - std::cerr << "Errno: " << errno << std::endl; - return false; - } - - } -#endif +class FileSystemWatcher : public QFileSystemWatcher +{ + Q_OBJECT public: - FileSystemWatcher(QObject *parent): QFileSystemWatcher(parent) { - m_filters << "*.torrent" << "*.magnet"; - connect(this, SIGNAL(directoryChanged(QString)), this, SLOT(scanLocalFolder(QString))); - } + explicit FileSystemWatcher(QObject *parent = 0); + ~FileSystemWatcher(); - ~FileSystemWatcher() { -#ifndef Q_OS_WIN - if (watch_timer) - delete watch_timer; -#endif - if (m_partialTorrentTimer) - delete m_partialTorrentTimer; - } - - QStringList directories() const { - QStringList dirs; -#ifndef Q_OS_WIN - if (watch_timer) { - foreach (const QDir &dir, watched_folders) - dirs << dir.canonicalPath(); - } -#endif - dirs << QFileSystemWatcher::directories(); - return dirs; - } - - void addPath(const QString & path) { -#if !defined Q_OS_WIN && !defined Q_OS_HAIKU - QDir dir(path); - if (!dir.exists()) - return; - // Check if the path points to a network file system or not - if (isNetworkFileSystem(path)) { - // Network mode - qDebug("Network folder detected: %s", qPrintable(path)); - qDebug("Using file polling mode instead of inotify..."); - watched_folders << dir; - // Set up the watch timer - if (!watch_timer) { - watch_timer = new QTimer(this); - connect(watch_timer, SIGNAL(timeout()), this, SLOT(scanNetworkFolders())); - watch_timer->start(WATCH_INTERVAL); // 5 sec - } - } else { -#endif - // Normal mode - qDebug("FS Watching is watching %s in normal mode", qPrintable(path)); - QFileSystemWatcher::addPath(path); - scanLocalFolder(path); -#if !defined Q_OS_WIN && !defined Q_OS_HAIKU - } -#endif - } - - void removePath(const QString & path) { -#ifndef Q_OS_WIN - QDir dir(path); - for (int i = 0; i < watched_folders.count(); ++i) { - if (QDir(watched_folders.at(i)) == dir) { - watched_folders.removeAt(i); - if (watched_folders.isEmpty()) - delete watch_timer; - return; - } - } -#endif - // Normal mode - QFileSystemWatcher::removePath(path); - } - -protected slots: - void scanLocalFolder(QString path) { - qDebug("scanLocalFolder(%s) called", qPrintable(path)); - QStringList torrents; - // Local folders scan - addTorrentsFromDir(QDir(path), torrents); - // Report detected torrent files - if (!torrents.empty()) { - qDebug("The following files are being reported: %s", qPrintable(torrents.join("\n"))); - emit torrentsAdded(torrents); - } - } - - void scanNetworkFolders() { -#ifndef Q_OS_WIN - qDebug("scanNetworkFolders() called"); - QStringList torrents; - // Network folders scan - foreach (const QDir &dir, watched_folders) { - //qDebug("FSWatcher: Polling manually folder %s", qPrintable(dir.path())); - addTorrentsFromDir(dir, torrents); - } - // Report detected torrent files - if (!torrents.empty()) { - qDebug("The following files are being reported: %s", qPrintable(torrents.join("\n"))); - emit torrentsAdded(torrents); - } -#endif - } - - void processPartialTorrents() { - QStringList no_longer_partial; - - // Check which torrents are still partial - foreach (const QString& torrent_path, m_partialTorrents.keys()) { - if (!QFile::exists(torrent_path)) { - m_partialTorrents.remove(torrent_path); - continue; - } - if (fsutils::isValidTorrentFile(torrent_path)) { - no_longer_partial << torrent_path; - m_partialTorrents.remove(torrent_path); - } else { - if (m_partialTorrents[torrent_path] >= MAX_PARTIAL_RETRIES) { - m_partialTorrents.remove(torrent_path); - QFile::rename(torrent_path, torrent_path+".invalid"); - } else { - m_partialTorrents[torrent_path]++; - } - } - } - - // Stop the partial timer if necessary - if (m_partialTorrents.empty()) { - m_partialTorrentTimer->stop(); - m_partialTorrentTimer->deleteLater(); - qDebug("No longer any partial torrent."); - } else { - qDebug("Still %d partial torrents after delayed processing.", m_partialTorrents.count()); - m_partialTorrentTimer->start(WATCH_INTERVAL); - } - // Notify of new torrents - if (!no_longer_partial.isEmpty()) - emit torrentsAdded(no_longer_partial); - } + QStringList directories() const; + void addPath(const QString &path); + void removePath(const QString &path); signals: - void torrentsAdded(QStringList &pathList); + void torrentsAdded(QStringList &pathList); + +protected slots: + void scanLocalFolder(QString path); + void scanNetworkFolders(); + void processPartialTorrents(); private: - void startPartialTorrentTimer() { - Q_ASSERT(!m_partialTorrents.isEmpty()); - if (!m_partialTorrentTimer) { - m_partialTorrentTimer = new QTimer(); - connect(m_partialTorrentTimer, SIGNAL(timeout()), SLOT(processPartialTorrents())); - m_partialTorrentTimer->setSingleShot(true); - m_partialTorrentTimer->start(WATCH_INTERVAL); - } - } - - void addTorrentsFromDir(const QDir &dir, QStringList &torrents) { - const QStringList files = dir.entryList(m_filters, QDir::Files, QDir::Unsorted); - foreach (const QString &file, files) { - const QString file_abspath = dir.absoluteFilePath(file); - if (file_abspath.endsWith(".magnet")) { - QFile f(file_abspath); - if (f.open(QIODevice::ReadOnly) - && !misc::magnetUriToHash(QString::fromLocal8Bit(f.readAll())).isEmpty()) { - torrents << file_abspath; - } - } else if (fsutils::isValidTorrentFile(file_abspath)) { - torrents << file_abspath; - } else { - if (!m_partialTorrents.contains(file_abspath)) { - qDebug("Partial torrent detected at: %s", qPrintable(file_abspath)); - qDebug("Delay the file's processing..."); - m_partialTorrents.insert(file_abspath, 0); - } - } - } - if (!m_partialTorrents.empty()) - startPartialTorrentTimer(); - } + void startPartialTorrentTimer(); + void addTorrentsFromDir(const QDir &dir, QStringList &torrents); +#if !defined Q_OS_WIN && !defined Q_OS_HAIKU + static bool isNetworkFileSystem(QString path); +#endif +#ifndef Q_OS_WIN + QList m_watchedFolders; + QPointer m_watchTimer; +#endif + QStringList m_filters; + // Partial torrents + QHash m_partialTorrents; + QPointer m_partialTorrentTimer; }; #endif // FILESYSTEMWATCHER_H diff --git a/src/core/scannedfoldersmodel.cpp b/src/core/scannedfoldersmodel.cpp index b0158f5ec..261609634 100644 --- a/src/core/scannedfoldersmodel.cpp +++ b/src/core/scannedfoldersmodel.cpp @@ -1,5 +1,5 @@ /* - * Bittorrent Client using Qt4 and libtorrent. + * Bittorrent Client using Qt and libtorrent. * Copyright (C) 2010 Christian Kandeler, Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -28,171 +28,206 @@ * Contact : chris@qbittorrent.org */ -#include "scannedfoldersmodel.h" -#include "preferences.h" -#include "filesystemwatcher.h" - #include #include #include +#include #include -#include "misc.h" -namespace { - const int PathColumn = 0; - const int DownloadAtTorrentColumn = 1; +#include "misc.h" +#include "fs_utils.h" +#include "preferences.h" +#include "filesystemwatcher.h" +#include "scannedfoldersmodel.h" + +namespace +{ + const int PathColumn = 0; + const int DownloadAtTorrentColumn = 1; } -class ScanFoldersModel::PathData { +class ScanFoldersModel::PathData +{ public: - PathData(const QString &path) : path(path), downloadAtPath(false) {} - PathData(const QString &path, bool download_at_path) : path(path), downloadAtPath(download_at_path) {} - const QString path; - bool downloadAtPath; + PathData(const QString &path) + : path(path) + , downloadAtPath(false) + { + } + + PathData(const QString &path, bool download_at_path) + : path(path) + , downloadAtPath(download_at_path) + { + } + + const QString path; + bool downloadAtPath; }; -ScanFoldersModel *ScanFoldersModel::instance(QObject *parent) { - //Q_ASSERT(!parent != !m_instance); - if (!m_instance) - m_instance = new ScanFoldersModel(parent); - return m_instance; -} - -ScanFoldersModel::ScanFoldersModel(QObject *parent) : - QAbstractTableModel(parent), m_fsWatcher(0) -{ } - -ScanFoldersModel::~ScanFoldersModel() { - qDeleteAll(m_pathList); -} - -int ScanFoldersModel::rowCount(const QModelIndex &parent) const { - return parent.isValid() ? 0 : m_pathList.count(); -} - -int ScanFoldersModel::columnCount(const QModelIndex &parent) const { - Q_UNUSED(parent); - return 2; -} - -QVariant ScanFoldersModel::data(const QModelIndex &index, int role) const { - if (!index.isValid() || index.row() >= rowCount()) - return QVariant(); - - const PathData* pathData = m_pathList.at(index.row()); - if (index.column() == PathColumn && role == Qt::DisplayRole) { - - return fsutils::toNativePath(pathData->path); - } - if (index.column() == DownloadAtTorrentColumn && role == Qt::CheckStateRole) - return pathData->downloadAtPath ? Qt::Checked : Qt::Unchecked; - return QVariant(); -} - -QVariant ScanFoldersModel::headerData(int section, Qt::Orientation orientation, int role) const { - if (orientation != Qt::Horizontal || role != Qt::DisplayRole || section < 0 || section >= columnCount()) - return QVariant(); - - if (section == PathColumn) - return tr("Watched Folder"); - return tr("Download here"); -} - -Qt::ItemFlags ScanFoldersModel::flags(const QModelIndex &index) const { - if (!index.isValid() || index.row() >= rowCount() || index.column() != DownloadAtTorrentColumn) - return QAbstractTableModel::flags(index); - return QAbstractTableModel::flags(index) | Qt::ItemIsUserCheckable; -} - -bool ScanFoldersModel::setData(const QModelIndex &index, const QVariant &value, int role) { - if (!index.isValid() || index.row() >= rowCount() || index.column() > DownloadAtTorrentColumn || role != Qt::CheckStateRole) - return false; - Q_ASSERT(index.column() == DownloadAtTorrentColumn); - m_pathList[index.row()]->downloadAtPath = (value.toInt() == Qt::Checked); - emit dataChanged(index, index); - return true; -} - -ScanFoldersModel::PathStatus ScanFoldersModel::addPath(const QString &path, bool download_at_path) { - QDir dir(path); - if (!dir.exists()) - return DoesNotExist; - if (!dir.isReadable()) - return CannotRead; - const QString &canonicalPath = dir.canonicalPath(); - if (findPathData(canonicalPath) != -1) - return AlreadyInList; - if (!m_fsWatcher) { - m_fsWatcher = new FileSystemWatcher(this); - connect(m_fsWatcher, SIGNAL(torrentsAdded(QStringList&)), this, SIGNAL(torrentsAdded(QStringList&))); - } - beginInsertRows(QModelIndex(), rowCount(), rowCount()); - m_pathList << new PathData(canonicalPath, download_at_path); - endInsertRows(); - // Start scanning - m_fsWatcher->addPath(canonicalPath); - return Ok; -} - -void ScanFoldersModel::removePath(int row) { - Q_ASSERT(row >= 0 && row < rowCount()); - beginRemoveRows(QModelIndex(), row, row); - m_fsWatcher->removePath(m_pathList.at(row)->path); - m_pathList.removeAt(row); - endRemoveRows(); -} - -bool ScanFoldersModel::removePath(const QString &path) { - const int row = findPathData(path); - if (row == -1) - return false; - removePath(row); - return true; -} - -ScanFoldersModel::PathStatus ScanFoldersModel::setDownloadAtPath(int row, bool downloadAtPath) { - Q_ASSERT(row >= 0 && row < rowCount()); - - bool &oldValue = m_pathList[row]->downloadAtPath; - if (oldValue != downloadAtPath) { - if (downloadAtPath) { - QTemporaryFile testFile(m_pathList[row]->path + "/tmpFile"); - if (!testFile.open()) - return CannotWrite; - } - oldValue = downloadAtPath; - const QModelIndex changedIndex = index(row, DownloadAtTorrentColumn); - emit dataChanged(changedIndex, changedIndex); - } - return Ok; -} - -bool ScanFoldersModel::downloadInTorrentFolder(const QString &filePath) const { - const int row = findPathData(QFileInfo(filePath).dir().path()); - Q_ASSERT(row != -1); - return m_pathList.at(row)->downloadAtPath; -} - -int ScanFoldersModel::findPathData(const QString &path) const { - for (int i = 0; i < m_pathList.count(); ++i) { - const PathData* pathData = m_pathList.at(i); - if (pathData->path == fsutils::fromNativePath(path)) - return i; - } - - return -1; -} - -void ScanFoldersModel::makePersistent() { - Preferences* const pref = Preferences::instance(); - QStringList paths; - QList downloadInFolderInfo; - foreach (const PathData* pathData, m_pathList) { - paths << pathData->path; - downloadInFolderInfo << pathData->downloadAtPath; - } - pref->setScanDirs(paths); - pref->setDownloadInScanDirs(downloadInFolderInfo); -} - ScanFoldersModel *ScanFoldersModel::m_instance = 0; + +ScanFoldersModel *ScanFoldersModel::instance(QObject *parent) +{ + //Q_ASSERT(!parent != !m_instance); + if (!m_instance) + m_instance = new ScanFoldersModel(parent); + return m_instance; +} + +ScanFoldersModel::ScanFoldersModel(QObject *parent) + : QAbstractTableModel(parent) + , m_fsWatcher(0) +{ +} + +ScanFoldersModel::~ScanFoldersModel() +{ + qDeleteAll(m_pathList); +} + +int ScanFoldersModel::rowCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : m_pathList.count(); +} + +int ScanFoldersModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return 2; +} + +QVariant ScanFoldersModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || (index.row() >= rowCount())) + return QVariant(); + + const PathData *pathData = m_pathList.at(index.row()); + if ((index.column() == PathColumn) && (role == Qt::DisplayRole)) + return fsutils::toNativePath(pathData->path); + + if ((index.column() == DownloadAtTorrentColumn) && (role == Qt::CheckStateRole)) + return (pathData->downloadAtPath ? Qt::Checked : Qt::Unchecked); + + return QVariant(); +} + +QVariant ScanFoldersModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if ((orientation != Qt::Horizontal) || (role != Qt::DisplayRole) || (section < 0) || (section >= columnCount())) + return QVariant(); + + if (section == PathColumn) + return tr("Watched Folder"); + + return tr("Download here"); +} + +Qt::ItemFlags ScanFoldersModel::flags(const QModelIndex &index) const +{ + if (!index.isValid() || (index.row() >= rowCount()) || (index.column() != DownloadAtTorrentColumn)) + return QAbstractTableModel::flags(index); + + return (QAbstractTableModel::flags(index) | Qt::ItemIsUserCheckable); +} + +bool ScanFoldersModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid() || (index.row() >= rowCount()) || (index.column() > DownloadAtTorrentColumn) || (role != Qt::CheckStateRole)) + return false; + + Q_ASSERT(index.column() == DownloadAtTorrentColumn); + m_pathList[index.row()]->downloadAtPath = (value.toInt() == Qt::Checked); + emit dataChanged(index, index); + return true; +} + +ScanFoldersModel::PathStatus ScanFoldersModel::addPath(const QString &path, bool downloadAtPath) +{ + QDir dir(path); + if (!dir.exists()) return DoesNotExist; + if (!dir.isReadable()) return CannotRead; + + const QString &canonicalPath = dir.canonicalPath(); + if (findPathData(canonicalPath) != -1) return AlreadyInList; + + if (!m_fsWatcher) { + m_fsWatcher = new FileSystemWatcher(this); + connect(m_fsWatcher, SIGNAL(torrentsAdded(QStringList&)), this, SIGNAL(torrentsAdded(QStringList&))); + } + + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + m_pathList << new PathData(canonicalPath, downloadAtPath); + endInsertRows(); + + // Start scanning + m_fsWatcher->addPath(canonicalPath); + return Ok; +} + +void ScanFoldersModel::removePath(int row) +{ + Q_ASSERT((row >= 0) && (row < rowCount())); + beginRemoveRows(QModelIndex(), row, row); + m_fsWatcher->removePath(m_pathList.at(row)->path); + m_pathList.removeAt(row); + endRemoveRows(); +} + +bool ScanFoldersModel::removePath(const QString &path) +{ + const int row = findPathData(path); + if (row == -1) return false; + + removePath(row); + return true; +} + +ScanFoldersModel::PathStatus ScanFoldersModel::setDownloadAtPath(int row, bool downloadAtPath) +{ + Q_ASSERT((row >= 0) && (row < rowCount())); + + bool &oldValue = m_pathList[row]->downloadAtPath; + if (oldValue != downloadAtPath) { + if (downloadAtPath) { + QTemporaryFile testFile(m_pathList[row]->path + "/tmpFile"); + if (!testFile.open()) return CannotWrite; + } + + oldValue = downloadAtPath; + const QModelIndex changedIndex = index(row, DownloadAtTorrentColumn); + emit dataChanged(changedIndex, changedIndex); + } + + return Ok; +} + +bool ScanFoldersModel::downloadInTorrentFolder(const QString &filePath) const +{ + const int row = findPathData(QFileInfo(filePath).dir().path()); + Q_ASSERT(row != -1); + return m_pathList.at(row)->downloadAtPath; +} + +int ScanFoldersModel::findPathData(const QString &path) const +{ + for (int i = 0; i < m_pathList.count(); ++i) + if (m_pathList.at(i)->path == fsutils::fromNativePath(path)) + return i; + + return -1; +} + +void ScanFoldersModel::makePersistent() +{ + Preferences *const pref = Preferences::instance(); + QStringList paths; + QList downloadInFolderInfo; + foreach (const PathData *pathData, m_pathList) { + paths << pathData->path; + downloadInFolderInfo << pathData->downloadAtPath; + } + + pref->setScanDirs(paths); + pref->setDownloadInScanDirs(downloadInFolderInfo); +} diff --git a/src/core/scannedfoldersmodel.h b/src/core/scannedfoldersmodel.h index ab2f51960..ad17602b3 100644 --- a/src/core/scannedfoldersmodel.h +++ b/src/core/scannedfoldersmodel.h @@ -1,5 +1,5 @@ /* - * Bittorrent Client using Qt4 and libtorrent. + * Bittorrent Client using Qt and libtorrent. * Copyright (C) 2010 Christian Kandeler, Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -33,48 +33,60 @@ #include #include -#include + +QT_BEGIN_NAMESPACE +class QStringList; +QT_END_NAMESPACE class FileSystemWatcher; -class ScanFoldersModel : public QAbstractTableModel { - Q_OBJECT - Q_DISABLE_COPY(ScanFoldersModel) +class ScanFoldersModel : public QAbstractTableModel +{ + Q_OBJECT + Q_DISABLE_COPY(ScanFoldersModel) public: - enum PathStatus { Ok, DoesNotExist, CannotRead, CannotWrite, AlreadyInList }; - static ScanFoldersModel *instance(QObject *parent = 0); - virtual ~ScanFoldersModel(); + enum PathStatus + { + Ok, + DoesNotExist, + CannotRead, + CannotWrite, + AlreadyInList + }; - 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; + static ScanFoldersModel *instance(QObject *parent = 0); + virtual ~ScanFoldersModel(); - // TODO: removePaths(); singular version becomes private helper functions; - // also: remove functions should take modelindexes - PathStatus addPath(const QString &path, bool download_at_path); - void removePath(int row); - bool removePath(const QString &path); - PathStatus setDownloadAtPath(int row, bool downloadAtPath); + 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; - bool downloadInTorrentFolder(const QString &filePath) const; - void makePersistent(); + // TODO: removePaths(); singular version becomes private helper functions; + // also: remove functions should take modelindexes + PathStatus addPath(const QString &path, bool downloadAtPath); + void removePath(int row); + bool removePath(const QString &path); + PathStatus setDownloadAtPath(int row, bool downloadAtPath); + + bool downloadInTorrentFolder(const QString &filePath) const; + void makePersistent(); signals: - // The absolute paths of new torrent files in the scanned directories. - void torrentsAdded(QStringList &pathList); + // The absolute paths of new torrent files in the scanned directories. + void torrentsAdded(QStringList &pathList); private: - explicit ScanFoldersModel(QObject *parent); - virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); - static ScanFoldersModel *m_instance; - class PathData; - int findPathData(const QString &path) const; + explicit ScanFoldersModel(QObject *parent = 0); + virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + static ScanFoldersModel *m_instance; + class PathData; + int findPathData(const QString &path) const; - QList m_pathList; - FileSystemWatcher *m_fsWatcher; + QList m_pathList; + FileSystemWatcher *m_fsWatcher; }; #endif // SCANNEDFOLDERSMODEL_H From d16d1fdb3a6bdd5d9b08d6ed44e7436b15211eea Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Sun, 19 Apr 2015 18:17:47 +0300 Subject: [PATCH 08/12] Redesign main core classes. --- src/app/application.cpp | 164 +- src/app/application.h | 11 + src/app/main.cpp | 3 - src/core/bittorrent/cachestatus.cpp | 64 + src/core/bittorrent/cachestatus.h | 53 + src/core/bittorrent/infohash.cpp | 98 + src/core/bittorrent/infohash.h | 67 + src/core/bittorrent/magneturi.cpp | 95 + src/core/bittorrent/magneturi.h | 68 + src/core/bittorrent/peerinfo.cpp | 271 ++ src/core/bittorrent/peerinfo.h | 99 + .../bittorrent/private/bandwidthscheduler.cpp | 96 + .../bittorrent/private/bandwidthscheduler.h | 50 + .../bittorrent/private/filterparserthread.cpp | 439 +++ .../private}/filterparserthread.h | 48 +- src/core/bittorrent/private/sessionprivate.h | 83 + src/core/bittorrent/private/speedmonitor.cpp | 56 + src/core/bittorrent/private/speedmonitor.h | 84 + src/core/bittorrent/private/statistics.cpp | 115 + src/core/bittorrent/private/statistics.h | 41 + .../bittorrent/private/torrenthandleprivate.h | 53 + src/core/bittorrent/session.cpp | 2440 +++++++++++++ src/core/bittorrent/session.h | 365 ++ src/core/bittorrent/sessionstatus.cpp | 96 + src/core/bittorrent/sessionstatus.h | 69 + src/core/bittorrent/torrentcreatorthread.cpp | 166 + src/core/bittorrent/torrentcreatorthread.h | 73 + src/core/bittorrent/torrenthandle.cpp | 1887 ++++++++++ src/core/bittorrent/torrenthandle.h | 380 ++ src/core/bittorrent/torrentinfo.cpp | 230 ++ src/core/bittorrent/torrentinfo.h | 88 + .../{qtracker.cpp => bittorrent/tracker.cpp} | 143 +- src/core/bittorrent/tracker.h | 103 + src/core/bittorrent/trackerentry.cpp | 93 + src/core/bittorrent/trackerentry.h | 68 + src/core/core.pri | 65 +- src/core/filesystemwatcher.cpp | 14 +- src/core/filesystemwatcher.h | 2 +- src/core/fs_utils.cpp | 56 - src/core/fs_utils.h | 3 - src/core/iconprovider.cpp | 64 + src/core/iconprovider.h | 55 + src/core/logger.cpp | 13 +- src/core/logger.h | 8 +- src/core/misc.cpp | 81 +- src/core/misc.h | 33 +- src/core/net/downloadhandler.cpp | 5 +- src/core/net/downloadhandler.h | 1 + src/core/net/portforwarder.cpp | 160 + src/core/net/portforwarder.h | 84 + src/core/preferences.cpp | 34 +- src/core/preferences.h | 28 +- src/core/qtlibtorrent/bandwidthscheduler.h | 79 - src/core/qtlibtorrent/filterparserthread.cpp | 394 -- src/core/qtlibtorrent/qbtsession.cpp | 3162 ----------------- src/core/qtlibtorrent/qbtsession.h | 352 -- src/core/qtlibtorrent/qtlibtorrent.pri | 17 - src/core/qtlibtorrent/qtorrenthandle.cpp | 856 ----- src/core/qtlibtorrent/qtorrenthandle.h | 170 - src/core/qtracker.h | 98 - ...dfoldersmodel.cpp => scanfoldersmodel.cpp} | 72 +- ...annedfoldersmodel.h => scanfoldersmodel.h} | 29 +- src/core/torrentfilter.cpp | 159 + src/core/torrentfilter.h | 93 + src/core/torrentpersistentdata.cpp | 527 --- src/core/torrentpersistentdata.h | 181 - src/core/tristatebool.cpp | 60 + src/core/tristatebool.h | 53 + src/core/types.h | 63 + src/core/utils/string.cpp | 43 + src/core/utils/string.h | 43 + src/gui/addnewtorrentdialog.cpp | 393 +- src/gui/addnewtorrentdialog.h | 22 +- src/gui/advancedsettings.h | 2 +- src/gui/{torrentcreator => }/createtorrent.ui | 0 src/gui/deletionconfirmationdlg.h | 6 +- src/gui/executionlog.cpp | 6 +- src/gui/geoip/geoipmanager.cpp | 16 +- src/gui/geoip/geoipmanager.h | 4 +- src/gui/gui.pri | 15 +- src/gui/guiiconprovider.cpp | 125 + src/gui/guiiconprovider.h | 62 + src/gui/loglistwidget.cpp | 6 +- src/gui/mainwindow.cpp | 322 +- src/gui/mainwindow.h | 28 +- src/gui/options_imp.cpp | 32 +- src/gui/previewselect.cpp | 25 +- src/gui/previewselect.h | 7 +- src/gui/properties/downloadedpiecesbar.cpp | 65 +- src/gui/properties/downloadedpiecesbar.h | 24 +- src/gui/properties/peeraddition.h | 102 +- src/gui/properties/peerlistwidget.cpp | 280 +- src/gui/properties/peerlistwidget.h | 37 +- src/gui/properties/pieceavailabilitybar.cpp | 53 +- src/gui/properties/pieceavailabilitybar.h | 18 +- src/gui/properties/propertieswidget.cpp | 279 +- src/gui/properties/propertieswidget.h | 19 +- src/gui/properties/proplistdelegate.h | 4 +- src/gui/properties/proptabbar.cpp | 12 +- src/gui/properties/trackerlist.cpp | 266 +- src/gui/properties/trackerlist.h | 13 +- src/gui/properties/trackersadditiondlg.h | 56 +- src/gui/rss/automatedrssdownloader.cpp | 20 +- src/gui/rss/cookiesdlg.cpp | 6 +- src/gui/rss/feedlistwidget.cpp | 4 +- src/gui/rss/rss_imp.cpp | 54 +- src/gui/rss/rssfeed.cpp | 49 +- src/gui/rss/rssfeed.h | 5 +- src/gui/rss/rssfolder.cpp | 6 +- src/gui/rss/rssmanager.cpp | 9 +- src/gui/rss/rssmanager.h | 3 - src/gui/rss/rssparser.cpp | 2 +- src/gui/shutdownconfirm.cpp | 4 +- src/gui/shutdownconfirm.h | 6 +- src/gui/speedlimitdlg.h | 2 +- src/gui/statsdialog.cpp | 64 +- src/gui/statsdialog.h | 2 - src/gui/statusbar.cpp | 42 +- src/gui/torrentcontentmodel.cpp | 42 +- src/gui/torrentcontentmodel.h | 11 +- src/gui/torrentcontentmodelfile.cpp | 17 +- src/gui/torrentcontentmodelfile.h | 7 +- src/gui/torrentcontentmodelfolder.cpp | 13 +- src/gui/torrentcontentmodelitem.cpp | 19 +- src/gui/torrentcontentmodelitem.h | 8 +- src/gui/torrentcreator/torrentcreator.pri | 10 - .../torrentcreatordlg.cpp | 72 +- .../{torrentcreator => }/torrentcreatordlg.h | 12 +- src/gui/torrentimportdlg.cpp | 95 +- src/gui/torrentimportdlg.h | 21 +- src/gui/torrentmodel.cpp | 504 +-- src/gui/torrentmodel.h | 58 +- src/gui/trackerlogin.cpp | 13 +- src/gui/trackerlogin.h | 12 +- src/gui/transferlistdelegate.cpp | 55 +- src/gui/transferlistfilterswidget.cpp | 139 +- src/gui/transferlistfilterswidget.h | 21 +- src/gui/transferlistsortmodel.cpp | 175 +- src/gui/transferlistsortmodel.h | 19 +- src/gui/transferlistwidget.cpp | 406 +-- src/gui/transferlistwidget.h | 16 +- src/searchengine/engineselectdlg.cpp | 35 +- src/searchengine/engineselectdlg.h | 9 +- src/searchengine/searchengine.cpp | 20 +- src/src.pro | 4 +- src/webui/btjson.cpp | 279 +- src/webui/btjson.h | 2 - src/webui/prefjson.cpp | 11 +- src/webui/webapplication.cpp | 200 +- src/webui/webui.cpp | 32 +- src/webui/webui.h | 1 + src/webui/webui.pri | 2 - 152 files changed, 11366 insertions(+), 8967 deletions(-) create mode 100644 src/core/bittorrent/cachestatus.cpp create mode 100644 src/core/bittorrent/cachestatus.h create mode 100644 src/core/bittorrent/infohash.cpp create mode 100644 src/core/bittorrent/infohash.h create mode 100644 src/core/bittorrent/magneturi.cpp create mode 100644 src/core/bittorrent/magneturi.h create mode 100644 src/core/bittorrent/peerinfo.cpp create mode 100644 src/core/bittorrent/peerinfo.h create mode 100644 src/core/bittorrent/private/bandwidthscheduler.cpp create mode 100644 src/core/bittorrent/private/bandwidthscheduler.h create mode 100644 src/core/bittorrent/private/filterparserthread.cpp rename src/core/{qtlibtorrent => bittorrent/private}/filterparserthread.h (76%) create mode 100644 src/core/bittorrent/private/sessionprivate.h create mode 100644 src/core/bittorrent/private/speedmonitor.cpp create mode 100644 src/core/bittorrent/private/speedmonitor.h create mode 100644 src/core/bittorrent/private/statistics.cpp create mode 100644 src/core/bittorrent/private/statistics.h create mode 100644 src/core/bittorrent/private/torrenthandleprivate.h create mode 100644 src/core/bittorrent/session.cpp create mode 100644 src/core/bittorrent/session.h create mode 100644 src/core/bittorrent/sessionstatus.cpp create mode 100644 src/core/bittorrent/sessionstatus.h create mode 100644 src/core/bittorrent/torrentcreatorthread.cpp create mode 100644 src/core/bittorrent/torrentcreatorthread.h create mode 100644 src/core/bittorrent/torrenthandle.cpp create mode 100644 src/core/bittorrent/torrenthandle.h create mode 100644 src/core/bittorrent/torrentinfo.cpp create mode 100644 src/core/bittorrent/torrentinfo.h rename src/core/{qtracker.cpp => bittorrent/tracker.cpp} (54%) create mode 100644 src/core/bittorrent/tracker.h create mode 100644 src/core/bittorrent/trackerentry.cpp create mode 100644 src/core/bittorrent/trackerentry.h create mode 100644 src/core/iconprovider.cpp create mode 100644 src/core/iconprovider.h create mode 100644 src/core/net/portforwarder.cpp create mode 100644 src/core/net/portforwarder.h delete mode 100644 src/core/qtlibtorrent/bandwidthscheduler.h delete mode 100644 src/core/qtlibtorrent/filterparserthread.cpp delete mode 100644 src/core/qtlibtorrent/qbtsession.cpp delete mode 100644 src/core/qtlibtorrent/qbtsession.h delete mode 100644 src/core/qtlibtorrent/qtlibtorrent.pri delete mode 100644 src/core/qtlibtorrent/qtorrenthandle.cpp delete mode 100644 src/core/qtlibtorrent/qtorrenthandle.h delete mode 100644 src/core/qtracker.h rename src/core/{scannedfoldersmodel.cpp => scanfoldersmodel.cpp} (76%) rename src/core/{scannedfoldersmodel.h => scanfoldersmodel.h} (78%) create mode 100644 src/core/torrentfilter.cpp create mode 100644 src/core/torrentfilter.h delete mode 100644 src/core/torrentpersistentdata.cpp delete mode 100644 src/core/torrentpersistentdata.h create mode 100644 src/core/tristatebool.cpp create mode 100644 src/core/tristatebool.h create mode 100644 src/core/types.h create mode 100644 src/core/utils/string.cpp create mode 100644 src/core/utils/string.h rename src/gui/{torrentcreator => }/createtorrent.ui (100%) create mode 100644 src/gui/guiiconprovider.cpp create mode 100644 src/gui/guiiconprovider.h delete mode 100644 src/gui/torrentcreator/torrentcreator.pri rename src/gui/{torrentcreator => }/torrentcreatordlg.cpp (80%) rename src/gui/{torrentcreator => }/torrentcreatordlg.h (94%) 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 From 427688cb34f5e10b398f5b5eb43f5ce04d5d071c Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Wed, 6 May 2015 14:53:16 +0300 Subject: [PATCH 09/12] Remove unused sources. --- src/core/downloadthread.cpp | 341 ------------------ src/core/downloadthread.h | 76 ---- src/core/qtlibtorrent/alertdispatcher.cpp | 135 ------- src/core/qtlibtorrent/alertdispatcher.h | 80 ---- src/core/qtlibtorrent/torrentspeedmonitor.cpp | 195 ---------- src/core/qtlibtorrent/torrentspeedmonitor.h | 62 ---- src/core/qtlibtorrent/torrentstatistics.cpp | 100 ----- src/core/qtlibtorrent/torrentstatistics.h | 41 --- src/core/qtlibtorrent/trackerinfos.h | 59 --- src/gui/iconprovider.cpp | 123 ------- src/gui/iconprovider.h | 63 ---- .../torrentcreator/torrentcreatorthread.cpp | 142 -------- src/gui/torrentcreator/torrentcreatorthread.h | 73 ---- src/gui/torrentfilterenum.h | 48 --- src/webui/qtorrentfilter.cpp | 145 -------- src/webui/qtorrentfilter.h | 67 ---- 16 files changed, 1750 deletions(-) delete mode 100644 src/core/downloadthread.cpp delete mode 100644 src/core/downloadthread.h delete mode 100644 src/core/qtlibtorrent/alertdispatcher.cpp delete mode 100644 src/core/qtlibtorrent/alertdispatcher.h delete mode 100644 src/core/qtlibtorrent/torrentspeedmonitor.cpp delete mode 100644 src/core/qtlibtorrent/torrentspeedmonitor.h delete mode 100644 src/core/qtlibtorrent/torrentstatistics.cpp delete mode 100644 src/core/qtlibtorrent/torrentstatistics.h delete mode 100644 src/core/qtlibtorrent/trackerinfos.h delete mode 100644 src/gui/iconprovider.cpp delete mode 100644 src/gui/iconprovider.h delete mode 100644 src/gui/torrentcreator/torrentcreatorthread.cpp delete mode 100644 src/gui/torrentcreator/torrentcreatorthread.h delete mode 100644 src/gui/torrentfilterenum.h delete mode 100644 src/webui/qtorrentfilter.cpp delete mode 100644 src/webui/qtorrentfilter.h diff --git a/src/core/downloadthread.cpp b/src/core/downloadthread.cpp deleted file mode 100644 index 5bd6414e7..000000000 --- a/src/core/downloadthread.cpp +++ /dev/null @@ -1,341 +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 "downloadthread.h" -#include "preferences.h" -#include "qinisettings.h" -#include "fs_utils.h" -#include - -/** Download Thread **/ - -DownloadThread::DownloadThread(QObject* parent) - : QObject(parent) -{ - connect(&m_networkManager, SIGNAL(finished (QNetworkReply*)), this, SLOT(processDlFinished(QNetworkReply*))); -#ifndef QT_NO_OPENSSL - connect(&m_networkManager, SIGNAL(sslErrors(QNetworkReply*, QList)), this, SLOT(ignoreSslErrors(QNetworkReply*, QList))); -#endif -} - -QByteArray DownloadThread::gUncompress(Bytef *inData, size_t len) -{ - if (len <= 4) { - qWarning("gUncompress: Input data is truncated"); - return QByteArray(); - } - - QByteArray result; - - z_stream strm; - static const int CHUNK_SIZE = 1024; - char out[CHUNK_SIZE]; - - /* allocate inflate state */ - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - strm.avail_in = len; - strm.next_in = inData; - - const int windowBits = 15; - const int ENABLE_ZLIB_GZIP = 32; - - int ret = inflateInit2(&strm, windowBits | ENABLE_ZLIB_GZIP); // gzip decoding - if (ret != Z_OK) - return QByteArray(); - - // run inflate() - do { - strm.avail_out = CHUNK_SIZE; - strm.next_out = reinterpret_cast(out); - - ret = inflate(&strm, Z_NO_FLUSH); - Q_ASSERT(ret != Z_STREAM_ERROR); // state not clobbered - - switch (ret) { - case Z_NEED_DICT: - case Z_DATA_ERROR: - case Z_MEM_ERROR: - (void) inflateEnd(&strm); - return QByteArray(); - } - - result.append(out, CHUNK_SIZE - strm.avail_out); - } - while (!strm.avail_out); - - // clean up and return - inflateEnd(&strm); - return result; -} - -void DownloadThread::processDlFinished(QNetworkReply *reply) -{ - QString url = reply->url().toString(); - qDebug("Download finished: %s", qPrintable(url)); - // Check if the request was successful - if (reply->error() != QNetworkReply::NoError) { - // Failure - qDebug("Download failure (%s), reason: %s", qPrintable(url), qPrintable(errorCodeToString(reply->error()))); - emit downloadFailure(url, errorCodeToString(reply->error())); - reply->deleteLater(); - return; - } - - // Check if the server ask us to redirect somewhere else - const QVariant redirection = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); - if (redirection.isValid()) { - // We should redirect - QUrl newUrl = redirection.toUrl(); - // Resolve relative urls - if (newUrl.isRelative()) - newUrl = reply->url().resolved(newUrl); - const QString newUrlString = newUrl.toString(); - qDebug("Redirecting from %s to %s", qPrintable(url), qPrintable(newUrlString)); - - // Redirect to magnet workaround - if (newUrlString.startsWith("magnet:", Qt::CaseInsensitive)) { - qDebug("Magnet redirect detected."); - reply->abort(); - emit magnetRedirect(newUrlString, url); - reply->deleteLater(); - return; - } - - m_redirectMapping.insert(newUrlString, url); - // redirecting with first cookies - downloadUrl(newUrlString, m_networkManager.cookieJar()->cookiesForUrl(url)); - reply->deleteLater(); - return; - } - - // Checking if it was redirected, restoring initial URL - if (m_redirectMapping.contains(url)) - url = m_redirectMapping.take(url); - - // Success - QTemporaryFile *tmpfile = new QTemporaryFile; - if (tmpfile->open()) { - tmpfile->setAutoRemove(false); - QString filePath = tmpfile->fileName(); - qDebug("Temporary filename is: %s", qPrintable(filePath)); - if (reply->isOpen() || reply->open(QIODevice::ReadOnly)) { - QByteArray replyData = reply->readAll(); - if (reply->rawHeader("Content-Encoding") == "gzip") { - // uncompress gzip reply - replyData = gUncompress(reinterpret_cast(replyData.data()), replyData.length()); - } - tmpfile->write(replyData); - tmpfile->close(); - // XXX: tmpfile needs to be deleted on Windows before using the file - // or it will complain that the file is used by another process. - delete tmpfile; - // Send finished signal - emit downloadFinished(url, filePath); - } - else { - delete tmpfile; - fsutils::forceRemove(filePath); - // Error when reading the request - emit downloadFailure(url, tr("I/O Error")); - } - } - else { - delete tmpfile; - emit downloadFailure(url, tr("I/O Error")); - } - - // Clean up - reply->deleteLater(); -} - -void DownloadThread::downloadTorrentUrl(const QString &url, const QList &cookies) -{ - // Process request - QNetworkReply *reply = downloadUrl(url, cookies); - connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(checkDownloadSize(qint64, qint64))); -} - -QNetworkReply *DownloadThread::downloadUrl(const QString &url, const QList &cookies) -{ - // Update proxy settings - applyProxySettings(); - - // Set cookies - if (!cookies.empty()) { - qDebug("Setting %d cookies for url: %s", cookies.size(), qPrintable(url)); - m_networkManager.cookieJar()->setCookiesFromUrl(cookies, url); - } - - // Process download request - qDebug("url is %s", qPrintable(url)); - const QUrl qurl = QUrl::fromEncoded(url.toUtf8()); - QNetworkRequest request(qurl); - - // Spoof Firefox 3.5 user agent to avoid - // Web server banning - request.setRawHeader("User-Agent", "Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5"); - - qDebug("Downloading %s...", request.url().toEncoded().data()); - qDebug("%d cookies for this URL", m_networkManager.cookieJar()->cookiesForUrl(url).size()); - for (int i = 0; i < m_networkManager.cookieJar()->cookiesForUrl(url).size(); ++i) { - qDebug("%s=%s", m_networkManager.cookieJar()->cookiesForUrl(url).at(i).name().data(), m_networkManager.cookieJar()->cookiesForUrl(url).at(i).value().data()); - qDebug("Domain: %s, Path: %s", qPrintable(m_networkManager.cookieJar()->cookiesForUrl(url).at(i).domain()), qPrintable(m_networkManager.cookieJar()->cookiesForUrl(url).at(i).path())); - } - - // accept gzip - request.setRawHeader("Accept-Encoding", "gzip"); - return m_networkManager.get(request); -} - -void DownloadThread::checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal) -{ - QNetworkReply *reply = qobject_cast(sender()); - if (!reply) return; - - if (bytesTotal > 0) { - // Total number of bytes is available - if (bytesTotal > 10485760) { - // More than 10MB, this is probably not a torrent file, aborting... - reply->abort(); - reply->deleteLater(); - } - else { - disconnect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(checkDownloadSize(qint64, qint64))); - } - } - else { - if (bytesReceived > 10485760) { - // More than 10MB, this is probably not a torrent file, aborting... - reply->abort(); - reply->deleteLater(); - } - } -} - -void DownloadThread::applyProxySettings() -{ - QNetworkProxy proxy; - const Preferences* const pref = Preferences::instance(); - - if (pref->isProxyEnabled()) { - // Proxy enabled - proxy.setHostName(pref->getProxyIp()); - proxy.setPort(pref->getProxyPort()); - // Default proxy type is HTTP, we must change if it is SOCKS5 - const int proxyType = pref->getProxyType(); - if ((proxyType == Proxy::SOCKS5) || (proxyType == Proxy::SOCKS5_PW)) { - qDebug() << Q_FUNC_INFO << "using SOCKS proxy"; - proxy.setType(QNetworkProxy::Socks5Proxy); - } - else { - qDebug() << Q_FUNC_INFO << "using HTTP proxy"; - proxy.setType(QNetworkProxy::HttpProxy); - } - // Authentication? - if (pref->isProxyAuthEnabled()) { - qDebug("Proxy requires authentication, authenticating"); - proxy.setUser(pref->getProxyUsername()); - proxy.setPassword(pref->getProxyPassword()); - } - } - else { - proxy.setType(QNetworkProxy::NoProxy); - } - - m_networkManager.setProxy(proxy); -} - -QString DownloadThread::errorCodeToString(QNetworkReply::NetworkError status) -{ - switch(status) { - case QNetworkReply::HostNotFoundError: - return tr("The remote host name was not found (invalid hostname)"); - case QNetworkReply::OperationCanceledError: - return tr("The operation was canceled"); - case QNetworkReply::RemoteHostClosedError: - return tr("The remote server closed the connection prematurely, before the entire reply was received and processed"); - case QNetworkReply::TimeoutError: - return tr("The connection to the remote server timed out"); - case QNetworkReply::SslHandshakeFailedError: - return tr("SSL/TLS handshake failed"); - case QNetworkReply::ConnectionRefusedError: - return tr("The remote server refused the connection"); - case QNetworkReply::ProxyConnectionRefusedError: - return tr("The connection to the proxy server was refused"); - case QNetworkReply::ProxyConnectionClosedError: - return tr("The proxy server closed the connection prematurely"); - case QNetworkReply::ProxyNotFoundError: - return tr("The proxy host name was not found"); - case QNetworkReply::ProxyTimeoutError: - return tr("The connection to the proxy timed out or the proxy did not reply in time to the request sent"); - case QNetworkReply::ProxyAuthenticationRequiredError: - return tr("The proxy requires authentication in order to honour the request but did not accept any credentials offered"); - case QNetworkReply::ContentAccessDenied: - return tr("The access to the remote content was denied (401)"); - case QNetworkReply::ContentOperationNotPermittedError: - return tr("The operation requested on the remote content is not permitted"); - case QNetworkReply::ContentNotFoundError: - return tr("The remote content was not found at the server (404)"); - case QNetworkReply::AuthenticationRequiredError: - return tr("The remote server requires authentication to serve the content but the credentials provided were not accepted"); - case QNetworkReply::ProtocolUnknownError: - return tr("The Network Access API cannot honor the request because the protocol is not known"); - case QNetworkReply::ProtocolInvalidOperationError: - return tr("The requested operation is invalid for this protocol"); - case QNetworkReply::UnknownNetworkError: - return tr("An unknown network-related error was detected"); - case QNetworkReply::UnknownProxyError: - return tr("An unknown proxy-related error was detected"); - case QNetworkReply::UnknownContentError: - return tr("An unknown error related to the remote content was detected"); - case QNetworkReply::ProtocolFailure: - return tr("A breakdown in protocol was detected"); - default: - return tr("Unknown error"); - } -} - -#ifndef QT_NO_OPENSSL -void DownloadThread::ignoreSslErrors(QNetworkReply *reply, const QList &errors) -{ - Q_UNUSED(errors) - // Ignore all SSL errors - reply->ignoreSslErrors(); -} -#endif diff --git a/src/core/downloadthread.h b/src/core/downloadthread.h deleted file mode 100644 index 8eeb61849..000000000 --- a/src/core/downloadthread.h +++ /dev/null @@ -1,76 +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 DOWNLOADTHREAD_H -#define DOWNLOADTHREAD_H - -#include -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE -class QNetworkAccessManager; -QT_END_NAMESPACE - -class DownloadThread : public QObject -{ - Q_OBJECT - -public: - DownloadThread(QObject *parent = 0); - QNetworkReply *downloadUrl(const QString &url, const QList &cookies = QList()); - void downloadTorrentUrl(const QString &url, const QList &cookies = QList()); - -signals: - void downloadFinished(const QString &url, const QString &file_path); - void downloadFailure(const QString &url, const QString &reason); - void magnetRedirect(const QString &url_new, const QString &url_old); - -private slots: - void processDlFinished(QNetworkReply *reply); - void checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal); -#ifndef QT_NO_OPENSSL - void ignoreSslErrors(QNetworkReply *,const QList &); -#endif - -private: - static QByteArray gUncompress(Bytef *inData, size_t len); - QString errorCodeToString(QNetworkReply::NetworkError status); - void applyProxySettings(); - - QNetworkAccessManager m_networkManager; - QHash m_redirectMapping; - -}; - -#endif diff --git a/src/core/qtlibtorrent/alertdispatcher.cpp b/src/core/qtlibtorrent/alertdispatcher.cpp deleted file mode 100644 index cf5618019..000000000 --- a/src/core/qtlibtorrent/alertdispatcher.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2014 Ivan Sorokin - * - * 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 : vanyacpp@gmail.com - */ - -#include "alertdispatcher.h" - -#include -#include -#include - -const size_t DEFAULT_ALERTS_CAPACITY = 32; - -struct QAlertDispatcher::Tag { - Tag(QAlertDispatcher* dispatcher); - - QAlertDispatcher* dispatcher; - QMutex alerts_mutex; -}; - -QAlertDispatcher::Tag::Tag(QAlertDispatcher* dispatcher) - : dispatcher(dispatcher) -{} - -QAlertDispatcher::QAlertDispatcher(libtorrent::session *session, QObject* parent) - : QObject(parent) - , m_session(session) - , current_tag(new Tag(this)) - , event_posted(false) -{ - alerts.reserve(DEFAULT_ALERTS_CAPACITY); - m_session->set_alert_dispatch(boost::bind(&QAlertDispatcher::dispatch, current_tag, _1)); -} - -QAlertDispatcher::~QAlertDispatcher() { - // When QAlertDispatcher is destoyed, libtorrent still can call - // QAlertDispatcher::dispatch a few times after destruction. This is - // handled by passing a "tag". A tag is a object that references QAlertDispatch. - // Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag - // and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called - // with invalid tag it simply discard an alert. - - { - QMutexLocker lock(¤t_tag->alerts_mutex); - current_tag->dispatcher = 0; - current_tag.clear(); - } - - typedef boost::function)> dispatch_function_t; - m_session->set_alert_dispatch(dispatch_function_t()); -} - -void QAlertDispatcher::getPendingAlertsNoWait(std::vector& out) { - Q_ASSERT(out.empty()); - out.reserve(DEFAULT_ALERTS_CAPACITY); - - QMutexLocker lock(¤t_tag->alerts_mutex); - alerts.swap(out); - event_posted = false; -} - -void QAlertDispatcher::getPendingAlerts(std::vector& out, unsigned long time) { - Q_ASSERT(out.empty()); - out.reserve(DEFAULT_ALERTS_CAPACITY); - - QMutexLocker lock(¤t_tag->alerts_mutex); - - while (alerts.empty()) - alerts_condvar.wait(¤t_tag->alerts_mutex, time); - - alerts.swap(out); - event_posted = false; -} - -void QAlertDispatcher::dispatch(QSharedPointer tag, - std::auto_ptr alert_ptr) { - QMutexLocker lock(&(tag->alerts_mutex)); - QAlertDispatcher* that = tag->dispatcher; - if (!that) - return; - - bool was_empty = that->alerts.empty(); - - that->alerts.push_back(alert_ptr.get()); - alert_ptr.release(); - - if (was_empty) - that->alerts_condvar.wakeAll(); - - that->enqueueToMainThread(); - - Q_ASSERT(that->current_tag == tag); -} - -void QAlertDispatcher::enqueueToMainThread() { - if (!event_posted) { - event_posted = true; - QMetaObject::invokeMethod(this, "deliverSignal", Qt::QueuedConnection); - } -} - -void QAlertDispatcher::deliverSignal() { - emit alertsReceived(); - - QMutexLocker lock(¤t_tag->alerts_mutex); - event_posted = false; - - if (!alerts.empty()) - enqueueToMainThread(); -} diff --git a/src/core/qtlibtorrent/alertdispatcher.h b/src/core/qtlibtorrent/alertdispatcher.h deleted file mode 100644 index e77f22474..000000000 --- a/src/core/qtlibtorrent/alertdispatcher.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2014 Ivan Sorokin - * - * 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 : vanyacpp@gmail.com - */ - -#ifndef ALERTDISPATCHER_H -#define ALERTDISPATCHER_H - -#include -#include -#include -#include -#include - -#include -#include - -namespace libtorrent { -class session; -class alert; -} - -class QAlertDispatcher : public QObject { - Q_OBJECT - Q_DISABLE_COPY(QAlertDispatcher) - - struct Tag; - -public: - QAlertDispatcher(libtorrent::session *session, QObject* parent); - ~QAlertDispatcher(); - - void getPendingAlertsNoWait(std::vector&); - void getPendingAlerts(std::vector&, unsigned long time = ULONG_MAX); - -signals: - void alertsReceived(); - -private: - static void dispatch(QSharedPointer, - std::auto_ptr); - void enqueueToMainThread(); - -private slots: - void deliverSignal(); - -private: - libtorrent::session *m_session; - QWaitCondition alerts_condvar; - std::vector alerts; - QSharedPointer current_tag; - bool event_posted; -}; - -#endif // ALERTDISPATCHER_H diff --git a/src/core/qtlibtorrent/torrentspeedmonitor.cpp b/src/core/qtlibtorrent/torrentspeedmonitor.cpp deleted file mode 100644 index 1cb6640e2..000000000 --- a/src/core/qtlibtorrent/torrentspeedmonitor.cpp +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Bittorrent Client using Qt4 and libtorrent. - * 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. - * - * Contact : chris@qbittorrent.org - */ - -#include - -#include "qbtsession.h" -#include "core/misc.h" -#include "torrentspeedmonitor.h" - -using namespace libtorrent; - -namespace { - -template struct Sample { - Sample() - : download() - , upload() - {} - - Sample(T download, T upload) - : download(download) - , upload(upload) - {} - - template - explicit Sample(Sample other) - : download(static_cast(other.download)) - , upload(static_cast(other.upload)) - {} - - T download; - T upload; -}; - -template -Sample& operator+=(Sample& lhs, Sample const& rhs) { - lhs.download += rhs.download; - lhs.upload += rhs.upload; - return lhs; -} - -template -Sample& operator-=(Sample& lhs, Sample const& rhs) { - lhs.download -= rhs.download; - lhs.upload -= rhs.upload; - return lhs; -} - -template -Sample operator+(Sample const& lhs, Sample const& rhs) { - return Sample(lhs.download + rhs.download, lhs.upload + rhs.upload); -} - -template -Sample operator-(Sample const& lhs, Sample const& rhs) { - return Sample(lhs.download - rhs.download, lhs.upload - rhs.upload); -} - -template -Sample operator*(Sample const& lhs, T rhs) { - return Sample(lhs.download * rhs, lhs.upload * rhs); -} - -template -Sample operator*(T lhs,Sample const& rhs) { - return Sample(lhs * rhs.download, lhs * rhs.upload); -} - -template -Sample operator/(Sample const& lhs, T rhs) { - return Sample(lhs.download / rhs, lhs.upload / rhs); -} -} - -class SpeedSample { - -public: - SpeedSample() {} - void addSample(Sample const& item); - Sample average() const; - -private: - static const int max_samples = 30; - -private: - QList > m_speedSamples; - Sample m_sum; -}; - -TorrentSpeedMonitor::TorrentSpeedMonitor(QBtSession* session) - : m_session(session) -{ - connect(m_session, SIGNAL(torrentAboutToBeRemoved(QTorrentHandle)), SLOT(removeSamples(QTorrentHandle))); - connect(m_session, SIGNAL(pausedTorrent(QTorrentHandle)), SLOT(removeSamples(QTorrentHandle))); - connect(m_session, SIGNAL(statsReceived(libtorrent::stats_alert)), SLOT(statsReceived(libtorrent::stats_alert))); -} - -TorrentSpeedMonitor::~TorrentSpeedMonitor() -{} - -void SpeedSample::addSample(Sample const& item) -{ - m_speedSamples.push_back(item); - m_sum += Sample(item); - if (m_speedSamples.size() > max_samples) { - m_sum -= Sample(m_speedSamples.front()); - m_speedSamples.pop_front(); - } -} - -Sample SpeedSample::average() const -{ - if (m_speedSamples.empty()) - return Sample(); - - return Sample(m_sum) * (qreal(1.) / m_speedSamples.size()); -} - -void TorrentSpeedMonitor::removeSamples(const QTorrentHandle& h) { - try { - m_samples.remove(h.hash()); - } catch(invalid_handle&) {} -} - -qlonglong TorrentSpeedMonitor::getETA(const QString &hash, const libtorrent::torrent_status &status) const -{ - if (QTorrentHandle::is_paused(status)) - return MAX_ETA; - - QHash::const_iterator i = m_samples.find(hash); - if (i == m_samples.end()) - return MAX_ETA; - - const Sample speed_average = i->average(); - - if (QTorrentHandle::is_seed(status)) { - if (!speed_average.upload) - return MAX_ETA; - - bool _unused; - qreal max_ratio = m_session->getMaxRatioPerTorrent(hash, &_unused); - if (max_ratio < 0) - return MAX_ETA; - - libtorrent::size_type realDL = status.all_time_download; - if (realDL <= 0) - realDL = status.total_wanted; - - return (realDL * max_ratio - status.all_time_upload) / speed_average.upload; - } - - if (!speed_average.download) - return MAX_ETA; - - return (status.total_wanted - status.total_wanted_done) / speed_average.download; -} - -void TorrentSpeedMonitor::statsReceived(const stats_alert &stats) -{ - Q_ASSERT(stats.interval >= 1000); - - Sample transferred(stats.transferred[stats_alert::download_payload], - stats.transferred[stats_alert::upload_payload]); - - Sample normalized = Sample(Sample(transferred) * 1000LL / static_cast(stats.interval)); - - m_samples[misc::toQString(stats.handle.info_hash())].addSample(normalized); -} diff --git a/src/core/qtlibtorrent/torrentspeedmonitor.h b/src/core/qtlibtorrent/torrentspeedmonitor.h deleted file mode 100644 index cd0c69a92..000000000 --- a/src/core/qtlibtorrent/torrentspeedmonitor.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Bittorrent Client using Qt4 and libtorrent. - * 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. - * - * Contact : chris@qbittorrent.org - */ - -#ifndef TORRENTSPEEDMONITOR_H -#define TORRENTSPEEDMONITOR_H - -#include -#include -#include -#include "qtorrenthandle.h" -#include - -class QBtSession; -class SpeedSample; - -class TorrentSpeedMonitor : public QObject -{ - Q_OBJECT - Q_DISABLE_COPY(TorrentSpeedMonitor) - -public: - explicit TorrentSpeedMonitor(QBtSession* session); - ~TorrentSpeedMonitor(); - qlonglong getETA(const QString &hash, const libtorrent::torrent_status &status) const; - -private slots: - void statsReceived(const libtorrent::stats_alert& stats); - void removeSamples(const QTorrentHandle& h); - -private: - QHash m_samples; - QBtSession *m_session; -}; - -#endif // TORRENTSPEEDMONITOR_H diff --git a/src/core/qtlibtorrent/torrentstatistics.cpp b/src/core/qtlibtorrent/torrentstatistics.cpp deleted file mode 100644 index 436c9c37c..000000000 --- a/src/core/qtlibtorrent/torrentstatistics.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include "torrentstatistics.h" - -#include - -#include - -#include "qbtsession.h" -#include "core/qinisettings.h" -#include "core/preferences.h" - -TorrentStatistics::TorrentStatistics(QBtSession* session, QObject* parent) - : QObject(parent) - , m_session(session) - , m_sessionUL(0) - , m_sessionDL(0) - , m_lastWrite(0) - , m_dirty(false) -{ - loadStats(); - connect(&m_timer, SIGNAL(timeout()), this, SLOT(gatherStats())); - m_timer.start(60 * 1000); -} - -TorrentStatistics::~TorrentStatistics() { - if (m_dirty) - m_lastWrite = 0; - saveStats(); -} - -quint64 TorrentStatistics::getAlltimeDL() const { - return m_alltimeDL + m_sessionDL; -} - -quint64 TorrentStatistics::getAlltimeUL() const { - return m_alltimeUL + m_sessionUL; -} - -void TorrentStatistics::gatherStats() { - libtorrent::session_status ss = m_session->getSessionStatus(); - if (ss.total_download > m_sessionDL) { - m_sessionDL = ss.total_download; - m_dirty = true; - } - if (ss.total_upload > m_sessionUL) { - m_sessionUL = ss.total_upload; - m_dirty = true; - } - - saveStats(); -} - -void TorrentStatistics::saveStats() const { - if (!(m_dirty && (QDateTime::currentMSecsSinceEpoch() - m_lastWrite >= 15*60*1000) )) - 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 = QDateTime::currentMSecsSinceEpoch(); -} - -void TorrentStatistics::loadStats() { - // 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) { - saveStats(); - pref->removeStats(); - } -} diff --git a/src/core/qtlibtorrent/torrentstatistics.h b/src/core/qtlibtorrent/torrentstatistics.h deleted file mode 100644 index 76751241f..000000000 --- a/src/core/qtlibtorrent/torrentstatistics.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef TORRENTSTATISTICS_H -#define TORRENTSTATISTICS_H - -#include -#include - -class QBtSession; - -class TorrentStatistics : QObject -{ - Q_OBJECT - Q_DISABLE_COPY(TorrentStatistics) - -public: - TorrentStatistics(QBtSession* session, QObject* parent = 0); - ~TorrentStatistics(); - - quint64 getAlltimeDL() const; - quint64 getAlltimeUL() const; - -private slots: - void gatherStats(); - -private: - void saveStats() const; - void loadStats(); - -private: - QBtSession* 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 // TORRENTSTATISTICS_H diff --git a/src/core/qtlibtorrent/trackerinfos.h b/src/core/qtlibtorrent/trackerinfos.h deleted file mode 100644 index f222e5a2e..000000000 --- a/src/core/qtlibtorrent/trackerinfos.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Bittorrent Client using Qt4 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 TRACKERINFOS_H -#define TRACKERINFOS_H - -#include - -class TrackerInfos { -public: - QString name_or_url; - QString last_message; - unsigned long num_peers; - - //TrackerInfos() {} - TrackerInfos(const TrackerInfos &b) - : name_or_url(b.name_or_url) - , last_message(b.last_message) - , num_peers(b.num_peers) - { - Q_ASSERT(!name_or_url.isEmpty()); - } - - TrackerInfos(QString name_or_url) - : name_or_url(name_or_url) - , last_message("") - , num_peers(0) - { - } -}; - -#endif // TRACKERINFOS_H diff --git a/src/gui/iconprovider.cpp b/src/gui/iconprovider.cpp deleted file mode 100644 index 3d79e2787..000000000 --- a/src/gui/iconprovider.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Bittorrent Client using Qt4 and libtorrent. - * 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. - * - * Contact : chris@qbittorrent.org - */ - -#include "iconprovider.h" -#include "core/preferences.h" - -#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) -#include -#include -#endif - -IconProvider* IconProvider::m_instance = 0; - -IconProvider::IconProvider() -{ -#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) - m_useSystemTheme = Preferences::instance()->useSystemIconTheme(); -#endif -} - -IconProvider * IconProvider::instance() -{ - if (!m_instance) - m_instance = new IconProvider; - return m_instance; -} - -void IconProvider::drop() -{ - if (m_instance) { - delete m_instance; - m_instance = 0; - } -} - -QIcon IconProvider::getIcon(const QString &iconId) -{ -#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) - if (m_useSystemTheme) { - QIcon icon = QIcon::fromTheme(iconId, QIcon(":/icons/oxygen/"+iconId+".png")); - icon = generateDifferentSizes(icon); - return icon; - } -#endif - return QIcon(":/icons/oxygen/"+iconId+".png"); -} - -#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) -void IconProvider::useSystemIconTheme(bool enable) -{ - m_useSystemTheme = enable; -} - -// 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. -QIcon IconProvider::generateDifferentSizes(const QIcon& icon) -{ - QIcon new_icon; - QList required_sizes; - required_sizes << QSize(16, 16) << QSize(24, 24) << QSize(32, 32); - QList modes; - modes << QIcon::Normal << QIcon::Active << QIcon::Selected << QIcon::Disabled; - foreach (const QSize& size, required_sizes) { - 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); - new_icon.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); - new_icon.addPixmap(pixon, mode, QIcon::On); - } - } - return new_icon; -} -#endif - -QString IconProvider::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()) return ":/icons/oxygen/"+iconId+".png"; - QPixmap px = icon.pixmap(32); - px.save(path); - } - return path; - } -#endif - return ":/icons/oxygen/"+iconId+".png"; -} diff --git a/src/gui/iconprovider.h b/src/gui/iconprovider.h deleted file mode 100644 index 0f56df273..000000000 --- a/src/gui/iconprovider.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Bittorrent Client using Qt4 and libtorrent. - * 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. - * - * Contact : chris@qbittorrent.org - */ - -#ifndef ICONPROVIDER_H -#define ICONPROVIDER_H - -#include -#include - -class IconProvider -{ - Q_DISABLE_COPY(IconProvider) - -private: - explicit IconProvider(); - static IconProvider* m_instance; - -public: - static IconProvider* instance(); - static void drop(); - QIcon getIcon(const QString& iconId); - QString getIconPath(const QString& iconId); - -#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) -public: - void useSystemIconTheme(bool enable); - -private: - QIcon generateDifferentSizes(const QIcon& icon); - -private: - bool m_useSystemTheme; -#endif -}; - -#endif // ICONPROVIDER_H diff --git a/src/gui/torrentcreator/torrentcreatorthread.cpp b/src/gui/torrentcreator/torrentcreatorthread.cpp deleted file mode 100644 index 43853befd..000000000 --- a/src/gui/torrentcreator/torrentcreatorthread.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Bittorrent Client using Qt4 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 "torrentcreatorthread.h" -#include "core/fs_utils.h" -#include "core/misc.h" - -#include -#include -#include - -using namespace libtorrent; - -// do not include files and folders whose -// name starts with a . -bool file_filter(std::string const& f) -{ - if (filename(f)[0] == '.') return false; - return true; -} - -void TorrentCreatorThread::create(QString _input_path, QString _save_path, QStringList _trackers, QStringList _url_seeds, QString _comment, bool _is_private, int _piece_size) -{ - input_path = fsutils::fromNativePath(_input_path); - save_path = fsutils::fromNativePath(_save_path); - if (QFile(save_path).exists()) - fsutils::forceRemove(save_path); - trackers = _trackers; - url_seeds = _url_seeds; - comment = _comment; - is_private = _is_private; - piece_size = _piece_size; - abort = false; - start(); -} - -void sendProgressUpdateSignal(int i, int num, TorrentCreatorThread *parent) { - parent->sendProgressSignal((int)(i*100./(float)num)); -} - -void TorrentCreatorThread::sendProgressSignal(int progress) { - emit updateProgress(progress); -} - -void TorrentCreatorThread::run() { - emit updateProgress(0); - QString creator_str("qBittorrent " VERSION); - try { - file_storage fs; - // Adding files to the torrent - libtorrent::add_files(fs, fsutils::toNativePath(input_path).toUtf8().constData(), file_filter); - if (abort) return; - create_torrent t(fs, piece_size); - - // Add url seeds - foreach (const QString &seed, url_seeds) { - t.add_url_seed(seed.trimmed().toStdString()); - } - int tier = 0; - bool newline = false; - foreach (const QString &tracker, trackers) { - if (tracker.isEmpty()) { - if (newline) - continue; - ++tier; - newline = true; - continue; - } - t.add_tracker(tracker.trimmed().toStdString(), tier); - newline = false; - } - if (abort) return; - // calculate the hash for all pieces - const QString parent_path = fsutils::branchPath(input_path) + "/"; - set_piece_hashes(t, fsutils::toNativePath(parent_path).toUtf8().constData(), boost::bind(sendProgressUpdateSignal, _1, t.num_pieces(), this)); - // Set qBittorrent as creator and add user comment to - // torrent_info structure - t.set_creator(creator_str.toUtf8().constData()); - t.set_comment(comment.toUtf8().constData()); - // Is private ? - t.set_priv(is_private); - if (abort) return; - // create the torrent and print it to out - qDebug("Saving to %s", qPrintable(save_path)); -#ifdef _MSC_VER - wchar_t *wsave_path = new wchar_t[save_path.length()+1]; - int len = fsutils::toNativePath(save_path).toWCharArray(wsave_path); - wsave_path[len] = '\0'; - std::ofstream outfile(wsave_path, std::ios_base::out|std::ios_base::binary); - delete[] wsave_path; -#else - std::ofstream outfile(fsutils::toNativePath(save_path).toLocal8Bit().constData(), std::ios_base::out|std::ios_base::binary); -#endif - if (outfile.fail()) - throw std::exception(); - bencode(std::ostream_iterator(outfile), t.generate()); - outfile.close(); - emit updateProgress(100); - emit creationSuccess(save_path, parent_path); - } catch (std::exception& e) { - emit creationFailure(misc::toQStringU(e.what())); - } -} diff --git a/src/gui/torrentcreator/torrentcreatorthread.h b/src/gui/torrentcreator/torrentcreatorthread.h deleted file mode 100644 index 3e87008d6..000000000 --- a/src/gui/torrentcreator/torrentcreatorthread.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Bittorrent Client using Qt4 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 TORRENTCREATORTHREAD_H -#define TORRENTCREATORTHREAD_H - -#include -#include -#include - -class TorrentCreatorThread : public QThread { - Q_OBJECT - -public: - TorrentCreatorThread(QDialog *_parent) { - parent = _parent; - } - ~TorrentCreatorThread() { - abort = true; - wait(); - } - void create(QString _input_path, QString _save_path, QStringList _trackers, QStringList _url_seeds, QString _comment, bool _is_private, int _piece_size); - void sendProgressSignal(int progress); - void abortCreation() { abort = true; } - -protected: - void run(); - -signals: - void creationFailure(QString msg); - void creationSuccess(QString path, QString branch_path); - void updateProgress(int progress); - -private: - QString input_path; - QString save_path; - QStringList trackers; - QStringList url_seeds; - QString comment; - bool is_private; - int piece_size; - bool abort; - QDialog *parent; -}; - -#endif // TORRENTCREATORTHREAD_H diff --git a/src/gui/torrentfilterenum.h b/src/gui/torrentfilterenum.h deleted file mode 100644 index 8ba7cbbcd..000000000 --- a/src/gui/torrentfilterenum.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2014 sledgehammer999 - * - * 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 : hammered999@gmail.com - */ - -#ifndef TORRENTFILTERENUM_H -#define TORRENTFILTERENUM_H - -namespace TorrentFilter -{ - enum TorrentFilter - { - ALL, - DOWNLOADING, - SEEDING, - COMPLETED, - RESUMED, - PAUSED, - ACTIVE, - INACTIVE - }; -} -#endif // TORRENTFILTERENUM_H diff --git a/src/webui/qtorrentfilter.cpp b/src/webui/qtorrentfilter.cpp deleted file mode 100644 index 5cd62d531..000000000 --- a/src/webui/qtorrentfilter.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/* - * 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 "core/torrentpersistentdata.h" -#include "qtorrentfilter.h" - -QTorrentFilter::QTorrentFilter(QString filter, QString label) - : type_(All) - , label_(label) -{ - 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; -} - -bool QTorrentFilter::apply(const QTorrentHandle& h) const -{ - if (!torrentHasLabel(h)) - return false; - - switch (type_) { - case Downloading: - return isTorrentDownloading(h); - case Seeding: - return isTorrentSeeding(h); - case Completed: - return isTorrentCompleted(h); - case Paused: - return isTorrentPaused(h); - case Resumed: - return isTorrentResumed(h); - case Active: - return isTorrentActive(h); - case Inactive: - return isTorrentInactive(h); - default: // All - return true; - } -} - -bool QTorrentFilter::isTorrentDownloading(const QTorrentHandle &h) const -{ - const QTorrentState state = h.torrentState(); - - return state == QTorrentState::Downloading - || state == QTorrentState::StalledDownloading - || state == QTorrentState::CheckingDownloading - || state == QTorrentState::PausedDownloading - || state == QTorrentState::QueuedDownloading - || state == QTorrentState::Error; -} - -bool QTorrentFilter::isTorrentSeeding(const QTorrentHandle &h) const -{ - const QTorrentState state = h.torrentState(); - - return state == QTorrentState::Uploading - || state == QTorrentState::StalledUploading - || state == QTorrentState::CheckingUploading - || state == QTorrentState::QueuedUploading; -} - -bool QTorrentFilter::isTorrentCompleted(const QTorrentHandle &h) const -{ - const QTorrentState state = h.torrentState(); - - return state == QTorrentState::Uploading - || state == QTorrentState::StalledUploading - || state == QTorrentState::CheckingUploading - || state == QTorrentState::PausedUploading - || state == QTorrentState::QueuedUploading; -} - -bool QTorrentFilter::isTorrentPaused(const QTorrentHandle &h) const -{ - const QTorrentState state = h.torrentState(); - - return state == QTorrentState::PausedUploading - || state == QTorrentState::Error; -} - -bool QTorrentFilter::isTorrentResumed(const QTorrentHandle &h) const -{ - const QTorrentState state = h.torrentState(); - - return state != QTorrentState::PausedUploading - && state != QTorrentState::PausedDownloading; -} - -bool QTorrentFilter::isTorrentActive(const QTorrentHandle &h) const -{ - const QTorrentState state = h.torrentState(); - - return state == QTorrentState::Downloading - || state == QTorrentState::Uploading; -} - -bool QTorrentFilter::isTorrentInactive(const QTorrentHandle &h) const -{ - return !isTorrentActive(h); -} - -bool QTorrentFilter::torrentHasLabel(const QTorrentHandle &h) const -{ - if (label_.isNull()) - return true; - else - return TorrentPersistentData::instance()->getLabel(h.hash()) == label_; -} diff --git a/src/webui/qtorrentfilter.h b/src/webui/qtorrentfilter.h deleted file mode 100644 index dd399dadd..000000000 --- a/src/webui/qtorrentfilter.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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 QTORRENTFILTER_H -#define QTORRENTFILTER_H - -#include "qtorrenthandle.h" - -class QTorrentFilter -{ -public: - enum - { - All, - Downloading, - Seeding, - Completed, - Paused, - Resumed, - Active, - Inactive - }; - - // label: pass empty string for "no label" or null string (QString()) for "any label" - QTorrentFilter(QString filter, QString label = QString()); - bool apply(const QTorrentHandle& h) const; - -private: - int type_; - QString label_; - - bool isTorrentDownloading(const QTorrentHandle &h) const; - bool isTorrentSeeding(const QTorrentHandle &h) const; - bool isTorrentCompleted(const QTorrentHandle &h) const; - bool isTorrentPaused(const QTorrentHandle &h) const; - bool isTorrentResumed(const QTorrentHandle &h) const; - bool isTorrentActive(const QTorrentHandle &h) const; - bool isTorrentInactive(const QTorrentHandle &h) const; - bool torrentHasLabel(const QTorrentHandle &h) const; -}; - -#endif // QTORRENTFILTER_H From 191cdc2849360c1f3a7e1e1822d239e6ba6bf3d2 Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Wed, 6 May 2015 14:53:27 +0300 Subject: [PATCH 10/12] Move utilities to core/utils folder. Also move the names to Utils namespace. --- src/app/application.cpp | 20 +- src/app/application.h | 4 +- src/app/main.cpp | 12 +- src/core/bittorrent/magneturi.cpp | 4 +- src/core/bittorrent/peerinfo.cpp | 2 +- src/core/bittorrent/session.cpp | 104 ++-- src/core/bittorrent/torrentcreatorthread.cpp | 26 +- src/core/bittorrent/torrenthandle.cpp | 88 ++-- src/core/bittorrent/torrentinfo.cpp | 22 +- src/core/bittorrent/tracker.cpp | 4 +- src/core/bittorrent/trackerentry.cpp | 6 +- src/core/core.pri | 8 +- src/core/fs_utils.cpp | 475 ------------------ src/core/net/downloadhandler.cpp | 10 +- src/core/preferences.cpp | 58 +-- src/core/scanfoldersmodel.cpp | 10 +- src/core/types.h | 10 + src/core/utils/fs.cpp | 492 +++++++++++++++++++ src/core/{fs_utils.h => utils/fs.h} | 67 +-- src/core/{ => utils}/misc.cpp | 153 ++---- src/core/{ => utils}/misc.h | 74 ++- src/core/utils/string.cpp | 95 +++- src/core/utils/string.h | 16 +- src/gui/addnewtorrentdialog.cpp | 42 +- src/gui/deletionconfirmationdlg.h | 4 +- src/gui/geoip/geoipmanager.cpp | 6 +- src/gui/mainwindow.cpp | 26 +- src/gui/options_imp.cpp | 44 +- src/gui/previewlistdelegate.h | 7 +- src/gui/previewselect.cpp | 8 +- src/gui/programupdater.cpp | 4 +- src/gui/properties/peerlistdelegate.h | 9 +- src/gui/properties/peerlistsortmodel.h | 2 +- src/gui/properties/propertieswidget.cpp | 59 +-- src/gui/properties/proplistdelegate.h | 7 +- src/gui/properties/trackerlist.cpp | 2 +- src/gui/properties/trackersadditiondlg.h | 8 +- src/gui/rss/automatedrssdownloader.cpp | 6 +- src/gui/rss/htmlbrowser.cpp | 4 +- src/gui/rss/rssdownloadrule.cpp | 4 +- src/gui/rss/rssfeed.cpp | 6 +- src/gui/rss/rssparser.cpp | 8 +- src/gui/shutdownconfirm.cpp | 17 +- src/gui/shutdownconfirm.h | 8 +- src/gui/speedlimitdlg.cpp | 2 +- src/gui/speedlimitdlg.h | 2 +- src/gui/statsdialog.cpp | 21 +- src/gui/statusbar.cpp | 10 +- src/gui/torrentcontentfiltermodel.cpp | 3 +- src/gui/torrentcontentmodel.cpp | 6 +- src/gui/torrentcontentmodelitem.cpp | 4 +- src/gui/torrentcreatordlg.cpp | 20 +- src/gui/torrentimportdlg.cpp | 22 +- src/gui/torrentmodel.cpp | 2 +- src/gui/transferlistdelegate.cpp | 23 +- src/gui/transferlistfilterswidget.cpp | 17 +- src/gui/transferlistsortmodel.cpp | 5 +- src/gui/transferlistwidget.cpp | 10 +- src/searchengine/engineselectdlg.cpp | 48 +- src/searchengine/searchengine.cpp | 20 +- src/searchengine/searchlistdelegate.h | 4 +- src/searchengine/searchsortmodel.h | 4 +- src/searchengine/searchtab.cpp | 2 +- src/searchengine/supportedengines.h | 4 +- src/webui/btjson.cpp | 8 +- src/webui/prefjson.cpp | 16 +- src/webui/webapplication.cpp | 13 +- 67 files changed, 1172 insertions(+), 1135 deletions(-) delete mode 100644 src/core/fs_utils.cpp create mode 100644 src/core/utils/fs.cpp rename src/core/{fs_utils.h => utils/fs.h} (51%) rename src/core/{ => utils}/misc.cpp (79%) rename src/core/{ => utils}/misc.h (50%) diff --git a/src/app/application.cpp b/src/app/application.cpp index 6aab748d0..9ffd1104d 100644 --- a/src/app/application.cpp +++ b/src/app/application.cpp @@ -60,7 +60,7 @@ #include "application.h" #include "core/logger.h" #include "core/preferences.h" -#include "core/misc.h" +#include "core/utils/misc.h" #include "core/iconprovider.h" #include "core/scanfoldersmodel.h" #include "core/net/smtp.h" @@ -74,7 +74,7 @@ Application::Application(const QString &id, int &argc, char **argv) : BaseApplication(id, argc, argv) , m_running(false) #ifndef DISABLE_GUI - , m_shutdownAct(NO_SHUTDOWN) + , m_shutdownAct(ShutdownAction::None) #endif { Logger::initInstance(); @@ -118,11 +118,11 @@ 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("Torrent size: %1").arg(Utils::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"; + .arg(Utils::Misc::userFriendlyDuration(torrent->activeTime())) + "\n\n\n"; content += QObject::tr("Thank you for using qBittorrent.") + "\n"; // Send the notification email @@ -169,13 +169,13 @@ void Application::allTorrentsFinished() bool shutdown = pref->shutdownWhenDownloadsComplete(); // Confirm shutdown - ShutDownAction action = NO_SHUTDOWN; + ShutdownAction action = ShutdownAction::None; if (suspend) - action = SUSPEND_COMPUTER; + action = ShutdownAction::Suspend; else if (hibernate) - action = HIBERNATE_COMPUTER; + action = ShutdownAction::Hibernate; else if (shutdown) - action = SHUTDOWN_COMPUTER; + action = ShutdownAction::Shutdown; if (!ShutdownConfirmDlg::askForConfirmation(action)) return; @@ -458,9 +458,9 @@ void Application::cleanup() shutdownBRDestroy((HWND)m_window->effectiveWinId()); #endif // Q_OS_WIN delete m_window; - if (m_shutdownAct != NO_SHUTDOWN) { + if (m_shutdownAct != ShutdownAction::None) { qDebug() << "Sending computer shutdown/suspend/hibernate signal..."; - misc::shutdownComputer(m_shutdownAct); + Utils::Misc::shutdownComputer(m_shutdownAct); } #endif } diff --git a/src/app/application.h b/src/app/application.h index 73e277de5..9ed308e20 100644 --- a/src/app/application.h +++ b/src/app/application.h @@ -50,7 +50,7 @@ QT_END_NAMESPACE typedef QtSingleCoreApplication BaseApplication; #endif -#include "core/misc.h" +#include "core/utils/misc.h" #ifndef DISABLE_WEBUI class WebUI; @@ -96,7 +96,7 @@ private: #ifndef DISABLE_GUI QPointer m_window; - ShutDownAction m_shutdownAct; + ShutdownAction m_shutdownAct; #endif #ifndef DISABLE_WEBUI diff --git a/src/app/main.cpp b/src/app/main.cpp index a9c9d2904..22a1f5157 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -66,7 +66,7 @@ Q_IMPORT_PLUGIN(qico) #include #include #include "application.h" -#include "core/misc.h" +#include "core/utils/misc.h" #include "core/preferences.h" // Signal handlers @@ -123,7 +123,7 @@ int main(int argc, char *argv[]) bool isOneArg = (argc == 2); // Create Application - QString appId = QLatin1String("qBittorrent-") + misc::getUserIDString(); + QString appId = QLatin1String("qBittorrent-") + Utils::Misc::getUserIDString(); QScopedPointer app(new Application(appId, argc, argv)); const QBtCommandLineParameters params = parseCommandLine(); @@ -188,7 +188,7 @@ int main(int argc, char *argv[]) #endif qDebug("qBittorrent is already running for this user."); - misc::msleep(300); + Utils::Misc::msleep(300); app->sendParams(params.torrents); return EXIT_SUCCESS; @@ -395,7 +395,7 @@ void displayUsage(const QString& prg_name) #else QMessageBox msgBox(QMessageBox::Information, QObject::tr("Help"), makeUsage(prg_name), QMessageBox::Ok); msgBox.show(); // Need to be shown or to moveToCenter does not work - msgBox.move(misc::screenCenter(&msgBox)); + msgBox.move(Utils::Misc::screenCenter(&msgBox)); msgBox.exec(); #endif } @@ -407,7 +407,7 @@ void displayBadArgMessage(const QString& message) QMessageBox msgBox(QMessageBox::Critical, QObject::tr("Bad command line"), message + QLatin1Char('\n') + help, QMessageBox::Ok); msgBox.show(); // Need to be shown or to moveToCenter does not work - msgBox.move(misc::screenCenter(&msgBox)); + msgBox.move(Utils::Misc::screenCenter(&msgBox)); msgBox.exec(); #else std::cerr << qPrintable(QObject::tr("Bad command line: ")); @@ -439,7 +439,7 @@ bool userAgreesWithLegalNotice() msgBox.addButton(QObject::tr("Cancel"), QMessageBox::RejectRole); QAbstractButton *agree_button = msgBox.addButton(QObject::tr("I Agree"), QMessageBox::AcceptRole); msgBox.show(); // Need to be shown or to moveToCenter does not work - msgBox.move(misc::screenCenter(&msgBox)); + msgBox.move(Utils::Misc::screenCenter(&msgBox)); msgBox.exec(); if (msgBox.clickedButton() == agree_button) { // Save the answer diff --git a/src/core/bittorrent/magneturi.cpp b/src/core/bittorrent/magneturi.cpp index 6f8189f54..4cabb67d3 100644 --- a/src/core/bittorrent/magneturi.cpp +++ b/src/core/bittorrent/magneturi.cpp @@ -48,10 +48,10 @@ MagnetUri::MagnetUri(const QString &url) m_valid = true; m_hash = m_addTorrentParams.info_hash; - m_name = String::fromStdString(m_addTorrentParams.name); + m_name = Utils::String::fromStdString(m_addTorrentParams.name); foreach (const std::string &tracker, m_addTorrentParams.trackers) - m_trackers.append(String::fromStdString(tracker)); + m_trackers.append(Utils::String::fromStdString(tracker)); #if LIBTORRENT_VERSION_NUM >= 10000 foreach (const std::string &urlSeed, m_addTorrentParams.url_seeds) diff --git a/src/core/bittorrent/peerinfo.cpp b/src/core/bittorrent/peerinfo.cpp index 6c8653e7c..60c116578 100644 --- a/src/core/bittorrent/peerinfo.cpp +++ b/src/core/bittorrent/peerinfo.cpp @@ -202,7 +202,7 @@ PeerAddress PeerInfo::address() const QString PeerInfo::client() const { - return String::fromStdString(m_nativeInfo.client); + return Utils::String::fromStdString(m_nativeInfo.client); } diff --git a/src/core/bittorrent/session.cpp b/src/core/bittorrent/session.cpp index 17eb33f32..e855abafc 100644 --- a/src/core/bittorrent/session.cpp +++ b/src/core/bittorrent/session.cpp @@ -66,8 +66,8 @@ using namespace BitTorrent; #include "geoipmanager.h" #endif -#include "core/misc.h" -#include "core/fs_utils.h" +#include "core/utils/misc.h" +#include "core/utils/fs.h" #include "core/utils/string.h" #include "core/logger.h" #include "core/preferences.h" @@ -163,7 +163,7 @@ Session::Session(QObject *parent) // 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())); + Logger::instance()->addMessage("Peer ID: " + Utils::String::fromStdString(fingerprint.to_string())); m_nativeSession->set_alert_dispatch(boost::bind(&Session::dispatchAlerts, this, _1)); @@ -318,12 +318,12 @@ Session *Session::instance() void Session::loadState() { - const QString statePath = fsutils::cacheLocation() + QLatin1String("/ses_state"); + const QString statePath = Utils::Fs::cacheLocation() + QLatin1String("/ses_state"); if (!QFile::exists(statePath)) return; if (QFile(statePath).size() == 0) { // Remove empty invalid state file - fsutils::forceRemove(statePath); + Utils::Fs::forceRemove(statePath); return; } @@ -343,7 +343,7 @@ void Session::saveState() { qDebug("Saving session state to disk..."); - const QString state_path = fsutils::cacheLocation() + QLatin1String("/ses_state"); + const QString state_path = Utils::Fs::cacheLocation() + QLatin1String("/ses_state"); libt::entry session_state; m_nativeSession->save_state(session_state); std::vector out; @@ -364,7 +364,7 @@ void Session::setSessionSettings() 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))); + logger->addMessage(tr("HTTP user agent is %1").arg(Utils::String::fromStdString(sessionSettings.user_agent))); sessionSettings.upnp_ignore_nonrouters = true; sessionSettings.use_dht_as_fallback = false; @@ -422,7 +422,7 @@ void Session::setSessionSettings() // IP address to announce to trackers QString announce_ip = pref->getNetworkAddress(); if (!announce_ip.isEmpty()) - sessionSettings.announce_ip = String::toStdString(announce_ip); + sessionSettings.announce_ip = Utils::String::toStdString(announce_ip); // Super seeding sessionSettings.strict_super_seeding = pref->isSuperSeedingEnabled(); // * Max Half-open connections @@ -668,13 +668,13 @@ void Session::configure() libt::proxy_settings proxySettings; if (pref->isProxyEnabled()) { qDebug("Enabling P2P proxy"); - proxySettings.hostname = String::toStdString(pref->getProxyIp()); + proxySettings.hostname = Utils::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()); + proxySettings.username = Utils::String::toStdString(pref->getProxyUsername()); + proxySettings.password = Utils::String::toStdString(pref->getProxyPassword()); qDebug("username is %s", proxySettings.username.c_str()); qDebug("password is %s", proxySettings.password.c_str()); } @@ -780,7 +780,7 @@ 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 + Utils::Fs::forceRemove(filePath); // remove temporary file } void Session::changeSpeedLimitMode(bool alternative) @@ -870,8 +870,8 @@ bool Session::deleteTorrent(const QString &hash, bool deleteLocalFiles) // 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); + Utils::Fs::forceRemove(unwantedFile); + const QString parentFolder = Utils::Fs::branchPath(unwantedFile); qDebug("Attempt to remove parent folder (if empty): %s", qPrintable(parentFolder)); QDir().rmpath(parentFolder); } @@ -883,7 +883,7 @@ bool Session::deleteTorrent(const QString &hash, bool deleteLocalFiles) 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)); + Utils::Fs::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())); @@ -1038,10 +1038,10 @@ bool Session::addTorrent(QString source, const AddTorrentParams ¶ms) if (source.startsWith("bc://bt/", Qt::CaseInsensitive)) { qDebug("Converting bc link to magnet link"); - source = misc::bcLinkToMagnet(source); + source = Utils::Misc::bcLinkToMagnet(source); } - if (misc::isUrl(source)) { + if (Utils::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 */); @@ -1164,7 +1164,7 @@ bool Session::addTorrent_impl(const AddTorrentData &addData, const MagnetUri &ma savePath += "/"; } - p.save_path = String::toStdString(fsutils::toNativePath(savePath)); + p.save_path = Utils::String::toStdString(Utils::Fs::toNativePath(savePath)); // Check if save path exists, creating it otherwise if (!QDir(savePath).exists()) QDir().mkpath(savePath); @@ -1214,7 +1214,7 @@ bool Session::loadMetadata(const QString &magnetUri) #endif QString savePath = QString("%1/%2").arg(QDir::tempPath()).arg(hash); - p.save_path = String::toStdString(fsutils::toNativePath(savePath)); + p.save_path = Utils::String::toStdString(Utils::Fs::toNativePath(savePath)); // Check if save path exists, creating it otherwise if (!QDir(savePath).exists()) QDir().mkpath(savePath); @@ -1253,7 +1253,7 @@ void Session::exportTorrentFile(TorrentHandle *const torrent, TorrentExportFolde 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)) { + if (QFile::exists(newTorrentPath) && Utils::Fs::sameFiles(torrentPath, newTorrentPath)) { // Append hash to torrent name to make it unique newTorrentPath = exportPath.absoluteFilePath(torrent->name() + "-" + torrentFilename); } @@ -1285,7 +1285,7 @@ void Session::exportTorrentFiles(QString path) if (QFile::exists(srcPath)) { QString dstPath = exportDir.absoluteFilePath(QString("%1.torrent").arg(torrent->name())); if (QFile::exists(dstPath)) { - if (!fsutils::sameFiles(srcPath, dstPath)) { + if (!Utils::Fs::sameFiles(srcPath, dstPath)) { dstPath = exportDir.absoluteFilePath(QString("%1-%2.torrent").arg(torrent->name()).arg(torrent->hash())); } else { @@ -1375,7 +1375,7 @@ void Session::enableDHT(bool enable) } 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); + logger->addMessage(tr("DHT support [OFF]. Reason: %1").arg(Utils::String::fromStdString(e.what())), Log::CRITICAL); } } } @@ -1442,7 +1442,7 @@ void Session::setDefaultSavePath(const QString &path) { if (path.isEmpty()) return; - QString defaultSavePath = fsutils::fromNativePath(path); + QString defaultSavePath = Utils::Fs::fromNativePath(path); if (!defaultSavePath.endsWith("/")) defaultSavePath += "/"; if (m_defaultSavePath != defaultSavePath) { @@ -1457,7 +1457,7 @@ void Session::setDefaultTempPath(const QString &path) QString tempPath; if (!path.isEmpty()) { - tempPath = fsutils::fromNativePath(path); + tempPath = Utils::Fs::fromNativePath(path); if (!tempPath.endsWith("/")) tempPath += "/"; } @@ -1506,7 +1506,7 @@ void Session::setListeningPort(int port) 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); + 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(Utils::String::fromStdString(ec.message())), Log::CRITICAL); return; } @@ -1716,7 +1716,7 @@ void Session::handleTorrentFinished(TorrentHandle *const torrent) } else { qDebug("Caught error loading torrent"); - Logger::instance()->addMessage(tr("Unable to decode %1 torrent file.").arg(fsutils::toNativePath(torrentFullpath)), Log::CRITICAL); + Logger::instance()->addMessage(tr("Unable to decode %1 torrent file.").arg(Utils::Fs::toNativePath(torrentFullpath)), Log::CRITICAL); } } } @@ -1773,7 +1773,7 @@ bool Session::hasPerTorrentRatioLimit() const void Session::initResumeFolder() { - m_resumeFolderPath = fsutils::expandPathAbs(fsutils::QDesktopServicesDataLocation() + RESUME_FOLDER); + m_resumeFolderPath = Utils::Fs::expandPathAbs(Utils::Fs::QDesktopServicesDataLocation() + RESUME_FOLDER); QDir resumeFolderDir(m_resumeFolderPath); if (resumeFolderDir.exists() || resumeFolderDir.mkpath(resumeFolderDir.absolutePath())) { m_resumeFolderLock.setFileName(resumeFolderDir.absoluteFilePath("session.lock")); @@ -1795,9 +1795,9 @@ void Session::enableIPFilter(const QString &filterPath, bool force) 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)); + if (m_filterPath.isEmpty() || m_filterPath != Utils::Fs::fromNativePath(filterPath) || force) { + m_filterPath = Utils::Fs::fromNativePath(filterPath); + m_filterParser->processFilterFile(Utils::Fs::fromNativePath(filterPath)); } } @@ -1824,7 +1824,7 @@ void Session::recursiveTorrentDownload(const InfoHash &hash) 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())); + .arg(Utils::Fs::toNativePath(torrentRelpath)).arg(torrent->name())); const QString torrentFullpath = torrent->savePath() + "/" + torrentRelpath; AddTorrentParams params; @@ -1857,18 +1857,18 @@ void Session::setProxySettings(libt::proxy_settings proxySettings) 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); + proxy_str = QString("http://%1:%2@%3:%4").arg(Utils::String::fromStdString(proxySettings.username)).arg(Utils::String::fromStdString(proxySettings.password)) + .arg(Utils::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); + proxy_str = QString("http://%1:%2").arg(Utils::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); + proxy_str = QString("%1:%2").arg(Utils::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); + proxy_str = QString("%1:%2@%3:%4").arg(Utils::String::fromStdString(proxySettings.username)).arg(Utils::String::fromStdString(proxySettings.password)) + .arg(Utils::String::fromStdString(proxySettings.hostname)).arg(proxySettings.port); break; default: qDebug("Disabling HTTP communications proxy"); @@ -1930,11 +1930,11 @@ void Session::startUpTorrents() 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); + .arg(Utils::Fs::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); + .arg(Utils::Fs::toNativePath(hash)), Log::CRITICAL); } } } @@ -2074,7 +2074,7 @@ void Session::handleAlert(libt::alert *a) } } catch (std::exception &exc) { - qWarning() << "Caught exception in readAlerts(): " << String::fromStdString(exc.what()); + qWarning() << "Caught exception in readAlerts(): " << Utils::String::fromStdString(exc.what()); } } @@ -2090,7 +2090,7 @@ 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()); + QString msg = Utils::String::fromStdString(p->message()); logger->addMessage(tr("Couldn't add torrent. Reason: %1").arg(msg), Log::WARNING); emit addTorrentFailed(msg); return; @@ -2166,7 +2166,7 @@ void Session::handleTorrentDeletedAlert(libt::torrent_deleted_alert *p) 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); + bool ok = Utils::Fs::smartRemoveEmptyFolderTree(dirpath); Q_UNUSED(ok); qDebug() << "Folder was removed: " << ok; } @@ -2194,7 +2194,7 @@ void Session::handleFileErrorAlert(libt::file_error_alert *p) // NOTE: Check this function! TorrentHandle *const torrent = m_torrents.value(p->handle.info_hash()); if (torrent) { - QString msg = String::fromStdString(p->message()); + QString msg = Utils::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); @@ -2203,13 +2203,13 @@ void Session::handleFileErrorAlert(libt::file_error_alert *p) 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); + Logger::instance()->addMessage(tr("UPnP/NAT-PMP: Port mapping failure, message: %1").arg(Utils::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); + Logger::instance()->addMessage(tr("UPnP/NAT-PMP: Port mapping successful, message: %1").arg(Utils::String::fromStdString(p->message())), Log::INFO); } void Session::handlePeerBlockedAlert(libt::peer_blocked_alert *p) @@ -2257,7 +2257,7 @@ void Session::handlePeerBanAlert(libt::peer_ban_alert *p) 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); + Logger::instance()->addMessage(tr("Url seed lookup failed for url: %1, message: %2").arg(Utils::String::fromStdString(p->url)).arg(Utils::String::fromStdString(p->message())), Log::CRITICAL); } void Session::handleListenSucceededAlert(libt::listen_succeeded_alert *p) @@ -2304,7 +2304,7 @@ void Session::handleListenFailedAlert(libt::listen_failed_alert *p) 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); + .arg(Utils::String::fromStdString(p->error.message())), Log::CRITICAL); } void Session::handleExternalIPAlert(libt::external_ip_alert *p) @@ -2367,10 +2367,10 @@ bool loadTorrentResumeData(const QByteArray &data, AddTorrentData &out) 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.savePath = Utils::Fs::fromNativePath(Utils::String::fromStdString(fast.dict_find_string_value("qBt-savePath"))); + out.ratioLimit = Utils::String::fromStdString(fast.dict_find_string_value("qBt-ratioLimit")).toDouble(); + out.label = Utils::String::fromStdString(fast.dict_find_string_value("qBt-label")); + out.name = Utils::String::fromStdString(fast.dict_find_string_value("qBt-name")); out.hasSeedStatus = fast.dict_find_int_value("qBt-seedStatus"); out.disableTempPath = fast.dict_find_int_value("qBt-tempPathDisabled"); @@ -2384,7 +2384,7 @@ bool Session::writeResumeDataFile(TorrentHandle *const torrent, const libt::entr 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)); + Utils::Fs::forceRemove(resumeDataDir.absoluteFilePath(file)); QString filename = QString("%1.fastresume.%2").arg(torrent->hash()).arg(torrent->queuePosition()); QString filepath = resumeDataDir.absoluteFilePath(filename); diff --git a/src/core/bittorrent/torrentcreatorthread.cpp b/src/core/bittorrent/torrentcreatorthread.cpp index fba0ed5d2..f9d89ab7c 100644 --- a/src/core/bittorrent/torrentcreatorthread.cpp +++ b/src/core/bittorrent/torrentcreatorthread.cpp @@ -44,8 +44,8 @@ #include #include -#include "core/fs_utils.h" -#include "core/misc.h" +#include "core/utils/fs.h" +#include "core/utils/misc.h" #include "core/utils/string.h" #include "torrentcreatorthread.h" @@ -73,10 +73,10 @@ TorrentCreatorThread::~TorrentCreatorThread() 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); + m_inputPath = Utils::Fs::fromNativePath(inputPath); + m_savePath = Utils::Fs::fromNativePath(savePath); if (QFile(m_savePath).exists()) - fsutils::forceRemove(m_savePath); + Utils::Fs::forceRemove(m_savePath); m_trackers = trackers; m_urlSeeds = urlSeeds; m_comment = comment; @@ -105,14 +105,14 @@ void TorrentCreatorThread::run() try { libt::file_storage fs; // Adding files to the torrent - libt::add_files(fs, String::toStdString(fsutils::toNativePath(m_inputPath)), fileFilter); + libt::add_files(fs, Utils::String::toStdString(Utils::Fs::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())); + t.add_url_seed(Utils::String::toStdString(seed.trimmed())); int tier = 0; bool newline = false; @@ -124,14 +124,14 @@ void TorrentCreatorThread::run() newline = true; continue; } - t.add_tracker(String::toStdString(tracker.trimmed()), tier); + t.add_tracker(Utils::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())); + const QString parentPath = Utils::Fs::branchPath(m_inputPath) + "/"; + libt::set_piece_hashes(t, Utils::String::toStdString(Utils::Fs::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()); @@ -144,12 +144,12 @@ void TorrentCreatorThread::run() 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); + int len = Utils::Fs::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); + std::ofstream outfile(Utils::Fs::toNativePath(m_savePath).toLocal8Bit().constData(), std::ios_base::out | std::ios_base::binary); #endif if (outfile.fail()) throw std::exception(); @@ -161,6 +161,6 @@ void TorrentCreatorThread::run() emit creationSuccess(m_savePath, parentPath); } catch (std::exception& e) { - emit creationFailure(String::fromStdString(e.what())); + emit creationFailure(Utils::String::fromStdString(e.what())); } } diff --git a/src/core/bittorrent/torrenthandle.cpp b/src/core/bittorrent/torrenthandle.cpp index f47d09253..f2ef6a45d 100644 --- a/src/core/bittorrent/torrenthandle.cpp +++ b/src/core/bittorrent/torrenthandle.cpp @@ -45,11 +45,11 @@ #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 "core/utils/fs.h" +#include "core/utils/misc.h" #include "session.h" #include "peerinfo.h" #include "trackerentry.h" @@ -186,7 +186,7 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle , m_state(TorrentState::Unknown) , m_name(data.name) , m_addedTime(data.resumed ? data.addedTime : QDateTime::currentDateTime()) - , m_savePath(fsutils::toNativePath(data.savePath)) + , m_savePath(Utils::Fs::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)) @@ -230,7 +230,7 @@ QString TorrentHandle::name() const #if LIBTORRENT_VERSION_NUM < 10000 name = m_nativeName; #else - name = String::fromStdString(m_nativeStatus.name); + name = Utils::String::fromStdString(m_nativeStatus.name); #endif } @@ -293,12 +293,12 @@ qlonglong TorrentHandle::wastedSize() const QString TorrentHandle::currentTracker() const { - return String::fromStdString(m_nativeStatus.current_tracker); + return Utils::String::fromStdString(m_nativeStatus.current_tracker); } QString TorrentHandle::savePath() const { - return fsutils::fromNativePath(m_savePath); + return Utils::Fs::fromNativePath(m_savePath); } QString TorrentHandle::rootPath() const @@ -318,13 +318,13 @@ QString TorrentHandle::nativeActualSavePath() const #if LIBTORRENT_VERSION_NUM < 10000 return m_nativeSavePath; #else - return String::fromStdString(m_nativeStatus.save_path); + return Utils::String::fromStdString(m_nativeStatus.save_path); #endif } QString TorrentHandle::actualSavePath() const { - return fsutils::fromNativePath(nativeActualSavePath()); + return Utils::Fs::fromNativePath(nativeActualSavePath()); } QList TorrentHandle::trackers() const @@ -437,7 +437,7 @@ 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())); + SAFE_CALL_BOOL(add_url_seed, Utils::String::toStdString(urlSeed.toString())); } bool TorrentHandle::removeUrlSeed(const QUrl &urlSeed) @@ -445,13 +445,13 @@ 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())); + SAFE_CALL_BOOL(remove_url_seed, Utils::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); + libt::address addr = libt::address::from_string(Utils::String::toStdString(peerAddress.ip.toString()), ec); if (ec) return false; libt::asio::ip::tcp::endpoint ep(addr, peerAddress.port); @@ -479,7 +479,7 @@ QString TorrentHandle::savePathParsed() const else p = savePath(); - return fsutils::toNativePath(p); + return Utils::Fs::toNativePath(p); } int TorrentHandle::filesCount() const @@ -544,7 +544,7 @@ QString TorrentHandle::filePath(int index) const QString TorrentHandle::fileName(int index) const { if (!hasMetadata()) return QString(); - return fsutils::fileName(filePath(index)); + return Utils::Fs::fileName(filePath(index)); } qlonglong TorrentHandle::fileSize(int index) const @@ -561,7 +561,7 @@ QStringList TorrentHandle::absoluteFilePaths() const QDir saveDir(actualSavePath()); QStringList res; for (int i = 0; i < filesCount(); ++i) - res << fsutils::expandPathAbs(saveDir.absoluteFilePath(filePath(i))); + res << Utils::Fs::expandPathAbs(saveDir.absoluteFilePath(filePath(i))); return res; } @@ -577,7 +577,7 @@ QStringList TorrentHandle::absoluteFilePathsUnwanted() const 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))); + const QString path = Utils::Fs::expandPathAbs(saveDir.absoluteFilePath(filePath(i))); if (path.contains(".unwanted")) res << path; } @@ -720,8 +720,8 @@ bool TorrentHandle::hasFirstLastPiecePriority() const 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)) { + const QString ext = Utils::Fs::fileExtension(filePath(i)); + if (Utils::Misc::isPreviewable(ext) && (fp[i] > 0)) { extremities = fileExtremityPieces(i); found = true; break; @@ -824,7 +824,7 @@ int TorrentHandle::queuePosition() const QString TorrentHandle::error() const { - return String::fromStdString(m_nativeStatus.error); + return Utils::String::fromStdString(m_nativeStatus.error); } qlonglong TorrentHandle::totalDownload() const @@ -1114,7 +1114,7 @@ void TorrentHandle::move(QString path) // even if new path same as default. m_useDefaultSavePath = false; - path = fsutils::toNativePath(path); + path = Utils::Fs::toNativePath(path); if (path == savePath()) return; if (!useTempPath()) { @@ -1183,8 +1183,8 @@ void TorrentHandle::setFirstLastPiecePriority(bool b) 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)) { + const QString ext = Utils::Fs::fileExtension(path); + if (Utils::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]; @@ -1258,7 +1258,7 @@ void TorrentHandle::setTrackerLogin(const QString &username, const QString &pass void TorrentHandle::renameFile(int index, const QString &name) { qDebug() << Q_FUNC_INFO << index << name; - SAFE_CALL(rename_file, index, String::toStdString(fsutils::toNativePath(name))); + SAFE_CALL(rename_file, index, Utils::String::toStdString(Utils::Fs::toNativePath(name))); } bool TorrentHandle::saveTorrentFile(const QString &path) @@ -1307,7 +1307,7 @@ void TorrentHandle::handleStorageMovedAlert(libtorrent::storage_moved_alert *p) return; } - QString newPath = String::fromStdString(p->path); + QString newPath = Utils::String::fromStdString(p->path); if (newPath != m_newPath) { qWarning("TorrentHandleImpl::handleStorageMoved(): New path doesn't match a path in a queue."); return; @@ -1328,7 +1328,7 @@ void TorrentHandle::handleStorageMovedAlert(libtorrent::storage_moved_alert *p) } // Attempt to remove old folder if empty - QDir oldSaveDir(fsutils::fromNativePath(m_oldPath)); + QDir oldSaveDir(Utils::Fs::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); @@ -1348,7 +1348,7 @@ void TorrentHandle::handleStorageMovedFailedAlert(libtorrent::storage_moved_fail } Logger::instance()->addMessage(tr("Could not move torrent: '%1'. Reason: %2") - .arg(name()).arg(String::fromStdString(p->message())), Log::CRITICAL); + .arg(name()).arg(Utils::String::fromStdString(p->message())), Log::CRITICAL); m_newPath.clear(); if (!m_queuedPath.isEmpty()) { @@ -1364,7 +1364,7 @@ void TorrentHandle::handleStorageMovedFailedAlert(libtorrent::storage_moved_fail void TorrentHandle::handleTrackerReplyAlert(libtorrent::tracker_reply_alert *p) { - QString trackerUrl = String::fromStdString(p->url); + QString trackerUrl = Utils::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 @@ -1375,8 +1375,8 @@ void TorrentHandle::handleTrackerReplyAlert(libtorrent::tracker_reply_alert *p) void TorrentHandle::handleTrackerWarningAlert(libtorrent::tracker_warning_alert *p) { - QString trackerUrl = String::fromStdString(p->url); - QString message = String::fromStdString(p->msg); + QString trackerUrl = Utils::String::fromStdString(p->url); + QString message = Utils::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 @@ -1386,8 +1386,8 @@ void TorrentHandle::handleTrackerWarningAlert(libtorrent::tracker_warning_alert void TorrentHandle::handleTrackerErrorAlert(libtorrent::tracker_error_alert *p) { - QString trackerUrl = String::fromStdString(p->url); - QString message = String::fromStdString(p->msg); + QString trackerUrl = Utils::String::fromStdString(p->url); + QString message = Utils::String::fromStdString(p->msg); qDebug("Received a tracker error for %s: %s", qPrintable(trackerUrl), qPrintable(message)); m_trackerInfos[trackerUrl].lastMessage = message; @@ -1487,13 +1487,13 @@ void TorrentHandle::handleFastResumeRejectedAlert(libtorrent::fastresume_rejecte } 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); + .arg(name()).arg(Utils::String::fromStdString(p->message())), Log::CRITICAL); } } void TorrentHandle::handleFileRenamedAlert(libtorrent::file_renamed_alert *p) { - QString newName = fsutils::fromNativePath(String::fromStdString(p->name)); + QString newName = Utils::Fs::fromNativePath(Utils::String::fromStdString(p->name)); // TODO: Check this! if (filesCount() > 1) { @@ -1573,7 +1573,7 @@ void TorrentHandle::adjustSavePath() QString defaultSavePath = m_session->defaultSavePath(); if (m_session->useAppendLabelToSavePath() && !m_label.isEmpty()) defaultSavePath += QString("%1/").arg(m_label); - defaultSavePath = fsutils::toNativePath(defaultSavePath); + defaultSavePath = Utils::Fs::toNativePath(defaultSavePath); if (m_savePath != defaultSavePath) { if (!useTempPath()) { moveStorage(defaultSavePath); @@ -1702,7 +1702,7 @@ void TorrentHandle::adjustActualSavePath() qDebug("Moving torrent to its temp save path: %s", qPrintable(path)); } - moveStorage(fsutils::toNativePath(path)); + moveStorage(Utils::Fs::toNativePath(path)); } libtorrent::torrent_handle TorrentHandle::nativeHandle() const @@ -1764,8 +1764,8 @@ 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())); + m_nativeName = Utils::String::fromStdString(m_nativeHandle.name()); + m_nativeSavePath = Utils::Fs::fromNativePath(Utils::String::fromStdString(m_nativeHandle.save_path())); } catch (std::exception &exc) { qDebug("torrent_handle method inside TorrentHandleImpl::updateStatus() throws exception: %s", exc.what()); @@ -1835,11 +1835,11 @@ void TorrentHandle::prioritizeFiles(const QVector &priorities) // Move unwanted files to a .unwanted subfolder if (priorities[i] == 0) { QString oldAbsPath = QDir(spath).absoluteFilePath(filepath); - QString parentAbsPath = fsutils::branchPath(oldAbsPath); + QString parentAbsPath = Utils::Fs::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); + QString newAbsPath = unwantedAbsPath + "/" + Utils::Fs::fileName(filepath); qDebug() << "Unwanted path is" << unwantedAbsPath; if (QFile::exists(newAbsPath)) { qWarning() << "File" << newAbsPath << "already exists at destination."; @@ -1852,25 +1852,25 @@ void TorrentHandle::prioritizeFiles(const QVector &priorities) if (created) { // Hide the folder on Windows qDebug() << "Hiding folder (Windows)"; - std::wstring winPath = fsutils::toNativePath(unwantedAbsPath).toStdWString(); + std::wstring winPath = Utils::Fs::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); + QString parentPath = Utils::Fs::branchPath(filepath); if (!parentPath.isEmpty() && !parentPath.endsWith("/")) parentPath += "/"; - renameFile(i, parentPath + ".unwanted/" + fsutils::fileName(filepath)); + renameFile(i, parentPath + ".unwanted/" + Utils::Fs::fileName(filepath)); } } // Move wanted files back to their original folder if (priorities[i] > 0) { - QString parentRelPath = fsutils::branchPath(filepath); + QString parentRelPath = Utils::Fs::branchPath(filepath); if (QDir(parentRelPath).dirName() == ".unwanted") { - QString oldName = fsutils::fileName(filepath); - QString newRelPath = fsutils::branchPath(parentRelPath); + QString oldName = Utils::Fs::fileName(filepath); + QString newRelPath = Utils::Fs::branchPath(parentRelPath); if (newRelPath.isEmpty()) renameFile(i, oldName); else diff --git a/src/core/bittorrent/torrentinfo.cpp b/src/core/bittorrent/torrentinfo.cpp index 2f00a96ba..6447698bc 100644 --- a/src/core/bittorrent/torrentinfo.cpp +++ b/src/core/bittorrent/torrentinfo.cpp @@ -34,8 +34,8 @@ #include #include -#include "core/misc.h" -#include "core/fs_utils.h" +#include "core/utils/misc.h" +#include "core/utils/fs.h" #include "core/utils/string.h" #include "infohash.h" #include "trackerentry.h" @@ -64,7 +64,7 @@ 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)); + TorrentInfo info(new libt::torrent_info(Utils::String::toStdString(Utils::Fs::toNativePath(path)), ec)); if (ec) { error = QString::fromUtf8(ec.message().c_str()); qDebug("Cannot load .torrent file: %s", qPrintable(error)); @@ -93,7 +93,7 @@ InfoHash TorrentInfo::hash() const QString TorrentInfo::name() const { if (!isValid()) return QString(); - return String::fromStdString(m_nativeInfo->name()); + return Utils::String::fromStdString(m_nativeInfo->name()); } QDateTime TorrentInfo::creationDate() const @@ -106,13 +106,13 @@ QDateTime TorrentInfo::creationDate() const QString TorrentInfo::creator() const { if (!isValid()) return QString(); - return String::fromStdString(m_nativeInfo->creator()); + return Utils::String::fromStdString(m_nativeInfo->creator()); } QString TorrentInfo::comment() const { if (!isValid()) return QString(); - return String::fromStdString(m_nativeInfo->comment()); + return Utils::String::fromStdString(m_nativeInfo->comment()); } bool TorrentInfo::isPrivate() const @@ -148,7 +148,7 @@ int TorrentInfo::piecesCount() const QString TorrentInfo::filePath(int index) const { if (!isValid()) return QString(); - return fsutils::fromNativePath(String::fromStdString(m_nativeInfo->files().file_path(index))); + return Utils::Fs::fromNativePath(Utils::String::fromStdString(m_nativeInfo->files().file_path(index))); } QStringList TorrentInfo::filePaths() const @@ -162,13 +162,13 @@ QStringList TorrentInfo::filePaths() const QString TorrentInfo::fileName(int index) const { - return fsutils::fileName(filePath(index)); + return Utils::Fs::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))); + return Utils::Fs::fromNativePath(Utils::String::fromStdString(m_nativeInfo->orig_files().file_path(index))); } qlonglong TorrentInfo::fileSize(int index) const @@ -215,13 +215,13 @@ QByteArray TorrentInfo::metadata() const QString TorrentInfo::toMagnetUri() const { if (!isValid()) return QString(); - return String::fromStdString(libt::make_magnet_uri(*m_nativeInfo)); + return Utils::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)); + m_nativeInfo->rename_file(index, Utils::String::toStdString(newPath)); } boost::intrusive_ptr TorrentInfo::nativeInfo() const diff --git a/src/core/bittorrent/tracker.cpp b/src/core/bittorrent/tracker.cpp index 9b6558813..fca7fbf40 100644 --- a/src/core/bittorrent/tracker.cpp +++ b/src/core/bittorrent/tracker.cpp @@ -65,8 +65,8 @@ libtorrent::entry Peer::toEntry(bool noPeerId) const { libtorrent::entry::dictionary_type peerMap; if (!noPeerId) - peerMap["id"] = libtorrent::entry(String::toStdString(peerId)); - peerMap["ip"] = libtorrent::entry(String::toStdString(ip)); + peerMap["id"] = libtorrent::entry(Utils::String::toStdString(peerId)); + peerMap["ip"] = libtorrent::entry(Utils::String::toStdString(ip)); peerMap["port"] = libtorrent::entry(port); return libtorrent::entry(peerMap); diff --git a/src/core/bittorrent/trackerentry.cpp b/src/core/bittorrent/trackerentry.cpp index 444cc446a..14537a10e 100644 --- a/src/core/bittorrent/trackerentry.cpp +++ b/src/core/bittorrent/trackerentry.cpp @@ -28,14 +28,14 @@ #include -#include "core/misc.h" +#include "core/utils/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))) + : m_nativeEntry(libtorrent::announce_entry(Utils::String::toStdString(url))) { } @@ -51,7 +51,7 @@ TrackerEntry::TrackerEntry(const TrackerEntry &other) QString TrackerEntry::url() const { - return String::fromStdString(m_nativeEntry.url); + return Utils::String::fromStdString(m_nativeEntry.url); } int TrackerEntry::tier() const diff --git a/src/core/core.pri b/src/core/core.pri index c6bb3cf34..38045766d 100644 --- a/src/core/core.pri +++ b/src/core/core.pri @@ -1,7 +1,5 @@ HEADERS += \ $$PWD/types.h \ - $$PWD/misc.h \ - $$PWD/fs_utils.h \ $$PWD/tristatebool.h \ $$PWD/filesystemwatcher.h \ $$PWD/qinisettings.h \ @@ -38,13 +36,13 @@ HEADERS += \ $$PWD/bittorrent/private/bandwidthscheduler.h \ $$PWD/bittorrent/private/filterparserthread.h \ $$PWD/bittorrent/private/statistics.h \ + $$PWD/utils/fs.h \ + $$PWD/utils/misc.h \ $$PWD/utils/string.h \ $$PWD/torrentfilter.h \ $$PWD/scanfoldersmodel.h SOURCES += \ - $$PWD/misc.cpp \ - $$PWD/fs_utils.cpp \ $$PWD/tristatebool.cpp \ $$PWD/filesystemwatcher.cpp \ $$PWD/logger.cpp \ @@ -76,6 +74,8 @@ SOURCES += \ $$PWD/bittorrent/private/bandwidthscheduler.cpp \ $$PWD/bittorrent/private/filterparserthread.cpp \ $$PWD/bittorrent/private/statistics.cpp \ + $$PWD/utils/fs.cpp \ + $$PWD/utils/misc.cpp \ $$PWD/utils/string.cpp \ $$PWD/torrentfilter.cpp \ $$PWD/scanfoldersmodel.cpp diff --git a/src/core/fs_utils.cpp b/src/core/fs_utils.cpp deleted file mode 100644 index 012d1e61f..000000000 --- a/src/core/fs_utils.cpp +++ /dev/null @@ -1,475 +0,0 @@ -/* - * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2012 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 "fs_utils.h" -#include "misc.h" - -#include -#include -#include -#include -#include -#include -#include - -#ifdef Q_OS_MAC -#include -#include -#endif - -#ifndef Q_OS_WIN -#if defined(Q_OS_MAC) || defined(Q_OS_FREEBSD) -#include -#include -#elif defined(Q_OS_HAIKU) -#include -#else -#include -#endif -#else -#include -#include -#endif - -#if defined(Q_OS_WIN) || defined(Q_OS_OS2) -#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) -#include -#else -#include -#endif -#endif - -using namespace libtorrent; - -/** - * Converts a path to a string suitable for display. - * This function makes sure the directory separator used is consistent - * with the OS being run. - */ -QString fsutils::toNativePath(const QString& path) { - return QDir::toNativeSeparators(path); -} - -QString fsutils::fromNativePath(const QString &path) { - return QDir::fromNativeSeparators(path); -} - -/** - * Returns the file extension part of a file name. - */ -QString fsutils::fileExtension(const QString &filename) { - QString ext = QString(filename).remove(".!qB"); - const int point_index = ext.lastIndexOf("."); - return (point_index >= 0) ? ext.mid(point_index + 1) : QString(); -} - -QString fsutils::fileName(const QString& file_path) { - QString path = fsutils::fromNativePath(file_path); - const int slash_index = path.lastIndexOf("/"); - if (slash_index == -1) - return path; - return path.mid(slash_index + 1); -} - -QString fsutils::folderName(const QString& file_path) { - QString path = fsutils::fromNativePath(file_path); - const int slash_index = path.lastIndexOf("/"); - if (slash_index == -1) - return path; - return path.left(slash_index); -} - -/** - * Remove an empty folder tree. - * - * This function will also remove .DS_Store files on Mac OS and - * Thumbs.db on Windows. - */ -bool fsutils::smartRemoveEmptyFolderTree(const QString& dir_path) { - qDebug() << Q_FUNC_INFO << dir_path; - if (dir_path.isEmpty()) - return false; - - QDir dir(dir_path); - if (!dir.exists()) - return true; - - // Remove Files created by the OS -#if defined Q_OS_MAC - fsutils::forceRemove(dir_path + QLatin1String("/.DS_Store")); -#elif defined Q_OS_WIN - fsutils::forceRemove(dir_path + QLatin1String("/Thumbs.db")); -#endif - - QFileInfoList sub_files = dir.entryInfoList(); - foreach (const QFileInfo& info, sub_files) { - QString sub_name = info.fileName(); - if (sub_name == "." || sub_name == "..") - continue; - - QString sub_path = info.absoluteFilePath(); - qDebug() << Q_FUNC_INFO << "sub file: " << sub_path; - if (info.isDir()) { - if (!smartRemoveEmptyFolderTree(sub_path)) { - qWarning() << Q_FUNC_INFO << "Failed to remove folder: " << sub_path; - return false; - } - } else { - if (info.isHidden()) { - qDebug() << Q_FUNC_INFO << "Removing hidden file: " << sub_path; - if (!fsutils::forceRemove(sub_path)) { - qWarning() << Q_FUNC_INFO << "Failed to remove " << sub_path; - return false; - } - } else { - qWarning() << Q_FUNC_INFO << "Folder is not empty, aborting. Found: " << sub_path; - } - } - } - qDebug() << Q_FUNC_INFO << "Calling rmdir on " << dir_path; - return QDir().rmdir(dir_path); -} - -/** - * Removes the file with the given file_path. - * - * This function will try to fix the file permissions before removing it. - */ -bool fsutils::forceRemove(const QString& file_path) { - QFile f(file_path); - if (!f.exists()) - return true; - // Make sure we have read/write permissions - f.setPermissions(f.permissions()|QFile::ReadOwner|QFile::WriteOwner|QFile::ReadUser|QFile::WriteUser); - // Remove the file - return f.remove(); -} - -/** - * Returns the size of a file. - * If the file is a folder, it will compute its size based on its content. - * - * Returns -1 in case of error. - */ -qint64 fsutils::computePathSize(const QString& path) { - // Check if it is a file - QFileInfo fi(path); - if (!fi.exists()) return -1; - if (fi.isFile()) return fi.size(); - // Compute folder size based on its content - qint64 size = 0; - foreach (const QFileInfo &subfi, QDir(path).entryInfoList(QDir::Dirs|QDir::Files)) { - if (subfi.fileName().startsWith(".")) continue; - if (subfi.isDir()) - size += fsutils::computePathSize(subfi.absoluteFilePath()); - else - size += subfi.size(); - } - return size; -} - -/** - * Makes deep comparison of two files to make sure they are identical. - */ -bool fsutils::sameFiles(const QString& path1, const QString& path2) { - QFile f1(path1), f2(path2); - if (!f1.exists() || !f2.exists()) return false; - if (f1.size() != f2.size()) return false; - if (!f1.open(QIODevice::ReadOnly)) return false; - if (!f2.open(QIODevice::ReadOnly)) { - f1.close(); - return false; - } - bool same = true; - while(!f1.atEnd() && !f2.atEnd()) { - if (f1.read(1024) != f2.read(1024)) { - same = false; - break; - } - } - f1.close(); f2.close(); - return same; -} - -QString fsutils::toValidFileSystemName(QString filename) { - qDebug("toValidFSName: %s", qPrintable(filename)); - const QRegExp regex("[\\\\/:?\"*<>|]"); - filename.replace(regex, " "); - qDebug("toValidFSName, result: %s", qPrintable(filename)); - return filename.trimmed(); -} - -bool fsutils::isValidFileSystemName(const QString& filename) { - if (filename.isEmpty()) return false; - const QRegExp regex("[\\\\/:?\"*<>|]"); - return !filename.contains(regex); -} - -long long fsutils::freeDiskSpaceOnPath(QString path) { - if (path.isEmpty()) return -1; - QDir dir_path(path); - if (!dir_path.exists()) { - QStringList parts = path.split("/"); - while (parts.size() > 1 && !QDir(parts.join("/")).exists()) { - parts.removeLast(); - } - dir_path = QDir(parts.join("/")); - if (!dir_path.exists()) return -1; - } - Q_ASSERT(dir_path.exists()); - -#ifndef Q_OS_WIN - unsigned long long available; -#ifdef Q_OS_HAIKU - const QString statfs_path = dir_path.path()+"/."; - dev_t device = dev_for_path (qPrintable(statfs_path)); - if (device >= 0) { - fs_info info; - if(fs_stat_dev(device, &info)==B_OK){ - available = ((unsigned long long)(info.free_blocks*info.block_size)); - return available; - } - } - return -1; -#else - struct statfs stats; - const QString statfs_path = dir_path.path()+"/."; - const int ret = statfs (qPrintable(statfs_path), &stats) ; - if (ret == 0) { - available = ((unsigned long long)stats.f_bavail) * - ((unsigned long long)stats.f_bsize) ; - return available; - } else { - return -1; - } -#endif -#else - typedef BOOL (WINAPI *GetDiskFreeSpaceEx_t)(LPCTSTR, - PULARGE_INTEGER, - PULARGE_INTEGER, - PULARGE_INTEGER); - GetDiskFreeSpaceEx_t - pGetDiskFreeSpaceEx = (GetDiskFreeSpaceEx_t)::GetProcAddress - ( - ::GetModuleHandle(TEXT("kernel32.dll")), - "GetDiskFreeSpaceExW" - ); - if ( pGetDiskFreeSpaceEx ) - { - ULARGE_INTEGER bytesFree, bytesTotal; - unsigned long long *ret; - if (pGetDiskFreeSpaceEx((LPCTSTR)(fsutils::toNativePath(dir_path.path())).utf16(), &bytesFree, &bytesTotal, NULL)) { - ret = (unsigned long long*)&bytesFree; - return *ret; - } else { - return -1; - } - } else { - return -1; - } -#endif -} - -QString fsutils::branchPath(const QString& file_path, QString* removed) { - QString ret = fsutils::fromNativePath(file_path); - if (ret.endsWith("/")) - ret.chop(1); - const int slashIndex = ret.lastIndexOf("/"); - if (slashIndex >= 0) { - if (removed) - *removed = ret.mid(slashIndex + 1); - ret = ret.left(slashIndex); - } - return ret; -} - -bool fsutils::sameFileNames(const QString &first, const QString &second) { -#if defined(Q_OS_UNIX) || defined(Q_WS_QWS) - return QString::compare(first, second, Qt::CaseSensitive) == 0; -#else - return QString::compare(first, second, Qt::CaseInsensitive) == 0; -#endif -} - -QString fsutils::expandPath(const QString &path) { - QString ret = fsutils::fromNativePath(path.trimmed()); - if (ret.isEmpty()) - return ret; - - return QDir::cleanPath(ret); -} - -QString fsutils::expandPathAbs(const QString& path) { - QString ret = fsutils::expandPath(path); - - if (!QDir::isAbsolutePath(ret)) - ret = QDir(ret).absolutePath(); - - return ret; -} - -QString fsutils::QDesktopServicesDataLocation() { - QString result; -#ifdef Q_OS_WIN - LPWSTR path=new WCHAR[256]; - if (SHGetSpecialFolderPath(0, path, CSIDL_LOCAL_APPDATA, FALSE)) - result = fsutils::fromNativePath(QString::fromWCharArray(path)); - if (!QCoreApplication::applicationName().isEmpty()) - result += QLatin1String("/") + qApp->applicationName(); -#else -#ifdef Q_OS_MAC - FSRef ref; - OSErr err = FSFindFolder(kUserDomain, kApplicationSupportFolderType, false, &ref); - if (err) - return QString(); - QString path; - QByteArray ba(2048, 0); - if (FSRefMakePath(&ref, reinterpret_cast(ba.data()), ba.size()) == noErr) - result = QString::fromUtf8(ba).normalized(QString::NormalizationForm_C); - result += QLatin1Char('/') + qApp->applicationName(); -#else - QString xdgDataHome = QLatin1String(qgetenv("XDG_DATA_HOME")); - if (xdgDataHome.isEmpty()) - xdgDataHome = QDir::homePath() + QLatin1String("/.local/share"); - xdgDataHome += QLatin1String("/data/") - + qApp->applicationName(); - result = xdgDataHome; -#endif -#endif - if (!result.endsWith("/")) - result += "/"; - return result; -} - -QString fsutils::QDesktopServicesCacheLocation() { - QString result; -#if defined(Q_OS_WIN) || defined(Q_OS_OS2) - result = QDesktopServicesDataLocation() + QLatin1String("cache"); -#else -#ifdef Q_OS_MAC - // http://developer.apple.com/documentation/Carbon/Reference/Folder_Manager/Reference/reference.html - FSRef ref; - OSErr err = FSFindFolder(kUserDomain, kCachedDataFolderType, false, &ref); - if (err) - return QString(); - QByteArray ba(2048, 0); - if (FSRefMakePath(&ref, reinterpret_cast(ba.data()), ba.size()) == noErr) - result = QString::fromUtf8(ba).normalized(QString::NormalizationForm_C); - result += QLatin1Char('/') + qApp->applicationName(); -#else - QString xdgCacheHome = QLatin1String(qgetenv("XDG_CACHE_HOME")); - if (xdgCacheHome.isEmpty()) - xdgCacheHome = QDir::homePath() + QLatin1String("/.cache"); - xdgCacheHome += QLatin1Char('/') + QCoreApplication::applicationName(); - result = xdgCacheHome; -#endif -#endif - if (!result.endsWith("/")) - result += "/"; - return result; -} - -QString fsutils::QDesktopServicesDownloadLocation() { -#if defined(Q_OS_WIN) || defined(Q_OS_OS2) - // as long as it stays WinXP like we do the same on OS/2 - // TODO: Use IKnownFolderManager to get path of FOLDERID_Downloads - // instead of hardcoding "Downloads" - // Unfortunately, this would break compatibility with WinXP -#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) - return QDir(QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation)).absoluteFilePath( - QCoreApplication::translate("fsutils", "Downloads")); -#else - return QDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)).absoluteFilePath( - QCoreApplication::translate("fsutils", "Downloads")); -#endif -#endif - -#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) - QString save_path; - // Default save path on Linux - QString config_path = QString::fromLocal8Bit(qgetenv("XDG_CONFIG_HOME").constData()); - if (config_path.isEmpty()) - config_path = QDir::home().absoluteFilePath(".config"); - - QString user_dirs_file = config_path + "/user-dirs.dirs"; - if (QFile::exists(user_dirs_file)) { - QSettings settings(user_dirs_file, QSettings::IniFormat); - // We need to force UTF-8 encoding here since this is not - // the default for Ini files. - settings.setIniCodec("UTF-8"); - QString xdg_download_dir = settings.value("XDG_DOWNLOAD_DIR").toString(); - if (!xdg_download_dir.isEmpty()) { - // Resolve $HOME environment variables - xdg_download_dir.replace("$HOME", QDir::homePath()); - save_path = xdg_download_dir; - qDebug() << Q_FUNC_INFO << "SUCCESS: Using XDG path for downloads: " << save_path; - } - } - - // Fallback - if (!save_path.isEmpty() && !QFile::exists(save_path)) { - QDir().mkpath(save_path); - } - - if (save_path.isEmpty() || !QFile::exists(save_path)) { - save_path = QDir::home().absoluteFilePath(QCoreApplication::translate("fsutils", "Downloads")); - qDebug() << Q_FUNC_INFO << "using" << save_path << "as fallback since the XDG detection did not work"; - } - - return save_path; -#endif - -#ifdef Q_OS_MAC - // TODO: How to support this on Mac OS X? -#endif - - // Fallback - return QDir::home().absoluteFilePath(QCoreApplication::translate("fsutils", "Downloads")); -} - -QString fsutils::searchEngineLocation() { - QString folder = "nova"; - if (misc::pythonVersion() >= 3) - folder = "nova3"; - const QString location = fsutils::expandPathAbs(QDesktopServicesDataLocation() - + folder); - QDir locationDir(location); - if (!locationDir.exists()) - locationDir.mkpath(locationDir.absolutePath()); - return location; -} - -QString fsutils::cacheLocation() { - QString location = fsutils::expandPathAbs(QDesktopServicesCacheLocation()); - QDir locationDir(location); - if (!locationDir.exists()) - locationDir.mkpath(locationDir.absolutePath()); - return location; -} diff --git a/src/core/net/downloadhandler.cpp b/src/core/net/downloadhandler.cpp index 99bb88af5..f642c8b55 100644 --- a/src/core/net/downloadhandler.cpp +++ b/src/core/net/downloadhandler.cpp @@ -38,8 +38,8 @@ #include -#include "core/fs_utils.h" -#include "core/misc.h" +#include "core/utils/fs.h" +#include "core/utils/misc.h" #include "downloadmanager.h" #include "downloadhandler.h" @@ -108,7 +108,7 @@ void DownloadHandler::checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal) // Total number of bytes is available if (bytesTotal > m_sizeLimit) { m_reply->abort(); - emit downloadFailed(m_url, msg.arg(misc::friendlyUnit(bytesTotal)).arg(misc::friendlyUnit(m_sizeLimit))); + emit downloadFailed(m_url, msg.arg(Utils::Misc::friendlyUnit(bytesTotal)).arg(Utils::Misc::friendlyUnit(m_sizeLimit))); } else { disconnect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(checkDownloadSize(qint64, qint64))); @@ -116,7 +116,7 @@ void DownloadHandler::checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal) } else if (bytesReceived > m_sizeLimit) { m_reply->abort(); - emit downloadFailed(m_url, msg.arg(misc::friendlyUnit(bytesReceived)).arg(misc::friendlyUnit(m_sizeLimit))); + emit downloadFailed(m_url, msg.arg(Utils::Misc::friendlyUnit(bytesReceived)).arg(Utils::Misc::friendlyUnit(m_sizeLimit))); } } @@ -154,7 +154,7 @@ bool DownloadHandler::saveToFile(QString &filePath) } else { delete tmpfile; - fsutils::forceRemove(filePath); + Utils::Fs::forceRemove(filePath); } return false; diff --git a/src/core/preferences.cpp b/src/core/preferences.cpp index d82739788..3f8b4754b 100644 --- a/src/core/preferences.cpp +++ b/src/core/preferences.cpp @@ -50,8 +50,8 @@ #include #endif -#include "misc.h" -#include "fs_utils.h" +#include "core/utils/fs.h" +#include "core/utils/misc.h" Preferences* Preferences::m_instance = 0; @@ -169,7 +169,7 @@ bool Preferences::save() QString final_path = new_path; int index = final_path.lastIndexOf("_new", -1, Qt::CaseInsensitive); final_path.remove(index, 4); - fsutils::forceRemove(final_path); + Utils::Fs::forceRemove(final_path); QFile::rename(new_path, final_path); #else delete settings; @@ -347,7 +347,7 @@ void Preferences::setWinStartup(bool b) { QSettings settings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat); if (b) { - const QString bin_path = "\"" + fsutils::toNativePath(qApp->applicationFilePath()) + "\""; + const QString bin_path = "\"" + Utils::Fs::toNativePath(qApp->applicationFilePath()) + "\""; settings.setValue("qBittorrent", bin_path); } else { @@ -361,13 +361,13 @@ QString Preferences::getSavePath() const { QString save_path = value("Preferences/Downloads/SavePath").toString(); if (!save_path.isEmpty()) - return fsutils::fromNativePath(save_path); - return fsutils::QDesktopServicesDownloadLocation(); + return Utils::Fs::fromNativePath(save_path); + return Utils::Fs::QDesktopServicesDownloadLocation(); } void Preferences::setSavePath(const QString &save_path) { - setValue("Preferences/Downloads/SavePath", fsutils::fromNativePath(save_path)); + setValue("Preferences/Downloads/SavePath", Utils::Fs::fromNativePath(save_path)); } bool Preferences::isTempPathEnabled() const @@ -383,12 +383,12 @@ void Preferences::setTempPathEnabled(bool enabled) QString Preferences::getTempPath() const { const QString temp = QDir(getSavePath()).absoluteFilePath("temp"); - return fsutils::fromNativePath(value("Preferences/Downloads/TempPath", temp).toString()); + return Utils::Fs::fromNativePath(value("Preferences/Downloads/TempPath", temp).toString()); } void Preferences::setTempPath(const QString &path) { - setValue("Preferences/Downloads/TempPath", fsutils::fromNativePath(path)); + setValue("Preferences/Downloads/TempPath", Utils::Fs::fromNativePath(path)); } bool Preferences::useIncompleteFilesExtension() const @@ -413,12 +413,12 @@ void Preferences::setAppendTorrentLabel(bool b) QString Preferences::lastLocationPath() const { - return fsutils::fromNativePath(value("Preferences/Downloads/LastLocationPath").toString()); + return Utils::Fs::fromNativePath(value("Preferences/Downloads/LastLocationPath").toString()); } void Preferences::setLastLocationPath(const QString &path) { - setValue("Preferences/Downloads/LastLocationPath", fsutils::fromNativePath(path)); + setValue("Preferences/Downloads/LastLocationPath", Utils::Fs::fromNativePath(path)); } bool Preferences::preAllocateAllFiles() const @@ -469,7 +469,7 @@ QStringList Preferences::getScanDirs() const QStringList newList; foreach (const QString& s, originalList) - newList << fsutils::fromNativePath(s); + newList << Utils::Fs::fromNativePath(s); return newList; } @@ -479,28 +479,28 @@ void Preferences::setScanDirs(const QStringList &dirs) QStringList newList; if (!dirs.isEmpty()) foreach (const QString& s, dirs) - newList << fsutils::fromNativePath(s); + newList << Utils::Fs::fromNativePath(s); setValue("Preferences/Downloads/ScanDirs", newList); } QList Preferences::getDownloadInScanDirs() const { - return misc::boolListfromStringList(value("Preferences/Downloads/DownloadInScanDirs").toStringList()); + return Utils::Misc::boolListfromStringList(value("Preferences/Downloads/DownloadInScanDirs").toStringList()); } void Preferences::setDownloadInScanDirs(const QList &list) { - setValue("Preferences/Downloads/DownloadInScanDirs", misc::toStringList(list)); + setValue("Preferences/Downloads/DownloadInScanDirs", Utils::Misc::toStringList(list)); } QString Preferences::getScanDirsLastPath() const { - return fsutils::fromNativePath(value("Preferences/Downloads/ScanDirsLastPath").toString()); + return Utils::Fs::fromNativePath(value("Preferences/Downloads/ScanDirsLastPath").toString()); } void Preferences::setScanDirsLastPath(const QString &path) { - setValue("Preferences/Downloads/ScanDirsLastPath", fsutils::fromNativePath(path)); + setValue("Preferences/Downloads/ScanDirsLastPath", Utils::Fs::fromNativePath(path)); } bool Preferences::isTorrentExportEnabled() const @@ -510,12 +510,12 @@ bool Preferences::isTorrentExportEnabled() const QString Preferences::getTorrentExportDir() const { - return fsutils::fromNativePath(value("Preferences/Downloads/TorrentExportDir").toString()); + return Utils::Fs::fromNativePath(value("Preferences/Downloads/TorrentExportDir").toString()); } void Preferences::setTorrentExportDir(QString path) { - setValue("Preferences/Downloads/TorrentExportDir", fsutils::fromNativePath(path.trimmed())); + setValue("Preferences/Downloads/TorrentExportDir", Utils::Fs::fromNativePath(path.trimmed())); } bool Preferences::isFinishedTorrentExportEnabled() const @@ -525,12 +525,12 @@ bool Preferences::isFinishedTorrentExportEnabled() const QString Preferences::getFinishedTorrentExportDir() const { - return fsutils::fromNativePath(value("Preferences/Downloads/FinishedTorrentExportDir").toString()); + return Utils::Fs::fromNativePath(value("Preferences/Downloads/FinishedTorrentExportDir").toString()); } void Preferences::setFinishedTorrentExportDir(QString path) { - setValue("Preferences/Downloads/FinishedTorrentExportDir", fsutils::fromNativePath(path.trimmed())); + setValue("Preferences/Downloads/FinishedTorrentExportDir", Utils::Fs::fromNativePath(path.trimmed())); } bool Preferences::isMailNotificationEnabled() const @@ -975,12 +975,12 @@ void Preferences::setFilteringEnabled(bool enabled) QString Preferences::getFilter() const { - return fsutils::fromNativePath(value("Preferences/IPFilter/File").toString()); + return Utils::Fs::fromNativePath(value("Preferences/IPFilter/File").toString()); } void Preferences::setFilter(const QString &path) { - setValue("Preferences/IPFilter/File", fsutils::fromNativePath(path)); + setValue("Preferences/IPFilter/File", Utils::Fs::fromNativePath(path)); } QStringList Preferences::bannedIPs() const @@ -1275,12 +1275,12 @@ void Preferences::setAutoRunEnabled(bool enabled) QString Preferences::getAutoRunProgram() const { - return fsutils::fromNativePath(value("AutoRun/program").toString()); + return Utils::Fs::fromNativePath(value("AutoRun/program").toString()); } void Preferences::setAutoRunProgram(const QString &program) { - setValue("AutoRun/program", fsutils::fromNativePath(program)); + setValue("AutoRun/program", Utils::Fs::fromNativePath(program)); } bool Preferences::shutdownWhenDownloadsComplete() const @@ -1788,12 +1788,12 @@ bool Preferences::isMagnetLinkAssocSet() // Check magnet link assoc QRegExp exe_reg("\"([^\"]+)\".*"); - QString shell_command = fsutils::toNativePath(settings.value("magnet/shell/open/command/Default", "").toString()); + QString shell_command = Utils::Fs::toNativePath(settings.value("magnet/shell/open/command/Default", "").toString()); if (exe_reg.indexIn(shell_command) < 0) return false; QString assoc_exe = exe_reg.cap(1); qDebug("exe: %s", qPrintable(assoc_exe)); - if (assoc_exe.compare(fsutils::toNativePath(qApp->applicationFilePath()), Qt::CaseInsensitive) != 0) + if (assoc_exe.compare(Utils::Fs::toNativePath(qApp->applicationFilePath()), Qt::CaseInsensitive) != 0) return false; return true; @@ -1829,9 +1829,9 @@ void Preferences::setMagnetLinkAssoc(bool set) settings.setValue("magnet/Default", "URL:Magnet link"); settings.setValue("magnet/Content Type", "application/x-magnet"); settings.setValue("magnet/URL Protocol", ""); - settings.setValue("magnet/DefaultIcon/Default", fsutils::toNativePath(icon_str)); + settings.setValue("magnet/DefaultIcon/Default", Utils::Fs::toNativePath(icon_str)); settings.setValue("magnet/shell/Default", "open"); - settings.setValue("magnet/shell/open/command/Default", fsutils::toNativePath(command_str)); + settings.setValue("magnet/shell/open/command/Default", Utils::Fs::toNativePath(command_str)); } else if (isMagnetLinkAssocSet()) { settings.remove("magnet"); diff --git a/src/core/scanfoldersmodel.cpp b/src/core/scanfoldersmodel.cpp index a63071a38..73ef8c9d1 100644 --- a/src/core/scanfoldersmodel.cpp +++ b/src/core/scanfoldersmodel.cpp @@ -34,8 +34,8 @@ #include #include -#include "misc.h" -#include "fs_utils.h" +#include "utils/misc.h" +#include "utils/fs.h" #include "preferences.h" #include "filesystemwatcher.h" #include "bittorrent/session.h" @@ -122,7 +122,7 @@ QVariant ScanFoldersModel::data(const QModelIndex &index, int role) const const PathData *pathData = m_pathList.at(index.row()); if ((index.column() == PathColumn) && (role == Qt::DisplayRole)) - return fsutils::toNativePath(pathData->path); + return Utils::Fs::toNativePath(pathData->path); if ((index.column() == DownloadAtTorrentColumn) && (role == Qt::CheckStateRole)) return (pathData->downloadAtPath ? Qt::Checked : Qt::Unchecked); @@ -230,7 +230,7 @@ bool ScanFoldersModel::downloadInTorrentFolder(const QString &filePath) const int ScanFoldersModel::findPathData(const QString &path) const { for (int i = 0; i < m_pathList.count(); ++i) - if (m_pathList.at(i)->path == fsutils::fromNativePath(path)) + if (m_pathList.at(i)->path == Utils::Fs::fromNativePath(path)) return i; return -1; @@ -285,7 +285,7 @@ void ScanFoldersModel::addTorrentsToSession(const QStringList &pathList) BitTorrent::TorrentInfo torrentInfo = BitTorrent::TorrentInfo::loadFromFile(file); if (torrentInfo.isValid()) { BitTorrent::Session::instance()->addTorrent(torrentInfo, params); - fsutils::forceRemove(file); + Utils::Fs::forceRemove(file); } else { qDebug("Ignoring incomplete torrent file: %s", qPrintable(file)); diff --git a/src/core/types.h b/src/core/types.h index 5716ae34c..4415191ff 100644 --- a/src/core/types.h +++ b/src/core/types.h @@ -45,6 +45,7 @@ public:\ #define END_SCOPED_ENUM ; }; +const qlonglong MAX_ETA = 8640000; BEGIN_SCOPED_ENUM(MaxRatioAction) { @@ -60,4 +61,13 @@ BEGIN_SCOPED_ENUM(TorrentExportFolder) } END_SCOPED_ENUM +BEGIN_SCOPED_ENUM(ShutdownAction) +{ + None, + Shutdown, + Suspend, + Hibernate +} +END_SCOPED_ENUM + #endif // TYPES_H diff --git a/src/core/utils/fs.cpp b/src/core/utils/fs.cpp new file mode 100644 index 000000000..48af976ce --- /dev/null +++ b/src/core/utils/fs.cpp @@ -0,0 +1,492 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2012 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 + +#ifdef Q_OS_MAC +#include +#include +#endif + +#ifndef Q_OS_WIN +#if defined(Q_OS_MAC) || defined(Q_OS_FREEBSD) +#include +#include +#elif defined(Q_OS_HAIKU) +#include +#else +#include +#endif +#else +#include +#include +#endif + +#if defined(Q_OS_WIN) || defined(Q_OS_OS2) +#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) +#include +#else +#include +#endif +#endif + +#include "misc.h" +#include "fs.h" + +/** + * Converts a path to a string suitable for display. + * This function makes sure the directory separator used is consistent + * with the OS being run. + */ +QString Utils::Fs::toNativePath(const QString& path) +{ + return QDir::toNativeSeparators(path); +} + +QString Utils::Fs::fromNativePath(const QString &path) +{ + return QDir::fromNativeSeparators(path); +} + +/** + * Returns the file extension part of a file name. + */ +QString Utils::Fs::fileExtension(const QString &filename) +{ + QString ext = QString(filename).remove(".!qB"); + const int point_index = ext.lastIndexOf("."); + return (point_index >= 0) ? ext.mid(point_index + 1) : QString(); +} + +QString Utils::Fs::fileName(const QString& file_path) +{ + QString path = fromNativePath(file_path); + const int slash_index = path.lastIndexOf("/"); + if (slash_index == -1) + return path; + return path.mid(slash_index + 1); +} + +QString Utils::Fs::folderName(const QString& file_path) +{ + QString path = fromNativePath(file_path); + const int slash_index = path.lastIndexOf("/"); + if (slash_index == -1) + return path; + return path.left(slash_index); +} + +/** + * Remove an empty folder tree. + * + * This function will also remove .DS_Store files on Mac OS and + * Thumbs.db on Windows. + */ +bool Utils::Fs::smartRemoveEmptyFolderTree(const QString& dir_path) +{ + qDebug() << Q_FUNC_INFO << dir_path; + if (dir_path.isEmpty()) + return false; + + QDir dir(dir_path); + if (!dir.exists()) + return true; + + // Remove Files created by the OS +#if defined Q_OS_MAC + forceRemove(dir_path + QLatin1String("/.DS_Store")); +#elif defined Q_OS_WIN + forceRemove(dir_path + QLatin1String("/Thumbs.db")); +#endif + + QFileInfoList sub_files = dir.entryInfoList(); + foreach (const QFileInfo& info, sub_files) { + QString sub_name = info.fileName(); + if (sub_name == "." || sub_name == "..") + continue; + + QString sub_path = info.absoluteFilePath(); + qDebug() << Q_FUNC_INFO << "sub file: " << sub_path; + if (info.isDir()) { + if (!smartRemoveEmptyFolderTree(sub_path)) { + qWarning() << Q_FUNC_INFO << "Failed to remove folder: " << sub_path; + return false; + } + } + else { + if (info.isHidden()) { + qDebug() << Q_FUNC_INFO << "Removing hidden file: " << sub_path; + if (!forceRemove(sub_path)) { + qWarning() << Q_FUNC_INFO << "Failed to remove " << sub_path; + return false; + } + } + else { + qWarning() << Q_FUNC_INFO << "Folder is not empty, aborting. Found: " << sub_path; + } + } + } + qDebug() << Q_FUNC_INFO << "Calling rmdir on " << dir_path; + return QDir().rmdir(dir_path); +} + +/** + * Removes the file with the given file_path. + * + * This function will try to fix the file permissions before removing it. + */ +bool Utils::Fs::forceRemove(const QString& file_path) +{ + QFile f(file_path); + if (!f.exists()) + return true; + // Make sure we have read/write permissions + f.setPermissions(f.permissions() | QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::WriteUser); + // Remove the file + return f.remove(); +} + +/** + * Returns the size of a file. + * If the file is a folder, it will compute its size based on its content. + * + * Returns -1 in case of error. + */ +qint64 Utils::Fs::computePathSize(const QString& path) +{ + // Check if it is a file + QFileInfo fi(path); + if (!fi.exists()) return -1; + if (fi.isFile()) return fi.size(); + // Compute folder size based on its content + qint64 size = 0; + foreach (const QFileInfo &subfi, QDir(path).entryInfoList(QDir::Dirs|QDir::Files)) { + if (subfi.fileName().startsWith(".")) continue; + if (subfi.isDir()) + size += computePathSize(subfi.absoluteFilePath()); + else + size += subfi.size(); + } + return size; +} + +/** + * Makes deep comparison of two files to make sure they are identical. + */ +bool Utils::Fs::sameFiles(const QString& path1, const QString& path2) +{ + QFile f1(path1), f2(path2); + if (!f1.exists() || !f2.exists()) return false; + if (f1.size() != f2.size()) return false; + if (!f1.open(QIODevice::ReadOnly)) return false; + if (!f2.open(QIODevice::ReadOnly)) { + f1.close(); + return false; + } + bool same = true; + while(!f1.atEnd() && !f2.atEnd()) { + if (f1.read(1024) != f2.read(1024)) { + same = false; + break; + } + } + f1.close(); f2.close(); + return same; +} + +QString Utils::Fs::toValidFileSystemName(QString filename) +{ + qDebug("toValidFSName: %s", qPrintable(filename)); + const QRegExp regex("[\\\\/:?\"*<>|]"); + filename.replace(regex, " "); + qDebug("toValidFSName, result: %s", qPrintable(filename)); + return filename.trimmed(); +} + +bool Utils::Fs::isValidFileSystemName(const QString& filename) +{ + if (filename.isEmpty()) return false; + const QRegExp regex("[\\\\/:?\"*<>|]"); + return !filename.contains(regex); +} + +qlonglong Utils::Fs::freeDiskSpaceOnPath(QString path) +{ + if (path.isEmpty()) return -1; + QDir dir_path(path); + if (!dir_path.exists()) { + QStringList parts = path.split("/"); + while (parts.size() > 1 && !QDir(parts.join("/")).exists()) { + parts.removeLast(); + } + dir_path = QDir(parts.join("/")); + if (!dir_path.exists()) return -1; + } + Q_ASSERT(dir_path.exists()); + +#ifndef Q_OS_WIN + unsigned long long available; +#ifdef Q_OS_HAIKU + const QString statfs_path = dir_path.path() + "/."; + dev_t device = dev_for_path (qPrintable(statfs_path)); + if (device >= 0) { + fs_info info; + if (fs_stat_dev(device, &info) == B_OK) { + available = ((unsigned long long)(info.free_blocks * info.block_size)); + return available; + } + } + return -1; +#else + struct statfs stats; + const QString statfs_path = dir_path.path() + "/."; + const int ret = statfs(qPrintable(statfs_path), &stats); + if (ret == 0) { + available = ((unsigned long long)stats.f_bavail) + * ((unsigned long long)stats.f_bsize); + return available; + } + else { + return -1; + } +#endif +#else + typedef BOOL (WINAPI *GetDiskFreeSpaceEx_t)(LPCTSTR, + PULARGE_INTEGER, + PULARGE_INTEGER, + PULARGE_INTEGER); + GetDiskFreeSpaceEx_t pGetDiskFreeSpaceEx = + (GetDiskFreeSpaceEx_t)::GetProcAddress(::GetModuleHandleW(L"kernel32.dll"), "GetDiskFreeSpaceExW"); + if (pGetDiskFreeSpaceEx) { + ULARGE_INTEGER bytesFree, bytesTotal; + unsigned long long *ret; + if (pGetDiskFreeSpaceEx((LPCTSTR)(toNativePath(dir_path.path())).utf16(), &bytesFree, &bytesTotal, NULL)) { + ret = (unsigned long long*)&bytesFree; + return *ret; + } + else { + return -1; + } + } + else { + return -1; + } +#endif +} + +QString Utils::Fs::branchPath(const QString& file_path, QString* removed) +{ + QString ret = fromNativePath(file_path); + if (ret.endsWith("/")) + ret.chop(1); + const int slashIndex = ret.lastIndexOf("/"); + if (slashIndex >= 0) { + if (removed) + *removed = ret.mid(slashIndex + 1); + ret = ret.left(slashIndex); + } + return ret; +} + +bool Utils::Fs::sameFileNames(const QString &first, const QString &second) +{ +#if defined(Q_OS_UNIX) || defined(Q_WS_QWS) + return QString::compare(first, second, Qt::CaseSensitive) == 0; +#else + return QString::compare(first, second, Qt::CaseInsensitive) == 0; +#endif +} + +QString Utils::Fs::expandPath(const QString &path) +{ + QString ret = fromNativePath(path.trimmed()); + if (ret.isEmpty()) + return ret; + + return QDir::cleanPath(ret); +} + +QString Utils::Fs::expandPathAbs(const QString& path) +{ + QString ret = expandPath(path); + + if (!QDir::isAbsolutePath(ret)) + ret = QDir(ret).absolutePath(); + + return ret; +} + +QString Utils::Fs::QDesktopServicesDataLocation() +{ + QString result; +#ifdef Q_OS_WIN + LPWSTR path=new WCHAR[256]; + if (SHGetSpecialFolderPath(0, path, CSIDL_LOCAL_APPDATA, FALSE)) + result = fromNativePath(QString::fromWCharArray(path)); + if (!QCoreApplication::applicationName().isEmpty()) + result += QLatin1String("/") + qApp->applicationName(); +#else +#ifdef Q_OS_MAC + FSRef ref; + OSErr err = FSFindFolder(kUserDomain, kApplicationSupportFolderType, false, &ref); + if (err) + return QString(); + QString path; + QByteArray ba(2048, 0); + if (FSRefMakePath(&ref, reinterpret_cast(ba.data()), ba.size()) == noErr) + result = QString::fromUtf8(ba).normalized(QString::NormalizationForm_C); + result += QLatin1Char('/') + qApp->applicationName(); +#else + QString xdgDataHome = QLatin1String(qgetenv("XDG_DATA_HOME")); + if (xdgDataHome.isEmpty()) + xdgDataHome = QDir::homePath() + QLatin1String("/.local/share"); + xdgDataHome += QLatin1String("/data/") + + qApp->applicationName(); + result = xdgDataHome; +#endif +#endif + if (!result.endsWith("/")) + result += "/"; + return result; +} + +QString Utils::Fs::QDesktopServicesCacheLocation() +{ + QString result; +#if defined(Q_OS_WIN) || defined(Q_OS_OS2) + result = QDesktopServicesDataLocation() + QLatin1String("cache"); +#else +#ifdef Q_OS_MAC + // http://developer.apple.com/documentation/Carbon/Reference/Folder_Manager/Reference/reference.html + FSRef ref; + OSErr err = FSFindFolder(kUserDomain, kCachedDataFolderType, false, &ref); + if (err) + return QString(); + QByteArray ba(2048, 0); + if (FSRefMakePath(&ref, reinterpret_cast(ba.data()), ba.size()) == noErr) + result = QString::fromUtf8(ba).normalized(QString::NormalizationForm_C); + result += QLatin1Char('/') + qApp->applicationName(); +#else + QString xdgCacheHome = QLatin1String(qgetenv("XDG_CACHE_HOME")); + if (xdgCacheHome.isEmpty()) + xdgCacheHome = QDir::homePath() + QLatin1String("/.cache"); + xdgCacheHome += QLatin1Char('/') + QCoreApplication::applicationName(); + result = xdgCacheHome; +#endif +#endif + if (!result.endsWith("/")) + result += "/"; + return result; +} + +QString Utils::Fs::QDesktopServicesDownloadLocation() +{ +#if defined(Q_OS_WIN) || defined(Q_OS_OS2) + // as long as it stays WinXP like we do the same on OS/2 + // TODO: Use IKnownFolderManager to get path of FOLDERID_Downloads + // instead of hardcoding "Downloads" + // Unfortunately, this would break compatibility with WinXP +#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) + return QDir(QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation)).absoluteFilePath( + QCoreApplication::translate("fsutils", "Downloads")); +#else + return QDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)).absoluteFilePath( + QCoreApplication::translate("fsutils", "Downloads")); +#endif +#endif + +#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) + QString save_path; + // Default save path on Linux + QString config_path = QString::fromLocal8Bit(qgetenv("XDG_CONFIG_HOME").constData()); + if (config_path.isEmpty()) + config_path = QDir::home().absoluteFilePath(".config"); + + QString user_dirs_file = config_path + "/user-dirs.dirs"; + if (QFile::exists(user_dirs_file)) { + QSettings settings(user_dirs_file, QSettings::IniFormat); + // We need to force UTF-8 encoding here since this is not + // the default for Ini files. + settings.setIniCodec("UTF-8"); + QString xdg_download_dir = settings.value("XDG_DOWNLOAD_DIR").toString(); + if (!xdg_download_dir.isEmpty()) { + // Resolve $HOME environment variables + xdg_download_dir.replace("$HOME", QDir::homePath()); + save_path = xdg_download_dir; + qDebug() << Q_FUNC_INFO << "SUCCESS: Using XDG path for downloads: " << save_path; + } + } + + // Fallback + if (!save_path.isEmpty() && !QFile::exists(save_path)) { + QDir().mkpath(save_path); + } + + if (save_path.isEmpty() || !QFile::exists(save_path)) { + save_path = QDir::home().absoluteFilePath(QCoreApplication::translate("fsutils", "Downloads")); + qDebug() << Q_FUNC_INFO << "using" << save_path << "as fallback since the XDG detection did not work"; + } + + return save_path; +#endif + +#ifdef Q_OS_MAC + // TODO: How to support this on Mac OS X? +#endif + + // Fallback + return QDir::home().absoluteFilePath(QCoreApplication::translate("fsutils", "Downloads")); +} + +QString Utils::Fs::searchEngineLocation() +{ + QString folder = "nova"; + if (Utils::Misc::pythonVersion() >= 3) + folder = "nova3"; + const QString location = expandPathAbs(QDesktopServicesDataLocation() + folder); + QDir locationDir(location); + if (!locationDir.exists()) + locationDir.mkpath(locationDir.absolutePath()); + return location; +} + +QString Utils::Fs::cacheLocation() +{ + QString location = expandPathAbs(QDesktopServicesCacheLocation()); + QDir locationDir(location); + if (!locationDir.exists()) + locationDir.mkpath(locationDir.absolutePath()); + return location; +} diff --git a/src/core/fs_utils.h b/src/core/utils/fs.h similarity index 51% rename from src/core/fs_utils.h rename to src/core/utils/fs.h index d5dceb94b..7bee4389c 100644 --- a/src/core/fs_utils.h +++ b/src/core/utils/fs.h @@ -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 @@ -28,43 +28,44 @@ * Contact : chris@qbittorrent.org */ -#ifndef FS_UTILS_H -#define FS_UTILS_H - -#include +#ifndef UTILS_FS_H +#define UTILS_FS_H /** * Utility functions related to file system. */ -namespace fsutils + +#include + +namespace Utils { + namespace Fs + { + QString toNativePath(const QString& path); + QString fromNativePath(const QString& path); + QString fileExtension(const QString& filename); + QString fileName(const QString& file_path); + QString folderName(const QString& file_path); + qint64 computePathSize(const QString& path); + bool sameFiles(const QString& path1, const QString& path2); + QString toValidFileSystemName(QString filename); + bool isValidFileSystemName(const QString& filename); + qlonglong freeDiskSpaceOnPath(QString path); + QString branchPath(const QString& file_path, QString* removed = 0); + bool sameFileNames(const QString& first, const QString& second); + QString expandPath(const QString& path); + QString expandPathAbs(const QString& path); + bool smartRemoveEmptyFolderTree(const QString& dir_path); + bool forceRemove(const QString& file_path); - QString toNativePath(const QString& path); - QString fromNativePath(const QString& path); - QString fileExtension(const QString& filename); - QString fileName(const QString& file_path); - QString folderName(const QString& file_path); - qint64 computePathSize(const QString& path); - bool sameFiles(const QString& path1, const QString& path2); - QString toValidFileSystemName(QString filename); - bool isValidFileSystemName(const QString& filename); - long long freeDiskSpaceOnPath(QString path); - QString branchPath(const QString& file_path, QString* removed = 0); - bool sameFileNames(const QString& first, const QString& second); - QString expandPath(const QString& path); - QString expandPathAbs(const QString& path); - bool smartRemoveEmptyFolderTree(const QString& dir_path); - bool forceRemove(const QString& file_path); - - /* Ported from Qt4 to drop dependency on QtGui */ - QString QDesktopServicesDataLocation(); - QString QDesktopServicesCacheLocation(); - QString QDesktopServicesDownloadLocation(); - /* End of Qt4 code */ - QString searchEngineLocation(); - QString cacheLocation(); - + /* Ported from Qt4 to drop dependency on QtGui */ + QString QDesktopServicesDataLocation(); + QString QDesktopServicesCacheLocation(); + QString QDesktopServicesDownloadLocation(); + /* End of Qt4 code */ + QString searchEngineLocation(); + QString cacheLocation(); + } } -#endif // FS_UTILS_H - +#endif // UTILS_FS_H diff --git a/src/core/misc.cpp b/src/core/utils/misc.cpp similarity index 79% rename from src/core/misc.cpp rename to src/core/utils/misc.cpp index 153035a1e..89f21dae2 100644 --- a/src/core/misc.cpp +++ b/src/core/utils/misc.cpp @@ -28,8 +28,6 @@ * Contact : chris@qbittorrent.org */ -#include - #include #include #include @@ -38,7 +36,6 @@ #include #include #include -#include #include #ifdef DISABLE_GUI @@ -69,6 +66,7 @@ const int UNLEN = 256; #endif #endif // DISABLE_GUI +#include "core/utils/string.h" #include "misc.h" static struct { const char *source; const char *comment; } units[] = { @@ -80,16 +78,16 @@ static struct { const char *source; const char *comment; } units[] = { }; #ifndef DISABLE_GUI -void misc::shutdownComputer(ShutDownAction action) +void Utils::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 - if (action != SHUTDOWN_COMPUTER) { + if (action != ShutdownAction::Shutdown) { // Some recent systems use systemd's logind QDBusInterface login1Iface("org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", QDBusConnection::systemBus()); if (login1Iface.isValid()) { - if (action == SUSPEND_COMPUTER) + if (action == ShutdownAction::Suspend) login1Iface.call("Suspend", false); else login1Iface.call("Hibernate", false); @@ -99,7 +97,7 @@ void misc::shutdownComputer(ShutDownAction action) QDBusInterface upowerIface("org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.UPower", QDBusConnection::systemBus()); if (upowerIface.isValid()) { - if (action == SUSPEND_COMPUTER) + if (action == ShutdownAction::Suspend) upowerIface.call("Suspend"); else upowerIface.call("Hibernate"); @@ -109,7 +107,7 @@ void misc::shutdownComputer(ShutDownAction action) QDBusInterface halIface("org.freedesktop.Hal", "/org/freedesktop/Hal/devices/computer", "org.freedesktop.Hal.Device.SystemPowerManagement", QDBusConnection::systemBus()); - if (action == SUSPEND_COMPUTER) + if (action == ShutdownAction::Suspend) halIface.call("Suspend", 5); else halIface.call("Hibernate"); @@ -138,7 +136,7 @@ void misc::shutdownComputer(ShutDownAction action) #endif #ifdef Q_OS_MAC AEEventID EventToSend; - if (action != SHUTDOWN_COMPUTER) + if (action != ShutdownAction::Shutdown) EventToSend = kAESleep; else EventToSend = kAEShutDown; @@ -193,9 +191,9 @@ void misc::shutdownComputer(ShutDownAction action) if (GetLastError() != ERROR_SUCCESS) return; - if (action == SUSPEND_COMPUTER) + if (action == ShutdownAction::Suspend) SetSuspendState(false, false, false); - else if (action == HIBERNATE_COMPUTER) + else if (action == ShutdownAction::Hibernate) SetSuspendState(true, false, false); else InitiateSystemShutdownA(0, QCoreApplication::translate("misc", "qBittorrent will shutdown the computer now because all downloads are complete.").toLocal8Bit().data(), 10, true, false); @@ -210,7 +208,7 @@ void misc::shutdownComputer(ShutDownAction action) #ifndef DISABLE_GUI // Get screen center -QPoint misc::screenCenter(QWidget *win) +QPoint Utils::Misc::screenCenter(QWidget *win) { int scrn = 0; const QWidget *w = win->window(); @@ -231,7 +229,7 @@ QPoint misc::screenCenter(QWidget *win) * Detects the version of python by calling * "python --version" and parsing the output. */ -int misc::pythonVersion() +int Utils::Misc::pythonVersion() { static int version = -1; if (version < 0) { @@ -257,7 +255,7 @@ int misc::pythonVersion() // see http://en.wikipedia.org/wiki/Kilobyte // value must be given in bytes // to send numbers instead of strings with suffixes -QString misc::friendlyUnit(qreal val, bool is_speed) +QString Utils::Misc::friendlyUnit(qreal val, bool is_speed) { if (val < 0) return QCoreApplication::translate("misc", "Unknown", "Unknown (size)"); @@ -268,13 +266,13 @@ QString misc::friendlyUnit(qreal val, bool is_speed) if (i == 0) ret = QString::number((long)val) + " " + QCoreApplication::translate("misc", units[0].source, units[0].comment); else - ret = accurateDoubleToString(val, 1) + " " + QCoreApplication::translate("misc", units[i].source, units[i].comment); + ret = Utils::String::fromDouble(val, 1) + " " + QCoreApplication::translate("misc", units[i].source, units[i].comment); if (is_speed) ret += QCoreApplication::translate("misc", "/s", "per second"); return ret; } -bool misc::isPreviewable(const QString& extension) +bool Utils::Misc::isPreviewable(const QString& extension) { static QSet multimedia_extensions; if (multimedia_extensions.empty()) { @@ -327,7 +325,7 @@ bool misc::isPreviewable(const QString& extension) return multimedia_extensions.contains(extension.toUpper()); } -QString misc::bcLinkToMagnet(QString bc_link) +QString Utils::Misc::bcLinkToMagnet(QString bc_link) { QByteArray raw_bc = bc_link.toUtf8(); raw_bc = raw_bc.mid(8); // skip bc://bt/ @@ -344,7 +342,7 @@ QString misc::bcLinkToMagnet(QString bc_link) // Take a number of seconds and return an user-friendly // time duration like "1d 2h 10m". -QString misc::userFriendlyDuration(qlonglong seconds) +QString Utils::Misc::userFriendlyDuration(qlonglong seconds) { if (seconds < 0 || seconds >= MAX_ETA) return QString::fromUtf8("∞"); @@ -366,7 +364,7 @@ QString misc::userFriendlyDuration(qlonglong seconds) return QString::fromUtf8("∞"); } -QString misc::getUserIDString() +QString Utils::Misc::getUserIDString() { QString uid = "0"; #ifdef Q_OS_WIN @@ -380,7 +378,7 @@ QString misc::getUserIDString() return uid; } -QStringList misc::toStringList(const QList &l) +QStringList Utils::Misc::toStringList(const QList &l) { QStringList ret; foreach (const bool &b, l) @@ -388,7 +386,7 @@ QStringList misc::toStringList(const QList &l) return ret; } -QList misc::intListfromStringList(const QStringList &l) +QList Utils::Misc::intListfromStringList(const QStringList &l) { QList ret; foreach (const QString &s, l) @@ -396,7 +394,7 @@ QList misc::intListfromStringList(const QStringList &l) return ret; } -QList misc::boolListfromStringList(const QStringList &l) +QList Utils::Misc::boolListfromStringList(const QStringList &l) { QList ret; foreach (const QString &s, l) @@ -404,14 +402,14 @@ QList misc::boolListfromStringList(const QStringList &l) return ret; } -bool misc::isUrl(const QString &s) +bool Utils::Misc::isUrl(const QString &s) { const QString scheme = QUrl(s).scheme(); QRegExp is_url("http[s]?|ftp", Qt::CaseInsensitive); return is_url.exactMatch(scheme); } -QString misc::parseHtmlLinks(const QString &raw_text) +QString Utils::Misc::parseHtmlLinks(const QString &raw_text) { QString result = raw_text; static QRegExp reURL( @@ -472,107 +470,20 @@ QString misc::parseHtmlLinks(const QString &raw_text) return result; } -#ifndef DISABLE_GUI -bool misc::naturalSort(QString left, QString right, bool &result) // uses lessThan comparison -{ // Return value indicates if functions was successful - // result argument will contain actual comparison result if function was successful - int posL = 0; - int posR = 0; - do { - for (;; ) { - if (posL == left.size() || posR == right.size()) - return false; // No data - - QChar leftChar = left.at(posL); - QChar rightChar = right.at(posR); - bool leftCharIsDigit = leftChar.isDigit(); - bool rightCharIsDigit = rightChar.isDigit(); - if (leftCharIsDigit != rightCharIsDigit) - return false; // Digit positions mismatch - - if (leftCharIsDigit) - break; // Both are digit, break this loop and compare numbers - - if (leftChar != rightChar) - return false; // Strings' subsets before digit do not match - - ++posL; - ++posR; - } - - QString temp; - while (posL < left.size()) { - if (left.at(posL).isDigit()) - temp += left.at(posL); - else - break; - posL++; - } - int numL = temp.toInt(); - temp.clear(); - - while (posR < right.size()) { - if (right.at(posR).isDigit()) - temp += right.at(posR); - else - break; - posR++; - } - int numR = temp.toInt(); - - if (numL != numR) { - result = (numL < numR); - return true; - } - - // Strings + digits do match and we haven't hit string end - // Do another round - - } while (true); - - return false; -} -#endif - -// to send numbers instead of strings with suffixes -QString misc::accurateDoubleToString(const double &n, const int &precision) +namespace { - /* HACK because QString rounds up. Eg QString::number(0.999*100.0, 'f' ,1) == 99.9 - ** but QString::number(0.9999*100.0, 'f' ,1) == 100.0 The problem manifests when - ** the number has more digits after the decimal than we want AND the digit after - ** our 'wanted' is >= 5. In this case our last digit gets rounded up. So for each - ** precision we add an extra 0 behind 1 in the below algorithm. */ - - double prec = std::pow(10.0, precision); - return QLocale::system().toString(std::floor(n * prec) / prec, 'f', precision); -} - -// Implements constant-time comparison to protect against timing attacks -// Taken from https://crackstation.net/hashing-security.htm -bool misc::slowEquals(const QByteArray &a, const QByteArray &b) -{ - int lengthA = a.length(); - int lengthB = b.length(); - - int diff = lengthA ^ lengthB; - for(int i = 0; i < lengthA && i < lengthB; i++) - diff |= a[i] ^ b[i]; - - return (diff == 0); -} - -namespace { -// Trick to get a portable sleep() function -class SleeperThread: public QThread { -public: - static void msleep(unsigned long msecs) + // Trick to get a portable sleep() function + class SleeperThread : public QThread { - QThread::msleep(msecs); - } -}; + public: + static void msleep(unsigned long msecs) + { + QThread::msleep(msecs); + } + }; } -void misc::msleep(unsigned long msecs) +void Utils::Misc::msleep(unsigned long msecs) { SleeperThread::msleep(msecs); } diff --git a/src/core/misc.h b/src/core/utils/misc.h similarity index 50% rename from src/core/misc.h rename to src/core/utils/misc.h index 70afc1cd6..94f921ab0 100644 --- a/src/core/misc.h +++ b/src/core/utils/misc.h @@ -28,8 +28,8 @@ * Contact : chris@qbittorrent.org */ -#ifndef MISC_H -#define MISC_H +#ifndef UTILS_MISC_H +#define UTILS_MISC_H #include #include @@ -39,57 +39,43 @@ #include #include #include - -namespace BitTorrent { class TorrentHandle; } - -const qlonglong MAX_ETA = 8640000; -enum ShutDownAction { NO_SHUTDOWN, SHUTDOWN_COMPUTER, SUSPEND_COMPUTER, HIBERNATE_COMPUTER }; +#include "core/types.h" /* Miscellaneaous functions that can be useful */ -namespace misc + +namespace Utils { + namespace Misc + { + QString parseHtmlLinks(const QString &raw_text); + bool isUrl(const QString &s); #ifndef DISABLE_GUI - void shutdownComputer(ShutDownAction action = SHUTDOWN_COMPUTER); + void shutdownComputer(ShutdownAction action); + // Get screen center + QPoint screenCenter(QWidget *win); #endif + int pythonVersion(); + // return best userfriendly storage unit (B, KiB, MiB, GiB, TiB) + // use Binary prefix standards from IEC 60027-2 + // see http://en.wikipedia.org/wiki/Kilobyte + // value must be given in bytes + QString friendlyUnit(qreal val, bool is_speed = false); + bool isPreviewable(const QString& extension); - QString parseHtmlLinks(const QString &raw_text); + QString bcLinkToMagnet(QString bc_link); + // Take a number of seconds and return an user-friendly + // time duration like "1d 2h 10m". + QString userFriendlyDuration(qlonglong seconds); + QString getUserIDString(); - bool isUrl(const QString &s); + // Convert functions + QStringList toStringList(const QList &l); + QList intListfromStringList(const QStringList &l); + QList boolListfromStringList(const QStringList &l); -#ifndef DISABLE_GUI - // Get screen center - QPoint screenCenter(QWidget *win); -#endif - int pythonVersion(); - // return best userfriendly storage unit (B, KiB, MiB, GiB, TiB) - // use Binary prefix standards from IEC 60027-2 - // see http://en.wikipedia.org/wiki/Kilobyte - // value must be given in bytes - QString friendlyUnit(qreal val, bool is_speed = false); - bool isPreviewable(const QString& extension); - - QString bcLinkToMagnet(QString bc_link); - // Take a number of seconds and return an user-friendly - // time duration like "1d 2h 10m". - QString userFriendlyDuration(qlonglong seconds); - QString getUserIDString(); - - // Convert functions - QStringList toStringList(const QList &l); - QList intListfromStringList(const QStringList &l); - QList boolListfromStringList(const QStringList &l); - - QString accurateDoubleToString(const double &n, const int &precision); - -#ifndef DISABLE_GUI - bool naturalSort(QString left, QString right, bool& result); -#endif - - // 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 msleep(unsigned long msecs); + void msleep(unsigned long msecs); + } } #endif diff --git a/src/core/utils/string.cpp b/src/core/utils/string.cpp index 4544138bd..6dffa2615 100644 --- a/src/core/utils/string.cpp +++ b/src/core/utils/string.cpp @@ -29,15 +29,106 @@ #include #include +#include +#include #include "string.h" -QString String::fromStdString(const std::string &str) +QString Utils::String::fromStdString(const std::string &str) { return QString::fromUtf8(str.c_str()); } -std::string String::toStdString(const QString &str) +std::string Utils::String::toStdString(const QString &str) { QByteArray utf8 = str.toUtf8(); return std::string(utf8.constData(), utf8.length()); } + +// uses lessThan comparison +bool Utils::String::naturalSort(QString left, QString right, bool &result) +{ + // Return value indicates if functions was successful + // result argument will contain actual comparison result if function was successful + int posL = 0; + int posR = 0; + do { + forever { + if (posL == left.size() || posR == right.size()) + return false; // No data + + QChar leftChar = left.at(posL); + QChar rightChar = right.at(posR); + bool leftCharIsDigit = leftChar.isDigit(); + bool rightCharIsDigit = rightChar.isDigit(); + if (leftCharIsDigit != rightCharIsDigit) + return false; // Digit positions mismatch + + if (leftCharIsDigit) + break; // Both are digit, break this loop and compare numbers + + if (leftChar != rightChar) + return false; // Strings' subsets before digit do not match + + ++posL; + ++posR; + } + + QString temp; + while (posL < left.size()) { + if (left.at(posL).isDigit()) + temp += left.at(posL); + else + break; + posL++; + } + int numL = temp.toInt(); + temp.clear(); + + while (posR < right.size()) { + if (right.at(posR).isDigit()) + temp += right.at(posR); + else + break; + posR++; + } + int numR = temp.toInt(); + + if (numL != numR) { + result = (numL < numR); + return true; + } + + // Strings + digits do match and we haven't hit string end + // Do another round + + } while (true); + + return false; +} + +// to send numbers instead of strings with suffixes +QString Utils::String::fromDouble(double n, int precision) +{ + /* HACK because QString rounds up. Eg QString::number(0.999*100.0, 'f' ,1) == 99.9 + ** but QString::number(0.9999*100.0, 'f' ,1) == 100.0 The problem manifests when + ** the number has more digits after the decimal than we want AND the digit after + ** our 'wanted' is >= 5. In this case our last digit gets rounded up. So for each + ** precision we add an extra 0 behind 1 in the below algorithm. */ + + double prec = std::pow(10.0, precision); + return QLocale::system().toString(std::floor(n * prec) / prec, 'f', precision); +} + +// Implements constant-time comparison to protect against timing attacks +// Taken from https://crackstation.net/hashing-security.htm +bool Utils::String::slowEquals(const QByteArray &a, const QByteArray &b) +{ + int lengthA = a.length(); + int lengthB = b.length(); + + int diff = lengthA ^ lengthB; + for (int i = 0; (i < lengthA) && (i < lengthB); i++) + diff |= a[i] ^ b[i]; + + return (diff == 0); +} diff --git a/src/core/utils/string.h b/src/core/utils/string.h index b39ff654e..d2863dc7c 100644 --- a/src/core/utils/string.h +++ b/src/core/utils/string.h @@ -33,11 +33,21 @@ #include class QString; +class QByteArray; -namespace String +namespace Utils { - QString fromStdString(const std::string &str); - std::string toStdString(const QString &str); + namespace String + { + QString fromStdString(const std::string &str); + std::string toStdString(const QString &str); + bool naturalSort(QString left, QString right, bool &result); + QString fromDouble(double n, int precision); + + // 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); + } } #endif // UTILS_STRING_H diff --git a/src/gui/addnewtorrentdialog.cpp b/src/gui/addnewtorrentdialog.cpp index aa39758ca..2311584a8 100644 --- a/src/gui/addnewtorrentdialog.cpp +++ b/src/gui/addnewtorrentdialog.cpp @@ -40,7 +40,7 @@ #include "core/bittorrent/magneturi.h" #include "core/bittorrent/torrentinfo.h" #include "guiiconprovider.h" -#include "core/fs_utils.h" +#include "core/utils/fs.h" #include "autoexpandabledialog.h" #include "messageboxraised.h" @@ -66,7 +66,7 @@ AddNewTorrentDialog::AddNewTorrentDialog(QWidget *parent) Preferences* const pref = Preferences::instance(); ui->start_torrent_cb->setChecked(!pref->addTorrentsInPause()); - ui->save_path_combo->addItem(fsutils::toNativePath(pref->getSavePath()), pref->getSavePath()); + ui->save_path_combo->addItem(Utils::Fs::toNativePath(pref->getSavePath()), pref->getSavePath()); loadSavePathHistory(); connect(ui->save_path_combo, SIGNAL(currentIndexChanged(int)), SLOT(onSavePathChanged(int))); connect(ui->browse_button, SIGNAL(clicked()), SLOT(browseButton_clicked())); @@ -125,12 +125,12 @@ 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); + source = Utils::Misc::bcLinkToMagnet(source); } AddNewTorrentDialog *dlg = new AddNewTorrentDialog(parent); - if (misc::isUrl(source)) { + if (Utils::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))); @@ -303,7 +303,7 @@ void AddNewTorrentDialog::updateFileNameInSavePaths(const QString &new_filename) { 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))); + ui->save_path_combo->setItemText(i, Utils::Fs::toNativePath(folder.absoluteFilePath(new_filename))); } } @@ -325,9 +325,9 @@ void AddNewTorrentDialog::updateDiskSpaceLabel() } } - QString size_string = torrent_size ? misc::friendlyUnit(torrent_size) : QString(tr("Not Available", "This size is unavailable.")); + QString size_string = torrent_size ? Utils::Misc::friendlyUnit(torrent_size) : QString(tr("Not Available", "This size is unavailable.")); size_string += " ("; - size_string += tr("Disk space: %1").arg(misc::friendlyUnit(fsutils::freeDiskSpaceOnPath( + size_string += tr("Disk space: %1").arg(Utils::Misc::friendlyUnit(Utils::Fs::freeDiskSpaceOnPath( ui->save_path_combo->itemData( ui->save_path_combo->currentIndex()).toString()))); size_string += ")"; @@ -353,10 +353,10 @@ void AddNewTorrentDialog::browseButton_clicked() QString new_path, old_filename, new_filename; if (m_torrentInfo.isValid() && (m_torrentInfo.filesCount() == 1)) { - old_filename = fsutils::fileName(cur_save_path); + old_filename = Utils::Fs::fileName(cur_save_path); new_path = QFileDialog::getSaveFileName(this, tr("Choose save path"), cur_save_path, QString(), 0, QFileDialog::DontConfirmOverwrite); if (!new_path.isEmpty()) - new_path = fsutils::branchPath(new_path, &new_filename); + new_path = Utils::Fs::branchPath(new_path, &new_filename); qDebug() << "new_path: " << new_path; qDebug() << "new_filename: " << new_filename; } @@ -374,13 +374,13 @@ void AddNewTorrentDialog::browseButton_clicked() else { // New path, prepend to combo box if (!new_filename.isEmpty()) - ui->save_path_combo->insertItem(0, fsutils::toNativePath(QDir(new_path).absoluteFilePath(new_filename)), new_path); + ui->save_path_combo->insertItem(0, Utils::Fs::toNativePath(QDir(new_path).absoluteFilePath(new_filename)), new_path); else - ui->save_path_combo->insertItem(0, fsutils::toNativePath(new_path), new_path); + ui->save_path_combo->insertItem(0, Utils::Fs::toNativePath(new_path), new_path); ui->save_path_combo->setCurrentIndex(0); } // Update file name in all save_paths - if (!new_filename.isEmpty() && !fsutils::sameFileNames(old_filename, new_filename)) { + if (!new_filename.isEmpty() && !Utils::Fs::sameFileNames(old_filename, new_filename)) { m_torrentInfo.renameFile(0, new_filename); updateFileNameInSavePaths(new_filename); } @@ -419,7 +419,7 @@ void AddNewTorrentDialog::renameSelectedFile() tr("New name:"), QLineEdit::Normal, index.data().toString(), &ok).trimmed(); if (ok && !new_name_last.isEmpty()) { - if (!fsutils::isValidFileSystemName(new_name_last)) { + if (!Utils::Fs::isValidFileSystemName(new_name_last)) { MessageBoxRaised::warning(this, tr("The file could not be renamed"), tr("This file name contains forbidden characters, please choose a different one."), QMessageBox::Ok); @@ -428,22 +428,22 @@ 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_torrentInfo.filePath(file_index)); + QString old_name = Utils::Fs::fromNativePath(m_torrentInfo.filePath(file_index)); qDebug("Old name: %s", qPrintable(old_name)); QStringList path_items = old_name.split("/"); path_items.removeLast(); path_items << new_name_last; QString new_name = path_items.join("/"); - if (fsutils::sameFileNames(old_name, new_name)) { + if (Utils::Fs::sameFileNames(old_name, new_name)) { qDebug("Name did not change"); return; } - new_name = fsutils::expandPath(new_name); + new_name = Utils::Fs::expandPath(new_name); qDebug("New name: %s", qPrintable(new_name)); // Check if that name is already used for (int i = 0; i < m_torrentInfo.filesCount(); ++i) { if (i == file_index) continue; - if (fsutils::sameFileNames(m_torrentInfo.filePath(i), new_name)) { + if (Utils::Fs::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."), @@ -490,7 +490,7 @@ void AddNewTorrentDialog::renameSelectedFile() 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); + new_name = Utils::Fs::expandPath(new_name); qDebug("Rename %s to %s", qPrintable(current_name), qPrintable(new_name)); m_torrentInfo.renameFile(i, new_name); } @@ -505,7 +505,7 @@ void AddNewTorrentDialog::renameSelectedFile() void AddNewTorrentDialog::setdialogPosition() { qApp->processEvents(); - QPoint center(misc::screenCenter(this)); + QPoint center(Utils::Misc::screenCenter(this)); // Adjust y int y = Preferences::instance()->getAddNewTorrentDialogPos(); if (y >= 0) { @@ -526,7 +526,7 @@ void AddNewTorrentDialog::loadSavePathHistory() QStringList raw_path_history = Preferences::instance()->getAddNewTorrentDialogPathHistory(); foreach (const QString &sp, raw_path_history) if (QDir(sp) != default_save_path) - ui->save_path_combo->addItem(fsutils::toNativePath(sp), sp); + ui->save_path_combo->addItem(Utils::Fs::toNativePath(sp), sp); } void AddNewTorrentDialog::displayContentTreeMenu(const QPoint&) @@ -694,7 +694,7 @@ void AddNewTorrentDialog::setupTreeview() // Update save paths (append file name to them) 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))); + ui->save_path_combo->setItemText(i, Utils::Fs::toNativePath(QDir(ui->save_path_combo->itemText(i)).absoluteFilePath(single_file_relpath))); } } diff --git a/src/gui/deletionconfirmationdlg.h b/src/gui/deletionconfirmationdlg.h index 09e061ebf..551ac08bd 100644 --- a/src/gui/deletionconfirmationdlg.h +++ b/src/gui/deletionconfirmationdlg.h @@ -36,7 +36,7 @@ #include "ui_confirmdeletiondlg.h" #include "core/preferences.h" #include "guiiconprovider.h" -#include "core/misc.h" +#include "core/utils/misc.h" class DeletionConfirmationDlg : public QDialog, private Ui::confirmDeletionDlg { Q_OBJECT @@ -53,7 +53,7 @@ class DeletionConfirmationDlg : public QDialog, private Ui::confirmDeletionDlg { lbl_warn->setFixedWidth(lbl_warn->height()); rememberBtn->setIcon(GuiIconProvider::instance()->getIcon("object-locked")); - move(misc::screenCenter(this)); + move(Utils::Misc::screenCenter(this)); checkPermDelete->setChecked(Preferences::instance()->deleteTorrentFilesAsDefault()); connect(checkPermDelete, SIGNAL(clicked()), this, SLOT(updateRememberButtonState())); buttonBox->button(QDialogButtonBox::Cancel)->setFocus(); diff --git a/src/gui/geoip/geoipmanager.cpp b/src/gui/geoip/geoipmanager.cpp index ef6edeb1e..a22394d91 100644 --- a/src/gui/geoip/geoipmanager.cpp +++ b/src/gui/geoip/geoipmanager.cpp @@ -64,7 +64,7 @@ #include #include -#include "core/fs_utils.h" +#include "core/utils/fs.h" #include using namespace libtorrent; @@ -73,7 +73,7 @@ QString GeoIPManager::geoipFolder(bool embedded) { #ifdef WITH_GEOIP_EMBEDDED if (embedded) return ":/geoip/"; - return fsutils::QDesktopServicesDataLocation()+"geoip"+"/"; + return Utils::Fs::QDesktopServicesDataLocation()+"geoip"+"/"; #else Q_UNUSED(embedded); if (QFile::exists("/usr/local/share/GeoIP/GeoIP.dat")) @@ -102,7 +102,7 @@ void GeoIPManager::exportEmbeddedDb() { } // Remove destination files if (QFile::exists(geoipDBpath(false))) - fsutils::forceRemove(geoipDBpath(false)); + Utils::Fs::forceRemove(geoipDBpath(false)); // Copy from executable to hard disk qDebug("%s -> %s", qPrintable(geoipDBpath(true)), qPrintable(geoipDBpath(false))); if (!QFile::copy(geoipDBpath(true), geoipDBpath(false))) { diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index cf8fab575..709f4750a 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -49,7 +49,7 @@ #include "mainwindow.h" #include "transferlistwidget.h" -#include "core/misc.h" +#include "core/utils/misc.h" #include "torrentcreatordlg.h" #include "downloadfromurldlg.h" #include "addnewtorrentdialog.h" @@ -874,7 +874,7 @@ void MainWindow::showEvent(QShowEvent *e) // Make sure the window is initially centered if (!m_posInitialized) { - move(misc::screenCenter(this)); + move(Utils::Misc::screenCenter(this)); m_posInitialized = true; } } @@ -1045,9 +1045,9 @@ void MainWindow::on_actionOpen_triggered() } // Save last dir to remember it - QStringList top_dir = fsutils::fromNativePath(pathsList.at(0)).split("/"); + QStringList top_dir = Utils::Fs::fromNativePath(pathsList.at(0)).split("/"); top_dir.removeLast(); - pref->setMainLastDir(fsutils::fromNativePath(top_dir.join("/"))); + pref->setMainLastDir(Utils::Fs::fromNativePath(top_dir.join("/"))); } void MainWindow::activate() @@ -1186,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(status.payloadDownloadRate(), true)); + html += " " + tr("DL speed: %1", "e.g: Download speed: 10 KiB/s").arg(Utils::Misc::friendlyUnit(status.payloadDownloadRate(), true)); html += "
"; html += "
"; - html += " " + tr("UP speed: %1", "e.g: Upload speed: 10 KiB/s").arg(misc::friendlyUnit(status.payloadUploadRate(), true)); + html += " " + tr("UP speed: %1", "e.g: Upload speed: 10 KiB/s").arg(Utils::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(status.payloadDownloadRate(), true)); + QString html = tr("DL speed: %1", "e.g: Download speed: 10 KiB/s").arg(Utils::Misc::friendlyUnit(status.payloadDownloadRate(), true)); html += "\n"; - html += tr("UP speed: %1", "e.g: Upload speed: 10 KiB/s").arg(misc::friendlyUnit(status.payloadUploadRate(), true)); + html += tr("UP speed: %1", "e.g: Upload speed: 10 KiB/s").arg(Utils::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(status.payloadDownloadRate(), true)).arg(misc::friendlyUnit(status.payloadUploadRate(), true)).arg(QString::fromUtf8(VERSION))); + setWindowTitle(tr("[D: %1, U: %2] qBittorrent %3", "D = Download; U = Upload; %3 is qBittorrent version").arg(Utils::Misc::friendlyUnit(status.payloadDownloadRate(), true)).arg(Utils::Misc::friendlyUnit(status.payloadUploadRate(), true)).arg(QString::fromUtf8(VERSION))); } void MainWindow::showNotificationBaloon(QString title, QString msg) const @@ -1377,7 +1377,7 @@ void MainWindow::on_actionSearch_engine_triggered() bool res = false; // Check if python is already in PATH - if (misc::pythonVersion() > 0) + if (Utils::Misc::pythonVersion() > 0) res = true; else res = addPythonPathToEnv(); @@ -1567,7 +1567,7 @@ bool MainWindow::addPythonPathToEnv() path_envar = ""; path_envar = python_path + ";" + path_envar; qDebug("New PATH envvar is: %s", qPrintable(path_envar)); - qputenv("PATH", fsutils::toNativePath(path_envar).toLocal8Bit()); + qputenv("PATH", Utils::Fs::toNativePath(path_envar).toLocal8Bit()); return true; } return false; @@ -1590,7 +1590,7 @@ void MainWindow::pythonDownloadSuccess(const QString &url, const QString &filePa QProcess installer; qDebug("Launching Python installer in passive mode..."); - installer.start("msiexec.exe /passive /i " + fsutils::toNativePath(filePath) + ".msi"); + installer.start("msiexec.exe /passive /i " + Utils::Fs::toNativePath(filePath) + ".msi"); // Wait for setup to complete installer.waitForFinished(); @@ -1598,7 +1598,7 @@ void MainWindow::pythonDownloadSuccess(const QString &url, const QString &filePa qDebug("Installer stderr: %s", installer.readAllStandardError().data()); qDebug("Setup should be complete!"); // Delete temp file - fsutils::forceRemove(filePath); + Utils::Fs::forceRemove(filePath); // Reload search engine has_python = addPythonPathToEnv(); if (has_python) { diff --git a/src/gui/options_imp.cpp b/src/gui/options_imp.cpp index 17c6492ea..e1124123b 100644 --- a/src/gui/options_imp.cpp +++ b/src/gui/options_imp.cpp @@ -43,7 +43,7 @@ #include "options_imp.h" #include "core/preferences.h" -#include "core/fs_utils.h" +#include "core/utils/fs.h" #include "advancedsettings.h" #include "core/scanfoldersmodel.h" #include "core/bittorrent/session.h" @@ -545,14 +545,14 @@ void options_imp::loadOptions() { #endif // End General preferences // Downloads preferences - textSavePath->setText(fsutils::toNativePath(pref->getSavePath())); + textSavePath->setText(Utils::Fs::toNativePath(pref->getSavePath())); if (pref->isTempPathEnabled()) { // enable checkTempFolder->setChecked(true); } else { checkTempFolder->setChecked(false); } - textTempPath->setText(fsutils::toNativePath(pref->getTempPath())); + textTempPath->setText(Utils::Fs::toNativePath(pref->getTempPath())); checkAppendLabel->setChecked(pref->appendTorrentLabel()); checkAppendqB->setChecked(pref->useIncompleteFilesExtension()); checkPreallocateAll->setChecked(pref->preAllocateAllFiles()); @@ -560,7 +560,7 @@ void options_imp::loadOptions() { checkAdditionDialogFront->setChecked(pref->additionDialogFront()); checkStartPaused->setChecked(pref->addTorrentsInPause()); - strValue = fsutils::toNativePath(pref->getTorrentExportDir()); + strValue = Utils::Fs::toNativePath(pref->getTorrentExportDir()); if (strValue.isEmpty()) { // Disable checkExportDir->setChecked(false); @@ -570,7 +570,7 @@ void options_imp::loadOptions() { textExportDir->setText(strValue); } - strValue = fsutils::toNativePath(pref->getFinishedTorrentExportDir()); + strValue = Utils::Fs::toNativePath(pref->getFinishedTorrentExportDir()); if (strValue.isEmpty()) { // Disable checkExportDirFin->setChecked(false); @@ -735,7 +735,7 @@ void options_imp::loadOptions() { // Misc preferences // * IP Filter checkIPFilter->setChecked(pref->isFilteringEnabled()); - textFilterPath->setText(fsutils::toNativePath(pref->getFilter())); + textFilterPath->setText(Utils::Fs::toNativePath(pref->getFilter())); // End IP Filter // Queueing system preferences checkEnableQueueing->setChecked(pref->isQueueingSystemEnabled()); @@ -850,13 +850,13 @@ qreal options_imp::getMaxRatio() const { QString options_imp::getSavePath() const { if (textSavePath->text().trimmed().isEmpty()) { QString save_path = Preferences::instance()->getSavePath(); - textSavePath->setText(fsutils::toNativePath(save_path)); + textSavePath->setText(Utils::Fs::toNativePath(save_path)); } - return fsutils::expandPathAbs(textSavePath->text()); + return Utils::Fs::expandPathAbs(textSavePath->text()); } QString options_imp::getTempPath() const { - return fsutils::expandPathAbs(textTempPath->text()); + return Utils::Fs::expandPathAbs(textTempPath->text()); } bool options_imp::isTempPathEnabled() const { @@ -1051,13 +1051,13 @@ void options_imp::setLocale(const QString &localeStr) { QString options_imp::getTorrentExportDir() const { if (checkExportDir->isChecked()) - return fsutils::expandPathAbs(textExportDir->text()); + return Utils::Fs::expandPathAbs(textExportDir->text()); return QString(); } QString options_imp::getFinishedTorrentExportDir() const { if (checkExportDirFin->isChecked()) - return fsutils::expandPathAbs(textExportDirFin->text()); + return Utils::Fs::expandPathAbs(textExportDirFin->text()); return QString(); } @@ -1078,7 +1078,7 @@ int options_imp::getActionOnDblClOnTorrentFn() const { void options_imp::on_addScanFolderButton_clicked() { Preferences* const pref = Preferences::instance(); const QString dir = QFileDialog::getExistingDirectory(this, tr("Add directory to scan"), - fsutils::toNativePath(fsutils::folderName(pref->getScanDirsLastPath()))); + Utils::Fs::toNativePath(Utils::Fs::folderName(pref->getScanDirsLastPath()))); if (!dir.isEmpty()) { const ScanFoldersModel::PathStatus status = ScanFoldersModel::instance()->addPath(dir, false); QString error; @@ -1120,7 +1120,7 @@ void options_imp::handleScanFolderViewSelectionChanged() { QString options_imp::askForExportDir(const QString& currentExportPath) { - QDir currentExportDir(fsutils::expandPathAbs(currentExportPath)); + QDir currentExportDir(Utils::Fs::expandPathAbs(currentExportPath)); QString dir; if (!currentExportPath.isEmpty() && currentExportDir.exists()) { dir = QFileDialog::getExistingDirectory(this, tr("Choose export directory"), currentExportDir.absolutePath()); @@ -1133,17 +1133,17 @@ QString options_imp::askForExportDir(const QString& currentExportPath) void options_imp::on_browseExportDirButton_clicked() { const QString newExportDir = askForExportDir(textExportDir->text()); if (!newExportDir.isNull()) - textExportDir->setText(fsutils::toNativePath(newExportDir)); + textExportDir->setText(Utils::Fs::toNativePath(newExportDir)); } void options_imp::on_browseExportDirFinButton_clicked() { const QString newExportDir = askForExportDir(textExportDirFin->text()); if (!newExportDir.isNull()) - textExportDirFin->setText(fsutils::toNativePath(newExportDir)); + textExportDirFin->setText(Utils::Fs::toNativePath(newExportDir)); } void options_imp::on_browseFilterButton_clicked() { - const QString filter_path = fsutils::expandPathAbs(textFilterPath->text()); + const QString filter_path = Utils::Fs::expandPathAbs(textFilterPath->text()); QDir filterDir(filter_path); QString ipfilter; if (!filter_path.isEmpty() && filterDir.exists()) { @@ -1152,12 +1152,12 @@ void options_imp::on_browseFilterButton_clicked() { ipfilter = QFileDialog::getOpenFileName(this, tr("Choose an ip filter file"), QDir::homePath(), tr("Filters")+QString(" (*.dat *.p2p *.p2b)")); } if (!ipfilter.isNull()) - textFilterPath->setText(fsutils::toNativePath(ipfilter)); + textFilterPath->setText(Utils::Fs::toNativePath(ipfilter)); } // Display dialog to choose save dir void options_imp::on_browseSaveDirButton_clicked() { - const QString save_path = fsutils::expandPathAbs(textSavePath->text()); + const QString save_path = Utils::Fs::expandPathAbs(textSavePath->text()); QDir saveDir(save_path); QString dir; if (!save_path.isEmpty() && saveDir.exists()) { @@ -1166,11 +1166,11 @@ void options_imp::on_browseSaveDirButton_clicked() { dir = QFileDialog::getExistingDirectory(this, tr("Choose a save directory"), QDir::homePath()); } if (!dir.isNull()) - textSavePath->setText(fsutils::toNativePath(dir)); + textSavePath->setText(Utils::Fs::toNativePath(dir)); } void options_imp::on_browseTempDirButton_clicked() { - const QString temp_path = fsutils::expandPathAbs(textTempPath->text()); + const QString temp_path = Utils::Fs::expandPathAbs(textTempPath->text()); QDir tempDir(temp_path); QString dir; if (!temp_path.isEmpty() && tempDir.exists()) { @@ -1179,12 +1179,12 @@ void options_imp::on_browseTempDirButton_clicked() { dir = QFileDialog::getExistingDirectory(this, tr("Choose a save directory"), QDir::homePath()); } if (!dir.isNull()) - textTempPath->setText(fsutils::toNativePath(dir)); + textTempPath->setText(Utils::Fs::toNativePath(dir)); } // Return Filter object to apply to BT session QString options_imp::getFilter() const { - return fsutils::fromNativePath(textFilterPath->text()); + return Utils::Fs::fromNativePath(textFilterPath->text()); } // Web UI diff --git a/src/gui/previewlistdelegate.h b/src/gui/previewlistdelegate.h index 37951901c..10849be2d 100644 --- a/src/gui/previewlistdelegate.h +++ b/src/gui/previewlistdelegate.h @@ -37,7 +37,8 @@ #include #include #include -#include "core/misc.h" +#include "core/utils/misc.h" +#include "core/utils/string.h" #include "previewselect.h" #ifdef Q_OS_WIN @@ -63,13 +64,13 @@ class PreviewListDelegate: public QItemDelegate { switch(index.column()) { case PreviewSelect::SIZE: QItemDelegate::drawBackground(painter, opt, index); - QItemDelegate::drawDisplay(painter, opt, option.rect, misc::friendlyUnit(index.data().toLongLong())); + QItemDelegate::drawDisplay(painter, opt, option.rect, Utils::Misc::friendlyUnit(index.data().toLongLong())); break; case PreviewSelect::PROGRESS:{ QStyleOptionProgressBarV2 newopt; qreal progress = index.data().toDouble()*100.; newopt.rect = opt.rect; - newopt.text = ((progress == 100.0) ? QString("100%") : misc::accurateDoubleToString(progress, 1) + "%"); + newopt.text = ((progress == 100.0) ? QString("100%") : Utils::String::fromDouble(progress, 1) + "%"); newopt.progress = (int)progress; newopt.maximum = 100; newopt.minimum = 0; diff --git a/src/gui/previewselect.cpp b/src/gui/previewselect.cpp index e6d0f7b5f..da3109f98 100644 --- a/src/gui/previewselect.cpp +++ b/src/gui/previewselect.cpp @@ -33,10 +33,10 @@ #include #include -#include "core/misc.h" +#include "core/utils/misc.h" #include "previewlistdelegate.h" #include "previewselect.h" -#include "core/fs_utils.h" +#include "core/utils/fs.h" #include "core/preferences.h" PreviewSelect::PreviewSelect(QWidget* parent, BitTorrent::TorrentHandle *const torrent) @@ -64,8 +64,8 @@ PreviewSelect::PreviewSelect(QWidget* parent, BitTorrent::TorrentHandle *const t QString fileName = torrent->fileName(i); if (fileName.endsWith(".!qB")) fileName.chop(4); - QString extension = fsutils::fileExtension(fileName).toUpper(); - if (misc::isPreviewable(extension)) { + QString extension = Utils::Fs::fileExtension(fileName).toUpper(); + if (Utils::Misc::isPreviewable(extension)) { int row = previewListModel->rowCount(); previewListModel->insertRow(row); previewListModel->setData(previewListModel->index(row, NAME), QVariant(fileName)); diff --git a/src/gui/programupdater.cpp b/src/gui/programupdater.cpp index 80191acf1..b71958b42 100644 --- a/src/gui/programupdater.cpp +++ b/src/gui/programupdater.cpp @@ -39,7 +39,7 @@ #include #include "programupdater.h" -#include "core/fs_utils.h" +#include "core/utils/fs.h" #include "core/preferences.h" #ifdef Q_OS_MAC @@ -122,7 +122,7 @@ void ProgramUpdater::rssDownloadFinished(QNetworkReply *reply) } else if (xml.isEndElement()) { if (in_item && xml.name() == "title") { in_title = false; - const QString ext = fsutils::fileExtension(item_title).toUpper(); + const QString ext = Utils::Fs::fileExtension(item_title).toUpper(); qDebug("Found an update with file extension: %s", qPrintable(ext)); if (ext == FILE_EXT) { qDebug("The last update available is %s", qPrintable(item_title)); diff --git a/src/gui/properties/peerlistdelegate.h b/src/gui/properties/peerlistdelegate.h index aca206768..eb68fb1d6 100644 --- a/src/gui/properties/peerlistdelegate.h +++ b/src/gui/properties/peerlistdelegate.h @@ -33,7 +33,8 @@ #include #include -#include "core/misc.h" +#include "core/utils/misc.h" +#include "core/utils/string.h" class PeerListDelegate: public QItemDelegate { Q_OBJECT @@ -54,21 +55,21 @@ public: case TOT_DOWN: case TOT_UP: QItemDelegate::drawBackground(painter, opt, index); - QItemDelegate::drawDisplay(painter, opt, option.rect, misc::friendlyUnit(index.data().toLongLong())); + QItemDelegate::drawDisplay(painter, opt, option.rect, Utils::Misc::friendlyUnit(index.data().toLongLong())); break; case DOWN_SPEED: case UP_SPEED:{ QItemDelegate::drawBackground(painter, opt, index); qreal speed = index.data().toDouble(); if (speed > 0.0) - QItemDelegate::drawDisplay(painter, opt, opt.rect, misc::friendlyUnit(speed)+tr("/s", "/second (i.e. per second)")); + QItemDelegate::drawDisplay(painter, opt, opt.rect, Utils::Misc::friendlyUnit(speed)+tr("/s", "/second (i.e. per second)")); break; } case PROGRESS: case RELEVANCE:{ QItemDelegate::drawBackground(painter, opt, index); qreal progress = index.data().toDouble(); - QItemDelegate::drawDisplay(painter, opt, opt.rect, misc::accurateDoubleToString(progress*100.0, 1)+"%"); + QItemDelegate::drawDisplay(painter, opt, opt.rect, Utils::String::fromDouble(progress*100.0, 1)+"%"); break; } default: diff --git a/src/gui/properties/peerlistsortmodel.h b/src/gui/properties/peerlistsortmodel.h index ae3459215..d37a9ccd1 100644 --- a/src/gui/properties/peerlistsortmodel.h +++ b/src/gui/properties/peerlistsortmodel.h @@ -52,7 +52,7 @@ protected: Q_ASSERT(vR.isValid()); bool res = false; - if (misc::naturalSort(vL.toString(), vR.toString(), res)) + if (Utils::String::naturalSort(vL.toString(), vR.toString(), res)) return res; return QSortFilterProxyModel::lessThan(left, right); diff --git a/src/gui/properties/propertieswidget.cpp b/src/gui/properties/propertieswidget.cpp index f7e1fee0d..71fb90a07 100644 --- a/src/gui/properties/propertieswidget.cpp +++ b/src/gui/properties/propertieswidget.cpp @@ -57,7 +57,8 @@ #include "proptabbar.h" #include "guiiconprovider.h" #include "lineedit.h" -#include "core/fs_utils.h" +#include "core/utils/fs.h" +#include "core/utils/string.h" #include "autoexpandabledialog.h" PropertiesWidget::PropertiesWidget(QWidget *parent, MainWindow* main_window, TransferListWidget *transferList): @@ -264,9 +265,9 @@ void PropertiesWidget::loadTorrentInfos(BitTorrent::TorrentHandle *const torrent // Creation date lbl_creationDate->setText(m_torrent->creationDate().toString()); // Piece size - pieceSize_lbl->setText(misc::friendlyUnit(m_torrent->pieceLength())); + pieceSize_lbl->setText(Utils::Misc::friendlyUnit(m_torrent->pieceLength())); // Comment - comment_text->setHtml(misc::parseHtmlLinks(m_torrent->comment())); + comment_text->setHtml(Utils::Misc::parseHtmlLinks(m_torrent->comment())); // URL seeds loadUrlSeeds(); // List files in torrent @@ -333,14 +334,14 @@ void PropertiesWidget::loadDynamicData() { // Transfer infos if (stackedProperties->currentIndex() == PropTabBar::MAIN_TAB) { - 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()); + wasted->setText(Utils::Misc::friendlyUnit(m_torrent->wastedSize())); + upTotal->setText(Utils::Misc::friendlyUnit(m_torrent->totalUpload()) + " ("+Utils::Misc::friendlyUnit(m_torrent->totalPayloadUpload())+" "+tr("this session")+")"); + dlTotal->setText(Utils::Misc::friendlyUnit(m_torrent->totalDownload()) + " ("+Utils::Misc::friendlyUnit(m_torrent->totalPayloadDownload())+" "+tr("this session")+")"); + lbl_uplimit->setText(m_torrent->uploadLimit() <= 0 ? QString::fromUtf8("∞") : Utils::Misc::friendlyUnit(m_torrent->uploadLimit())+tr("/s", "/second (i.e. per second)")); + lbl_dllimit->setText(m_torrent->downloadLimit() <= 0 ? QString::fromUtf8("∞") : Utils::Misc::friendlyUnit(m_torrent->downloadLimit())+tr("/s", "/second (i.e. per second)")); + QString elapsed_txt = Utils::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()))+")"; + elapsed_txt += " ("+tr("Seeded for %1", "e.g. Seeded for 3m10s").arg(Utils::Misc::userFriendlyDuration(m_torrent->seedingTime()))+")"; } lbl_elapsed->setText(elapsed_txt); if (m_torrent->connectionsLimit() > 0) @@ -348,10 +349,10 @@ void PropertiesWidget::loadDynamicData() { else lbl_connections->setText(QString::number(m_torrent->connectionsLimit())); // Update next announce time - reannounce_lbl->setText(misc::userFriendlyDuration(m_torrent->nextAnnounce())); + reannounce_lbl->setText(Utils::Misc::userFriendlyDuration(m_torrent->nextAnnounce())); // Update ratio info const qreal ratio = m_torrent->realRatio(); - shareRatio->setText(ratio > BitTorrent::TorrentHandle::MAX_RATIO ? QString::fromUtf8("∞") : misc::accurateDoubleToString(ratio, 2)); + shareRatio->setText(ratio > BitTorrent::TorrentHandle::MAX_RATIO ? QString::fromUtf8("∞") : Utils::String::fromDouble(ratio, 2)); if (!m_torrent->isSeed() && m_torrent->hasMetadata()) { showPiecesDownloaded(true); // Downloaded pieces @@ -360,13 +361,13 @@ void PropertiesWidget::loadDynamicData() { if (!m_torrent->isPaused() && !m_torrent->isQueued() && !m_torrent->isChecking()) { showPiecesAvailability(true); pieces_availability->setAvailability(m_torrent->pieceAvailability()); - avail_average_lbl->setText(misc::accurateDoubleToString(m_torrent->distributedCopies(), 3)); + avail_average_lbl->setText(Utils::String::fromDouble(m_torrent->distributedCopies(), 3)); } else { showPiecesAvailability(false); } // Progress qreal progress = m_torrent->progress() * 100.; - progress_lbl->setText(misc::accurateDoubleToString(progress, 1)+"%"); + progress_lbl->setText(Utils::String::fromDouble(progress, 1)+"%"); } else { showPiecesAvailability(false); showPiecesDownloaded(false); @@ -422,13 +423,13 @@ void PropertiesWidget::openFile(const QModelIndex &index) { int i = PropListModel->getFileIndex(index); const QDir saveDir(m_torrent->actualSavePath()); const QString filename = m_torrent->filePath(i); - const QString file_path = fsutils::expandPath(saveDir.absoluteFilePath(filename)); + const QString file_path = Utils::Fs::expandPath(saveDir.absoluteFilePath(filename)); qDebug("Trying to open file at %s", qPrintable(file_path)); // Flush data m_torrent->flushCache(); if (QFile::exists(file_path)) { if (file_path.startsWith("//")) - QDesktopServices::openUrl(fsutils::toNativePath("file:" + file_path)); + QDesktopServices::openUrl(Utils::Fs::toNativePath("file:" + file_path)); else QDesktopServices::openUrl(QUrl::fromLocalFile(file_path)); } @@ -457,17 +458,17 @@ void PropertiesWidget::openFolder(const QModelIndex &index, bool containing_fold #endif const QDir saveDir(m_torrent->actualSavePath()); const QString relative_path = path_items.join("/"); - absolute_path = fsutils::expandPath(saveDir.absoluteFilePath(relative_path)); + absolute_path = Utils::Fs::expandPath(saveDir.absoluteFilePath(relative_path)); } else { 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)); + absolute_path = Utils::Fs::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); + absolute_path = Utils::Fs::folderName(absolute_path); #endif } @@ -481,7 +482,7 @@ void PropertiesWidget::openFolder(const QModelIndex &index, bool containing_fold if (containing_folder) { // Syntax is: explorer /select, "C:\Folder1\Folder2\file_to_select" // Dir separators MUST be win-style slashes - QProcess::startDetached("explorer.exe", QStringList() << "/select," << fsutils::toNativePath(absolute_path)); + QProcess::startDetached("explorer.exe", QStringList() << "/select," << Utils::Fs::toNativePath(absolute_path)); } else { #elif defined(Q_OS_UNIX) && !defined(Q_OS_MAC) if (containing_folder) { @@ -491,11 +492,11 @@ void PropertiesWidget::openFolder(const QModelIndex &index, bool containing_fold proc.waitForFinished(); output = proc.readLine().simplified(); if (output == "dolphin.desktop") - proc.startDetached("dolphin", QStringList() << "--select" << fsutils::toNativePath(absolute_path)); + proc.startDetached("dolphin", QStringList() << "--select" << Utils::Fs::toNativePath(absolute_path)); else if (output == "nautilus-folder-handler.desktop") - proc.startDetached("nautilus", QStringList() << "--no-desktop" << fsutils::toNativePath(absolute_path)); + proc.startDetached("nautilus", QStringList() << "--no-desktop" << Utils::Fs::toNativePath(absolute_path)); else if (output == "kfmclient_dir.desktop") - proc.startDetached("konqueror", QStringList() << "--select" << fsutils::toNativePath(absolute_path)); + proc.startDetached("konqueror", QStringList() << "--select" << Utils::Fs::toNativePath(absolute_path)); else QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo(absolute_path).absolutePath())); } else { @@ -503,7 +504,7 @@ void PropertiesWidget::openFolder(const QModelIndex &index, bool containing_fold if (QFile::exists(absolute_path)) { // Hack to access samba shares with QDesktopServices::openUrl if (absolute_path.startsWith("//")) - QDesktopServices::openUrl(fsutils::toNativePath("file:" + absolute_path)); + QDesktopServices::openUrl(Utils::Fs::toNativePath("file:" + absolute_path)); else QDesktopServices::openUrl(QUrl::fromLocalFile(absolute_path)); } else { @@ -616,7 +617,7 @@ void PropertiesWidget::renameSelectedFile() { tr("New name:"), QLineEdit::Normal, index.data().toString(), &ok).trimmed(); if (ok && !new_name_last.isEmpty()) { - if (!fsutils::isValidFileSystemName(new_name_last)) { + if (!Utils::Fs::isValidFileSystemName(new_name_last)) { QMessageBox::warning(this, tr("The file could not be renamed"), tr("This file name contains forbidden characters, please choose a different one."), QMessageBox::Ok); @@ -638,7 +639,7 @@ void PropertiesWidget::renameSelectedFile() { qDebug("Name did not change"); return; } - new_name = fsutils::expandPath(new_name); + new_name = Utils::Fs::expandPath(new_name); // Check if that name is already used for (int i = 0; i < m_torrent->filesCount(); ++i) { if (i == file_index) continue; @@ -701,7 +702,7 @@ void PropertiesWidget::renameSelectedFile() { new_name.replace(0, old_path.length(), new_path); if (!force_recheck && QDir(m_torrent->actualSavePath()).exists(new_name)) force_recheck = true; - new_name = fsutils::expandPath(new_name); + new_name = Utils::Fs::expandPath(new_name); qDebug("Rename %s to %s", qPrintable(current_name), qPrintable(new_name)); m_torrent->renameFile(i, new_name); } @@ -714,8 +715,8 @@ void PropertiesWidget::renameSelectedFile() { 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) - misc::msleep(100); + // FIXME: We should not sleep here (freezes the UI for 1 second) + Utils::Misc::msleep(100); --timeout; } } diff --git a/src/gui/properties/proplistdelegate.h b/src/gui/properties/proplistdelegate.h index fb228899f..d46c2cccf 100644 --- a/src/gui/properties/proplistdelegate.h +++ b/src/gui/properties/proplistdelegate.h @@ -40,7 +40,8 @@ #include #include #include -#include "core/misc.h" +#include "core/utils/misc.h" +#include "core/utils/string.h" #include "propertieswidget.h" #ifdef Q_OS_WIN @@ -75,14 +76,14 @@ public: switch(index.column()) { case PCSIZE: QItemDelegate::drawBackground(painter, opt, index); - QItemDelegate::drawDisplay(painter, opt, option.rect, misc::friendlyUnit(index.data().toLongLong())); + QItemDelegate::drawDisplay(painter, opt, option.rect, Utils::Misc::friendlyUnit(index.data().toLongLong())); break; case PROGRESS:{ if (index.data().toDouble() >= 0) { QStyleOptionProgressBarV2 newopt; qreal progress = index.data().toDouble()*100.; newopt.rect = opt.rect; - newopt.text = ((progress == 100.0) ? QString("100%") : misc::accurateDoubleToString(progress, 1) + "%"); + newopt.text = ((progress == 100.0) ? QString("100%") : Utils::String::fromDouble(progress, 1) + "%"); newopt.progress = (int)progress; newopt.maximum = 100; newopt.minimum = 0; diff --git a/src/gui/properties/trackerlist.cpp b/src/gui/properties/trackerlist.cpp index 8b3ceb7b1..2e83754ee 100644 --- a/src/gui/properties/trackerlist.cpp +++ b/src/gui/properties/trackerlist.cpp @@ -45,7 +45,7 @@ #include "core/bittorrent/peerinfo.h" #include "core/bittorrent/trackerentry.h" #include "core/preferences.h" -#include "core/misc.h" +#include "core/utils/misc.h" #include "autoexpandabledialog.h" TrackerList::TrackerList(PropertiesWidget *properties): QTreeWidget(), properties(properties) { diff --git a/src/gui/properties/trackersadditiondlg.h b/src/gui/properties/trackersadditiondlg.h index 81fb6b469..378566c17 100644 --- a/src/gui/properties/trackersadditiondlg.h +++ b/src/gui/properties/trackersadditiondlg.h @@ -37,13 +37,13 @@ #include #include #include "guiiconprovider.h" -#include "core/misc.h" +#include "core/utils/misc.h" #include "ui_trackersadditiondlg.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" +#include "core/utils/fs.h" class TrackersAdditionDlg : public QDialog, private Ui::TrackersAdditionDlg{ Q_OBJECT @@ -80,7 +80,7 @@ public slots: QMessageBox::warning(this, tr("I/O Error"), tr("Error while trying to open the downloaded file."), QMessageBox::Ok); setCursor(Qt::ArrowCursor); uTorrentListButton->setEnabled(true); - fsutils::forceRemove(path); + Utils::Fs::forceRemove(path); return; } @@ -109,7 +109,7 @@ public slots: } // Clean up list_file.close(); - fsutils::forceRemove(path); + Utils::Fs::forceRemove(path); //To restore the cursor ... setCursor(Qt::ArrowCursor); uTorrentListButton->setEnabled(true); diff --git a/src/gui/rss/automatedrssdownloader.cpp b/src/gui/rss/automatedrssdownloader.cpp index 75ff98421..290626e4a 100644 --- a/src/gui/rss/automatedrssdownloader.cpp +++ b/src/gui/rss/automatedrssdownloader.cpp @@ -42,7 +42,7 @@ #include "rssfeed.h" #include "guiiconprovider.h" #include "autoexpandabledialog.h" -#include "core/fs_utils.h" +#include "core/utils/fs.h" AutomatedRssDownloader::AutomatedRssDownloader(const QWeakPointer& manager, QWidget *parent) : QDialog(parent), @@ -247,7 +247,7 @@ void AutomatedRssDownloader::updateRuleDefinitionBox() else ui->lineEFilter->clear(); ui->saveDiffDir_check->setChecked(!rule->savePath().isEmpty()); - ui->lineSavePath->setText(fsutils::toNativePath(rule->savePath())); + ui->lineSavePath->setText(Utils::Fs::toNativePath(rule->savePath())); ui->checkRegex->setChecked(rule->useRegex()); if (rule->label().isEmpty()) { ui->comboLabel->setCurrentIndex(-1); @@ -398,7 +398,7 @@ void AutomatedRssDownloader::on_browseSP_clicked() { QString save_path = QFileDialog::getExistingDirectory(this, tr("Destination directory"), QDir::homePath()); if (!save_path.isEmpty()) - ui->lineSavePath->setText(fsutils::toNativePath(save_path)); + ui->lineSavePath->setText(Utils::Fs::toNativePath(save_path)); } void AutomatedRssDownloader::on_exportBtn_clicked() diff --git a/src/gui/rss/htmlbrowser.cpp b/src/gui/rss/htmlbrowser.cpp index 07455fb3f..dbcec40ba 100644 --- a/src/gui/rss/htmlbrowser.cpp +++ b/src/gui/rss/htmlbrowser.cpp @@ -10,14 +10,14 @@ #include #include -#include "core/fs_utils.h" +#include "core/utils/fs.h" HtmlBrowser::HtmlBrowser(QWidget* parent) : QTextBrowser(parent) { m_netManager = new QNetworkAccessManager(this); m_diskCache = new QNetworkDiskCache(this); - m_diskCache->setCacheDirectory(QDir::cleanPath(fsutils::cacheLocation() + "/rss")); + m_diskCache->setCacheDirectory(QDir::cleanPath(Utils::Fs::cacheLocation() + "/rss")); m_diskCache->setMaximumCacheSize(50 * 1024 * 1024); qDebug() << "HtmlBrowser cache path:" << m_diskCache->cacheDirectory() << " max size:" << m_diskCache->maximumCacheSize() / 1024 / 1024 << "MB"; m_netManager->setCache(m_diskCache); diff --git a/src/gui/rss/rssdownloadrule.cpp b/src/gui/rss/rssdownloadrule.cpp index 2127dcecf..bd3911eb6 100644 --- a/src/gui/rss/rssdownloadrule.cpp +++ b/src/gui/rss/rssdownloadrule.cpp @@ -36,7 +36,7 @@ #include "core/preferences.h" #include "rssfeed.h" #include "rssarticle.h" -#include "core/fs_utils.h" +#include "core/utils/fs.h" RssDownloadRule::RssDownloadRule(): m_enabled(false), m_useRegex(false), m_apstate(USE_GLOBAL) { @@ -179,7 +179,7 @@ bool RssDownloadRule::operator==(const RssDownloadRule &other) const { void RssDownloadRule::setSavePath(const QString &save_path) { if (!save_path.isEmpty() && QDir(save_path) != QDir(Preferences::instance()->getSavePath())) - m_savePath = fsutils::fromNativePath(save_path); + m_savePath = Utils::Fs::fromNativePath(save_path); else m_savePath = QString(); } diff --git a/src/gui/rss/rssfeed.cpp b/src/gui/rss/rssfeed.cpp index 91b8751c1..b0e7df0d0 100644 --- a/src/gui/rss/rssfeed.cpp +++ b/src/gui/rss/rssfeed.cpp @@ -37,11 +37,11 @@ #include "core/qinisettings.h" #include "rssarticle.h" #include "rssparser.h" -#include "core/misc.h" +#include "core/utils/misc.h" #include "rssdownloadrulelist.h" #include "core/net/downloadmanager.h" #include "core/net/downloadhandler.h" -#include "core/fs_utils.h" +#include "core/utils/fs.h" #include "core/logger.h" bool rssArticleDateRecentThan(const RssArticlePtr& left, const RssArticlePtr& right) @@ -78,7 +78,7 @@ RssFeed::RssFeed(RssManager* manager, RssFolder* parent, const QString& url): RssFeed::~RssFeed() { if (!m_icon.startsWith(":/") && QFile::exists(m_icon)) - fsutils::forceRemove(m_icon); + Utils::Fs::forceRemove(m_icon); } void RssFeed::saveItemsToDisk() diff --git a/src/gui/rss/rssparser.cpp b/src/gui/rss/rssparser.cpp index 2cd6b43b9..6773a71d7 100644 --- a/src/gui/rss/rssparser.cpp +++ b/src/gui/rss/rssparser.cpp @@ -29,7 +29,7 @@ */ #include "rssparser.h" -#include "core/fs_utils.h" +#include "core/utils/fs.h" #include #include @@ -214,7 +214,7 @@ void RssParser::parseRssFile(const QString& feedUrl, const QString& filePath) { qDebug() << Q_FUNC_INFO << feedUrl << filePath; m_mutex.lock(); - ParsingJob job = { feedUrl, fsutils::fromNativePath(filePath) }; + ParsingJob job = { feedUrl, Utils::Fs::fromNativePath(filePath) }; m_queue.enqueue(job); // Wake up thread. if (m_queue.count() == 1) { @@ -499,11 +499,11 @@ void RssParser::parseFeed(const ParsingJob& job) // Clean up fileRss.close(); emit feedParsingFinished(job.feedUrl, QString()); - fsutils::forceRemove(job.filePath); + Utils::Fs::forceRemove(job.filePath); } void RssParser::reportFailure(const ParsingJob& job, const QString& error) { emit feedParsingFinished(job.feedUrl, error); - fsutils::forceRemove(job.filePath); + Utils::Fs::forceRemove(job.filePath); } diff --git a/src/gui/shutdownconfirm.cpp b/src/gui/shutdownconfirm.cpp index 636340f38..8c01b8fad 100644 --- a/src/gui/shutdownconfirm.cpp +++ b/src/gui/shutdownconfirm.cpp @@ -30,17 +30,18 @@ * Contact : hammered999@gmail.com */ +#include "core/types.h" #include "shutdownconfirm.h" #include -ShutdownConfirmDlg::ShutdownConfirmDlg(const ShutDownAction &action) +ShutdownConfirmDlg::ShutdownConfirmDlg(const ShutdownAction &action) : m_exitNow(0) , m_timeout(15) , m_action(action) { // Title and button - if (m_action == NO_SHUTDOWN) { + if (m_action == ShutdownAction::None) { setWindowTitle(tr("Exit confirmation")); m_exitNow = addButton(tr("Exit now"), QMessageBox::AcceptRole); } @@ -62,7 +63,7 @@ ShutdownConfirmDlg::ShutdownConfirmDlg(const ShutDownAction &action) connect(&m_timer, SIGNAL(m_timeout()), this, SLOT(updateSeconds())); show(); // Move to center - move(misc::screenCenter(this)); + move(Utils::Misc::screenCenter(this)); } void ShutdownConfirmDlg::showEvent(QShowEvent *event) @@ -71,7 +72,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(); @@ -104,16 +105,16 @@ void ShutdownConfirmDlg::updateText() QString text; switch (m_action) { - case NO_SHUTDOWN: + case ShutdownAction::None: text = tr("qBittorrent will now exit unless you cancel within the next %1 seconds.").arg(QString::number(m_timeout)); break; - case SHUTDOWN_COMPUTER: + case ShutdownAction::Shutdown: text = tr("The computer will now be switched off unless you cancel within the next %1 seconds.").arg(QString::number(m_timeout)); break; - case SUSPEND_COMPUTER: + case ShutdownAction::Suspend: text = tr("The computer will now go to sleep mode unless you cancel within the next %1 seconds.").arg(QString::number(m_timeout)); break; - case HIBERNATE_COMPUTER: + case ShutdownAction::Hibernate: text = tr("The computer will now go to hibernation mode unless you cancel within the next %1 seconds.").arg(QString::number(m_timeout)); break; } diff --git a/src/gui/shutdownconfirm.h b/src/gui/shutdownconfirm.h index 9495f41a8..26a3f8d05 100644 --- a/src/gui/shutdownconfirm.h +++ b/src/gui/shutdownconfirm.h @@ -33,17 +33,17 @@ #include #include -#include "core/misc.h" +#include "core/utils/misc.h" 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.cpp b/src/gui/speedlimitdlg.cpp index 932c86f7d..3b5402ee4 100644 --- a/src/gui/speedlimitdlg.cpp +++ b/src/gui/speedlimitdlg.cpp @@ -37,7 +37,7 @@ SpeedLimitDialog::SpeedLimitDialog(QWidget *parent): QDialog(parent) // Connect to slots connect(bandwidthSlider, SIGNAL(valueChanged(int)), this, SLOT(updateSpinValue(int))); connect(spinBandwidth, SIGNAL(valueChanged(int)), this, SLOT(updateSliderValue(int))); - move(misc::screenCenter(this)); + move(Utils::Misc::screenCenter(this)); } SpeedLimitDialog::~SpeedLimitDialog() diff --git a/src/gui/speedlimitdlg.h b/src/gui/speedlimitdlg.h index fd3c36026..7e26da027 100644 --- a/src/gui/speedlimitdlg.h +++ b/src/gui/speedlimitdlg.h @@ -34,7 +34,7 @@ #include #include #include "ui_bandwidth_limit.h" -#include "core/misc.h" +#include "core/utils/misc.h" #include "core/bittorrent/session.h" class SpeedLimitDialog : public QDialog, private Ui_bandwidth_dlg { diff --git a/src/gui/statsdialog.cpp b/src/gui/statsdialog.cpp index 01f957f60..3305f9198 100644 --- a/src/gui/statsdialog.cpp +++ b/src/gui/statsdialog.cpp @@ -31,7 +31,8 @@ #include "statsdialog.h" #include "ui_statsdialog.h" -#include "core/misc.h" +#include "core/utils/misc.h" +#include "core/utils/string.h" #include "core/bittorrent/session.h" #include "core/bittorrent/sessionstatus.h" #include "core/bittorrent/cachestatus.h" @@ -65,21 +66,21 @@ void StatsDialog::updateUI() { // Alltime DL/UL quint64 atd = BitTorrent::Session::instance()->getAlltimeDL(); quint64 atu = BitTorrent::Session::instance()->getAlltimeUL(); - ui->labelAlltimeDL->setText(misc::friendlyUnit(atd)); - ui->labelAlltimeUL->setText(misc::friendlyUnit(atu)); + ui->labelAlltimeDL->setText(Utils::Misc::friendlyUnit(atd)); + ui->labelAlltimeUL->setText(Utils::Misc::friendlyUnit(atu)); // Total waste (this session) - ui->labelWaste->setText(misc::friendlyUnit(ss.totalWasted())); + ui->labelWaste->setText(Utils::Misc::friendlyUnit(ss.totalWasted())); // Global ratio ui->labelGlobalRatio->setText( ( atd > 0 && atu > 0 ) ? - misc::accurateDoubleToString((qreal)atu / (qreal)atd, 2) : + Utils::String::fromDouble((qreal)atu / (qreal)atd, 2) : "-" ); // Cache hits qreal readRatio = cs.readRatio(); - ui->labelCacheHits->setText((readRatio >= 0) ? misc::accurateDoubleToString(100 * readRatio, 2) : "-"); + ui->labelCacheHits->setText((readRatio >= 0) ? Utils::String::fromDouble(100 * readRatio, 2) : "-"); // Buffers size - ui->labelTotalBuf->setText(misc::friendlyUnit(cs.totalUsedBuffers() * 16 * 1024)); + ui->labelTotalBuf->setText(Utils::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. @@ -90,15 +91,15 @@ void StatsDialog::updateUI() { peers += torrent->peersCount(); ui->labelWriteStarve->setText(QString("%1%").arg(((ss.diskWriteQueue() > 0) && (peers > 0)) - ? misc::accurateDoubleToString((100. * ss.diskWriteQueue()) / peers, 2) + ? Utils::String::fromDouble((100. * ss.diskWriteQueue()) / peers, 2) : "0")); ui->labelReadStarve->setText(QString("%1%").arg(((ss.diskReadQueue() > 0) && (peers > 0)) - ? misc::accurateDoubleToString((100. * ss.diskReadQueue()) / peers, 2) + ? Utils::String::fromDouble((100. * ss.diskReadQueue()) / peers, 2) : "0")); // Disk queues ui->labelQueuedJobs->setText(QString::number(cs.jobQueueLength())); ui->labelJobsTime->setText(QString::number(cs.averageJobTime())); - ui->labelQueuedBytes->setText(misc::friendlyUnit(cs.queuedBytes())); + ui->labelQueuedBytes->setText(Utils::Misc::friendlyUnit(cs.queuedBytes())); // Total connected peers ui->labelPeers->setText(QString::number(ss.peersCount())); diff --git a/src/gui/statusbar.cpp b/src/gui/statusbar.cpp index 7d13b1a7c..083fc08d4 100644 --- a/src/gui/statusbar.cpp +++ b/src/gui/statusbar.cpp @@ -38,7 +38,7 @@ #include "speedlimitdlg.h" #include "guiiconprovider.h" #include "core/preferences.h" -#include "core/misc.h" +#include "core/utils/misc.h" #include "core/logger.h" StatusBar::StatusBar(QStatusBar *bar) @@ -181,15 +181,15 @@ void StatusBar::refreshStatusBar() { //statusSep1->setVisible(false); } // Update speed labels - QString speedLbl = misc::friendlyUnit(sessionStatus.payloadDownloadRate(), true)+" ("+misc::friendlyUnit(sessionStatus.totalPayloadDownload())+")"; + QString speedLbl = Utils::Misc::friendlyUnit(sessionStatus.payloadDownloadRate(), true)+" ("+Utils::Misc::friendlyUnit(sessionStatus.totalPayloadDownload())+")"; int speedLimit = BitTorrent::Session::instance()->downloadRateLimit(); if (speedLimit) - speedLbl = "["+misc::friendlyUnit(speedLimit, true)+"] " + speedLbl; + speedLbl = "["+Utils::Misc::friendlyUnit(speedLimit, true)+"] " + speedLbl; dlSpeedLbl->setText(speedLbl); speedLimit = BitTorrent::Session::instance()->uploadRateLimit(); - speedLbl = misc::friendlyUnit(sessionStatus.payloadUploadRate(), true)+" ("+misc::friendlyUnit(sessionStatus.totalPayloadUpload())+")"; + speedLbl = Utils::Misc::friendlyUnit(sessionStatus.payloadUploadRate(), true)+" ("+Utils::Misc::friendlyUnit(sessionStatus.totalPayloadUpload())+")"; if (speedLimit) - speedLbl = "["+misc::friendlyUnit(speedLimit, true)+"] " + speedLbl; + speedLbl = "["+Utils::Misc::friendlyUnit(speedLimit, true)+"] " + speedLbl; upSpeedLbl->setText(speedLbl); } diff --git a/src/gui/torrentcontentfiltermodel.cpp b/src/gui/torrentcontentfiltermodel.cpp index 583c04cb5..dd1afa754 100644 --- a/src/gui/torrentcontentfiltermodel.cpp +++ b/src/gui/torrentcontentfiltermodel.cpp @@ -28,6 +28,7 @@ * Contact : chris@qbittorrent.org */ +#include "core/utils/string.h" #include "torrentcontentfiltermodel.h" #include "torrentcontentmodel.h" @@ -95,7 +96,7 @@ bool TorrentContentFilterModel::lessThan(const QModelIndex &left, const QModelIn rightType = m_model->itemType(m_model->index(right.row(), 0, right.parent())); if (leftType == rightType) { bool res = false; - if (misc::naturalSort(vL.toString(), vR.toString(), res)) + if (Utils::String::naturalSort(vL.toString(), vR.toString(), res)) return res; return QSortFilterProxyModel::lessThan(left, right); } diff --git a/src/gui/torrentcontentmodel.cpp b/src/gui/torrentcontentmodel.cpp index 94f0f3228..018bcd2f8 100644 --- a/src/gui/torrentcontentmodel.cpp +++ b/src/gui/torrentcontentmodel.cpp @@ -32,8 +32,8 @@ #include #include "guiiconprovider.h" -#include "core/misc.h" -#include "core/fs_utils.h" +#include "core/utils/misc.h" +#include "core/utils/fs.h" #include "torrentcontentmodel.h" #include "torrentcontentmodelitem.h" #include "torrentcontentmodelfolder.h" @@ -296,7 +296,7 @@ void TorrentContentModel::setupModelData(const BitTorrent::TorrentInfo &info) // Iterate over files for (int i = 0; i < info.filesCount(); ++i) { current_parent = m_rootItem; - QString path = fsutils::fromNativePath(info.filePath(i)); + QString path = Utils::Fs::fromNativePath(info.filePath(i)); // Iterate of parts of the path to create necessary folders QStringList pathFolders = path.split("/", QString::SkipEmptyParts); pathFolders.removeLast(); diff --git a/src/gui/torrentcontentmodelitem.cpp b/src/gui/torrentcontentmodelitem.cpp index 7b9c8dbed..0f31dc296 100644 --- a/src/gui/torrentcontentmodelitem.cpp +++ b/src/gui/torrentcontentmodelitem.cpp @@ -28,8 +28,8 @@ * Contact : chris@qbittorrent.org */ -#include "core/misc.h" -#include "core/fs_utils.h" +#include "core/utils/misc.h" +#include "core/utils/fs.h" #include "torrentcontentmodelitem.h" #include "torrentcontentmodelfolder.h" #include diff --git a/src/gui/torrentcreatordlg.cpp b/src/gui/torrentcreatordlg.cpp index 9dd7d5f9a..2c49864f3 100644 --- a/src/gui/torrentcreatordlg.cpp +++ b/src/gui/torrentcreatordlg.cpp @@ -33,8 +33,8 @@ #include #include "torrentcreatordlg.h" -#include "core/fs_utils.h" -#include "core/misc.h" +#include "core/utils/fs.h" +#include "core/utils/misc.h" #include "core/preferences.h" #include "guiiconprovider.h" #include "core/bittorrent/session.h" @@ -73,7 +73,7 @@ void TorrentCreatorDlg::on_addFolder_button_clicked() { QString dir = QFileDialog::getExistingDirectory(this, tr("Select a folder to add to the torrent"), last_path, QFileDialog::ShowDirsOnly); if (!dir.isEmpty()) { pref->setCreateTorLastAddPath(dir); - textInputPath->setText(fsutils::toNativePath(dir)); + textInputPath->setText(Utils::Fs::toNativePath(dir)); // Update piece size if (checkAutoPieceSize->isChecked()) updateOptimalPieceSize(); @@ -85,8 +85,8 @@ void TorrentCreatorDlg::on_addFile_button_clicked() { QString last_path = pref->getCreateTorLastAddPath(); QString file = QFileDialog::getOpenFileName(this, tr("Select a file to add to the torrent"), last_path); if (!file.isEmpty()) { - pref->setCreateTorLastAddPath(fsutils::branchPath(file)); - textInputPath->setText(fsutils::toNativePath(file)); + pref->setCreateTorLastAddPath(Utils::Fs::branchPath(file)); + textInputPath->setText(Utils::Fs::toNativePath(file)); // Update piece size if (checkAutoPieceSize->isChecked()) updateOptimalPieceSize(); @@ -99,7 +99,7 @@ int TorrentCreatorDlg::getPieceSize() const { // Main function that create a .torrent file void TorrentCreatorDlg::on_createButton_clicked() { - QString input = fsutils::fromNativePath(textInputPath->text()).trimmed(); + QString input = Utils::Fs::fromNativePath(textInputPath->text()).trimmed(); if (input.endsWith("/")) input.chop(1); if (input.isEmpty()) { @@ -115,7 +115,7 @@ void TorrentCreatorDlg::on_createButton_clicked() { QString destination = QFileDialog::getSaveFileName(this, tr("Select destination torrent file"), last_path, tr("Torrent Files")+QString::fromUtf8(" (*.torrent)")); if (!destination.isEmpty()) { - pref->setCreateTorLastSavePath(fsutils::branchPath(destination)); + pref->setCreateTorLastSavePath(Utils::Fs::branchPath(destination)); if (!destination.toUpper().endsWith(".TORRENT")) destination += QString::fromUtf8(".torrent"); } else { @@ -150,7 +150,7 @@ void TorrentCreatorDlg::handleCreationSuccess(QString path, QString branch_path) setCursor(QCursor(Qt::ArrowCursor)); if (checkStartSeeding->isChecked()) { // Create save path temp data - BitTorrent::TorrentInfo t = BitTorrent::TorrentInfo::loadFromFile(fsutils::toNativePath(path)); + BitTorrent::TorrentInfo t = BitTorrent::TorrentInfo::loadFromFile(Utils::Fs::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; @@ -164,7 +164,7 @@ void TorrentCreatorDlg::handleCreationSuccess(QString path, QString branch_path) BitTorrent::Session::instance()->addTorrent(t, params); } - QMessageBox::information(0, tr("Torrent creation"), tr("Torrent was created successfully:")+" "+fsutils::toNativePath(path)); + QMessageBox::information(0, tr("Torrent creation"), tr("Torrent was created successfully:")+" "+Utils::Fs::toNativePath(path)); close(); } @@ -217,7 +217,7 @@ void TorrentCreatorDlg::on_checkAutoPieceSize_clicked(bool checked) void TorrentCreatorDlg::updateOptimalPieceSize() { - qint64 torrent_size = fsutils::computePathSize(textInputPath->text()); + qint64 torrent_size = Utils::Fs::computePathSize(textInputPath->text()); qDebug("Torrent size is %lld", torrent_size); if (torrent_size < 0) return; int i = 0; diff --git a/src/gui/torrentimportdlg.cpp b/src/gui/torrentimportdlg.cpp index b76cf214b..798925f5e 100644 --- a/src/gui/torrentimportdlg.cpp +++ b/src/gui/torrentimportdlg.cpp @@ -38,7 +38,7 @@ #include "core/bittorrent/infohash.h" #include "core/bittorrent/session.h" #include "guiiconprovider.h" -#include "core/fs_utils.h" +#include "core/utils/fs.h" TorrentImportDlg::TorrentImportDlg(QWidget *parent): QDialog(parent), @@ -73,15 +73,15 @@ void TorrentImportDlg::on_browseContentBtn_clicked() { const QString default_dir = Preferences::instance()->getTorImportLastContentDir(); bool multifile = (m_torrentInfo.filesCount() > 1); - QString filePath = fsutils::fromNativePath(m_torrentInfo.filePath(0)); + QString filePath = Utils::Fs::fromNativePath(m_torrentInfo.filePath(0)); if (!multifile && (filePath.indexOf('/') != -1)) multifile = true; if (!multifile) { // Single file torrent - const QString file_name = fsutils::fileName(filePath); + const QString file_name = Utils::Fs::fileName(filePath); qDebug("Torrent has only one file: %s", qPrintable(file_name)); - QString extension = fsutils::fileExtension(file_name); + QString extension = Utils::Fs::fileExtension(file_name); qDebug("File extension is : %s", qPrintable(extension)); QString filter; if (!extension.isEmpty()) { @@ -96,7 +96,7 @@ void TorrentImportDlg::on_browseContentBtn_clicked() return; } // Update display - ui->lineContent->setText(fsutils::toNativePath(m_contentPath)); + ui->lineContent->setText(Utils::Fs::toNativePath(m_contentPath)); // Check file size const qint64 file_size = QFile(m_contentPath).size(); if (m_torrentInfo.fileSize(0) == file_size) { @@ -130,16 +130,16 @@ void TorrentImportDlg::on_browseContentBtn_clicked() return; } // Update the display - ui->lineContent->setText(fsutils::toNativePath(m_contentPath)); + ui->lineContent->setText(Utils::Fs::toNativePath(m_contentPath)); bool size_mismatch = false; QDir content_dir(m_contentPath); content_dir.cdUp(); // Check file sizes 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)) { + if (QFile(Utils::Fs::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()); + qPrintable(Utils::Fs::expandPath(content_dir.absoluteFilePath(rel_path))), (long long int) QFile(Utils::Fs::expandPath(content_dir.absoluteFilePath(rel_path))).size()); qDebug("%s is %lld", qPrintable(rel_path), (long long int)m_torrentInfo.fileSize(i)); size_mismatch = true; @@ -204,8 +204,8 @@ void TorrentImportDlg::importTorrent() BitTorrent::Session::instance()->addTorrent(torrentInfo, params); // Remember the last opened folder Preferences* const pref = Preferences::instance(); - pref->setMainLastDir(fsutils::fromNativePath(torrentPath)); - pref->setTorImportLastContentDir(fsutils::fromNativePath(contentPath)); + pref->setMainLastDir(Utils::Fs::fromNativePath(torrentPath)); + pref->setTorImportLastContentDir(Utils::Fs::fromNativePath(contentPath)); return; } qDebug() << Q_FUNC_INFO << "EXIT"; @@ -223,7 +223,7 @@ void TorrentImportDlg::loadTorrent(const QString &torrentPath) } else { // Update display - ui->lineTorrent->setText(fsutils::toNativePath(torrentPath)); + ui->lineTorrent->setText(Utils::Fs::toNativePath(torrentPath)); ui->browseContentBtn->setEnabled(true); // Load the file names initializeFilesPath(); diff --git a/src/gui/torrentmodel.cpp b/src/gui/torrentmodel.cpp index b82cb3bd8..6884adc0b 100644 --- a/src/gui/torrentmodel.cpp +++ b/src/gui/torrentmodel.cpp @@ -35,7 +35,7 @@ #include "core/bittorrent/session.h" #include "core/torrentfilter.h" -#include "core/fs_utils.h" +#include "core/utils/fs.h" #include "torrentmodel.h" namespace { diff --git a/src/gui/transferlistdelegate.cpp b/src/gui/transferlistdelegate.cpp index f0239a0a9..f73a0a9a6 100644 --- a/src/gui/transferlistdelegate.cpp +++ b/src/gui/transferlistdelegate.cpp @@ -34,7 +34,8 @@ #include #include #include -#include "core/misc.h" +#include "core/utils/misc.h" +#include "core/utils/string.h" #include "torrentmodel.h" #include "core/bittorrent/session.h" #include "core/bittorrent/torrenthandle.h" @@ -65,13 +66,13 @@ void TransferListDelegate::paint(QPainter * painter, const QStyleOptionViewItem case TorrentModelItem::TR_TOTAL_SIZE: { QItemDelegate::drawBackground(painter, opt, index); opt.displayAlignment = Qt::AlignRight | Qt::AlignVCenter; - QItemDelegate::drawDisplay(painter, opt, option.rect, misc::friendlyUnit(index.data().toLongLong())); + QItemDelegate::drawDisplay(painter, opt, option.rect, Utils::Misc::friendlyUnit(index.data().toLongLong())); break; } case TorrentModelItem::TR_ETA: { QItemDelegate::drawBackground(painter, opt, index); opt.displayAlignment = Qt::AlignRight | Qt::AlignVCenter; - QItemDelegate::drawDisplay(painter, opt, option.rect, misc::userFriendlyDuration(index.data().toLongLong())); + QItemDelegate::drawDisplay(painter, opt, option.rect, Utils::Misc::userFriendlyDuration(index.data().toLongLong())); break; } case TorrentModelItem::TR_SEEDS: @@ -142,7 +143,7 @@ void TransferListDelegate::paint(QPainter * painter, const QStyleOptionViewItem QItemDelegate::drawBackground(painter, opt, index); const qulonglong speed = index.data().toULongLong(); opt.displayAlignment = Qt::AlignRight | Qt::AlignVCenter; - QItemDelegate::drawDisplay(painter, opt, opt.rect, misc::friendlyUnit(speed)+tr("/s", "/second (.i.e per second)")); + QItemDelegate::drawDisplay(painter, opt, opt.rect, Utils::Misc::friendlyUnit(speed)+tr("/s", "/second (.i.e per second)")); break; } case TorrentModelItem::TR_UPLIMIT: @@ -150,15 +151,15 @@ void TransferListDelegate::paint(QPainter * painter, const QStyleOptionViewItem QItemDelegate::drawBackground(painter, opt, index); const qlonglong limit = index.data().toLongLong(); opt.displayAlignment = Qt::AlignRight | Qt::AlignVCenter; - QItemDelegate::drawDisplay(painter, opt, opt.rect, limit > 0 ? misc::accurateDoubleToString(limit/1024., 1) + " " + tr("KiB/s", "KiB/second (.i.e per second)") : QString::fromUtf8("∞")); + QItemDelegate::drawDisplay(painter, opt, opt.rect, limit > 0 ? Utils::String::fromDouble(limit/1024., 1) + " " + tr("KiB/s", "KiB/second (.i.e per second)") : QString::fromUtf8("∞")); break; } case TorrentModelItem::TR_TIME_ELAPSED: { QItemDelegate::drawBackground(painter, opt, index); - QString txt = misc::userFriendlyDuration(index.data().toLongLong()); + QString txt = Utils::Misc::userFriendlyDuration(index.data().toLongLong()); qlonglong seeding_time = index.data(Qt::UserRole).toLongLong(); if (seeding_time > 0) - txt += " ("+tr("Seeded for %1", "e.g. Seeded for 3m10s").arg(misc::userFriendlyDuration(seeding_time))+")"; + txt += " ("+tr("Seeded for %1", "e.g. Seeded for 3m10s").arg(Utils::Misc::userFriendlyDuration(seeding_time))+")"; QItemDelegate::drawDisplay(painter, opt, opt.rect, txt); break; } @@ -173,7 +174,7 @@ 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 > BitTorrent::TorrentHandle::MAX_RATIO)) ? QString::fromUtf8("∞") : misc::accurateDoubleToString(ratio, 2)); + ((ratio == -1) || (ratio > BitTorrent::TorrentHandle::MAX_RATIO)) ? QString::fromUtf8("∞") : Utils::String::fromDouble(ratio, 2)); break; } case TorrentModelItem::TR_PRIORITY: { @@ -191,7 +192,7 @@ void TransferListDelegate::paint(QPainter * painter, const QStyleOptionViewItem QStyleOptionProgressBarV2 newopt; qreal progress = index.data().toDouble()*100.; newopt.rect = opt.rect; - newopt.text = ((progress == 100.0) ? QString("100%") : misc::accurateDoubleToString(progress, 1) + "%"); + newopt.text = ((progress == 100.0) ? QString("100%") : Utils::String::fromDouble(progress, 1) + "%"); newopt.progress = (int)progress; newopt.maximum = 100; newopt.minimum = 0; @@ -219,9 +220,9 @@ void TransferListDelegate::paint(QPainter * painter, const QStyleOptionViewItem // Show '< 1m ago' when elapsed time is 0 elapsed = 1; if (elapsed < 0) - elapsedString = misc::userFriendlyDuration(elapsed); + elapsedString = Utils::Misc::userFriendlyDuration(elapsed); else - elapsedString = tr("%1 ago", "e.g.: 1h 20m ago").arg(misc::userFriendlyDuration(elapsed)); + elapsedString = tr("%1 ago", "e.g.: 1h 20m ago").arg(Utils::Misc::userFriendlyDuration(elapsed)); QItemDelegate::drawDisplay(painter, opt, option.rect, elapsedString); break; } diff --git a/src/gui/transferlistfilterswidget.cpp b/src/gui/transferlistfilterswidget.cpp index 19429554d..ec4961c13 100644 --- a/src/gui/transferlistfilterswidget.cpp +++ b/src/gui/transferlistfilterswidget.cpp @@ -44,14 +44,15 @@ #include "core/preferences.h" #include "torrentmodel.h" #include "guiiconprovider.h" -#include "core/fs_utils.h" +#include "core/utils/fs.h" +#include "core/utils/string.h" #include "autoexpandabledialog.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/utils/misc.h" #include "core/logger.h" FiltersBase::FiltersBase(QWidget *parent, TransferListWidget *transferList) @@ -202,7 +203,7 @@ void LabelFiltersList::addItem(QString &label, bool hasTorrent) { int labelCount = 0; QListWidgetItem *labelItem = 0; - label = fsutils::toValidFileSystemName(label.trimmed()); + label = Utils::Fs::toValidFileSystemName(label.trimmed()); item(0)->setText(tr("All (%1)", "this is for the label filter").arg(m_totalTorrents)); if (label.isEmpty()) { @@ -235,7 +236,7 @@ void LabelFiltersList::addItem(QString &label, bool hasTorrent) Q_ASSERT(count() >= 2); for (int i = 2; itext(), less))) + if (!(Utils::String::naturalSort(label, item(i)->text(), less))) less = (label.localeAwareCompare(item(i)->text()) < 0); if (less) { insertItem(i, labelItem); @@ -357,7 +358,7 @@ void LabelFiltersList::showMenu(QPoint) invalid = false; label = AutoExpandableDialog::getText(this, tr("New Label"), tr("Label:"), QLineEdit::Normal, label, &ok); if (ok && !label.isEmpty()) { - if (fsutils::isValidFileSystemName(label)) { + if (Utils::Fs::isValidFileSystemName(label)) { addItem(label, false); } else { @@ -445,7 +446,7 @@ TrackerFiltersList::TrackerFiltersList(QWidget *parent, TransferListWidget *tran TrackerFiltersList::~TrackerFiltersList() { foreach (const QString &iconPath, m_iconPaths) - fsutils::forceRemove(iconPath); + Utils::Fs::forceRemove(iconPath); } void TrackerFiltersList::addItem(const QString &tracker, const QString &hash) @@ -493,7 +494,7 @@ void TrackerFiltersList::addItem(const QString &tracker, const QString &hash) Q_ASSERT(count() >= 4); for (int i = 4; itext(), less))) + if (!(Utils::String::naturalSort(host, item(i)->text(), less))) less = (host.localeAwareCompare(item(i)->text()) < 0); if (less) { insertItem(i, trackerItem); @@ -633,7 +634,7 @@ void TrackerFiltersList::handleFavicoDownload(const QString& url, const QString& else { Logger::instance()->addMessage(tr("Couldn't decode favico for url `%1`.").arg(url), Log::WARNING); } - fsutils::forceRemove(filePath); + Utils::Fs::forceRemove(filePath); } else { trackerItem->setData(Qt::DecorationRole, QVariant(QIcon(filePath))); diff --git a/src/gui/transferlistsortmodel.cpp b/src/gui/transferlistsortmodel.cpp index 12bafcca3..70eb4228b 100644 --- a/src/gui/transferlistsortmodel.cpp +++ b/src/gui/transferlistsortmodel.cpp @@ -30,7 +30,8 @@ #include -#include "core/misc.h" +#include "core/types.h" +#include "core/utils/string.h" #include "core/bittorrent/torrenthandle.h" #include "torrentmodel.h" #include "transferlistsortmodel.h" @@ -81,7 +82,7 @@ bool TransferListSortModel::lessThan(const QModelIndex &left, const QModelIndex return lowerPositionThan(left, right); bool res = false; - if (misc::naturalSort(vL.toString(), vR.toString(), res)) + if (Utils::String::naturalSort(vL.toString(), vR.toString(), res)) return res; return QSortFilterProxyModel::lessThan(left, right); diff --git a/src/gui/transferlistwidget.cpp b/src/gui/transferlistwidget.cpp index 13a51ba61..712b42118 100644 --- a/src/gui/transferlistwidget.cpp +++ b/src/gui/transferlistwidget.cpp @@ -57,7 +57,7 @@ #include "deletionconfirmationdlg.h" #include "propertieswidget.h" #include "guiiconprovider.h" -#include "core/fs_utils.h" +#include "core/utils/fs.h" #include "autoexpandabledialog.h" #include "transferlistsortmodel.h" @@ -255,7 +255,7 @@ void TransferListWidget::setSelectedTorrentsLocation() BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash); if (!torrent) continue; - torrent->move(fsutils::expandPathAbs(dir)); + torrent->move(Utils::Fs::expandPathAbs(dir)); main_window->getProperties()->updateSavePath(torrent); } } @@ -622,7 +622,7 @@ void TransferListWidget::askNewLabelForSelection() invalid = false; const QString label = AutoExpandableDialog::getText(this, tr("New Label"), tr("Label:"), QLineEdit::Normal, "", &ok).trimmed(); if (ok && !label.isEmpty()) { - if (fsutils::isValidFileSystemName(label)) { + if (Utils::Fs::isValidFileSystemName(label)) { setSelectionLabel(label); } else { @@ -635,10 +635,10 @@ void TransferListWidget::askNewLabelForSelection() bool TransferListWidget::openUrl(const QString &_path) const { - const QString path = fsutils::fromNativePath(_path); + const QString path = Utils::Fs::fromNativePath(_path); // Hack to access samba shares with QDesktopServices::openUrl if (path.startsWith("//")) - return QDesktopServices::openUrl(fsutils::toNativePath("file:" + path)); + return QDesktopServices::openUrl(Utils::Fs::toNativePath("file:" + path)); else return QDesktopServices::openUrl(QUrl::fromLocalFile(path)); } diff --git a/src/searchengine/engineselectdlg.cpp b/src/searchengine/engineselectdlg.cpp index b189d0732..5cea0d9df 100644 --- a/src/searchengine/engineselectdlg.cpp +++ b/src/searchengine/engineselectdlg.cpp @@ -31,8 +31,8 @@ #include "engineselectdlg.h" #include "core/net/downloadmanager.h" #include "core/net/downloadhandler.h" -#include "core/fs_utils.h" -#include "core/misc.h" +#include "core/utils/fs.h" +#include "core/utils/misc.h" #include "ico.h" #include "searchengine.h" #include "pluginsource.h" @@ -48,7 +48,7 @@ #include enum EngineColumns {ENGINE_NAME, ENGINE_URL, ENGINE_STATE, ENGINE_ID}; -const QString UPDATE_URL = QString("https://raw.github.com/qbittorrent/qBittorrent/master/src/searchengine/") + (misc::pythonVersion() >= 3 ? "nova3" : "nova") + "/engines/"; +const QString UPDATE_URL = QString("https://raw.github.com/qbittorrent/qBittorrent/master/src/searchengine/") + (Utils::Misc::pythonVersion() >= 3 ? "nova3" : "nova") + "/engines/"; engineSelectDlg::engineSelectDlg(QWidget *parent, SupportedEngines *supported_engines) : QDialog(parent), supported_engines(supported_engines) { setupUi(this); @@ -76,7 +76,7 @@ void engineSelectDlg::dropEvent(QDropEvent *event) { QStringList files = event->mimeData()->text().split(QString::fromUtf8("\n")); foreach (QString file, files) { qDebug("dropped %s", qPrintable(file)); - if (misc::isUrl(file)) { + if (Utils::Misc::isUrl(file)) { setCursor(QCursor(Qt::WaitCursor)); downloadFromUrl(file); continue; @@ -84,7 +84,7 @@ void engineSelectDlg::dropEvent(QDropEvent *event) { if (file.endsWith(".py", Qt::CaseInsensitive)) { if (file.startsWith("file:", Qt::CaseInsensitive)) file = QUrl(file).toLocalFile(); - QString plugin_name = fsutils::fileName(file); + QString plugin_name = Utils::Fs::fileName(file); plugin_name.chop(3); // Remove extension installPlugin(file, plugin_name); } @@ -155,7 +155,7 @@ void engineSelectDlg::on_actionUninstall_triggered() { }else { // Proceed with uninstall // remove it from hard drive - QDir enginesFolder(fsutils::searchEngineLocation() + "/engines"); + QDir enginesFolder(Utils::Fs::searchEngineLocation() + "/engines"); QStringList filters; filters << id+".*"; QStringList files = enginesFolder.entryList(filters, QDir::Files, QDir::Unsorted); @@ -221,7 +221,7 @@ QTreeWidgetItem* engineSelectDlg::findItemWithID(QString id) { } bool engineSelectDlg::isUpdateNeeded(QString plugin_name, qreal new_version) const { - qreal old_version = SearchEngine::getPluginVersion(fsutils::searchEngineLocation() + "/engines/" + plugin_name + ".py"); + qreal old_version = SearchEngine::getPluginVersion(Utils::Fs::searchEngineLocation() + "/engines/" + plugin_name + ".py"); qDebug("IsUpdate needed? tobeinstalled: %.2f, alreadyinstalled: %.2f", new_version, old_version); return (new_version > old_version); } @@ -236,13 +236,13 @@ void engineSelectDlg::installPlugin(QString path, QString plugin_name) { return; } // Process with install - QString dest_path = fsutils::searchEngineLocation() + "/engines/" + plugin_name + ".py"; + QString dest_path = Utils::Fs::searchEngineLocation() + "/engines/" + plugin_name + ".py"; bool update = false; if (QFile::exists(dest_path)) { // Backup in case install fails QFile::copy(dest_path, dest_path+".bak"); - fsutils::forceRemove(dest_path); - fsutils::forceRemove(dest_path+"c"); + Utils::Fs::forceRemove(dest_path); + Utils::Fs::forceRemove(dest_path+"c"); update = true; } // Copy the plugin @@ -253,22 +253,22 @@ void engineSelectDlg::installPlugin(QString path, QString plugin_name) { if (!supported_engines->contains(plugin_name)) { if (update) { // Remove broken file - fsutils::forceRemove(dest_path); + Utils::Fs::forceRemove(dest_path); // restore backup QFile::copy(dest_path+".bak", dest_path); - fsutils::forceRemove(dest_path+".bak"); + Utils::Fs::forceRemove(dest_path+".bak"); QMessageBox::warning(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin could not be updated, keeping old version.", "%1 is the name of the search engine").arg(plugin_name)); return; } else { // Remove broken file - fsutils::forceRemove(dest_path); + Utils::Fs::forceRemove(dest_path); QMessageBox::warning(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin could not be installed.", "%1 is the name of the search engine").arg(plugin_name)); return; } } // Install was successful, remove backup if (update) { - fsutils::forceRemove(dest_path+".bak"); + Utils::Fs::forceRemove(dest_path+".bak"); } if (update) { QMessageBox::information(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin was successfully updated.", "%1 is the name of the search engine").arg(plugin_name)); @@ -301,12 +301,12 @@ void engineSelectDlg::addNewEngine(QString engine_name) { setRowColor(pluginsTree->indexOfTopLevelItem(item), "red"); } // Handle icon - QString iconPath = fsutils::searchEngineLocation() + "/engines/" + engine->getName() + ".png"; + QString iconPath = Utils::Fs::searchEngineLocation() + "/engines/" + engine->getName() + ".png"; if (QFile::exists(iconPath)) { // Good, we already have the icon item->setData(ENGINE_NAME, Qt::DecorationRole, QVariant(QIcon(iconPath))); } else { - iconPath = fsutils::searchEngineLocation() + "/engines/" + engine->getName() + ".ico"; + iconPath = Utils::Fs::searchEngineLocation() + "/engines/" + engine->getName() + ".ico"; if (QFile::exists(iconPath)) { // ICO support item->setData(ENGINE_NAME, Qt::DecorationRole, QVariant(QIcon(iconPath))); } else { @@ -398,7 +398,7 @@ bool engineSelectDlg::parseVersionsFile(QString versions_file) { // Close file versions.close(); // Clean up tmp file - fsutils::forceRemove(versions_file); + Utils::Fs::forceRemove(versions_file); if (file_correct && !updated) { QMessageBox::information(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("All your plugins are already up to date.")); } @@ -413,7 +413,7 @@ void engineSelectDlg::downloadFromUrl(const QString &url) } void engineSelectDlg::processDownloadedFile(const QString &url, QString filePath) { - filePath = fsutils::fromNativePath(filePath); + filePath = Utils::Fs::fromNativePath(filePath); setCursor(QCursor(Qt::ArrowCursor)); qDebug("engineSelectDlg received %s", qPrintable(url)); if (url.endsWith("favicon.ico", Qt::CaseInsensitive)) { @@ -428,29 +428,29 @@ void engineSelectDlg::processDownloadedFile(const QString &url, QString filePath QFile icon(filePath); icon.open(QIODevice::ReadOnly); if (ICOHandler::canRead(&icon)) - iconPath = fsutils::searchEngineLocation() + "/engines/" + id + ".ico"; + iconPath = Utils::Fs::searchEngineLocation() + "/engines/" + id + ".ico"; else - iconPath = fsutils::searchEngineLocation() + "/engines/" + id + ".png"; + iconPath = Utils::Fs::searchEngineLocation() + "/engines/" + id + ".png"; QFile::copy(filePath, iconPath); item->setData(ENGINE_NAME, Qt::DecorationRole, QVariant(QIcon(iconPath))); } } // Delete tmp file - fsutils::forceRemove(filePath); + Utils::Fs::forceRemove(filePath); return; } if (url.endsWith("versions.txt")) { if (!parseVersionsFile(filePath)) { QMessageBox::warning(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("Sorry, update server is temporarily unavailable.")); } - fsutils::forceRemove(filePath); + Utils::Fs::forceRemove(filePath); return; } if (url.endsWith(".py", Qt::CaseInsensitive)) { - QString plugin_name = fsutils::fileName(url); + QString plugin_name = Utils::Fs::fileName(url); plugin_name.chop(3); // Remove extension installPlugin(filePath, plugin_name); - fsutils::forceRemove(filePath); + Utils::Fs::forceRemove(filePath); return; } } diff --git a/src/searchengine/searchengine.cpp b/src/searchengine/searchengine.cpp index 72f285be5..aac5f44a8 100644 --- a/src/searchengine/searchengine.cpp +++ b/src/searchengine/searchengine.cpp @@ -49,8 +49,8 @@ #include "searchengine.h" #include "core/bittorrent/session.h" -#include "core/fs_utils.h" -#include "core/misc.h" +#include "core/utils/fs.h" +#include "core/utils/misc.h" #include "core/preferences.h" #include "searchlistdelegate.h" #include "mainwindow.h" @@ -217,7 +217,7 @@ void SearchEngine::on_search_button_clicked() { // Getting checked search engines QStringList params; search_stopped = false; - params << fsutils::toNativePath(fsutils::searchEngineLocation() + "/nova2.py"); + params << Utils::Fs::toNativePath(Utils::Fs::searchEngineLocation() + "/nova2.py"); if (selectedEngine() == "all") params << supported_engines->enginesAll().join(","); else if (selectedEngine() == "enabled") params << supported_engines->enginesEnabled().join(","); else if (selectedEngine() == "multi") params << supported_engines->enginesEnabled().join(","); @@ -255,7 +255,7 @@ void SearchEngine::saveResultsColumnsWidth() { void SearchEngine::downloadTorrent(QString engine_url, QString torrent_url) { if (torrent_url.startsWith("bc://bt/", Qt::CaseInsensitive)) { qDebug("Converting bc link to magnet link"); - torrent_url = misc::bcLinkToMagnet(torrent_url); + torrent_url = Utils::Misc::bcLinkToMagnet(torrent_url); } qDebug() << Q_FUNC_INFO << torrent_url; if (torrent_url.startsWith("magnet:")) { @@ -268,7 +268,7 @@ void SearchEngine::downloadTorrent(QString engine_url, QString torrent_url) { connect(downloadProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(downloadFinished(int,QProcess::ExitStatus))); downloaders << downloadProcess; QStringList params; - params << fsutils::toNativePath(fsutils::searchEngineLocation() + "/nova2dl.py"); + params << Utils::Fs::toNativePath(Utils::Fs::searchEngineLocation() + "/nova2dl.py"); params << engine_url; params << torrent_url; // Launch search @@ -323,16 +323,16 @@ void SearchEngine::downloadFinished(int exitcode, QProcess::ExitStatus) { static void removePythonScriptIfExists(const QString& script_path) { - fsutils::forceRemove(script_path); - fsutils::forceRemove(script_path + "c"); + Utils::Fs::forceRemove(script_path); + Utils::Fs::forceRemove(script_path + "c"); } // Update nova.py search plugin if necessary void SearchEngine::updateNova() { qDebug("Updating nova"); // create nova directory if necessary - QDir search_dir(fsutils::searchEngineLocation()); - QString nova_folder = misc::pythonVersion() >= 3 ? "nova3" : "nova"; + QDir search_dir(Utils::Fs::searchEngineLocation()); + QString nova_folder = Utils::Misc::pythonVersion() >= 3 ? "nova3" : "nova"; QFile package_file(search_dir.absoluteFilePath("__init__.py")); package_file.open(QIODevice::WriteOnly | QIODevice::Text); package_file.close(); @@ -382,7 +382,7 @@ void SearchEngine::updateNova() { removePythonScriptIfExists(filePath); QFile::copy(":/"+nova_folder+"/sgmllib3.py", filePath); } - QDir destDir(QDir(fsutils::searchEngineLocation()).absoluteFilePath("engines")); + QDir destDir(QDir(Utils::Fs::searchEngineLocation()).absoluteFilePath("engines")); QDir shipped_subDir(":/"+nova_folder+"/engines/"); QStringList files = shipped_subDir.entryList(); foreach (const QString &file, files) { diff --git a/src/searchengine/searchlistdelegate.h b/src/searchengine/searchlistdelegate.h index ad07963c0..8f6c4c37c 100644 --- a/src/searchengine/searchlistdelegate.h +++ b/src/searchengine/searchlistdelegate.h @@ -36,7 +36,7 @@ #include #include #include -#include "core/misc.h" +#include "core/utils/misc.h" #include "searchengine.h" class SearchListDelegate: public QItemDelegate { @@ -53,7 +53,7 @@ class SearchListDelegate: public QItemDelegate { switch(index.column()) { case SearchSortModel::SIZE: QItemDelegate::drawBackground(painter, opt, index); - QItemDelegate::drawDisplay(painter, opt, option.rect, misc::friendlyUnit(index.data().toLongLong())); + QItemDelegate::drawDisplay(painter, opt, option.rect, Utils::Misc::friendlyUnit(index.data().toLongLong())); break; default: QItemDelegate::paint(painter, option, index); diff --git a/src/searchengine/searchsortmodel.h b/src/searchengine/searchsortmodel.h index bb5817b2a..c0cf7ec52 100644 --- a/src/searchengine/searchsortmodel.h +++ b/src/searchengine/searchsortmodel.h @@ -2,7 +2,7 @@ #define SEARCHSORTMODEL_H #include -#include "core/misc.h" +#include "core/utils/string.h" class SearchSortModel : public QSortFilterProxyModel { Q_OBJECT @@ -23,7 +23,7 @@ protected: Q_ASSERT(vR.isValid()); bool res = false; - if (misc::naturalSort(vL.toString(), vR.toString(), res)) + if (Utils::String::naturalSort(vL.toString(), vR.toString(), res)) return res; return QSortFilterProxyModel::lessThan(left, right); diff --git a/src/searchengine/searchtab.cpp b/src/searchengine/searchtab.cpp index 63ac09fc3..6b3805063 100644 --- a/src/searchengine/searchtab.cpp +++ b/src/searchengine/searchtab.cpp @@ -36,7 +36,7 @@ #include "searchtab.h" #include "searchlistdelegate.h" -#include "core/misc.h" +#include "core/utils/misc.h" #include "searchengine.h" #include "core/preferences.h" diff --git a/src/searchengine/supportedengines.h b/src/searchengine/supportedengines.h index e453f6b5a..a03db1b41 100644 --- a/src/searchengine/supportedengines.h +++ b/src/searchengine/supportedengines.h @@ -41,7 +41,7 @@ #include #include -#include "core/fs_utils.h" +#include "core/utils/fs.h" #include "core/preferences.h" class SearchCategories: public QObject, public QHash { @@ -148,7 +148,7 @@ public slots: QProcess nova; nova.setEnvironment(QProcess::systemEnvironment()); QStringList params; - params << fsutils::toNativePath(fsutils::searchEngineLocation()+"/nova2.py"); + params << Utils::Fs::toNativePath(Utils::Fs::searchEngineLocation()+"/nova2.py"); params << "--capabilities"; nova.start("python", params, QIODevice::ReadOnly); nova.waitForStarted(); diff --git a/src/webui/btjson.cpp b/src/webui/btjson.cpp index 9eaee5535..27a357ea8 100644 --- a/src/webui/btjson.cpp +++ b/src/webui/btjson.cpp @@ -29,8 +29,8 @@ */ #include "btjson.h" -#include "core/misc.h" -#include "core/fs_utils.h" +#include "core/utils/misc.h" +#include "core/utils/fs.h" #include "core/preferences.h" #include "core/bittorrent/session.h" #include "core/bittorrent/sessionstatus.h" @@ -411,7 +411,7 @@ QByteArray btjson::getPropertiesForTorrent(const QString& hash) if (!torrent->hasMetadata()) return QByteArray(); - data[KEY_PROP_SAVE_PATH] = fsutils::toNativePath(torrent->savePath()); + data[KEY_PROP_SAVE_PATH] = Utils::Fs::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(); @@ -462,7 +462,7 @@ QByteArray btjson::getFilesForTorrent(const QString& hash) QString fileName = torrent->filePath(i); if (fileName.endsWith(".!qB", Qt::CaseInsensitive)) fileName.chop(4); - file_dict[KEY_FILE_NAME] = fsutils::toNativePath(fileName); + file_dict[KEY_FILE_NAME] = Utils::Fs::toNativePath(fileName); const qlonglong size = torrent->fileSize(i); file_dict[KEY_FILE_SIZE] = size; file_dict[KEY_FILE_PROGRESS] = fp[i]; diff --git a/src/webui/prefjson.cpp b/src/webui/prefjson.cpp index 5fb2d59cd..9de161e9e 100644 --- a/src/webui/prefjson.cpp +++ b/src/webui/prefjson.cpp @@ -31,7 +31,7 @@ #include "prefjson.h" #include "core/preferences.h" #include "core/scanfoldersmodel.h" -#include "core/fs_utils.h" +#include "core/utils/fs.h" #ifndef QT_NO_OPENSSL #include @@ -53,12 +53,12 @@ QByteArray prefjson::getPreferences() // UI data["locale"] = pref->getLocale(); // Downloads - data["save_path"] = fsutils::toNativePath(pref->getSavePath()); + data["save_path"] = Utils::Fs::toNativePath(pref->getSavePath()); data["temp_path_enabled"] = pref->isTempPathEnabled(); - data["temp_path"] = fsutils::toNativePath(pref->getTempPath()); + data["temp_path"] = Utils::Fs::toNativePath(pref->getTempPath()); QVariantList l; foreach (const QString& s, pref->getScanDirs()) { - l << fsutils::toNativePath(s); + l << Utils::Fs::toNativePath(s); } data["scan_dirs"] = l; QVariantList var_list; @@ -67,7 +67,7 @@ QByteArray prefjson::getPreferences() } data["download_in_scan_dirs"] = var_list; data["export_dir_enabled"] = pref->isTorrentExportEnabled(); - data["export_dir"] = fsutils::toNativePath(pref->getTorrentExportDir()); + data["export_dir"] = Utils::Fs::toNativePath(pref->getTorrentExportDir()); data["mail_notification_enabled"] = pref->isMailNotificationEnabled(); data["mail_notification_email"] = pref->getMailNotificationEmail(); data["mail_notification_smtp"] = pref->getMailNotificationSMTP(); @@ -76,7 +76,7 @@ QByteArray prefjson::getPreferences() data["mail_notification_username"] = pref->getMailNotificationSMTPUsername(); data["mail_notification_password"] = pref->getMailNotificationSMTPPassword(); data["autorun_enabled"] = pref->isAutoRunEnabled(); - data["autorun_program"] = fsutils::toNativePath(pref->getAutoRunProgram()); + data["autorun_program"] = Utils::Fs::toNativePath(pref->getAutoRunProgram()); data["preallocate_all"] = pref->preAllocateAllFiles(); data["queueing_enabled"] = pref->isQueueingSystemEnabled(); data["max_active_downloads"] = pref->getMaxActiveDownloads(); @@ -122,7 +122,7 @@ QByteArray prefjson::getPreferences() data["proxy_password"] = pref->getProxyPassword(); // IP Filter data["ip_filter_enabled"] = pref->isFilteringEnabled(); - data["ip_filter_path"] = fsutils::toNativePath(pref->getFilter()); + data["ip_filter_path"] = Utils::Fs::toNativePath(pref->getFilter()); // Web UI data["web_ui_port"] = pref->getWebUiPort(); data["web_ui_username"] = pref->getWebUiUsername(); @@ -189,7 +189,7 @@ void prefjson::setPreferences(const QString& json) foreach (const QString &new_folder, new_folders) { qDebug("New watched folder: %s", qPrintable(new_folder)); // Update new folders - if (!old_folders.contains(fsutils::fromNativePath(new_folder))) { + if (!old_folders.contains(Utils::Fs::fromNativePath(new_folder))) { ScanFoldersModel::instance()->addPath(new_folder, download_at_path.at(i)); } ++i; diff --git a/src/webui/webapplication.cpp b/src/webui/webapplication.cpp index 12fa7034f..aae42a6d0 100644 --- a/src/webui/webapplication.cpp +++ b/src/webui/webapplication.cpp @@ -33,8 +33,9 @@ #include #include #include "core/iconprovider.h" -#include "core/misc.h" -#include "core/fs_utils.h" +#include "core/utils/misc.h" +#include "core/utils/fs.h" +#include "core/utils/string.h" #include "core/preferences.h" #include "btjson.h" #include "prefjson.h" @@ -164,8 +165,8 @@ void WebApplication::action_public_login() md5.addData(request().posts["password"].toLocal8Bit()); QString pass = md5.result().toHex(); - bool equalUser = misc::slowEquals(request().posts["username"].toUtf8(), pref->getWebUiUsername().toUtf8()); - bool equalPass = misc::slowEquals(pass.toUtf8(), pref->getWebUiPassword().toUtf8()); + bool equalUser = Utils::String::slowEquals(request().posts["username"].toUtf8(), pref->getWebUiUsername().toUtf8()); + bool equalPass = Utils::String::slowEquals(pass.toUtf8(), pref->getWebUiPassword().toUtf8()); if (equalUser && equalPass) { sessionStart(); @@ -303,7 +304,7 @@ void WebApplication::action_command_download() if (!url.isEmpty()) { if (url.startsWith("bc://bt/", Qt::CaseInsensitive)) { qDebug("Converting bc link to magnet link"); - url = misc::bcLinkToMagnet(url); + url = Utils::Misc::bcLinkToMagnet(url); } BitTorrent::Session::instance()->addTorrent(url); @@ -332,7 +333,7 @@ void WebApplication::action_command_upload() } } // Clean up - fsutils::forceRemove(filePath); + Utils::Fs::forceRemove(filePath); } else { qWarning() << "I/O Error: Could not create temporary file"; From 5f288d228dd1f4bf496957ade2ab51b56c28fc1b Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Wed, 13 May 2015 18:39:48 +0300 Subject: [PATCH 11/12] Fix coding style (Issue #2192). --- src/core/http/connection.h | 36 +- src/core/http/irequesthandler.h | 14 +- src/core/http/requestparser.h | 60 ++- src/core/http/responsebuilder.h | 32 +- src/core/http/responsegenerator.h | 12 +- src/core/http/server.cpp | 2 +- src/core/http/server.h | 56 ++- src/core/http/types.h | 88 ++-- src/core/net/dnsupdater.cpp | 414 ++++++++--------- src/core/net/dnsupdater.h | 74 +-- src/core/net/smtp.cpp | 718 +++++++++++++++--------------- src/core/net/smtp.h | 98 ++-- 12 files changed, 820 insertions(+), 784 deletions(-) diff --git a/src/core/http/connection.h b/src/core/http/connection.h index 19c8acb6a..95498b1e6 100644 --- a/src/core/http/connection.h +++ b/src/core/http/connection.h @@ -42,30 +42,28 @@ QT_END_NAMESPACE namespace Http { + class IRequestHandler; -class IRequestHandler; + class Connection : public QObject + { + Q_OBJECT + Q_DISABLE_COPY(Connection) -class Connection : public QObject -{ - Q_OBJECT - Q_DISABLE_COPY(Connection) + public: + Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = 0); + ~Connection(); -public: - Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = 0); - ~Connection(); + private slots: + void read(); -private slots: - void read(); - -private: - static bool acceptsGzipEncoding(const QString &encoding); - void sendResponse(const Response &response); - - QTcpSocket *m_socket; - IRequestHandler *m_requestHandler; - QByteArray m_receivedData; -}; + private: + static bool acceptsGzipEncoding(const QString &encoding); + void sendResponse(const Response &response); + QTcpSocket *m_socket; + IRequestHandler *m_requestHandler; + QByteArray m_receivedData; + }; } #endif // HTTP_CONNECTION_H diff --git a/src/core/http/irequesthandler.h b/src/core/http/irequesthandler.h index 2fab8d5c6..0d2cf9a5c 100644 --- a/src/core/http/irequesthandler.h +++ b/src/core/http/irequesthandler.h @@ -33,14 +33,12 @@ namespace Http { - -class IRequestHandler -{ -public: - virtual ~IRequestHandler() {} - virtual Response processRequest(const Request &request, const Environment &env) = 0; -}; - + class IRequestHandler + { + public: + virtual ~IRequestHandler() {} + virtual Response processRequest(const Request &request, const Environment &env) = 0; + }; } #endif // HTTP_IREQUESTHANDLER_H diff --git a/src/core/http/requestparser.h b/src/core/http/requestparser.h index ec8265e38..5ea8e05e4 100644 --- a/src/core/http/requestparser.h +++ b/src/core/http/requestparser.h @@ -36,39 +36,37 @@ namespace Http { - -class RequestParser -{ -public: - enum ErrorCode + class RequestParser { - NoError = 0, - IncompleteRequest, - BadRequest + public: + enum ErrorCode + { + NoError = 0, + IncompleteRequest, + BadRequest + }; + + // when result != NoError parsed request is undefined + // Warning! Header names are converted to lower-case. + static ErrorCode parse(const QByteArray &data, Request &request, uint maxContentLength = 10000000 /* ~10MB */); + + private: + RequestParser(uint maxContentLength); + + ErrorCode parseHttpRequest(const QByteArray &data, Request &request); + + bool parseHttpHeader(const QByteArray &data); + bool parseStartingLine(const QString &line); + bool parseContent(const QByteArray &data); + bool parseFormData(const QByteArray &data); + QList splitMultipartData(const QByteArray &data, const QByteArray &boundary); + + static bool parseHeaderLine(const QString &line, QPair &out); + static bool parseHeaderValue(const QString &value, QStringMap &out); + + const uint m_maxContentLength; + Request m_request; }; - - // when result != NoError parsed request is undefined - // Warning! Header names are converted to lower-case. - static ErrorCode parse(const QByteArray &data, Request &request, uint maxContentLength = 10000000 /* ~10MB */); - -private: - RequestParser(uint maxContentLength); - - ErrorCode parseHttpRequest(const QByteArray &data, Request &request); - - bool parseHttpHeader(const QByteArray &data); - bool parseStartingLine(const QString &line); - bool parseContent(const QByteArray &data); - bool parseFormData(const QByteArray &data); - QList splitMultipartData(const QByteArray &data, const QByteArray &boundary); - - static bool parseHeaderLine(const QString &line, QPair &out); - static bool parseHeaderValue(const QString &value, QStringMap &out); - - const uint m_maxContentLength; - Request m_request; -}; - } #endif // HTTP_REQUESTPARSER_H diff --git a/src/core/http/responsebuilder.h b/src/core/http/responsebuilder.h index 0a3b0d8f1..53e92c418 100644 --- a/src/core/http/responsebuilder.h +++ b/src/core/http/responsebuilder.h @@ -34,27 +34,25 @@ namespace Http { + class ResponseBuilder : public QObject + { + public: + explicit ResponseBuilder(QObject *parent = 0); -class ResponseBuilder : public QObject -{ -public: - explicit ResponseBuilder(QObject *parent = 0); + protected: + void status(uint code = 200, const QString &text = QLatin1String("OK")); + void header(const QString &name, const QString &value); + void print(const QString &text, const QString &type = CONTENT_TYPE_HTML); + void print(const QByteArray &data, const QString &type = CONTENT_TYPE_HTML); + void clear(); -protected: - void status(uint code = 200, const QString &text = QLatin1String("OK")); - void header(const QString &name, const QString &value); - void print(const QString &text, const QString &type = CONTENT_TYPE_HTML); - void print(const QByteArray &data, const QString &type = CONTENT_TYPE_HTML); - void clear(); + Response response() const; - Response response() const; - -private: - void print_impl(const QByteArray &data, const QString &type); - - Response m_response; -}; + private: + void print_impl(const QByteArray &data, const QString &type); + Response m_response; + }; } #endif // HTTP_RESPONSEBUILDER_H diff --git a/src/core/http/responsegenerator.h b/src/core/http/responsegenerator.h index 43d73d709..e12b5c344 100644 --- a/src/core/http/responsegenerator.h +++ b/src/core/http/responsegenerator.h @@ -37,13 +37,11 @@ namespace Http { - -class ResponseGenerator -{ -public: - static QByteArray generate(Response response); -}; - + class ResponseGenerator + { + public: + static QByteArray generate(Response response); + }; } #endif // HTTP_RESPONSEGENERATOR_H diff --git a/src/core/http/server.cpp b/src/core/http/server.cpp index 8bf905fec..f9f58e10c 100644 --- a/src/core/http/server.cpp +++ b/src/core/http/server.cpp @@ -38,7 +38,7 @@ using namespace Http; -Server::Server(IRequestHandler *requestHandler, QObject* parent) +Server::Server(IRequestHandler *requestHandler, QObject *parent) : QTcpServer(parent) , m_requestHandler(requestHandler) #ifndef QT_NO_OPENSSL diff --git a/src/core/http/server.h b/src/core/http/server.h index c8cb774c3..87d62fe92 100644 --- a/src/core/http/server.h +++ b/src/core/http/server.h @@ -41,40 +41,38 @@ namespace Http { + class IRequestHandler; + class Connection; -class IRequestHandler; -class Connection; + class Server : public QTcpServer + { + Q_OBJECT + Q_DISABLE_COPY(Server) -class Server : public QTcpServer -{ - Q_OBJECT - Q_DISABLE_COPY(Server) + public: + Server(IRequestHandler *requestHandler, QObject *parent = 0); + ~Server(); -public: - Server(IRequestHandler *requestHandler, QObject *parent = 0); - ~Server(); + #ifndef QT_NO_OPENSSL + void enableHttps(const QSslCertificate &certificate, const QSslKey &key); + void disableHttps(); + #endif -#ifndef QT_NO_OPENSSL - void enableHttps(const QSslCertificate &certificate, const QSslKey &key); - void disableHttps(); -#endif - -private: -#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) - void incomingConnection(qintptr socketDescriptor); -#else - void incomingConnection(int socketDescriptor); -#endif - -private: - IRequestHandler *m_requestHandler; -#ifndef QT_NO_OPENSSL - bool m_https; - QSslCertificate m_certificate; - QSslKey m_key; -#endif -}; + private: + #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + void incomingConnection(qintptr socketDescriptor); + #else + void incomingConnection(int socketDescriptor); + #endif + private: + IRequestHandler *m_requestHandler; + #ifndef QT_NO_OPENSSL + bool m_https; + QSslCertificate m_certificate; + QSslKey m_key; + #endif + }; } #endif // HTTP_SERVER_H diff --git a/src/core/http/types.h b/src/core/http/types.h index 5857031bb..5a2d9cf49 100644 --- a/src/core/http/types.h +++ b/src/core/http/types.h @@ -37,59 +37,57 @@ typedef QMap QStringMap; namespace Http { + const QString HEADER_SET_COOKIE = "Set-Cookie"; + const QString HEADER_CONTENT_TYPE = "Content-Type"; + const QString HEADER_CONTENT_ENCODING = "Content-Encoding"; + const QString HEADER_CONTENT_LENGTH = "Content-Length"; + const QString HEADER_CACHE_CONTROL = "Cache-Control"; -const QString HEADER_SET_COOKIE = "Set-Cookie"; -const QString HEADER_CONTENT_TYPE = "Content-Type"; -const QString HEADER_CONTENT_ENCODING = "Content-Encoding"; -const QString HEADER_CONTENT_LENGTH = "Content-Length"; -const QString HEADER_CACHE_CONTROL = "Cache-Control"; + const QString CONTENT_TYPE_CSS = "text/css; charset=UTF-8"; + const QString CONTENT_TYPE_GIF = "image/gif"; + const QString CONTENT_TYPE_HTML = "text/html; charset=UTF-8"; + const QString CONTENT_TYPE_JS = "text/javascript; charset=UTF-8"; + const QString CONTENT_TYPE_PNG = "image/png"; + const QString CONTENT_TYPE_TXT = "text/plain; charset=UTF-8"; -const QString CONTENT_TYPE_CSS = "text/css; charset=UTF-8"; -const QString CONTENT_TYPE_GIF = "image/gif"; -const QString CONTENT_TYPE_HTML = "text/html; charset=UTF-8"; -const QString CONTENT_TYPE_JS = "text/javascript; charset=UTF-8"; -const QString CONTENT_TYPE_PNG = "image/png"; -const QString CONTENT_TYPE_TXT = "text/plain; charset=UTF-8"; + struct Environment + { + QHostAddress clientAddress; + }; -struct Environment -{ - QHostAddress clientAddress; -}; + struct UploadedFile + { + QString filename; // original filename + QString type; // MIME type + QByteArray data; // File data + }; -struct UploadedFile -{ - QString filename; // original filename - QString type; // MIME type - QByteArray data; // File data -}; + struct Request + { + QString method; + QString path; + QStringMap headers; + QStringMap gets; + QStringMap posts; + QMap files; + }; -struct Request -{ - QString method; - QString path; - QStringMap headers; - QStringMap gets; - QStringMap posts; - QMap files; -}; + struct ResponseStatus + { + uint code; + QString text; -struct ResponseStatus -{ - uint code; - QString text; + ResponseStatus(uint code = 200, const QString& text = "OK"): code(code), text(text) {} + }; - ResponseStatus(uint code = 200, const QString& text = "OK"): code(code), text(text) {} -}; - -struct Response -{ - ResponseStatus status; - QStringMap headers; - QByteArray content; - - Response(uint code = 200, const QString& text = "OK"): status(code, text) {} -}; + struct Response + { + ResponseStatus status; + QStringMap headers; + QByteArray content; + Response(uint code = 200, const QString& text = "OK"): status(code, text) {} + }; } #endif // HTTP_TYPES_H diff --git a/src/core/net/dnsupdater.cpp b/src/core/net/dnsupdater.cpp index bb47528b8..11f0e392f 100644 --- a/src/core/net/dnsupdater.cpp +++ b/src/core/net/dnsupdater.cpp @@ -35,265 +35,271 @@ #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) #include #endif -#include "dnsupdater.h" + #include "core/logger.h" +#include "dnsupdater.h" using namespace Net; -DNSUpdater::DNSUpdater(QObject *parent) : - QObject(parent), m_state(OK), m_service(DNS::NONE) +DNSUpdater::DNSUpdater(QObject *parent) + : QObject(parent) + , m_state(OK) + , m_service(DNS::NONE) { - updateCredentials(); + updateCredentials(); - // Load saved settings from previous session - const Preferences* const pref = Preferences::instance(); - m_lastIPCheckTime = pref->getDNSLastUpd(); - m_lastIP = QHostAddress(pref->getDNSLastIP()); + // Load saved settings from previous session + const Preferences *const pref = Preferences::instance(); + m_lastIPCheckTime = pref->getDNSLastUpd(); + m_lastIP = QHostAddress(pref->getDNSLastIP()); - // Start IP checking timer - m_ipCheckTimer.setInterval(IP_CHECK_INTERVAL_MS); - connect(&m_ipCheckTimer, SIGNAL(timeout()), SLOT(checkPublicIP())); - m_ipCheckTimer.start(); + // Start IP checking timer + m_ipCheckTimer.setInterval(IP_CHECK_INTERVAL_MS); + connect(&m_ipCheckTimer, SIGNAL(timeout()), SLOT(checkPublicIP())); + m_ipCheckTimer.start(); - // Check lastUpdate to avoid flooding - if (!m_lastIPCheckTime.isValid() || - m_lastIPCheckTime.secsTo(QDateTime::currentDateTime())*1000 > IP_CHECK_INTERVAL_MS) { - checkPublicIP(); - } + // Check lastUpdate to avoid flooding + if (!m_lastIPCheckTime.isValid() + || (m_lastIPCheckTime.secsTo(QDateTime::currentDateTime()) * 1000 > IP_CHECK_INTERVAL_MS)) { + checkPublicIP(); + } } -DNSUpdater::~DNSUpdater() { - // Save lastupdate time and last ip - Preferences* const pref = Preferences::instance(); - pref->setDNSLastUpd(m_lastIPCheckTime); - pref->setDNSLastIP(m_lastIP.toString()); +DNSUpdater::~DNSUpdater() +{ + // Save lastupdate time and last ip + Preferences *const pref = Preferences::instance(); + pref->setDNSLastUpd(m_lastIPCheckTime); + pref->setDNSLastIP(m_lastIP.toString()); } void DNSUpdater::checkPublicIP() { - Q_ASSERT(m_state == OK); - QNetworkAccessManager *manager = new QNetworkAccessManager(this); - connect(manager, SIGNAL(finished(QNetworkReply*)), - SLOT(ipRequestFinished(QNetworkReply*))); - m_lastIPCheckTime = QDateTime::currentDateTime(); - QNetworkRequest request; - request.setUrl(QUrl("http://checkip.dyndns.org")); - request.setRawHeader("User-Agent", "qBittorrent/" VERSION" chris@qbittorrent.org"); - manager->get(request); + Q_ASSERT(m_state == OK); + QNetworkAccessManager *manager = new QNetworkAccessManager(this); + connect(manager, SIGNAL(finished(QNetworkReply *)), SLOT(ipRequestFinished(QNetworkReply *))); + m_lastIPCheckTime = QDateTime::currentDateTime(); + QNetworkRequest request; + request.setUrl(QUrl("http://checkip.dyndns.org")); + request.setRawHeader("User-Agent", "qBittorrent/" VERSION" chris@qbittorrent.org"); + manager->get(request); } void DNSUpdater::ipRequestFinished(QNetworkReply *reply) { - qDebug() << Q_FUNC_INFO; - if (reply->error()) { - // Error - qWarning() << Q_FUNC_INFO << "Error:" << reply->errorString(); - } else { - // Parse response - QRegExp ipregex("Current IP Address:\\s+([^<]+)"); - QString ret = reply->readAll(); - if (ipregex.indexIn(ret) >= 0) { - QString ip_str = ipregex.cap(1); - qDebug() << Q_FUNC_INFO << "Regular expression captured the following IP:" << ip_str; - QHostAddress new_ip(ip_str); - if (!new_ip.isNull()) { - if (m_lastIP != new_ip) { - qDebug() << Q_FUNC_INFO << "The IP address changed, report the change to DynDNS..."; - qDebug() << m_lastIP.toString() << "->" << new_ip.toString(); - m_lastIP = new_ip; - updateDNSService(); - } - } else { - qWarning() << Q_FUNC_INFO << "Failed to construct a QHostAddress from the IP string"; - } - } else { - qWarning() << Q_FUNC_INFO << "Regular expression failed ot capture the IP address"; + qDebug() << Q_FUNC_INFO; + if (reply->error()) { + // Error + qWarning() << Q_FUNC_INFO << "Error:" << reply->errorString(); } - } - // Clean up - reply->deleteLater(); - sender()->deleteLater(); + else { + // Parse response + QRegExp ipregex("Current IP Address:\\s+([^<]+)"); + QString ret = reply->readAll(); + if (ipregex.indexIn(ret) >= 0) { + QString ip_str = ipregex.cap(1); + qDebug() << Q_FUNC_INFO << "Regular expression captured the following IP:" << ip_str; + QHostAddress new_ip(ip_str); + if (!new_ip.isNull()) { + if (m_lastIP != new_ip) { + qDebug() << Q_FUNC_INFO << "The IP address changed, report the change to DynDNS..."; + qDebug() << m_lastIP.toString() << "->" << new_ip.toString(); + m_lastIP = new_ip; + updateDNSService(); + } + } + else { + qWarning() << Q_FUNC_INFO << "Failed to construct a QHostAddress from the IP string"; + } + } + else { + qWarning() << Q_FUNC_INFO << "Regular expression failed ot capture the IP address"; + } + } + // Clean up + reply->deleteLater(); + sender()->deleteLater(); } void DNSUpdater::updateDNSService() { - qDebug() << Q_FUNC_INFO; - // Prepare request - QNetworkAccessManager *manager = new QNetworkAccessManager(this); - connect(manager, SIGNAL(finished(QNetworkReply*)), - SLOT(ipUpdateFinished(QNetworkReply*))); - m_lastIPCheckTime = QDateTime::currentDateTime(); - QNetworkRequest request; - request.setUrl(getUpdateUrl()); - request.setRawHeader("User-Agent", "qBittorrent/" VERSION" chris@qbittorrent.org"); - manager->get(request); + qDebug() << Q_FUNC_INFO; + // Prepare request + QNetworkAccessManager *manager = new QNetworkAccessManager(this); + connect(manager, SIGNAL(finished(QNetworkReply *)), SLOT(ipUpdateFinished(QNetworkReply *))); + m_lastIPCheckTime = QDateTime::currentDateTime(); + QNetworkRequest request; + request.setUrl(getUpdateUrl()); + request.setRawHeader("User-Agent", "qBittorrent/" VERSION" chris@qbittorrent.org"); + manager->get(request); } QUrl DNSUpdater::getUpdateUrl() const { - QUrl url; + QUrl url; #ifdef QT_NO_OPENSSL - url.setScheme("http"); + url.setScheme("http"); #else - url.setScheme("https"); + url.setScheme("https"); #endif - url.setUserName(m_username); - url.setPassword(m_password); + url.setUserName(m_username); + url.setPassword(m_password); - Q_ASSERT(!m_lastIP.isNull()); - // Service specific - switch(m_service) { - case DNS::DYNDNS: - url.setHost("members.dyndns.org"); - break; - case DNS::NOIP: - url.setHost("dynupdate.no-ip.com"); - break; - default: - qWarning() << "Unrecognized Dynamic DNS service!"; - Q_ASSERT(0); - } - url.setPath("/nic/update"); + Q_ASSERT(!m_lastIP.isNull()); + // Service specific + switch(m_service) { + case DNS::DYNDNS: + url.setHost("members.dyndns.org"); + break; + case DNS::NOIP: + url.setHost("dynupdate.no-ip.com"); + break; + default: + qWarning() << "Unrecognized Dynamic DNS service!"; + Q_ASSERT(0); + } + url.setPath("/nic/update"); #if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) - url.addQueryItem("hostname", m_domain); - url.addQueryItem("myip", m_lastIP.toString()); + url.addQueryItem("hostname", m_domain); + url.addQueryItem("myip", m_lastIP.toString()); #else - QUrlQuery urlQuery(url); - urlQuery.addQueryItem("hostname", m_domain); - urlQuery.addQueryItem("myip", m_lastIP.toString()); - url.setQuery(urlQuery); + QUrlQuery urlQuery(url); + urlQuery.addQueryItem("hostname", m_domain); + urlQuery.addQueryItem("myip", m_lastIP.toString()); + url.setQuery(urlQuery); #endif - Q_ASSERT(url.isValid()); + Q_ASSERT(url.isValid()); - qDebug() << Q_FUNC_INFO << url.toString(); - return url; + qDebug() << Q_FUNC_INFO << url.toString(); + return url; } void DNSUpdater::ipUpdateFinished(QNetworkReply *reply) { - if (reply->error()) { - // Error - qWarning() << Q_FUNC_INFO << "Error:" << reply->errorString(); - } else { - // Pase reply - processIPUpdateReply(reply->readAll()); - } - // Clean up - reply->deleteLater(); - sender()->deleteLater(); + if (reply->error()) { + // Error + qWarning() << Q_FUNC_INFO << "Error:" << reply->errorString(); + } + else { + // Pase reply + processIPUpdateReply(reply->readAll()); + } + // Clean up + reply->deleteLater(); + sender()->deleteLater(); } void DNSUpdater::processIPUpdateReply(const QString &reply) { - Logger* const logger = Logger::instance(); - qDebug() << Q_FUNC_INFO << reply; - QString code = reply.split(" ").first(); - qDebug() << Q_FUNC_INFO << "Code:" << code; - if (code == "good" || code == "nochg") { - logger->addMessage(tr("Your dynamic DNS was successfully updated."), Log::INFO); - return; - } - if (code == "911" || code == "dnserr") { - logger->addMessage(tr("Dynamic DNS error: The service is temporarily unavailable, it will be retried in 30 minutes."), Log::CRITICAL); + Logger *const logger = Logger::instance(); + qDebug() << Q_FUNC_INFO << reply; + QString code = reply.split(" ").first(); + qDebug() << Q_FUNC_INFO << "Code:" << code; + if (code == "good" || code == "nochg") { + logger->addMessage(tr("Your dynamic DNS was successfully updated."), Log::INFO); + return; + } + if ((code == "911") || (code == "dnserr")) { + logger->addMessage(tr("Dynamic DNS error: The service is temporarily unavailable, it will be retried in 30 minutes."), Log::CRITICAL); + m_lastIP.clear(); + // It will retry in 30 minutes because the timer was not stopped + return; + } + // Everything bellow is an error, stop updating until the user updates something + m_ipCheckTimer.stop(); m_lastIP.clear(); - // It will retry in 30 minutes because the timer was not stopped - return; - } - // Everything bellow is an error, stop updating until the user updates something - m_ipCheckTimer.stop(); - m_lastIP.clear(); - if (code == "nohost") { - logger->addMessage(tr("Dynamic DNS error: hostname supplied does not exist under specified account."), Log::CRITICAL); - m_state = INVALID_CREDS; - return; - } - if (code == "badauth") { - logger->addMessage(tr("Dynamic DNS error: Invalid username/password."), Log::CRITICAL); - m_state = INVALID_CREDS; - return; - } - if (code == "badagent") { - logger->addMessage(tr("Dynamic DNS error: qBittorrent was blacklisted by the service, please report a bug at http://bugs.qbittorrent.org."), - Log::CRITICAL); - m_state = FATAL; - return; - } - if (code == "!donator") { - logger->addMessage(tr("Dynamic DNS error: %1 was returned by the service, please report a bug at http://bugs.qbittorrent.org.").arg("!donator"), - Log::CRITICAL); - m_state = FATAL; - return; - } - if (code == "abuse") { - logger->addMessage(tr("Dynamic DNS error: Your username was blocked due to abuse."), Log::CRITICAL); - m_state = FATAL; - return; - } + if (code == "nohost") { + logger->addMessage(tr("Dynamic DNS error: hostname supplied does not exist under specified account."), Log::CRITICAL); + m_state = INVALID_CREDS; + return; + } + if (code == "badauth") { + logger->addMessage(tr("Dynamic DNS error: Invalid username/password."), Log::CRITICAL); + m_state = INVALID_CREDS; + return; + } + if (code == "badagent") { + logger->addMessage(tr("Dynamic DNS error: qBittorrent was blacklisted by the service, please report a bug at http://bugs.qbittorrent.org."), + Log::CRITICAL); + m_state = FATAL; + return; + } + if (code == "!donator") { + logger->addMessage(tr("Dynamic DNS error: %1 was returned by the service, please report a bug at http://bugs.qbittorrent.org.").arg("!donator"), + Log::CRITICAL); + m_state = FATAL; + return; + } + if (code == "abuse") { + logger->addMessage(tr("Dynamic DNS error: Your username was blocked due to abuse."), Log::CRITICAL); + m_state = FATAL; + return; + } } void DNSUpdater::updateCredentials() { - if (m_state == FATAL) return; - Preferences* const pref = Preferences::instance(); - Logger* const logger = Logger::instance(); - bool change = false; - // Get DNS service information - if (m_service != pref->getDynDNSService()) { - m_service = pref->getDynDNSService(); - change = true; - } - if (m_domain != pref->getDynDomainName()) { - m_domain = pref->getDynDomainName(); - QRegExp domain_regex("^(?:(?!\\d|-)[a-zA-Z0-9\\-]{1,63}\\.)+[a-zA-Z]{2,}$"); - if (domain_regex.indexIn(m_domain) < 0) { - logger->addMessage(tr("Dynamic DNS error: supplied domain name is invalid."), Log::CRITICAL); - m_lastIP.clear(); - m_ipCheckTimer.stop(); - m_state = INVALID_CREDS; - return; + if (m_state == FATAL) return; + Preferences *const pref = Preferences::instance(); + Logger *const logger = Logger::instance(); + bool change = false; + // Get DNS service information + if (m_service != pref->getDynDNSService()) { + m_service = pref->getDynDNSService(); + change = true; } - change = true; - } - if (m_username != pref->getDynDNSUsername()) { - m_username = pref->getDynDNSUsername(); - if (m_username.length() < 4) { - logger->addMessage(tr("Dynamic DNS error: supplied username is too short."), Log::CRITICAL); - m_lastIP.clear(); - m_ipCheckTimer.stop(); - m_state = INVALID_CREDS; - return; + if (m_domain != pref->getDynDomainName()) { + m_domain = pref->getDynDomainName(); + QRegExp domain_regex("^(?:(?!\\d|-)[a-zA-Z0-9\\-]{1,63}\\.)+[a-zA-Z]{2,}$"); + if (domain_regex.indexIn(m_domain) < 0) { + logger->addMessage(tr("Dynamic DNS error: supplied domain name is invalid."), Log::CRITICAL); + m_lastIP.clear(); + m_ipCheckTimer.stop(); + m_state = INVALID_CREDS; + return; + } + change = true; } - change = true; - } - if (m_password != pref->getDynDNSPassword()) { - m_password = pref->getDynDNSPassword(); - if (m_password.length() < 4) { - logger->addMessage(tr("Dynamic DNS error: supplied password is too short."), Log::CRITICAL); - m_lastIP.clear(); - m_ipCheckTimer.stop(); - m_state = INVALID_CREDS; - return; + if (m_username != pref->getDynDNSUsername()) { + m_username = pref->getDynDNSUsername(); + if (m_username.length() < 4) { + logger->addMessage(tr("Dynamic DNS error: supplied username is too short."), Log::CRITICAL); + m_lastIP.clear(); + m_ipCheckTimer.stop(); + m_state = INVALID_CREDS; + return; + } + change = true; + } + if (m_password != pref->getDynDNSPassword()) { + m_password = pref->getDynDNSPassword(); + if (m_password.length() < 4) { + logger->addMessage(tr("Dynamic DNS error: supplied password is too short."), Log::CRITICAL); + m_lastIP.clear(); + m_ipCheckTimer.stop(); + m_state = INVALID_CREDS; + return; + } + change = true; } - change = true; - } - if (m_state == INVALID_CREDS && change) { - m_state = OK; // Try again - m_ipCheckTimer.start(); - checkPublicIP(); - } + if ((m_state == INVALID_CREDS) && change) { + m_state = OK; // Try again + m_ipCheckTimer.start(); + checkPublicIP(); + } } QUrl DNSUpdater::getRegistrationUrl(int service) { - switch(service) { - case DNS::DYNDNS: - return QUrl("https://www.dyndns.com/account/services/hosts/add.html"); - case DNS::NOIP: - return QUrl("http://www.no-ip.com/services/managed_dns/free_dynamic_dns.html"); - default: - Q_ASSERT(0); - } - return QUrl(); + switch(service) { + case DNS::DYNDNS: + return QUrl("https://www.dyndns.com/account/services/hosts/add.html"); + case DNS::NOIP: + return QUrl("http://www.no-ip.com/services/managed_dns/free_dynamic_dns.html"); + default: + Q_ASSERT(0); + } + return QUrl(); } diff --git a/src/core/net/dnsupdater.h b/src/core/net/dnsupdater.h index 9a252405a..0cc809360 100644 --- a/src/core/net/dnsupdater.h +++ b/src/core/net/dnsupdater.h @@ -39,48 +39,52 @@ #include "core/preferences.h" namespace Net -{ +{ + // Based on http://www.dyndns.com/developers/specs/ + class DNSUpdater : public QObject + { + Q_OBJECT -/*! - * Based on http://www.dyndns.com/developers/specs/ - */ -class DNSUpdater : public QObject -{ - Q_OBJECT -public: - explicit DNSUpdater(QObject *parent = 0); - ~DNSUpdater(); - static QUrl getRegistrationUrl(int service); + public: + explicit DNSUpdater(QObject *parent = 0); + ~DNSUpdater(); -public slots: - void updateCredentials(); + static QUrl getRegistrationUrl(int service); -private slots: - void checkPublicIP(); - void ipRequestFinished(QNetworkReply* reply); - void updateDNSService(); - void ipUpdateFinished(QNetworkReply* reply); + public slots: + void updateCredentials(); -private: - QUrl getUpdateUrl() const; - void processIPUpdateReply(const QString &reply); + private slots: + void checkPublicIP(); + void ipRequestFinished(QNetworkReply *reply); + void updateDNSService(); + void ipUpdateFinished(QNetworkReply *reply); -private: - QHostAddress m_lastIP; - QDateTime m_lastIPCheckTime; - QTimer m_ipCheckTimer; - int m_state; - // Service creds - DNS::Service m_service; - QString m_domain; - QString m_username; - QString m_password; + private: + QUrl getUpdateUrl() const; + void processIPUpdateReply(const QString &reply); -private: - static const int IP_CHECK_INTERVAL_MS = 1800000; // 30 min - enum State { OK, INVALID_CREDS, FATAL }; -}; + private: + QHostAddress m_lastIP; + QDateTime m_lastIPCheckTime; + QTimer m_ipCheckTimer; + int m_state; + // Service creds + DNS::Service m_service; + QString m_domain; + QString m_username; + QString m_password; + private: + static const int IP_CHECK_INTERVAL_MS = 1800000; // 30 min + + enum State + { + OK, + INVALID_CREDS, + FATAL + }; + }; } #endif // DNSUPDATER_H diff --git a/src/core/net/smtp.cpp b/src/core/net/smtp.cpp index 1a685b48b..5123ccf81 100644 --- a/src/core/net/smtp.cpp +++ b/src/core/net/smtp.cpp @@ -50,433 +50,453 @@ #include #include -namespace { -const short DEFAULT_PORT = 25; -const short DEFAULT_PORT_SSL = 465; - -QByteArray hmacMD5(QByteArray key, const QByteArray &msg) +namespace { - const int blockSize = 64; // HMAC-MD5 block size - if (key.length() > blockSize) { // if key is longer than block size (64), reduce key length with MD5 compression - key = QCryptographicHash::hash(key, QCryptographicHash::Md5); - } + const short DEFAULT_PORT = 25; + const short DEFAULT_PORT_SSL = 465; - QByteArray innerPadding(blockSize, char(0x36)); // initialize inner padding with char "6" - QByteArray outerPadding(blockSize, char(0x5c)); // initialize outer padding with char "\" - // ascii characters 0x36 ("6") and 0x5c ("\") are selected because they have large - // Hamming distance (http://en.wikipedia.org/wiki/Hamming_distance) + QByteArray hmacMD5(QByteArray key, const QByteArray &msg) + { + const int blockSize = 64; // HMAC-MD5 block size + if (key.length() > blockSize) { // if key is longer than block size (64), reduce key length with MD5 compression + key = QCryptographicHash::hash(key, QCryptographicHash::Md5); + } - for (int i = 0; i < key.length(); i++) { - innerPadding[i] = innerPadding[i] ^ key.at(i); // XOR operation between every byte in key and innerpadding, of key length - outerPadding[i] = outerPadding[i] ^ key.at(i); // XOR operation between every byte in key and outerpadding, of key length - } + QByteArray innerPadding(blockSize, char(0x36)); // initialize inner padding with char "6" + QByteArray outerPadding(blockSize, char(0x5c)); // initialize outer padding with char "\" + // ascii characters 0x36 ("6") and 0x5c ("\") are selected because they have large + // Hamming distance (http://en.wikipedia.org/wiki/Hamming_distance) - // result = hash ( outerPadding CONCAT hash ( innerPadding CONCAT baseString ) ).toBase64 - QByteArray total = outerPadding; - QByteArray part = innerPadding; - part.append(msg); - total.append(QCryptographicHash::hash(part, QCryptographicHash::Md5)); - return QCryptographicHash::hash(total, QCryptographicHash::Md5); -} + for (int i = 0; i < key.length(); i++) { + innerPadding[i] = innerPadding[i] ^ key.at(i); // XOR operation between every byte in key and innerpadding, of key length + outerPadding[i] = outerPadding[i] ^ key.at(i); // XOR operation between every byte in key and outerpadding, of key length + } -QByteArray determineFQDN() -{ - QString hostname = QHostInfo::localHostName(); - if (hostname.isEmpty()) - hostname = "localhost"; + // result = hash ( outerPadding CONCAT hash ( innerPadding CONCAT baseString ) ).toBase64 + QByteArray total = outerPadding; + QByteArray part = innerPadding; + part.append(msg); + total.append(QCryptographicHash::hash(part, QCryptographicHash::Md5)); + return QCryptographicHash::hash(total, QCryptographicHash::Md5); + } - return hostname.toLocal8Bit(); -} + QByteArray determineFQDN() + { + QString hostname = QHostInfo::localHostName(); + if (hostname.isEmpty()) + hostname = "localhost"; + + return hostname.toLocal8Bit(); + } } // namespace using namespace Net; -Smtp::Smtp(QObject *parent): QObject(parent), - state(Init), use_ssl(false) { +Smtp::Smtp(QObject *parent) + : QObject(parent) + , m_state(Init) + , m_useSsl(false) +{ #ifndef QT_NO_OPENSSL - socket = new QSslSocket(this); + m_socket = new QSslSocket(this); #else - socket = new QTcpSocket(this); + m_socket = new QTcpSocket(this); #endif - connect(socket, SIGNAL(readyRead()), SLOT(readyRead())); - connect(socket, SIGNAL(disconnected()), SLOT(deleteLater())); + connect(m_socket, SIGNAL(readyRead()), SLOT(readyRead())); + connect(m_socket, SIGNAL(disconnected()), SLOT(deleteLater())); - // Test hmacMD5 function (http://www.faqs.org/rfcs/rfc2202.html) - Q_ASSERT(hmacMD5("Jefe", "what do ya want for nothing?").toHex() - == "750c783e6ab0b503eaa86e310a5db738"); - Q_ASSERT(hmacMD5(QByteArray::fromHex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"), - "Hi There").toHex() - == "9294727a3638bb1c13f48ef8158bfc9d"); + // Test hmacMD5 function (http://www.faqs.org/rfcs/rfc2202.html) + Q_ASSERT(hmacMD5("Jefe", "what do ya want for nothing?").toHex() + == "750c783e6ab0b503eaa86e310a5db738"); + Q_ASSERT(hmacMD5(QByteArray::fromHex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"), "Hi There").toHex() + == "9294727a3638bb1c13f48ef8158bfc9d"); } -Smtp::~Smtp() { - qDebug() << Q_FUNC_INFO; +Smtp::~Smtp() +{ + qDebug() << Q_FUNC_INFO; } -void Smtp::sendMail(const QString &from, const QString &to, const QString &subject, const QString &body) { - const Preferences* const pref = Preferences::instance(); - QTextCodec* latin1 = QTextCodec::codecForName("latin1"); - message = ""; - message += encode_mime_header("Date", QDateTime::currentDateTime().toUTC().toString("ddd, d MMM yyyy hh:mm:ss UT"), latin1); - message += encode_mime_header("From", from, latin1); - message += encode_mime_header("Subject", subject, latin1); - message += encode_mime_header("To", to, latin1); - message += "MIME-Version: 1.0\r\n"; - message += "Content-Type: text/plain; charset=UTF-8\r\n"; - message += "Content-Transfer-Encoding: base64\r\n"; - message += "\r\n"; - // Encode the body in base64 - QString crlf_body = body; - QByteArray b = crlf_body.replace("\n","\r\n").toUtf8().toBase64(); - int ct = b.length(); - for (int i = 0; i < ct; i += 78) - { - message += b.mid(i, 78); - } - this->from = from; - rcpt = to; - // Authentication - if (pref->getMailNotificationSMTPAuth()) { - username = pref->getMailNotificationSMTPUsername(); - password = pref->getMailNotificationSMTPPassword(); - } +void Smtp::sendMail(const QString &from, const QString &to, const QString &subject, const QString &body) +{ + const Preferences* const pref = Preferences::instance(); + QTextCodec* latin1 = QTextCodec::codecForName("latin1"); + m_message = ""; + m_message += encodeMimeHeader("Date", QDateTime::currentDateTime().toUTC().toString("ddd, d MMM yyyy hh:mm:ss UT"), latin1); + m_message += encodeMimeHeader("From", from, latin1); + m_message += encodeMimeHeader("Subject", subject, latin1); + m_message += encodeMimeHeader("To", to, latin1); + m_message += "MIME-Version: 1.0\r\n"; + m_message += "Content-Type: text/plain; charset=UTF-8\r\n"; + m_message += "Content-Transfer-Encoding: base64\r\n"; + m_message += "\r\n"; + // Encode the body in base64 + QString crlf_body = body; + QByteArray b = crlf_body.replace("\n","\r\n").toUtf8().toBase64(); + int ct = b.length(); + for (int i = 0; i < ct; i += 78) + m_message += b.mid(i, 78); + m_from = from; + m_rcpt = to; + // Authentication + if (pref->getMailNotificationSMTPAuth()) { + m_username = pref->getMailNotificationSMTPUsername(); + m_password = pref->getMailNotificationSMTPPassword(); + } - // Connect to SMTP server + // Connect to SMTP server #ifndef QT_NO_OPENSSL - if (pref->getMailNotificationSMTPSSL()) { - socket->connectToHostEncrypted(pref->getMailNotificationSMTP(), DEFAULT_PORT_SSL); - use_ssl = true; - } else { + if (pref->getMailNotificationSMTPSSL()) { + m_socket->connectToHostEncrypted(pref->getMailNotificationSMTP(), DEFAULT_PORT_SSL); + m_useSsl = true; + } + else { #endif - socket->connectToHost(pref->getMailNotificationSMTP(), DEFAULT_PORT); - use_ssl = false; + m_socket->connectToHost(pref->getMailNotificationSMTP(), DEFAULT_PORT); + m_useSsl = false; #ifndef QT_NO_OPENSSL - } + } #endif } void Smtp::readyRead() { - qDebug() << Q_FUNC_INFO; - // SMTP is line-oriented - buffer += socket->readAll(); - while (true) - { - int pos = buffer.indexOf("\r\n"); - if (pos < 0) return; // Loop exit condition - QByteArray line = buffer.left(pos); - buffer = buffer.mid(pos + 2); - qDebug() << "Response line:" << line; - // Extract reponse code - QByteArray code = line.left(3); + qDebug() << Q_FUNC_INFO; + // SMTP is line-oriented + m_buffer += m_socket->readAll(); + while (true) { + int pos = m_buffer.indexOf("\r\n"); + if (pos < 0) return; // Loop exit condition + QByteArray line = m_buffer.left(pos); + m_buffer = m_buffer.mid(pos + 2); + qDebug() << "Response line:" << line; + // Extract reponse code + QByteArray code = line.left(3); - switch(state) { - case Init: { - if (code[0] == '2') { - // The server may send a multiline greeting/INIT/220 response. - // We wait until it finishes. - if (line[3] != ' ') - break; - // Connection was successful - ehlo(); - } else { - logError("Connection failed, unrecognized reply: "+line); - state = Close; - } - break; - } - case EhloSent: - case HeloSent: - case EhloGreetReceived: - parseEhloResponse(code, line[3] != ' ', line.mid(4)); - break; + switch (m_state) { + case Init: { + if (code[0] == '2') { + // The server may send a multiline greeting/INIT/220 response. + // We wait until it finishes. + if (line[3] != ' ') + break; + // Connection was successful + ehlo(); + } + else { + logError("Connection failed, unrecognized reply: "+line); + m_state = Close; + } + break; + } + case EhloSent: + case HeloSent: + case EhloGreetReceived: + parseEhloResponse(code, line[3] != ' ', line.mid(4)); + break; #ifndef QT_NO_OPENSSL - case StartTLSSent: - if (code == "220") { - socket->startClientEncryption(); - ehlo(); - } else { - authenticate(); - } - break; + case StartTLSSent: + if (code == "220") { + m_socket->startClientEncryption(); + ehlo(); + } + else { + authenticate(); + } + break; #endif - case AuthRequestSent: - case AuthUsernameSent: - if (authType == AuthPlain) authPlain(); - else if (authType == AuthLogin) authLogin(); - else authCramMD5(line.mid(4)); - break; - case AuthSent: - case Authenticated: - if (code[0] == '2') { - qDebug() << "Sending ..."; - socket->write("mail from:<" + from.toLatin1() + ">\r\n"); - socket->flush(); - state = Rcpt; - } else { - // Authentication failed! - logError("Authentication failed, msg: "+line); - state = Close; - } - break; - case Rcpt: - if (code[0] == '2') { - socket->write("rcpt to:<" + rcpt.toLatin1() + ">\r\n"); - socket->flush(); - state = Data; - } else { - logError(" was rejected by server, msg: "+line); - state = Close; - } - break; - case Data: - if (code[0] == '2') { - socket->write("data\r\n"); - socket->flush(); - state = Body; - } else { - logError(" was rejected by server, msg: "+line); - state = Close; - } - break; - case Body: - if (code[0] == '3') { - socket->write(message + "\r\n.\r\n"); - socket->flush(); - state = Quit; - } else { - logError(" was rejected by server, msg: "+line); - state = Close; - } - break; - case Quit: - if (code[0] == '2') { - socket->write("QUIT\r\n"); - socket->flush(); - // here, we just close. - state = Close; - } else { - logError("Message was rejected by the server, error: "+line); - state = Close; - } - break; - default: - qDebug() << "Disconnecting from host"; - socket->disconnectFromHost(); - return; + case AuthRequestSent: + case AuthUsernameSent: + if (m_authType == AuthPlain) authPlain(); + else if (m_authType == AuthLogin) authLogin(); + else authCramMD5(line.mid(4)); + break; + case AuthSent: + case Authenticated: + if (code[0] == '2') { + qDebug() << "Sending ..."; + m_socket->write("mail from:<" + m_from.toLatin1() + ">\r\n"); + m_socket->flush(); + m_state = Rcpt; + } + else { + // Authentication failed! + logError("Authentication failed, msg: "+line); + m_state = Close; + } + break; + case Rcpt: + if (code[0] == '2') { + m_socket->write("rcpt to:<" + m_rcpt.toLatin1() + ">\r\n"); + m_socket->flush(); + m_state = Data; + } + else { + logError(" was rejected by server, msg: "+line); + m_state = Close; + } + break; + case Data: + if (code[0] == '2') { + m_socket->write("data\r\n"); + m_socket->flush(); + m_state = Body; + } + else { + logError(" was rejected by server, msg: "+line); + m_state = Close; + } + break; + case Body: + if (code[0] == '3') { + m_socket->write(m_message + "\r\n.\r\n"); + m_socket->flush(); + m_state = Quit; + } + else { + logError(" was rejected by server, msg: "+line); + m_state = Close; + } + break; + case Quit: + if (code[0] == '2') { + m_socket->write("QUIT\r\n"); + m_socket->flush(); + // here, we just close. + m_state = Close; + } + else { + logError("Message was rejected by the server, error: "+line); + m_state = Close; + } + break; + default: + qDebug() << "Disconnecting from host"; + m_socket->disconnectFromHost(); + return; + } } - } } -QByteArray Smtp::encode_mime_header(const QString& key, const QString& value, QTextCodec* latin1, const QByteArray& prefix) +QByteArray Smtp::encodeMimeHeader(const QString &key, const QString &value, QTextCodec *latin1, const QByteArray &prefix) { - QByteArray rv = ""; - QByteArray line = key.toLatin1() + ": "; - if (!prefix.isEmpty()) line += prefix; - if (!value.contains("=?") && latin1->canEncode(value)) { - bool firstWord = true; - foreach (const QByteArray& word, value.toLatin1().split(' ')) { - if (line.size() > 78) { - rv = rv + line + "\r\n"; - line.clear(); - } - if (firstWord) - line += word; - else - line += " " + word; - firstWord = false; + QByteArray rv = ""; + QByteArray line = key.toLatin1() + ": "; + if (!prefix.isEmpty()) line += prefix; + if (!value.contains("=?") && latin1->canEncode(value)) { + bool firstWord = true; + foreach (const QByteArray& word, value.toLatin1().split(' ')) { + if (line.size() > 78) { + rv = rv + line + "\r\n"; + line.clear(); + } + if (firstWord) + line += word; + else + line += " " + word; + firstWord = false; + } } - } else { - // The text cannot be losslessly encoded as Latin-1. Therefore, we - // must use base64 encoding. - QByteArray utf8 = value.toUtf8(); - // Use base64 encoding - QByteArray base64 = utf8.toBase64(); - int ct = base64.length(); - line += "=?utf-8?b?"; - for (int i = 0; i < ct; i += 4) { - /*if (line.length() > 72) { + else { + // The text cannot be losslessly encoded as Latin-1. Therefore, we + // must use base64 encoding. + QByteArray utf8 = value.toUtf8(); + // Use base64 encoding + QByteArray base64 = utf8.toBase64(); + int ct = base64.length(); + line += "=?utf-8?b?"; + for (int i = 0; i < ct; i += 4) { + /*if (line.length() > 72) { rv += line + "?\n\r"; line = " =?utf-8?b?"; }*/ - line = line + base64.mid(i, 4); + line = line + base64.mid(i, 4); + } + line += "?="; // end encoded-word atom } - line += "?="; // end encoded-word atom - } - return rv + line + "\r\n"; + return rv + line + "\r\n"; } void Smtp::ehlo() { - QByteArray address = determineFQDN(); - socket->write("ehlo " + address + "\r\n"); - socket->flush(); - state = EhloSent; + QByteArray address = determineFQDN(); + m_socket->write("ehlo " + address + "\r\n"); + m_socket->flush(); + m_state = EhloSent; } void Smtp::helo() { - QByteArray address = determineFQDN(); - socket->write("helo " + address + "\r\n"); - socket->flush(); - state = HeloSent; + QByteArray address = determineFQDN(); + m_socket->write("helo " + address + "\r\n"); + m_socket->flush(); + m_state = HeloSent; } -void Smtp::parseEhloResponse(const QByteArray& code, bool continued, const QString& line) +void Smtp::parseEhloResponse(const QByteArray &code, bool continued, const QString &line) { - if (code != "250") { - // Error - if (state == EhloSent) { - // try to send HELO instead of EHLO - qDebug() << "EHLO failed, trying HELO instead..."; - helo(); - } else { - // Both EHLO and HELO failed, chances are this is NOT - // a SMTP server - logError("Both EHLO and HELO failed, msg: "+line); - state = Close; + if (code != "250") { + // Error + if (m_state == EhloSent) { + // try to send HELO instead of EHLO + qDebug() << "EHLO failed, trying HELO instead..."; + helo(); + } + else { + // Both EHLO and HELO failed, chances are this is NOT + // a SMTP server + logError("Both EHLO and HELO failed, msg: "+line); + m_state = Close; + } + return; } - return; - } - if (state != EhloGreetReceived) { - if (!continued) { - // greeting only, no extensions - qDebug() << "No extension"; - state = EhloDone; - } else { - // greeting followed by extensions - state = EhloGreetReceived; - qDebug () << "EHLO greet received"; - return; + + if (m_state != EhloGreetReceived) { + if (!continued) { + // greeting only, no extensions + qDebug() << "No extension"; + m_state = EhloDone; + } + else { + // greeting followed by extensions + m_state = EhloGreetReceived; + qDebug () << "EHLO greet received"; + return; + } + } + else { + qDebug() << Q_FUNC_INFO << "Supported extension: " << line.section(' ', 0, 0).toUpper() + << line.section(' ', 1); + m_extensions[line.section(' ', 0, 0).toUpper()] = line.section(' ', 1); + if (!continued) + m_state = EhloDone; + } + + if (m_state != EhloDone) return; + + if (m_extensions.contains("STARTTLS") && m_useSsl) { + qDebug() << "STARTTLS"; + startTLS(); + } + else { + authenticate(); } - } else { - qDebug() << Q_FUNC_INFO << "Supported extension: " << line.section(' ', 0, 0).toUpper() - << line.section(' ', 1); - extensions[line.section(' ', 0, 0).toUpper()] = line.section(' ', 1); - if (!continued) - state = EhloDone; - } - if (state != EhloDone) return; - if (extensions.contains("STARTTLS") && use_ssl) { - qDebug() << "STARTTLS"; - startTLS(); - } else { - authenticate(); - } } void Smtp::authenticate() { - qDebug() << Q_FUNC_INFO; - if (!extensions.contains("AUTH") || - username.isEmpty() || password.isEmpty()) { - // Skip authentication - qDebug() << "Skipping authentication..."; - state = Authenticated; - // At this point the server will not send any response - // So fill the buffer with a fake one to pass the tests - // in readyRead() - buffer.push_front("250 QBT FAKE RESPONSE\r\n"); - return; - } - // AUTH extension is supported, check which - // authentication modes are supported by - // the server - QStringList auth = extensions["AUTH"].toUpper().split(' ', QString::SkipEmptyParts); - if (auth.contains("CRAM-MD5")) { - qDebug() << "Using CRAM-MD5 authentication..."; - authCramMD5(); - } - else if (auth.contains("PLAIN")) { - qDebug() << "Using PLAIN authentication..."; - authPlain(); - } - else if (auth.contains("LOGIN")) { - qDebug() << "Using LOGIN authentication..."; - authLogin(); - } else { - // Skip authentication - logError("The SMTP server does not seem to support any of the authentications modes " - "we support [CRAM-MD5|PLAIN|LOGIN], skipping authentication, " - "knowing it is likely to fail... Server Auth Modes: "+auth.join("|")); - state = Authenticated; - // At this point the server will not send any response - // So fill the buffer with a fake one to pass the tests - // in readyRead() - buffer.push_front("250 QBT FAKE RESPONSE\r\n"); - } + qDebug() << Q_FUNC_INFO; + if (!m_extensions.contains("AUTH") || + m_username.isEmpty() || m_password.isEmpty()) { + // Skip authentication + qDebug() << "Skipping authentication..."; + m_state = Authenticated; + // At this point the server will not send any response + // So fill the buffer with a fake one to pass the tests + // in readyRead() + m_buffer.push_front("250 QBT FAKE RESPONSE\r\n"); + return; + } + // AUTH extension is supported, check which + // authentication modes are supported by + // the server + QStringList auth = m_extensions["AUTH"].toUpper().split(' ', QString::SkipEmptyParts); + if (auth.contains("CRAM-MD5")) { + qDebug() << "Using CRAM-MD5 authentication..."; + authCramMD5(); + } + else if (auth.contains("PLAIN")) { + qDebug() << "Using PLAIN authentication..."; + authPlain(); + } + else if (auth.contains("LOGIN")) { + qDebug() << "Using LOGIN authentication..."; + authLogin(); + } + else { + // Skip authentication + logError("The SMTP server does not seem to support any of the authentications modes " + "we support [CRAM-MD5|PLAIN|LOGIN], skipping authentication, " + "knowing it is likely to fail... Server Auth Modes: "+auth.join("|")); + m_state = Authenticated; + // At this point the server will not send any response + // So fill the buffer with a fake one to pass the tests + // in readyRead() + m_buffer.push_front("250 QBT FAKE RESPONSE\r\n"); + } } void Smtp::startTLS() { - qDebug() << Q_FUNC_INFO; + qDebug() << Q_FUNC_INFO; #ifndef QT_NO_OPENSSL - socket->write("starttls\r\n"); - socket->flush(); - state = StartTLSSent; + m_socket->write("starttls\r\n"); + m_socket->flush(); + m_state = StartTLSSent; #else - authenticate(); + authenticate(); #endif } void Smtp::authCramMD5(const QByteArray& challenge) { - if (state != AuthRequestSent) { - socket->write("auth cram-md5\r\n"); - socket->flush(); - authType = AuthCramMD5; - state = AuthRequestSent; - } else { - QByteArray response = username.toLatin1() + ' ' - + hmacMD5(password.toLatin1(), QByteArray::fromBase64(challenge)).toHex(); - socket->write(response.toBase64() + "\r\n"); - socket->flush(); - state = AuthSent; - } + if (m_state != AuthRequestSent) { + m_socket->write("auth cram-md5\r\n"); + m_socket->flush(); + m_authType = AuthCramMD5; + m_state = AuthRequestSent; + } + else { + QByteArray response = m_username.toLatin1() + ' ' + + hmacMD5(m_password.toLatin1(), QByteArray::fromBase64(challenge)).toHex(); + m_socket->write(response.toBase64() + "\r\n"); + m_socket->flush(); + m_state = AuthSent; + } } void Smtp::authPlain() { - if (state != AuthRequestSent) { - authType = AuthPlain; - // Prepare Auth string - QByteArray auth; - auth += '\0'; - auth += username.toLatin1(); - qDebug() << "username: " << username.toLatin1(); - auth += '\0'; - auth += password.toLatin1(); - qDebug() << "password: " << password.toLatin1(); - // Send it - socket->write("auth plain "+ auth.toBase64() + "\r\n"); - socket->flush(); - state = AuthSent; - } + if (m_state != AuthRequestSent) { + m_authType = AuthPlain; + // Prepare Auth string + QByteArray auth; + auth += '\0'; + auth += m_username.toLatin1(); + qDebug() << "username: " << m_username.toLatin1(); + auth += '\0'; + auth += m_password.toLatin1(); + qDebug() << "password: " << m_password.toLatin1(); + // Send it + m_socket->write("auth plain "+ auth.toBase64() + "\r\n"); + m_socket->flush(); + m_state = AuthSent; + } } void Smtp::authLogin() { - if (state != AuthRequestSent && state != AuthUsernameSent) { - socket->write("auth login\r\n"); - socket->flush(); - authType = AuthLogin; - state = AuthRequestSent; - } - else if (state == AuthRequestSent) { - socket->write(username.toLatin1().toBase64() + "\r\n"); - socket->flush(); - state = AuthUsernameSent; - } - else { - socket->write(password.toLatin1().toBase64() + "\r\n"); - socket->flush(); - state = AuthSent; - } + if ((m_state != AuthRequestSent) && (m_state != AuthUsernameSent)) { + m_socket->write("auth login\r\n"); + m_socket->flush(); + m_authType = AuthLogin; + m_state = AuthRequestSent; + } + else if (m_state == AuthRequestSent) { + m_socket->write(m_username.toLatin1().toBase64() + "\r\n"); + m_socket->flush(); + m_state = AuthUsernameSent; + } + else { + m_socket->write(m_password.toLatin1().toBase64() + "\r\n"); + m_socket->flush(); + m_state = AuthSent; + } } void Smtp::logError(const QString &msg) { - qDebug() << "Email Notification Error:" << msg; - Logger::instance()->addMessage(tr("Email Notification Error:") + " " + msg, Log::CRITICAL); + qDebug() << "Email Notification Error:" << msg; + Logger::instance()->addMessage(tr("Email Notification Error:") + " " + msg, Log::CRITICAL); } diff --git a/src/core/net/smtp.h b/src/core/net/smtp.h index 9b332416d..4ca441e59 100644 --- a/src/core/net/smtp.h +++ b/src/core/net/smtp.h @@ -52,54 +52,74 @@ QT_END_NAMESPACE namespace Net { + class Smtp : public QObject + { + Q_OBJECT -class Smtp : public QObject { - Q_OBJECT + public: + Smtp(QObject *parent = 0); + ~Smtp(); -public: - Smtp(QObject *parent = 0); - ~Smtp(); - void sendMail(const QString &from, const QString &to, const QString &subject, const QString &body); + void sendMail(const QString &m_from, const QString &to, const QString &subject, const QString &body); -private slots: - void readyRead(); + private slots: + void readyRead(); -private: - QByteArray encode_mime_header(const QString& key, const QString& value, QTextCodec* latin1, const QByteArray& prefix=QByteArray()); - void ehlo(); - void helo(); - void parseEhloResponse(const QByteArray& code, bool continued, const QString& line); - void authenticate(); - void startTLS(); - void authCramMD5(const QByteArray& challenge = QByteArray()); - void authPlain(); - void authLogin(); - void logError(const QString &msg); + private: + enum States + { + Rcpt, + EhloSent, + HeloSent, + EhloDone, + EhloGreetReceived, + AuthRequestSent, + AuthSent, + AuthUsernameSent, + Authenticated, + StartTLSSent, + Data, + Init, + Body, + Quit, + Close + }; -private: - enum states { Rcpt, EhloSent, HeloSent, EhloDone, EhloGreetReceived, AuthRequestSent, AuthSent, - AuthUsernameSent, Authenticated, StartTLSSent, Data, Init, Body, Quit, Close }; - enum AuthType { AuthPlain, AuthLogin, AuthCramMD5 }; + enum AuthType + { + AuthPlain, + AuthLogin, + AuthCramMD5 + }; -private: - QByteArray message; + QByteArray encodeMimeHeader(const QString &key, const QString &value, QTextCodec *latin1, const QByteArray &prefix = QByteArray()); + void ehlo(); + void helo(); + void parseEhloResponse(const QByteArray &code, bool continued, const QString &line); + void authenticate(); + void startTLS(); + void authCramMD5(const QByteArray &challenge = QByteArray()); + void authPlain(); + void authLogin(); + void logError(const QString &msg); + + QByteArray m_message; #ifndef QT_NO_OPENSSL - QSslSocket *socket; + QSslSocket *m_socket; #else - QTcpSocket *socket; + QTcpSocket *m_socket; #endif - QString from; - QString rcpt; - QString response; - int state; - QHash extensions; - QByteArray buffer; - bool use_ssl; - AuthType authType; - QString username; - QString password; -}; - + QString m_from; + QString m_rcpt; + QString m_response; + int m_state; + QHash m_extensions; + QByteArray m_buffer; + bool m_useSsl; + AuthType m_authType; + QString m_username; + QString m_password; + }; } #endif From 7d73bddfd20b344cffc3cd4cbb08426202c289bb Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Tue, 2 Jun 2015 12:09:15 +0300 Subject: [PATCH 12/12] Split some "header only" sources. --- src/gui/addnewtorrentdialog.cpp | 34 ++-- src/gui/properties/peeraddition.cpp | 81 +++++++++ src/gui/properties/peeraddition.h | 61 ++----- src/gui/properties/peerlistwidget.cpp | 29 ++-- src/gui/properties/properties.pri | 3 + src/gui/properties/propertieswidget.cpp | 11 +- src/gui/properties/proplistdelegate.cpp | 191 +++++++++++++++++++++ src/gui/properties/proplistdelegate.h | 178 +++---------------- src/gui/properties/trackerlist.cpp | 10 +- src/gui/properties/trackersadditiondlg.cpp | 130 ++++++++++++++ src/gui/properties/trackersadditiondlg.h | 115 ++----------- 11 files changed, 508 insertions(+), 335 deletions(-) create mode 100644 src/gui/properties/peeraddition.cpp create mode 100644 src/gui/properties/proplistdelegate.cpp create mode 100644 src/gui/properties/trackersadditiondlg.cpp diff --git a/src/gui/addnewtorrentdialog.cpp b/src/gui/addnewtorrentdialog.cpp index 2311584a8..1afafa26a 100644 --- a/src/gui/addnewtorrentdialog.cpp +++ b/src/gui/addnewtorrentdialog.cpp @@ -28,22 +28,6 @@ * Contact : chris@qbittorrent.org */ -#include "addnewtorrentdialog.h" -#include "ui_addnewtorrentdialog.h" -#include "proplistdelegate.h" -#include "torrentcontentmodel.h" -#include "torrentcontentfiltermodel.h" -#include "core/preferences.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/utils/fs.h" -#include "autoexpandabledialog.h" -#include "messageboxraised.h" - #include #include #include @@ -51,6 +35,24 @@ #include #include +#include "core/preferences.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 "core/bittorrent/torrenthandle.h" +#include "core/utils/fs.h" +#include "core/utils/misc.h" +#include "guiiconprovider.h" +#include "autoexpandabledialog.h" +#include "messageboxraised.h" +#include "ui_addnewtorrentdialog.h" +#include "proplistdelegate.h" +#include "torrentcontentmodel.h" +#include "torrentcontentfiltermodel.h" +#include "addnewtorrentdialog.h" + AddNewTorrentDialog::AddNewTorrentDialog(QWidget *parent) : QDialog(parent) , ui(new Ui::AddNewTorrentDialog) diff --git a/src/gui/properties/peeraddition.cpp b/src/gui/properties/peeraddition.cpp new file mode 100644 index 000000000..491c7418b --- /dev/null +++ b/src/gui/properties/peeraddition.cpp @@ -0,0 +1,81 @@ +/* + * Bittorrent Client using Qt 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 "core/bittorrent/peerinfo.h" +#include "peeraddition.h" + +PeerAdditionDlg::PeerAdditionDlg(QWidget *parent) + : QDialog(parent) +{ + setupUi(this); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + connect(buttonBox, SIGNAL(accepted()), this, SLOT(validateInput())); +} + +QHostAddress PeerAdditionDlg::getAddress() const +{ + return QHostAddress(lineIP->text()); +} + + +ushort PeerAdditionDlg::getPort() const +{ + return spinPort->value(); +} + + +BitTorrent::PeerAddress PeerAdditionDlg::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; +} + + +void PeerAdditionDlg::validateInput() +{ + if (getAddress().isNull()) + QMessageBox::warning(this, tr("Invalid IP"), tr("The IP you provided is invalid."), QMessageBox::Ok); + else + accept(); +} diff --git a/src/gui/properties/peeraddition.h b/src/gui/properties/peeraddition.h index c415268c5..fd5ae4e05 100644 --- a/src/gui/properties/peeraddition.h +++ b/src/gui/properties/peeraddition.h @@ -32,64 +32,29 @@ #define PEERADDITION_H #include -#include -#include - -#include "core/bittorrent/peerinfo.h" #include "ui_peer.h" -class PeerAdditionDlg: public QDialog, private Ui::addPeerDialog +class QHostAddress; + +namespace BitTorrent +{ + struct PeerAddress; +} + +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(QWidget *parent = 0); - ~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; - } + QHostAddress getAddress() const; + ushort getPort() const; + static BitTorrent::PeerAddress askForPeerAddress(); protected slots: - void validateInput() { - if (getAddress().isNull()) - QMessageBox::warning(this, tr("Invalid IP"), tr("The IP you provided is invalid."), QMessageBox::Ok); - else - accept(); - } + void validateInput(); }; #endif // PEERADDITION_H diff --git a/src/gui/properties/peerlistwidget.cpp b/src/gui/properties/peerlistwidget.cpp index e45102634..00af5f701 100644 --- a/src/gui/properties/peerlistwidget.cpp +++ b/src/gui/properties/peerlistwidget.cpp @@ -28,26 +28,27 @@ * Contact : chris@qbittorrent.org */ -#include "peerlistwidget.h" -#include "peerlistdelegate.h" -#include "peerlistsortmodel.h" -#include "core/net/reverseresolution.h" -#include "core/preferences.h" -#include "propertieswidget.h" -#include "geoipmanager.h" -#include "peeraddition.h" -#include "speedlimitdlg.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 "core/net/reverseresolution.h" +#include "core/bittorrent/torrenthandle.h" +#include "core/bittorrent/peerinfo.h" +#include "core/preferences.h" +#include "core/logger.h" +#include "propertieswidget.h" +#include "geoipmanager.h" +#include "peeraddition.h" +#include "speedlimitdlg.h" +#include "guiiconprovider.h" +#include "peerlistdelegate.h" +#include "peerlistsortmodel.h" +#include "peerlistwidget.h" PeerListWidget::PeerListWidget(PropertiesWidget *parent): QTreeView(parent), m_properties(parent), m_displayFlags(false) diff --git a/src/gui/properties/properties.pri b/src/gui/properties/properties.pri index 3072ee0d0..347d70242 100644 --- a/src/gui/properties/properties.pri +++ b/src/gui/properties/properties.pri @@ -17,8 +17,11 @@ HEADERS += $$PWD/propertieswidget.h \ $$PWD/proptabbar.h SOURCES += $$PWD/propertieswidget.cpp \ + $$PWD/proplistdelegate.cpp \ $$PWD/peerlistwidget.cpp \ $$PWD/trackerlist.cpp \ $$PWD/proptabbar.cpp \ $$PWD/downloadedpiecesbar.cpp \ + $$PWD/peeraddition.cpp \ + $$PWD/trackersadditiondlg.cpp \ $$PWD/pieceavailabilitybar.cpp diff --git a/src/gui/properties/propertieswidget.cpp b/src/gui/properties/propertieswidget.cpp index 71fb90a07..e7223d353 100644 --- a/src/gui/properties/propertieswidget.cpp +++ b/src/gui/properties/propertieswidget.cpp @@ -42,9 +42,11 @@ #include #include -#include "propertieswidget.h" -#include "transferlistwidget.h" #include "core/bittorrent/session.h" +#include "core/preferences.h" +#include "core/utils/fs.h" +#include "core/utils/misc.h" +#include "core/utils/string.h" #include "proplistdelegate.h" #include "torrentcontentfiltermodel.h" #include "torrentcontentmodel.h" @@ -53,13 +55,12 @@ #include "mainwindow.h" #include "downloadedpiecesbar.h" #include "pieceavailabilitybar.h" -#include "core/preferences.h" #include "proptabbar.h" #include "guiiconprovider.h" #include "lineedit.h" -#include "core/utils/fs.h" -#include "core/utils/string.h" +#include "transferlistwidget.h" #include "autoexpandabledialog.h" +#include "propertieswidget.h" PropertiesWidget::PropertiesWidget(QWidget *parent, MainWindow* main_window, TransferListWidget *transferList): QWidget(parent), transferList(transferList), main_window(main_window), m_torrent(0) { diff --git a/src/gui/properties/proplistdelegate.cpp b/src/gui/properties/proplistdelegate.cpp new file mode 100644 index 000000000..26f16c3c3 --- /dev/null +++ b/src/gui/properties/proplistdelegate.cpp @@ -0,0 +1,191 @@ +/* + * Bittorrent Client using Qt 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 + +#ifdef Q_OS_WIN +#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) +#include +#else +#include +#endif +#endif + +#include "core/utils/misc.h" +#include "core/utils/string.h" +#include "propertieswidget.h" +#include "proplistdelegate.h" + +PropListDelegate::PropListDelegate(PropertiesWidget *properties, QObject *parent) + : QItemDelegate(parent) + , m_properties(properties) +{ +} + +void PropListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + painter->save(); + QStyleOptionViewItemV2 opt = QItemDelegate::setOptions(index, option); + + switch(index.column()) { + case PCSIZE: + QItemDelegate::drawBackground(painter, opt, index); + QItemDelegate::drawDisplay(painter, opt, option.rect, Utils::Misc::friendlyUnit(index.data().toLongLong())); + break; + case PROGRESS: + if (index.data().toDouble() >= 0) { + QStyleOptionProgressBarV2 newopt; + qreal progress = index.data().toDouble() * 100.; + newopt.rect = opt.rect; + newopt.text = ((progress == 100.0) ? QString("100%") : Utils::String::fromDouble(progress, 1) + "%"); + newopt.progress = (int)progress; + newopt.maximum = 100; + newopt.minimum = 0; + newopt.state |= QStyle::State_Enabled; + newopt.textVisible = true; +#ifndef Q_OS_WIN + QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt, painter); +#else + // XXX: To avoid having the progress text on the right of the bar +#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) + QPlastiqueStyle st; +#else + QProxyStyle st("fusion"); +#endif + st.drawControl(QStyle::CE_ProgressBar, &newopt, painter, 0); +#endif + } + else { + // Do not display anything if the file is disabled (progress == 0) + QItemDelegate::drawBackground(painter, opt, index); + } + break; + case PRIORITY: { + QItemDelegate::drawBackground(painter, opt, index); + QString text = ""; + switch (index.data().toInt()) { + case -1: + text = tr("Mixed", "Mixed (priorities"); + break; + case 0: + text = tr("Not downloaded"); + break; + case 2: + text = tr("High", "High (priority)"); + break; + case 7: + text = tr("Maximum", "Maximum (priority)"); + break; + default: + text = tr("Normal", "Normal (priority)"); + break; + } + QItemDelegate::drawDisplay(painter, opt, option.rect, text); + } + break; + default: + QItemDelegate::paint(painter, option, index); + break; + } + painter->restore(); +} + +void PropListDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const +{ + QComboBox *combobox = static_cast(editor); + // Set combobox index + switch(index.data().toInt()) { + case 2: + combobox->setCurrentIndex(1); + break; + case 7: + combobox->setCurrentIndex(2); + break; + default: + combobox->setCurrentIndex(0); + break; + } +} + +QWidget *PropListDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &index) const +{ + if (index.column() != PRIORITY) return 0; + + if (m_properties) { + BitTorrent::TorrentHandle *const torrent = m_properties->getCurrentTorrent(); + if (!torrent || !torrent->hasMetadata() || torrent->isSeed()) + return 0; + } + + if (index.data().toInt() <= 0) { + // IGNORED or MIXED + return 0; + } + + QComboBox* editor = new QComboBox(parent); + editor->setFocusPolicy(Qt::StrongFocus); + editor->addItem(tr("Normal", "Normal (priority)")); + editor->addItem(tr("High", "High (priority)")); + editor->addItem(tr("Maximum", "Maximum (priority)")); + return editor; +} + +void PropListDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const +{ + QComboBox *combobox = static_cast(editor); + int value = combobox->currentIndex(); + qDebug("PropListDelegate: setModelData(%d)", value); + + switch(value) { + case 1: + model->setData(index, 2); // HIGH + break; + case 2: + model->setData(index, 7); // MAX + break; + default: + model->setData(index, 1); // NORMAL + } + + emit filteredFilesChanged(); +} + +void PropListDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &) const +{ + qDebug("UpdateEditor Geometry called"); + editor->setGeometry(option.rect); +} diff --git a/src/gui/properties/proplistdelegate.h b/src/gui/properties/proplistdelegate.h index d46c2cccf..10cab6f71 100644 --- a/src/gui/properties/proplistdelegate.h +++ b/src/gui/properties/proplistdelegate.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,168 +32,42 @@ #define PROPLISTDELEGATE_H #include -#include -#include -#include -#include -#include -#include -#include -#include -#include "core/utils/misc.h" -#include "core/utils/string.h" -#include "propertieswidget.h" -#ifdef Q_OS_WIN -#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) -#include -#else -#include -#endif -#endif +class QPainter; +class QModelIndex; +class QStyleOptionViewItem; +class QAbstractItemModel; +class PropertiesWidget; // Defines for properties list columns -enum PropColumn {NAME, PCSIZE, PROGRESS, PRIORITY}; +enum PropColumn +{ + NAME, + PCSIZE, + PROGRESS, + PRIORITY +}; -class PropListDelegate: public QItemDelegate { - Q_OBJECT - -private: - PropertiesWidget *properties; - -signals: - void filteredFilesChanged() const; +class PropListDelegate : public QItemDelegate +{ + Q_OBJECT public: - PropListDelegate(PropertiesWidget* properties=0, QObject *parent=0) : QItemDelegate(parent), properties(properties) { - } + PropListDelegate(PropertiesWidget *properties = 0, QObject *parent = 0); - ~PropListDelegate() {} - - void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { - painter->save(); - QStyleOptionViewItemV2 opt = QItemDelegate::setOptions(index, option); - switch(index.column()) { - case PCSIZE: - QItemDelegate::drawBackground(painter, opt, index); - QItemDelegate::drawDisplay(painter, opt, option.rect, Utils::Misc::friendlyUnit(index.data().toLongLong())); - break; - case PROGRESS:{ - if (index.data().toDouble() >= 0) { - QStyleOptionProgressBarV2 newopt; - qreal progress = index.data().toDouble()*100.; - newopt.rect = opt.rect; - newopt.text = ((progress == 100.0) ? QString("100%") : Utils::String::fromDouble(progress, 1) + "%"); - newopt.progress = (int)progress; - newopt.maximum = 100; - newopt.minimum = 0; - newopt.state |= QStyle::State_Enabled; - newopt.textVisible = true; -#ifndef Q_OS_WIN - QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt, painter); -#else - // XXX: To avoid having the progress text on the right of the bar -#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) - QPlastiqueStyle st; -#else - QProxyStyle st("fusion"); -#endif - st.drawControl(QStyle::CE_ProgressBar, &newopt, painter, 0); -#endif - } else { - // Do not display anything if the file is disabled (progress == -1) - QItemDelegate::drawBackground(painter, opt, index); - } - break; - } - case PRIORITY: { - QItemDelegate::drawBackground(painter, opt, index); - QString text = ""; - switch(index.data().toInt()) { - case -1: - text = tr("Mixed", "Mixed (priorities"); - break; - case 0: - text = tr("Not downloaded"); - break; - case 2: - text = tr("High", "High (priority)"); - break; - case 7: - text = tr("Maximum", "Maximum (priority)"); - break; - default: - text = tr("Normal", "Normal (priority)"); - break; - } - QItemDelegate::drawDisplay(painter, opt, option.rect, text); - break; - } - default: - QItemDelegate::paint(painter, option, index); - break; - } - painter->restore(); - } - - void setEditorData(QWidget *editor, const QModelIndex &index) const { - QComboBox *combobox = static_cast(editor); - // Set combobox index - switch(index.data().toInt()) { - case 2: - combobox->setCurrentIndex(1); - break; - case 7: - combobox->setCurrentIndex(2); - break; - default: - combobox->setCurrentIndex(0); - break; - } - } - - QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &index) const { - if (index.column() != PRIORITY) return 0; - if (properties) { - BitTorrent::TorrentHandle *const torrent = properties->getCurrentTorrent(); - if (!torrent || !torrent->hasMetadata() || torrent->isSeed()) - return 0; - } - if (index.data().toInt() <= 0) { - // IGNORED or MIXED - return 0; - } - QComboBox* editor = new QComboBox(parent); - editor->setFocusPolicy(Qt::StrongFocus); - editor->addItem(tr("Normal", "Normal (priority)")); - editor->addItem(tr("High", "High (priority)")); - editor->addItem(tr("Maximum", "Maximum (priority)")); - return editor; - } + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + void setEditorData(QWidget *editor, const QModelIndex &index) const; + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &index) const; public slots: - void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { - QComboBox *combobox = static_cast(editor); - int value = combobox->currentIndex(); - qDebug("PropListDelegate: setModelData(%d)", value); - switch(value) { - case 1: - model->setData(index, 2); // HIGH - break; - case 2: - model->setData(index, 7); // MAX - break; - default: - model->setData(index, 1); // NORMAL - } - emit filteredFilesChanged(); - } + void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; + void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const; - void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { - qDebug("UpdateEditor Geometry called"); - editor->setGeometry(option.rect); - } +signals: + void filteredFilesChanged() const; +private: + PropertiesWidget *m_properties; }; #endif diff --git a/src/gui/properties/trackerlist.cpp b/src/gui/properties/trackerlist.cpp index 2e83754ee..479e21996 100644 --- a/src/gui/properties/trackerlist.cpp +++ b/src/gui/properties/trackerlist.cpp @@ -36,17 +36,19 @@ #include #include #include -#include "trackerlist.h" -#include "propertieswidget.h" -#include "trackersadditiondlg.h" -#include "guiiconprovider.h" +#include + #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/utils/misc.h" +#include "propertieswidget.h" +#include "trackersadditiondlg.h" +#include "guiiconprovider.h" #include "autoexpandabledialog.h" +#include "trackerlist.h" TrackerList::TrackerList(PropertiesWidget *properties): QTreeWidget(), properties(properties) { // Graphical settings diff --git a/src/gui/properties/trackersadditiondlg.cpp b/src/gui/properties/trackersadditiondlg.cpp new file mode 100644 index 000000000..e04a53839 --- /dev/null +++ b/src/gui/properties/trackersadditiondlg.cpp @@ -0,0 +1,130 @@ +/* + * Bittorrent Client using Qt 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 "core/utils/misc.h" +#include "core/utils/fs.h" +#include "core/net/downloadmanager.h" +#include "core/net/downloadhandler.h" +#include "core/bittorrent/trackerentry.h" +#include "core/bittorrent/torrenthandle.h" +#include "guiiconprovider.h" +#include "trackersadditiondlg.h" + +TrackersAdditionDlg::TrackersAdditionDlg(BitTorrent::TorrentHandle *const torrent, QWidget *parent) + : QDialog(parent) + , m_torrent(torrent) +{ + setupUi(this); + // Icons + uTorrentListButton->setIcon(GuiIconProvider::instance()->getIcon("download")); +} + +QStringList TrackersAdditionDlg::newTrackers() const +{ + return trackers_list->toPlainText().trimmed().split("\n"); +} + +void TrackersAdditionDlg::on_uTorrentListButton_clicked() +{ + uTorrentListButton->setEnabled(false); + 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); +} + +void TrackersAdditionDlg::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); + Utils::Fs::forceRemove(path); + return; + } + + // 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, 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"); + int nb = 0; + while (!list_file.atEnd()) { + const QByteArray line = list_file.readLine().trimmed(); + if (line.isEmpty()) continue; + BitTorrent::TrackerEntry newTracker(line); + if (!existingTrackers.contains(newTracker)) { + trackers_list->insertPlainText(line + "\n"); + ++nb; + } + } + // Clean up + list_file.close(); + Utils::Fs::forceRemove(path); + //To restore the cursor ... + setCursor(Qt::ArrowCursor); + uTorrentListButton->setEnabled(true); + // Display information message if necessary + if (nb == 0) + QMessageBox::information(this, tr("No change"), tr("No additional trackers were found."), QMessageBox::Ok); +} + +void TrackersAdditionDlg::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); +} + +QStringList TrackersAdditionDlg::askForTrackers(BitTorrent::TorrentHandle *const torrent) +{ + QStringList trackers; + TrackersAdditionDlg dlg(torrent); + if (dlg.exec() == QDialog::Accepted) + return dlg.newTrackers(); + + return trackers; +} diff --git a/src/gui/properties/trackersadditiondlg.h b/src/gui/properties/trackersadditiondlg.h index 378566c17..2b335d27c 100644 --- a/src/gui/properties/trackersadditiondlg.h +++ b/src/gui/properties/trackersadditiondlg.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,110 +32,33 @@ #define TRACKERSADDITION_H #include -#include -#include -#include -#include -#include "guiiconprovider.h" -#include "core/utils/misc.h" #include "ui_trackersadditiondlg.h" -#include "core/net/downloadmanager.h" -#include "core/net/downloadhandler.h" -#include "core/bittorrent/trackerentry.h" -#include "core/bittorrent/torrenthandle.h" -#include "core/utils/fs.h" -class TrackersAdditionDlg : public QDialog, private Ui::TrackersAdditionDlg{ - Q_OBJECT +class QString; +class QStringList; -private: - BitTorrent::TorrentHandle *const m_torrent; +namespace BitTorrent +{ + class TorrentHandle; +} + +class TrackersAdditionDlg : public QDialog, private Ui::TrackersAdditionDlg +{ + Q_OBJECT public: - TrackersAdditionDlg(BitTorrent::TorrentHandle *const torrent, QWidget *parent = 0): QDialog(parent), m_torrent(torrent) { - setupUi(this); - // Icons - uTorrentListButton->setIcon(GuiIconProvider::instance()->getIcon("download")); - } + TrackersAdditionDlg(BitTorrent::TorrentHandle *const torrent, QWidget *parent = 0); - ~TrackersAdditionDlg() {} - - QStringList newTrackers() const { - return trackers_list->toPlainText().trimmed().split("\n"); - } + QStringList newTrackers() const; + static QStringList askForTrackers(BitTorrent::TorrentHandle *const torrent); public slots: - void on_uTorrentListButton_clicked() { - uTorrentListButton->setEnabled(false); - 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); - } + void on_uTorrentListButton_clicked(); + void parseUTorrentList(const QString &, const QString &path); + void getTrackerError(const QString &, const QString &error); - 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); - Utils::Fs::forceRemove(path); - return; - } - - // 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, 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"); - int nb = 0; - while (!list_file.atEnd()) { - const QByteArray line = list_file.readLine().trimmed(); - if (line.isEmpty()) continue; - BitTorrent::TrackerEntry newTracker(line); - if (!existingTrackers.contains(newTracker)) { - trackers_list->insertPlainText(line + "\n"); - ++nb; - } - } - // Clean up - list_file.close(); - Utils::Fs::forceRemove(path); - //To restore the cursor ... - setCursor(Qt::ArrowCursor); - uTorrentListButton->setEnabled(true); - // Display information message if necessary - if (nb == 0) { - QMessageBox::information(this, tr("No change"), tr("No additional trackers were found."), QMessageBox::Ok); - } - } - - 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); - } - -public: - - static QStringList askForTrackers(BitTorrent::TorrentHandle *const torrent) { - QStringList trackers; - TrackersAdditionDlg dlg(torrent); - if (dlg.exec() == QDialog::Accepted) { - return dlg.newTrackers(); - } - return trackers; - } +private: + BitTorrent::TorrentHandle *const m_torrent; }; #endif