You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
259 lines
11 KiB
259 lines
11 KiB
/*! @file |
|
|
|
@id $Id$ |
|
*/ |
|
// 1 2 3 4 5 6 7 8 |
|
// 45678901234567890123456789012345678901234567890123456789012345678901234567890 |
|
|
|
#include <QtNetwork/QNetworkReply> |
|
#include <QtNetwork/QSslError> |
|
#include <map> |
|
|
|
#include <cassert> |
|
#include <QtCore/QDebug> |
|
#ifndef LOG |
|
#define LOG qDebug()<<__PRETTY_FUNCTION__ |
|
#endif |
|
|
|
class DownloadManager: public QObject { |
|
Q_OBJECT; |
|
public: |
|
|
|
DownloadManager& operator+=(QNetworkReply* reply) { |
|
LOG; |
|
add(reply); |
|
return *this; |
|
} |
|
|
|
static QString networkError(QNetworkReply::NetworkError err) { |
|
LOG<<err; |
|
switch (err) { |
|
case QNetworkReply::NoError: |
|
return tr("Network connection successful, remote host can be" |
|
" reached."); |
|
case QNetworkReply::ConnectionRefusedError: |
|
return tr("The remote server refused the connection (the server is" |
|
" not accepting requests)."); |
|
case QNetworkReply::RemoteHostClosedError: |
|
return tr("The remote server closed the connection prematurely," |
|
" before the entire reply was received and processed."); |
|
case QNetworkReply::HostNotFoundError: |
|
return tr("The remote host name was not found (invalid hostname)."); |
|
case QNetworkReply::TimeoutError: |
|
return tr("The connection to the remote server timed out."); |
|
case QNetworkReply::OperationCanceledError: |
|
return tr("The operation was canceled via calls to abort() or" |
|
" close() before it was finished."); |
|
case QNetworkReply::SslHandshakeFailedError: |
|
return tr("The SSL/TLS handshake failed and the encrypted channel" |
|
" could not be established. See SSL-Errors above."); |
|
case QNetworkReply::ProxyConnectionRefusedError: |
|
return tr("The connection to the proxy server was refused (the" |
|
" proxy server is not accepting requests)."); |
|
case QNetworkReply::ProxyConnectionClosedError: |
|
return tr("The proxy server closed the connection prematurely," |
|
" before the entire reply was received and processed."); |
|
case QNetworkReply::ProxyNotFoundError: |
|
return tr("The proxy host name was not found (invalid proxy" |
|
" hostname)."); |
|
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" |
|
" (if any)."); |
|
case QNetworkReply::ContentAccessDenied: |
|
return tr("The access to the remote content was denied (similar to" |
|
" HTTP error 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 (similar" |
|
" to HTTP error 404)."); |
|
case QNetworkReply::AuthenticationRequiredError: |
|
return tr("The remote server requires authentication to serve the" |
|
" content but the credentials provided were not accepted" |
|
" (if any)."); |
|
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 unknonwn error related to the remote content was" |
|
" detected."); |
|
case QNetworkReply::ProtocolFailure: |
|
return tr("A breakdown in protocol was detected (parsing error," |
|
" invalid or unexpected responses, etc.)."); |
|
default: |
|
return tr("<strong>Unknown network error (code: %1).</string>") |
|
.arg(err); |
|
} |
|
} |
|
|
|
Q_SIGNALS: |
|
|
|
void progress(qint64 done, qint64 total); |
|
void started(); |
|
void finished(); |
|
void error(QString); |
|
|
|
public Q_SLOTS: |
|
|
|
void add(QNetworkReply* reply) { |
|
LOG<<reply->url().toString(); |
|
_downloads[reply].progress = Progress(0, 0); |
|
assert(connect(reply, SIGNAL(downloadProgress(qint64, qint64)), |
|
SLOT(downloadProgress(qint64, qint64)))); |
|
assert(connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), |
|
SLOT(error(QNetworkReply::NetworkError)))); |
|
assert(connect(reply, SIGNAL(finished()), |
|
SLOT(slotFinished()))); |
|
assert(connect(reply, SIGNAL(metaDataChanged()), |
|
SLOT(metaDataChanged()))); |
|
assert(connect(reply, SIGNAL(sslErrors(const QList<QSslError>&)), |
|
SLOT(sslErrors(const QList<QSslError>&)))); |
|
assert(connect(reply, SIGNAL(uploadProgress(qint64, qint64)), |
|
SLOT(uploadProgress(qint64, qint64)))); |
|
if (_downloads.size()==1) started(); |
|
} |
|
|
|
void abort() { |
|
while (_downloads.size()) _downloads.begin()->first->abort(); |
|
} |
|
|
|
private: |
|
|
|
void calcProgress() { |
|
//LOG; |
|
qint64 done(0); |
|
qint64 total(0); |
|
for (Downloads::iterator it(_downloads.begin()); |
|
it!=_downloads.end(); ++it) { |
|
done += it->second.progress.first; |
|
total += it->second.progress.second; |
|
} |
|
progress(done, total); |
|
} |
|
|
|
private Q_SLOTS: |
|
|
|
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) { |
|
//LOG<<bytesReceived<<bytesTotal; |
|
_downloads[qobject_cast<QNetworkReply*>(sender())].progress.first |
|
= bytesReceived; |
|
_downloads[qobject_cast<QNetworkReply*>(sender())].progress.second |
|
= bytesTotal; |
|
calcProgress(); |
|
} |
|
void error(QNetworkReply::NetworkError code) { |
|
LOG<<"Status:"<<networkError(code); |
|
QNetworkReply* reply(qobject_cast<QNetworkReply*>(sender())); |
|
_downloads[reply].error += |
|
tr("<h1>Network Error</h1>" |
|
"<dl><dt>URL:</dt><dd>%1</dd>" |
|
"<dt>Error Code:</dt><dd>%3</dd>" |
|
"<dt>Error Details:</dt><dd>%2</dd></dl>") |
|
.arg(reply->url().toString()) |
|
.arg(networkError(code)) |
|
.arg(code); |
|
} |
|
void slotFinished() { |
|
LOG; |
|
QNetworkReply* reply(qobject_cast<QNetworkReply*>(sender())); |
|
if (_downloads[reply].error.size()) |
|
error(_downloads[reply].error); |
|
_downloads.erase(reply); |
|
if (_downloads.size()==0) finished(); |
|
} |
|
void metaDataChanged() { |
|
LOG; |
|
} |
|
void sslErrors(const QList<QSslError> & errors) { |
|
LOG; |
|
QNetworkReply* reply(qobject_cast<QNetworkReply*>(sender())); |
|
for (QList<QSslError>::const_iterator err(errors.begin()); |
|
err!=errors.end(); ++err) { |
|
LOG<<"SSL-Error: "<<(int)err->error()<<": "<<err->errorString(); |
|
LOG<<"Certificate Issuer: " |
|
<<"O="<<err->certificate().issuerInfo(QSslCertificate::Organization) |
|
<<"CN="<<err->certificate().issuerInfo(QSslCertificate::CommonName) |
|
<<"L="<<err->certificate().issuerInfo(QSslCertificate::LocalityName) |
|
<<"OU="<<err->certificate().issuerInfo(QSslCertificate::OrganizationalUnitName) |
|
<<"C="<<err->certificate().issuerInfo(QSslCertificate::CountryName) |
|
<<"ST="<<err->certificate().issuerInfo(QSslCertificate::StateOrProvinceName); |
|
LOG<<"Certificate Subject: " |
|
<<"O="<<err->certificate().subjectInfo(QSslCertificate::Organization) |
|
<<"CN="<<err->certificate().subjectInfo(QSslCertificate::CommonName) |
|
<<"L="<<err->certificate().subjectInfo(QSslCertificate::LocalityName) |
|
<<"OU="<<err->certificate().subjectInfo(QSslCertificate::OrganizationalUnitName) |
|
<<"C="<<err->certificate().subjectInfo(QSslCertificate::CountryName) |
|
<<"ST="<<err->certificate().subjectInfo(QSslCertificate::StateOrProvinceName); |
|
LOG<<"Certificate:\n"<<err->certificate().toPem(); |
|
_downloads[reply].error += |
|
tr("<h1>SSL Error</h1>" |
|
"<dl><dt>URL:</dt><dd>%1</dd>" |
|
"<dt>Error Code:</dt><dd>%3</dd>" |
|
"<dt>Error Details:</dt><dd>%2</dd></dl>" |
|
"<table>" |
|
"<caption>Certificate Issuer</caption>" |
|
"<tr><th>Organization:</th><td>%4</td></tr>" |
|
"<tr><th>Common Name:</th><td>%5</td></tr>" |
|
"<tr><th>Location:</th><td>%6</td></tr>" |
|
"<tr><th>Organizational Unit:</th><td>%7</td></tr>" |
|
"<tr><th>Country:</th><td>%8</td></tr>" |
|
"<tr><th>State or Provive:</th><td>%9</td></tr>" |
|
"</table>" |
|
"<table>" |
|
"<caption>Certificate Subject</caption>" |
|
"<tr><th>Organization:</th><td>%10</td></tr>" |
|
"<tr><th>Common Name:</th><td>%11</td></tr>" |
|
"<tr><th>Location:</th><td>%12</td></tr>" |
|
"<tr><th>Organizational Unit:</th><td>%13</td></tr>" |
|
"<tr><th>Country:</th><td>%14</td></tr>" |
|
"<tr><th>State or Provive:</th><td>%15</td></tr>" |
|
"</table>") |
|
.arg(reply->url().toString()) |
|
.arg(err->errorString()) |
|
.arg(err->error()) |
|
.arg(err->certificate().issuerInfo(QSslCertificate::Organization)) |
|
.arg(err->certificate().issuerInfo(QSslCertificate::CommonName)) |
|
.arg(err->certificate().issuerInfo(QSslCertificate::LocalityName)) |
|
.arg(err->certificate().issuerInfo(QSslCertificate::OrganizationalUnitName)) |
|
.arg(err->certificate().issuerInfo(QSslCertificate::CountryName)) |
|
.arg(err->certificate().issuerInfo(QSslCertificate::StateOrProvinceName)) |
|
.arg(err->certificate().subjectInfo(QSslCertificate::Organization)) |
|
.arg(err->certificate().subjectInfo(QSslCertificate::CommonName)) |
|
.arg(err->certificate().subjectInfo(QSslCertificate::LocalityName)) |
|
.arg(err->certificate().subjectInfo(QSslCertificate::OrganizationalUnitName)) |
|
.arg(err->certificate().subjectInfo(QSslCertificate::CountryName)) |
|
.arg(err->certificate().subjectInfo(QSslCertificate::StateOrProvinceName)); |
|
} |
|
} |
|
|
|
void uploadProgress(qint64 bytesSent, qint64 bytesTotal) { |
|
//LOG<<bytesSent<<bytesTotal; |
|
_downloads[qobject_cast<QNetworkReply*>(sender())].progress.first |
|
= bytesSent; |
|
_downloads[qobject_cast<QNetworkReply*>(sender())].progress.second |
|
= bytesTotal; |
|
calcProgress(); |
|
} |
|
|
|
private: |
|
|
|
typedef std::pair<qint64, qint64> Progress; |
|
struct Download { |
|
Progress progress; |
|
QString error; |
|
}; |
|
typedef std::map<QNetworkReply*, Download> Downloads; |
|
|
|
Downloads _downloads; |
|
};
|
|
|