A simple Qt based browser with no bullshit that supports PKCS#11 tokens (such as the SuisseID).
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.
 
 
 
 

398 lines
16 KiB

/*! @file
@id $Id$
*/
// 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
#ifndef __DOWNLOAD_MANAGER_HXX
#define __DOWNLOAD_MANAGER_HXX
#include <qbrowserlib/log.hxx>
#include <qbrowserlib/sslclientnetworkmanager.hxx>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QSslError>
#include <QtNetwork/QSslConfiguration>
#include <map>
#include <cassert>
//! @addtogroup qbrowserlib
//! @{
namespace qbrowserlib {
class DownloadManager: public QObject {
Q_OBJECT;
public:
//! Append a Network Manager
DownloadManager& operator+=(QNetworkAccessManager* n) {
TRC;
SslClientAuthNetworkAccessManager* n2
(dynamic_cast<SslClientAuthNetworkAccessManager*>(n));
return operator+=(n2);
}
//! Append a Network Manager
DownloadManager& operator+=(SslClientAuthNetworkAccessManager* n) {
TRC;
assert(connect(n, SIGNAL(created(QNetworkReply*)),
SLOT(add(QNetworkReply*))));
return *this;
}
//! Append a Network Reply
DownloadManager& operator+=(QNetworkReply* reply) {
TRC;
add(reply);
return *this;
}
static QString networkError(QNetworkReply::NetworkError err) {
TRC_FN; LOG_FN<<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("Unknown network error (code: %1).").arg(err);
}
}
Q_SIGNALS:
void progress(qint64 done, qint64 total);
void started();
void finished();
void error(QString);
void metaDataChanged(QNetworkReply*);
public Q_SLOTS:
void add(QNetworkReply* reply) {
TRC; LOG<<_downloads.size()<<reply->url().toString();
LOG<<reply;
_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(destroyed(QObject*)),
SLOT(slotDestroyed(QObject*))));
assert(connect(reply, SIGNAL(finished()),
SLOT(slotFinished())));
assert(connect(reply, SIGNAL(metaDataChanged()),
SLOT(slotMetaDataChanged())));
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() {
//TRC;
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) {
//TRC; 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) {
TRC; 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 slotDestroyed(QObject* obj) {
TRC; LOG<<_downloads.size();
_downloads.erase((QNetworkReply*)obj);
}
void slotFinished() {
TRC; LOG<<_downloads.size();
QNetworkReply* reply(qobject_cast<QNetworkReply*>(sender()));
if (_downloads[reply].error.size())
error(_downloads[reply].error);
_downloads.erase(reply);
if (_downloads.size()==0) finished();
}
void slotMetaDataChanged() {
TRC;
QNetworkReply* reply(qobject_cast<QNetworkReply*>(sender()));
if (!reply) return;
LOG<<"Location:"<<reply->header(QNetworkRequest::LocationHeader)
.toString();
LOG<<"Content-Type:"<<reply->header(QNetworkRequest::ContentTypeHeader)
.toString();
LOG<<"Content-Disposition:"<<reply->rawHeader("Content-Disposition");
LOG<<"Status:"<<networkError(reply->error());
LOG<<"URL:"<<reply->url().toString();
LOG<<"File:"<<reply->url().toLocalFile();
LOG<<"Path:"<<reply->url().path();
metaDataChanged(reply);
}
void sslErrors(const QList<QSslError> & errors) {
TRC;
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();
switch (err->error()) {
case QSslError::SelfSignedCertificate:
case QSslError::SelfSignedCertificateInChain: {
QSslConfiguration sslConfig
(QSslConfiguration::defaultConfiguration());
QList<QSslCertificate> certs(sslConfig.caCertificates());
for (QList<QSslCertificate>::iterator cert(certs.begin());
cert!=certs.end(); ++cert) {
if (err->certificate().subjectInfo(QSslCertificate::CommonName)
== cert->subjectInfo(QSslCertificate::CommonName)) {
LOG<<"Found matching CN:"
<<cert->subjectInfo(QSslCertificate::CommonName);
if (err->certificate()==*cert) {
LOG<<"QT-BUG! Certificate matches known certificate";
//! @bug work around qt bug (Qt Mac 4.8.2)
reply->ignoreSslErrors(errors);
} else {
LOG<<"CERTIFICATE ERROR! Certificates are different";
}
}
}
} break;
default:; // ignore
}
_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 Province:</th><td>%15</td></tr>"
"</table>")
.arg(reply->url().toString())
.arg(err->errorString())
.arg(err->error())
.arg(err->certificate().issuerInfo(QSslCertificate::Organization)
#if QT_VERSION >= 0x050000
.join("<br/>")
#endif
)
.arg(err->certificate().issuerInfo(QSslCertificate::CommonName)
#if QT_VERSION >= 0x050000
.join("<br/>")
#endif
)
.arg(err->certificate().issuerInfo(QSslCertificate::LocalityName)
#if QT_VERSION >= 0x050000
.join("<br/>")
#endif
)
.arg(err->certificate()
.issuerInfo(QSslCertificate::OrganizationalUnitName)
#if QT_VERSION >= 0x050000
.join("<br/>")
#endif
)
.arg(err->certificate().issuerInfo(QSslCertificate::CountryName)
#if QT_VERSION >= 0x050000
.join("<br/>")
#endif
)
.arg(err->certificate()
.issuerInfo(QSslCertificate::StateOrProvinceName)
#if QT_VERSION >= 0x050000
.join("<br/>")
#endif
)
.arg(err->certificate().subjectInfo(QSslCertificate::Organization)
#if QT_VERSION >= 0x050000
.join("<br/>")
#endif
)
.arg(err->certificate().subjectInfo(QSslCertificate::CommonName)
#if QT_VERSION >= 0x050000
.join("<br/>")
#endif
)
.arg(err->certificate().subjectInfo(QSslCertificate::LocalityName)
#if QT_VERSION >= 0x050000
.join("<br/>")
#endif
)
.arg(err->certificate()
.subjectInfo(QSslCertificate::OrganizationalUnitName)
#if QT_VERSION >= 0x050000
.join("<br/>")
#endif
)
.arg(err->certificate().subjectInfo(QSslCertificate::CountryName)
#if QT_VERSION >= 0x050000
.join("<br/>")
#endif
)
.arg(err->certificate()
.subjectInfo(QSslCertificate::StateOrProvinceName)
#if QT_VERSION >= 0x050000
.join("<br/>")
#endif
);
}
}
void uploadProgress(qint64 bytesSent, qint64 bytesTotal) {
//TRC; 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;
};
}
//! @}
#endif