summaryrefslogtreecommitdiffstats
path: root/installerbuilder/libinstaller/downloadarchivesjob.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'installerbuilder/libinstaller/downloadarchivesjob.cpp')
-rw-r--r--installerbuilder/libinstaller/downloadarchivesjob.cpp341
1 files changed, 341 insertions, 0 deletions
diff --git a/installerbuilder/libinstaller/downloadarchivesjob.cpp b/installerbuilder/libinstaller/downloadarchivesjob.cpp
new file mode 100644
index 000000000..3365a317e
--- /dev/null
+++ b/installerbuilder/libinstaller/downloadarchivesjob.cpp
@@ -0,0 +1,341 @@
+/**************************************************************************
+**
+** This file is part of Qt SDK**
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).*
+**
+** Contact: Nokia Corporation qt-info@nokia.com**
+**
+** No Commercial Usage
+**
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.
+**
+** If you are unsure which license is appropriate for your use, please contact
+** (qt-info@nokia.com).
+**
+**************************************************************************/
+#include "downloadarchivesjob.h"
+
+#include <QFile>
+#include <QTimer>
+#include <QMessageBox>
+#include <QDebug>
+#include <QTimerEvent>
+
+#include <KDUpdater/FileDownloader>
+#include <KDUpdater/FileDownloaderFactory>
+
+#include "common/binaryformatenginehandler.h"
+#include "common/utils.h"
+
+#include "cryptosignatureverifier.h"
+#include "qinstaller.h"
+#include "qinstallercomponent.h"
+#include "messageboxhandler.h"
+
+using namespace QInstaller;
+using namespace KDUpdater;
+
+/*
+TRANSLATOR QInstaller::DownloadArchivesJob
+*/
+
+/**
+ * Creates a new DownloadArchivesJob with \a parent.
+ */
+DownloadArchivesJob::DownloadArchivesJob(const QByteArray& publicKey, Installer* parent )
+ : KDJob( parent ),
+ cancelled( false ),
+ publicKey( publicKey ),
+ downloader( 0 ),
+ filesToDownload( 0 ),
+ filesDownloaded( 0 ),
+ parent( parent ),
+ lastFileProgress(0),
+ progressChangedTimerId(0)
+{
+ setCapabilities( Cancelable );
+}
+
+/**
+ * Destroys the DownloadArchivesJob.
+ * All temporary files get deleted.
+ */
+DownloadArchivesJob::~DownloadArchivesJob()
+{
+ for( QStringList::const_iterator it = temporaryFiles.constBegin(); it != temporaryFiles.constEnd(); ++it ) {
+ QFile f( *it );
+ if ( !f.remove() )
+ qWarning( "Could not delete file %s: %s", qPrintable(*it), qPrintable(f.errorString()) );
+ }
+ if( downloader != 0 )
+ 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 > >& archs )
+{
+ filesToDownload = archs.count();
+ archives = archs;
+}
+
+/**
+ * \reimp
+ */
+void DownloadArchivesJob::doStart()
+{
+ filesDownloaded = 0;
+ fetchNextArchiveHash();
+}
+
+/**
+ * \reimp
+ */
+void DownloadArchivesJob::doCancel()
+{
+ cancelled = true;
+ if( downloader != 0 )
+ downloader->cancelDownload();
+}
+
+void DownloadArchivesJob::fetchNextArchiveHash()
+{
+ if ( parent->testChecksum() )
+ {
+ if( cancelled )
+ {
+ finishWithError( tr( "Canceled" ) );
+ return;
+ }
+
+ if( archives.isEmpty() )
+ {
+ emitFinished();
+ return;
+ }
+
+ if( downloader != 0 )
+ downloader->deleteLater();
+
+ QString hashUrl = archives.first().second;
+ hashUrl += QLatin1String( ".sha1" );
+ const QUrl url( hashUrl );
+ const QUrl sigUrl = publicKey.isEmpty() ? QUrl() : QUrl( archives.first().second + QLatin1String( ".sig" ) );
+ const CryptoSignatureVerifier verifier( publicKey );
+ downloader = FileDownloaderFactory::instance().create( url.scheme(), publicKey.isEmpty() ? 0 : &verifier, sigUrl, this );
+
+ if ( !downloader ) {
+ archives.pop_front();
+ const QString errMsg = tr("Scheme not supported: %1 (%2)").arg( url.scheme(), url.toString() );
+ verbose() << errMsg << std::endl;
+ emit outputTextChanged( errMsg );
+ QMetaObject::invokeMethod( this, "fetchNextArchiveHash", Qt::QueuedConnection );
+ return;
+ }
+
+ connect( downloader, SIGNAL( downloadCompleted() ), this, SLOT( finishedHashDownload() ), Qt::QueuedConnection );
+ connect( downloader, SIGNAL( downloadCanceled() ), this, SLOT( downloadCanceled() ) );
+ connect( downloader, SIGNAL( downloadAborted( QString ) ), this, SLOT( downloadFailed( QString ) ), Qt::QueuedConnection );
+ //hashes are not registered as files - so we can't handle this as a normal progress
+ //connect( downloader, SIGNAL( downloadProgress( double ) ), this, SLOT( emitDownloadProgress( double ) ) );
+ downloader->setUrl( url );
+ downloader->setAutoRemoveDownloadedFile( false );
+
+ const QString comp = QFileInfo( QFileInfo( archives.first().first ).path() ).fileName();
+ const Component* const component = parent->componentByName( comp );
+
+ emit outputTextChanged( tr( "Downloading archive hash for component %1" ).arg( component->displayName() ) );
+ downloader->download();
+ }
+ else
+ QMetaObject::invokeMethod( this, "fetchNextArchive", Qt::QueuedConnection );
+}
+
+void DownloadArchivesJob::finishedHashDownload()
+{
+ Q_ASSERT( downloader != 0 );
+
+ const QString tempFile = downloader->downloadedFileName();
+ QFile sha1HashFile( tempFile );
+ if ( sha1HashFile.open( QFile::ReadOnly ) )
+ currentHash = sha1HashFile.readAll();
+ else
+ finishWithError( tr( "Downloading hash signature failed" ) );
+
+ temporaryFiles.push_back( tempFile );
+
+ fetchNextArchive();
+}
+
+/**
+ * Fetches the next archive and registers it in the installer.
+ */
+void DownloadArchivesJob::fetchNextArchive()
+{
+ if( cancelled )
+ {
+ finishWithError( tr( "Canceled" ) );
+ return;
+ }
+
+ if( archives.isEmpty() )
+ {
+ emitFinished();
+ return;
+ }
+
+ if( downloader != 0 )
+ downloader->deleteLater();
+
+ const QUrl url( archives.first().second );
+ const QUrl sigUrl = publicKey.isEmpty() ? QUrl() : QUrl( archives.first().second + QLatin1String( ".sig" ) );
+ const CryptoSignatureVerifier verifier( publicKey );
+ downloader = FileDownloaderFactory::instance().create( url.scheme(), publicKey.isEmpty() ? 0 : &verifier, sigUrl, this );
+
+ if ( !downloader ) {
+ archives.pop_front();
+ const QString errMsg = tr("Scheme not supported: %1 (%2)").arg( url.scheme(), url.toString() );
+ verbose() << errMsg;
+ emit outputTextChanged( errMsg );
+ QMetaObject::invokeMethod( this, "fetchNextArchive", Qt::QueuedConnection );
+ return;
+ }
+
+ connect( downloader, SIGNAL( downloadCompleted() ), this, SLOT( registerFile() ), Qt::QueuedConnection );
+ connect( downloader, SIGNAL( downloadCanceled() ), this, SLOT( downloadCanceled() ) );
+ connect( downloader, SIGNAL( downloadAborted( QString ) ), this, SLOT( downloadFailed( QString ) ), Qt::QueuedConnection );
+ connect( downloader, SIGNAL( downloadProgress( double ) ), this, SLOT( emitDownloadProgress( double ) ) );
+ downloader->setUrl( url );
+ downloader->setAutoRemoveDownloadedFile( false );
+
+ const QString archive = QFileInfo( archives.first().first ).fileName();
+ const QString comp = QFileInfo( QFileInfo( archives.first().first ).path() ).fileName();
+ const Component* const component = parent->componentByName( comp );
+
+ emit outputTextChanged( tr( "Downloading archive for component %1" ).arg( component->displayName() ) );
+ emit progressChanged( double(filesDownloaded) / filesToDownload );
+ 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 )
+{
+ lastFileProgress = progress;
+ if (!progressChangedTimerId) {
+ progressChangedTimerId = startTimer(5);
+ }
+}
+
+/**
+ * this is used to reduce the progressChanged signals
+ */
+void DownloadArchivesJob::timerEvent(QTimerEvent *event)
+{
+ if (event->timerId() == progressChangedTimerId) {
+ killTimer(progressChangedTimerId);
+ progressChangedTimerId = 0;
+ emit progressChanged( ( double(filesDownloaded) + lastFileProgress ) / filesToDownload );
+ }
+}
+
+
+/**
+ * Registers the just downloaded file in the intaller's file system.
+ */
+void DownloadArchivesJob::registerFile()
+{
+ Q_ASSERT( downloader != 0 );
+ ++filesDownloaded;
+ if (progressChangedTimerId) {
+ killTimer(progressChangedTimerId);
+ progressChangedTimerId = 0;
+ emit progressChanged( double(filesDownloaded) / filesToDownload );
+ }
+
+ const QString tempFile = downloader->downloadedFileName();
+ QFile archiveFile( tempFile );
+ if ( archiveFile.open( QFile::ReadOnly ) )
+ {
+ if ( parent->testChecksum() )
+ {
+ const QByteArray archiveHash = QCryptographicHash::hash( archiveFile.readAll(), QCryptographicHash::Sha1 ).toHex();
+ if ( archiveHash != currentHash )
+ {
+ const QMessageBox::Button res = MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String( "DownloadError" ),
+ tr( "Download Error" ), tr( "Hash verification failed for %1: Hashes do not match" ).arg( tempFile ),
+ QMessageBox::Retry | QMessageBox::Cancel, QMessageBox::Cancel );
+ if ( res == QMessageBox::Cancel )
+ {
+ finishWithError( tr( "Could not verify Hash") );
+ return;
+ }
+ else
+ {
+ fetchNextArchiveHash();
+ return;
+ }
+ }
+ }
+ }
+ else
+ {
+ finishWithError( tr( "Could not open %1" ).arg( tempFile ) );
+ }
+ const QString path = archives.first().first;
+ archives.pop_front();
+ temporaryFiles.push_back( tempFile );
+
+ QInstallerCreator::BinaryFormatEngineHandler::instance()->registerArchive( path, tempFile );
+
+ fetchNextArchiveHash();
+}
+
+void DownloadArchivesJob::downloadCanceled()
+{
+ emitFinishedWithError( Canceled, downloader->errorString() );
+}
+
+void DownloadArchivesJob::downloadFailed( const QString& error )
+{
+ const QMessageBox::StandardButton b = MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("archiveDownloadError"),
+ tr("Download Error"),
+ tr("Could not download archive %1: %2").arg( archives.first().second, error ),
+ QMessageBox::Retry | QMessageBox::Cancel );
+ if ( b == QMessageBox::Retry )
+ QMetaObject::invokeMethod( this, "fetchNextArchive", Qt::QueuedConnection );
+ else
+ downloadCanceled();
+}
+
+void DownloadArchivesJob::finishWithError( const QString& error )
+{
+ const FileDownloader* const dl = dynamic_cast< const FileDownloader* >( sender() );
+ if( dl != 0 )
+ emitFinishedWithError( DownloadError, tr( "Could not fetch archives: %1\nError while loading %2" ).arg( error, dl->url().toString() ) );
+ else
+ emitFinishedWithError( DownloadError, tr( "Could not fetch archives: %1\nError while loading %2" ).arg( error, downloader->url().toString() ) );
+}