|
|
@ -1,6 +1,6 @@ |
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* Bittorrent Client using Qt and libtorrent. |
|
|
|
* Bittorrent Client using Qt and libtorrent. |
|
|
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru> |
|
|
|
* Copyright (C) 2015, 2018 Vladimir Golovnev <glassez@yandex.ru> |
|
|
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org> |
|
|
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org> |
|
|
|
* |
|
|
|
* |
|
|
|
* This program is free software; you can redistribute it and/or |
|
|
|
* This program is free software; you can redistribute it and/or |
|
|
@ -29,6 +29,7 @@ |
|
|
|
|
|
|
|
|
|
|
|
#include "downloadhandler.h" |
|
|
|
#include "downloadhandler.h" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <QCoreApplication> |
|
|
|
#include <QDebug> |
|
|
|
#include <QDebug> |
|
|
|
#include <QNetworkAccessManager> |
|
|
|
#include <QNetworkAccessManager> |
|
|
|
#include <QNetworkCookie> |
|
|
|
#include <QNetworkCookie> |
|
|
@ -43,35 +44,46 @@ |
|
|
|
#include "base/utils/misc.h" |
|
|
|
#include "base/utils/misc.h" |
|
|
|
#include "downloadmanager.h" |
|
|
|
#include "downloadmanager.h" |
|
|
|
|
|
|
|
|
|
|
|
static QString errorCodeToString(QNetworkReply::NetworkError status); |
|
|
|
namespace |
|
|
|
|
|
|
|
{ |
|
|
|
using namespace Net; |
|
|
|
QString tr(const char *message); |
|
|
|
|
|
|
|
QString errorCodeToString(QNetworkReply::NetworkError status); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
DownloadHandler::DownloadHandler(QNetworkReply *reply, DownloadManager *manager, bool saveToFile, qint64 limit, bool handleRedirectToMagnet) |
|
|
|
Net::DownloadHandler::DownloadHandler(QNetworkReply *reply, DownloadManager *manager, const DownloadRequest &downloadRequest) |
|
|
|
: QObject(manager) |
|
|
|
: QObject(manager) |
|
|
|
, m_reply(reply) |
|
|
|
, m_reply(reply) |
|
|
|
, m_manager(manager) |
|
|
|
, m_manager(manager) |
|
|
|
, m_saveToFile(saveToFile) |
|
|
|
, m_downloadRequest(downloadRequest) |
|
|
|
, m_sizeLimit(limit) |
|
|
|
|
|
|
|
, m_handleRedirectToMagnet(handleRedirectToMagnet) |
|
|
|
|
|
|
|
, m_url(reply->url().toString()) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
init(); |
|
|
|
if (reply) |
|
|
|
|
|
|
|
assignNetworkReply(reply); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
DownloadHandler::~DownloadHandler() |
|
|
|
Net::DownloadHandler::~DownloadHandler() |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (m_reply) |
|
|
|
if (m_reply) |
|
|
|
delete 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
|
|
|
|
// Returns original url
|
|
|
|
QString DownloadHandler::url() const |
|
|
|
QString Net::DownloadHandler::url() const |
|
|
|
{ |
|
|
|
{ |
|
|
|
return m_url; |
|
|
|
return m_downloadRequest.url(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void DownloadHandler::processFinishedDownload() |
|
|
|
void Net::DownloadHandler::processFinishedDownload() |
|
|
|
{ |
|
|
|
{ |
|
|
|
QString url = m_reply->url().toString(); |
|
|
|
QString url = m_reply->url().toString(); |
|
|
|
qDebug("Download finished: %s", qUtf8Printable(url)); |
|
|
|
qDebug("Download finished: %s", qUtf8Printable(url)); |
|
|
@ -79,7 +91,7 @@ void DownloadHandler::processFinishedDownload() |
|
|
|
if (m_reply->error() != QNetworkReply::NoError) { |
|
|
|
if (m_reply->error() != QNetworkReply::NoError) { |
|
|
|
// Failure
|
|
|
|
// Failure
|
|
|
|
qDebug("Download failure (%s), reason: %s", qUtf8Printable(url), qUtf8Printable(errorCodeToString(m_reply->error()))); |
|
|
|
qDebug("Download failure (%s), reason: %s", qUtf8Printable(url), qUtf8Printable(errorCodeToString(m_reply->error()))); |
|
|
|
emit downloadFailed(m_url, errorCodeToString(m_reply->error())); |
|
|
|
emit downloadFailed(m_downloadRequest.url(), errorCodeToString(m_reply->error())); |
|
|
|
this->deleteLater(); |
|
|
|
this->deleteLater(); |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else { |
|
|
@ -97,15 +109,15 @@ void DownloadHandler::processFinishedDownload() |
|
|
|
replyData = Utils::Gzip::decompress(replyData); |
|
|
|
replyData = Utils::Gzip::decompress(replyData); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (m_saveToFile) { |
|
|
|
if (m_downloadRequest.saveToFile()) { |
|
|
|
QString filePath; |
|
|
|
QString filePath; |
|
|
|
if (saveToFile(replyData, filePath)) |
|
|
|
if (saveToFile(replyData, filePath)) |
|
|
|
emit downloadFinished(m_url, filePath); |
|
|
|
emit downloadFinished(m_downloadRequest.url(), filePath); |
|
|
|
else |
|
|
|
else |
|
|
|
emit downloadFailed(m_url, tr("I/O Error")); |
|
|
|
emit downloadFailed(m_downloadRequest.url(), tr("I/O Error")); |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else { |
|
|
|
emit downloadFinished(m_url, replyData); |
|
|
|
emit downloadFinished(m_downloadRequest.url(), replyData); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this->deleteLater(); |
|
|
|
this->deleteLater(); |
|
|
@ -113,35 +125,27 @@ void DownloadHandler::processFinishedDownload() |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void DownloadHandler::checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal) |
|
|
|
void Net::DownloadHandler::checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal) |
|
|
|
{ |
|
|
|
{ |
|
|
|
QString msg = tr("The file size is %1. It exceeds the download limit of %2."); |
|
|
|
QString msg = tr("The file size is %1. It exceeds the download limit of %2."); |
|
|
|
|
|
|
|
|
|
|
|
if (bytesTotal > 0) { |
|
|
|
if (bytesTotal > 0) { |
|
|
|
// Total number of bytes is available
|
|
|
|
// Total number of bytes is available
|
|
|
|
if (bytesTotal > m_sizeLimit) { |
|
|
|
if (bytesTotal > m_downloadRequest.limit()) { |
|
|
|
m_reply->abort(); |
|
|
|
m_reply->abort(); |
|
|
|
emit downloadFailed(m_url, msg.arg(Utils::Misc::friendlyUnit(bytesTotal), Utils::Misc::friendlyUnit(m_sizeLimit))); |
|
|
|
emit downloadFailed(m_downloadRequest.url(), msg.arg(Utils::Misc::friendlyUnit(bytesTotal), Utils::Misc::friendlyUnit(m_downloadRequest.limit()))); |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else { |
|
|
|
disconnect(m_reply, &QNetworkReply::downloadProgress, this, &Net::DownloadHandler::checkDownloadSize); |
|
|
|
disconnect(m_reply, &QNetworkReply::downloadProgress, this, &Net::DownloadHandler::checkDownloadSize); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
else if (bytesReceived > m_sizeLimit) { |
|
|
|
else if (bytesReceived > m_downloadRequest.limit()) { |
|
|
|
m_reply->abort(); |
|
|
|
m_reply->abort(); |
|
|
|
emit downloadFailed(m_url, msg.arg(Utils::Misc::friendlyUnit(bytesReceived), Utils::Misc::friendlyUnit(m_sizeLimit))); |
|
|
|
emit downloadFailed(m_downloadRequest.url(), msg.arg(Utils::Misc::friendlyUnit(bytesReceived), Utils::Misc::friendlyUnit(m_downloadRequest.limit()))); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void DownloadHandler::init() |
|
|
|
bool Net::DownloadHandler::saveToFile(const QByteArray &replyData, QString &filePath) |
|
|
|
{ |
|
|
|
|
|
|
|
m_reply->setParent(this); |
|
|
|
|
|
|
|
if (m_sizeLimit > 0) |
|
|
|
|
|
|
|
connect(m_reply, &QNetworkReply::downloadProgress, this, &Net::DownloadHandler::checkDownloadSize); |
|
|
|
|
|
|
|
connect(m_reply, &QNetworkReply::finished, this, &Net::DownloadHandler::processFinishedDownload); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool DownloadHandler::saveToFile(const QByteArray &replyData, QString &filePath) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
QTemporaryFile *tmpfile = new QTemporaryFile(Utils::Fs::tempPath() + "XXXXXX"); |
|
|
|
QTemporaryFile *tmpfile = new QTemporaryFile(Utils::Fs::tempPath() + "XXXXXX"); |
|
|
|
if (!tmpfile->open()) { |
|
|
|
if (!tmpfile->open()) { |
|
|
@ -168,82 +172,104 @@ bool DownloadHandler::saveToFile(const QByteArray &replyData, QString &filePath) |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void DownloadHandler::handleRedirection(QUrl newUrl) |
|
|
|
void Net::DownloadHandler::handleRedirection(QUrl newUrl) |
|
|
|
{ |
|
|
|
{ |
|
|
|
// Resolve relative urls
|
|
|
|
// Resolve relative urls
|
|
|
|
if (newUrl.isRelative()) |
|
|
|
if (newUrl.isRelative()) |
|
|
|
newUrl = m_reply->url().resolved(newUrl); |
|
|
|
newUrl = m_reply->url().resolved(newUrl); |
|
|
|
|
|
|
|
|
|
|
|
const QString newUrlString = newUrl.toString(); |
|
|
|
const QString newUrlString = newUrl.toString(); |
|
|
|
qDebug("Redirecting from %s to %s", qUtf8Printable(m_reply->url().toString()), qUtf8Printable(newUrlString)); |
|
|
|
qDebug("Redirecting from %s to %s...", qUtf8Printable(m_reply->url().toString()), qUtf8Printable(newUrlString)); |
|
|
|
|
|
|
|
|
|
|
|
// Redirect to magnet workaround
|
|
|
|
// Redirect to magnet workaround
|
|
|
|
if (newUrlString.startsWith("magnet:", Qt::CaseInsensitive)) { |
|
|
|
if (newUrlString.startsWith("magnet:", Qt::CaseInsensitive)) { |
|
|
|
qDebug("Magnet redirect detected."); |
|
|
|
qDebug("Magnet redirect detected."); |
|
|
|
m_reply->abort(); |
|
|
|
m_reply->abort(); |
|
|
|
if (m_handleRedirectToMagnet) |
|
|
|
if (m_downloadRequest.handleRedirectToMagnet()) |
|
|
|
emit redirectedToMagnet(m_url, newUrlString); |
|
|
|
emit redirectedToMagnet(m_downloadRequest.url(), newUrlString); |
|
|
|
else |
|
|
|
else |
|
|
|
emit downloadFailed(m_url, tr("Unexpected redirect to magnet URI.")); |
|
|
|
emit downloadFailed(m_downloadRequest.url(), tr("Unexpected redirect to magnet URI.")); |
|
|
|
|
|
|
|
|
|
|
|
this->deleteLater(); |
|
|
|
this->deleteLater(); |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else { |
|
|
|
DownloadHandler *tmp = m_manager->downloadUrl(newUrlString, m_saveToFile, m_sizeLimit, m_handleRedirectToMagnet); |
|
|
|
DownloadHandler *redirected = m_manager->download(DownloadRequest(m_downloadRequest).url(newUrlString)); |
|
|
|
m_reply->deleteLater(); |
|
|
|
connect(redirected, &DownloadHandler::destroyed, this, &DownloadHandler::deleteLater); |
|
|
|
m_reply = tmp->m_reply; |
|
|
|
connect(redirected, &DownloadHandler::downloadFailed, this, [this](const QString &, const QString &reason) |
|
|
|
init(); |
|
|
|
{ |
|
|
|
tmp->m_reply = nullptr; |
|
|
|
emit downloadFailed(url(), reason); |
|
|
|
delete tmp; |
|
|
|
}); |
|
|
|
|
|
|
|
connect(redirected, &DownloadHandler::redirectedToMagnet, this, [this](const QString &, const QString &magnetUri) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
emit redirectedToMagnet(url(), magnetUri); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
connect(redirected, static_cast<void (DownloadHandler::*)(const QString &, const QString &)>(&DownloadHandler::downloadFinished) |
|
|
|
|
|
|
|
, this, [this](const QString &, const QString &fileName) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
emit downloadFinished(url(), fileName); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
connect(redirected, static_cast<void (DownloadHandler::*)(const QString &, const QByteArray &)>(&DownloadHandler::downloadFinished) |
|
|
|
|
|
|
|
, this, [this](const QString &, const QByteArray &data) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
emit downloadFinished(url(), data); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
QString tr(const char *message) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return QCoreApplication::translate("DownloadHandler", message); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
QString errorCodeToString(QNetworkReply::NetworkError status) |
|
|
|
QString errorCodeToString(QNetworkReply::NetworkError status) |
|
|
|
{ |
|
|
|
{ |
|
|
|
switch (status) { |
|
|
|
switch (status) { |
|
|
|
case QNetworkReply::HostNotFoundError: |
|
|
|
case QNetworkReply::HostNotFoundError: |
|
|
|
return QObject::tr("The remote host name was not found (invalid hostname)"); |
|
|
|
return tr("The remote host name was not found (invalid hostname)"); |
|
|
|
case QNetworkReply::OperationCanceledError: |
|
|
|
case QNetworkReply::OperationCanceledError: |
|
|
|
return QObject::tr("The operation was canceled"); |
|
|
|
return tr("The operation was canceled"); |
|
|
|
case QNetworkReply::RemoteHostClosedError: |
|
|
|
case QNetworkReply::RemoteHostClosedError: |
|
|
|
return QObject::tr("The remote server closed the connection prematurely, before the entire reply was received and processed"); |
|
|
|
return tr("The remote server closed the connection prematurely, before the entire reply was received and processed"); |
|
|
|
case QNetworkReply::TimeoutError: |
|
|
|
case QNetworkReply::TimeoutError: |
|
|
|
return QObject::tr("The connection to the remote server timed out"); |
|
|
|
return tr("The connection to the remote server timed out"); |
|
|
|
case QNetworkReply::SslHandshakeFailedError: |
|
|
|
case QNetworkReply::SslHandshakeFailedError: |
|
|
|
return QObject::tr("SSL/TLS handshake failed"); |
|
|
|
return tr("SSL/TLS handshake failed"); |
|
|
|
case QNetworkReply::ConnectionRefusedError: |
|
|
|
case QNetworkReply::ConnectionRefusedError: |
|
|
|
return QObject::tr("The remote server refused the connection"); |
|
|
|
return tr("The remote server refused the connection"); |
|
|
|
case QNetworkReply::ProxyConnectionRefusedError: |
|
|
|
case QNetworkReply::ProxyConnectionRefusedError: |
|
|
|
return QObject::tr("The connection to the proxy server was refused"); |
|
|
|
return tr("The connection to the proxy server was refused"); |
|
|
|
case QNetworkReply::ProxyConnectionClosedError: |
|
|
|
case QNetworkReply::ProxyConnectionClosedError: |
|
|
|
return QObject::tr("The proxy server closed the connection prematurely"); |
|
|
|
return tr("The proxy server closed the connection prematurely"); |
|
|
|
case QNetworkReply::ProxyNotFoundError: |
|
|
|
case QNetworkReply::ProxyNotFoundError: |
|
|
|
return QObject::tr("The proxy host name was not found"); |
|
|
|
return tr("The proxy host name was not found"); |
|
|
|
case QNetworkReply::ProxyTimeoutError: |
|
|
|
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"); |
|
|
|
return tr("The connection to the proxy timed out or the proxy did not reply in time to the request sent"); |
|
|
|
case QNetworkReply::ProxyAuthenticationRequiredError: |
|
|
|
case QNetworkReply::ProxyAuthenticationRequiredError: |
|
|
|
return QObject::tr("The proxy requires authentication in order to honor the request but did not accept any credentials offered"); |
|
|
|
return tr("The proxy requires authentication in order to honor the request but did not accept any credentials offered"); |
|
|
|
case QNetworkReply::ContentAccessDenied: |
|
|
|
case QNetworkReply::ContentAccessDenied: |
|
|
|
return QObject::tr("The access to the remote content was denied (401)"); |
|
|
|
return tr("The access to the remote content was denied (401)"); |
|
|
|
case QNetworkReply::ContentOperationNotPermittedError: |
|
|
|
case QNetworkReply::ContentOperationNotPermittedError: |
|
|
|
return QObject::tr("The operation requested on the remote content is not permitted"); |
|
|
|
return tr("The operation requested on the remote content is not permitted"); |
|
|
|
case QNetworkReply::ContentNotFoundError: |
|
|
|
case QNetworkReply::ContentNotFoundError: |
|
|
|
return QObject::tr("The remote content was not found at the server (404)"); |
|
|
|
return tr("The remote content was not found at the server (404)"); |
|
|
|
case QNetworkReply::AuthenticationRequiredError: |
|
|
|
case QNetworkReply::AuthenticationRequiredError: |
|
|
|
return QObject::tr("The remote server requires authentication to serve the content but the credentials provided were not accepted"); |
|
|
|
return tr("The remote server requires authentication to serve the content but the credentials provided were not accepted"); |
|
|
|
case QNetworkReply::ProtocolUnknownError: |
|
|
|
case QNetworkReply::ProtocolUnknownError: |
|
|
|
return QObject::tr("The Network Access API cannot honor the request because the protocol is not known"); |
|
|
|
return tr("The Network Access API cannot honor the request because the protocol is not known"); |
|
|
|
case QNetworkReply::ProtocolInvalidOperationError: |
|
|
|
case QNetworkReply::ProtocolInvalidOperationError: |
|
|
|
return QObject::tr("The requested operation is invalid for this protocol"); |
|
|
|
return tr("The requested operation is invalid for this protocol"); |
|
|
|
case QNetworkReply::UnknownNetworkError: |
|
|
|
case QNetworkReply::UnknownNetworkError: |
|
|
|
return QObject::tr("An unknown network-related error was detected"); |
|
|
|
return tr("An unknown network-related error was detected"); |
|
|
|
case QNetworkReply::UnknownProxyError: |
|
|
|
case QNetworkReply::UnknownProxyError: |
|
|
|
return QObject::tr("An unknown proxy-related error was detected"); |
|
|
|
return tr("An unknown proxy-related error was detected"); |
|
|
|
case QNetworkReply::UnknownContentError: |
|
|
|
case QNetworkReply::UnknownContentError: |
|
|
|
return QObject::tr("An unknown error related to the remote content was detected"); |
|
|
|
return tr("An unknown error related to the remote content was detected"); |
|
|
|
case QNetworkReply::ProtocolFailure: |
|
|
|
case QNetworkReply::ProtocolFailure: |
|
|
|
return QObject::tr("A breakdown in protocol was detected"); |
|
|
|
return tr("A breakdown in protocol was detected"); |
|
|
|
default: |
|
|
|
default: |
|
|
|
return QObject::tr("Unknown error"); |
|
|
|
return tr("Unknown error"); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|