AusweisApp2/src/file_provider/Downloader.cpp

214 lines
5.7 KiB
C++
Raw Normal View History

2017-12-20 14:54:05 +01:00
/*!
2019-05-22 10:08:38 +02:00
* \copyright Copyright (c) 2016-2019 Governikus GmbH & Co. KG, Germany
2017-12-20 14:54:05 +01:00
*/
#include "Downloader.h"
2019-01-03 15:06:22 +01:00
#include "LogHandler.h"
2017-12-20 14:54:05 +01:00
#include "ScopeGuard.h"
#include "SingletonHelper.h"
#include "TlsChecker.h"
2019-01-03 15:06:22 +01:00
#include <http_parser.h>
2017-12-20 14:54:05 +01:00
#include <QFile>
#include <QLocale>
#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(network)
Q_DECLARE_LOGGING_CATEGORY(fileprovider)
using namespace governikus;
defineSingleton(Downloader)
Downloader & Downloader::getInstance()
{
return *Instance;
}
void Downloader::scheduleDownload(QSharedPointer<QNetworkRequest> request)
{
mPendingRequests.enqueue(request);
startDownloadIfPending();
}
void Downloader::startDownloadIfPending()
{
if (mCurrentReply)
{
qCDebug(fileprovider) << "A download is already in progress... delaying.";
return;
}
if (mPendingRequests.isEmpty())
{
qCDebug(fileprovider) << "No pending requests to be started.";
return;
}
mCurrentRequest = mPendingRequests.dequeue();
mCurrentReply = Env::getSingleton<NetworkManager>()->get(*mCurrentRequest);
connect(mCurrentReply, &QNetworkReply::sslErrors, this, &Downloader::onSslErrors);
connect(mCurrentReply, &QNetworkReply::encrypted, this, &Downloader::onSslHandshakeDone);
connect(mCurrentReply, &QNetworkReply::metaDataChanged, this, &Downloader::onMetadataChanged);
connect(mCurrentReply, &QNetworkReply::finished, this, &Downloader::onNetworkReplyFinished);
}
void Downloader::onSslErrors(const QList<QSslError>& pErrors)
{
TlsChecker::containsFatalError(mCurrentReply, pErrors);
}
void Downloader::onSslHandshakeDone()
{
const auto& cfg = mCurrentReply->sslConfiguration();
2019-01-03 15:06:22 +01:00
TlsChecker::logSslConfig(cfg, spawnMessageLogger(network));
2017-12-20 14:54:05 +01:00
if (!Env::getSingleton<NetworkManager>()->checkUpdateServerCertificate(*mCurrentReply))
{
const QString& textForLog = mCurrentRequest->url().fileName();
2019-05-22 10:08:38 +02:00
qCCritical(fileprovider).nospace() << "Untrusted certificate found [" << textForLog << "]: " << cfg.peerCertificate();
2017-12-20 14:54:05 +01:00
mCurrentReply->abort();
}
}
void Downloader::onMetadataChanged()
{
const QString& fileName = mCurrentRequest->url().fileName();
2019-01-03 15:06:22 +01:00
const auto statusCode = mCurrentReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (statusCode == HTTP_STATUS_OK)
2017-12-20 14:54:05 +01:00
{
2019-01-03 15:06:22 +01:00
qCDebug(fileprovider) << "Continue request for" << fileName;
2017-12-20 14:54:05 +01:00
return;
}
2019-01-03 15:06:22 +01:00
qCDebug(fileprovider) << "Abort request for" << fileName;
mCurrentReply->abort();
2017-12-20 14:54:05 +01:00
}
void Downloader::onNetworkReplyFinished()
{
2019-01-03 15:06:22 +01:00
qCDebug(fileprovider) << "Downloader finished:" << mCurrentReply->request().url().fileName();
2017-12-20 14:54:05 +01:00
const ScopeGuard guard([this] {
mCurrentReply->deleteLater();
mCurrentReply = nullptr;
startDownloadIfPending();
});
if (mCurrentRequest.isNull())
{
qCCritical(fileprovider) << "Internal error: no running download request.";
Q_EMIT fireDownloadFailed(mCurrentRequest->url(), GlobalStatus::Code::Network_Other_Error);
return;
}
const QUrl url = mCurrentRequest->url();
const QString& textForLog = mCurrentRequest->url().fileName();
if (!Env::getSingleton<NetworkManager>()->checkUpdateServerCertificate(*mCurrentReply))
{
qCCritical(fileprovider).nospace() << "Connection not secure [" << textForLog << "]";
Q_EMIT fireDownloadFailed(url, GlobalStatus::Code::Network_Ssl_Establishment_Error);
return;
}
2019-01-03 15:06:22 +01:00
const auto statusCode = NetworkManager::getLoggedStatusCode(mCurrentReply, spawnMessageLogger(network));
switch (statusCode)
2017-12-20 14:54:05 +01:00
{
2019-01-03 15:06:22 +01:00
case HTTP_STATUS_OK:
{
QDateTime lastModified = mCurrentReply->header(QNetworkRequest::KnownHeaders::LastModifiedHeader).toDateTime();
if (!lastModified.isValid())
{
qCWarning(fileprovider) << "Server did not provide a valid LastModifiedHeader";
lastModified = QDateTime::currentDateTime();
}
2017-12-20 14:54:05 +01:00
Q_EMIT fireDownloadSuccess(mCurrentRequest->url(), lastModified, mCurrentReply->readAll());
break;
2019-01-03 15:06:22 +01:00
}
2017-12-20 14:54:05 +01:00
2019-01-03 15:06:22 +01:00
case HTTP_STATUS_NOT_MODIFIED:
2017-12-20 14:54:05 +01:00
Q_EMIT fireDownloadUnnecessary(url);
break;
2019-01-03 15:06:22 +01:00
case HTTP_STATUS_NOT_FOUND:
2017-12-20 14:54:05 +01:00
Q_EMIT fireDownloadFailed(url, GlobalStatus::Code::Downloader_File_Not_Found);
break;
default:
if (mCurrentReply->error() != QNetworkReply::NoError)
{
qCCritical(fileprovider).nospace() << mCurrentReply->errorString() << " [" << textForLog << "]";
2019-01-03 15:06:22 +01:00
Q_EMIT fireDownloadFailed(url, NetworkManager::toStatus(mCurrentReply).getStatusCode());
2017-12-20 14:54:05 +01:00
}
else
{
2019-01-03 15:06:22 +01:00
qCCritical(fileprovider).nospace() << "Invalid HTTP status code for [" << textForLog << "]";
2017-12-20 14:54:05 +01:00
Q_EMIT fireDownloadFailed(url, GlobalStatus::Code::Network_Other_Error);
}
}
}
Downloader::Downloader()
: mCurrentRequest()
, mCurrentReply(nullptr)
, mPendingRequests()
{
}
Downloader::~Downloader()
{
if (mCurrentReply != nullptr)
{
if (mCurrentReply->isRunning() && !mCurrentRequest.isNull())
{
const QString& textForLog = mCurrentRequest->url().fileName();
qCDebug(fileprovider).nospace() << "Scheduling pending update request [" << textForLog << "] for deletion";
}
mCurrentReply->deleteLater();
mCurrentReply = nullptr;
}
}
void Downloader::download(const QUrl& pUpdateUrl)
{
qCDebug(fileprovider) << "Download:" << pUpdateUrl;
2019-01-03 15:06:22 +01:00
auto request = QSharedPointer<QNetworkRequest>::create(pUpdateUrl);
2017-12-20 14:54:05 +01:00
scheduleDownload(request);
}
void Downloader::downloadIfNew(const QUrl& pUpdateUrl,
const QDateTime& pCurrentTimestamp)
{
qCDebug(fileprovider) << "Download:" << pUpdateUrl;
2019-01-03 15:06:22 +01:00
auto request = QSharedPointer<QNetworkRequest>::create(pUpdateUrl);
2017-12-20 14:54:05 +01:00
const QString& timeStampString = QLocale::c().toString(pCurrentTimestamp, QStringLiteral("ddd, dd MMM yyyy hh:mm:ss 'GMT'"));
if (!timeStampString.isEmpty())
{
request->setRawHeader(QByteArrayLiteral("If-Modified-Since"), timeStampString.toLatin1());
}
// See Section 14.25 at https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
// Example timestamp string: Sun, 06 Nov 1994 08:49:37 GMT
scheduleDownload(request);
}