diff --git a/src/base/CMakeLists.txt b/src/base/CMakeLists.txt index 103e0f9a6..00fb433e6 100644 --- a/src/base/CMakeLists.txt +++ b/src/base/CMakeLists.txt @@ -29,7 +29,6 @@ http/responsegenerator.h http/server.h http/types.h net/dnsupdater.h -net/downloadhandler.h net/downloadmanager.h net/geoipmanager.h net/portforwarder.h @@ -100,7 +99,6 @@ http/responsebuilder.cpp http/responsegenerator.cpp http/server.cpp net/dnsupdater.cpp -net/downloadhandler.cpp net/downloadmanager.cpp net/geoipmanager.cpp net/portforwarder.cpp diff --git a/src/base/base.pri b/src/base/base.pri index c820af369..71013cd33 100644 --- a/src/base/base.pri +++ b/src/base/base.pri @@ -34,7 +34,6 @@ HEADERS += \ $$PWD/indexrange.h \ $$PWD/logger.h \ $$PWD/net/dnsupdater.h \ - $$PWD/net/downloadhandler.h \ $$PWD/net/downloadmanager.h \ $$PWD/net/geoipmanager.h \ $$PWD/net/portforwarder.h \ @@ -103,7 +102,6 @@ SOURCES += \ $$PWD/iconprovider.cpp \ $$PWD/logger.cpp \ $$PWD/net/dnsupdater.cpp \ - $$PWD/net/downloadhandler.cpp \ $$PWD/net/downloadmanager.cpp \ $$PWD/net/geoipmanager.cpp \ $$PWD/net/portforwarder.cpp \ diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp index 0c1d7290a..8de1acea4 100644 --- a/src/base/bittorrent/session.cpp +++ b/src/base/bittorrent/session.cpp @@ -67,7 +67,6 @@ #include "base/exceptions.h" #include "base/global.h" #include "base/logger.h" -#include "base/net/downloadhandler.h" #include "base/net/downloadmanager.h" #include "base/net/portforwarder.h" #include "base/net/proxyconfigurationmanager.h" @@ -1794,10 +1793,9 @@ bool Session::addTorrent(const QString &source, const AddTorrentParams ¶ms) if (Net::DownloadManager::hasSupportedScheme(source)) { LogMsg(tr("Downloading '%1', please wait...", "e.g: Downloading 'xxx.torrent', please wait...").arg(source)); // Launch downloader - const Net::DownloadHandler *handler = - Net::DownloadManager::instance()->download(Net::DownloadRequest(source).limit(10485760 /* 10MB */)); - connect(handler, &Net::DownloadHandler::finished, this, &Session::handleDownloadFinished); - m_downloadedTorrents[handler->url()] = params; + Net::DownloadManager::instance()->download(Net::DownloadRequest(source).limit(10485760 /* 10MB */) + , this, &Session::handleDownloadFinished); + m_downloadedTorrents[source] = params; return true; } diff --git a/src/base/net/dnsupdater.cpp b/src/base/net/dnsupdater.cpp index 6479c7d63..86a96afc5 100644 --- a/src/base/net/dnsupdater.cpp +++ b/src/base/net/dnsupdater.cpp @@ -33,7 +33,6 @@ #include #include "base/logger.h" -#include "base/net/downloadhandler.h" #include "base/net/downloadmanager.h" using namespace Net; @@ -74,9 +73,9 @@ void DNSUpdater::checkPublicIP() { Q_ASSERT(m_state == OK); - DownloadHandler *handler = DownloadManager::instance()->download( - DownloadRequest("http://checkip.dyndns.org").userAgent("qBittorrent/" QBT_VERSION_2)); - connect(handler, &DownloadHandler::finished, this, &DNSUpdater::ipRequestFinished); + DownloadManager::instance()->download( + DownloadRequest("http://checkip.dyndns.org").userAgent("qBittorrent/" QBT_VERSION_2) + , this, &DNSUpdater::ipRequestFinished); m_lastIPCheckTime = QDateTime::currentDateTime(); } @@ -116,9 +115,9 @@ void DNSUpdater::updateDNSService() qDebug() << Q_FUNC_INFO; m_lastIPCheckTime = QDateTime::currentDateTime(); - DownloadHandler *handler = DownloadManager::instance()->download( - DownloadRequest(getUpdateUrl()).userAgent("qBittorrent/" QBT_VERSION_2)); - connect(handler, &DownloadHandler::finished, this, &DNSUpdater::ipUpdateFinished); + DownloadManager::instance()->download( + DownloadRequest(getUpdateUrl()).userAgent("qBittorrent/" QBT_VERSION_2) + , this, &DNSUpdater::ipUpdateFinished); } QString DNSUpdater::getUpdateUrl() const diff --git a/src/base/net/downloadhandler.cpp b/src/base/net/downloadhandler.cpp deleted file mode 100644 index 78de6109d..000000000 --- a/src/base/net/downloadhandler.cpp +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2015, 2018 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 "downloadhandler.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "base/utils/fs.h" -#include "base/utils/gzip.h" -#include "base/utils/misc.h" - -namespace -{ - const int MAX_REDIRECTIONS = 20; // the common value for web browsers - - bool saveToFile(const QByteArray &replyData, QString &filePath) - { - QTemporaryFile tmpfile {Utils::Fs::tempPath() + "XXXXXX"}; - tmpfile.setAutoRemove(false); - - if (!tmpfile.open()) - return false; - - filePath = tmpfile.fileName(); - - tmpfile.write(replyData); - return true; - } -} - -Net::DownloadHandler::DownloadHandler(QNetworkReply *reply, DownloadManager *manager, const DownloadRequest &downloadRequest) - : QObject(manager) - , m_reply(reply) - , m_manager(manager) - , m_downloadRequest(downloadRequest) -{ - if (reply) - assignNetworkReply(reply); - m_result.url = url(); - m_result.status = DownloadStatus::Success; -} - -Net::DownloadHandler::~DownloadHandler() -{ - if (m_reply) - delete m_reply; -} - -void Net::DownloadHandler::assignNetworkReply(QNetworkReply *reply) -{ - Q_ASSERT(reply); - - m_reply = reply; - m_reply->setParent(this); - if (m_downloadRequest.limit() > 0) - connect(m_reply, &QNetworkReply::downloadProgress, this, &Net::DownloadHandler::checkDownloadSize); - connect(m_reply, &QNetworkReply::finished, this, &Net::DownloadHandler::processFinishedDownload); -} - -// Returns original url -QString Net::DownloadHandler::url() const -{ - return m_downloadRequest.url(); -} - -void Net::DownloadHandler::processFinishedDownload() -{ - const QString url = m_reply->url().toString(); - qDebug("Download finished: %s", qUtf8Printable(url)); - - // Check if the request was successful - if (m_reply->error() != QNetworkReply::NoError) { - // Failure - qDebug("Download failure (%s), reason: %s", qUtf8Printable(url), qUtf8Printable(errorCodeToString(m_reply->error()))); - setError(errorCodeToString(m_reply->error())); - finish(); - return; - } - - // 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()); - return; - } - - // Success - m_result.data = (m_reply->rawHeader("Content-Encoding") == "gzip") - ? Utils::Gzip::decompress(m_reply->readAll()) - : m_reply->readAll(); - - if (m_downloadRequest.saveToFile()) { - QString filePath; - if (saveToFile(m_result.data, filePath)) - m_result.filePath = filePath; - else - setError(tr("I/O Error")); - } - - finish(); -} - -void Net::DownloadHandler::checkDownloadSize(const qint64 bytesReceived, const qint64 bytesTotal) -{ - if ((bytesTotal > 0) && (bytesTotal <= m_downloadRequest.limit())) { - // Total number of bytes is available - disconnect(m_reply, &QNetworkReply::downloadProgress, this, &Net::DownloadHandler::checkDownloadSize); - return; - } - - if ((bytesTotal > m_downloadRequest.limit()) || (bytesReceived > m_downloadRequest.limit())) { - m_reply->abort(); - setError(tr("The file size is %1. It exceeds the download limit of %2.") - .arg(Utils::Misc::friendlyUnit(bytesTotal) - , Utils::Misc::friendlyUnit(m_downloadRequest.limit()))); - finish(); - } -} - -void Net::DownloadHandler::handleRedirection(const QUrl &newUrl) -{ - if (m_redirectionCounter >= MAX_REDIRECTIONS) { - setError(tr("Exceeded max redirections (%1)").arg(MAX_REDIRECTIONS)); - finish(); - return; - } - - // Resolve relative urls - const QUrl resolvedUrl = (newUrl.isRelative()) ? m_reply->url().resolved(newUrl) : newUrl; - const QString newUrlString = resolvedUrl.toString(); - qDebug("Redirecting from %s to %s...", qUtf8Printable(m_reply->url().toString()), qUtf8Printable(newUrlString)); - - // Redirect to magnet workaround - if (newUrlString.startsWith("magnet:", Qt::CaseInsensitive)) { - qDebug("Magnet redirect detected."); - m_reply->abort(); - m_result.status = DownloadStatus::RedirectedToMagnet; - m_result.magnet = newUrlString; - - finish(); - return; - } - - DownloadHandler *redirected = m_manager->download(DownloadRequest(m_downloadRequest).url(newUrlString)); - redirected->m_redirectionCounter = (m_redirectionCounter + 1); - connect(redirected, &DownloadHandler::finished, this, [this](const DownloadResult &result) - { - m_result = result; - m_result.url = url(); - finish(); - }); -} - -void Net::DownloadHandler::setError(const QString &error) -{ - m_result.errorString = error; - m_result.status = DownloadStatus::Failed; -} - -void Net::DownloadHandler::finish() -{ - emit finished(m_result); - deleteLater(); -} - -QString Net::DownloadHandler::errorCodeToString(const 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 honor 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"); - } -} diff --git a/src/base/net/downloadhandler.h b/src/base/net/downloadhandler.h deleted file mode 100644 index 113920ad6..000000000 --- a/src/base/net/downloadhandler.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2015, 2018 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 -#include - -#include "downloadmanager.h" - -class QUrl; - -namespace Net -{ - class DownloadManager; - - class DownloadHandler : public QObject - { - Q_OBJECT - Q_DISABLE_COPY(DownloadHandler) - - friend class DownloadManager; - - DownloadHandler(QNetworkReply *reply, DownloadManager *manager, const DownloadRequest &downloadRequest); - - public: - ~DownloadHandler() override; - - QString url() const; - - signals: - void finished(const DownloadResult &result); - - private slots: - void processFinishedDownload(); - void checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal); - - private: - void assignNetworkReply(QNetworkReply *reply); - void handleRedirection(const QUrl &newUrl); - void setError(const QString &error); - void finish(); - - static QString errorCodeToString(QNetworkReply::NetworkError status); - - QNetworkReply *m_reply; - DownloadManager *m_manager; - const DownloadRequest m_downloadRequest; - short m_redirectionCounter = 0; - DownloadResult m_result; - }; -} - -#endif // NET_DOWNLOADHANDLER_H diff --git a/src/base/net/downloadmanager.cpp b/src/base/net/downloadmanager.cpp index eb9eb924d..3c33a93bb 100644 --- a/src/base/net/downloadmanager.cpp +++ b/src/base/net/downloadmanager.cpp @@ -39,12 +39,15 @@ #include #include #include +#include #include #include "base/global.h" #include "base/logger.h" #include "base/preferences.h" -#include "downloadhandler.h" +#include "base/utils/fs.h" +#include "base/utils/gzip.h" +#include "base/utils/misc.h" #include "proxyconfigurationmanager.h" // Spoof Firefox 38 user agent to avoid web server banning @@ -108,6 +111,35 @@ namespace } }; + class DownloadHandlerImpl : public Net::DownloadHandler + { + Q_DISABLE_COPY(DownloadHandlerImpl) + + public: + DownloadHandlerImpl(Net::DownloadManager *manager, const Net::DownloadRequest &downloadRequest); + ~DownloadHandlerImpl() override; + + QString url() const; + const Net::DownloadRequest downloadRequest() const; + + void assignNetworkReply(QNetworkReply *reply); + + private: + void processFinishedDownload(); + void checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal); + void handleRedirection(const QUrl &newUrl); + void setError(const QString &error); + void finish(); + + static QString errorCodeToString(QNetworkReply::NetworkError status); + + QNetworkReply *m_reply = nullptr; + Net::DownloadManager *m_manager = nullptr; + const Net::DownloadRequest m_downloadRequest; + short m_redirectionCounter = 0; + Net::DownloadResult m_result; + }; + QNetworkRequest createNetworkRequest(const Net::DownloadRequest &downloadRequest) { QNetworkRequest request {downloadRequest.url()}; @@ -124,6 +156,22 @@ namespace return request; } + + const int MAX_REDIRECTIONS = 20; // the common value for web browsers + + bool saveToFile(const QByteArray &replyData, QString &filePath) + { + QTemporaryFile tmpfile {Utils::Fs::tempPath() + "XXXXXX"}; + tmpfile.setAutoRemove(false); + + if (!tmpfile.open()) + return false; + + filePath = tmpfile.fileName(); + + tmpfile.write(replyData); + return true; + } } Net::DownloadManager *Net::DownloadManager::m_instance = nullptr; @@ -164,20 +212,24 @@ Net::DownloadHandler *Net::DownloadManager::download(const DownloadRequest &down const QNetworkRequest request = createNetworkRequest(downloadRequest); const ServiceID id = ServiceID::fromURL(request.url()); const bool isSequentialService = m_sequentialServices.contains(id); + + auto downloadHandler = new DownloadHandlerImpl {this, downloadRequest}; + connect(downloadHandler, &DownloadHandler::finished, downloadHandler, &QObject::deleteLater); + connect(downloadHandler, &QObject::destroyed, this, [this, id, downloadHandler]() + { + m_waitingJobs[id].removeOne(downloadHandler); + }); + if (!isSequentialService || !m_busyServices.contains(id)) { qDebug("Downloading %s...", qUtf8Printable(downloadRequest.url())); if (isSequentialService) m_busyServices.insert(id); - return new DownloadHandler { - m_networkManager.get(request), this, downloadRequest}; + downloadHandler->assignNetworkReply(m_networkManager.get(request)); + } + else { + m_waitingJobs[id].enqueue(downloadHandler); } - auto *downloadHandler = new DownloadHandler {nullptr, this, downloadRequest}; - connect(downloadHandler, &DownloadHandler::destroyed, this, [this, id, downloadHandler]() - { - m_waitingJobs[id].removeOne(downloadHandler); - }); - m_waitingJobs[id].enqueue(downloadHandler); return downloadHandler; } @@ -262,9 +314,9 @@ void Net::DownloadManager::handleReplyFinished(const QNetworkReply *reply) return; } - DownloadHandler *handler = waitingJobsIter.value().dequeue(); - qDebug("Downloading %s...", qUtf8Printable(handler->m_downloadRequest.url())); - handler->assignNetworkReply(m_networkManager.get(createNetworkRequest(handler->m_downloadRequest))); + auto handler = static_cast(waitingJobsIter.value().dequeue()); + qDebug("Downloading %s...", qUtf8Printable(handler->url())); + handler->assignNetworkReply(m_networkManager.get(createNetworkRequest(handler->downloadRequest()))); handler->disconnect(this); } @@ -342,3 +394,194 @@ bool Net::operator==(const ServiceID &lhs, const ServiceID &rhs) { return ((lhs.hostName == rhs.hostName) && (lhs.port == rhs.port)); } + +namespace +{ + DownloadHandlerImpl::DownloadHandlerImpl(Net::DownloadManager *manager, const Net::DownloadRequest &downloadRequest) + : DownloadHandler {manager} + , m_manager {manager} + , m_downloadRequest {downloadRequest} + { + m_result.url = url(); + m_result.status = Net::DownloadStatus::Success; + } + + DownloadHandlerImpl::~DownloadHandlerImpl() + { + if (m_reply) + delete m_reply; + } + + void DownloadHandlerImpl::assignNetworkReply(QNetworkReply *reply) + { + Q_ASSERT(reply); + + m_reply = reply; + m_reply->setParent(this); + if (m_downloadRequest.limit() > 0) + connect(m_reply, &QNetworkReply::downloadProgress, this, &DownloadHandlerImpl::checkDownloadSize); + connect(m_reply, &QNetworkReply::finished, this, &DownloadHandlerImpl::processFinishedDownload); + } + + // Returns original url + QString DownloadHandlerImpl::url() const + { + return m_downloadRequest.url(); + } + + const Net::DownloadRequest DownloadHandlerImpl::downloadRequest() const + { + return m_downloadRequest; + } + + void DownloadHandlerImpl::processFinishedDownload() + { + const QString url = m_reply->url().toString(); + qDebug("Download finished: %s", qUtf8Printable(url)); + + // Check if the request was successful + if (m_reply->error() != QNetworkReply::NoError) { + // Failure + qDebug("Download failure (%s), reason: %s", qUtf8Printable(url), qUtf8Printable(errorCodeToString(m_reply->error()))); + setError(errorCodeToString(m_reply->error())); + finish(); + return; + } + + // 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()); + return; + } + + // Success + m_result.data = (m_reply->rawHeader("Content-Encoding") == "gzip") + ? Utils::Gzip::decompress(m_reply->readAll()) + : m_reply->readAll(); + + if (m_downloadRequest.saveToFile()) { + QString filePath; + if (saveToFile(m_result.data, filePath)) + m_result.filePath = filePath; + else + setError(tr("I/O Error")); + } + + finish(); + } + + void DownloadHandlerImpl::checkDownloadSize(const qint64 bytesReceived, const qint64 bytesTotal) + { + if ((bytesTotal > 0) && (bytesTotal <= m_downloadRequest.limit())) { + // Total number of bytes is available + disconnect(m_reply, &QNetworkReply::downloadProgress, this, &DownloadHandlerImpl::checkDownloadSize); + return; + } + + if ((bytesTotal > m_downloadRequest.limit()) || (bytesReceived > m_downloadRequest.limit())) { + m_reply->abort(); + setError(tr("The file size is %1. It exceeds the download limit of %2.") + .arg(Utils::Misc::friendlyUnit(bytesTotal) + , Utils::Misc::friendlyUnit(m_downloadRequest.limit()))); + finish(); + } + } + + void DownloadHandlerImpl::handleRedirection(const QUrl &newUrl) + { + if (m_redirectionCounter >= MAX_REDIRECTIONS) { + setError(tr("Exceeded max redirections (%1)").arg(MAX_REDIRECTIONS)); + finish(); + return; + } + + // Resolve relative urls + const QUrl resolvedUrl = newUrl.isRelative() ? m_reply->url().resolved(newUrl) : newUrl; + const QString newUrlString = resolvedUrl.toString(); + qDebug("Redirecting from %s to %s...", qUtf8Printable(m_reply->url().toString()), qUtf8Printable(newUrlString)); + + // Redirect to magnet workaround + if (newUrlString.startsWith("magnet:", Qt::CaseInsensitive)) { + qDebug("Magnet redirect detected."); + m_result.status = Net::DownloadStatus::RedirectedToMagnet; + m_result.magnet = newUrlString; + m_result.errorString = tr("Redirected to magnet URI."); + + finish(); + return; + } + + auto redirected = static_cast( + m_manager->download(Net::DownloadRequest(m_downloadRequest).url(newUrlString))); + redirected->m_redirectionCounter = (m_redirectionCounter + 1); + connect(redirected, &DownloadHandlerImpl::finished, this, [this](const Net::DownloadResult &result) + { + m_result = result; + m_result.url = url(); + finish(); + }); + } + + void DownloadHandlerImpl::setError(const QString &error) + { + m_result.errorString = error; + m_result.status = Net::DownloadStatus::Failed; + } + + void DownloadHandlerImpl::finish() + { + emit finished(m_result); + } + + QString DownloadHandlerImpl::errorCodeToString(const 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 honor 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"); + } + } +} diff --git a/src/base/net/downloadmanager.h b/src/base/net/downloadmanager.h index 1ff70dbde..8fe784814 100644 --- a/src/base/net/downloadmanager.h +++ b/src/base/net/downloadmanager.h @@ -44,7 +44,23 @@ class QUrl; namespace Net { - class DownloadHandler; + struct ServiceID + { + QString hostName; + int port; + + static ServiceID fromURL(const QUrl &url); + }; + + uint qHash(const ServiceID &serviceID, uint seed); + bool operator==(const ServiceID &lhs, const ServiceID &rhs); + + enum class DownloadStatus + { + Success, + RedirectedToMagnet, + Failed + }; class DownloadRequest { @@ -71,13 +87,6 @@ namespace Net bool m_saveToFile = false; }; - enum class DownloadStatus - { - Success, - RedirectedToMagnet, - Failed - }; - struct DownloadResult { QString url; @@ -88,16 +97,17 @@ namespace Net QString magnet; }; - struct ServiceID + class DownloadHandler : public QObject { - QString hostName; - int port; + Q_OBJECT + Q_DISABLE_COPY(DownloadHandler) - static ServiceID fromURL(const QUrl &url); - }; + public: + using QObject::QObject; - uint qHash(const ServiceID &serviceID, uint seed); - bool operator==(const ServiceID &lhs, const ServiceID &rhs); + signals: + void finished(const DownloadResult &result); + }; class DownloadManager : public QObject { @@ -111,6 +121,9 @@ namespace Net DownloadHandler *download(const DownloadRequest &downloadRequest); + template + void download(const DownloadRequest &downloadRequest, Context context, Func slot); + void registerSequentialService(const ServiceID &serviceID); QList cookiesForUrl(const QUrl &url) const; @@ -137,6 +150,13 @@ namespace Net QSet m_busyServices; QHash> m_waitingJobs; }; + + template + void DownloadManager::download(const DownloadRequest &downloadRequest, Context context, Func slot) + { + const DownloadHandler *handler = download(downloadRequest); + connect(handler, &DownloadHandler::finished, context, slot); + } } #endif // NET_DOWNLOADMANAGER_H diff --git a/src/base/net/geoipmanager.cpp b/src/base/net/geoipmanager.cpp index 23cc151f7..a496b038a 100644 --- a/src/base/net/geoipmanager.cpp +++ b/src/base/net/geoipmanager.cpp @@ -40,7 +40,6 @@ #include "base/profile.h" #include "base/utils/fs.h" #include "base/utils/gzip.h" -#include "downloadhandler.h" #include "downloadmanager.h" #include "private/geoipdatabase.h" @@ -118,8 +117,7 @@ void GeoIPManager::manageDatabaseUpdate() void GeoIPManager::downloadDatabaseFile() { - const DownloadHandler *handler = DownloadManager::instance()->download({DATABASE_URL}); - connect(handler, &DownloadHandler::finished, this, &GeoIPManager::downloadFinished); + DownloadManager::instance()->download({DATABASE_URL}, this, &GeoIPManager::downloadFinished); } QString GeoIPManager::lookup(const QHostAddress &hostAddr) const diff --git a/src/base/rss/rss_feed.cpp b/src/base/rss/rss_feed.cpp index 4a2c6c298..0680aa954 100644 --- a/src/base/rss/rss_feed.cpp +++ b/src/base/rss/rss_feed.cpp @@ -44,7 +44,6 @@ #include "../asyncfilestorage.h" #include "../global.h" #include "../logger.h" -#include "../net/downloadhandler.h" #include "../net/downloadmanager.h" #include "../profile.h" #include "../utils/fs.h" @@ -130,8 +129,7 @@ void Feed::refresh() // NOTE: Should we allow manually refreshing for disabled session? - Net::DownloadHandler *handler = Net::DownloadManager::instance()->download(m_url); - connect(handler, &Net::DownloadHandler::finished, this, &Feed::handleDownloadFinished); + Net::DownloadManager::instance()->download(m_url, this, &Feed::handleDownloadFinished); m_isLoading = true; emit stateChanged(this); @@ -399,9 +397,9 @@ void Feed::downloadIcon() // XXX: This works for most sites but it is not perfect const QUrl url(m_url); const auto iconUrl = QString("%1://%2/favicon.ico").arg(url.scheme(), url.host()); - const Net::DownloadHandler *handler = Net::DownloadManager::instance()->download( - Net::DownloadRequest(iconUrl).saveToFile(true)); - connect(handler, &Net::DownloadHandler::finished, this, &Feed::handleIconDownloadFinished); + Net::DownloadManager::instance()->download( + Net::DownloadRequest(iconUrl).saveToFile(true) + , this, &Feed::handleIconDownloadFinished); } int Feed::updateArticles(const QList &loadedArticles) diff --git a/src/base/search/searchpluginmanager.cpp b/src/base/search/searchpluginmanager.cpp index 7d3d3bd7a..d751cdf80 100644 --- a/src/base/search/searchpluginmanager.cpp +++ b/src/base/search/searchpluginmanager.cpp @@ -43,7 +43,6 @@ #include "base/global.h" #include "base/logger.h" -#include "base/net/downloadhandler.h" #include "base/net/downloadmanager.h" #include "base/preferences.h" #include "base/profile.h" @@ -199,8 +198,8 @@ void SearchPluginManager::installPlugin(const QString &source) if (Net::DownloadManager::hasSupportedScheme(source)) { using namespace Net; - DownloadHandler *handler = DownloadManager::instance()->download(DownloadRequest(source).saveToFile(true)); - connect(handler, &Net::DownloadHandler::finished, this, &SearchPluginManager::pluginDownloadFinished); + DownloadManager::instance()->download(DownloadRequest(source).saveToFile(true) + , this, &SearchPluginManager::pluginDownloadFinished); } else { QString path = source; @@ -302,8 +301,8 @@ void SearchPluginManager::checkForUpdates() { // Download version file from update server using namespace Net; - DownloadHandler *handler = DownloadManager::instance()->download({m_updateUrl + "versions.txt"}); - connect(handler, &Net::DownloadHandler::finished, this, &SearchPluginManager::versionInfoDownloadFinished); + DownloadManager::instance()->download({m_updateUrl + "versions.txt"} + , this, &SearchPluginManager::versionInfoDownloadFinished); } SearchDownloadHandler *SearchPluginManager::downloadTorrent(const QString &siteUrl, const QString &url) diff --git a/src/gui/addnewtorrentdialog.cpp b/src/gui/addnewtorrentdialog.cpp index d40da8c01..a0760ddc3 100644 --- a/src/gui/addnewtorrentdialog.cpp +++ b/src/gui/addnewtorrentdialog.cpp @@ -42,7 +42,6 @@ #include "base/bittorrent/torrenthandle.h" #include "base/bittorrent/torrentinfo.h" #include "base/global.h" -#include "base/net/downloadhandler.h" #include "base/net/downloadmanager.h" #include "base/preferences.h" #include "base/settingsstorage.h" @@ -237,9 +236,9 @@ void AddNewTorrentDialog::show(const QString &source, const BitTorrent::AddTorre if (Net::DownloadManager::hasSupportedScheme(source)) { // Launch downloader - Net::DownloadHandler *handler = Net::DownloadManager::instance()->download( - Net::DownloadRequest(source).limit(10485760 /* 10MB */)); - connect(handler, &Net::DownloadHandler::finished, dlg, &AddNewTorrentDialog::handleDownloadFinished); + Net::DownloadManager::instance()->download( + Net::DownloadRequest(source).limit(10485760 /* 10MB */) + , dlg, &AddNewTorrentDialog::handleDownloadFinished); return; } diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index ed3e917f3..2db724e06 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -97,7 +97,6 @@ #include "utils.h" #ifdef Q_OS_WIN -#include "base/net/downloadhandler.h" #include "base/net/downloadmanager.h" #endif #ifdef Q_OS_MAC @@ -2018,9 +2017,9 @@ void MainWindow::installPython() const QString installerURL = ((QSysInfo::windowsVersion() >= QSysInfo::WV_VISTA) ? "https://www.python.org/ftp/python/3.6.6/python-3.6.6.exe" : "https://www.python.org/ftp/python/3.4.4/python-3.4.4.msi"); - Net::DownloadHandler *handler = Net::DownloadManager::instance()->download( - Net::DownloadRequest(installerURL).saveToFile(true)); - connect(handler, &Net::DownloadHandler::finished, this, &MainWindow::pythonDownloadFinished); + Net::DownloadManager::instance()->download( + Net::DownloadRequest(installerURL).saveToFile(true) + , this, &MainWindow::pythonDownloadFinished); } void MainWindow::pythonDownloadFinished(const Net::DownloadResult &result) diff --git a/src/gui/programupdater.cpp b/src/gui/programupdater.cpp index 46d84cd57..9e5b46e51 100644 --- a/src/gui/programupdater.cpp +++ b/src/gui/programupdater.cpp @@ -35,7 +35,6 @@ #include #include -#include "base/net/downloadhandler.h" #include "base/net/downloadmanager.h" #include "base/utils/fs.h" @@ -56,9 +55,9 @@ void ProgramUpdater::checkForUpdates() { // Don't change this User-Agent. In case our updater goes haywire, // the filehost can identify it and contact us. - Net::DownloadHandler *handler = Net::DownloadManager::instance()->download( - Net::DownloadRequest(RSS_URL).userAgent("qBittorrent/" QBT_VERSION_2 " ProgramUpdater (www.qbittorrent.org)")); - connect(handler, &Net::DownloadHandler::finished, this, &ProgramUpdater::rssDownloadFinished); + Net::DownloadManager::instance()->download( + Net::DownloadRequest(RSS_URL).userAgent("qBittorrent/" QBT_VERSION_2 " ProgramUpdater (www.qbittorrent.org)") + , this, &ProgramUpdater::rssDownloadFinished); } void ProgramUpdater::rssDownloadFinished(const Net::DownloadResult &result) diff --git a/src/gui/properties/trackersadditiondialog.cpp b/src/gui/properties/trackersadditiondialog.cpp index bc1f23e79..d06d700d2 100644 --- a/src/gui/properties/trackersadditiondialog.cpp +++ b/src/gui/properties/trackersadditiondialog.cpp @@ -35,7 +35,6 @@ #include "base/bittorrent/torrenthandle.h" #include "base/bittorrent/trackerentry.h" #include "base/global.h" -#include "base/net/downloadhandler.h" #include "base/net/downloadmanager.h" #include "base/utils/fs.h" #include "base/utils/misc.h" @@ -71,8 +70,8 @@ QStringList TrackersAdditionDialog::newTrackers() const void TrackersAdditionDialog::on_uTorrentListButton_clicked() { m_ui->uTorrentListButton->setEnabled(false); - Net::DownloadHandler *handler = Net::DownloadManager::instance()->download(m_ui->lineEditListURL->text()); - connect(handler, &Net::DownloadHandler::finished, this, &TrackersAdditionDialog::torrentListDownloadFinished); + Net::DownloadManager::instance()->download(m_ui->lineEditListURL->text() + , this, &TrackersAdditionDialog::torrentListDownloadFinished); // Just to show that it takes times setCursor(Qt::WaitCursor); } diff --git a/src/gui/search/pluginselectdialog.cpp b/src/gui/search/pluginselectdialog.cpp index e67e9c89f..2608cf4ef 100644 --- a/src/gui/search/pluginselectdialog.cpp +++ b/src/gui/search/pluginselectdialog.cpp @@ -40,7 +40,6 @@ #include #include "base/global.h" -#include "base/net/downloadhandler.h" #include "base/net/downloadmanager.h" #include "base/utils/fs.h" #include "autoexpandabledialog.h" @@ -291,9 +290,9 @@ void PluginSelectDialog::addNewPlugin(const QString &pluginName) else { // Icon is missing, we must download it using namespace Net; - DownloadHandler *handler = DownloadManager::instance()->download( - DownloadRequest(plugin->url + "/favicon.ico").saveToFile(true)); - connect(handler, &DownloadHandler::finished, this, &PluginSelectDialog::iconDownloadFinished); + DownloadManager::instance()->download( + DownloadRequest(plugin->url + "/favicon.ico").saveToFile(true) + , this, &PluginSelectDialog::iconDownloadFinished); } item->setText(PLUGIN_VERSION, plugin->version); } diff --git a/src/gui/transferlistfilterswidget.cpp b/src/gui/transferlistfilterswidget.cpp index 5e84f2568..8eaf58012 100644 --- a/src/gui/transferlistfilterswidget.cpp +++ b/src/gui/transferlistfilterswidget.cpp @@ -42,7 +42,6 @@ #include "base/bittorrent/trackerentry.h" #include "base/global.h" #include "base/logger.h" -#include "base/net/downloadhandler.h" #include "base/net/downloadmanager.h" #include "base/preferences.h" #include "base/torrentfilter.h" @@ -406,9 +405,9 @@ void TrackerFiltersList::trackerWarning(const QString &hash, const QString &trac void TrackerFiltersList::downloadFavicon(const QString &url) { if (!m_downloadTrackerFavicon) return; - Net::DownloadHandler *h = Net::DownloadManager::instance()->download( - Net::DownloadRequest(url).saveToFile(true)); - connect(h, &Net::DownloadHandler::finished, this, &TrackerFiltersList::handleFavicoDownloadFinished); + Net::DownloadManager::instance()->download( + Net::DownloadRequest(url).saveToFile(true) + , this, &TrackerFiltersList::handleFavicoDownloadFinished); } void TrackerFiltersList::handleFavicoDownloadFinished(const Net::DownloadResult &result)