summaryrefslogtreecommitdiffstats
path: root/src/libs/installer/downloadarchivesjob.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/installer/downloadarchivesjob.cpp')
-rw-r--r--src/libs/installer/downloadarchivesjob.cpp340
1 files changed, 340 insertions, 0 deletions
diff --git a/src/libs/installer/downloadarchivesjob.cpp b/src/libs/installer/downloadarchivesjob.cpp
new file mode 100644
index 000000000..f33108442
--- /dev/null
+++ b/src/libs/installer/downloadarchivesjob.cpp
@@ -0,0 +1,340 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#include "downloadarchivesjob.h"
+
+#include "binaryformatenginehandler.h"
+#include "component.h"
+#include "messageboxhandler.h"
+#include "packagemanagercore.h"
+
+#include "kdupdaterfiledownloader.h"
+#include "kdupdaterfiledownloaderfactory.h"
+
+#include <QtCore/QFile>
+#include <QtCore/QTimerEvent>
+
+using namespace QInstaller;
+using namespace KDUpdater;
+
+
+/*!
+ Creates a new DownloadArchivesJob with \a parent.
+*/
+DownloadArchivesJob::DownloadArchivesJob(PackageManagerCore *core)
+ : KDJob(core),
+ m_core(core),
+ m_downloader(0),
+ m_archivesDownloaded(0),
+ m_archivesToDownloadCount(0),
+ m_canceled(false),
+ m_lastFileProgress(0),
+ m_progressChangedTimerId(0)
+{
+ setCapabilities(Cancelable);
+}
+
+/*!
+ Destroys the DownloadArchivesJob.
+ All temporary files get deleted.
+*/
+DownloadArchivesJob::~DownloadArchivesJob()
+{
+ foreach (const QString &fileName, m_temporaryFiles) {
+ QFile file(fileName);
+ if (file.exists() && !file.remove())
+ qWarning("Could not delete file %s: %s", qPrintable(fileName), qPrintable(file.errorString()));
+ }
+
+ if (m_downloader)
+ m_downloader->deleteLater();
+}
+
+/*!
+ Sets the archives to download. The first value of each pair contains the file name to register
+ the file in the installer's internal file system, the second one the source url.
+*/
+void DownloadArchivesJob::setArchivesToDownload(const QList<QPair<QString, QString> > &archives)
+{
+ m_archivesToDownload = archives;
+ m_archivesToDownloadCount = archives.count();
+}
+
+/*!
+ \reimp
+*/
+void DownloadArchivesJob::doStart()
+{
+ m_archivesDownloaded = 0;
+ fetchNextArchiveHash();
+}
+
+/*!
+ \reimp
+*/
+void DownloadArchivesJob::doCancel()
+{
+ m_canceled = true;
+ if (m_downloader != 0)
+ m_downloader->cancelDownload();
+}
+
+void DownloadArchivesJob::fetchNextArchiveHash()
+{
+ if (m_core->testChecksum()) {
+ if (m_canceled) {
+ finishWithError(tr("Canceled"));
+ return;
+ }
+
+ if (m_archivesToDownload.isEmpty()) {
+ emitFinished();
+ return;
+ }
+
+ if (m_downloader)
+ m_downloader->deleteLater();
+
+ m_downloader = setupDownloader(QLatin1String(".sha1"));
+ if (!m_downloader) {
+ m_archivesToDownload.removeFirst();
+ QMetaObject::invokeMethod(this, "fetchNextArchiveHash", Qt::QueuedConnection);
+ return;
+ }
+
+ connect(m_downloader, SIGNAL(downloadCompleted()), this, SLOT(finishedHashDownload()),
+ Qt::QueuedConnection);
+ m_downloader->download();
+ } else {
+ QMetaObject::invokeMethod(this, "fetchNextArchive", Qt::QueuedConnection);
+ }
+}
+
+void DownloadArchivesJob::finishedHashDownload()
+{
+ Q_ASSERT(m_downloader != 0);
+
+ const QString tempFile = m_downloader->downloadedFileName();
+ QFile sha1HashFile(tempFile);
+ if (sha1HashFile.open(QFile::ReadOnly))
+ m_currentHash = sha1HashFile.readAll();
+ else
+ finishWithError(tr("Downloading hash signature failed."));
+
+ m_temporaryFiles.insert(tempFile);
+
+ fetchNextArchive();
+}
+
+/*!
+ Fetches the next archive and registers it in the installer.
+*/
+void DownloadArchivesJob::fetchNextArchive()
+{
+ if (m_canceled) {
+ finishWithError(tr("Canceled"));
+ return;
+ }
+
+ if (m_archivesToDownload.isEmpty()) {
+ emitFinished();
+ return;
+ }
+
+ if (m_downloader != 0)
+ m_downloader->deleteLater();
+
+ m_downloader = setupDownloader();
+ if (!m_downloader) {
+ m_archivesToDownload.removeFirst();
+ QMetaObject::invokeMethod(this, "fetchNextArchive", Qt::QueuedConnection);
+ return;
+ }
+
+ emit progressChanged(double(m_archivesDownloaded) / m_archivesToDownloadCount);
+ connect(m_downloader, SIGNAL(downloadProgress(double)), this, SLOT(emitDownloadProgress(double)));
+ connect(m_downloader, SIGNAL(downloadCompleted()), this, SLOT(registerFile()), Qt::QueuedConnection);
+
+ m_downloader->download();
+}
+
+/*!
+ Emits the global download progress during a single download in a lazy way (uses a timer to reduce to
+ much processChanged).
+*/
+void DownloadArchivesJob::emitDownloadProgress(double progress)
+{
+ m_lastFileProgress = progress;
+ if (!m_progressChangedTimerId)
+ m_progressChangedTimerId = startTimer(5);
+}
+
+/*!
+ This is used to reduce the progressChanged signals.
+*/
+void DownloadArchivesJob::timerEvent(QTimerEvent *event)
+{
+ if (event->timerId() == m_progressChangedTimerId) {
+ killTimer(m_progressChangedTimerId);
+ m_progressChangedTimerId = 0;
+ emit progressChanged((double(m_archivesDownloaded) + m_lastFileProgress) / m_archivesToDownloadCount);
+ }
+}
+
+/*!
+ Registers the just downloaded file in the intaller's file system.
+*/
+void DownloadArchivesJob::registerFile()
+{
+ Q_ASSERT(m_downloader != 0);
+
+ ++m_archivesDownloaded;
+ if (m_progressChangedTimerId) {
+ killTimer(m_progressChangedTimerId);
+ m_progressChangedTimerId = 0;
+ emit progressChanged(double(m_archivesDownloaded) / m_archivesToDownloadCount);
+ }
+
+ const QString tempFile = m_downloader->downloadedFileName();
+ if (m_core->testChecksum()) {
+ QFile archiveFile(tempFile);
+ if (archiveFile.open(QFile::ReadOnly)) {
+ static QByteArray buffer(1024 * 1024, '\0');
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ while (true) {
+ const qint64 numRead = archiveFile.read(buffer.data(), buffer.size());
+ if (numRead <= 0)
+ break;
+ hash.addData(buffer.constData(), numRead);
+ }
+
+ const QByteArray archiveHash = hash.result().toHex();
+ if ((archiveHash != m_currentHash) && (!m_canceled)) {
+ //TODO: Maybe we should try to download the file again automatically
+ const QMessageBox::Button res =
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("DownloadError"), tr("Download Error"), tr("Hash verification while "
+ "downloading failed. This is a temporary error, please retry."),
+ QMessageBox::Retry | QMessageBox::Cancel, QMessageBox::Cancel);
+
+ if (res == QMessageBox::Cancel) {
+ finishWithError(tr("Could not verify Hash"));
+ return;
+ }
+
+ fetchNextArchiveHash();
+ return;
+ }
+ } else {
+ finishWithError(tr("Could not open %1").arg(tempFile));
+ }
+ }
+
+ m_temporaryFiles.insert(tempFile);
+ const QPair<QString, QString> pair = m_archivesToDownload.takeFirst();
+ QInstallerCreator::BinaryFormatEngineHandler::instance()->registerArchive(pair.first, tempFile);
+
+ fetchNextArchiveHash();
+}
+
+void DownloadArchivesJob::downloadCanceled()
+{
+ emitFinishedWithError(KDJob::Canceled, m_downloader->errorString());
+}
+
+void DownloadArchivesJob::downloadFailed(const QString &error)
+{
+ if (m_canceled)
+ return;
+
+ const QMessageBox::StandardButton b =
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("archiveDownloadError"), tr("Download Error"), tr("Could not download archive: %1 : %2")
+ .arg(m_archivesToDownload.first().second, error), QMessageBox::Retry | QMessageBox::Cancel);
+
+ if (b == QMessageBox::Retry)
+ QMetaObject::invokeMethod(this, "fetchNextArchiveHash", Qt::QueuedConnection);
+ else
+ downloadCanceled();
+}
+
+void DownloadArchivesJob::finishWithError(const QString &error)
+{
+ const FileDownloader *const dl = dynamic_cast<const FileDownloader*> (sender());
+ const QString msg = tr("Could not fetch archives: %1\nError while loading %2");
+ if (dl != 0)
+ emitFinishedWithError(QInstaller::DownloadError, msg.arg(error, dl->url().toString()));
+ else
+ emitFinishedWithError(QInstaller::DownloadError, msg.arg(error, m_downloader->url().toString()));
+}
+
+KDUpdater::FileDownloader *DownloadArchivesJob::setupDownloader(const QString &prefix)
+{
+ KDUpdater::FileDownloader *downloader = 0;
+ const QFileInfo fi = QFileInfo(m_archivesToDownload.first().first);
+ const Component *const component = m_core->componentByName(QFileInfo(fi.path()).fileName());
+ if (component) {
+ const QUrl url(m_archivesToDownload.first().second + prefix);
+ const QString &scheme = url.scheme();
+ downloader = FileDownloaderFactory::instance().create(scheme, this);
+
+ if (downloader) {
+ downloader->setUrl(url);
+ downloader->setAutoRemoveDownloadedFile(false);
+
+ QAuthenticator auth;
+ auth.setUser(component->value(QLatin1String("username")));
+ auth.setPassword(component->value(QLatin1String("password")));
+ downloader->setAuthenticator(auth);
+
+ connect(downloader, SIGNAL(downloadCanceled()), this, SLOT(downloadCanceled()));
+ connect(downloader, SIGNAL(downloadAborted(QString)), this, SLOT(downloadFailed(QString)),
+ Qt::QueuedConnection);
+ connect(downloader, SIGNAL(downloadStatus(QString)), this, SIGNAL(downloadStatusChanged(QString)));
+
+ if (scheme == QLatin1String("http") || scheme == QLatin1String("ftp") ||
+ scheme == QLatin1String("file")) {
+ downloader->setDownloadedFileName(component->localTempPath() + QLatin1String("/")
+ + component->name() + QLatin1String("/") + fi.fileName() + prefix);
+ }
+
+ QString message = tr("Downloading archive hash for component: %1");
+ if (prefix.isEmpty())
+ message = tr("Downloading archive for component: %1");
+ emit outputTextChanged(message.arg(component->displayName()));
+ } else {
+ emit outputTextChanged(tr("Scheme not supported: %1 (%2)").arg(scheme, url.toString()));
+ }
+ } else {
+ emit outputTextChanged(tr("Could not find component for: %1.").arg(QFileInfo(fi.path()).fileName()));
+ }
+ return downloader;
+}