Mercurial > trustbridge
view ui/downloader.cpp @ 389:3be838c3e4d8
Handle installation success. Save / load last installed list.
This also adds a feature that you can specifiy the download
resource when release build is not set so that you can
update a list without modifying the server data.
author | Andre Heinecke <andre.heinecke@intevation.de> |
---|---|
date | Tue, 15 Apr 2014 19:05:49 +0200 |
parents | 63b79d135631 |
children | 17e1c8f37d72 |
line wrap: on
line source
#include "downloader.h" #ifndef DOWNLOAD_SERVER #define DOWNLOAD_SERVER "https://www.intevation.de" #endif #include <QFile> #include <QDir> #include <QDebug> #include <QStandardPaths> #include <QLocale> #include <QSaveFile> #include <polarssl/net.h> #include <polarssl/ssl.h> #include <polarssl/entropy.h> #include <polarssl/ctr_drbg.h> #include <polarssl/error.h> #include <polarssl/certs.h> #define MAX_SW_SIZE 10485760 #define MAX_LIST_SIZE 1048576 Downloader::Downloader(QObject* parent, const QString& url, const QByteArray& certificate, const QDateTime& newestSW, const QDateTime& newestList, const QString& resourceSW, const QString& resourceList): QThread(parent), mLastModSW(newestSW), mLastModList(newestList), mResourceSW(resourceSW), mResourceList(resourceList), mSSLConnection(url, certificate) { } Downloader::~Downloader() { } QString Downloader::getDataDirectory() { QString candidate = QStandardPaths::writableLocation(QStandardPaths::DataLocation); if (candidate.isEmpty()) { qDebug() << "Could not find writeable locaction for me"; return QString(); } QDir cDir(candidate); if (!cDir.exists()) { if (!cDir.mkpath(candidate)) { qDebug() << "Could not create path to: " << candidate; return QString(); } } return cDir.absolutePath(); } QMap<QString, QString> Downloader::parseHeaders(QByteArray *data) { int bodyStart = data->indexOf("\r\n\r\n"); QMap<QString, QString> retval; QByteArray headers; QString response(*data); if (bodyStart == -1) { qDebug() << "Could not find header end."; emit error(tr("Invalid response"), SSLConnection::InvalidResponse); return retval; } /* Take the headers with one additional line break */ headers = data->left(bodyStart + 2); /* Chop off the head */ foreach (const QString& line, response.split("\r\n")) { int sepPos = -1; sepPos = line.indexOf(": "); if (sepPos == -1) { continue; } QString key = line.left(sepPos); QString value = line.right(line.size() - sepPos - 2); retval.insert(key, value); } *data = data->right(data->size() - bodyStart - 4); return retval; } QDateTime Downloader::getLastModifiedHeader(const QString &resource) { int ret = -1; QByteArray response; QLocale cLocale = QLocale::c(); QMap<QString, QString> headers; QString headRequest = QString::fromLatin1("HEAD %1 HTTP/1.0\r\n\r\n").arg(resource); ret = mSSLConnection.write(headRequest.toUtf8()); if (ret != 0) { emit error (tr("Connection lost"), SSLConnection::ConnectionLost); return QDateTime(); } response = mSSLConnection.read(1024); if (response.isNull()) { qDebug() << "No response"; emit error (tr("Connection lost"), SSLConnection::ConnectionLost); return QDateTime(); } headers = parseHeaders(&response); const QString lastModified = headers.value("Last-Modified"); qDebug() << "Headers: " << headers; if (!lastModified.isEmpty()) { QDateTime candidate = cLocale.toDateTime(lastModified, "ddd, dd MMM yyyy HH:mm:ss' GMT'"); if (candidate.isValid()) { return candidate; } } emit error (tr("Invalid response from the server"), SSLConnection::InvalidResponse); qDebug() << "Response from server was: " << response; return QDateTime(); } bool Downloader::downloadFile(const QString &resource, const QString &fileName, size_t maxSize) { int ret = -1; size_t bytesRead = 0; QString getRequest = QString::fromLatin1("GET %1 HTTP/1.0\r\n\r\n").arg(resource); QSaveFile outputFile(fileName); ret = mSSLConnection.write(getRequest.toUtf8()); // Open / Create the file to write to. if (!outputFile.open(QIODevice::WriteOnly)) { qDebug() << "Failed to open file"; return false; } if (ret != 0) { emit error(tr("Connection lost"), SSLConnection::ConnectionLost); return false; } bool inBody = false; QMap <QString, QString> headers; do { /* Read the response in 8KiB chunks */ int responseSize = 0; QByteArray response = mSSLConnection.read(8192); if (response.isNull()) { qDebug() << "Error reading response"; emit error(tr("Connection lost"), SSLConnection::ConnectionLost); return false; } responseSize = response.size(); if (!inBody) { headers = parseHeaders(&response); inBody = true; } outputFile.write(response); bytesRead += responseSize; if (responseSize < 8192) { /* Nothing more to read */ break; } /* TODO Emit progress */ } while (bytesRead < maxSize); return outputFile.commit(); } void Downloader::run() { int ret; QDateTime remoteModList; QDateTime remoteModSW; if (!mSSLConnection.initialized()) { emit error(tr("Failed to initialize SSL Module."), SSLConnection::ErrUnknown); return; } ret = mSSLConnection.connect(); if (ret != 0) { emit error(tr("Failed to connect."), mSSLConnection.getLastError()); return; } emit progress(tr("Connected"), 1, -1); remoteModSW = getLastModifiedHeader(mResourceSW); remoteModList = getLastModifiedHeader(mResourceList); if (!remoteModSW.isValid() || !remoteModList.isValid()) { qDebug() << "Could not parse headers"; return; } if (!mLastModSW.isValid() || remoteModSW > mLastModSW) { QString dataDirectory = getDataDirectory(); if (dataDirectory.isEmpty()) { qDebug() << "Failed to get data directory"; return; } QString fileName = dataDirectory.append("/SW-") .append(remoteModSW.toString("yyyymmddHHmmss")) .append(".exe"); qDebug() << "fileName: " << fileName; if (!downloadFile(mResourceSW, fileName, MAX_SW_SIZE)) { return; } emit newSoftwareAvailable(fileName, remoteModSW); } else if (!mLastModList.isValid() || remoteModList > mLastModList) { QString dataDirectory = getDataDirectory(); if (dataDirectory.isEmpty()) { qDebug() << "Failed to get data directory"; return; } QString fileName = dataDirectory.append("/list-") .append(remoteModSW.toString("yyyymmddHHmmss")) .append(".txt"); qDebug() << "fileName: " << fileName; if (!downloadFile(mResourceList, fileName, MAX_LIST_SIZE)) { return; } emit newListAvailable(fileName, remoteModList); } emit progress(tr("Closing"), 1, -1); }