|
|
|
/*! @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. The sslErrors() signal should"
|
|
|
|
" have been emitted.");
|
|
|
|
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();
|
|
|
|
|
|
|
|
public Q_SLOTS:
|
|
|
|
|
|
|
|
void add(QNetworkReply* reply) {
|
|
|
|
LOG<<reply->url().toString();
|
|
|
|
_downloads[reply] = 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.first;
|
|
|
|
total += it->second.second;
|
|
|
|
}
|
|
|
|
progress(done, total);
|
|
|
|
}
|
|
|
|
|
|
|
|
private Q_SLOTS:
|
|
|
|
|
|
|
|
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
|
|
|
|
//LOG<<bytesReceived<<bytesTotal;
|
|
|
|
_downloads[qobject_cast<QNetworkReply*>(sender())].first = bytesReceived;
|
|
|
|
_downloads[qobject_cast<QNetworkReply*>(sender())].second = bytesTotal;
|
|
|
|
calcProgress();
|
|
|
|
}
|
|
|
|
void error(QNetworkReply::NetworkError code) {
|
|
|
|
LOG<<"Status:"<<networkError(code);
|
|
|
|
}
|
|
|
|
void slotFinished() {
|
|
|
|
LOG;
|
|
|
|
_downloads.erase(qobject_cast<QNetworkReply*>(sender()));
|
|
|
|
if (_downloads.size()==0) finished();
|
|
|
|
}
|
|
|
|
void metaDataChanged() {
|
|
|
|
LOG;
|
|
|
|
}
|
|
|
|
void sslErrors(const QList<QSslError> & errors) {
|
|
|
|
LOG;
|
|
|
|
qobject_cast<QNetworkReply*>(sender())->ignoreSslErrors(errors);
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void uploadProgress(qint64 bytesSent, qint64 bytesTotal) {
|
|
|
|
//LOG<<bytesSent<<bytesTotal;
|
|
|
|
_downloads[qobject_cast<QNetworkReply*>(sender())].first = bytesSent;
|
|
|
|
_downloads[qobject_cast<QNetworkReply*>(sender())].second = bytesTotal;
|
|
|
|
calcProgress();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
typedef std::pair<qint64, qint64> Progress;
|
|
|
|
typedef std::map<QNetworkReply*, Progress> Downloads;
|
|
|
|
|
|
|
|
Downloads _downloads;
|
|
|
|
};
|