diff options
Diffstat (limited to 'installerbuilder/libinstaller/kdtools/KDUpdater')
64 files changed, 11820 insertions, 0 deletions
diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/Application b/installerbuilder/libinstaller/kdtools/KDUpdater/Application new file mode 100644 index 000000000..7545e9447 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/Application @@ -0,0 +1 @@ +#include "kdupdaterapplication.h" diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/FileDownloader b/installerbuilder/libinstaller/kdtools/KDUpdater/FileDownloader new file mode 100644 index 000000000..beca4f9c1 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/FileDownloader @@ -0,0 +1 @@ +#include "kdupdaterfiledownloader.h" diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/FileDownloaderFactory b/installerbuilder/libinstaller/kdtools/KDUpdater/FileDownloaderFactory new file mode 100644 index 000000000..65dbb7a4e --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/FileDownloaderFactory @@ -0,0 +1 @@ +#include "kdupdaterfiledownloaderfactory.h" diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/KDUpdater b/installerbuilder/libinstaller/kdtools/KDUpdater/KDUpdater new file mode 100644 index 000000000..a327ece5b --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/KDUpdater @@ -0,0 +1,20 @@ +#include "kdupdater.h" +#include "kdupdaterapplication.h" +#include "kdupdatercrypto.h" +#include "kdupdaterfiledownloader.h" +#include "kdupdaterfiledownloaderfactory.h" +#include "kdupdaterpackagesinfo.h" +#include "kdupdaterpackagesview.h" +#include "kdupdatersignatureverificationresult.h" +#include "kdupdatersignatureverificationrunnable.h" +#include "kdupdatersignatureverifier.h" +#include "kdupdatertask.h" +#include "kdupdaterupdate.h" +#include "kdupdaterupdatefinder.h" +#include "kdupdaterupdateinstaller.h" +#include "kdupdaterupdateoperation.h" +#include "kdupdaterupdateoperationfactory.h" +#include "kdupdaterupdateoperations.h" +#include "kdupdaterupdatesdialog.h" +#include "kdupdaterupdatesourcesinfo.h" +#include "kdupdaterupdatesourcesview.h" diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/KDUpdater.pri b/installerbuilder/libinstaller/kdtools/KDUpdater/KDUpdater.pri new file mode 100644 index 000000000..d97399040 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/KDUpdater.pri @@ -0,0 +1,73 @@ +DEPENDPATH += $$PWD +INCLUDEPATH += $$PWD + +CONFIG( shared, static|shared) { + DEFINES += BUILD_SHARED_KDUPDATER +} + +HEADERS += $$PWD/kdupdater.h \ + $$PWD/kdupdaterapplication.h \ + $$PWD/kdupdatercrypto.h \ + $$PWD/kdupdaterfiledownloader.h \ + $$PWD/kdupdaterfiledownloader_p.h \ + $$PWD/kdupdaterfiledownloaderfactory.h \ + $$PWD/kdupdaterpackagesinfo.h \ + $$PWD/kdupdaterpackagesview.h \ + $$PWD/kdupdatersignatureverificationresult.h \ + $$PWD/kdupdatersignatureverifier.h \ + $$PWD/kdupdaterupdate.h \ + $$PWD/kdupdaterupdateoperation.h \ + $$PWD/kdupdaterupdateoperationfactory.h \ + $$PWD/kdupdaterupdateoperations.h \ + $$PWD/kdupdaterupdatesourcesinfo.h \ + $$PWD/kdupdatertask.h \ + $$PWD/kdupdatersignatureverificationrunnable.h \ + $$PWD/kdupdaterupdatefinder.h \ + $$PWD/kdupdaterupdatesinfo_p.h \ + $$PWD/kdupdaterupdateinstaller.h \ + $$PWD/kdupdaterufuncompressor_p.h \ + $$PWD/kdupdaterupdatesdialog.h \ + $$PWD/kdupdaterupdatesourcesview.h \ + $$PWD/kdupdaterufcompresscommon_p.h \ + $$PWD/environment.h + +SOURCES += $$PWD/kdupdaterapplication.cpp \ + $$PWD/kdupdatercrypto.cpp \ + $$PWD/kdupdaterfiledownloader.cpp \ + $$PWD/kdupdaterfiledownloaderfactory.cpp \ + $$PWD/kdupdaterpackagesinfo.cpp \ + $$PWD/kdupdaterpackagesview.cpp \ + $$PWD/kdupdatersignatureverificationresult.cpp \ + $$PWD/kdupdatersignatureverifier.cpp \ + $$PWD/kdupdaterupdate.cpp \ + $$PWD/kdupdaterupdateoperation.cpp \ + $$PWD/kdupdaterupdateoperationfactory.cpp \ + $$PWD/kdupdaterupdateoperations.cpp \ + $$PWD/kdupdaterupdatesourcesinfo.cpp \ + $$PWD/kdupdatertask.cpp \ + $$PWD/kdupdatersignatureverificationrunnable.cpp \ + $$PWD/kdupdaterupdatefinder.cpp \ + $$PWD/kdupdaterupdatesinfo.cpp \ + $$PWD/kdupdaterupdateinstaller.cpp \ + $$PWD/kdupdaterufuncompressor.cpp \ + $$PWD/kdupdaterupdatesdialog.cpp \ + $$PWD/kdupdaterupdatesourcesview.cpp \ + $$PWD/kdupdaterufcompresscommon.cpp \ + $$PWD/environment.cpp + + + +FORMS += $$PWD/updatesdialog.ui \ + $$PWD/addupdatesourcedialog.ui + +DEFINES += KDUPDATERGUITEXTBROWSER \ + KDUPDATERVIEW=QTextBrowser +QT += gui + +TRY_INCLUDEPATHS = /include /usr/include /usr/local/include $$QMAKE_INCDIR $$INCLUDEPATH +win32:TRY_INCLUDEPATHS += $$PWD/../../3rdparty/openssl-0.9.8k/src/include +linux-lsb-g++:TRY_INCLUDEPATHS = $$QMAKE_INCDIR $$INCLUDEPATH +for(p, TRY_INCLUDEPATHS) { + pp = $$join(p, "", "", "/openssl") + exists($$pp):INCLUDEPATH *= $$p +} diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/PackagesInfo b/installerbuilder/libinstaller/kdtools/KDUpdater/PackagesInfo new file mode 100644 index 000000000..8e709882d --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/PackagesInfo @@ -0,0 +1 @@ +#include "kdupdaterpackagesinfo.h" diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/SignatureVerificationResult b/installerbuilder/libinstaller/kdtools/KDUpdater/SignatureVerificationResult new file mode 100644 index 000000000..5a71cdfde --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/SignatureVerificationResult @@ -0,0 +1 @@ +#include "kdupdatersignatureverificationresult.h" diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/SignatureVerifier b/installerbuilder/libinstaller/kdtools/KDUpdater/SignatureVerifier new file mode 100644 index 000000000..3d9d7f946 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/SignatureVerifier @@ -0,0 +1 @@ +#include "kdupdatersignatureverifier.h" diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/Update b/installerbuilder/libinstaller/kdtools/KDUpdater/Update new file mode 100644 index 000000000..8328b020b --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/Update @@ -0,0 +1 @@ +#include "kdupdaterupdate.h" diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/UpdateFinder b/installerbuilder/libinstaller/kdtools/KDUpdater/UpdateFinder new file mode 100644 index 000000000..71666b90d --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/UpdateFinder @@ -0,0 +1 @@ +#include "kdupdaterupdatefinder.h" diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/UpdateOperation b/installerbuilder/libinstaller/kdtools/KDUpdater/UpdateOperation new file mode 100644 index 000000000..fd82bafdb --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/UpdateOperation @@ -0,0 +1 @@ +#include "kdupdaterupdateoperation.h" diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/UpdateOperationFactory b/installerbuilder/libinstaller/kdtools/KDUpdater/UpdateOperationFactory new file mode 100644 index 000000000..bd13c19c5 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/UpdateOperationFactory @@ -0,0 +1 @@ +#include "kdupdaterupdateoperationfactory.h" diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/UpdateSourcesInfo b/installerbuilder/libinstaller/kdtools/KDUpdater/UpdateSourcesInfo new file mode 100644 index 000000000..b422ec594 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/UpdateSourcesInfo @@ -0,0 +1 @@ +#include "kdupdaterupdatesourcesinfo.h" diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/UpdatesDialog b/installerbuilder/libinstaller/kdtools/KDUpdater/UpdatesDialog new file mode 100644 index 000000000..b1fbb7803 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/UpdatesDialog @@ -0,0 +1 @@ +#include "kdupdaterupdatesdialog.h" diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/addupdatesourcedialog.ui b/installerbuilder/libinstaller/kdtools/KDUpdater/addupdatesourcedialog.ui new file mode 100644 index 000000000..c0de05109 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/addupdatesourcedialog.ui @@ -0,0 +1,193 @@ +<ui version="4.0" > + <class>AddUpdateSourceDialog</class> + <widget class="QDialog" name="AddUpdateSourceDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>239</height> + </rect> + </property> + <property name="windowTitle" > + <string>Add Update Source</string> + </property> + <layout class="QVBoxLayout" > + <item> + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>&Name</string> + </property> + <property name="buddy" > + <cstring>txtName</cstring> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLineEdit" name="txtName" > + <property name="font" > + <font> + <pointsize>10</pointsize> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>&Title</string> + </property> + <property name="buddy" > + <cstring>txtTitle</cstring> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QLineEdit" name="txtTitle" > + <property name="font" > + <font> + <pointsize>9</pointsize> + <italic>false</italic> + </font> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="label_3" > + <property name="text" > + <string>&Description</string> + </property> + <property name="alignment" > + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + <property name="buddy" > + <cstring>txtDescription</cstring> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QTextEdit" name="txtDescription" /> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="label_4" > + <property name="text" > + <string>&URL</string> + </property> + <property name="buddy" > + <cstring>txtUrl</cstring> + </property> + </widget> + </item> + <item row="3" column="1" > + <widget class="QLineEdit" name="txtUrl" > + <property name="palette" > + <palette> + <active> + <colorrole role="Text" > + <brush brushstyle="SolidPattern" > + <color alpha="255" > + <red>0</red> + <green>45</green> + <blue>195</blue> + </color> + </brush> + </colorrole> + </active> + <inactive> + <colorrole role="Text" > + <brush brushstyle="SolidPattern" > + <color alpha="255" > + <red>0</red> + <green>45</green> + <blue>195</blue> + </color> + </brush> + </colorrole> + </inactive> + <disabled> + <colorrole role="Text" > + <brush brushstyle="SolidPattern" > + <color alpha="255" > + <red>106</red> + <green>104</green> + <blue>100</blue> + </color> + </brush> + </colorrole> + </disabled> + </palette> + </property> + <property name="font" > + <font> + <underline>true</underline> + </font> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>AddUpdateSourceDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>257</x> + <y>229</y> + </hint> + <hint type="destinationlabel" > + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>AddUpdateSourceDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>325</x> + <y>229</y> + </hint> + <hint type="destinationlabel" > + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/environment.cpp b/installerbuilder/libinstaller/kdtools/KDUpdater/environment.cpp new file mode 100644 index 000000000..c1ddc11f1 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/environment.cpp @@ -0,0 +1,55 @@ +#include "environment.h" + +#include <QHash> +#include <QProcess> +#include <QProcessEnvironment> + +using namespace KDUpdater; + +class Environment::Private { +public: + static Environment* s_instance; + QHash<QString, QString> tempValues; +}; + +Environment* Environment::Private::s_instance = 0; + +Environment::Environment() + : d( new Private ) +{ +} + +Environment::~Environment() { + delete d; +} + +Environment* Environment::instance() { + if ( !Private::s_instance ) + Private::s_instance = new Environment; + return Private::s_instance; +} + +QString Environment::value( const QString& key, const QString& defvalue ) const { + const QHash<QString,QString>::ConstIterator it = d->tempValues.constFind( key ); + if ( it != d->tempValues.constEnd() ) + return *it; + else + return QProcessEnvironment::systemEnvironment().value( key, defvalue ); +} + +void Environment::setTemporaryValue( const QString& key, const QString& value ) { + d->tempValues.insert( key, value ); +} + +QProcessEnvironment Environment::applyTo( const QProcessEnvironment& qpe_ ) const { + QProcessEnvironment qpe( qpe_ ); + QHash<QString, QString>::ConstIterator it = d->tempValues.constBegin(); + const QHash<QString, QString>::ConstIterator end = d->tempValues.constEnd(); + for ( ; it != end; ++it ) + qpe.insert( it.key(), it.value() ); + return qpe; +} + +void Environment::applyTo( QProcess* proc ) { + proc->setProcessEnvironment( applyTo( proc->processEnvironment() ) ); +} diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/environment.h b/installerbuilder/libinstaller/kdtools/KDUpdater/environment.h new file mode 100644 index 000000000..001afbbc2 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/environment.h @@ -0,0 +1,36 @@ +#ifndef LIBINSTALLER_ENVIRONMENT_H +#define LIBINSTALLER_ENVIRONMENT_H + +#include "kdupdaterupdateoperation.h" + +#include <QString> + +class QProcess; +class QProcessEnvironment; + +namespace KDUpdater { + +class KDTOOLS_UPDATER_EXPORT Environment { + public: + static Environment* instance(); + + ~Environment(); + + QString value( const QString& key, const QString& defaultValue=QString() ) const; + void setTemporaryValue( const QString& key, const QString& value ); + + QProcessEnvironment applyTo( const QProcessEnvironment& qpe ) const; + void applyTo( QProcess* process ); + + private: + Environment(); + + private: + Q_DISABLE_COPY(Environment) + class Private; + Private* const d; +}; + +} + +#endif diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdater.h b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdater.h new file mode 100644 index 000000000..9e0007155 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdater.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KD_UPDATER_H +#define KD_UPDATER_H + +#include <QtGlobal> + +#ifdef KDTOOLS_SHARED + #if defined(BUILD_SHARED_KDUPDATER) + #define KDTOOLS_UPDATER_EXPORT Q_DECL_EXPORT + #else + #define KDTOOLS_UPDATER_EXPORT Q_DECL_IMPORT + #endif +#else + #define KDTOOLS_UPDATER_EXPORT +#endif + +namespace KDUpdater +{ + enum Error + { + ENoError = 0, + ECannotStartTask, + ECannotPauseTask, + ECannotResumeTask, + ECannotStopTask, + EUnknown + }; + + enum UpdateType { + PackageUpdate = 0x1, + CompatUpdate = 0x2, + NewPackage = 0x4, + AllUpdate = PackageUpdate | CompatUpdate + }; + Q_DECLARE_FLAGS( UpdateTypes, UpdateType ) + Q_DECLARE_OPERATORS_FOR_FLAGS( UpdateTypes ) + + KDTOOLS_UPDATER_EXPORT int compareVersion(const QString& v1, const QString& v2); +} + +#endif diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterapplication.cpp b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterapplication.cpp new file mode 100644 index 000000000..cf4379c90 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterapplication.cpp @@ -0,0 +1,324 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "kdupdaterapplication.h" +#include "kdupdaterpackagesinfo.h" +#include "kdupdatersignatureverifier.h" +#include "kdupdaterupdatesourcesinfo.h" + +#include <QCoreApplication> +#include <QDebug> +#include <QDir> +#include <QSettings> + +using namespace KDUpdater; + +/*! + \defgroup kdupdater KD Updater + \since_l 2.1 + + "KD Updater" is a library from KDAB that helps in enabling automatic updates for your applications. + All classes belonging to the "KD Updater" library are defined in the \ref KDUpdater namespace. + + TODO: this comes from the former mainpage: +KD Updater is a tool to automatically detect, retrieve, install and activate updates to software +applications and libraries. It is intended to be used with Qt based applications, and developed +against the Qt 4 series. It is a library that users link to their application. It uses only accepted +standard protocols, and does not require any other 3rd party libraries that are not shipped with +Qt. + +KD Updater is generic in that it is not developed for one specific application. The first version is +experimental. If it proves successful and useful, it will be integrated into KDAB's KD Tools +package. It is part of KDAB's strategy to provide functionality missing in Qt that is required for +medium-to-large scale software systems. +*/ + +/*! + \namespace KDUpdater +*/ + +ConfigurationInterface::~ConfigurationInterface() +{ +} + +namespace { + class DefaultConfigImpl : public ConfigurationInterface + { + public: + QVariant value( const QString& key ) const + { + QSettings settings; + settings.beginGroup( QLatin1String("KDUpdater") ); + return settings.value( key ); + } + + void setValue( const QString& key, const QVariant& value ) + { + QSettings settings; + settings.beginGroup( QLatin1String("KDUpdater") ); + settings.setValue( key, value ); + } + }; +} + +/*! + \class KDUpdater::Application kdupdaterapplication.h KDUpdaterApplication + \ingroup kdupdater + \brief This class represents an application that can be updated. + + A KDUpdater application is an application that needs to interact with one or more update servers and + downloads/installs updates This class helps in describing an application in terms of: + \li application Directory + \li packages XML file name and its corresponding KDUpdater::PackagesInfo object + \li update Sources XML file name and its corresponding KDUpdater::UpdateSourcesInfo object + + User can also retrieve some informations from this class: + \li application name + \li application version + \li compat level +*/ + +struct Application::ApplicationData +{ + explicit ApplicationData( ConfigurationInterface* config ) : + packagesInfo(0), + updateSourcesInfo(0), + configurationInterface( config ? config : new DefaultConfigImpl ) + { + const QStringList oldFiles = configurationInterface->value( QLatin1String("FilesForDelayedDeletion") ).toStringList(); + Q_FOREACH( const QString& i, oldFiles ) { //TODO this should happen asnyc and report errors, I guess + QFile f( i ); + if ( f.exists() && !f.remove() ) { + qWarning( "Could not delete file %s: %s", qPrintable(i), qPrintable(f.errorString()) ); + filesForDelayedDeletion << i; // try again next time + } + } + configurationInterface->setValue( QLatin1String("FilesForDelayedDeletion"), filesForDelayedDeletion ); + } + + ~ApplicationData() + { + delete packagesInfo; + delete updateSourcesInfo; + qDeleteAll( verifiers ); + delete configurationInterface; + } + + static Application* instance; + + QString applicationDirectory; + PackagesInfo* packagesInfo; + UpdateSourcesInfo* updateSourcesInfo; + QMap<Application::SignatureTarget, const SignatureVerifier*> verifiers; + QStringList filesForDelayedDeletion; + ConfigurationInterface* configurationInterface; +}; + +Application* Application::ApplicationData::instance = 0; + +/*! + Constructor of the Application class. The class will be constructed and configured to + assume the application directory to be the directory in which the application exists. The + application name is assumed to be QCoreApplication::applicationName() +*/ +Application::Application(ConfigurationInterface* config, QObject* p) : QObject(p) +{ + d = new Application::ApplicationData( config ); + d->packagesInfo = new PackagesInfo(this); + d->updateSourcesInfo = new UpdateSourcesInfo(this); + + setApplicationDirectory( QCoreApplication::applicationDirPath() ); + + ApplicationData::instance = this; +} + +/*! + Destructor +*/ +Application::~Application() +{ + if( this == ApplicationData::instance ) + ApplicationData::instance = 0; + delete d; +} + +/*! + Returns a previousle created Application instance. + */ +Application* Application::instance() +{ + return ApplicationData::instance; +} + +/*! + Changes the applicationDirPath directory to \c dir. Packages.xml and UpdateSources.xml found in the new + application directory will be used. +*/ +void Application::setApplicationDirectory(const QString& dir) +{ + if( d->applicationDirectory == dir ) + return; + + QDir dirObj(dir); + + // FIXME: Perhaps we should check whether dir exists on the local file system or not + d->applicationDirectory = dirObj.absolutePath(); + setPackagesXMLFileName( QString::fromLatin1( "%1/Packages.xml" ).arg(dir) ); + setUpdateSourcesXMLFileName( QString::fromLatin1( "%1/UpdateSources.xml" ).arg(dir) ); +} + +/*! + Returns path to the application directory. +*/ +QString Application::applicationDirectory() const +{ + return d->applicationDirectory; +} + +/*! + Returns the application name. +*/ +QString Application::applicationName() const +{ + if( d->packagesInfo->isValid() ) + return d->packagesInfo->applicationName(); + + return QCoreApplication::applicationName(); +} + +/*! + Returns the application version. +*/ +QString Application::applicationVersion() const +{ + if( d->packagesInfo->isValid() ) + return d->packagesInfo->applicationVersion(); + + return QString(); +} + +const SignatureVerifier* Application::signatureVerifier( SignatureTarget target ) const +{ + return d->verifiers.value( target ); +} + +void Application::setSignatureVerifier( SignatureTarget target, const SignatureVerifier* v ) +{ + delete d->verifiers.value( target ); + d->verifiers.remove( target ); + if ( v ) + d->verifiers.insert( target, v->clone() ); +} + +/*! + Returns the compat level that this application is in. +*/ +int Application::compatLevel() const +{ + if(d->packagesInfo->isValid()) + return d->packagesInfo->compatLevel(); + + return -1; +} + +void Application::addUpdateSource( const QString& name, const QString& title, + const QString& description, const QUrl& url, int priority ) +{ + UpdateSourceInfo info; + info.name = name; + info.title = title; + info.description = description; + info.url = url; + info.priority = priority; + d->updateSourcesInfo->addUpdateSourceInfo( info ); +} + + +/*! + Sets the file name of the Package XML file for this application. By default this is assumed to be + Packages.xml in the application directory. + + \sa KDUpdater::PackagesInfo::setFileName() +*/ +void Application::setPackagesXMLFileName(const QString& fileName) +{ + d->packagesInfo->setFileName( fileName ); +} + +/*! + Returns the Package XML file name. +*/ +QString Application::packagesXMLFileName() const +{ + return d->packagesInfo->fileName(); +} + +/*! + Returns the \ref PackagesInfo object associated with this application. +*/ +PackagesInfo* Application::packagesInfo() const +{ + return d->packagesInfo; +} + +/*! + Sets the file name of the Package XML file for this application. By default this is assumed to be + Packages.xml in the application directory. + + \sa KDUpdater::UpdateSourcesInfo::setFileName() +*/ +void Application::setUpdateSourcesXMLFileName(const QString& fileName) +{ + d->updateSourcesInfo->setFileName( fileName ); +} + +/*! + Returns the Update Sources XML file name. +*/ +QString Application::updateSourcesXMLFileName() const +{ + return d->updateSourcesInfo->fileName(); +} + +/*! + Returns the \ref UpdateSourcesInfo object associated with this application. +*/ +UpdateSourcesInfo* Application::updateSourcesInfo() const +{ + return d->updateSourcesInfo; +} + +void Application::printError( int errorCode, const QString& error ) +{ + qDebug() << errorCode << error; +} + +QStringList Application::filesForDelayedDeletion() const +{ + return d->filesForDelayedDeletion; +} + +void Application::addFilesForDelayedDeletion( const QStringList& files ) +{ + d->filesForDelayedDeletion << files; + d->configurationInterface->setValue( QLatin1String("FilesForDelayedDeletion"), d->filesForDelayedDeletion ); +} diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterapplication.h b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterapplication.h new file mode 100644 index 000000000..3970236d3 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterapplication.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KD_UPDATER_APPLICATION_H +#define KD_UPDATER_APPLICATION_H + +#include "kdupdater.h" +#include <QObject> + +class QUrl; + +namespace KDUpdater +{ + class PackagesInfo; + class SignatureVerifier; + class UpdateSourcesInfo; + + class ConfigurationInterface { + public: + virtual ~ConfigurationInterface(); + virtual QVariant value( const QString& key ) const = 0; + virtual void setValue( const QString& key, const QVariant& value ) = 0; + }; + + class KDTOOLS_UPDATER_EXPORT Application : public QObject + { + Q_OBJECT + + public: + enum SignatureTarget { + Metadata, + Packages + }; + + explicit Application( ConfigurationInterface* config=0, QObject* parent=0); + ~Application(); + + static Application* instance(); + + void setApplicationDirectory(const QString& dir); + QString applicationDirectory() const; + + QString applicationName() const; + QString applicationVersion() const; + int compatLevel() const; + + const SignatureVerifier* signatureVerifier( SignatureTarget target ) const; + void setSignatureVerifier( SignatureTarget target, const SignatureVerifier* verifier ); + + void setPackagesXMLFileName(const QString& fileName); + QString packagesXMLFileName() const; + PackagesInfo* packagesInfo() const; + + void addUpdateSource( const QString& name, const QString& title, + const QString& description, const QUrl& url, int priority = -1 ); + + void setUpdateSourcesXMLFileName(const QString& fileName); + QString updateSourcesXMLFileName() const; + UpdateSourcesInfo* updateSourcesInfo() const; + + QStringList filesForDelayedDeletion() const; + void addFilesForDelayedDeletion( const QStringList& files ); + + public Q_SLOTS: + void printError( int errorCode, const QString& error ); + + private: + struct ApplicationData; + ApplicationData* d; + }; + +} + +#endif diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatercrypto.cpp b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatercrypto.cpp new file mode 100644 index 000000000..8e5832084 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatercrypto.cpp @@ -0,0 +1,743 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "kdupdatercrypto.h" + +#include <openssl/ssl.h> +#include <openssl/rsa.h> + +#include <QCoreApplication> +#include <QCryptographicHash> +#include <QDebug> +#include <QStringList> +#include <QFile> +#include <QMutex> +#include <QLibrary> +#include <QDir> +#include <QTemporaryFile> + +//BEGIN code taken from qsslsocket_openssl_symbols.cpp + +#define DUMMYARG + +#define CLEARFUNC(func) \ + _kd_##func = 0; + +#define RESOLVEFUNC(func) \ + if (!(_kd_##func = _kd_PTR_##func(libs->resolve(#func)))) \ + qWarning("QSslSocket: cannot resolve "#func); + +#define DEFINEFUNC(ret, func, arg, a, err, funcret) \ + typedef ret (*_kd_PTR_##func)(arg); \ + _kd_PTR_##func _kd_##func; \ + ret kd_##func(arg) { \ + Q_ASSERT(_kd_##func); \ + funcret _kd_##func(a); \ + } + +#define DEFINEFUNC3(ret, func, arg1, a, arg2, b, arg3, c, err, funcret) \ + typedef ret (*_kd_PTR_##func)(arg1, arg2, arg3); \ + _kd_PTR_##func _kd_##func; \ + ret kd_##func(arg1, arg2, arg3) { \ + Q_ASSERT(_kd_##func); \ + funcret _kd_##func(a, b, c); \ + } + +#define DEFINEFUNC4(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, err, funcret) \ + typedef ret (*_kd_PTR_##func)(arg1, arg2, arg3, arg4); \ + _kd_PTR_##func _kd_##func; \ + ret kd_##func(arg1, arg2, arg3, arg4) { \ + Q_ASSERT(_kd_##func); \ + funcret _kd_##func(a, b, c, d); \ + } + +#define DEFINEFUNC5(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, err, funcret) \ + typedef ret (*_kd_PTR_##func)(arg1, arg2, arg3, arg4, arg5); \ + _kd_PTR_##func _kd_##func; \ + ret kd_##func(arg1, arg2, arg3, arg4, arg5) { \ + Q_ASSERT(_kd_##func); \ + funcret _kd_##func(a, b, c, d, e); \ + } + +#define DEFINEFUNC6(ret, func, arg1, a, arg2, b, arg3, c, arg4, d, arg5, e, arg6, f, err, funcret) \ + typedef ret (*_kd_PTR_##func)(arg1, arg2, arg3, arg4, arg5, arg6); \ + _kd_PTR_##func _kd_##func; \ + ret kd_##func(arg1, arg2, arg3, arg4, arg5, arg6) { \ + Q_ASSERT(_kd_##func); \ + funcret _kd_##func(a, b, c, d, e, f); \ + } + + +# ifdef Q_OS_UNIX +static bool libGreaterThan(const QString &lhs, const QString &rhs) +{ + QStringList lhsparts = lhs.split(QLatin1Char('.')); + QStringList rhsparts = rhs.split(QLatin1Char('.')); + Q_ASSERT(lhsparts.count() > 1 && rhsparts.count() > 1); + + for (int i = 1; i < rhsparts.count(); ++i) { + if (lhsparts.count() <= i) + // left hand side is shorter, so it's less than rhs + return false; + + bool ok = false; + int b = 0; + int a = lhsparts.at(i).toInt(&ok); + if (ok) + b = rhsparts.at(i).toInt(&ok); + if (ok) { + // both toInt succeeded + if (a == b) + continue; + return a > b; + } else { + // compare as strings; + if (lhsparts.at(i) == rhsparts.at(i)) + continue; + return lhsparts.at(i) > rhsparts.at(i); + } + } + + // they compared strictly equally so far + // lhs cannot be less than rhs + return true; +} +static QStringList findAllLibSsl() +{ + QStringList paths; +# ifdef Q_OS_DARWIN + paths = QString::fromLatin1(qgetenv("DYLD_LIBRARY_PATH")) + .split(QLatin1Char(':'), QString::SkipEmptyParts); +# else + paths = QString::fromLatin1(qgetenv("LD_LIBRARY_PATH")) + .split(QLatin1Char(':'), QString::SkipEmptyParts); +# endif + paths << QLatin1String("/usr/lib") << QLatin1String("/usr/local/lib"); + + QStringList foundSsls; + Q_FOREACH (const QString &path, paths) { + QDir dir = QDir(path); + QStringList entryList = dir.entryList(QStringList() << QLatin1String("libssl.*"), QDir::Files); + + qSort(entryList.begin(), entryList.end(), libGreaterThan); + Q_FOREACH (const QString &entry, entryList) + foundSsls << path + QLatin1Char('/') + entry; + } + + return foundSsls; +} +#endif + +class KDLibraryLoader : public QObject +{ + static QMutex* mutex; +public: + KDLibraryLoader() + { + const QMutexLocker ml( mutex ); + if( tempDir.isEmpty() ) + { + QTemporaryFile file( QDir::temp().absoluteFilePath( QString::fromLatin1( "templibsXXXXXX" ) ) ); + file.open(); + tempDir = file.fileName(); + file.close(); + file.remove(); + } + QDir::temp().mkdir( QFileInfo( tempDir ).fileName() ); + } + + ~KDLibraryLoader() + { + const QMutexLocker ml( mutex ); + for( QVector< QLibrary* >::const_iterator it = loadedLibraries.begin(); it != loadedLibraries.end(); ++it ) + { + QLibrary* const lib = *it; + lib->unload(); + delete lib; + } + + for( QStringList::const_iterator it = temporaryFiles.begin(); it != temporaryFiles.end(); ++it ) + { + QFile file( *it ); + file.setPermissions( file.permissions() | QFile::WriteOwner | QFile::WriteUser ); + file.remove(); + } + + QDir::temp().rmdir( QFileInfo( tempDir ).fileName() ); + } + + bool load( const QString& filename ) + { + const QMutexLocker ml( mutex ); + // does it work out of the box? great! + QLibrary* const lib = new QLibrary; + loadedLibraries.push_back( lib ); + lib->setFileName( filename ); + if( lib->load() ) + return true; + + // if this failed, we copy the filename to a different place + // it might as well come from a resource + const QString realFilename = QFileInfo( QDir( tempDir ), QFileInfo( filename ).fileName() ).absoluteFilePath(); + + QFile::copy( filename, realFilename ); + temporaryFiles.push_back( realFilename ); + + lib->setFileName( realFilename ); + if( lib->load() ) + return true; + + // if all fails... sorry, we can't perform magic. Seriously! + QFile::remove( realFilename ); + temporaryFiles.pop_back(); + + delete lib; + loadedLibraries.pop_back(); + + return false; + } + + template< typename VERSION > + bool load( const QString& filename, VERSION version ) + { + const QMutexLocker ml( mutex ); + // does it work out of the box? great! + QLibrary* const lib = new QLibrary; + loadedLibraries.push_back( lib ); + lib->setFileNameAndVersion( filename, version ); + if( lib->load() ) + return true; + + // if this failed, we copy the filename to a different place + // it might as well come from a resource + const QString realFilename = QFileInfo( QDir( tempDir ), QFileInfo( filename ).fileName() ).absoluteFilePath(); + + QFile::copy( filename, realFilename ); + temporaryFiles.push_back( realFilename ); + + lib->setFileNameAndVersion( realFilename, version ); + if( lib->load() ) + return true; + + // if all fails... sorry, we can't perform magic. Seriously! + QFile::remove( realFilename ); + temporaryFiles.pop_back(); + + delete lib; + loadedLibraries.pop_back(); + + return false; + + } + + void* resolve( const char* symbol ) + { + const QMutexLocker ml( mutex ); + for( QVector< QLibrary* >::const_iterator it = loadedLibraries.begin(); it != loadedLibraries.end(); ++it ) + { + QLibrary* const lib = *it; + void* const ptr = lib->resolve( symbol ); + if( ptr != 0 ) + return ptr; + } + return 0; + } + +private: + QVector< QLibrary* > loadedLibraries; + QStringList temporaryFiles; + static QString tempDir; +}; + +QString KDLibraryLoader::tempDir; + +// TODO: this one get leaked +QMutex* KDLibraryLoader::mutex = new QMutex; + +static KDLibraryLoader* loadOpenSsl() +{ + KDLibraryLoader* result = new KDLibraryLoader; +#ifdef Q_OS_WIN + if( result->load( QLatin1String( ":/openssllibs/libeay32.dll" ) ) && result->load( QLatin1String( ":/openssllibs/libssl32.dll" ) ) ) + return result; + + delete result; + return 0; + +# elif defined(Q_OS_UNIX) + // Try to find the libssl library on the system. + // + // Up until Qt 4.3, this only searched for the "ssl" library at version -1, that + // is, libssl.so on most Unix systems. However, the .so file isn't present in + // user installations because it's considered a development file. + // + // The right thing to do is to load the library at the major version we know how + // to work with: the SHLIB_VERSION_NUMBER version (macro defined in opensslv.h) + // + // However, OpenSSL is a well-known case of binary-compatibility breakage. To + // avoid such problems, many system integrators and Linux distributions change + // the soname of the binary, letting the full version number be the soname. So + // we'll find libssl.so.0.9.7, libssl.so.0.9.8, etc. in the system. For that + // reason, we will search a few common paths (see findAllLibSsl() above) in hopes + // we find one that works. + // + // It is important, however, to try the canonical name and the unversioned name + // without going through the loop. By not specifying a path, we let the system + // dlopen(3) function determine it for us. This will include any DT_RUNPATH or + // DT_RPATH tags on our library header as well as other system-specific search + // paths. See the man page for dlopen(3) on your system for more information. + +#ifdef SHLIB_VERSION_NUMBER + // first attempt: the canonical name is libssl.so.<SHLIB_VERSION_NUMBER> + + if( result->load( QLatin1String( "ssl" ), QLatin1String( SHLIB_VERSION_NUMBER ) ) && + result->load( QLatin1String( "crypto" ), QLatin1String( SHLIB_VERSION_NUMBER ) ) ) { + // libssl.so.<SHLIB_VERSION_NUMBER> and libcrypto.so.<SHLIB_VERSION_NUMBER> found + return result; + } else { + delete result; + result = new KDLibraryLoader; + } +#endif + + // second attempt: find the development files libssl.so and libcrypto.so + if( result->load( QLatin1String( "ssl" ), -1 ) && + result->load( QLatin1String( "crypto" ), -1 ) ) { + // libssl.so.0 and libcrypto.so.0 found + return result; + } else { + delete result; + result = new KDLibraryLoader; + } + + // third attempt: loop on the most common library paths and find libssl + QStringList sslList = findAllLibSsl(); + Q_FOREACH (const QString &ssl, sslList) { + QString crypto = ssl; + crypto.replace(QLatin1String("ssl"), QLatin1String("crypto")); + if( result->load( ssl, -1 ) && + result->load( crypto ), -1 ) { + // libssl.so.0 and libcrypto.so.0 found + return result; + } else { + delete result; + result = new KDLibraryLoader; + } + } + + // failed to load anything + delete result; + return 0; + +# else + // not implemented for this platform yet + return 0; +# endif +} + +//END code taken from qsslsocket_openssl_symbols.cpp + +class KDUpdaterCrypto::Private +{ + public: + KDUpdaterCrypto *q; + + QByteArray m_privateKey; + QByteArray m_privatePassword; + RSA *m_private_key; + BIO *m_private_key_bio; + + QByteArray m_publicKey; + RSA *m_public_key; + BIO *m_public_key_bio; + + KDLibraryLoader* libLoader; + + const PasswordProvider* passwordProvider; + + DEFINEFUNC(int, SSL_library_init, void, DUMMYARG, return -1, return) + DEFINEFUNC(int, BIO_free, BIO *a, a, return 0, return) + DEFINEFUNC(BIO *, BIO_new, BIO_METHOD *a, a, return 0, return) + DEFINEFUNC(BIO_METHOD *, BIO_s_mem, void, DUMMYARG, return 0, return) + DEFINEFUNC3(int, BIO_write, BIO *a, a, const void *b, b, int c, c, return -1, return) + DEFINEFUNC(RSA*, RSA_new, void, DUMMYARG, return 0, return) + DEFINEFUNC(void, RSA_free, RSA *a, a, return, DUMMYARG) + DEFINEFUNC(int, RSA_size, RSA *a, a, return 0, return) + DEFINEFUNC5(int, RSA_public_encrypt, int flen, flen, unsigned char *from, from, unsigned char *to, to, RSA *rsa, rsa, int padding, padding, return -1, return) + DEFINEFUNC5(int, RSA_private_decrypt, int flen, flen, unsigned char *from, from, unsigned char *to, to, RSA *rsa, rsa, int padding, padding, return -1, return) + DEFINEFUNC6(int, RSA_sign, int type, type, const unsigned char *m, m, unsigned int m_len, m_len, unsigned char *sigret, sigret, unsigned int *siglen, siglen, RSA *rsa, rsa, return 0, return) + DEFINEFUNC6(int, RSA_verify, int type, type, const unsigned char *m, m, unsigned int m_len, m_len, unsigned char *sigret, sigret, unsigned int siglen, siglen, RSA *rsa, rsa, return 0, return) + DEFINEFUNC4(RSA*, PEM_read_bio_RSAPrivateKey, BIO *bp, bp, RSA **x, x, pem_password_cb *cb, cb, void *u, u, return 0, return) + DEFINEFUNC4(RSA*, PEM_read_bio_RSA_PUBKEY, BIO *bp, bp, RSA **x, x, pem_password_cb *cb, cb, void *u, u, return 0, return) + + explicit Private( KDUpdaterCrypto* q ) + : q( q ), + m_private_key( 0 ), + m_private_key_bio( 0 ), + m_public_key( 0 ), + m_public_key_bio( 0 ), + libLoader( 0 ), + passwordProvider( 0 ) + { + CLEARFUNC(SSL_library_init) + CLEARFUNC(BIO_free) + CLEARFUNC(BIO_new) + CLEARFUNC(BIO_s_mem) + CLEARFUNC(BIO_write) + CLEARFUNC(RSA_new) + CLEARFUNC(RSA_free) + CLEARFUNC(RSA_size) + CLEARFUNC(PEM_read_bio_RSAPrivateKey) + CLEARFUNC(PEM_read_bio_RSA_PUBKEY) + CLEARFUNC(RSA_public_encrypt) + CLEARFUNC(RSA_private_decrypt) + CLEARFUNC(RSA_sign) + CLEARFUNC(RSA_verify) + } + ~Private() + { + finish(); + CLEARFUNC(SSL_library_init) + CLEARFUNC(BIO_free) + CLEARFUNC(BIO_new) + CLEARFUNC(BIO_s_mem) + CLEARFUNC(BIO_write) + CLEARFUNC(RSA_new) + CLEARFUNC(RSA_free) + CLEARFUNC(RSA_size) + CLEARFUNC(PEM_read_bio_RSAPrivateKey) + CLEARFUNC(PEM_read_bio_RSA_PUBKEY) + CLEARFUNC(RSA_public_encrypt) + CLEARFUNC(RSA_private_decrypt) + CLEARFUNC(RSA_sign) + CLEARFUNC(RSA_verify) + delete libLoader; + } + + bool resolveOpenSslSymbols() + { + volatile bool symbolsResolved = false; + volatile bool triedToResolveSymbols = false; + //QMutexLocker locker(QMutexPool::globalInstanceGet((void *)&kd_SSL_library_init)); + if (symbolsResolved) + return true; + if (triedToResolveSymbols) + return false; + if( libLoader != 0 ) + return true; + triedToResolveSymbols = true; + + KDLibraryLoader* const libs = loadOpenSsl(); + if( libs == 0 ) + // failed to load them + return false; + + RESOLVEFUNC(SSL_library_init) + RESOLVEFUNC(BIO_free) + RESOLVEFUNC(BIO_new) + RESOLVEFUNC(BIO_s_mem) + RESOLVEFUNC(BIO_write) + RESOLVEFUNC(RSA_new) + RESOLVEFUNC(RSA_free) + RESOLVEFUNC(RSA_size) + RESOLVEFUNC(PEM_read_bio_RSAPrivateKey) + RESOLVEFUNC(PEM_read_bio_RSA_PUBKEY) + RESOLVEFUNC(RSA_public_encrypt) + RESOLVEFUNC(RSA_private_decrypt) + RESOLVEFUNC(RSA_sign) + RESOLVEFUNC(RSA_verify) + + symbolsResolved = true; + libLoader = libs; + return true; + } + + void init() + { + resolveOpenSslSymbols(); + const int inited = kd_SSL_library_init(); + Q_UNUSED( inited ); + Q_ASSERT(inited); + //OpenSSL_add_all_algorithms(); + } + + void finish() + { + finishPrivateKey(); + finishPublicKey(); + } + + void finishPrivateKey() + { + if( m_private_key ) { + kd_RSA_free(m_private_key); + m_private_key = 0; + } + if( m_private_key_bio ) { + kd_BIO_free(m_private_key_bio); + m_private_key_bio = 0; + } + } + + void finishPublicKey() + { + if( m_public_key ) { + kd_RSA_free(m_public_key); + m_public_key = 0; + } + if( m_public_key_bio ) { + kd_BIO_free(m_public_key_bio); + m_public_key_bio = 0; + } + } + + static int password_callback(char *buf, int bufsiz, int verify, void *userdata) + { + Q_UNUSED( verify ); + //qDebug()<<"KDUpdaterCrypto::password_callback verify="<<verify; + KDUpdaterCrypto *crypto = static_cast<KDUpdaterCrypto*>(userdata); + const QByteArray password = crypto->d->passwordProvider == 0 ? crypto->privatePassword() + : crypto->d->passwordProvider->password(); + int len = password.length(); + if (len <= 0) + return 0; + if (len > bufsiz) + len = bufsiz; + memcpy(buf, password.constData(), len); + return len; + } + + bool initPrivateKey() + { + finishPrivateKey(); + + m_private_key_bio = kd_BIO_new(kd_BIO_s_mem()); + Q_ASSERT(m_private_key_bio); + + Q_ASSERT( ! m_privateKey.isNull()); + const int priv_bio_size = kd_BIO_write(m_private_key_bio, m_privateKey.constData(), m_privateKey.length()); + Q_UNUSED( priv_bio_size ); + Q_ASSERT(priv_bio_size >= 1); + + m_private_key = kd_RSA_new(); + Q_ASSERT(m_private_key); + if( ! kd_PEM_read_bio_RSAPrivateKey(m_private_key_bio, &m_private_key, Private::password_callback, q)) { + qWarning() << "PEM_read_bio_RSAPrivateKey failed"; + kd_RSA_free( m_private_key ); + m_private_key = 0; + return false; + } + return true; + } + + bool initPublicKey() + { + finishPublicKey(); + + m_public_key_bio = kd_BIO_new(kd_BIO_s_mem()); + Q_ASSERT(m_public_key_bio); + + Q_ASSERT( ! m_publicKey.isNull()); + const int pub_bio_size = kd_BIO_write(m_public_key_bio, m_publicKey.constData(), m_publicKey.length()); + Q_UNUSED( pub_bio_size ); + Q_ASSERT(pub_bio_size >= 1); + + m_public_key = kd_RSA_new(); + Q_ASSERT(m_public_key); + if( ! kd_PEM_read_bio_RSA_PUBKEY(m_public_key_bio, &m_public_key, password_callback, q)) { + qWarning() << "PEM_read_bio_RSAPublicKey failed"; + kd_RSA_free( m_public_key ); + m_public_key = 0; + return false; + } + return true; + } +}; + +KDUpdaterCrypto::KDUpdaterCrypto() + : d(new Private(this)) +{ + d->init(); +} + +KDUpdaterCrypto::~KDUpdaterCrypto() +{ +} + +QByteArray KDUpdaterCrypto::privateKey() const +{ + return d->m_privateKey; +} + +void KDUpdaterCrypto::setPrivateKey(const QByteArray &key) +{ + d->m_privateKey = key; + d->finish(); +} + +QByteArray KDUpdaterCrypto::privatePassword() const +{ + return d->m_privatePassword; +} + +void KDUpdaterCrypto::setPrivatePassword(const QByteArray &passwd) +{ + d->m_privatePassword = passwd; + d->finish(); +} + +void KDUpdaterCrypto::setPrivatePasswordProvider( const PasswordProvider* provider ) +{ + d->passwordProvider = provider; +} + +QByteArray KDUpdaterCrypto::publicKey() const +{ + return d->m_publicKey; +} + +void KDUpdaterCrypto::setPublicKey(const QByteArray &key) +{ + d->m_publicKey = key; + d->finish(); +} + +QByteArray KDUpdaterCrypto::encrypt(const QByteArray &plaintext) +{ + if( !d->m_public_key && !d->initPublicKey() ) + return QByteArray(); + + Q_ASSERT(d->m_public_key); + unsigned char *encrypted = new unsigned char[ d->kd_RSA_size(d->m_public_key) ]; + const int encryptedlen = d->kd_RSA_public_encrypt(plaintext.length(), (unsigned char *) plaintext.constData(), encrypted, d->m_public_key, RSA_PKCS1_PADDING); + const QByteArray ret = encryptedlen>=0 ? QByteArray( (const char*) encrypted, encryptedlen ) : QByteArray(); + Q_ASSERT(!ret.isNull()); + delete [] encrypted; + return ret; +} + +QByteArray KDUpdaterCrypto::decrypt(const QByteArray &encryptedtext) +{ + if( !d->m_private_key && !d->initPrivateKey() ) + return QByteArray(); + + Q_ASSERT(d->m_private_key); + unsigned char *decrypted = new unsigned char[ d->kd_RSA_size(d->m_private_key) ]; + const int decryptedlen = d->kd_RSA_private_decrypt(encryptedtext.length(), (unsigned char *) encryptedtext.constData(), decrypted, d->m_private_key, RSA_PKCS1_PADDING); + const QByteArray ret = decryptedlen>=0 ? QByteArray( (const char*) decrypted, decryptedlen ) : QByteArray(); + delete [] decrypted; + return ret; +} + +bool KDUpdaterCrypto::verify(const QByteArray &data, const QByteArray &signature) +{ + if( !d->m_public_key && !d->initPublicKey() ) + return false; + + unsigned char *sigret = (unsigned char*) signature.constData(); + unsigned int siglen = signature.length(); + const int verifyresult = d->kd_RSA_verify(NID_sha1, (const unsigned char*) data.constData(), data.length(), sigret, siglen, d->m_public_key); + return verifyresult == 1; +} + +static QByteArray hash( QIODevice* dev, QCryptographicHash::Algorithm method ) +{ + QByteArray buffer; + buffer.resize( 8192 ); + QCryptographicHash h( method ); + while( !dev->atEnd() ) + { + const int read = dev->read( buffer.data(), buffer.length() ); + h.addData( buffer.constData(), read ); + } + return h.result(); +} + +bool KDUpdaterCrypto::verify( QIODevice* dev, const QByteArray& signature ) +{ + const bool wasOpen = dev->isOpen(); + if( !wasOpen && !dev->open( QIODevice::ReadOnly ) ) + { + qWarning() << "KDUpdaterCrypto::verify: could not open the device"; + return false; + } + + const qint64 pos = dev->pos(); + const bool result = verify( hash( dev, QCryptographicHash::Sha1 ), signature ); + dev->seek( pos ); + if( !wasOpen ) + dev->close(); + return result; + +} + +bool KDUpdaterCrypto::verify( const QString& dataPath, const QByteArray& signature ) +{ + QFile dFile( dataPath ); + return verify( &dFile, signature ); +} + +bool KDUpdaterCrypto::verify( const QString& dataPath, const QString& signaturePath ) +{ + QFile sFile( signaturePath ); + if( !sFile.open( QIODevice::ReadOnly ) ) + { + qWarning() << "KDUpdaterCrypto::veryfi: could not open the signature file"; + return false; + } + + return verify( dataPath, sFile.readAll() ); +} + +QByteArray KDUpdaterCrypto::sign( QIODevice* dev ) +{ + const bool wasOpen = dev->isOpen(); + if( !wasOpen && !dev->open( QIODevice::ReadOnly ) ) + return QByteArray(); + + const qint64 pos = dev->pos(); + const QByteArray signature = sign( hash( dev, QCryptographicHash::Sha1 ) ); + dev->seek( pos ); + if( !wasOpen ) + dev->close(); + return signature; +} + +QByteArray KDUpdaterCrypto::sign( const QString& path ) +{ + QFile file( path ); + return sign( &file ); +} + +QByteArray KDUpdaterCrypto::sign(const QByteArray &data) +{ + if( !d->m_private_key && !d->initPrivateKey() ) + return QByteArray(); + + const char *msg = data.constData(); + unsigned char *sigret = new unsigned char[ d->kd_RSA_size(d->m_private_key) ]; + unsigned int siglen; + const int signresult = d->kd_RSA_sign(NID_sha1, (const unsigned char*) msg, strlen(msg), sigret, &siglen, d->m_private_key); + const QByteArray ret = signresult == 1 ? QByteArray( (const char*) sigret, siglen) : QByteArray(); + delete [] sigret; + return ret; +} diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatercrypto.h b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatercrypto.h new file mode 100644 index 000000000..15d0011a0 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatercrypto.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef __KDTOOLS_KDUPDATERCRYPTO_H__ +#define __KDTOOLS_KDUPDATERCRYPTO_H__ + +#include "kdupdater.h" + +class QByteArray; +class QIODevice; + +#include <KDToolsCore/pimpl_ptr.h> + +/** + * Class that provides cryptographic functionality like signing and verifying + * or encrypting and decrypting content. + */ +class KDTOOLS_UPDATER_EXPORT KDUpdaterCrypto { + public: + class PasswordProvider + { + public: + virtual ~PasswordProvider() {} + virtual QByteArray password() const = 0; + }; + + KDUpdaterCrypto(); + virtual ~KDUpdaterCrypto(); + + /** + * The private key. + */ + QByteArray privateKey() const; + void setPrivateKey(const QByteArray &key); + + /** + * The password for the private key. + */ + QByteArray privatePassword() const; + void setPrivatePassword(const QByteArray &passwd); + + void setPrivatePasswordProvider( const PasswordProvider* provider ); + + /** + * The public key. + */ + QByteArray publicKey() const; + void setPublicKey(const QByteArray &key); + + /** + * Encrypt content using the public key. + */ + QByteArray encrypt(const QByteArray &plaintext); + + /** + * Decript encrypted content using the private key. + */ + QByteArray decrypt(const QByteArray &encryptedtext); + + /** + * Sign content with the private key. + */ + QByteArray sign(const QByteArray &data); + QByteArray sign( const QString& path ); + QByteArray sign( QIODevice* dev ); + + /** + * Verify signed content with the public key. + */ + bool verify(const QByteArray &data, const QByteArray &signature); + bool verify( const QString& dataPath, const QString& signaturePath ); + bool verify( const QString& dataPath, const QByteArray& signature ); + bool verify( QIODevice* dev, const QByteArray& signature ); + + private: + class Private; + kdtools::pimpl_ptr< Private > d; +}; + +#endif diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterfiledownloader.cpp b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterfiledownloader.cpp new file mode 100644 index 000000000..c23badb5b --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterfiledownloader.cpp @@ -0,0 +1,1165 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "kdupdaterfiledownloader_p.h" +#include "kdupdaterfiledownloaderfactory.h" +#include "kdupdatersignatureverifier.h" +#include "kdupdatersignatureverificationresult.h" + +#include <KDToolsCore/KDAutoPointer> + +#include <QFile> +#include <QFtp> +#include <QNetworkAccessManager> +#include <QNetworkProxyFactory> +#include <QPointer> +#include <QUrl> +#include <QTemporaryFile> +#include <QFileInfo> +#include <QCryptographicHash> +#include <QThreadPool> +#include <QDebug> + +using namespace KDUpdater; + +static double calcProgress(qint32 done, qint32 total) +{ + return total ? (double(done) / double(total)) : 0 ; +} + +QByteArray KDUpdater::calculateHash( QIODevice* device, QCryptographicHash::Algorithm algo ) { + Q_ASSERT( device ); + QCryptographicHash hash( algo ); + QByteArray buffer; + buffer.resize( 512 * 1024 ); + while ( true ) { + const qint64 numRead = device->read( buffer.data(), buffer.size() ); + if ( numRead <= 0 ) + return hash.result(); + hash.addData( buffer.constData(), numRead ); + } + return QByteArray(); // never reached +} + +QByteArray KDUpdater::calculateHash( const QString& path, QCryptographicHash::Algorithm algo ) { + QFile file( path ); + if ( !file.open( QIODevice::ReadOnly ) ) + return QByteArray(); + return calculateHash( &file, algo ); +} + +class HashVerificationJob::Private { +public: + Private() : hash( QCryptographicHash::Sha1 ), error( HashVerificationJob::ReadError ), timerId( -1 ) { + } + + QPointer<QIODevice> device; + QByteArray sha1Sum; + QCryptographicHash hash; + HashVerificationJob::Error error; + int timerId; +}; + +HashVerificationJob::HashVerificationJob( QObject* parent ) : QObject( parent ), d( new Private ) +{ +} + +HashVerificationJob::~HashVerificationJob() +{ +} + +void HashVerificationJob::setDevice( QIODevice* dev ) +{ + d->device = dev; +} + +void HashVerificationJob::setSha1Sum( const QByteArray& sum ) +{ + d->sha1Sum = sum; +} + +int HashVerificationJob::error() const +{ + return d->error; +} + +bool HashVerificationJob::hasError() const +{ + return d->error != NoError; +} + +void HashVerificationJob::start() +{ + Q_ASSERT( d->device ); + d->timerId = startTimer( 0 ); +} + +void HashVerificationJob::emitFinished() +{ + emit finished( this ); + deleteLater(); +} + +void HashVerificationJob::timerEvent( QTimerEvent* ) +{ + Q_ASSERT( d->timerId >= 0 ); + if ( d->sha1Sum.isEmpty() ) { + killTimer( d->timerId ); + d->timerId = -1; + d->error = NoError; + d->device->close(); + emitFinished(); + return; + } + + QByteArray buf; + buf.resize( 128 * 1024 ); + const qint64 read = d->device->read( buf.data(), buf.size() ); + if ( read > 0 ) { + d->hash.addData( buf.constData(), read ); + return; + } + + d->error = d->hash.result() == d->sha1Sum ? NoError : SumsDifferError; + killTimer( d->timerId ); + d->timerId = -1; + emitFinished(); +} + +//////////////////////////////////////////////////////////////////////////// +// KDUpdater::FileDownloader +//////////////////////////////////////////////////////////////////////////// + +/*! + \internal + \ingroup kdupdater + \class KDUpdater::FileDownloader kdupdaterfiledownloader.h + + Base class for file downloaders used in KDUpdater. File downloaders are used by + the KDUpdater::Update class to download update files. Each subclass of FileDownloader + can download file from a specific category of sources (eg. local, ftp, http etc). + + This is an internal class, not a part of the public API. Currently we have three + subclasses of FileDownloader + \li LocalFileDownloader - downloads from the local file system + \li FtpDownloader - downloads from a FTP site + \li HttpDownloader - downloads from a HTTP site + + Usage + + \code + KDUpdater::FileDownloader* downloader = new KDUpdater::(some subclass name) + + downloader->setUrl( url ); + downloader->download(); + +// wait for downloadCompleted() signal + +QString downloadedFile = downloader->downloadedFileName(); +\endcode +*/ + +struct KDUpdater::FileDownloader::FileDownloaderData +{ + FileDownloaderData() : autoRemove( true ) { + } + + QUrl url; + QString scheme; + QByteArray sha1Sum; + QString errorString; + bool autoRemove; + bool followRedirect; +}; + +KDUpdater::FileDownloader::FileDownloader(const QString& scheme, QObject* parent) + : QObject(parent), + d( new FileDownloaderData ) +{ + d->scheme = scheme; + d->followRedirect = false; +} + +KDUpdater::FileDownloader::~FileDownloader() +{ +} + +void KDUpdater::FileDownloader::setUrl(const QUrl& url) +{ + d->url = url; +} + +QUrl KDUpdater::FileDownloader::url() const +{ + return d->url; +} + +void KDUpdater::FileDownloader::setSha1Sum( const QByteArray& sum ) +{ + d->sha1Sum = sum; +} + +QByteArray KDUpdater::FileDownloader::sha1Sum() const +{ + return d->sha1Sum; +} + +QString FileDownloader::errorString() const +{ + return d->errorString; +} + +void FileDownloader::setDownloadAborted( const QString& error ) +{ + d->errorString = error; + emit downloadAborted( error ); +} + +void KDUpdater::FileDownloader::setDownloadCompleted( const QString& path ) +{ + KDAutoPointer<HashVerificationJob> job( new HashVerificationJob ); + QFile* file = new QFile( path, job.get() ); + if ( !file->open( QIODevice::ReadOnly ) ) { + emit downloadProgress( 1 ); + onError(); + setDownloadAborted( tr("Could not reopen downloaded file %1 for reading: %2").arg( path, file->errorString() ) ); + return; + } + + job->setDevice( file ); + job->setSha1Sum( d->sha1Sum ); + connect( job.get(), SIGNAL(finished(KDUpdater::HashVerificationJob*)), this, SLOT(sha1SumVerified(KDUpdater::HashVerificationJob*)) ); + job.release()->start(); +} + +void KDUpdater::FileDownloader::sha1SumVerified( KDUpdater::HashVerificationJob* job ) +{ + if ( job->hasError() ) { + onError(); + setDownloadAborted( tr("Cryptographic hashes do not match.") ); + } + else { + onSuccess(); + emit downloadCompleted(); + } +} + +QString KDUpdater::FileDownloader::scheme() const +{ + return d->scheme; +} + +void KDUpdater::FileDownloader::setAutoRemoveDownloadedFile(bool val) +{ + d->autoRemove = val; +} + +void KDUpdater::FileDownloader::setFollowRedirects( bool val ) +{ + d->followRedirect = val; +} + +bool KDUpdater::FileDownloader::followRedirects() const +{ + return d->followRedirect; +} + +bool KDUpdater::FileDownloader::isAutoRemoveDownloadedFile() const +{ + return d->autoRemove; +} + +void KDUpdater::FileDownloader::download() { + QMetaObject::invokeMethod( this, "doDownload", Qt::QueuedConnection ); +} + +void KDUpdater::FileDownloader::cancelDownload() +{ + // Do nothing +} + + + +//////////////////////////////////////////////////////////////////////////// +// KDUpdater::FileDownloader +//////////////////////////////////////////////////////////////////////////// + +/* + Even though QFile::copy() does the task of copying local files from one place + to another, I prefer to use the timer and copy one block of data per unit time. + + This is because, it is possible that the user of KDUpdater is simultaneously + downloading several files. Sometimes in tandem with other file downloaders. + If the local file that is being downloaded takes a long time; then that will + hang the other downloads. + + On the otherhand, local downloads need not actually download the file. It can + simply pass on the source file as destination file. At this moment however, + I think the user of LocalFileDownloader will assume that the downloaded file + can be fiddled around with without worrying about whether it would mess up + the original source or not. +*/ + +struct KDUpdater::LocalFileDownloader::LocalFileDownloaderData +{ + LocalFileDownloaderData() : source(0), destination(0), + downloaded(false), timerId(-1) { } + + QFile* source; + QTemporaryFile* destination; + QString destFileName; + bool downloaded; + int timerId; +}; + +KDUpdater::LocalFileDownloader::LocalFileDownloader(QObject* parent) + :KDUpdater::FileDownloader(QLatin1String( "file" ), parent), + d ( new LocalFileDownloaderData ) +{ +} + +KDUpdater::LocalFileDownloader::~LocalFileDownloader() +{ + if( this->isAutoRemoveDownloadedFile() && !d->destFileName.isEmpty() ) + QFile::remove(d->destFileName); + + delete d; +} + +bool KDUpdater::LocalFileDownloader::canDownload() const +{ + const QString localFile = url().toLocalFile(); + QFileInfo fi( localFile ); + return fi.exists() && fi.isReadable(); +} + +bool KDUpdater::LocalFileDownloader::isDownloaded() const +{ + return d->downloaded; +} + +void KDUpdater::LocalFileDownloader::doDownload() +{ + // Already downloaded + if( d->downloaded ) + return; + + // Already started downloading + if( d->timerId >= 0 ) + return; + + // Open source and destination files + QString localFile = this->url().toLocalFile(); + d->source = new QFile(localFile, this); + d->destination = new QTemporaryFile(this); + + if( !d->source->open(QFile::ReadOnly) ) + { + onError(); + setDownloadAborted(tr("Cannot open source file for reading.")); + return; + } + + if( !d->destination->open() ) + { + onError(); + setDownloadAborted(tr("Cannot open destination file for writing.")); + return; + } + + // Start a timer and kickoff the copy process + d->timerId = startTimer(0); // as fast as possible + emit downloadStarted(); + emit downloadProgress(0); +} + +QString KDUpdater::LocalFileDownloader::downloadedFileName() const +{ + return d->destFileName; +} + +KDUpdater::LocalFileDownloader* KDUpdater::LocalFileDownloader::clone( QObject* parent ) const +{ + return new LocalFileDownloader( parent ); +} + +void KDUpdater::LocalFileDownloader::cancelDownload() +{ + if( d->timerId < 0 ) + return; + + killTimer( d->timerId ); + d->timerId = -1; + + onError(); + emit downloadCanceled(); +} + +void KDUpdater::LocalFileDownloader::timerEvent(QTimerEvent*) +{ + if( !d->source || !d->destination ) + return; + + const qint64 blockSize = 32768; + QByteArray buffer; + buffer.resize( blockSize ); + const qint64 numRead = d->source->read( buffer.data(), buffer.size() ); + qint64 toWrite = numRead; + while ( toWrite > 0 ) { + const qint64 numWritten = d->destination->write( buffer.constData() + numRead - toWrite, toWrite ); + if ( numWritten < 0 ) { + killTimer( d->timerId ); + d->timerId = -1; + onError(); + setDownloadAborted( tr("Writing to %1 failed: %2").arg( d->destination->fileName(), d->destination->errorString() ) ); + return; + } + toWrite -= numWritten; + } + + if( numRead > 0 ) { + emit downloadProgress( calcProgress(d->source->pos(), d->source->size()) ); + return; + } + + d->destination->flush(); + + killTimer( d->timerId ); + d->timerId = -1; + + setDownloadCompleted( d->destination->fileName() ); +} + +void LocalFileDownloader::onSuccess() +{ + d->downloaded = true; + d->destFileName = d->destination->fileName(); + d->destination->setAutoRemove( false ); + d->destination->close(); + delete d->destination; + d->destination = 0; + delete d->source; + d->source = 0; +} + +void LocalFileDownloader::onError() +{ + d->downloaded = false; + d->destFileName.clear(); + delete d->destination; + d->destination = 0; + delete d->source; + d->source = 0; +} + +struct KDUpdater::ResourceFileDownloader::ResourceFileDownloaderData +{ + ResourceFileDownloaderData() + : downloaded( false ), + timerId( -1 ) + { + } + + QString destFileName; + bool downloaded; + int timerId; +}; + +KDUpdater::ResourceFileDownloader::ResourceFileDownloader(QObject* parent) + :KDUpdater::FileDownloader(QLatin1String( "resource" ), parent), + d ( new ResourceFileDownloaderData ) +{ +} + +KDUpdater::ResourceFileDownloader::~ResourceFileDownloader() +{ + delete d; +} + +bool KDUpdater::ResourceFileDownloader::canDownload() const +{ + QUrl url = this->url(); + url.setScheme( QString::fromLatin1( "file" ) ); + QString localFile = QString::fromLatin1( ":%1" ).arg( url.toLocalFile() ); + QFileInfo fi(localFile); + return fi.exists() && fi.isReadable(); +} + +bool KDUpdater::ResourceFileDownloader::isDownloaded() const +{ + return d->downloaded; +} + +void KDUpdater::ResourceFileDownloader::doDownload() +{ + // Already downloaded + if( d->downloaded ) + return; + + // Already started downloading + if( d->timerId >= 0 ) + return; + + // Open source and destination files + QUrl url = this->url(); + url.setScheme( QString::fromLatin1( "file" ) ); + d->destFileName = QString::fromLatin1( ":%1" ).arg( url.toLocalFile() ); + + // Start a timer and kickoff the copy process + d->timerId = startTimer(0); // as fast as possible + emit downloadStarted(); + emit downloadProgress(0); +} + +QString KDUpdater::ResourceFileDownloader::downloadedFileName() const +{ + return d->destFileName; +} + +KDUpdater::ResourceFileDownloader* KDUpdater::ResourceFileDownloader::clone( QObject* parent ) const +{ + return new ResourceFileDownloader( parent ); +} + +void KDUpdater::ResourceFileDownloader::cancelDownload() +{ + if( d->timerId < 0 ) + return; + + killTimer( d->timerId ); + d->timerId = -1; + + emit downloadCanceled(); +} + +void KDUpdater::ResourceFileDownloader::timerEvent(QTimerEvent*) +{ + killTimer( d->timerId ); + d->timerId = -1; + setDownloadCompleted( d->destFileName ); +} + +void KDUpdater::ResourceFileDownloader::onSuccess() +{ + d->downloaded = true; +} + +void KDUpdater::ResourceFileDownloader::onError() +{ + d->downloaded = false; +} + +//////////////////////////////////////////////////////////////////////////// +// KDUpdater::FileDownloader +//////////////////////////////////////////////////////////////////////////// + +struct KDUpdater::FtpDownloader::FtpDownloaderData +{ + FtpDownloaderData() : ftp(0), destination(0), + downloaded(false), ftpCmdId(-1), aborted(false) { } + + QFtp* ftp; + QTemporaryFile* destination; + QString destFileName; + bool downloaded; + int ftpCmdId; + bool aborted; +}; + +KDUpdater::FtpDownloader::FtpDownloader(QObject* parent) + : KDUpdater::FileDownloader(QLatin1String( "ftp" ), parent), + d ( new FtpDownloaderData ) +{ +} + +KDUpdater::FtpDownloader::~FtpDownloader() +{ + if( this->isAutoRemoveDownloadedFile() && !d->destFileName.isEmpty() ) + QFile::remove(d->destFileName); + + delete d; +} + +bool KDUpdater::FtpDownloader::canDownload() const +{ + // TODO: Check whether the ftp file actually exists or not. + return true; +} + +bool KDUpdater::FtpDownloader::isDownloaded() const +{ + return d->downloaded; +} + +void KDUpdater::FtpDownloader::doDownload() +{ + if( d->downloaded ) + return; + + if( d->ftp ) + return; + + d->ftp = new QFtp(this); + connect(d->ftp, SIGNAL(done(bool)), this, SLOT(ftpDone(bool))); + connect(d->ftp, SIGNAL(commandStarted(int)), this, SLOT(ftpCmdStarted(int))); + connect(d->ftp, SIGNAL(commandFinished(int,bool)), this, SLOT(ftpCmdFinished(int,bool))); + connect(d->ftp, SIGNAL(stateChanged(int)), this, SLOT(ftpStateChanged(int))); + connect(d->ftp, SIGNAL(dataTransferProgress(qint64,qint64)), this, SLOT(ftpDataTransferProgress(qint64,qint64))); + + d->ftp->connectToHost( url().host(), url().port(21) ); + d->ftp->login(); +} + +QString KDUpdater::FtpDownloader::downloadedFileName() const +{ + return d->destFileName; +} + +KDUpdater::FtpDownloader* KDUpdater::FtpDownloader::clone( QObject* parent ) const +{ + return new FtpDownloader( parent ); +} + + +void KDUpdater::FtpDownloader::cancelDownload() +{ + if( d->ftp ) + { + d->aborted = true; + d->ftp->abort(); + } +} + +void KDUpdater::FtpDownloader::ftpDone(bool error) +{ + if( error ) + { + d->ftp->deleteLater(); + d->ftp = 0; + d->ftpCmdId = -1; + + onError(); + + if( d->aborted ) + { + d->aborted = false; + emit downloadCanceled(); + } + else + setDownloadAborted( d->ftp->errorString() ); + } + //PENDING what about the non-error case?? +} + +void KDUpdater::FtpDownloader::ftpCmdStarted(int id) +{ + if( id != d->ftpCmdId ) + return; + + emit downloadStarted(); + emit downloadProgress(0); +} + +void KDUpdater::FtpDownloader::ftpCmdFinished(int id, bool error) +{ + if( id != d->ftpCmdId || error ) // PENDING why error -> return?? + return; + + disconnect(d->ftp, 0, this, 0); + d->ftp->deleteLater(); + d->ftp = 0; + d->ftpCmdId = -1; + d->destination->flush(); + + setDownloadCompleted( d->destination->fileName() ); +} + +void FtpDownloader::onSuccess() +{ + d->downloaded = true; + d->destFileName = d->destination->fileName(); + d->destination->setAutoRemove( false ); + delete d->destination; + d->destination = 0; + +} + +void FtpDownloader::onError() +{ + d->downloaded = false; + d->destFileName.clear(); + delete d->destination; + d->destination = 0; + +} + +void KDUpdater::FtpDownloader::ftpStateChanged(int state) +{ + switch(state) + { + case QFtp::Connected: + // begin the download + d->destination = new QTemporaryFile(this); + d->destination->open(); //PENDING handle error + d->ftpCmdId = d->ftp->get( url().path(), d->destination ); + break; + case QFtp::Unconnected: + // download was unconditionally aborted + disconnect(d->ftp, 0, this, 0); + d->ftp->deleteLater(); + d->ftp = 0; + d->ftpCmdId = -1; + onError(); + setDownloadAborted(tr("Download was aborted due to network errors.")); + break; + } +} + +void KDUpdater::FtpDownloader::ftpDataTransferProgress(qint64 done, qint64 total) +{ + emit downloadProgress( calcProgress(done, total) ); +} + +//////////////////////////////////////////////////////////////////////////// +// KDUpdater::FileDownloader +//////////////////////////////////////////////////////////////////////////// + +struct KDUpdater::HttpDownloader::HttpDownloaderData +{ + explicit HttpDownloaderData( HttpDownloader* qq ) : q( qq ), http(0), destination(0), downloaded(false), + aborted(false), retrying(false) { } + + HttpDownloader* const q; + QNetworkAccessManager manager; + QNetworkReply* http; + QTemporaryFile* destination; + QString destFileName; + bool downloaded; + bool aborted; + bool retrying; + + void shutDown() { + disconnect( http, SIGNAL( finished() ), q, SLOT( httpReqFinished() ) ); + http->deleteLater(); + http = 0; + destination->close(); + destination->deleteLater(); + destination = 0; + + } +}; + +KDUpdater::HttpDownloader::HttpDownloader(QObject* parent) + : KDUpdater::FileDownloader(QLatin1String( "http" ), parent), + d ( new HttpDownloaderData( this ) ) +{ +} + +KDUpdater::HttpDownloader::~HttpDownloader() +{ + if( this->isAutoRemoveDownloadedFile() && !d->destFileName.isEmpty() ) + QFile::remove(d->destFileName); + delete d; +} + +bool KDUpdater::HttpDownloader::canDownload() const +{ + // TODO: Check whether the ftp file actually exists or not. + return true; +} + +bool KDUpdater::HttpDownloader::isDownloaded() const +{ + return d->downloaded; +} + +void KDUpdater::HttpDownloader::doDownload() +{ + if( d->downloaded ) + return; + + if( d->http ) + return; + + d->http = d->manager.get( QNetworkRequest( url() ) ); + + connect( d->http, SIGNAL( readyRead() ), this, SLOT( httpReadyRead() ) ); + connect( d->http, SIGNAL( downloadProgress( qint64, qint64 ) ), this, SLOT( httpReadProgress( qint64, qint64) ) ); + connect( d->http, SIGNAL( finished() ), this, SLOT( httpReqFinished() ) ); + connect( d->http, SIGNAL( error( QNetworkReply::NetworkError ) ), this, SLOT( httpError( QNetworkReply::NetworkError ) ) ); + + /* + // In a future update, authentication should also be supported. + + connect(d->http, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + this, SLOT(httpProxyAuth(QNetworkProxy,QAuthenticator*))); + connect(d->http, SIGNAL(authenticationRequired(QString,QAuthenticator*)), + this, SLOT(httpAuth(QString,QAuthenticator*))); + */ + + // Begin the download + d->destination = new QTemporaryFile(this); + if ( !d->destination->open() ) { + const QString err = d->destination->errorString(); + d->shutDown(); + setDownloadAborted( tr("Cannot download %1: Could not create temporary file: %2").arg( url().toString(), err ) ); + return; + } +} + +QString KDUpdater::HttpDownloader::downloadedFileName() const +{ + return d->destFileName; +} + +KDUpdater::HttpDownloader* KDUpdater::HttpDownloader::clone( QObject* parent ) const +{ + return new HttpDownloader( parent ); +} + +void KDUpdater::HttpDownloader::httpReadyRead() +{ + static QByteArray buffer( 16384, '\0' ); + while( d->http->bytesAvailable() ) + { + const qint64 read = d->http->read( buffer.data(), buffer.size() ); + qint64 written = 0; + while( written < read ) { + const qint64 numWritten = d->destination->write( buffer.data() + written, read - written ); + if ( numWritten < 0 ) { + const QString err = d->destination->errorString(); + d->shutDown(); + setDownloadAborted( tr("Cannot download %1: Writing to temporary file failed: %2").arg( url().toString(), err ) ); + return; + } + written += numWritten; + } + } +} + +void KDUpdater::HttpDownloader::httpError( QNetworkReply::NetworkError ) +{ + static bool setProxySettings = false; + if( !d->retrying && !setProxySettings ) + { + d->shutDown(); + d->retrying = true; + setProxySettings = true; + + // silently force retry with global proxy settings + QNetworkProxyFactory::setUseSystemConfiguration( true ); + + doDownload(); + return; + } + httpDone( true ); +} + +void KDUpdater::HttpDownloader::cancelDownload() +{ + d->aborted = true; + if( d->http ) + { + d->http->abort(); + httpDone( true ); + } +} + +void KDUpdater::HttpDownloader::httpDone( bool error ) +{ + if( error ) + { + QString err; + if( d->http ) + { + err = d->http->errorString(); + d->http->deleteLater(); + d->http = 0; + onError(); + } + + if( d->aborted ) + { + d->aborted = false; + emit downloadCanceled(); + } + else + setDownloadAborted( err ); + } + //PENDING: what about the non-error case?? +} + +void KDUpdater::HttpDownloader::onError() +{ + d->downloaded = false; + d->destFileName.clear(); + delete d->destination; + d->destination = 0; +} + +void KDUpdater::HttpDownloader::onSuccess() +{ + d->downloaded = true; + d->destFileName = d->destination->fileName(); + d->destination->setAutoRemove( false ); + delete d->destination; + d->destination = 0; +} + +void KDUpdater::HttpDownloader::httpReqFinished() +{ + const QVariant redirect = d->http == 0 ? QVariant() : d->http->attribute( QNetworkRequest::RedirectionTargetAttribute ); + const QUrl redirectUrl = redirect.toUrl(); + //if ( redirect.isValid() ) + // redirectUrl = redirect.toUrl(); + if ( followRedirects() && redirectUrl.isValid() ) + { + // clean the previous download + d->http->deleteLater(); + d->http = 0; + d->destination->close(); + d->destination->deleteLater(); + d->destination = 0; + + d->http = d->manager.get( QNetworkRequest( redirectUrl ) ); + + connect( d->http, SIGNAL( readyRead() ), this, SLOT( httpReadyRead() ) ); + connect( d->http, SIGNAL( downloadProgress( qint64, qint64 ) ), this, SLOT( httpReadProgress( qint64, qint64) ) ); + connect( d->http, SIGNAL( finished() ), this, SLOT( httpReqFinished() ) ); + connect( d->http, SIGNAL( error( QNetworkReply::NetworkError ) ), this, SLOT( httpError( QNetworkReply::NetworkError ) ) ); + + // Begin the download + d->destination = new QTemporaryFile(this); + d->destination->open(); //PENDING handle error + } + else + { + if( d->http == 0 ) + return; + httpReadyRead(); + d->destination->flush(); + setDownloadCompleted( d->destination->fileName() ); + d->http->deleteLater(); + d->http = 0; + } +} + +void KDUpdater::HttpDownloader::httpReadProgress( qint64 done, qint64 total) +{ + emit downloadProgress( calcProgress( done, total ) ); +} + +class SignatureVerificationDownloader::Private +{ + SignatureVerificationDownloader* const q; +public: + explicit Private( FileDownloader* dl, SignatureVerificationDownloader* qq ) : q( qq ), verifier( 0 ), downloader( dl ), sigDownloader( 0 ), actualDownloadDone( false ) + { + Q_ASSERT( downloader ); + q->connect( downloader.get(), SIGNAL( downloadProgress( double ) ), q, SIGNAL( downloadProgress( double ) ) ); + q->connect( downloader.get(), SIGNAL(downloadStarted()), q, SLOT(dataDownloadStarted()) ); + q->connect( downloader.get(), SIGNAL(downloadCompleted()), q, SLOT(dataDownloadCompleted()) ); + q->connect( downloader.get(), SIGNAL(downloadCanceled()), q, SLOT(dataDownloadCanceled()) ); + q->connect( downloader.get(), SIGNAL(downloadAborted(QString)), q, SLOT(dataDownloadAborted(QString)) ); + } + + ~Private() + { + delete verifier; + } + + const SignatureVerifier* verifier; + KDAutoPointer< FileDownloader > downloader; + KDAutoPointer< FileDownloader > sigDownloader; + QUrl signatureUrl; + SignatureVerificationResult result; + QString downloadedFileName; + bool actualDownloadDone : 1; +}; + +SignatureVerificationDownloader::SignatureVerificationDownloader( FileDownloader* downloader, QObject* parent ) : FileDownloader( downloader->scheme(), parent ), d( new Private( downloader, this ) ) +{ +} + +SignatureVerificationDownloader::~SignatureVerificationDownloader() +{ +} + +QUrl SignatureVerificationDownloader::signatureUrl() const +{ + return d->signatureUrl; +} + +void SignatureVerificationDownloader::setSignatureUrl( const QUrl& url ) +{ + d->signatureUrl = url; +} + +const SignatureVerifier* SignatureVerificationDownloader::signatureVerifier() const { + return d->verifier; +} + +void SignatureVerificationDownloader::setSignatureVerifier( const SignatureVerifier* verifier ) { + delete d->verifier; + d->verifier = verifier ? verifier->clone() : 0; +} + +SignatureVerificationResult SignatureVerificationDownloader::result() const +{ + return d->result; +} + +bool SignatureVerificationDownloader::canDownload() const +{ + return d->downloader->canDownload(); +} + +bool SignatureVerificationDownloader::isDownloaded() const +{ + return d->downloader->isDownloaded(); +} + +QString SignatureVerificationDownloader::downloadedFileName() const +{ + return d->downloadedFileName; +} + +FileDownloader* SignatureVerificationDownloader::clone( QObject* parent ) const +{ + return new SignatureVerificationDownloader( d->downloader->clone(), parent ); +} + +void SignatureVerificationDownloader::onError() +{ + d->sigDownloader.reset(); + if ( QFile::exists( d->downloadedFileName ) ) + QFile::remove( d->downloadedFileName ); +} + +void SignatureVerificationDownloader::onSuccess() +{ + d->sigDownloader.reset(); +} + +void SignatureVerificationDownloader::cancelDownload() +{ + if ( !d->actualDownloadDone ) { + d->downloader->cancelDownload(); + return; + } + if ( d->sigDownloader ) { + d->sigDownloader->cancelDownload(); + } +} + +void SignatureVerificationDownloader::doDownload() +{ + Q_ASSERT( d->verifier ); + Q_ASSERT( d->downloader ); + d->downloader->setUrl( url() ); + d->downloader->setSha1Sum( sha1Sum() ); + d->downloader->download(); +} + +void SignatureVerificationDownloader::dataDownloadStarted() +{ + emit downloadStarted(); +} + +void SignatureVerificationDownloader::dataDownloadAborted( const QString& err ) +{ + setDownloadAborted( err ); +} + +void SignatureVerificationDownloader::dataDownloadCanceled() +{ + emit downloadCanceled(); +} + +static QUrl suggestSignatureUrl( const QUrl& url ) { + return url.toString() + QLatin1String(".sig"); +} + +void SignatureVerificationDownloader::dataDownloadCompleted() +{ + d->downloadedFileName = d->downloader->downloadedFileName(); + d->actualDownloadDone = true; + + QUrl url = d->signatureUrl; + if ( url.isEmpty() ) + url = suggestSignatureUrl( d->downloader->url() ); + d->sigDownloader.reset( FileDownloaderFactory::instance().create( url.scheme(), this ) ); + if ( !d->sigDownloader ) { + setDownloadAborted( tr("Could not download signature: scheme %1 not supported").arg( url.scheme() ) ); + return; + } + d->sigDownloader->setUrl( url ); + connect( d->sigDownloader.get(), SIGNAL(downloadCompleted()), this, SLOT(signatureDownloadCompleted()) ); + connect( d->sigDownloader.get(), SIGNAL(downloadCanceled()), this, SLOT(signatureDownloadCanceled()) ); + connect( d->sigDownloader.get(), SIGNAL(downloadAborted(QString)), this, SLOT(signatureDownloadAborted(QString)) ); + d->sigDownloader->download(); +} + +void SignatureVerificationDownloader::signatureDownloadAborted( const QString& err ) +{ + setDownloadAborted( tr("Downloading signature: %1").arg( err ) ); +} + +void SignatureVerificationDownloader::signatureDownloadCanceled() +{ + emit downloadCanceled(); +} + +void SignatureVerificationDownloader::signatureDownloadCompleted() +{ + QFile sigFile( d->sigDownloader->downloadedFileName() ); + if ( !sigFile.open( QIODevice::ReadOnly ) ) { + setDownloadAborted( tr("Could not open signature file: %1").arg( sigFile.errorString() ) ); + return; + } + + const QByteArray signature = sigFile.readAll(); + + QFile dataFile( d->downloadedFileName ); + if ( !dataFile.open( QIODevice::ReadOnly ) ) { + setDownloadAborted( tr("Could not open file for verification: %1").arg( dataFile.errorString() ) ); + return; + } + const QByteArray dataHash = calculateHash(&dataFile, QCryptographicHash::Sha1); + + //const QString sigPath = d->sigDownloader->downloadedFileName(); + d->result = d->verifier->verify( dataHash, signature ); + if( ! d->result.isValid() ) { + setDownloadAborted( d->result.errorString() ); + return; + } + setDownloadCompleted( d->downloadedFileName ); + +#if 0 + SignatureVerificationRunnable* runnable = new SignatureVerificationRunnable; + runnable->setSignature( signature ); + runnable->setData( dataFile.release() ); + runnable->setVerifier( d->verifier ); + runnable->addResultListener( this, "verificationResult" ); + QThreadPool::globalInstance()->start( runnable ); +#endif +} diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterfiledownloader.h b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterfiledownloader.h new file mode 100644 index 000000000..754917aa8 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterfiledownloader.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KD_UPDATER_FILE_DOWNLOADER_H +#define KD_UPDATER_FILE_DOWNLOADER_H + +#include "kdupdater.h" +#include <KDToolsCore/pimpl_ptr.h> + +#include <QtCore/QObject> +#include <QtCore/QUrl> +#include <QtCore/QCryptographicHash> + +namespace KDUpdater +{ + KDTOOLS_UPDATER_EXPORT QByteArray calculateHash( QIODevice* device, QCryptographicHash::Algorithm algo ); + KDTOOLS_UPDATER_EXPORT QByteArray calculateHash( const QString& path, QCryptographicHash::Algorithm algo ); + + class HashVerificationJob; + + class KDTOOLS_UPDATER_EXPORT FileDownloader : public QObject + { + Q_OBJECT + Q_PROPERTY( bool autoRemoveDownloadedFile READ isAutoRemoveDownloadedFile WRITE setAutoRemoveDownloadedFile ) + Q_PROPERTY( QUrl url READ url WRITE setUrl ) + Q_PROPERTY( QString scheme READ scheme ) + + public: + explicit FileDownloader(const QString& scheme, QObject* parent=0); + ~FileDownloader(); + + void setUrl(const QUrl& url); + QUrl url() const; + + void setSha1Sum( const QByteArray& sha1 ); + QByteArray sha1Sum() const; + + QString errorString() const; + QString scheme() const; + + virtual bool canDownload() const = 0; + virtual bool isDownloaded() const = 0; + virtual QString downloadedFileName() const = 0; + virtual FileDownloader* clone( QObject* parent=0 ) const = 0; + + void download(); + + void setAutoRemoveDownloadedFile(bool val); + bool isAutoRemoveDownloadedFile() const; + + void setFollowRedirects( bool val ); + bool followRedirects() const; + + public Q_SLOTS: + virtual void cancelDownload(); + void sha1SumVerified( KDUpdater::HashVerificationJob* job ); + + protected: + virtual void onError() = 0; + virtual void onSuccess() = 0; + + Q_SIGNALS: + void downloadProgress(double); + void downloadStarted(); + void downloadCanceled(); + +#ifndef Q_MOC_RUN + private: +#endif + void downloadCompleted(); + void downloadAborted(const QString& errorMessage); + + protected: + void setDownloadCompleted( const QString& filepath ); + void setDownloadAborted( const QString& error ); + + private Q_SLOTS: + virtual void doDownload() = 0; + + private: + struct FileDownloaderData; + kdtools::pimpl_ptr<FileDownloaderData> d; + }; +} + +#endif diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterfiledownloader_p.h b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterfiledownloader_p.h new file mode 100644 index 000000000..516bc3c3d --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterfiledownloader_p.h @@ -0,0 +1,243 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KD_UPDATER_FILE_DOWNLOADER_P_H +#define KD_UPDATER_FILE_DOWNLOADER_P_H + +#include "kdupdaterfiledownloader.h" +#include <KDToolsCore/pimpl_ptr.h> + +#include <QtCore/QCryptographicHash> +#include <QtNetwork/QNetworkReply> + +class QIODevice; + +// these classes are not a part of the public API + +namespace KDUpdater +{ + + //TODO make it a KDJob once merged + class HashVerificationJob : public QObject + { + Q_OBJECT + public: + enum Error { + NoError=0, + ReadError=128, + SumsDifferError + }; + + explicit HashVerificationJob( QObject* parent=0 ); + ~HashVerificationJob(); + + void setDevice( QIODevice* dev ); + void setSha1Sum( const QByteArray& data ); + + bool hasError() const; + int error() const; + + void start(); + + Q_SIGNALS: + void finished( KDUpdater::HashVerificationJob* ); + + private: + void emitFinished(); + /* reimp */ void timerEvent( QTimerEvent* te ); + + private: + class Private; + kdtools::pimpl_ptr<Private> d; + }; + + class LocalFileDownloader : public FileDownloader + { + Q_OBJECT + + public: + explicit LocalFileDownloader(QObject* parent=0); + ~LocalFileDownloader(); + + bool canDownload() const; + bool isDownloaded() const; + QString downloadedFileName() const; + /* reimp */ LocalFileDownloader* clone( QObject* parent=0 ) const; + + public Q_SLOTS: + void cancelDownload(); + + protected: + void timerEvent(QTimerEvent* te); + /* reimp */ void onError(); + /* reimp */ void onSuccess(); + + private Q_SLOTS: + /* reimp */ void doDownload(); + + private: + struct LocalFileDownloaderData; + LocalFileDownloaderData* d; + }; + + class ResourceFileDownloader : public FileDownloader + { + Q_OBJECT + + public: + explicit ResourceFileDownloader(QObject* parent=0); + ~ResourceFileDownloader(); + + bool canDownload() const; + bool isDownloaded() const; + QString downloadedFileName() const; + /* reimp */ ResourceFileDownloader* clone( QObject* parent=0 ) const; + + public Q_SLOTS: + void cancelDownload(); + + protected: + void timerEvent(QTimerEvent* te); + /* reimp */ void onError(); + /* reimp */ void onSuccess(); + + private Q_SLOTS: + /* reimp */ void doDownload(); + + private: + struct ResourceFileDownloaderData; + ResourceFileDownloaderData* d; + }; + + class FtpDownloader : public FileDownloader + { + Q_OBJECT + + public: + explicit FtpDownloader(QObject* parent=0); + ~FtpDownloader(); + + bool canDownload() const; + bool isDownloaded() const; + QString downloadedFileName() const; + /* reimp */ FtpDownloader* clone( QObject* parent=0 ) const; + + public Q_SLOTS: + void cancelDownload(); + + protected: + /* reimp */ void onError(); + /* reimp */ void onSuccess(); + + private Q_SLOTS: + /* reimp */ void doDownload(); + void ftpDone(bool error); + void ftpCmdStarted(int id); + void ftpCmdFinished(int id, bool error); + void ftpStateChanged(int state); + void ftpDataTransferProgress(qint64 done, qint64 total); + + private: + struct FtpDownloaderData; + FtpDownloaderData* d; + }; + + class HttpDownloader : public FileDownloader + { + Q_OBJECT + + public: + explicit HttpDownloader(QObject* parent=0); + ~HttpDownloader(); + + bool canDownload() const; + bool isDownloaded() const; + QString downloadedFileName() const; + /* reimp */ HttpDownloader* clone( QObject* parent=0 ) const; + + public Q_SLOTS: + void cancelDownload(); + + protected: + /* reimp */ void onError(); + /* reimp */ void onSuccess(); + + private Q_SLOTS: + /* reimp */ void doDownload(); + void httpReadyRead(); + void httpReadProgress( qint64 done, qint64 total ); + void httpError( QNetworkReply::NetworkError ); + void httpDone( bool error ); + void httpReqFinished(); + + private: + struct HttpDownloaderData; + HttpDownloaderData* d; + }; + + class SignatureVerificationResult; + class SignatureVerifier; + + class SignatureVerificationDownloader : public FileDownloader + { + Q_OBJECT + public: + explicit SignatureVerificationDownloader( FileDownloader* downloader, QObject* parent=0 ); + ~SignatureVerificationDownloader(); + + QUrl signatureUrl() const; + void setSignatureUrl( const QUrl& url ); + + const SignatureVerifier* signatureVerifier() const; + void setSignatureVerifier( const SignatureVerifier* verifier ); + + SignatureVerificationResult result() const; + + /* reimp */ bool canDownload() const; + /* reimp */ bool isDownloaded() const; + /* reimp */ QString downloadedFileName() const; + /* reimp */ FileDownloader* clone( QObject* parent=0 ) const; + + public Q_SLOTS: + /* reimp */ void cancelDownload(); + + protected: + /* reimp */ void onError(); + /* reimp */ void onSuccess(); + + private Q_SLOTS: + /* reimp */ void doDownload(); + void dataDownloadStarted(); + void dataDownloadCanceled(); + void dataDownloadCompleted(); + void dataDownloadAborted(const QString& errorMessage); + void signatureDownloadCanceled(); + void signatureDownloadCompleted(); + void signatureDownloadAborted(const QString& errorMessage); + + private: + class Private; + kdtools::pimpl_ptr<Private> d; + }; +} + +#endif diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterfiledownloaderfactory.cpp b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterfiledownloaderfactory.cpp new file mode 100644 index 000000000..cba271e96 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterfiledownloaderfactory.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "kdupdaterfiledownloaderfactory.h" +#include "kdupdaterfiledownloader_p.h" + +#include <cassert> + +/*! + \internal + \ingroup kdupdater + \class KDUpdater::FileDownloaderFactory kdupdaterfiledownloaderfactory.h + \brief Factory for \ref KDUpdater::FileDownloader + + This class acts as a factory for \ref KDUpdater::FileDownloader. You can register + one or more file downloaders with this factory and query them based on their scheme. + + This class follows the singleton design pattern. Only one instance of this class can + be created and its reference can be fetched from the \ref instance() method. +*/ + +using namespace KDUpdater; + +struct FileDownloaderFactory::FileDownloaderFactoryData +{ + bool m_followRedirects; +}; + +FileDownloaderFactory& FileDownloaderFactory::instance() +{ + static KDUpdater::FileDownloaderFactory theFactory; + return theFactory; +} + +/*! + Constructor +*/ +FileDownloaderFactory::FileDownloaderFactory() + : d ( new FileDownloaderFactoryData ) +{ + // Register the default file downloader set + registerFileDownloader< LocalFileDownloader >( QLatin1String( "file" ) ); + registerFileDownloader< FtpDownloader >( QLatin1String( "ftp" ) ); + registerFileDownloader< HttpDownloader >( QLatin1String( "http" ) ); + registerFileDownloader< ResourceFileDownloader >( QLatin1String( "resource" ) ); + d->m_followRedirects = false; +} + +void FileDownloaderFactory::setFollowRedirects( bool val ) +{ + FileDownloaderFactory::instance().d->m_followRedirects = val; +} + +bool FileDownloaderFactory::followRedirects() +{ + return FileDownloaderFactory::instance().d->m_followRedirects; +} + +FileDownloaderFactory::~FileDownloaderFactory() +{ +} + +/*! + Returns a new instance to the \ref KDUpdater::FileDownloader based whose scheme is equal to the string + passed as parameter to this function. + \note Ownership of this object remains to the programmer. +*/ +FileDownloader* FileDownloaderFactory::create( const QString& scheme, QObject* parent ) const +{ + return create( scheme, 0, QUrl(), parent ); +} + +FileDownloader* FileDownloaderFactory::create( const QString& scheme, const SignatureVerifier* verifier, const QUrl& signatureUrl, QObject* parent ) const +{ + FileDownloader* const downloader = KDGenericFactory< FileDownloader >::create( scheme ); + if( downloader != 0 ) { + downloader->setFollowRedirects( d->m_followRedirects ); + downloader->setParent( parent ); + } + if( !verifier ) + return downloader; + + SignatureVerificationDownloader* const svdl = new SignatureVerificationDownloader( downloader, parent ); + svdl->setSignatureVerifier( verifier ); + svdl->setSignatureUrl( signatureUrl ); + return svdl; +} + +/*! + KDUpdater::FileDownloaderFactory::registerFileDownlooader + Registers a new file downloader with the factory. If there is already a downloader with the same scheme, + the downloader is replaced. The ownership of the downloader is transfered to the factory. +*/ + +/*! + Returns the number of file downloaders in the factory. +*/ +int FileDownloaderFactory::fileDownloaderCount() const +{ + return productCount(); +} + +/*! + Returns a list of schemes supported by this factory, i.e. for which a + FileDownloader was registered. +*/ +QStringList FileDownloaderFactory::supportedSchemes() const +{ + return availableProducts(); +} diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterfiledownloaderfactory.h b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterfiledownloaderfactory.h new file mode 100644 index 000000000..b4c284640 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterfiledownloaderfactory.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KD_UPDATER_FILE_DOWNLOADER_FACTORY_H +#define KD_UPDATER_FILE_DOWNLOADER_FACTORY_H + +#include "kdupdater.h" +#include <KDToolsCore/pimpl_ptr.h> +#include <KDToolsCore/kdgenericfactory.h> + +#include <QtCore/QStringList> +#include <QtCore/QUrl> + +class QObject; + +namespace KDUpdater +{ + class FileDownloader; + class SignatureVerifier; + + class KDTOOLS_UPDATER_EXPORT FileDownloaderFactory : public KDGenericFactory< FileDownloader > + { + Q_DISABLE_COPY( FileDownloaderFactory ) + public: + static FileDownloaderFactory& instance(); + ~FileDownloaderFactory(); + + template< typename T > + void registerFileDownloader( const QString& scheme ) + { + registerProduct< T >( scheme ); + } + QStringList supportedSchemes() const; + + int fileDownloaderCount() const; + FileDownloader* create( const QString& scheme, QObject* parent ) const; + FileDownloader* create( const QString& scheme, const SignatureVerifier* verifier = 0, const QUrl& signatureUrl = QUrl(), QObject* parent = 0 ) const; + static void setFollowRedirects( bool val ); + static bool followRedirects(); + + private: + FileDownloaderFactory(); + + private: + struct FileDownloaderFactoryData; + kdtools::pimpl_ptr<FileDownloaderFactoryData> d; + }; +} + + +#endif + diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterpackagesinfo.cpp b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterpackagesinfo.cpp new file mode 100644 index 000000000..c6ce195ea --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterpackagesinfo.cpp @@ -0,0 +1,566 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "kdupdaterpackagesinfo.h" +#include "kdupdaterapplication.h" + +#include <QFileInfo> +#include <QDomDocument> +#include <QDomElement> +#include <QVector> + +using namespace KDUpdater; + +/*! + \ingroup kdupdater + \class KDUpdater::PackagesInfo kdupdaterpackagesinfo.h KDUpdaterPackagesInfo + \brief Provides access to information about packages installed on the application side. + + This class parses the XML package file specified via the setFileName() method and + provides access to the the information defined within the package file through an + easy to use API. You can: + \li get application name via the \ref applicationName() method + \li get application version via the \ref applicationVersion() method + \li get information about the number of packages installed and their meta-data via the + \ref packageInfoCount() and \ref packageInfo() methods. + + Instances of this class cannot be created. Each instance of \ref KDUpdater::Application + has one instance of this class associated with it. You can fetch a pointer to an instance + of this class for an application via the \ref KDUpdater::Application::packagesInfo() + method. +*/ + +/*! \enum UpdatePackagesInfo::Error + * Error codes related to retrieving update sources + */ + +/*! \var UpdatePackagesInfo::Error UpdatePackagesInfo::NoError + * No error occurred + */ + +/*! \var UpdatePackagesInfo::Error UpdatePackagesInfo::NotYetReadError + * The package information was not parsed yet from the XML file + */ + +/*! \var UpdatePackagesInfo::Error UpdatePackagesInfo::CouldNotReadPackageFileError + * the specified update source file could not be read (does not exist or not readable) + */ + +/*! \var UpdatePackagesInfo::Error UpdatePackagesInfo::InvalidXmlError + * The source file contains invalid XML. + */ + +/*! \var UpdatePackagesInfo::Error UpdatePackagesInfo::InvalidContentError + * The source file contains valid XML, but does not match the expected format for package descriptions + */ + +struct PackagesInfo::PackagesInfoData +{ + PackagesInfoData( PackagesInfo* qq ) : + q( qq ), + application(0), + error(PackagesInfo::NotYetReadError), + compatLevel(-1), + modified( false ) + {} + PackagesInfo* q; + Application* application; + QString errorMessage; + PackagesInfo::Error error; + QString fileName; + QString applicationName; + QString applicationVersion; + int compatLevel; + bool modified; + + QVector<PackageInfo> packageInfoList; + + void addPackageFrom(const QDomElement& packageE); + void setInvalidContentError( const QString& detail ); +}; + +void PackagesInfo::PackagesInfoData::setInvalidContentError(const QString& detail) +{ + error = PackagesInfo::InvalidContentError; + errorMessage = tr("%1 contains invalid content: %2").arg(fileName, detail); +} + +/*! + \internal +*/ +PackagesInfo::PackagesInfo(Application* application) + : QObject(application), + d( new PackagesInfoData( this ) ) +{ + d->application = application; +} + +/*! + \internal +*/ +PackagesInfo::~PackagesInfo() +{ + writeToDisk(); + delete d; +} + +/*! + Returns a pointer to the application, whose package information this class provides + access to. +*/ +Application* PackagesInfo::application() const +{ + return d->application; +} + +/*! + Returns true if the PackagesInfo are valid else false is returned in which case + the \a errorString() method can be used to receive a describing error message. +*/ +bool PackagesInfo::isValid() const +{ + return d->error == NoError; +} + +/*! + Returns a human-readable error message. +*/ +QString PackagesInfo::errorString() const +{ + return d->errorMessage; +} + +PackagesInfo::Error PackagesInfo::error() const +{ + return d->error; +} + +/*! + Sets the complete file name of the Packages.xml file. The function also issues a call to + \ref refresh() to reload package information from the XML file. + + \sa KDUpdater::Application::setPackagesXMLFileName() +*/ +void PackagesInfo::setFileName(const QString& fileName) +{ + if( d->fileName == fileName ) + return; + + d->fileName = fileName; + refresh(); +} + +/*! + Returns the name of the Packages.xml file that this class referred to. +*/ +QString PackagesInfo::fileName() const +{ + return d->fileName; +} + +/*! + Sets the application name. By default this is the name specified in + the ApplicationName XML element of the Packages.xml file. +*/ +void PackagesInfo::setApplicationName(const QString& name) +{ + d->applicationName = name; + d->modified = true; +} + +/*! + Returns the application name. +*/ +QString PackagesInfo::applicationName() const +{ + return d->applicationName; +} + +/*! + Sets the application version. By default this is the version specified + in the ApplicationVersion XML element of Packages.xml. +*/ +void PackagesInfo::setApplicationVersion(const QString& version) +{ + d->applicationVersion = version; + d->modified = true; +} + +/*! + Returns the application version. +*/ +QString PackagesInfo::applicationVersion() const +{ + return d->applicationVersion; +} + +/*! + Returns the number of \ref KDUpdater::PackageInfo objects contained in this class. +*/ +int PackagesInfo::packageInfoCount() const +{ + return d->packageInfoList.count(); +} + +/*! + Returns the package info structure (\ref KDUpdater::PackageInfo) at index. If index is + out of range then an empty package info structure is returned. +*/ +PackageInfo PackagesInfo::packageInfo(int index) const +{ + if( index < 0 || index >= d->packageInfoList.count() ) + return PackageInfo(); + + return d->packageInfoList[index]; +} + +/*! + Returns the compat level of the application. +*/ +int PackagesInfo::compatLevel() const +{ + return d->compatLevel; +} + +/*! + This function returns the index of the package whose name is \c pkgName. If no such + package was found, this function returns -1. +*/ +int PackagesInfo::findPackageInfo(const QString& pkgName) const +{ + for(int i=0; i<d->packageInfoList.count(); i++) + { + if( d->packageInfoList[i].name == pkgName ) + return i; + } + + return -1; +} + +/*! + Returns all package info structures. +*/ +QVector<PackageInfo> PackagesInfo::packageInfos() const +{ + return d->packageInfoList; +} + +/*! + This function re-reads the Packages.xml file and updates itself. Changes to \ref applicationName() + and \ref applicationVersion() are lost after this function returns. The function emits a reset() + signal after completion. +*/ +void PackagesInfo::refresh() +{ + // First clear internal variables + d->applicationName.clear(); + d->applicationVersion.clear(); + d->packageInfoList.clear(); + d->modified = false; + + QFile file( d->fileName ); + + // if the file does not exist then we just skip the reading + if( !file.exists() ) + { + d->error = NoError; + d->errorMessage.clear(); + emit reset(); + return; + } + + // Open Packages.xml + if( !file.open(QFile::ReadOnly) ) + { + d->error = CouldNotReadPackageFileError; + d->errorMessage = tr("Could not read \"%1\"").arg(d->fileName); + emit reset(); + return; + } + + // Parse the XML document + QDomDocument doc; + QString parseErrorMessage; + int parseErrorLine; + int parseErrorColumn; + if( !doc.setContent( &file, &parseErrorMessage, &parseErrorLine, &parseErrorColumn ) ) + { + d->error = InvalidXmlError; + d->errorMessage = tr("Parse error in %1 at %2, %3: %4") + .arg(d->fileName, + QString::number(parseErrorLine), + QString::number(parseErrorColumn), + parseErrorMessage); + emit reset(); + return; + } + file.close(); + + // Now populate information from the XML file. + QDomElement rootE = doc.documentElement(); + if( rootE.tagName() != QLatin1String( "Packages" ) ) + { + d->setInvalidContentError(tr("root element %1 unexpected, should be \"Packages\"").arg(rootE.tagName())); + emit reset(); + return; + } + + QDomNodeList childNodes = rootE.childNodes(); + for(int i=0; i<childNodes.count(); i++) + { + QDomNode childNode = childNodes.item(i); + QDomElement childNodeE = childNode.toElement(); + if( childNodeE.isNull() ) + continue; + + if( childNodeE.tagName() == QLatin1String( "ApplicationName" ) ) + d->applicationName = childNodeE.text(); + else if( childNodeE.tagName() == QLatin1String( "ApplicationVersion" ) ) + d->applicationVersion = childNodeE.text(); + else if( childNodeE.tagName() == QLatin1String( "Package" ) ) + d->addPackageFrom( childNodeE ); + else if( childNodeE.tagName() == QLatin1String( "CompatLevel" ) ) + d->compatLevel = childNodeE.text().toInt(); + } + + d->error = NoError; + d->errorMessage.clear(); + emit reset(); +} + +/*! + Sets the application compat level. +*/ +void PackagesInfo::setCompatLevel(int level) +{ + d->compatLevel = level; + d->modified = true; +} + +/*! + Marks the package with \a name as installed in \a version. + */ +bool PackagesInfo::installPackage( const QString& name, const QString& version, const QString& title, const QString& description + , const QStringList& dependencies, bool forcedInstallation, bool virtualComp, quint64 uncompressedSize ) +{ + if( findPackageInfo( name ) != -1 ) + return updatePackage( name, version, QDate::currentDate() ); + + PackageInfo info; + info.name = name; + info.version = version; + info.installDate = QDate::currentDate(); + info.title = title; + info.description = description; + info.dependencies = dependencies; + info.forcedInstallation = forcedInstallation; + info.virtualComp = virtualComp; + info.uncompressedSize = uncompressedSize; + d->packageInfoList.push_back( info ); + d->modified = true; + return true; +} + +/*! + Update the package. +*/ +bool PackagesInfo::updatePackage(const QString &name, + const QString &version, + const QDate &date) +{ + int index = findPackageInfo(name); + + if (index==-1) return false; + + d->packageInfoList[index].version = version; + d->packageInfoList[index].lastUpdateDate = date; + d->modified = true; + return true; +} + +/*! + Remove the package with \a name. + */ +bool PackagesInfo::removePackage( const QString& name ) +{ + const int index = findPackageInfo( name ); + if( index == -1 ) + return false; + + d->packageInfoList.remove( index ); + d->modified = true; + return true; +} + +static void addTextChildHelper(QDomNode *node, + const QString &tag, + const QString &text) +{ + QDomElement domElement = node->ownerDocument().createElement(tag); + QDomText domText = node->ownerDocument().createTextNode(text); + + domElement.appendChild(domText); + node->appendChild(domElement); +} + +void PackagesInfo::writeToDisk() +{ + if( d->modified && ( !d->packageInfoList.isEmpty() || QFile::exists( d->fileName ) ) ) + { + QDomDocument doc; + QDomElement root = doc.createElement(QLatin1String( "Packages") ) ; + doc.appendChild(root); + + addTextChildHelper(&root, QLatin1String( "ApplicationName" ), d->applicationName); + addTextChildHelper(&root, QLatin1String( "ApplicationVersion" ), d->applicationVersion); + if (d->compatLevel!=-1) { + addTextChildHelper(&root, QLatin1String( "CompatLevel" ), QString::number(d->compatLevel)); + } + + Q_FOREACH (const PackageInfo &info, d->packageInfoList) { + QDomElement package = doc.createElement( QLatin1String( "Package" ) ); + + addTextChildHelper( &package, QLatin1String( "Name" ), info.name ); + addTextChildHelper( &package, QLatin1String( "Pixmap" ), info.pixmap ); + addTextChildHelper( &package, QLatin1String( "Title" ), info.title ); + addTextChildHelper( &package, QLatin1String( "Description" ), info.description ); + addTextChildHelper( &package, QLatin1String( "Version" ), info.version ); + addTextChildHelper( &package, QLatin1String( "LastUpdateDate" ), info.lastUpdateDate.toString( Qt::ISODate ) ); + addTextChildHelper( &package, QLatin1String( "InstallDate" ), info.installDate.toString( Qt::ISODate) ); + addTextChildHelper( &package, QLatin1String( "Size" ), QString::number( info.uncompressedSize ) ); + QString assembledDependencies = QLatin1String( "" ); + Q_FOREACH( const QString & val, info.dependencies ){ + assembledDependencies += val + QLatin1String( "," ); + } + if ( info.dependencies.count() > 0 ) + assembledDependencies.chop( 1 ); + addTextChildHelper( &package, QLatin1String( "Dependencies" ), assembledDependencies ); + if ( info.forcedInstallation ) + addTextChildHelper( &package, QLatin1String( "ForcedInstallation" ), QLatin1String( "true" ) ); + if ( info.virtualComp ) + addTextChildHelper( &package, QLatin1String( "Virtual" ), QLatin1String( "true" ) ); + + root.appendChild(package); + } + + // Open Packages.xml + QFile file( d->fileName ); + if( !file.open(QFile::WriteOnly) ) { + return; + } + + file.write(doc.toByteArray(4)); + file.close(); + d->modified = false; + } +} + +void PackagesInfo::PackagesInfoData::addPackageFrom(const QDomElement& packageE) +{ + if( packageE.isNull() ) + return; + + QDomNodeList childNodes = packageE.childNodes(); + if(childNodes.count() == 0) + return; + + PackageInfo info; + info.forcedInstallation = false; + info.virtualComp = false; + for(int i=0; i<childNodes.count(); i++) + { + QDomNode childNode = childNodes.item(i); + QDomElement childNodeE = childNode.toElement(); + if( childNodeE.isNull() ) + continue; + + if( childNodeE.tagName() == QLatin1String( "Name" ) ) + info.name = childNodeE.text(); + else if( childNodeE.tagName() == QLatin1String( "Pixmap" ) ) + info.pixmap = childNodeE.text(); + else if( childNodeE.tagName() == QLatin1String( "Title" ) ) + info.title = childNodeE.text(); + else if( childNodeE.tagName() == QLatin1String( "Description" ) ) + info.description = childNodeE.text(); + else if( childNodeE.tagName() == QLatin1String( "Version" ) ) + info.version = childNodeE.text(); + else if( childNodeE.tagName() == QLatin1String( "Virtual" ) ) + info.virtualComp = childNodeE.text().toLower() == QLatin1String( "true" ) ? true : false; + else if( childNodeE.tagName() == QLatin1String( "Size" ) ) + info.uncompressedSize = childNodeE.text().toULongLong(); + else if( childNodeE.tagName() == QLatin1String( "Dependencies" ) ) + info.dependencies = childNodeE.text().split( QLatin1String( "," ) ); + else if( childNodeE.tagName() == QLatin1String( "ForcedInstallation" ) ) + info.forcedInstallation = childNodeE.text().toLower() == QLatin1String( "true" ) ? true : false; + else if( childNodeE.tagName() == QLatin1String( "LastUpdateDate" ) ) + info.lastUpdateDate = QDate::fromString(childNodeE.text(), Qt::ISODate); + else if( childNodeE.tagName() == QLatin1String( "InstallDate" ) ) + info.installDate = QDate::fromString(childNodeE.text(), Qt::ISODate); + } + + this->packageInfoList.append( info ); +} + +/*! + \fn void KDUpdater::PackagesInfo::reset() + + This signal is emitted whenever the contents of this class is refreshed, usually from within + the \ref refresh() slot. +*/ + +/*! + \ingroup kdupdater + \struct KDUpdater::PackageInfo kdupdaterpackagesinfo.h KDUpdaterPackageInfo + \brief Describes a single installed package in the application. + + This structure contains information about a single installed package in the application. + The information contained in this structure corresponds to the information described + by the Package XML element in Packages.xml +*/ + +/*! + \var QString KDUpdater::PackageInfo::name +*/ + +/*! + \var QString KDUpdater::PackageInfo::pixmap +*/ + +/*! + \var QString KDUpdater::PackageInfo::title +*/ + +/*! + \var QString KDUpdater::PackageInfo::description +*/ + +/*! + \var QString KDUpdater::PackageInfo::version +*/ + +/*! + \var QDate KDUpdater::PackageInfo::lastUpdateDate +*/ + +/*! + \var QDate KDUpdater::PackageInfo::installDate +*/ diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterpackagesinfo.h b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterpackagesinfo.h new file mode 100644 index 000000000..8b38dd8d2 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterpackagesinfo.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KD_UPDATER_PACKAGES_INFO_H +#define KD_UPDATER_PACKAGES_INFO_H + +#include "kdupdater.h" +#include <QObject> +#include <QDate> +#include <QString> +#include <QStringList> +#include <QVariant> + +namespace KDUpdater +{ + class Application; + class UpdateInstaller; + + struct KDTOOLS_UPDATER_EXPORT PackageInfo + { + QString name; + QString pixmap; + QString title; + QString description; + QString version; + QStringList dependencies; + QStringList translations; + QDate lastUpdateDate; + QDate installDate; + bool forcedInstallation; + bool virtualComp; + quint64 uncompressedSize; + }; + + class KDTOOLS_UPDATER_EXPORT PackagesInfo : public QObject + { + Q_OBJECT + + public: + ~PackagesInfo(); + + enum Error + { + NoError=0, + NotYetReadError, + CouldNotReadPackageFileError, + InvalidXmlError, + InvalidContentError + }; + + Application* application() const; + + bool isValid() const; + QString errorString() const; + Error error() const; + + void setFileName(const QString& fileName); + QString fileName() const; + + void setApplicationName(const QString& name); + QString applicationName() const; + + void setApplicationVersion(const QString& version); + QString applicationVersion() const; + + int packageInfoCount() const; + PackageInfo packageInfo(int index) const; + int findPackageInfo(const QString& pkgName) const; + QVector<KDUpdater::PackageInfo> packageInfos() const; + void writeToDisk(); + + int compatLevel() const; + void setCompatLevel(int level); + + bool installPackage( const QString& pkgName, const QString& version, const QString& title = QString(), const QString& description = QString() + , const QStringList& dependencies = QStringList(), bool forcedInstallation = false, bool virtualComp = false, quint64 uncompressedSize = 0 ); + bool updatePackage(const QString &pkgName, const QString &version, const QDate &date ); + bool removePackage( const QString& pkgName ); + + public Q_SLOTS: + void refresh(); + + Q_SIGNALS: + void reset(); + + protected: + explicit PackagesInfo( Application * application=0 ); + + private: + friend class Application; + friend class UpdateInstaller; + struct PackagesInfoData; + PackagesInfoData* d; + }; + +} + +#endif diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterpackagesview.cpp b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterpackagesview.cpp new file mode 100644 index 000000000..4943c7b72 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterpackagesview.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "kdupdaterpackagesview.h" +#include "kdupdaterpackagesinfo.h" + +/*! + \ingroup kdupdater + \class KDUpdater::PackagesView kdupdaterpackagesview.h KDUpdaterPackagesView + \brief A widget that can show packages contained in \ref KDUpdater::PackagesInfo + + \ref KDUpdater::PackagesInfo, associated with \ref KDUpdater::Application, contains + information about all the packages installed in the application. This widget helps view the packages + in a list. + + \image html packagesview.jpg + + To use this widget, just create an instance and pass to \ref setPackageInfo() a pointer to + \ref KDUpdater::PackagesInfo whose information you want this widget to show. +*/ + +struct KDUpdater::PackagesView::PackagesViewData +{ + PackagesViewData( PackagesView* qq ) : + q( qq ), + packagesInfo(0) + {} + + PackagesView* q; + PackagesInfo* packagesInfo; +}; + +/*! + Constructor. +*/ +KDUpdater::PackagesView::PackagesView(QWidget* parent) + : QTreeWidget(parent), + d ( new PackagesViewData( this ) ) +{ + + setColumnCount(5); + setHeaderLabels( QStringList() << tr("Name") << tr("Title") + << tr("Description") << tr("Version") + << tr("Last Updated") ); + setRootIsDecorated(false); +} + +/*! + Destructor +*/ +KDUpdater::PackagesView::~PackagesView() +{ + delete d; +} + +/*! + Sets the package info whose information this widget should show. + + \code + KDUpdater::Application application; + + KDUpdater::PackagesView packageView; + packageView.setPackageInfo( application.packagesInfo() ); + packageView.show(); + \endcode + +*/ +void KDUpdater::PackagesView::setPackageInfo(KDUpdater::PackagesInfo* packagesInfo) +{ + if( d->packagesInfo == packagesInfo ) + return; + + if(d->packagesInfo) + disconnect(d->packagesInfo, 0, this, 0); + + d->packagesInfo = packagesInfo; + if(d->packagesInfo) + connect(d->packagesInfo, SIGNAL(reset()), this, SLOT(refresh())); + + refresh(); +} + +/*! + Returns a pointer to the package info whose information this widget is showing. +*/ +KDUpdater::PackagesInfo* KDUpdater::PackagesView::packagesInfo() const +{ + return d->packagesInfo; +} + +/*! + This slot reloads package information from the \ref KDUpdater::PackagesInfo associated + with this widget. + + \note By default, this slot is connected to the \ref KDUpdater::PackagesInfo::reset() + signal in \ref setPackageInfo() +*/ +void KDUpdater::PackagesView::refresh() +{ + this->clear(); + if( !d->packagesInfo ) + return; + + Q_FOREACH(const KDUpdater::PackageInfo& info, d->packagesInfo->packageInfos()) + { + QTreeWidgetItem* item = new QTreeWidgetItem(this); + item->setText(0, info.name); + item->setText(1, info.title); + item->setText(2, info.description); + item->setText(3, info.version); + item->setText(4, info.lastUpdateDate.toString()); + } + + resizeColumnToContents(0); +} diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterpackagesview.h b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterpackagesview.h new file mode 100644 index 000000000..8f42b1e19 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterpackagesview.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KD_UPDATER_PACKAGES_VIEW_H +#define KD_UPDATER_PACKAGES_VIEW_H + +#include "kdupdater.h" +#include <QTreeWidget> + +namespace KDUpdater +{ + class PackagesInfo; + + class KDTOOLS_UPDATER_EXPORT PackagesView : public QTreeWidget + { + Q_OBJECT + + public: + explicit PackagesView(QWidget* parent=0); + ~PackagesView(); + + void setPackageInfo(PackagesInfo* packageInfo); + PackagesInfo* packagesInfo() const; + + public Q_SLOTS: + void refresh(); + + private: + struct PackagesViewData; + PackagesViewData* d; + }; +} + +#endif diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatersignatureverificationresult.cpp b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatersignatureverificationresult.cpp new file mode 100644 index 000000000..0b1f61a57 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatersignatureverificationresult.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "kdupdatersignatureverificationresult.h" + +#include <QSharedData> +#include <QString> + +#include <algorithm> + +using namespace KDUpdater; + +class SignatureVerificationResult::Private : public QSharedData { +public: + Private() : QSharedData(), validity( SignatureVerificationResult::UnknownValidity ) { + } + Private( const Private& other ) : QSharedData( other ), validity( other.validity ), errorString( other.errorString ) { + } + + bool operator==( const Private& other ) const { + return validity == other.validity && errorString == other.errorString; + } + + SignatureVerificationResult::Validity validity; + QString errorString; +}; + +SignatureVerificationResult::SignatureVerificationResult( Validity validity ) + : d( new Private ) +{ + setValidity( validity ); +} + +SignatureVerificationResult::SignatureVerificationResult( const SignatureVerificationResult& other ) : d( other.d ) { +} + +SignatureVerificationResult::~SignatureVerificationResult() { +} + +SignatureVerificationResult& SignatureVerificationResult::operator=( const SignatureVerificationResult& other ) { + SignatureVerificationResult copy( other ); + std::swap( d, copy.d ); + return *this; +} + +bool SignatureVerificationResult::operator==( const SignatureVerificationResult& other ) const { + return *d == *other.d; +} + +bool SignatureVerificationResult::isValid() const { + return d->validity == ValidSignature; +} + +SignatureVerificationResult::Validity SignatureVerificationResult::validity() const { + return d->validity; +} + +void SignatureVerificationResult::setValidity( Validity validity ) { + d->validity = validity; +} + +QString SignatureVerificationResult::errorString() const { + return d->errorString; +} + +void SignatureVerificationResult::setErrorString( const QString& errorString ) { + d->errorString = errorString; +} + diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatersignatureverificationresult.h b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatersignatureverificationresult.h new file mode 100644 index 000000000..5319d35f3 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatersignatureverificationresult.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KD_UPDATER_SIGNATUREVERIFICATIONRESULT_H +#define KD_UPDATER_SIGNATUREVERIFICATIONRESULT_H + +#include "kdupdater.h" + +#include <QtCore/QMetaType> +#include <QtCore/QSharedDataPointer> + +class QString; + +namespace KDUpdater { + class KDTOOLS_UPDATER_EXPORT SignatureVerificationResult { + public: + enum Validity { + ValidSignature=0, + UnknownValidity, + InvalidSignature, + BadSignature + }; + + explicit SignatureVerificationResult( Validity validity = UnknownValidity ); + SignatureVerificationResult( const SignatureVerificationResult& other ); + ~SignatureVerificationResult(); + + SignatureVerificationResult& operator=( const SignatureVerificationResult& other ); + bool operator==( const SignatureVerificationResult& other ) const; + + bool isValid() const; + Validity validity() const; + void setValidity( Validity validity ); + + QString errorString() const; + void setErrorString( const QString& errorString ); + + private: + class Private; + QSharedDataPointer<Private> d; + }; +} + +Q_DECLARE_METATYPE( KDUpdater::SignatureVerificationResult ) + +#endif // KD_UPDATER_SIGNATUREVERIFICATIONRESULT_H diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatersignatureverificationrunnable.cpp b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatersignatureverificationrunnable.cpp new file mode 100644 index 000000000..19c6337e4 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatersignatureverificationrunnable.cpp @@ -0,0 +1,135 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "kdupdatersignatureverificationrunnable.h" +#include "kdupdatersignatureverifier.h" +#include "kdupdatersignatureverificationresult.h" + +#include <QByteArray> +#include <QIODevice> +#include <QMetaObject> +#include <QObject> +#include <QPointer> +#include <QThreadPool> +#include <QVariant> +#include <QVector> + +#include <cassert> + +using namespace KDUpdater; + +class Runnable::Private { +public: + QVector<QObject*> receivers; + QVector<QByteArray> methods; +}; + +Runnable::Runnable() : QRunnable(), d( new Private ) { +} + +Runnable::~Runnable() { +} + + +void Runnable::addResultListener( QObject* receiver, const char* method ) { + d->receivers.push_back( receiver ); + d->methods.push_back( QByteArray( method ) ); +} + +void Runnable::emitResult( const QGenericArgument& arg0, + const QGenericArgument& arg1, + const QGenericArgument& arg2, + const QGenericArgument& arg3, + const QGenericArgument& arg4, + const QGenericArgument& arg5, + const QGenericArgument& arg6, + const QGenericArgument& arg7, + const QGenericArgument& arg8, + const QGenericArgument& arg9 ) { + assert( d->receivers.size() == d->methods.size() ); + for ( int i = 0; i < d->receivers.size(); ++i ) { + QMetaObject::invokeMethod( d->receivers[i], + d->methods[i].constData(), + Qt::QueuedConnection, + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + arg7, + arg8, + arg9 ); + } +} + +class SignatureVerificationRunnable::Private { +public: + Private() : verifier( 0 ) {} + const SignatureVerifier* verifier; + QPointer<QIODevice> device; + QByteArray signature; +}; + +SignatureVerificationRunnable::SignatureVerificationRunnable() : Runnable(), d( new Private ) { +} + +SignatureVerificationRunnable::~SignatureVerificationRunnable() { +} + +const SignatureVerifier* SignatureVerificationRunnable::verifier() const { + return d->verifier; +} + +void SignatureVerificationRunnable::setVerifier( const SignatureVerifier* verifier ) { + delete d->verifier; + d->verifier = verifier ? verifier->clone() : 0; +} + +QByteArray SignatureVerificationRunnable::signature() const { + return d->signature; +} + +void SignatureVerificationRunnable::setSignature( const QByteArray& sig ) { + d->signature = sig; +} + +QIODevice* SignatureVerificationRunnable::data() const { + return d->device; +} + +void SignatureVerificationRunnable::setData( QIODevice* device ) { + d->device = device; +} + + +void SignatureVerificationRunnable::run() { + QThreadPool::globalInstance()->releaseThread(); + const SignatureVerificationResult result = d->verifier->verify( d->device->readAll(), d->signature ); + QThreadPool::globalInstance()->reserveThread(); + delete d->verifier; + delete d->device; + emitResult( Q_ARG( KDUpdater::SignatureVerificationResult, result ) ); +} + + diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatersignatureverificationrunnable.h b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatersignatureverificationrunnable.h new file mode 100644 index 000000000..afc201bea --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatersignatureverificationrunnable.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KDUPDATERSIGNATUREVERIFICATIONJOB_H +#define KDUPDATERSIGNATUREVERIFICATIONJOB_H + +#include <KDToolsCore/pimpl_ptr.h> +#include <QtCore/QGenericArgument> +#include <QtCore/QRunnable> + +class QByteArray; +class QIODevice; +class QObject; +template <typename T> class QVector; + +namespace KDUpdater { + class SignatureVerifier; + class SignatureVerificationResult; + + class Runnable : public QRunnable { + public: + Runnable(); + ~Runnable(); + + void addResultListener( QObject* receiver, const char* method ); + + protected: + void emitResult( const QGenericArgument& arg0=QGenericArgument( 0 ), + const QGenericArgument& arg1=QGenericArgument(), + const QGenericArgument& arg2=QGenericArgument(), + const QGenericArgument& arg3=QGenericArgument(), + const QGenericArgument& arg4=QGenericArgument(), + const QGenericArgument& arg5=QGenericArgument(), + const QGenericArgument& arg6=QGenericArgument(), + const QGenericArgument& arg7=QGenericArgument(), + const QGenericArgument& arg8=QGenericArgument(), + const QGenericArgument& arg9=QGenericArgument() ); + + private: + class Private; + kdtools::pimpl_ptr<Private> d; + }; + + class SignatureVerificationRunnable : public Runnable { + public: + explicit SignatureVerificationRunnable(); + ~SignatureVerificationRunnable(); + + const SignatureVerifier* verifier() const; + void setVerifier( const SignatureVerifier* verifier ); + + QByteArray signature() const; + void setSignature( const QByteArray& sig ); + + QIODevice* data() const; + void setData( QIODevice* device ); + + /* reimp */ void run(); + + private: + class Private; + kdtools::pimpl_ptr<Private> d; + }; +} + +#endif // KDUPDATERSIGNATUREVERIFICATIONJOB_H diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatersignatureverifier.cpp b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatersignatureverifier.cpp new file mode 100644 index 000000000..28cba7098 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatersignatureverifier.cpp @@ -0,0 +1,46 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "kdupdatersignatureverifier.h" +#include "kdupdatersignatureverificationresult.h" + +#include <QFile> + +using namespace KDUpdater; + +SignatureVerifier::~SignatureVerifier() +{ +} + +SignatureVerificationResult SignatureVerifier::verify( const QString& dataFile, const QString& signatureFile ) const +{ + QFile dFile( dataFile ); + QFile sFile( signatureFile ); + + if( !dFile.open( QIODevice::ReadOnly ) ) + return SignatureVerificationResult(); + + if( !sFile.open( QIODevice::ReadOnly ) ) + return SignatureVerificationResult( SignatureVerificationResult::BadSignature ); + + return verify( dFile.readAll(), sFile.readAll() ); +} diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatersignatureverifier.h b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatersignatureverifier.h new file mode 100644 index 000000000..dfb8cbddb --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatersignatureverifier.h @@ -0,0 +1,46 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KD_UPDATER_SIGNATUREVERIFIER_H +#define KD_UPDATER_SIGNATUREVERIFIER_H + +#include "kdupdater.h" + +class QByteArray; +class QIODevice; +class QString; + +namespace KDUpdater { + + class SignatureVerificationResult; + + class KDTOOLS_UPDATER_EXPORT SignatureVerifier { + public: + virtual ~SignatureVerifier(); + virtual SignatureVerifier* clone() const = 0; + virtual SignatureVerificationResult verify( const QByteArray &data, const QByteArray& signature ) const = 0; + virtual SignatureVerificationResult verify( const QString& dataFile, const QString& signatureFile ) const; + virtual QString type() const = 0; + }; +} + +#endif // KD_UPDATER_SIGNATUREVERIFIER_H diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatertask.cpp b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatertask.cpp new file mode 100644 index 000000000..ee8d5aca8 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatertask.cpp @@ -0,0 +1,440 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "kdupdatertask.h" + +/*! + \ingroup kdupdater + \class KDUpdater::Task kdupdatertask.h KDUpdaterTask + \brief Base class for all task classes in KDUpdater + + This class is the base class for all task classes in KDUpdater. Task is an activity that + occupies certain amount of execution time. It can be started, stopped (or canceled), paused and + resumed. Tasks can report progress and error messages which an application can show in any + sort of UI. The KDUpdater::Task class provides a common interface for dealing with all kinds of + tasks in KDUpdater. The class diagram show in this class documentation will help in pointing out + the task classes in KDUpdater. + + User should be carefull of these points: + \li Instances of this class cannot be created. Only instance of the subclasses can be created + \li Task classes can be started only once. +*/ + +struct KDUpdater::Task::TaskData +{ + TaskData( Task* qq) : + q( qq ) + { + caps = KDUpdater::Task::NoCapability; + errorCode = 0; + started = false; + finished = false; + paused = false; + stopped = false; + progressPc = 0; + } + + Task* q; + int caps; + QString name; + int errorCode; + QString errorText; + bool started; + bool finished; + bool paused; + bool stopped; + int progressPc; + QString progressText; +}; + +/*! + \internal +*/ +KDUpdater::Task::Task(const QString& name, int caps, QObject* parent) + : QObject(parent), + d( new TaskData( this ) ) +{ + d->caps = caps; + d->name = name; +}; + +/*! + \internal +*/ +KDUpdater::Task::~Task() +{ + delete d; +} + +/*! + Returns the name of the task. +*/ +QString KDUpdater::Task::name() const +{ + return d->name; +} + +/*! + Returns the capabilities of the task. It is a combination of one or more + Capability flags. Defined as follows + \code + enum KDUpdater::Task::Capability + { + NoCapability = 0, + Pausable = 1, + Stoppable = 2 + }; + \endcode +*/ +int KDUpdater::Task::capabilities() const +{ + return d->caps; +} + +/*! + Returns the last reported error code. +*/ +int KDUpdater::Task::error() const +{ + return d->errorCode; +} + +/*! + Returns the last reported error message text. +*/ +QString KDUpdater::Task::errorString() const +{ + return d->errorText; +} + +/*! + Returns whether the task has started and is running or not. +*/ +bool KDUpdater::Task::isRunning() const +{ + return d->started; +} + +/*! + Returns whether the task has finished or not. + + \note Stopped (or canceled) tasks are not finished tasks. +*/ +bool KDUpdater::Task::isFinished() const +{ + return d->finished; +} + +/*! + Returns whether the task is paused or not. +*/ +bool KDUpdater::Task::isPaused() const +{ + return d->paused; +} + +/*! + Returns whether the task is stopped or not. + + \note Finished tasks are not stopped classes. +*/ +bool KDUpdater::Task::isStopped() const +{ + return d->stopped; +} + +/*! + Returns the progress in percentage made by this task. +*/ +int KDUpdater::Task::progressPercent() const +{ + return d->progressPc; +} + +/*! + Returns a string that describes the progress made by this task as a string. +*/ +QString KDUpdater::Task::progressText() const +{ + return d->progressText; +} + +/*! + Starts the task. +*/ +void KDUpdater::Task::run() +{ + if(d->started) + { + qDebug("Trying to start an already started task"); + return; + } + + if(d->finished || d->stopped) + { + qDebug("Trying to start a finished or canceled task"); + return; + } + + d->stopped = false; + d->finished = false; // for the sake of completeness + d->started = true; + emit started(); + reportProgress(0, tr("%1 started").arg(d->name)); + + doRun(); +} + +/*! + Stops the task, provided the task has \ref Stoppable capability. + + \note Once the task is stopped, it cannot be restarted. +*/ +void KDUpdater::Task::stop() +{ + if( !(d->caps & Stoppable) ) + { + const QString errorMsg = tr("'%1' cannot be stopped").arg(d->name); + reportError(KDUpdater::ECannotStopTask, errorMsg); + return; + } + + if(!d->started) + { + qDebug("Trying to stop an unstarted task"); + return; + } + + if(d->finished || d->stopped) + { + qDebug("Trying to stop a finished or canceled task"); + return; + } + + d->stopped = doStop(); + if(!d->stopped) + { + const QString errorMsg = tr("Cannot stop task '%1'").arg(d->name); + reportError(KDUpdater::ECannotStopTask, errorMsg); + return; + } + + d->started = false; // the task is not running + d->finished = false; // the task is not finished, but was canceled half-way through + + emit stopped(); + deleteLater(); +} + +/*! + Paused the task, provided the task has \ref Pausable capability. +*/ +void KDUpdater::Task::pause() +{ + if( !(d->caps & Pausable) ) + { + const QString errorMsg = tr("'%1' cannot be paused").arg(d->name); + reportError(KDUpdater::ECannotPauseTask, errorMsg); + return; + } + + if(!d->started) + { + qDebug("Trying to pause an unstarted task"); + return; + } + + if(d->finished || d->stopped) + { + qDebug("Trying to pause a finished or canceled task"); + return; + } + + d->paused = doPause(); + if(!d->paused) + { + const QString errorMsg = tr("Cannot pause task '%1'").arg(d->name); + reportError(KDUpdater::ECannotPauseTask, errorMsg); + return; + } + + // The task state has to be started, paused but not finished or stopped. + // We need not set the flags below, but just in case. + // Perhaps we should do Q_ASSERT() ??? + d->started = true; + d->finished = false; + d->stopped = false; + + emit paused(); +} + +/*! + Resumes the task if it was paused. +*/ +void KDUpdater::Task::resume() +{ + if(!d->paused) + { + qDebug("Trying to resume an unpaused task"); + return; + } + + const bool val = doResume(); + if(!val) + { + const QString errorMsg = tr("Cannot resume task '%1'").arg(d->name); + reportError(KDUpdater::ECannotResumeTask, errorMsg); + return; + } + + // The task state should be started, but not paused, finished or stopped. + // We need not set the flags below, but just in case. + // Perhaps we should do Q_ASSERT() ??? + d->started = true; + d->paused = false; + d->finished = false; + d->stopped = false; + + emit resumed(); +} + +/*! + \internal +*/ +void KDUpdater::Task::reportProgress(int percent, const QString& text) +{ + if(d->progressPc == percent) + return; + + d->progressPc = percent; + d->progressText = text; + emit progressValue( d->progressPc ); + emit progressText( d->progressText ); +} + +/*! + \internal +*/ +void KDUpdater::Task::reportError(int errorCode, const QString& errorText) +{ + d->errorCode = errorCode; + d->errorText = errorText; + + emit error(d->errorCode, d->errorText); + deleteLater(); +} + +/*! + \internal +*/ +void KDUpdater::Task::reportDone() +{ + QString msg = tr("%1 done"); + reportProgress(100, msg); + + // State should be finished, but not started, paused or stopped. + d->finished = true; + d->started = false; + d->paused = false; + d->stopped = false; + d->errorCode = 0; + d->errorText.clear(); + + emit finished(); + deleteLater(); +} + +/*! + \fn virtual bool KDUpdater::Task::doStart() = 0; +*/ + +/*! + \fn virtual bool KDUpdater::Task::doStop() = 0; +*/ + +/*! + \fn virtual bool KDUpdater::Task::doPause() = 0; +*/ + +/*! + \fn virtual bool KDUpdater::Task::doResume() = 0; +*/ + +/*! + \signal void KDUpdater::Task::error(int code, const QString& errorText) + + This signal is emitted to notify an error during the execution of this task. + \param code Error code + \param errorText A string describing the error. + + Error codes are just integers, there are however built in errors represented + by the KDUpdater::Error enumeration + \code + enum Error + { + ECannotStartTask, + ECannotPauseTask, + ECannotResumeTask, + ECannotStopTask, + EUnknown + }; + \endcode +*/ + +/*! + \signal void KDUpdater::Task::progress(int percent, const QString& progressText) + + This signal is emitted to nofity progress made by the task. + + \param percent Percentage of progress made + \param progressText A string describing the progress made +*/ + +/*! + \signal void KDUpdater::Task::started() + + This signal is emitted when the task has started. +*/ + +/*! + \signal void KDUpdater::Task::paused() + + This signal is emitted when the task has paused. +*/ + +/*! + \signal void KDUpdater::Task::resumed() + + This signal is emitted when the task has resumed. +*/ + +/*! + \signal void KDUpdater::Task::stopped() + + This signal is emitted when the task has stopped (or canceled). +*/ + +/*! + \signal void KDUpdater::Task::finished() + + This signal is emitted when the task has finished. +*/ diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatertask.h b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatertask.h new file mode 100644 index 000000000..84dada516 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdatertask.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KD_UPDATER_TASK_H +#define KD_UPDATER_TASK_H + +#include "kdupdater.h" +#include <QObject> + +namespace KDUpdater +{ + class KDTOOLS_UPDATER_EXPORT Task : public QObject + { + Q_OBJECT + + public: + enum Capability + { + NoCapability = 0, + Pausable = 1, + Stoppable = 2 + }; + + virtual ~Task(); + + QString name() const; + int capabilities() const; + + int error() const; + QString errorString() const; + + bool isRunning() const; + bool isFinished() const; + bool isPaused() const; + bool isStopped() const; + + int progressPercent() const; + QString progressText() const; + + public Q_SLOTS: + void run(); + void stop(); + void pause(); + void resume(); + + Q_SIGNALS: + void error(int code, const QString& errorText); + void progressValue(int percent); + void progressText(const QString& progressText); + void started(); + void paused(); + void resumed(); + void stopped(); + void finished(); + + protected: + explicit Task(const QString& name, int caps=NoCapability, QObject* parent=0); + void reportProgress(int percent, const QString& progressText); + void reportError(int errorCode, const QString& errorText); + void reportDone(); + + void reportError(const QString& errorText) + { + reportError(EUnknown, errorText); + } + + protected: + // Task interface + virtual void doRun() = 0; + virtual bool doStop() = 0; + virtual bool doPause() = 0; + virtual bool doResume() = 0; + + private: + struct TaskData; + TaskData* d; + }; +} + +#endif diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterufcompresscommon.cpp b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterufcompresscommon.cpp new file mode 100644 index 000000000..4a93b92c2 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterufcompresscommon.cpp @@ -0,0 +1,131 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "kdupdaterufcompresscommon_p.h" + +#include <QCryptographicHash> +#include <QDataStream> + +using namespace KDUpdater; + +bool UFHeader::isValid() const +{ + return magic == QLatin1String( KD_UPDATER_UF_HEADER_MAGIC ) && + fileList.count() == permList.count() && + fileList.count() == isDirList.count(); +} + +void UFHeader::addToHash(QCryptographicHash& hash) const +{ + QByteArray data; + QDataStream stream( &data, QIODevice::WriteOnly ); + stream << *this; + hash.addData(data); +} + +UFEntry::UFEntry() + : permissions( 0 ) +{ +} + +bool UFEntry::isValid() const +{ + return !fileName.isEmpty(); +} + +void UFEntry::addToHash(QCryptographicHash& hash) const +{ + QByteArray data; + QDataStream stream( &data, QIODevice::WriteOnly ); + stream.setVersion( QDataStream::Qt_4_2 ); + stream << *this; + hash.addData(data); +} + +namespace KDUpdater +{ + +QDataStream& operator<<( QDataStream& stream, const UFHeader& hdr ) +{ + stream << hdr.magic; + stream << hdr.fileList; + stream << hdr.permList; + stream << hdr.isDirList; + return stream; +} + +QDataStream& operator>>( QDataStream& stream, UFHeader& hdr ) +{ + const QDataStream::Status oldStatus = stream.status(); + stream >> hdr.magic; + if( stream.status() == QDataStream::Ok && hdr.magic != QLatin1String( KD_UPDATER_UF_HEADER_MAGIC ) ) + stream.setStatus( QDataStream::ReadCorruptData ); + + if( stream.status() == QDataStream::Ok ) + stream >> hdr.fileList; + + if( stream.status() == QDataStream::Ok ) + stream >> hdr.permList; + + if( stream.status() == QDataStream::Ok ) + stream >> hdr.isDirList; + + if( stream.status() == QDataStream::Ok && ( hdr.fileList.count() != hdr.permList.count() || hdr.permList.count() != hdr.isDirList.count() ) ) + stream.setStatus( QDataStream::ReadCorruptData ); + + if( stream.status() != QDataStream::Ok ) + hdr = UFHeader(); + + if( oldStatus != QDataStream::Ok ) + stream.setStatus( oldStatus ); + + return stream; +} + +QDataStream& operator<<( QDataStream& stream, const UFEntry& entry ) +{ + stream << entry.fileName; + stream << entry.permissions; + stream << entry.fileData; + return stream; +} + +QDataStream& operator>>( QDataStream& stream, UFEntry& entry ) +{ + const QDataStream::Status oldStatus = stream.status(); + if( stream.status() == QDataStream::Ok ) + stream >> entry.fileName; + if( stream.status() == QDataStream::Ok ) + stream >> entry.permissions; + if( stream.status() == QDataStream::Ok ) + stream >> entry.fileData; + + if( stream.status() != QDataStream::Ok ) + entry = UFEntry(); + + if( oldStatus != QDataStream::Ok ) + stream.setStatus( oldStatus ); + + return stream; +} + +} diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterufcompresscommon_p.h b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterufcompresscommon_p.h new file mode 100644 index 000000000..65f6ea5cb --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterufcompresscommon_p.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef __KDTOOLS_KDUPDATE_UFCOMPRESSCOMMON_P_H__ +#define __KDTOOLS_KDUPDATE_UFCOMPRESSCOMMON_P_H__ + +#define KD_UPDATER_UF_HEADER_MAGIC "KDVCLZ" + +#include <KDToolsCore/kdtoolsglobal.h> + +#include <QtCore/QStringList> +#include <QtCore/QByteArray> +#include <QtCore/QVector> + +class QCryptographicHash; +class QDataStream; + +namespace KDUpdater +{ + struct KDTOOLS_UPDATER_EXPORT UFHeader + { + QString magic; + QStringList fileList; + QVector<quint64> permList; + QList<bool> isDirList; + + bool isValid() const; + + void addToHash( QCryptographicHash& hash ) const; + }; + + struct KDTOOLS_UPDATER_EXPORT UFEntry + { + QString fileName; + quint64 permissions; + QByteArray fileData; + + UFEntry(); + + bool isValid() const; + + void addToHash(QCryptographicHash& hash) const; + }; + + KDTOOLS_UPDATER_EXPORT QDataStream& operator<<( QDataStream& stream, const UFHeader& hdr ); + KDTOOLS_UPDATER_EXPORT QDataStream& operator>>( QDataStream& stream, UFHeader& hdr ); + + KDTOOLS_UPDATER_EXPORT QDataStream& operator<<( QDataStream& stream, const UFEntry& entry ); + KDTOOLS_UPDATER_EXPORT QDataStream& operator>>( QDataStream& stream, UFEntry& entry ); +} + +#endif diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterufuncompressor.cpp b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterufuncompressor.cpp new file mode 100644 index 000000000..f5c36e44f --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterufuncompressor.cpp @@ -0,0 +1,220 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "kdupdaterufuncompressor_p.h" +#include "kdupdaterufcompresscommon_p.h" + +#include <QCryptographicHash> +#include <QDir> +#include <QFile> +#include <QFileInfo> +#include <QFSFileEngine> +#include <QDebug> + +using namespace KDUpdater; + +class UFUncompressor::Private +{ +public: + QString ufFileName; + QString destination; + QString errorMessage; + + void setError(const QString& msg); +}; + +void UFUncompressor::Private::setError(const QString& msg) +{ + errorMessage = msg; +} + +UFUncompressor::UFUncompressor() +{ +} + +UFUncompressor::~UFUncompressor() +{ +} + +QString UFUncompressor::errorString() const +{ + return d->errorMessage; +} + +void UFUncompressor::setFileName(const QString& fileName) +{ + d->ufFileName = fileName; +} + +QString UFUncompressor::fileName() const +{ + return d->ufFileName; +} + +void UFUncompressor::setDestination(const QString& dest) +{ + d->destination = dest; +} + +QString UFUncompressor::destination() const +{ + return d->destination; +} + +bool UFUncompressor::uncompress() +{ + d->errorMessage.clear(); + + // First open the uf file for reading + QFile ufFile( d->ufFileName ); + if( !ufFile.open(QFile::ReadOnly) ) { + d->setError(tr("Couldn't open file for reading: %1").arg( ufFile.errorString() )); + return false; + } + + QDataStream ufDS( &ufFile ); + ufDS.setVersion( QDataStream::Qt_4_2 ); + QCryptographicHash hash( QCryptographicHash::Md5 ); + + // Now read the header. + UFHeader header; + ufDS >> header; + if( ufDS.status() != QDataStream::Ok || !header.isValid() ) + { + d->setError( tr( "Couldn't read the file header." ) ); + return false; + } + header.addToHash(hash); + + // Some basic checks. + if( header.magic != QLatin1String( KD_UPDATER_UF_HEADER_MAGIC ) ) { + d->setError(tr("Wrong file format (magic number not found)")); + return false; + } + + // Lets get to the destination directory + const QDir dir(d->destination); + QFSFileEngine fileEngine; + + // Lets create the required directory structure + int numExpectedFiles = 0; + for(int i=0; i<header.fileList.count(); i++) + { + const QString fileName = header.fileList[i]; + // qDebug("ToUncompress %s", qPrintable(fileName)); + if( header.isDirList[i] ) + { + if ( !dir.mkpath( fileName ) ) + { + d->setError(tr("Could not create folder: %1/%2").arg( d->destination, fileName )); + return false; + } + fileEngine.setFileName( QString(QLatin1String( "%1/%2" )).arg(d->destination, fileName) ); + fileEngine.setPermissions( header.permList[i] | QAbstractFileEngine::ExeOwnerPerm ); + } else { + ++numExpectedFiles; + } + } + + // Lets now create files within these directories + int numActualFiles = 0; + while( !ufDS.atEnd() && numActualFiles < numExpectedFiles ) + { + UFEntry ufEntry; + ufDS >> ufEntry; + if( ufDS.status() != QDataStream::Ok || !ufEntry.isValid() ) + { + d->setError( tr( "Could not read information for entry %1." ).arg( numActualFiles ) ); + return false; + } + ufEntry.addToHash(hash); + + const QString completeFileName = QString(QLatin1String( "%1/%2" )).arg(d->destination, ufEntry.fileName); + + const QByteArray ba = qUncompress( ufEntry.fileData ); + // check the size + QDataStream stream( ufEntry.fileData ); + stream.setVersion( QDataStream::Qt_4_2 ); + qint32 length = 0; + stream >> length; + if( ba.length() != length ) // uncompress failed + { + d->setError(tr("Could not uncompress entry %1, corrupt data").arg( ufEntry.fileName ) ); + return false; + + } + + + QFile ufeFile( completeFileName ); + if ( !ufeFile.open( QFile::WriteOnly ) ) + { + d->setError(tr("Could not open file %1 for writing: %2").arg( completeFileName, ufeFile.errorString() )); + return false; + } + + + const char* const data = ba.constData(); + const qint64 total = ba.size(); + qint64 written = 0; + + while ( written < total ) + { + const qint64 num = ufeFile.write( data+written, total-written ); + if ( num == -1 ) + { + d->setError( tr("Failed writing uncompressed data to %1: %2").arg( completeFileName, ufeFile.errorString() ) ); + return false; + } + written += num; + } + + ufeFile.close(); + + const QFile::Permissions perm = static_cast< QFile::Permissions >( ufEntry.permissions ); + ufeFile.setPermissions( perm ); + + if ( ufeFile.error() != QFile::NoError ) + { + ufeFile.remove(); + d->setError( tr("Failed writing uncompressed data to %1: %2").arg( completeFileName, ufeFile.errorString() ) ); + return false; + } + + qDebug("Uncompressed %s", qPrintable(completeFileName)); + ++numActualFiles; + } + + if( numExpectedFiles != numActualFiles ) { + d->errorMessage = tr("Corrupt file (wrong number of files)"); + return false; + } + + QByteArray hashdata; + ufDS >> hashdata; + + if( hashdata != hash.result() ) { + d->errorMessage = tr("Corrupt file (wrong hash)"); + return false; + } + + return true; +} diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterufuncompressor_p.h b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterufuncompressor_p.h new file mode 100644 index 000000000..5800887fc --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterufuncompressor_p.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef __KDTOOLS_KDUPDATERUFUNCOMPRESSOR_P_H__ +#define __KDTOOLS_KDUPDATERUFUNCOMPRESSOR_P_H__ + +#include <KDToolsCore/pimpl_ptr.h> + +#include <QtCore/QCoreApplication> + +class QString; + +namespace KDUpdater +{ + class KDTOOLS_UPDATER_EXPORT UFUncompressor + { + Q_DECLARE_TR_FUNCTIONS(KDUpdater::UFUncompressor) + + public: + UFUncompressor(); + ~UFUncompressor(); + + QString errorString() const; + + void setFileName(const QString& fileName); + QString fileName() const; + + void setDestination(const QString& dest); + QString destination() const; + + bool uncompress(); + + private: + class Private; + kdtools::pimpl_ptr< Private > d; + }; +} + +#endif diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdate.cpp b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdate.cpp new file mode 100644 index 000000000..a84f3799c --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdate.cpp @@ -0,0 +1,313 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "kdupdaterupdate.h" +#include "kdupdaterapplication.h" +#include "kdupdaterupdatesourcesinfo.h" +#include "kdupdaterfiledownloader_p.h" +#include "kdupdaterfiledownloaderfactory.h" +#include "kdupdaterupdateoperations.h" +#include "kdupdaterupdateoperationfactory.h" + +#include <QFile> + +/*! + \ingroup kdupdater + \class KDUpdater::Update kdupdaterupdate.h KDUpdaterUpdate + \brief Represents a single update + + The KDUpdater::Update class contains information and mechanisms to download one update. It is + created by KDUpdater::UpdateFinder and is used by KDUpdater::UpdateInstaller to download the UpdateFile + corresponding to the update. + + The class makes use of appropriate network protocols (HTTP, HTTPS, FTP, or Local File Copy) to + download the UpdateFile. + + The constructor of the KDUpdater::Update class is made protected, because it can be instantiated only by + KDUpdater::UpdateFinder (which is a friend class). The destructor however is public. +*/ + +struct KDUpdater::Update::UpdateData +{ + UpdateData( Update* qq ) : + q( qq ), + application( 0 ), + compressedSize( 0 ), + uncompressedSize( 0 ) + {} + + Update* q; + Application* application; + KDUpdater::UpdateSourceInfo sourceInfo; + QMap<QString, QVariant> data; + QUrl updateUrl; + UpdateType type; + QList<UpdateOperation*> operations; + QByteArray sha1sum; + + quint64 compressedSize; + quint64 uncompressedSize; + + KDUpdater::FileDownloader* fileDownloader; +}; + + +/*! + \internal +*/ +KDUpdater::Update::Update(KDUpdater::Application* application, const KDUpdater::UpdateSourceInfo& sourceInfo, + UpdateType type, const QUrl& updateUrl, const QMap<QString, QVariant>& data, quint64 compressedSize, quint64 uncompressedSize, const QByteArray& sha1sum ) + : KDUpdater::Task(QLatin1String( "Update" ), Stoppable, application), + d( new UpdateData( this ) ) +{ + d->application = application; + d->sourceInfo = sourceInfo; + d->data = data; + d->updateUrl = updateUrl; + d->type = type; + + d->compressedSize = compressedSize; + d->uncompressedSize = uncompressedSize; + d->sha1sum = sha1sum; + + const SignatureVerifier* verifier = d->application->signatureVerifier( Application::Packages ); + + d->fileDownloader = FileDownloaderFactory::instance().create( updateUrl.scheme(), verifier, QUrl(), this); + if(d->fileDownloader) + { + d->fileDownloader->setUrl(d->updateUrl); + d->fileDownloader->setSha1Sum( d->sha1sum ); + connect(d->fileDownloader, SIGNAL(downloadProgress(double)), this, SLOT(downloadProgress(double))); + connect(d->fileDownloader, SIGNAL(downloadCanceled()), this, SIGNAL(stopped())); + connect(d->fileDownloader, SIGNAL(downloadCompleted()), this, SIGNAL(finished())); + } + + switch( type ) { + case NewPackage: + case PackageUpdate: + { + KDUpdater::UpdateOperation* packageOperation = UpdateOperationFactory::instance().create( QLatin1String( "UpdatePackage" ) ); + QStringList args; + args << data.value( QLatin1String( "Name" ) ).toString() + << data.value( QLatin1String( "Version" ) ).toString() + << data.value( QLatin1String( "ReleaseDate" ) ).toString(); + packageOperation->setArguments(args); + packageOperation->setApplication( application ); + d->operations.append( packageOperation ); + break; + } + case CompatUpdate: + { + KDUpdater::UpdateOperation* compatOperation = UpdateOperationFactory::instance().create( QLatin1String( "UpdateCompatLevel" ) ); + QStringList args; + args << data.value( QLatin1String( "CompatLevel" ) ).toString(); + compatOperation->setArguments(args); + compatOperation->setApplication( application ); + d->operations.append( compatOperation ); + break; + } + default: + break; + } +} + +/*! + Destructor +*/ +KDUpdater::Update::~Update() +{ + const QString fileName = this->downloadedFileName(); + if( !fileName.isEmpty() ) + QFile::remove( fileName ); + qDeleteAll( d->operations ); + d->operations.clear(); + delete d; +} + +/*! + Returns the application for which this class is downloading the UpdateFile +*/ +KDUpdater::Application* KDUpdater::Update::application() const +{ + return d->application; +} + +/*! + Returns the release date of the update downloaded by this class +*/ +QDate KDUpdater::Update::releaseDate() const +{ + return d->data.value( QLatin1String( "ReleaseDate" ) ).toDate(); +} + +/*! + Returns data whose name is given in parameter, or an invalid QVariant if the data doesn't exist. +*/ +QVariant KDUpdater::Update::data( const QString& name ) const +{ + if ( d->data.contains( name ) ) + return d->data.value( name ); + return QVariant(); +} + +/*! + Returns the complete URL of the UpdateFile downloaded by this class. +*/ +QUrl KDUpdater::Update::updateUrl() const +{ + return d->updateUrl; +} + +/*! + Returns the update source info on which this update was created. +*/ +KDUpdater::UpdateSourceInfo KDUpdater::Update::sourceInfo() const +{ + return d->sourceInfo; +} + +/*! + * Returns the type of update + */ +KDUpdater::UpdateType KDUpdater::Update::type() const +{ + return d->type; +} + +/*! + Returns true of the update can be downloaded, false otherwise. The function + returns false if the URL scheme is not supported by this class. +*/ +bool KDUpdater::Update::canDownload() const +{ + return d->fileDownloader && d->fileDownloader->canDownload(); +} + +/*! + Returns true of the update has been downloaded. If this function returns true + the you can use the \ref downloadedFileName() method to get the complete name + of the downloaded UpdateFile. + + \note: The downloaded UpdateFile will be deleted when this class is destroyed +*/ +bool KDUpdater::Update::isDownloaded() const +{ + return d->fileDownloader && d->fileDownloader->isDownloaded(); +} + +/*! + Returns the name of the downloaded UpdateFile after the download is complete, ie + when \ref isDownloaded() returns true. +*/ +QString KDUpdater::Update::downloadedFileName() const +{ + if(d->fileDownloader) + return d->fileDownloader->downloadedFileName(); + + return QString(); +} + +/*! + \internal +*/ +void KDUpdater::Update::downloadProgress(double value) +{ + Q_ASSERT(value <= 1); + reportProgress(value * 100, tr("Downloading update...")); +} + +/*! + \internal +*/ +void KDUpdater::Update::downloadCompleted() +{ + reportProgress(100, tr("Update downloaded")); + reportDone(); +} + +/*! + \internal +*/ +void KDUpdater::Update::downloadAborted(const QString& msg) +{ + reportError(msg); +} + +/*! + \internal +*/ +void KDUpdater::Update::doRun() +{ + if(d->fileDownloader) + d->fileDownloader->download(); +} + +/*! + \internal +*/ +bool KDUpdater::Update::doStop() +{ + if(d->fileDownloader) + d->fileDownloader->cancelDownload(); + return true; +} + +/*! + \internal +*/ +bool KDUpdater::Update::doPause() +{ + return false; +} + +/*! + \internal +*/ +bool KDUpdater::Update::doResume() +{ + return false; +} + +/*! + Returns a list of operations needed by this update. For example, package update needs to change + the package version, compat update needs to change the compat level... + */ +QList<KDUpdater::UpdateOperation*> KDUpdater::Update::operations() const +{ + return d->operations; +} + +/*! + * Returns the compressed size of this update's data file. + */ +quint64 KDUpdater::Update::compressedSize() const +{ + return d->compressedSize; +} + +/*! + * Returns the uncompressed size of this update's data file. + */ +quint64 KDUpdater::Update::uncompressedSize() const +{ + return d->uncompressedSize; +} diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdate.h b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdate.h new file mode 100644 index 000000000..8ea4728b9 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdate.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KD_UPDATER_UPDATE_H +#define KD_UPDATER_UPDATE_H + +#include "kdupdater.h" +#include "kdupdatertask.h" +#include <QUrl> +#include <QDate> +#include <QMap> +#include <QVariant> +#include <QList> + +namespace KDUpdater +{ + class Application; + struct UpdateSourceInfo; + class UpdateFinder; + class UpdateOperation; + + class KDTOOLS_UPDATER_EXPORT Update : public Task + { + Q_OBJECT + + public: + ~Update(); + + Application* application() const; + + UpdateType type() const; + QUrl updateUrl() const; + QDate releaseDate() const; + QVariant data( const QString& name ) const; + UpdateSourceInfo sourceInfo() const; + + bool canDownload() const; + bool isDownloaded() const; + void download() { run(); } + QString downloadedFileName() const; + + QList<UpdateOperation*> operations() const; + + quint64 compressedSize() const; + quint64 uncompressedSize() const; + + private Q_SLOTS: + void downloadProgress(double); + void downloadAborted(const QString& msg); + void downloadCompleted(); + + private: + friend class UpdateFinder; + struct UpdateData; + UpdateData* d; + + void doRun(); + bool doStop(); + bool doPause(); + bool doResume(); + + Update(Application* application, const UpdateSourceInfo& sourceInfo, + UpdateType type, const QUrl& updateUrl, const QMap<QString, QVariant>& data, quint64 compressedSize, quint64 uncompressedSize, const QByteArray& sha1sum ); + }; +} + +#endif diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatefinder.cpp b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatefinder.cpp new file mode 100644 index 000000000..8d34586e8 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatefinder.cpp @@ -0,0 +1,892 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "kdupdaterupdatefinder.h" +#include "kdupdaterapplication.h" +#include "kdupdaterupdatesourcesinfo.h" +#include "kdupdaterpackagesinfo.h" +#include "kdupdaterupdate.h" +#include "kdupdaterfiledownloader_p.h" +#include "kdupdaterfiledownloaderfactory.h" +#include "kdupdaterupdatesinfo_p.h" +#include "kdupdatersignatureverifier.h" + +#include <QCoreApplication> +#include <QDebug> + +using namespace KDUpdater; + +/*! + \ingroup kdupdater + \class KDUpdater::UpdateFinder kdupdaterupdatefinder KDUpdaterUpdateFinder + \brief Finds updates applicable for a \ref KDUpdater::Application + + The KDUpdater::UpdateFinder class helps in searching for updates and installing it on the application. The + class basically processes the application's \ref KDUpdater::PackagesInfo and the UpdateXMLs it aggregates + from all the update sources described in KDUpdater::UpdateSourcesInfo and populates a list of + \ref KDUpdater::Update objects. This list can then be passed to \ref KDUpdater::UpdateInstaller for + actually downloading and installing the updates. + + + Usage: + \code + KDUpdater::UpdateFinder updateFinder( application ); + QProgressDialog finderProgressDlg; + + QObject::connect( &updateFinder, SIGNAL(progressValue(int)), + &finderProgressDlg, SLOT(setValue(int))); + QObject::connect( &updateFinder, SIGNAL(computeUpdatesCompleted()), + &finderProgressDlg, SLOT(accept())); + QObject::connect( &updateFinder, SIGNAL(computeUpdatesCanceled()), + &finderProgressDlg, SLOT(reject())); + + QObject::connect( &finderProgressDlg, SIGNAL(canceled()), + &updateFinder, SLOT(cancelComputeUpdates())); + + updateFinder.run(); + finderProgressDlg.exec(); + +// Control comes here after update finding is done or canceled. + +QList<KDUpdater::Update*> updates = updateFinder.updates(); +KDUpdater::UpdateInstaller updateInstaller; +updateInstaller.installUpdates( updates ); + +\endcode +*/ + + +// +// Private +// +class KDUpdater::UpdateFinder::Private +{ +public: + Private( UpdateFinder* qq ) : + q( qq ), + application(0), + updateType(KDUpdater::PackageUpdate) + {} + + ~Private() + { + qDeleteAll( updates ); + qDeleteAll( updatesInfoList ); + qDeleteAll( updateXmlFDList ); + } + + UpdateFinder* q; + KDUpdater::Application* application; + QList<KDUpdater::Update*> updates; + UpdateTypes updateType; + + // Temporary structure that notes down information about updates. + bool cancel; + int downloadCompleteCount; + QList<KDUpdater::UpdateSourceInfo> updateSourceInfoList; + QList<KDUpdater::UpdatesInfo*> updatesInfoList; + QList<KDUpdater::FileDownloader*> updateXmlFDList; + + void clear(); + void computeUpdates(); + void cancelComputeUpdates(); + bool downloadUpdateXMLFiles(); + bool computeApplicableUpdates(); + + QList<KDUpdater::UpdateInfo> applicableUpdates(KDUpdater::UpdatesInfo* updatesInfo, bool addNewPackages = false ); + void createUpdateObjects(const KDUpdater::UpdateSourceInfo& sourceInfo, + const QList<KDUpdater::UpdateInfo>& updateInfoList); + bool checkForUpdatePriority(const KDUpdater::UpdateSourceInfo& sourceInfo, + const KDUpdater::UpdateInfo& updateInfo); + int pickUpdateFileInfo(const QList<KDUpdater::UpdateFileInfo>& updateFiles); + void slotDownloadDone(); +}; + + +static int computeProgressPercentage(int min, int max, int percent) +{ + return min + qint64(max-min) * percent / 100; +} + +static int computePercent(int done, int total) +{ + return total ? done * Q_INT64_C(100) / total : 0 ; +} + +/*! + \internal + + Releases all internal resources consumed while downloading and computing updates. +*/ +void KDUpdater::UpdateFinder::Private::clear() +{ + qDeleteAll( updates ); + updates.clear(); + qDeleteAll( updatesInfoList ); + updatesInfoList.clear(); + qDeleteAll( updateXmlFDList ); + updateXmlFDList.clear(); + updateSourceInfoList.clear(); + + downloadCompleteCount = 0; +} + +/*! + \internal + + This method computes the updates that can be applied on the application by + studying the application's \ref KDUpdater::PackagesInfo object and the UpdateXML files + from each of the update sources described in \ref KDUpdater::UpdateSourcesInfo. + + This function can take a long time to complete. The following signals are emitted + during the execution of this function + + The function creates \ref KDUpdater::Update objects on the stack. All KDUpdater::Update objects + are made children of the application associated with this finder. + + The update sources are fetched from the \ref KDUpdater::UpdateSourcesInfo object associated with + the application. Package information is extracted from the \ref KDUpdater::PackagesInfo object + associated with the application. + + \note Each time this function is called, all the previously computed updates are discarded +and its resources are freed. +*/ +void KDUpdater::UpdateFinder::Private::computeUpdates() +{ + // Computing updates is done in two stages + // 1. Downloading Update XML files from all the update sources + // 2. Matching updates with Package XML and figuring out available updates + + cancel = false; + clear(); + + // First do some quick sanity checks on the packages info + KDUpdater::PackagesInfo* packages = application->packagesInfo(); + if( !packages ) { + q->reportError(tr("Could not access the package information of this application")); + return; + } + if( !packages->isValid() ) { + q->reportError(packages->errorString()); + return; + } + + // Now do some quick sanity checks on the update sources info + KDUpdater::UpdateSourcesInfo* sources = application->updateSourcesInfo(); + if( !sources ) { + q->reportError(tr("Could not access the update sources information of this application")); + return; + } + if( !sources->isValid() ) { + q->reportError(sources->errorString()); + return; + } + + // Now we can start... + + // Step 1: 0 - 49 percent + if(!downloadUpdateXMLFiles() || cancel) + { + clear(); + return; + } + + // Step 2: 50 - 100 percent + if(!computeApplicableUpdates() || cancel) + { + clear(); + return; + } + + // All done + q->reportProgress( 100, tr("%1 updates found").arg(updates.count()) ); + q->reportDone(); +} + +/*! + \internal + + Cancels the computation of updates. + + \sa \ref computeUpdates() +*/ +void KDUpdater::UpdateFinder::Private::cancelComputeUpdates() +{ + cancel = true; +} + +/*! + \internal + + This function downloads Updates.xml from all the update sources. A single application can potentially + have several update sources, hence we need to be asynchronous in downloading updates from different + sources. + + The function basically does this for each update source + a) Create a KDUpdater::FileDownloader and KDUpdater::UpdatesInfo for each update + b) Triggers the download of Updates.xml from each file downloader. + c) The downloadCompleted(), downloadCanceled() and downloadAborted() signals are connected + in each of the downloaders. Once all the downloads are complete and/or aborted, the next stage + would be done. + + The function gets into an event loop until all the downloads are complete. +*/ +bool KDUpdater::UpdateFinder::Private::downloadUpdateXMLFiles() +{ + if( !application ) + return false; + + KDUpdater::UpdateSourcesInfo* updateSources = application->updateSourcesInfo(); + if( !updateSources ) + return false; + + // Create KDUpdater::FileDownloader and KDUpdater::UpdatesInfo for each update + for(int i=0; i<updateSources->updateSourceInfoCount(); i++) + { + KDUpdater::UpdateSourceInfo info = updateSources->updateSourceInfo(i); + QUrl updateXmlUrl = QString::fromLatin1("%1/Updates.xml").arg(info.url.toString()); + + const SignatureVerifier* verifier = application->signatureVerifier( Application::Metadata ); + KDUpdater::FileDownloader* downloader = FileDownloaderFactory::instance().create(updateXmlUrl.scheme(), verifier, QUrl(), q); + if( !downloader ) + continue; + + downloader->setUrl(updateXmlUrl); + downloader->setAutoRemoveDownloadedFile(true); + + KDUpdater::UpdatesInfo* updatesInfo = new KDUpdater::UpdatesInfo; + updateSourceInfoList.append(info); + updateXmlFDList.append(downloader); + updatesInfoList.append(updatesInfo); + + connect(downloader, SIGNAL(downloadCompleted()), + q, SLOT(slotDownloadDone())); + connect(downloader, SIGNAL(downloadCanceled()), + q, SLOT(slotDownloadDone())); + connect(downloader, SIGNAL(downloadAborted(QString)), + q, SLOT(slotDownloadDone())); + } + + // Trigger download of Updates.xml file + downloadCompleteCount = 0; + for(int i=0; i<updateXmlFDList.count(); i++) + { + KDUpdater::FileDownloader* downloader = updateXmlFDList[i]; + downloader->download(); + } + + // Wait until all downloaders have completed their downloads. + while(1) + { + QCoreApplication::processEvents(); + if( cancel ) + return false; + if( downloadCompleteCount == updateXmlFDList.count()) + break; + + int pc = computePercent(downloadCompleteCount, updateXmlFDList.count()); + q->reportProgress(pc, tr("Downloading Updates.xml from update-sources")); + } + + // All the downloaders have now either downloaded or aborted the + // donwload of update XML files. + + // Lets now get rid of update sources whose Updates.xml could not be downloaded + for(int i=0; i<updateXmlFDList.count(); i++) + { + KDUpdater::FileDownloader* downloader = updateXmlFDList[i]; + if( downloader->isDownloaded() ) + continue; + + KDUpdater::UpdateSourceInfo info = updateSourceInfoList[i]; + QString msg = tr("Could not download updates from %1 ('%2')").arg(info.name, info.url.toString()); + q->reportError(msg); + + delete updatesInfoList[i]; + delete downloader; + updateXmlFDList.removeAt(i); + updatesInfoList.removeAt(i); + updateSourceInfoList.removeAt(i); + --i; + } + + if (updatesInfoList.isEmpty()) { + return false; + } + + // Lets parse the downloaded update XML files and get rid of the downloaders. + for(int i=0; i<updateXmlFDList.count(); i++) + { + KDUpdater::FileDownloader* downloader = updateXmlFDList[i]; + KDUpdater::UpdatesInfo* updatesInfo = updatesInfoList[i]; + + updatesInfo->setFileName( downloader->downloadedFileName() ); + + if (!updatesInfo->isValid()) { + QString msg = updatesInfo->errorString(); + q->reportError(msg); + + delete updatesInfoList[i]; + delete downloader; + updateXmlFDList.removeAt(i); + updatesInfoList.removeAt(i); + --i; + } + } + qDeleteAll( updateXmlFDList ); + updateXmlFDList.clear(); + + if (updatesInfoList.isEmpty()) { + return false; + } + + q->reportProgress( 49, tr("Updates.xml file(s) downloaded from update sources") ); + return true; +} + +/*! + \internal + + This function runs through all the KDUpdater::UpdatesInfo objects created during + the downloadUpdateXMLFiles() method and compares it with the data contained in + KDUpdater::PackagesInfo. There by figures out whether an update is applicable for + this application or not. +*/ +bool KDUpdater::UpdateFinder::Private::computeApplicableUpdates() +{ + if( updateType & KDUpdater::CompatUpdate ) + { + KDUpdater::UpdateInfo compatUpdateInfo; + KDUpdater::UpdateSourceInfo compatUpdateSourceInfo; + + // Required compat level + int reqCompatLevel = application->compatLevel()+1; + + q->reportProgress(60, tr("Looking for compatibility update...")); + + // We are only interested in compat updates. + for(int i=0; i<updatesInfoList.count(); i++) + { + KDUpdater::UpdatesInfo* info = updatesInfoList[i]; + KDUpdater::UpdateSourceInfo updateSource = updateSourceInfoList[i]; + + // If we already have a compat update, just check if the source currently being + // considered has a higher priority or not. + if(compatUpdateInfo.data.contains( QLatin1String( "CompatLevel" ) ) && updateSource.priority < compatUpdateSourceInfo.priority) + continue; + + // Lets look for comapt updates that provide compat level one-higher than + // the application's current compat level. + QList<KDUpdater::UpdateInfo> updatesInfo = info->updatesInfo( KDUpdater::CompatUpdate, reqCompatLevel ); + + if( updatesInfo.count() == 0 ) + continue; + + compatUpdateInfo = updatesInfo.at( 0 ); + compatUpdateSourceInfo = updateSource; + } + + bool found = (compatUpdateInfo.data.contains( QLatin1String( "CompatLevel" ) )); + if(found) + { + q->reportProgress(80, tr("Found compatibility update..")); + + // Lets create an update for this compat update. + QString updateName = tr("Compatibility level %1 update").arg(reqCompatLevel); + QUrl url; + + // Pick a update file based on arch and OS. + int pickUpdateFileIndex = pickUpdateFileInfo(compatUpdateInfo.updateFiles); + if(pickUpdateFileIndex < 0) + { + q->reportError(tr("Compatibility update for the required architecture and hardware configuration was not found")); + q->reportProgress(100, tr("Compatibility update not found")); + return false; + } + + KDUpdater::UpdateFileInfo fileInfo = compatUpdateInfo.updateFiles.at( pickUpdateFileIndex ); + + // Create an update for this entry + url = QString::fromLatin1( "%1/%2" ).arg( compatUpdateSourceInfo.url.toString(), fileInfo.fileName ); + KDUpdater::Update* update = q->constructUpdate(application, + compatUpdateSourceInfo, + KDUpdater::CompatUpdate, url, + compatUpdateInfo.data, fileInfo.compressedSize, fileInfo.uncompressedSize, fileInfo.sha1sum ); + + // Register the update + updates.append(update); + + // Done + q->reportProgress(100, tr("Compatibility update found")); + } + else + q->reportProgress(100, tr("No compatibility updates found")); + } + if ( updateType & PackageUpdate ) + { + // We are not looking for normal updates, not compat ones. + for(int i=0; i<updatesInfoList.count(); i++) + { + // Fetch updates applicable to this application. + KDUpdater::UpdatesInfo* info = updatesInfoList[i]; + QList<KDUpdater::UpdateInfo> updates = applicableUpdates(info , updateType & NewPackage ); + if( !updates.count() ) + continue; + + if( cancel ) + return false; + KDUpdater::UpdateSourceInfo updateSource = updateSourceInfoList[i]; + + // Create KDUpdater::Update objects for updates that have a valid + // UpdateFile + createUpdateObjects(updateSource, updates); + if( cancel ) + return false; + + // Report progress + int pc = computePercent(i, updatesInfoList.count()); + pc = computeProgressPercentage(51, 100, pc); + q->reportProgress( pc, tr("Computing applicable updates") ); + } + } + + q->reportProgress( 99, tr("Application updates computed") ); + return true; +} + +QList<KDUpdater::UpdateInfo> KDUpdater::UpdateFinder::Private::applicableUpdates( KDUpdater::UpdatesInfo* updatesInfo, bool addNewPackages ) +{ + QList<KDUpdater::UpdateInfo> retList; + + if( !updatesInfo || updatesInfo->updateInfoCount( PackageUpdate ) == 0 ) + return retList; + + KDUpdater::PackagesInfo* packages = this->application->packagesInfo(); + if( !packages ) + return retList; + + // Check to see if the updates info contains updates for any application + bool anyApp = updatesInfo->applicationName() == QLatin1String( "{AnyApplication}" ); + int appNameIndex = -1; + + if( !anyApp ) + { + // updatesInfo->applicationName() describes one application or a series of + // application names separated by commas. + QString appName = updatesInfo->applicationName(); + appName = appName.replace(QLatin1String( ", " ), + QLatin1String( "," )); + appName = appName.replace(QLatin1String( " ," ), + QLatin1String( "," )); + + // Catch hold of app names contained updatesInfo->applicationName() + QStringList apps = appName.split(QLatin1String( "," ), QString::SkipEmptyParts); + appNameIndex = apps.indexOf(this->application->applicationName()); + + // If the application appName isnt one of the app names, then + // the updates are not applicable. + if( appNameIndex < 0 ) + return retList; + } + +#if 0 //Nokia-SDK: ignore ApplicationVersion, it has no purpose and just causes problems if someone bumps the config.xml application version accidentally + // Check to see if the update repository versions match with app version + if( !anyApp ) + { + QString appVersion = updatesInfo->applicationVersion(); + appVersion = appVersion.replace(QLatin1String( ", " ), QLatin1String( "," )); + appVersion = appVersion.replace(QLatin1String( " ," ), QLatin1String( "," )); + QStringList versions = appVersion.split(QLatin1String( "," ), QString::SkipEmptyParts); + + if( appNameIndex >= versions.count() ) + return retList; // please give us well formatted Updates.xml files. + + QString version = versions[appNameIndex]; + if( KDUpdater::compareVersion(this->application->applicationVersion(), version) != 0 ) + return retList; + } +#endif + + // Check to see if version numbers match. This means that the version + // number of the update should be greater than the version number of + // the package that is currently installed. + QList<KDUpdater::UpdateInfo> updateList = updatesInfo->updatesInfo( KDUpdater::PackageUpdate ); + for(int i=0; i<updatesInfo->updateInfoCount( PackageUpdate ); i++) + { + KDUpdater::UpdateInfo updateInfo = updateList.at( i ); + if( !addNewPackages ) + { + int pkgInfoIdx = packages->findPackageInfo( updateInfo.data.value( QLatin1String( "Name" ) ).toString() ); + if( pkgInfoIdx < 0 ) + continue; + + KDUpdater::PackageInfo pkgInfo = packages->packageInfo( pkgInfoIdx ); + + // First check to see if the update version is more than package version + QString updateVersion = updateInfo.data.value( QLatin1String( "Version" ) ).toString(); + QString pkgVersion = pkgInfo.version; + if( KDUpdater::compareVersion(updateVersion, pkgVersion) <= 0 ) + continue; + + // It is quite possible that we may have already installed the update. + // Lets check the last update date of the package and the release date + // of the update. This way we can compare and figure out if the update + // has been installed or not. + QDate pkgDate = pkgInfo.lastUpdateDate; + QDate updateDate = updateInfo.data.value( QLatin1String( "ReleaseDate" ) ).toDate(); + if( pkgDate > updateDate ) + continue; + } + + // Bingo!, we found an update :-) + retList.append(updateInfo); + } + + return retList; +} + +void KDUpdater::UpdateFinder::Private::createUpdateObjects(const KDUpdater::UpdateSourceInfo& sourceInfo, const QList<KDUpdater::UpdateInfo>& updateInfoList) +{ + for(int i=0; i<updateInfoList.count(); i++) + { + KDUpdater::UpdateInfo info = updateInfoList[i]; + // Compat level checks + if( info.data.contains( QLatin1String( "RequiredCompatLevel" ) ) && + info.data.value( QLatin1String( "RequiredCompatLevel" ) ).toInt() != application->compatLevel() ) + { + qDebug() << "Update \"" << info.data.value( QLatin1String( "Name" ) ).toString() << "\" at \"" + << sourceInfo.name << "\"(\"" << sourceInfo.url.toString() << "\") requires a different compat level"; + continue; // Compatibility level mismatch + } + + // If another update of the same name exists, then use the update coming from + // a higher priority. + if( !checkForUpdatePriority(sourceInfo, info) ) + { + qDebug() << "Skipping Update \"" + << info.data.value( QLatin1String( "Name" ) ).toString() + << "\" from \"" + << sourceInfo.name + << "\"(\"" + << sourceInfo.url.toString() + << "\") because an update with the same name was found from a higher priority location"; + + continue; + } + + // Pick a update file based on arch and OS. + int pickUpdateFileIndex = this->pickUpdateFileInfo(info.updateFiles); + if(pickUpdateFileIndex < 0) + continue; + + KDUpdater::UpdateFileInfo fileInfo = info.updateFiles.at( pickUpdateFileIndex ); + + // Create an update for this entry + QUrl url( QString::fromLatin1("%1/%2").arg( sourceInfo.url.toString(), fileInfo.fileName ) ); + KDUpdater::Update* update = q->constructUpdate(application, sourceInfo, KDUpdater::PackageUpdate, url, info.data, fileInfo.compressedSize, fileInfo.uncompressedSize, fileInfo.sha1sum ); + + // Register the update + this->updates.append(update); + } +} + +bool KDUpdater::UpdateFinder::Private::checkForUpdatePriority(const KDUpdater::UpdateSourceInfo& sourceInfo, const KDUpdater::UpdateInfo& updateInfo) +{ + for(int i=0; i<this->updates.count(); i++) + { + KDUpdater::Update* update = this->updates[i]; + if( update->data( QLatin1String( "Name" ) ).toString() != updateInfo.data.value( QLatin1String( "Name" ) ).toString() ) + continue; + + // Bingo, update was previously found elsewhere. + + // If the existing update comes from a higher priority server, then cool :) + if( update->sourceInfo().priority > sourceInfo.priority ) + return false; + + // If the existing update has a higher version number, keep it + if ( KDUpdater::compareVersion(update->data( QLatin1String( "Version" ) ).toString(), + updateInfo.data.value( QLatin1String( "Version" ) ).toString()) > 0) + return false; + + // Otherwise the old update must be deleted. + this->updates.removeAll(update); + delete update; + + return true; + } + + // No update by that name was found, so what we have is a priority update. + return true; +} + +int KDUpdater::UpdateFinder::Private::pickUpdateFileInfo(const QList<KDUpdater::UpdateFileInfo>& updateFiles) +{ +#ifdef Q_WS_MAC + QString os = QLatin1String( "MacOSX" ); +#endif +#ifdef Q_WS_WIN + QString os = QLatin1String( "Windows" ); +#endif +#ifdef Q_WS_X11 + QString os = QLatin1String( "Linux" ); +#endif + + QString arch = QLatin1String( "i386" ); // only one architecture considered for now. + + for(int i=0; i<updateFiles.count(); i++) + { + KDUpdater::UpdateFileInfo fileInfo = updateFiles[i]; + + if( fileInfo.arch != arch ) + continue; + + if( fileInfo.os != QLatin1String( "Any" ) && fileInfo.os != os ) + continue; + + return i; + } + + return -1; +} + + + +// +// UpdateFinder +// + +/*! + Constructs a update finder for a given \ref KDUpdater::Application. +*/ +KDUpdater::UpdateFinder::UpdateFinder(KDUpdater::Application* application) + : KDUpdater::Task(QLatin1String( "UpdateFinder" ), Stoppable, application), + d( new Private( this ) ) +{ + d->application = application; +} + +/*! + Destructor +*/ +KDUpdater::UpdateFinder::~UpdateFinder() +{ + delete d; +} + +/*! + Returns a pointer to the update application for which this function computes all + the updates. +*/ +KDUpdater::Application* KDUpdater::UpdateFinder::application() const +{ + return d->application; +} + +/*! + Returns a list of KDUpdater::Update objects. The update objects returned in this list + are made children of the \ref KDUpdater::Application object associated with this class. +*/ +QList<KDUpdater::Update*> KDUpdater::UpdateFinder::updates() const +{ + return d->updates; +} + +/*! + Looks only for a certain type of update. By default, only package update +*/ +void KDUpdater::UpdateFinder::setUpdateType(UpdateTypes type) +{ + d->updateType = type; +} + +/*! + Returns the type of updates searched +*/ +KDUpdater::UpdateTypes KDUpdater::UpdateFinder::updateType() const +{ + return d->updateType; +} + +/*! + \internal + + Implemented from \ref KDUpdater::Task::doStart(). +*/ +void KDUpdater::UpdateFinder::doRun() +{ + d->computeUpdates(); +} + +/*! + \internal + + Implemented form \ref KDUpdater::Task::doStop() +*/ +bool KDUpdater::UpdateFinder::doStop() +{ + d->cancelComputeUpdates(); + + // Wait until the cancel has actually happened, and then return. + // Thinking of using QMutex for this. Frank/Till any suggestions? + + return true; +} + +/*! + \internal + + Implemented form \ref KDUpdater::Task::doStop() +*/ +bool KDUpdater::UpdateFinder::doPause() +{ + // Not a pausable task + return false; +} + +/*! + \internal + + Implemented form \ref KDUpdater::Task::doStop() +*/ +bool KDUpdater::UpdateFinder::doResume() +{ + // Not a pausable task, hence it is not resumable as well + return false; +} + +/*! + \internal +*/ +void KDUpdater::UpdateFinder::Private::slotDownloadDone() +{ + ++downloadCompleteCount; + + int pc = computePercent(downloadCompleteCount, updateXmlFDList.count()); + pc = computeProgressPercentage(0, 45, pc); + q->reportProgress( pc, tr("Downloading Updates.xml from update sources") ); +} + +/*! + \internal + */ +KDUpdater::Update* KDUpdater::UpdateFinder::constructUpdate( Application* application, const UpdateSourceInfo& sourceInfo, + UpdateType type, const QUrl& updateUrl, const QMap< QString, QVariant >& data, quint64 compressedSize, quint64 uncompressedSize, const QByteArray& sha1sum ) +{ + return new Update( application, sourceInfo, type, updateUrl, data, compressedSize, uncompressedSize, sha1sum ); +} + + +/*! + \ingroup kdupdater + + This function compares two version strings \c v1 and \c v2 and returns + -1, 0 or +1 based on the following rule + + \li Returns 0 if v1 == v2 + \li Returns -1 if v1 < v2 + \li Returns +1 if v1 > v2 + + The function is very similar to \c strcmp(), except that it works on version strings. + + Example: + \code + + KDUpdater::compareVersion("2.0", "2.1"); // Returns -1 + KDUpdater::compareVersion("2.1", "2.0"); // Returns +1 + KDUpdater::compareVersion("2.0", "2.0"); // Returns 0 + KDUpdater::compareVersion("2.1", "2.1"); // Returns 0 + + KDUpdater::compareVersion("2.0", "2.x"); // Returns 0 + KDUpdater::compareVersion("2.x", "2.0"); // Returns 0 + + KDUpdater::compareVersion("2.0.12.4", "2.1.10.4"); // Returns -1 + KDUpdater::compareVersion("2.0.12.x", "2.0.x"); // Returns 0 + KDUpdater::compareVersion("2.1.12.x", "2.0.x"); // Returns +1 + KDUpdater::compareVersion("2.1.12.x", "2.x"); // Returns 0 + KDUpdater::compareVersion("2.x", "2.1.12.x"); // Returns 0 + + \endcode +*/ +int KDUpdater::compareVersion(const QString& v1, const QString& v2) +{ + // For tests refer VersionCompareFnTest testcase. + + // Check for equality + if( v1 == v2 ) + return 0; + + // Split version numbers across . + const QStringList v1_comps = v1.split( QRegExp( QLatin1String( "\\.|-" ) ) ); + const QStringList v2_comps = v2.split( QRegExp( QLatin1String( "\\.|-" ) ) ); + + // Check each component of the version + int index = 0; + while(1) + { + if( index == v1_comps.count() && index < v2_comps.count() ) + return -1; + else if( index < v1_comps.count() && index == v2_comps.count() ) + return +1; + else if( index >= v1_comps.count() || index >= v2_comps.count() ) + break; + + bool v1_ok, v2_ok; + int v1_comp = v1_comps[index].toInt(&v1_ok); + int v2_comp = v2_comps[index].toInt(&v2_ok); + + if(!v1_ok) + { + if(v1_comps[index] == QLatin1String( "x" ) ) + return 0; + } + if(!v2_ok) + { + if(v2_comps[index] == QLatin1String( "x") ) + return 0; + } + if( !v1_ok && !v2_ok ) + { + return v1_comps[ index ].compare( v2_comps[ index ] ); + } + + if( v1_comp < v2_comp ) + return -1; + + if( v1_comp > v2_comp ) + return +1; + + // v1_comp == v2_comp + ++index; + } + + if( index < v2_comps.count() ) + return +1; + + if( index < v1_comps.count() ) + return -1; + + // Controversial return. I hope this never happens. + return 0; +} + +#include "moc_kdupdaterupdatefinder.cpp" diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatefinder.h b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatefinder.h new file mode 100644 index 000000000..14ebb9c97 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatefinder.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KD_UPDATER_UPDATE_FINDER_H +#define KD_UPDATER_UPDATE_FINDER_H + +#include "kdupdater.h" +#include "kdupdatertask.h" +#include <QList> +#include <QMap> + +class QUrl; + +namespace KDUpdater +{ + class Application; + class Update; + struct UpdateSourceInfo; + + class KDTOOLS_UPDATER_EXPORT UpdateFinder : public Task + { + Q_OBJECT + + public: + explicit UpdateFinder(Application* application); + ~UpdateFinder(); + + Application* application() const; + QList<Update*> updates() const; + + void setUpdateType( UpdateTypes type ); + UpdateTypes updateType() const; + + private: + void doRun(); + bool doStop(); + bool doPause(); + bool doResume(); + + Update* constructUpdate( Application* application, const UpdateSourceInfo& sourceInfo, + UpdateType type, const QUrl& updateUrl, const QMap< QString, QVariant >& data, quint64 compressedSize, quint64 uncompressedSize, const QByteArray& sha1sum ); + + + private: + class Private; + Private* const d; + Q_PRIVATE_SLOT( d, void slotDownloadDone() ) + }; +}; + +#endif diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdateinstaller.cpp b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdateinstaller.cpp new file mode 100644 index 000000000..3b8d2dbd4 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdateinstaller.cpp @@ -0,0 +1,481 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "kdupdaterupdateinstaller.h" +#include "kdupdaterpackagesinfo.h" +#include "kdupdaterapplication.h" +#include "kdupdaterupdate.h" +#include "kdupdaterupdateoperationfactory.h" +#include "kdupdaterupdateoperation.h" +#include "kdupdaterufuncompressor_p.h" + +#include <QCoreApplication> +#include <QFileInfo> +#include <QDir> +#include <QDomDocument> +#include <QDomElement> +#include <QDate> + +/*! + \ingroup kdupdater + \class KDUpdater::UpdateInstaller kdupdaterupdateinstaller.h KDUpdaterUpdateInstaller + \brief Installs updates, given a list of \ref KDUpdater::Update objects + + This class installs updates, given a list of \ref KDUpdater::Update objects via the + \ref installUpdates() method. To install the updates this class performs the following + for each update + + \li Downloads the update files from its source + \li Unpacks update files into a temporary directory + \li Parses and executes UpdateInstructions.xml by making use of \ref KDUpdater::UpdateOperation + objects sourced via \ref KDUpdater::UpdateOperationFactory + + \note All temporary files created during the installation of the update will be destroyed + immediately after the installation is complete. +*/ +class KDUpdater::UpdateInstaller::Private +{ + public: + Private( UpdateInstaller* qq ) : + q( qq ) + {} + + UpdateInstaller* q; + + KDUpdater::Application* application; + int updateDownloadDoneCount; + int updateDownloadProgress; + int totalUpdates; + QStringList toRemoveDirs; + + int totalProgressPc; + int currentProgressPc; + QList<KDUpdater::Update*> updates; + + void resolveArguments(QStringList& args); + + void removeDirectory(const QDir & dir); + void slotUpdateDownloadProgress(int percent); + void slotUpdateDownloadDone(); +}; + +// next two are duplicated from kdupdaterupdatefinder.cpp: + +static int computeProgressPercentage(int min, int max, int percent) +{ + return min + qint64(max-min) * percent / 100 ; +} + +static int computePercent(int done, int total) +{ + return total ? done * Q_INT64_C(100) / total : 0 ; +} + +/*! + Constructs an instance of this class for the \ref KDUpdater::Application passed as + parameter. Only updates meant for the specified application will be installed by this class. +*/ +KDUpdater::UpdateInstaller::UpdateInstaller(KDUpdater::Application* application) + : KDUpdater::Task(QLatin1String( "UpdateInstaller" ), NoCapability, application), + d( new Private( this ) ) +{ + d->application = application; +} + +/*! + Destructor +*/ +KDUpdater::UpdateInstaller::~UpdateInstaller() +{ + delete d; +} + +/*! + Returns the update application for which the update is being installed +*/ +KDUpdater::Application* KDUpdater::UpdateInstaller::application() const +{ + return d->application; +} + +/*! + Use this function to let the installer know what updates are to be installed. The + updates are actually installed when the \ref start() method is called on this class. +*/ +void KDUpdater::UpdateInstaller::setUpdatesToInstall(const QList<KDUpdater::Update*>& updates) +{ + d->updates = updates; +} + +/*! + Returns the updates that would be installed when the next time \ref the start) method is called. +*/ +QList<KDUpdater::Update*> KDUpdater::UpdateInstaller::updatesToInstall() const +{ + return d->updates; +} + +/*! + \internal +*/ +void KDUpdater::UpdateInstaller::doRun() +{ + QList<KDUpdater::Update*>& updates = d->updates; + + // First download all the updates + d->updateDownloadDoneCount = 0; + d->totalUpdates = updates.count(); + + for(int i=0; i<updates.count(); i++) + { + KDUpdater::Update* update = updates[i]; + if( update->application() != d->application ) + continue; + + update->setProperty("_ProgressPc_", 0); + connect(update, SIGNAL(progressValue(int)), this, SLOT(slotUpdateDownloadProgress(int))); + connect(update, SIGNAL(finished()), this, SLOT(slotUpdateDownloadDone())); + connect(update, SIGNAL(stopped()), this, SLOT(slotUpdateDownloadDone())); + update->download(); + } + + d->totalProgressPc = updates.count() * 100; + d->currentProgressPc = 0; + + // Wait until all updates have been downloaded + while(d->updateDownloadDoneCount != updates.count()) + { + QCoreApplication::processEvents(); + + // Normalized progress + int progressPc = computePercent(d->currentProgressPc, d->totalProgressPc); + + // Bring the progress to within 50 percent + progressPc = (progressPc>>1); + + // Report the progress + reportProgress(progressPc, tr("Downloading updates...")); + } + + // Global progress + reportProgress(50, tr("Updates downloaded...")); + + // Save the current working directory of the application + QDir oldCWD = QDir::current(); + + int pcDiff = computePercent(1, updates.count()); + pcDiff = computeProgressPercentage(50, 95, pcDiff) - 50; + + // Now install one update after another. + for(int i=0; i<updates.count(); i++) + { + KDUpdater::Update* update = updates[i]; + + // Global progress + QString msg = tr("Installing %1..").arg(update->name()); + int minPc = pcDiff*i + 50; + int maxPc = minPc + pcDiff; + reportProgress(minPc, msg); + + if( update->application() != d->application ) + continue; + + QDir::setCurrent(oldCWD.absolutePath()); + if (!installUpdate(update, minPc, maxPc)) { + d->application->packagesInfo()->writeToDisk(); + return; + } + } + + d->application->packagesInfo()->writeToDisk(); + + // Global progress + reportProgress(95, tr("Finished installing updates. Now removing temporary files and directories..")); + + // Restore the current working directory of the application + QDir::setCurrent(oldCWD.absolutePath()); + + // Remove all the toRemoveDirs + for(int i=0; i<d->toRemoveDirs.count(); i++) + { + QDir dir( d->toRemoveDirs[i] ); + d->removeDirectory( dir ); + + QString dirName = dir.dirName(); + dir.cdUp(); + dir.rmdir( dirName ); + } + d->toRemoveDirs.clear(); + + // Global progress + reportProgress(100, tr("Removed temporary files and directories")); + reportDone(); +} + +/*! + \internal +*/ +bool KDUpdater::UpdateInstaller::doStop() +{ + return false; +} + +/*! + \internal +*/ +bool KDUpdater::UpdateInstaller::doPause() +{ + return false; +} + +/*! + \internal +*/ +bool KDUpdater::UpdateInstaller::doResume() +{ + return false; +} + +bool KDUpdater::UpdateInstaller::installUpdate(KDUpdater::Update* update, int minPc, int maxPc) +{ + QString updateName( update->name() ); + + // Sanity checks + if( !update->isDownloaded() ) + { + QString msg = tr("Could not download update '%1'").arg(update->name()); + reportError(msg); + return false; + } + + // Step 1: Prepare a directory into which the UpdateFile will be unpacked. + // If update file is C:/Users/PRASHA~1/AppData/Local/Temp/qt_temp.Hp1204 and + // the applicationn name "MyApplication" + // Then the directory would be %USERDIR%/AppData/Local/Temp/MyApplication_Update1 + static int count = 0; + QString dirName = QString::fromLatin1("%1_Update%2").arg(d->application->applicationName(), QString::number(count++)); + QString updateFile = update->downloadedFileName(); + QFileInfo fi(updateFile); + QDir dir( fi.absolutePath() ); + dir.mkdir( dirName ); + dir.cd( dirName ); + d->toRemoveDirs << dir.absolutePath(); + + // Step 2: Unpack the update file into the update directory + KDUpdater::UFUncompressor uncompressor; + uncompressor.setFileName( updateFile ); + uncompressor.setDestination( dir.absolutePath() ); + + if (!uncompressor.uncompress()) { + reportError(tr("Couldn't uncompress update: %1") + .arg(uncompressor.errorString())); + return false; + } + + // Step 3: Find out the directory in which UpdateInstructions.xml can be found + QDir updateDir = dir; + while( !updateDir.exists(QLatin1String( "UpdateInstructions.xml" )) ) + { + QString path = updateDir.absolutePath(); + QFileInfoList fiList = updateDir.entryInfoList(QDir::Dirs|QDir::NoDotAndDotDot); + if( !fiList.count() ) // || fiList.count() >= 2 ) + { + QString msg = tr("Could not find UpdateInstructions.xml for %1").arg(update->name()); + reportError(msg); + return false; + } + + updateDir.cd(fiList.first().fileName()); + } + + // Set the application's current working directory as updateDir + QDir::setCurrent(updateDir.absolutePath()); + + // Step 4: Now load the UpdateInstructions.xml file + QDomDocument doc; + QFile file(updateDir.absoluteFilePath(QLatin1String( "UpdateInstructions.xml" ))); + if( !file.open(QFile::ReadOnly) ) + { + QString msg = tr("Could not read UpdateInstructions.xml of %1").arg(update->name()); + reportError(msg); + return false; + } + if( !doc.setContent(&file) ) + { + QString msg = tr("Could not read UpdateInstructions.xml of %1").arg(update->name()); + reportError(msg); + return false; + } + + // Now parse and execute update operations + QDomNodeList operEList = doc.elementsByTagName(QLatin1String( "UpdateOperation" )); + QString msg = tr("Installing %1").arg(updateName); + + for(int i=0; i<operEList.count(); i++) + { + int pc = computePercent(i+1, operEList.count()); + pc = computeProgressPercentage(minPc, maxPc, pc); + reportProgress(pc, msg); + + // Fetch the important XML elements in UpdateOperation + QDomElement operE = operEList.at(i).toElement(); + QDomElement nameE = operE.firstChildElement(QLatin1String( "Name" )); + QDomElement errorE = operE.firstChildElement(QLatin1String( "OnError" )); + QDomElement argE = operE.firstChildElement(QLatin1String( "Arg" )); + + // Figure out information about the update operation to perform + QString operName = nameE.text(); + QString onError = errorE.attribute(QLatin1String( "Action" ), QLatin1String( "Abort" ) ); + QStringList args; + while( !argE.isNull() ) + { + args << argE.text(); + argE = argE.nextSiblingElement(QLatin1String( "Arg" )); + } + + //QString operSignature = QString::fromLatin1("%1(%2)").arg(operName, args.join( QLatin1String( ", ") ) ); + + // Fetch update operation + KDUpdater::UpdateOperation* const updateOperation = KDUpdater::UpdateOperationFactory::instance().create(operName); + if( !updateOperation ) + { + QString errMsg = tr("Update operation %1 not supported").arg(operName); + reportError(errMsg); + + if( onError == QLatin1String( "Continue" ) ) + continue; + + if( onError == QLatin1String( "Abort" ) ) + return false; + + if( onError == QLatin1String( "AskUser" ) ) + { + // TODO: + continue; + } + } + + // Now resolve special fields in arguments + d->resolveArguments(args); + + // Now set the arguments to the update operation and execute the update operation + updateOperation->setArguments(args); + updateOperation->setApplication( d->application ); + const bool success = updateOperation->performOperation(); + + updateOperation->clear(); + + if( !success ) + { + QString errMsg = tr("Cannot execute '%1'").arg(updateOperation->operationCommand()); + reportError(errMsg); + + if( onError == QLatin1String( "Continue" ) ) + continue; + + if( onError == QLatin1String( "Abort" ) ) + return false; + + if( onError == QLatin1String( "AskUser" ) ) + { + // TODO: + continue; + } + } + delete updateOperation; + } + + Q_FOREACH( UpdateOperation* updateOperation, update->operations() ) { + updateOperation->performOperation(); + } + + msg = tr("Finished installing update %1").arg(update->name()); + reportProgress(maxPc, msg); + return true; +} + +/*! + \internal +*/ +void KDUpdater::UpdateInstaller::Private::slotUpdateDownloadProgress(int percent) +{ + // 0-49 percent progress is dedicated for the download of updates + KDUpdater::Update* update = qobject_cast<KDUpdater::Update*>(q->sender()); + if( !update ) + return; + + int oldPc = update->property("_ProgressPc_").toInt(); + int diffPc = percent-oldPc; + if(diffPc <= 0) + return; + + currentProgressPc += diffPc; + update->setProperty("_ProgressPc_", percent); +} + +/*! + \internal +*/ +void KDUpdater::UpdateInstaller::Private::slotUpdateDownloadDone() +{ + ++updateDownloadDoneCount; +} + +void KDUpdater::UpdateInstaller::Private::resolveArguments(QStringList& args) +{ + for(int i=0; i<args.count(); i++) + { + QString arg = args[i]; + + arg = arg.replace(QLatin1String( "{APPDIR}" ), application->applicationDirectory()); + arg = arg.replace(QLatin1String( "{HOME}" ), QDir::homePath()); + arg = arg.replace(QLatin1String( "{APPNAME}" ), application->applicationName()); + arg = arg.replace(QLatin1String( "{APPVERSION}" ), application->applicationVersion()); + arg = arg.replace(QLatin1String( "{CURPATH}" ), QDir::currentPath()); + arg = arg.replace(QLatin1String( "{ROOT}" ), QDir::rootPath()); + arg = arg.replace(QLatin1String( "{TEMP}" ), QDir::tempPath()); + + args[i] = arg; + } +} + +void KDUpdater::UpdateInstaller::Private::removeDirectory(const QDir & dir) +{ + QFileInfoList fiList = dir.entryInfoList(QDir::Dirs|QDir::Files|QDir::NoDotAndDotDot); + if( !fiList.count() ) + return; + + for(int i=0; i<fiList.count(); i++) + { + QFileInfo fi = fiList[i]; + if( fi.isDir() ) + { + QDir childDir = fi.absoluteFilePath(); + removeDirectory( childDir ); + dir.rmdir( childDir.dirName() ); + } + else if( fi.isFile() ) + QFile::remove( fi.absoluteFilePath() ); + } +} + +#include "moc_kdupdaterupdateinstaller.cpp" diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdateinstaller.h b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdateinstaller.h new file mode 100644 index 000000000..dc3e58e48 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdateinstaller.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KD_UPDATER_UPDATE_INSTALLER_H +#define KD_UPDATER_UPDATE_INSTALLER_H + +#include "kdupdater.h" +#include "kdupdatertask.h" +#include <QList> + +namespace KDUpdater +{ + class Application; + class Update; + + class KDTOOLS_UPDATER_EXPORT UpdateInstaller : public Task + { + Q_OBJECT + + public: + explicit UpdateInstaller(Application* application); + ~UpdateInstaller(); + + Application* application() const; + + void setUpdatesToInstall(const QList<Update*>& updates); + QList<Update*> updatesToInstall() const; + + private: + void doRun(); + bool doStop(); + bool doPause(); + bool doResume(); + + bool installUpdate(Update* update, int minPc, int maxPc); + + class Private; + Private * const d; + + Q_PRIVATE_SLOT( d, void slotUpdateDownloadProgress(int) ) + Q_PRIVATE_SLOT( d, void slotUpdateDownloadDone() ) + }; +}; + +#endif diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdateoperation.cpp b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdateoperation.cpp new file mode 100644 index 000000000..7c4121e8e --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdateoperation.cpp @@ -0,0 +1,417 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "kdupdaterupdateoperation.h" + +#include "kdupdaterapplication.h" + +#include <QDebug> +#include <QDir> +#include <QFileInfo> +#include <QTemporaryFile> + +/*! + \ingroup kdupdater + \class KDUpdater::UpdateOperation kdupdaterupdateoperation.h KDUpdaterUpdateOperation + \brief Abstract base class for update operations. + + The \ref KDUpdater::UpdateOperation is an abstract class that specifies an interface for + update operations. Concrete implementations of this class must perform a single update + operation like copy, move, delete etc. + + \note Two seperate threads cannot be using a single instance of KDUpdater::UpdateOperation + at the same time. +*/ + +/* + * \internal + * Returns a filename for a temporary file based on \a templateName + */ +static QString backupFileName( const QString& templateName = QString() ) +{ + const QFileInfo templ( templateName ); + QTemporaryFile file( QDir::temp().absoluteFilePath( templ.fileName() ) ); + file.open(); + const QString name = file.fileName(); + file.close(); + file.remove(); + return name; +} + +using namespace KDUpdater; + +struct UpdateOperation::UpdateOperationData +{ + UpdateOperationData( UpdateOperation* qq ) : + q( qq ), + error( 0 ), + application(0) + {} + + UpdateOperation* q; + QString name; + QStringList args; + QString errorString; + int error; + Application* application; + QVariantMap values; + QStringList delayedDeletionFiles; +}; + + +/*! + Constructor +*/ +UpdateOperation::UpdateOperation() + : d ( new UpdateOperationData( this ) ) +{ +} + +/*! + Destructor +*/ +UpdateOperation::~UpdateOperation() +{ + Application* const app = Application::instance(); + if( app ) + app->addFilesForDelayedDeletion( filesForDelayedDeletion() ); + delete d; +} + +/*! + Returns the update operation name. + + \sa setName() +*/ +QString UpdateOperation::name() const +{ + return d->name; +} + +/*! + Returns a command line string that describes the update operation. The returned + string would be of the form + + <name> <arg1> <arg2> <arg3> .... +*/ +QString UpdateOperation::operationCommand() const +{ + QString argsStr = d->args.join(QLatin1String( " " )); + return QString::fromLatin1( "%1 %2" ).arg(d->name, argsStr); +} + +/*! + Returns true if there exists a setting called \a name. Otherwise returns false. +*/ +bool UpdateOperation::hasValue( const QString& name ) const +{ + return d->values.contains( name ); +} + +/*! + Clears the value of setting \a name and removes it. + \post hasValue( \a name ) returns false. +*/ +void UpdateOperation::clearValue( const QString& name ) +{ + d->values.remove( name ); +} + +/*! + Returns the value of setting \a name. If the setting does not exists, + this returns an empty QVariant. +*/ +QVariant UpdateOperation::value( const QString& name ) const +{ + return hasValue( name ) ? d->values[ name ] : QVariant(); +} + +/*! + Sets the value of setting \a name to \a value. +*/ +void UpdateOperation::setValue( const QString& name, const QVariant& value ) +{ + d->values[ name ] = value; +} + +/*! + Sets a update operation name. Subclasses will have to provide a unique + name to describe this operation. +*/ +void UpdateOperation::setName(const QString& name) +{ + d->name = name; +} + +/*! + Through this function, arguments to the update operation can be specified + to the update operation. +*/ +void UpdateOperation::setArguments(const QStringList& args) +{ + d->args = args; +} + +/*! + Sets the Application for this operation. + This may be used by some operations +*/ +void UpdateOperation::setApplication( Application* application ) +{ + d->application = application; +} + +/*! + Returns the last set function arguments. +*/ +QStringList UpdateOperation::arguments() const +{ + return d->args; +} + +/*! + Returns error details in case performOperation() failed. +*/ +QString UpdateOperation::errorString() const +{ + return d->errorString; +} + +/*! + * Can be used by subclasses to report more detailed error codes (optional). + * To check if an operation was successful, use the return value of performOperation(). + */ +int UpdateOperation::error() const +{ + return d->error; +} + +/*! + * Used by subclasses to set the error string. + */ +void UpdateOperation::setErrorString( const QString& str ) +{ + d->errorString = str; +} + +/*! + * Used by subclasses to set the error code. + */ +void UpdateOperation::setError( int error, const QString& errorString ) +{ + d->error = error; + if( !errorString.isNull() ) + d->errorString = errorString; +} + +/*! + Clears the previously set argument list and application +*/ +void UpdateOperation::clear() +{ + d->args.clear(); + d->application = 0; +} + +QStringList UpdateOperation::filesForDelayedDeletion() const +{ + return d->delayedDeletionFiles; +} + +/*! + Registers a file to be deleted later, once the application was restarted + (and the file isn't used anymore for sure). + @param files the files to be registered +*/ +void UpdateOperation::registerForDelayedDeletion( const QStringList& files ) +{ + d->delayedDeletionFiles << files; +} + +/*! + Tries to delete \a file. If \a file can't be deleted, it gets registered for delayed deletion. +*/ +bool UpdateOperation::deleteFileNowOrLater( const QString& file, QString* errorString ) +{ + if( file.isEmpty() || QFile::remove( file ) ) + return true; + + if( !QFile::exists( file ) ) + return true; + + const QString backup = backupFileName( file ); + QFile f( file ); + if( !f.rename( backup ) ) + { + if ( errorString ) + *errorString = f.errorString(); + return false; + } + registerForDelayedDeletion( QStringList( backup ) ); + return true; +} + +/*! + Returns a pointer to the current Application +*/ +Application* UpdateOperation::application() const +{ + return d->application; +} + +/*! + \fn virtual void KDUpdater::UpdateOperation::backup() = 0; + + Subclasses must implement this function to backup any data before performing the action. +*/ + +/*! + \fn virtual bool KDUpdater::UpdateOperation::performOperation() = 0; + + Subclasses must implement this function to perform the update operation +*/ + +/*! + \fn virtual bool KDUpdater::UpdateOperation::undoOperation() = 0; + + Subclasses must implement this function to perform the reverse of the operation. +*/ + +/*! + \fn virtual bool KDUpdater::UpdateOperation::testOperation() = 0; + + Subclasses must implement this function to perform the test operation. +*/ + +/*! + \fn virtual bool KDUpdater::UpdateOperation::clone() = 0; + + Subclasses must implement this function to clone the current operation. +*/ + +/*! + Saves this UpdateOperation in XML. You can override this method to store your own extra-data. + The default implementation is taking care of arguments and values set via setValue. +*/ +QDomDocument UpdateOperation::toXml() const +{ + QDomDocument doc; + QDomElement root = doc.createElement( QLatin1String("operation") ); + doc.appendChild( root ); + QDomElement args = doc.createElement( QLatin1String("arguments") ); + Q_FOREACH( const QString &s, arguments() ) { + QDomElement arg = doc.createElement( QLatin1String("argument") ); + arg.appendChild( doc.createTextNode(s) ); + args.appendChild( arg ); + } + root.appendChild( args ); + if( d->values.isEmpty() ) + return doc; + + // append all values set with setValue + QDomElement values = doc.createElement( QLatin1String( "values" ) ); + for( QVariantMap::const_iterator it = d->values.begin(); it != d->values.end(); ++it ) + { + QDomElement value = doc.createElement( QLatin1String( "value" ) ); + const QVariant& variant = it.value(); + value.setAttribute( QLatin1String( "name" ), it.key() ); + value.setAttribute( QLatin1String( "type" ), QLatin1String( QVariant::typeToName( variant.type() ) ) ); + + if( variant.type() != QVariant::List && variant.type() != QVariant::StringList && qVariantCanConvert< QString >( variant ) ) + { + // it can convert to string? great! + value.appendChild( doc.createTextNode( variant.toString() ) ); + } + else + { + // no? then we have to go the hard way... + QByteArray data; + QDataStream stream( &data, QIODevice::WriteOnly ); + stream << variant; + value.appendChild( doc.createTextNode( QLatin1String( data.toBase64().data() ) ) ); + } + values.appendChild( value ); + } + root.appendChild( values ); + return doc; +} + +/*! + Restores UpdateOperation's arguments and values from the XML document \a doc. + Returns true on success, otherwise false. +*/ +bool UpdateOperation::fromXml( const QDomDocument &doc ) +{ + QStringList args; + const QDomElement root = doc.documentElement(); + const QDomElement argsElem = root.firstChildElement( QLatin1String("arguments") ); + Q_ASSERT( ! argsElem.isNull() ); + for( QDomNode n = argsElem.firstChild(); ! n.isNull(); n = n.nextSibling() ) { + const QDomElement e = n.toElement(); + if( !e.isNull() && e.tagName() == QLatin1String("argument") ) + args << e.text(); + } + setArguments(args); + + d->values.clear(); + const QDomElement values = root.firstChildElement( QLatin1String( "values" ) ); + for( QDomNode n = values.firstChild(); !n.isNull(); n = n.nextSibling() ) + { + const QDomElement v = n.toElement(); + if( v.isNull() || v.tagName() != QLatin1String( "value" ) ) + continue; + + const QString name = v.attribute( QLatin1String( "name" ) ); + const QString type = v.attribute( QLatin1String( "type" ) ); + const QString value = v.text(); + + const QVariant::Type t = QVariant::nameToType( type.toLatin1().data() ); + QVariant var = qVariantFromValue( value ); + if( t == QVariant::List || t == QVariant::StringList || !var.convert( t ) ) + { + QDataStream stream( QByteArray::fromBase64( value.toLatin1() ) ); + stream >> var; + } + + d->values[ name ] = var; + } + + return true; +} + +/*! + Restores UpdateOperation's arguments and values from the XML document at path \a xml. + Returns true on success, otherwise false. + \overload +*/ +bool UpdateOperation::fromXml( const QString &xml ) +{ + QDomDocument doc; + QString errorMsg; + int errorLine; + int errorColumn; + if ( ! doc.setContent( xml, &errorMsg, &errorLine, &errorColumn ) ) { + qWarning() << "Error parsing xml error=" << errorMsg << "line=" << errorLine << "column=" << errorColumn; + return false; + } + return fromXml(doc); +} diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdateoperation.h b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdateoperation.h new file mode 100644 index 000000000..53111d23a --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdateoperation.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KD_UPDATER_UPDATE_OPERATION_H +#define KD_UPDATER_UPDATE_OPERATION_H + +#include "kdupdater.h" + +#include <QCoreApplication> +#include <QStringList> +#include <QVariant> +#include <QDomDocument> + +namespace KDUpdater +{ + class Application; + + class KDTOOLS_UPDATER_EXPORT UpdateOperation + { + Q_DECLARE_TR_FUNCTIONS(UpdateOperation) + + public: + enum Error { + NoError=0, + InvalidArguments=1, + UserDefinedError=128 + }; + + UpdateOperation(); + virtual ~UpdateOperation(); + + QString name() const; + QString operationCommand() const; + + bool hasValue( const QString& name ) const; + void clearValue( const QString& name ); + QVariant value( const QString& name ) const; + void setValue( const QString& name, const QVariant &value ); + + void setArguments(const QStringList& args); + void setApplication( Application* application ); + QStringList arguments() const; + void clear(); + QString errorString() const; + int error() const; + QStringList filesForDelayedDeletion() const; + + virtual void backup() = 0; + virtual bool performOperation() = 0; + virtual bool undoOperation() = 0; + virtual bool testOperation() = 0; + virtual UpdateOperation* clone() const = 0; + + virtual QDomDocument toXml() const; + virtual bool fromXml( const QString &xml ); + virtual bool fromXml( const QDomDocument &doc ); + + protected: + void setName(const QString& name); + Application* application() const; + void setErrorString( const QString& errorString ); + void setError( int error, const QString& errorString = QString() ); + void registerForDelayedDeletion( const QStringList& files ); + bool deleteFileNowOrLater( const QString& file, QString* errorString=0 ); + + private: + struct UpdateOperationData; + UpdateOperationData* d; + }; +}; + +#endif diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdateoperationfactory.cpp b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdateoperationfactory.cpp new file mode 100644 index 000000000..076af5273 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdateoperationfactory.cpp @@ -0,0 +1,116 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "kdupdaterupdateoperationfactory.h" +#include "kdupdaterupdateoperations.h" + +#include <QHash> + +/*! + \ingroup kdupdater + \class KDUpdater::UpdateOperationFactory kdupdaterupdateoperationfactory.h KDUpdaterUpdateOperationFactory + \brief Factory for \ref KDUpdater::UpdateOperation + + This class acts as a factory for \ref KDUpdater::UpdateOperation. You can register + one or more update operations with this factory and query operations based on their name. + + This class follows the singleton design pattern. Only one instance of this class can + be created and its reference can be fetched from the \ref instance() method. +*/ + +/*! + \fn KDUpdater::UpdateOperationFactory::registerUpdateOperation( const QString& name ) + + Registers T as new UpdateOperation with \a name. When create() is called with that \a name, + T is constructed using its default constructor. +*/ + +using namespace KDUpdater; + +struct UpdateOperationFactory::UpdateOperationFactoryData +{ +}; + +/*! + Returns the UpdateOperationFactory instance. The instance is created if needed. +*/ +UpdateOperationFactory& UpdateOperationFactory::instance() +{ + static UpdateOperationFactory theFactory; + return theFactory; +} + +/*! + Constructor +*/ +UpdateOperationFactory::UpdateOperationFactory() + : d ( new UpdateOperationFactoryData ) +{ + // Register the default update operation set + registerUpdateOperation< CopyOperation >( QLatin1String( "Copy" ) ); + registerUpdateOperation< MoveOperation >( QLatin1String( "Move" ) ); + registerUpdateOperation< DeleteOperation >( QLatin1String( "Delete" ) ); + registerUpdateOperation< MkdirOperation >( QLatin1String( "Mkdir" ) ); + registerUpdateOperation< RmdirOperation >( QLatin1String( "Rmdir" ) ); + registerUpdateOperation< AppendFileOperation >( QLatin1String( "AppendFile" ) ); + registerUpdateOperation< PrependFileOperation >( QLatin1String( "PrependFile" ) ); + registerUpdateOperation< ExecuteOperation >( QLatin1String( "Execute" ) ); + registerUpdateOperation< UpdatePackageOperation >( QLatin1String( "UpdatePackage" ) ); + registerUpdateOperation< UpdateCompatOperation >( QLatin1String( "UpdateCompat" ) ); +} + +UpdateOperationFactory::~UpdateOperationFactory() +{ +} + +/*! + Returns the number of update operations in the factory. +*/ +int UpdateOperationFactory::updateOperationCount() const +{ + return productCount(); +} + +/*! + Returns a list containing the names of all available UpdateOperations. +*/ +QStringList UpdateOperationFactory::availableUpdateOperations() const +{ + return availableProducts(); +} + +/* + Unregisters the update operation previously registered with \a name. +*/ +void UpdateOperationFactory::unregisterUpdateOperation( const QString& name ) +{ + unregisterProduct( name ); +} + +/*! + Registers \a create to be a factory function to create an UpdateOperation with \a name. + \sa registerUpdateOperation +*/ +void UpdateOperationFactory::registerUpdateOperationFactory( const QString& name, UpdateOperationFactoryFunction create ) +{ + registerProductionFunction( name, create ); +} diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdateoperationfactory.h b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdateoperationfactory.h new file mode 100644 index 000000000..d42a871c3 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdateoperationfactory.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KD_UPDATER_UPDATE_OPERATION_FACTORY_H +#define KD_UPDATER_UPDATE_OPERATION_FACTORY_H + +#include <KDToolsCore/kdgenericfactory.h> + +#include "kdupdater.h" +#include <KDToolsCore/pimpl_ptr.h> + +namespace KDUpdater +{ + class UpdateOperation; + + typedef KDGenericFactory< UpdateOperation >::FactoryFunction UpdateOperationFactoryFunction; + + class KDTOOLS_UPDATER_EXPORT UpdateOperationFactory : public KDGenericFactory< UpdateOperation > + { + Q_DISABLE_COPY( UpdateOperationFactory ) + public: + static UpdateOperationFactory& instance(); + ~UpdateOperationFactory(); + + template< class T > + void registerUpdateOperation( const QString& name ) + { + registerProduct< T >( name ); + } + void registerUpdateOperationFactory( const QString& name, UpdateOperationFactoryFunction create ); + + void unregisterUpdateOperation( const QString& name ); + + int updateOperationCount() const; + QStringList availableUpdateOperations() const; + + protected: + UpdateOperationFactory(); + + private: + struct UpdateOperationFactoryData; + kdtools::pimpl_ptr<UpdateOperationFactoryData> d; + }; +} + +#endif diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdateoperations.cpp b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdateoperations.cpp new file mode 100644 index 000000000..a1486d34d --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdateoperations.cpp @@ -0,0 +1,1120 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "kdupdaterupdateoperations.h" +#include "kdupdaterapplication.h" +#include "kdupdaterpackagesinfo.h" +#include "environment.h" + +#include <QFile> +#include <QDir> +#include <QDirIterator> +#include <QProcess> +#include <QTextStream> +#include <QDebug> +#include <QTemporaryFile> + + +#include <cerrno> + +#define SUPPORT_DETACHED_PROCESS_EXECUTION + +#ifdef SUPPORT_DETACHED_PROCESS_EXECUTION +#ifdef Q_WS_WIN +#include <windows.h> +#endif +#endif + +using namespace KDUpdater; + +static bool removeDirectory( const QString& path, QString* errorString ) +{ + Q_ASSERT( errorString ); + const QFileInfoList entries = QDir( path ).entryInfoList( QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden ); + for( QFileInfoList::const_iterator it = entries.constBegin(); it != entries.constEnd(); ++it ) + { + if( it->isDir() && !it->isSymLink() ) + { + removeDirectory( it->filePath(), errorString ); + } + else + { + QFile f( it->filePath() ); + if( !f.remove() ) + return false; + } + } + + errno = 0; + const bool success = QDir().rmdir( path ); + if ( errno ) + *errorString = QLatin1String( strerror(errno) ); + return success; +} +/* + * \internal + * Returns a filename for a temporary file based on \a templateName + */ +static QString backupFileName( const QString& templateName = QString() ) +{ + QTemporaryFile file( templateName ); + file.open(); + const QString name = file.fileName(); + file.close(); + file.remove(); + return name; +} + +//////////////////////////////////////////////////////////////////////////// +// KDUpdater::CopyOperation +//////////////////////////////////////////////////////////////////////////// + +CopyOperation::CopyOperation() +{ + setName(QLatin1String( "Copy" )); +} + +CopyOperation::~CopyOperation() +{ + deleteFileNowOrLater( value( QLatin1String( "backupOfExistingDestination" ) ).toString() ); +} + +void CopyOperation::backup() +{ + const QString dest = arguments().last(); + if( !QFile::exists( dest ) ) + { + clearValue( QLatin1String( "backupOfExistingDestination" ) ); + return; + } + + setValue( QLatin1String( "backupOfExistingDestination" ), backupFileName( dest ) ); + + // race condition: The backup file could get created + // by another process right now. But this is the same + // in QFile::copy... + const bool success = QFile::rename( dest, value( QLatin1String( "backupOfExistingDestination" ) ).toString() ); + if(!success) + setError( UserDefinedError, tr("Could not backup file %1").arg(dest) ); +} + +bool CopyOperation::performOperation() +{ + // We need two args to complete the copy operation. + // First arg provides the complete file name of source + // Second arg provides the complete file name of dest + QStringList args = this->arguments(); + if( args.count() != 2 ) { + setError( InvalidArguments ); + setErrorString( tr("Invalid arguments: %1 arguments given, 2 expected.").arg( args.count() ) ); + return false; + } + QString source = args.first(); + QString dest = args.last(); + + // If destination file exists, then we cannot use QFile::copy() + // because it does not overwrite an existing file. So we remove + // the destination file. + if( QFile::exists(dest) ) + { + QFile file( dest ); + if( !file.remove() ) { + setError( UserDefinedError ); + setErrorString( tr("Could not remove destination file %1: %2.").arg( dest, file.errorString() ) ); + return false; + } + } + + QFile file( source ); + const bool copied = file.copy( dest ); + if ( !copied ) { + setError( UserDefinedError ); + setErrorString( tr("Could not copy %1 to %2: %3.").arg( source, dest, file.errorString() ) ); + } + return copied; +} + +bool CopyOperation::undoOperation() +{ + const QString dest = arguments().last(); + + QFile destF( dest ); + // first remove the dest + if( !destF.remove() ) + { + setError( UserDefinedError, tr("Could not delete file %1: %2").arg(dest, destF.errorString()) ); + return false; + } + + // no backup was done: + // the copy destination file wasn't existing yet - that's no error + if( !hasValue( QLatin1String( "backupOfExistingDestination" ) ) ) + return true; + + QFile backupF( value( QLatin1String( "backupOfExistingDestination" ) ).toString() ); + // otherwise we have to copy the backup back: + const bool success = backupF.rename( dest ); + if(!success) { + setError( UserDefinedError, tr("Could not restore backup file into %1: %2").arg(dest, backupF.errorString()) ); + } + return success; +} + +/*! + \reimp + */ +QDomDocument CopyOperation::toXml() const +{ + // we don't want to save the backupOfExistingDestination + if( !hasValue( QLatin1String( "backupOfExistingDestination" ) ) ) + return UpdateOperation::toXml(); + + CopyOperation* const me = const_cast< CopyOperation* >( this ); + + const QVariant v = value( QLatin1String( "backupOfExistingDestination" ) ); + me->clearValue( QLatin1String( "backupOfExistingDestination" ) ); + const QDomDocument xml = UpdateOperation::toXml(); + me->setValue( QLatin1String( "backupOfExistingDestination" ), v ); + return xml; +} + +bool CopyOperation::testOperation() +{ + // TODO + return true; +} + +CopyOperation* CopyOperation::clone() const +{ + return new CopyOperation(); +} + + +//////////////////////////////////////////////////////////////////////////// +// KDUpdater::MoveOperation +//////////////////////////////////////////////////////////////////////////// + +MoveOperation::MoveOperation() +{ + setName(QLatin1String( "Move" )); +} + +MoveOperation::~MoveOperation() +{ + deleteFileNowOrLater( value( QLatin1String( "backupOfExistingDestination" ) ).toString() ); +} + +void MoveOperation::backup() +{ + const QString dest = arguments().last(); + if( !QFile::exists( dest ) ) + { + clearValue( QLatin1String( "backupOfExistingDestination" ) ); + return; + } + + setValue( QLatin1String( "backupOfExistingDestination" ), backupFileName( dest ) ); + + // race condition: The backup file could get created + // by another process right now. But this is the same + // in QFile::copy... + const bool success = QFile::rename( dest, value( QLatin1String( "backupOfExistingDestination" ) ).toString() ); + if(!success) + setError( UserDefinedError, tr("Could not backup file %1").arg(dest) ); +} + +bool MoveOperation::performOperation() +{ + // We need two args to complete the copy operation. + // First arg provides the complete file name of source + // Second arg provides the complete file name of dest + QStringList args = this->arguments(); + if( args.count() != 2 ) { + setError( InvalidArguments ); + setErrorString( tr("Invalid arguments: %1 arguments given, 2 expected.").arg( args.count() ) ); + return false; + } + + QString source = args.first(); + QString dest = args.last(); + + // If destination file exists, then we cannot use QFile::copy() + // because it does not overwrite an existing file. So we remove + // the destination file. + if( QFile::exists(dest) ) + { + QFile file( dest ); + if( !file.remove(dest) ) { + setError( UserDefinedError ); + setErrorString( tr("Could not remove destination file %1: %2.").arg( dest, file.errorString() ) ); + return false; + } + } + + // Copy source to destination. + QFile file( source ); + const bool copied = file.copy( source, dest ); + if ( !copied ) { + setError( UserDefinedError ); + setErrorString( tr("Could not copy %1 to %2: %3.").arg( source, dest, file.errorString() ) ); + return false; + } + + return deleteFileNowOrLater( source ); +} + +bool MoveOperation::undoOperation() +{ + const QStringList args = arguments(); + const QString& source = args.first(); + const QString& dest = args.last(); + + // first: copy back the destination to source + QFile destF( dest ); + if( !destF.copy( source ) ) + { + setError( UserDefinedError, tr("Cannot copy %1 to %2: %3").arg( dest, source, destF.errorString() ) ); + return false; + } + + // second: delete the move destination + if( !deleteFileNowOrLater( dest ) ) + { + setError( UserDefinedError, tr( "Cannot remove file %1" ) ); + return false; + } + + // no backup was done: + // the move destination file wasn't existing yet - that's no error + if( !hasValue( QLatin1String( "backupOfExistingDestination" ) ) ) + return true; + + // otherwise we have to copy the backup back: + QFile backupF( value( QLatin1String( "backupOfExistingDestination" ) ).toString() ); + const bool success = backupF.rename( dest ); + if(!success) + setError( UserDefinedError, tr("Cannot restore backup file for %1: %2").arg(dest, backupF.errorString()) ); + + return success; +} + +bool MoveOperation::testOperation() +{ + // TODO + return true; +} + +MoveOperation* MoveOperation::clone() const +{ + return new MoveOperation; +} + + +//////////////////////////////////////////////////////////////////////////// +// KDUpdater::DeleteOperation +//////////////////////////////////////////////////////////////////////////// + +DeleteOperation::DeleteOperation() +{ + setName(QLatin1String( "Delete" )); +} + +DeleteOperation::~DeleteOperation() +{ + deleteFileNowOrLater( value( QLatin1String( "backupOfExistingFile" ) ).toString() ); +} + +void DeleteOperation::backup() +{ + const QString fileName = arguments().first(); + setValue( QLatin1String( "backupOfExistingFile" ), backupFileName( fileName ) ); + QFile file( fileName ); + const bool success = file.copy( value( QLatin1String( "backupOfExistingFile" ) ).toString() ); + if(!success) + setError( UserDefinedError, tr("Cannot create backup of %1: %2").arg(fileName, file.errorString()) ); +} + +bool DeleteOperation::performOperation() +{ + // Requires only one parameter. That is the name of + // the file to remove. + QStringList args = this->arguments(); + if( args.count() != 1 ) { + setError( InvalidArguments ); + setErrorString( tr("Invalid arguments: %1 arguments given, 1 expected.").arg( args.count() ) ); + return false; + } + + const QString fName = args.first(); + return deleteFileNowOrLater( fName ); +} + +bool DeleteOperation::undoOperation() +{ + if( !hasValue( QLatin1String( "backupOfExistingFile" ) ) ) + return true; + + const QString fileName = arguments().first(); + QFile backupF( value( QLatin1String( "backupOfExistingFile" ) ).toString() ); + const bool success = backupF.copy( fileName ) && deleteFileNowOrLater( backupF.fileName() ); + if(!success) + setError( UserDefinedError, tr("Cannot restore backup file for %1: %2").arg(fileName, backupF.errorString()) ); + + return success; +} + +bool DeleteOperation::testOperation() +{ + // TODO + return true; +} + +DeleteOperation* DeleteOperation::clone() const +{ + return new DeleteOperation; +} + +/*! + \reimp + */ +QDomDocument DeleteOperation::toXml() const +{ + // we don't want to save the backupOfExistingFile + if( !hasValue( QLatin1String( "backupOfExistingFile" ) ) ) + return UpdateOperation::toXml(); + + DeleteOperation* const me = const_cast< DeleteOperation* >( this ); + + const QVariant v = value( QLatin1String( "backupOfExistingFile" ) ); + me->clearValue( QLatin1String( "backupOfExistingFile" ) ); + const QDomDocument xml = UpdateOperation::toXml(); + me->setValue( QLatin1String( "backupOfExistingFile" ), v ); + return xml; +} + +//////////////////////////////////////////////////////////////////////////// +// KDUpdater::MkdirOperation +//////////////////////////////////////////////////////////////////////////// + +MkdirOperation::MkdirOperation() +{ + setName(QLatin1String( "Mkdir" )); +} + +MkdirOperation::~MkdirOperation() +{ + +} + +void MkdirOperation::backup() +{ + static const QRegExp re( QLatin1String( "\\\\|/" ) ); + static const QLatin1String sep( "/" ); + + QString path = arguments().first(); + path.replace( re, sep ); + + QDir createdDir = QDir::root(); + + // find out, which part of the path is the first one we actually need to create + int end = 0; + while( true ) + { + QString p = path.section( sep, 0, ++end ); + createdDir = QDir( p ); + if( !createdDir.exists() ) + break; + else if( p == path ) + { + // everything did already exist -> nothing to do for us (nothing to revert then, either) + createdDir = QDir::root(); + break; + } + } + + setValue( QLatin1String( "createddir" ), createdDir.absolutePath() ); +} + +bool MkdirOperation::performOperation() +{ + // Requires only one parameter. That is the name of + // the file to remove. + QStringList args = this->arguments(); + if( args.count() != 1 ) { + setError( InvalidArguments ); + setErrorString( tr("Invalid arguments: %1 arguments given, 1 expected.").arg( args.count() ) ); + return false; + } + QString dirName = args.first(); + const bool created = QDir::root().mkpath(dirName); + if ( !created ) { + setError( UserDefinedError ); + setErrorString( tr("Could not create folder %1: Unknown error.").arg( dirName ) ); + } + return created; +} + +bool MkdirOperation::undoOperation() +{ + Q_ASSERT( arguments().count() == 1 ); + QString dirName = arguments().first(); + + const QDir createdDir = QDir( value( QLatin1String( "createddir" ) ).toString() ); + const bool forceremoval = QVariant( value( QLatin1String( "forceremoval" ) ) ).toBool(); + + if( createdDir == QDir::root() ) + return true; + + QString errorString; + if( forceremoval ) + { + return removeDirectory( createdDir.path(), &errorString ); + } + + // even remove some hidden, OS-created files in there +#if defined Q_WS_MAC + QFile::remove( createdDir.path() + QLatin1String( "/.DS_Store" ) ); +#elif defined Q_WS_WIN + QFile::remove( createdDir.path() + QLatin1String( "/Thumbs.db" ) ); +#endif + + errno = 0; + const bool result = QDir::root().rmdir( createdDir.path() ); + if ( !result ) { + if ( errorString.isEmpty() ) + setError( UserDefinedError, tr("Cannot remove directory %1: %2").arg( createdDir.path(), errorString ) ); + else + setError( UserDefinedError, tr("Cannot remove directory %1: %2").arg( createdDir.path(), QLatin1String(strerror(errno)) ) ); + } + return result; +} + +bool KDUpdater::MkdirOperation::testOperation() +{ + // TODO + return true; +} + +MkdirOperation* MkdirOperation::clone() const +{ + return new MkdirOperation; +} + +//////////////////////////////////////////////////////////////////////////// +// KDUpdater::RmdirOperation +//////////////////////////////////////////////////////////////////////////// + +RmdirOperation::RmdirOperation() +{ + setValue( QLatin1String( "removed" ), false ); + setName(QLatin1String( "Rmdir" )); +} + +RmdirOperation::~RmdirOperation() +{ + +} + +void RmdirOperation::backup() +{ + // nothing to backup - rollback will just create the directory +} + +bool RmdirOperation::performOperation() +{ + // Requires only one parameter. That is the name of + // the file to remove. + QStringList args = this->arguments(); + if( args.count() != 1 ) { + setError( InvalidArguments ); + setErrorString( tr("Invalid arguments: %1 arguments given, 1 expected.").arg( args.count() ) ); + return false; + } + + QString dirName = args.first(); + QDir dir( dirName ); + if( !dir.exists() ) { + setError( UserDefinedError ); + setErrorString( tr("Could not remove folder %1: The folder does not exist.").arg( dirName ) ); + return false; + } + + errno = 0; + const bool removed = dir.rmdir( dirName ); + setValue( QLatin1String( "removed" ), removed ); + if ( !removed ) { + setError( UserDefinedError ); + setErrorString( tr("Could not remove folder %1: %2.").arg( dirName, QLatin1String(strerror(errno)) ) ); + } + return removed; +} + +bool RmdirOperation::undoOperation() +{ + if( !value( QLatin1String( "removed" ) ).toBool() ) + return true; + + const QFileInfo fi( arguments().first() ); + errno = 0; + const bool success = fi.dir().mkdir( fi.fileName() ); + if(!success) + setError( UserDefinedError, tr("Cannot recreate directory %1: %2").arg( fi.fileName(), QLatin1String(strerror(errno)) ) ); + + return success; +} + +bool RmdirOperation::testOperation() +{ + // TODO + return true; +} + +RmdirOperation* RmdirOperation::clone() const +{ + return new RmdirOperation; +} + + +//////////////////////////////////////////////////////////////////////////// +// KDUpdater::AppendFileOperation +//////////////////////////////////////////////////////////////////////////// + +AppendFileOperation::AppendFileOperation() +{ + setName(QLatin1String( "AppendFile" )); +} + +AppendFileOperation::~AppendFileOperation() +{ + +} + +void AppendFileOperation::backup() +{ + const QString filename = arguments().first(); + + QFile file( filename ); + if( !file.exists() ) + return; // nothing to backup + + setValue( QLatin1String( "backupOfFile" ), backupFileName( filename ) ); + if( !file.copy( value( QLatin1String( "backupOfFile" ) ).toString() ) ) + { + setError( UserDefinedError, tr("Cannot backup file %1: %2").arg(filename, file.errorString()) ); + clearValue( QLatin1String( "backupOfFile" ) ); + } +} + +bool AppendFileOperation::performOperation() +{ + // This operation takes two arguments. First argument is the name + // of the file into which a text has to be appended. Second argument + // is the text to append. + QStringList args = this->arguments(); + if( args.count() != 2 ) { + setError( InvalidArguments ); + setErrorString( tr("Invalid arguments: %1 arguments given, 2 expected.").arg( args.count() ) ); + return false; + } + + QString fName = args.first(); + QString text = args.last(); + + QFile file(fName); + if( !file.open(QFile::Append) ) + { + // first we rename the file, then we copy it to the real target and open the copy - the renamed original is then marked for deletion + const QString newName = backupFileName( fName ); + if( !QFile::rename( fName, newName ) && QFile::copy( newName, fName ) && file.open( QFile::Append ) ) + { + QFile::rename( newName, fName ); + setError( UserDefinedError ); + setErrorString( tr("Could not open file %1 for writing: %2.").arg( file.fileName(), file.errorString() ) ); + return false; + } + deleteFileNowOrLater( newName ); + } + + QTextStream ts(&file); + ts << text; + file.close(); + + return true; +} + +bool AppendFileOperation::undoOperation() +{ + // backupOfFile being empty -> file didn't exist before -> no error + const QString filename = arguments().first(); + const QString backupOfFile = value( QLatin1String( "backupOfFile" ) ).toString(); + if( !backupOfFile.isEmpty() && !QFile::exists( backupOfFile ) ) + { + setError( UserDefinedError, tr("Cannot find backup file for %1").arg(filename) ); + return false; + } + + const bool removed = deleteFileNowOrLater( filename ); + if ( !removed ) { + setError( UserDefinedError, tr("Could not restore backup file for %1.").arg( filename ) ); + return false; + } + + // got deleted? We might be done, if it didn't exist before + if( backupOfFile.isEmpty() ) + return true; + + QFile backupFile( backupOfFile ); + const bool success = backupFile.rename( filename ); + if ( !success ) + setError( UserDefinedError, tr("Could not restore backup file for %1: %2").arg(filename, backupFile.errorString()) ); + return success; +} + +bool AppendFileOperation::testOperation() +{ + // TODO + return true; +} + +AppendFileOperation* AppendFileOperation::clone() const +{ + return new AppendFileOperation; +} + + +//////////////////////////////////////////////////////////////////////////// +// KDUpdater::PrependFileOperation +//////////////////////////////////////////////////////////////////////////// + +PrependFileOperation::PrependFileOperation() +{ + setName(QLatin1String( "PrependFile" )); +} + +PrependFileOperation::~PrependFileOperation() +{ + +} + +void PrependFileOperation::backup() +{ + const QString filename = arguments().first(); + + QFile file( filename ); + if( !file.exists() ) + return; // nothing to backup + + setValue( QLatin1String( "backupOfFile" ), backupFileName( filename ) ); + if( !file.copy( value( QLatin1String( "backupOfFile" ) ).toString() ) ) + { + setError( UserDefinedError, tr("Cannot backup file %1: %2").arg(filename, file.errorString()) ); + clearValue( QLatin1String( "backupOfFile" ) ); + } +} + +bool PrependFileOperation::performOperation() +{ + // This operation takes two arguments. First argument is the name + // of the file into which a text has to be appended. Second argument + // is the text to append. + QStringList args = this->arguments(); + if( args.count() != 2 ) {\ + setError( InvalidArguments ); + setErrorString( tr("Invalid arguments: %1 arguments given, 2 expected.").arg( args.count() ) ); + return false; + } + + QString fName = args.first(); + QString text = args.last(); + + // Load the file first. + QFile file(fName); + if( !file.open(QFile::ReadOnly) ) { + setError( UserDefinedError ); + setErrorString( tr("Could not open file %1 for reading: %2.").arg( file.fileName(), file.errorString() ) ); + return false; + } + QString fContents( QLatin1String( file.readAll() ) ); + file.close(); + + // Prepend text to the file text + fContents = text + fContents; + + // Now re-open the file in write only mode. + if( !file.open(QFile::WriteOnly) ) { + // first we rename the file, then we copy it to the real target and open the copy - the renamed original is then marked for deletion + const QString newName = backupFileName( fName ); + if( !QFile::rename( fName, newName ) && QFile::copy( newName, fName ) && file.open( QFile::WriteOnly ) ) + { + QFile::rename( newName, fName ); + setError( UserDefinedError ); + setErrorString( tr("Could not open file %1 for writing: %2.").arg( file.fileName(), file.errorString() ) ); + return false; + } + deleteFileNowOrLater( newName ); + } + QTextStream ts(&file); + ts << fContents; + file.close(); + + return true; +} + +bool PrependFileOperation::undoOperation() +{ + // bockupOfFile being empty -> file didn't exist before -> no error + const QString filename = arguments().first(); + const QString backupOfFile = value( QLatin1String( "backupOfFile" ) ).toString(); + if( !backupOfFile.isEmpty() && !QFile::exists( backupOfFile ) ) + { + setError( UserDefinedError, tr("Cannot find backup file for %1").arg(filename) ); + return false; + } + + if( !deleteFileNowOrLater( filename ) ) + { + setError( UserDefinedError, tr("Cannot restore backup file for %1").arg( filename ) ); + return false; + } + + // got deleted? We might be done, if it didn't exist before + if( backupOfFile.isEmpty() ) + return true; + + QFile backupF( backupOfFile ); + const bool success = backupF.rename( filename ); + if(!success) + setError( UserDefinedError, tr("Cannot restore backup file for %1: %2").arg(filename, backupF.errorString()) ); + + return success; +} + +bool PrependFileOperation::testOperation() +{ + // TODO + return true; +} + +PrependFileOperation* PrependFileOperation::clone() const +{ + return new PrependFileOperation; +} + + +//////////////////////////////////////////////////////////////////////////// +// KDUpdater::ExecuteOperation +//////////////////////////////////////////////////////////////////////////// + +ExecuteOperation::ExecuteOperation() + : QObject() +{ + setName(QLatin1String( "Execute" )); +} + +ExecuteOperation::~ExecuteOperation() +{ + +} + +void ExecuteOperation::backup() +{ + // this is not possible, since the process can do whatever... +} + +#if defined( SUPPORT_DETACHED_PROCESS_EXECUTION ) && defined( Q_WS_WIN ) +// stolen from qprocess_win.cpp +static QString qt_create_commandline(const QString &program, const QStringList &arguments) +{ + QString args; + if (!program.isEmpty()) { + QString programName = program; + if (!programName.startsWith(QLatin1Char('\"')) && !programName.endsWith(QLatin1Char('\"')) && programName.contains(QLatin1Char(' '))) + programName = QLatin1Char('\"') + programName + QLatin1Char('\"'); + programName.replace(QLatin1Char('/'), QLatin1Char('\\')); + + // add the prgram as the first arg ... it works better + args = programName + QLatin1Char(' '); + } + + for (int i=0; i<arguments.size(); ++i) { + QString tmp = arguments.at(i); + // in the case of \" already being in the string the \ must also be escaped + tmp.replace( QLatin1String("\\\""), QLatin1String("\\\\\"") ); + // escape a single " because the arguments will be parsed + tmp.replace( QLatin1Char('\"'), QLatin1String("\\\"") ); + if (tmp.isEmpty() || tmp.contains(QLatin1Char(' ')) || tmp.contains(QLatin1Char('\t'))) { + // The argument must not end with a \ since this would be interpreted + // as escaping the quote -- rather put the \ behind the quote: e.g. + // rather use "foo"\ than "foo\" + QString endQuote(QLatin1Char('\"')); + int i = tmp.length(); + while (i>0 && tmp.at(i-1) == QLatin1Char('\\')) { + --i; + endQuote += QLatin1Char('\\'); + } + args += QLatin1String(" \"") + tmp.left(i) + endQuote; + } else { + args += QLatin1Char(' ') + tmp; + } + } + return args; +} +#endif + +bool ExecuteOperation::performOperation() +{ + // This operation receives only one argument. It is the complete + // command line of the external program to execute. + QStringList args = this->arguments(); + if( args.isEmpty() ) + { + setError( InvalidArguments ); + setErrorString( tr("Invalid arguments: %1 arguments given, 2 expected.").arg( args.count() ) ); + return false; + } + + QList< int > allowedExitCodes; + + QRegExp re( QLatin1String( "^\\{((-?\\d+,)*-?\\d+)\\}$" ) ); + if( re.exactMatch( args.first() ) ) + { + const QStringList numbers = re.cap( 1 ).split( QLatin1Char( ',' ) ); + for( QStringList::const_iterator it = numbers.begin(); it != numbers.end(); ++it ) + allowedExitCodes.push_back( it->toInt() ); + args.pop_front(); + } + else + { + allowedExitCodes.push_back( 0 ); + } + + bool success = false; +#ifdef SUPPORT_DETACHED_PROCESS_EXECUTION + // unix style: when there's an ampersand after the command, it's started detached + if( args.count() >= 2 && args.last() == QLatin1String( "&" ) ) + { + args.pop_back(); +#ifdef Q_WS_WIN + QString arguments = qt_create_commandline( args.front(), args.mid( 1 ) ); + + PROCESS_INFORMATION pinfo; + + STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0, + static_cast< ulong >( CW_USEDEFAULT ), static_cast< ulong >( CW_USEDEFAULT ), + static_cast< ulong >( CW_USEDEFAULT ), static_cast< ulong >( CW_USEDEFAULT ), + 0, 0, 0, STARTF_USESHOWWINDOW, SW_HIDE, 0, 0, 0, 0, 0 + }; + success = CreateProcess( 0, const_cast< wchar_t* >( static_cast< const wchar_t* >( arguments.utf16() ) ), + 0, 0, FALSE, CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE, 0, + 0, + &startupInfo, &pinfo ); + +#else + success = QProcess::startDetached( args.front(), args.mid( 1 ) ); +#endif + } + else +#endif + { + Environment::instance()->applyTo( &process ); //apply non-persistent variables + process.start( args.front(), args.mid( 1 ) ); + + QEventLoop loop; + QObject::connect( &process, SIGNAL( finished( int, QProcess::ExitStatus ) ), &loop, SLOT( quit() ) ); + QObject::connect( &process, SIGNAL(readyRead()), this, SLOT(readProcessOutput())); + success = process.waitForStarted( -1 ); + if( success ) + { + loop.exec(); + setValue( QLatin1String( "ExitCode" ), process.exitCode() ); + success = allowedExitCodes.contains( process.exitCode() ); + } + } + if(!success) + { + setError( UserDefinedError ); + setErrorString( tr("Execution failed: \"%1\"").arg( args.join( QLatin1String( " " ) ) ) ); + } + + return success; +} + +/*! + Cancels the ExecuteOperation. This methods tries to terminate the process + gracefully by calling QProcess::terminate. After 10 seconds, the process gets killed. + */ +void ExecuteOperation::cancelOperation() +{ + if( process.state() == QProcess::Running ) + process.terminate(); + if( !process.waitForFinished( 10000 ) ) + process.kill(); +} + +void ExecuteOperation::readProcessOutput() +{ + QByteArray output = process.readAll(); + if (!output.isEmpty()) + emit outputTextChanged(QString::fromLocal8Bit(output)); +} + +bool ExecuteOperation::undoOperation() +{ + // this is not possible, since the process can do whatever... + return false; +} + +bool ExecuteOperation::testOperation() +{ + // TODO + return true; +} + +ExecuteOperation* ExecuteOperation::clone() const +{ + return new ExecuteOperation; +} + + +//////////////////////////////////////////////////////////////////////////// +// KDUpdater::UpdatePackageOperation +//////////////////////////////////////////////////////////////////////////// + +UpdatePackageOperation::UpdatePackageOperation() +{ + setName(QLatin1String( "UpdatePackage" )); +} + +UpdatePackageOperation::~UpdatePackageOperation() +{ + +} + +void UpdatePackageOperation::backup() +{ + const PackageInfo info = application()->packagesInfo()->packageInfo( application()->packagesInfo()->findPackageInfo( arguments().first() ) ); + setValue( QLatin1String( "oldVersion" ), info.version ); + setValue( QLatin1String( "oldDate" ), info.lastUpdateDate ); +} + +bool UpdatePackageOperation::performOperation() +{ + // This operation receives three arguments : the name of the package + // the new version and the release date + const QStringList args = this->arguments(); + if( args.count() != 3 ) + { + setError( InvalidArguments, tr("Invalid arguments: %1 arguments given, 3 expected.").arg( args.count() ) ); + return false; + } + + const QString& packageName = args.at( 0 ); + const QString& version = args.at( 1 ); + const QDate date = QDate::fromString( args.at( 2 ) ); + const bool success = application()->packagesInfo()->updatePackage( packageName, version, date ); + if(!success) + setError( UserDefinedError, tr("Cannot update %1-%2").arg( packageName, version ) ); + + return success; +} + +bool UpdatePackageOperation::undoOperation() +{ + const QString packageName = arguments().first(); + const QString version = arguments().at( 1 ); + const QString oldVersion = value( QLatin1String( "oldVersion" ) ).toString(); + const QDate oldDate = value( QLatin1String( "oldDate" ) ).toDate(); + const bool success = application()->packagesInfo()->updatePackage( packageName, oldVersion, oldDate ); + if(!success) + setError( UserDefinedError, tr("Cannot restore %1-%2").arg( packageName, version ) ); + + return success; +} + +bool UpdatePackageOperation::testOperation() +{ + // TODO + return true; +} + +UpdatePackageOperation* UpdatePackageOperation::clone() const +{ + return new UpdatePackageOperation; +} + + +//////////////////////////////////////////////////////////////////////////// +// KDUpdater::UpdateCompatOperation +//////////////////////////////////////////////////////////////////////////// + +UpdateCompatOperation::UpdateCompatOperation() +{ + setName(QLatin1String( "UpdateCompatLevel" )); +} + +UpdateCompatOperation::~UpdateCompatOperation() +{ + +} + +void UpdateCompatOperation::backup() +{ + setValue( QLatin1String( "oldCompatLevel" ), application()->packagesInfo()->compatLevel() ); +} + +bool UpdateCompatOperation::performOperation() +{ + // This operation receives one argument : the new compat level + const QStringList args = this->arguments(); + if( args.count() != 1 ) + { + setError( InvalidArguments, tr("Invalid arguments: %1 arguments given, 1 expected.").arg( args.count() ) ); + return false; + } + + const int level = args.first().toInt(); + application()->packagesInfo()->setCompatLevel( level ); + return true; +} + +bool UpdateCompatOperation::undoOperation() +{ + if( !hasValue( QLatin1String( "oldCompatLevel" ) ) ) + { + setError( UserDefinedError, tr("Cannot restore previous compat-level") ); + return false; + } + + application()->packagesInfo()->setCompatLevel( value( QLatin1String( "oldCompatLevel" ) ).toInt() ); + return true; +} + +bool UpdateCompatOperation::testOperation() +{ + // TODO + return true; +} + +UpdateCompatOperation* UpdateCompatOperation::clone() const +{ + return new UpdateCompatOperation; +} diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdateoperations.h b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdateoperations.h new file mode 100644 index 000000000..4f1a2563e --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdateoperations.h @@ -0,0 +1,184 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KD_UPDATER_UPDATE_OPERATIONS_H +#define KD_UPDATER_UPDATE_OPERATIONS_H + +#include "kdupdaterupdateoperation.h" + +#include <QDir> +#include <QObject> +#include <QProcess> + +namespace KDUpdater +{ + + class KDTOOLS_UPDATER_EXPORT CopyOperation : public UpdateOperation + { + public: + CopyOperation(); + ~CopyOperation(); + + void backup(); + bool performOperation(); + bool undoOperation(); + bool testOperation(); + CopyOperation* clone() const; + + QDomDocument toXml() const; + }; + + class KDTOOLS_UPDATER_EXPORT MoveOperation : public UpdateOperation + { + public: + MoveOperation(); + ~MoveOperation(); + + void backup(); + bool performOperation(); + bool undoOperation(); + bool testOperation(); + MoveOperation* clone() const; + }; + + class KDTOOLS_UPDATER_EXPORT DeleteOperation : public UpdateOperation + { + public: + DeleteOperation(); + ~DeleteOperation(); + + void backup(); + bool performOperation(); + bool undoOperation(); + bool testOperation(); + DeleteOperation* clone() const; + + QDomDocument toXml() const; + }; + + class KDTOOLS_UPDATER_EXPORT MkdirOperation : public UpdateOperation + { + public: + MkdirOperation(); + ~MkdirOperation(); + + void backup(); + bool performOperation(); + bool undoOperation(); + bool testOperation(); + MkdirOperation* clone() const; + }; + + class KDTOOLS_UPDATER_EXPORT RmdirOperation : public UpdateOperation + { + public: + RmdirOperation(); + ~RmdirOperation(); + + void backup(); + bool performOperation(); + bool undoOperation(); + bool testOperation(); + RmdirOperation* clone() const; + }; + + class KDTOOLS_UPDATER_EXPORT AppendFileOperation : public UpdateOperation + { + public: + AppendFileOperation(); + ~AppendFileOperation(); + + void backup(); + bool performOperation(); + bool undoOperation(); + bool testOperation(); + AppendFileOperation* clone() const; + }; + + class KDTOOLS_UPDATER_EXPORT PrependFileOperation : public UpdateOperation + { + public: + PrependFileOperation(); + ~PrependFileOperation(); + + void backup(); + bool performOperation(); + bool undoOperation(); + bool testOperation(); + PrependFileOperation* clone() const; + }; + + class KDTOOLS_UPDATER_EXPORT ExecuteOperation : public QObject, public UpdateOperation + { + Q_OBJECT + public: + ExecuteOperation(); + ~ExecuteOperation(); + + void backup(); + bool performOperation(); + bool undoOperation(); + bool testOperation(); + ExecuteOperation* clone() const; + + public Q_SLOTS: + void cancelOperation(); + + private Q_SLOTS: + void readProcessOutput(); + + Q_SIGNALS: + void outputTextChanged(const QString &text); + + private: + QProcess process; + }; + + class KDTOOLS_UPDATER_EXPORT UpdatePackageOperation : public UpdateOperation + { + public: + UpdatePackageOperation(); + ~UpdatePackageOperation(); + + void backup(); + bool performOperation(); + bool undoOperation(); + bool testOperation(); + UpdatePackageOperation* clone() const; + }; + + class KDTOOLS_UPDATER_EXPORT UpdateCompatOperation : public UpdateOperation + { + public: + UpdateCompatOperation(); + ~UpdateCompatOperation(); + + void backup(); + bool performOperation(); + bool undoOperation(); + bool testOperation(); + UpdateCompatOperation* clone() const; + }; + +}; + +#endif diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatesdialog.cpp b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatesdialog.cpp new file mode 100644 index 000000000..2b8d54a6b --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatesdialog.cpp @@ -0,0 +1,305 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "kdupdaterupdatesdialog.h" +#include "kdupdaterpackagesinfo.h" +#include "kdupdaterupdate.h" +#include "kdupdaterapplication.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QDir> +#include <QtCore/QHash> +#include <QtCore/QSet> + +#if defined( KDUPDATERGUIWEBVIEW ) +#include <QtWebKit/QWebView> +#elif defined( KDUPDATERGUITEXTBROWSER ) +#include <QtGui/QTextBrowser> +#endif + +#include "ui_updatesdialog.h" + +/*! + \ingroup kdupdater + \class KDUpdater::UpdatesDialog kdupdaterupdatesdialog.h KDUpdaterUpdatesDialog + \brief A dialog that let the user chooses which updates he wants to install + + After \ref KDUpdater::UpdateFinder class finds all updates available for an application, + this dialog can be used to help the user select which update he wants to install. + + Usage: + \code + QList<KDUpdater::Update*> updates = updateFinder.updates(); + + KDUpdater::UpdatesDialog updatesDialog(this); + updatesDialog.setUpdates(updates); + + if( updatesDialog.exec() != QDialog::Accepted ) + { + qDeleteAll(updates); + updates.clear(); + return; + } + + QList<KDUpdater::Update*> reqUpdates; + for(int i=0; i<updates.count(); i++) + { + if( !updatesDialog.isUpdateAllowed(updates[i]) ) + continue; + reqUpdates.append(updates[i]); + } + \endcode +*/ + +class KDUpdater::UpdatesDialog::Private +{ + Q_DECLARE_TR_FUNCTIONS(KDUpdater::Private) + +public: + explicit Private( UpdatesDialog* qq ) : + q( qq ) + {} + + UpdatesDialog* q; + + Ui::UpdatesDialog ui; + + int currentUpdate; + QList<Update*> updates; + QSet<const Update*> status; + + void setCurrentUpdate(int index); + + QString packageDescription( Update* update ); + QString compatDescription( Update* update ); + void slotStateChanged(); + void slotPreviousClicked(); + void slotNextClicked(); +}; + +/*! + Constructor. +*/ +KDUpdater::UpdatesDialog::UpdatesDialog(QWidget *parent) + : QDialog(parent), + d( new Private( this ) ) +{ + d->ui.setupUi(this); + d->currentUpdate = -1; + + connect(d->ui.packageUpdateCheckBox, SIGNAL(stateChanged(int)), + this, SLOT(slotStateChanged())); + connect(d->ui.nextPackageButton, SIGNAL(clicked()), + this, SLOT(slotNextClicked())); + connect(d->ui.previousPackageButton, SIGNAL(clicked()), + this, SLOT(slotPreviousClicked())); +} + + +/*! + Destructor. +*/ +KDUpdater::UpdatesDialog::~UpdatesDialog() +{ + delete d; +} + + +/*! + Sets the list of updates available to the user. +*/ +void KDUpdater::UpdatesDialog::setUpdates(const QList<Update*> &updates) +{ + d->updates = updates; + d->status.clear(); + + d->ui.packageSwitchBar->setVisible( d->updates.size()>1 ); + + if (d->updates.isEmpty()) { + d->ui.descriptionLabel->setText(tr("<b>No update available...</b>")); + d->ui.descriptionLabel->setFixedSize(d->ui.descriptionLabel->sizeHint()); + d->ui.releaseNotesGroup->hide(); + d->ui.pixmapLabel->hide(); + } else if (d->updates.size()==1) { + //Only one update, so pre-accept it. + //OK/Cancel will do from the user POV + d->status.insert( d->updates.front() ); + } + + d->ui.totalPackageLabel->setText(QString::number(d->updates.size())); + d->setCurrentUpdate(0); +} + + +/*! + returns the list of updates available to the user. +*/ +QList<KDUpdater::Update*> KDUpdater::UpdatesDialog::updates() const +{ + return d->updates; +} + + +/*! + Returns true if the update needs to be installed. +*/ +bool KDUpdater::UpdatesDialog::isUpdateAllowed(const KDUpdater::Update *update) const +{ + return d->status.contains( update ); +} + +void KDUpdater::UpdatesDialog::Private::slotStateChanged() +{ + if (currentUpdate<0 || currentUpdate>=updates.size()) { + return; + } + + if ( ui.packageUpdateCheckBox->isChecked() ) + status.insert( updates[currentUpdate] ); + else + status.remove( updates[currentUpdate] ); +} + +void KDUpdater::UpdatesDialog::Private::slotPreviousClicked() +{ + setCurrentUpdate(currentUpdate-1); +} + +void KDUpdater::UpdatesDialog::Private::slotNextClicked() +{ + setCurrentUpdate(currentUpdate+1); +} + +void KDUpdater::UpdatesDialog::Private::setCurrentUpdate(int index) +{ + if (updates.isEmpty()) { + if (currentUpdate == -1) + return; + + currentUpdate = -1; + return; + } + + if (index<0 || index>=updates.size()) { + return; + } + + currentUpdate = index; + + KDUpdater::Update *update = updates.at( index ); + + QString description; + + switch ( update->type() ) { + case PackageUpdate: + case NewPackage: + description = packageDescription( update ); + break; + case CompatUpdate: + description = compatDescription( update ); + break; + default: + description = tr( "<unkown>" ); + } + + ui.descriptionLabel->setText(description); + ui.descriptionLabel->setMinimumHeight(ui.descriptionLabel->heightForWidth(400)); + + ui.packageUpdateCheckBox->setChecked( status.contains( update ) ); + + ui.currentPackageLabel->setText(QString::number(index+1)); + ui.nextPackageButton->setEnabled( index!=(updates.size()-1) ); + ui.previousPackageButton->setEnabled( index!=0 ); + + QDir appdir(update->application()->applicationDirectory()); + if (update->data( QLatin1String( "ReleaseNotes" ) ).isValid()) { + ui.releaseNotesGroup->show(); +#if defined( KDUPDATERGUIWEBVIEW ) + ui.releaseNotesView->setUrl( update->data( QLatin1String( "ReleaseNotes" ) ).toUrl() ); +#elif defined( KDUPDATERGUITEXTBROWSER ) + ui.releaseNotesView->setSource( update->data( QLatin1String( "ReleaseNotes" ) ).toUrl()); +#endif + } + else { + ui.releaseNotesGroup->hide(); + } +} + + +QString KDUpdater::UpdatesDialog::Private::packageDescription( KDUpdater::Update* update ) +{ + KDUpdater::PackagesInfo *packages = update->application()->packagesInfo(); + KDUpdater::PackageInfo info = packages->packageInfo( + packages->findPackageInfo(update->data( QLatin1String( "Name" ) ).toString())); + + QDir appdir(update->application()->applicationDirectory()); + QPixmap pixmap(appdir.filePath(info.pixmap)); + if (!pixmap.isNull()) { + ui.pixmapLabel->setPixmap(pixmap.scaled(96, 96)); + } + + + QString description = tr("<b>A new package update is available for %1!</b><br/><br/>" + "The package %2 %3 is now available -- you have version %4") + .arg(packages->applicationName(), + update->data( QLatin1String( "Name" ) ).toString(), + update->data( QLatin1String( "Version" ) ).toString(), + info.version); + + if (!info.title.isEmpty() || !info.description.isEmpty() ) { + description += QLatin1String( "<br/><br/>" ); + description += tr("<b>Package Details:</b>" ); + if ( !info.title.isEmpty() ) { + description += tr( "<br/><i>Title:</i> %1" ).arg( info.title ); + } + if ( !info.description.isEmpty() ) { + description += tr( "<br/><i>Description:</i> %1" ).arg( info.description ); + } + } + + if ( update->data( QLatin1String( "Description" ) ).isValid() ) { + description += QLatin1String( "<br/><br/>" ); + description += tr( "<b>Update description:</b><br/>%1" ) + .arg( update->data( QLatin1String( "Description" ) ).toString() ); + } + return description; +} + +QString KDUpdater::UpdatesDialog::Private::compatDescription( Update* update ) +{ + KDUpdater::PackagesInfo *packages = update->application()->packagesInfo(); + + QString description = tr("<b>A new compatibility update is available for %1!</b><br/><br/>" + "The compatibility level %2 is now available -- you have level %3") + .arg(packages->applicationName(), + QString::number(update->data( QLatin1String( "CompatLevel" ) ).toInt()), + QString::number(packages->compatLevel())); + + if ( update->data( QLatin1String( "Description" ) ).isValid() ) { + description += QLatin1String( "<br/><br/>" ); + description += tr( "<b>Update description:</b> %1" ) + .arg( update->data( QLatin1String( "Description" ) ).toString() ); + } + return description; +} + +#include "moc_kdupdaterupdatesdialog.cpp" diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatesdialog.h b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatesdialog.h new file mode 100644 index 000000000..85a90a595 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatesdialog.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KD_UPDATER_UPDATES_DIALOG_H +#define KD_UPDATER_UPDATES_DIALOG_H + +#include "kdupdater.h" +#include <QtCore/QList> +#include <QtGui/QDialog> + +namespace KDUpdater +{ + class Update; + + class KDTOOLS_UPDATER_EXPORT UpdatesDialog : public QDialog + { + Q_OBJECT + + public: + explicit UpdatesDialog(QWidget *parent = 0); + ~UpdatesDialog(); + + void setUpdates(const QList<Update*> &updates); + QList<Update*> updates() const; + + bool isUpdateAllowed( const Update * update ) const; + + private: + class Private; + Private * const d; + + Q_PRIVATE_SLOT( d, void slotStateChanged() ) + Q_PRIVATE_SLOT( d, void slotPreviousClicked() ) + Q_PRIVATE_SLOT( d, void slotNextClicked() ) + }; +} + +#endif diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatesinfo.cpp b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatesinfo.cpp new file mode 100644 index 000000000..4e9f60195 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatesinfo.cpp @@ -0,0 +1,372 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "kdupdaterupdatesinfo_p.h" + +#include <QCoreApplication> +#include <QDomDocument> +#include <QDomElement> +#include <QFile> +#include <QSharedData> + +// +// KDUpdater::UpdatesInfo::UpdatesInfoData +// +struct KDUpdater::UpdatesInfo::UpdatesInfoData : public QSharedData +{ + Q_DECLARE_TR_FUNCTIONS(KDUpdater::UpdatesInfoData) + +public: + UpdatesInfoData() : error(UpdatesInfo::NotYetReadError), compatLevel(-1) { } + + QString errorMessage; + UpdatesInfo::Error error; + QString updateXmlFile; + QString applicationName; + QString applicationVersion; + int compatLevel; + QList<KDUpdater::UpdateInfo> updateInfoList; + + void parseFile(const QString& updateXmlFile); + bool parsePackageUpdateElement(const QDomElement & updateE); + bool parseCompatUpdateElement(const QDomElement & updateE); + + void setInvalidContentError( const QString& detail ); +}; + +void KDUpdater::UpdatesInfo::UpdatesInfoData::setInvalidContentError(const QString& detail) +{ + error = UpdatesInfo::InvalidContentError; + errorMessage = tr("Updates.Xml contains invalid content: %1").arg(detail); +} + +void KDUpdater::UpdatesInfo::UpdatesInfoData::parseFile(const QString& updateXmlFile) +{ + QFile file( updateXmlFile ); + if( !file.open(QFile::ReadOnly) ) + { + error = UpdatesInfo::CouldNotReadUpdateInfoFileError; + errorMessage = tr("Could not read \"%1\"").arg(updateXmlFile); + return; + } + + QDomDocument doc; + QString parseErrorMessage; + int parseErrorLine; + int parseErrorColumn; + if( !doc.setContent( &file, &parseErrorMessage, &parseErrorLine, &parseErrorColumn ) ) + { + error = UpdatesInfo::InvalidXmlError; + errorMessage = tr("Parse error in %1 at %2, %3: %4") + .arg( updateXmlFile, + QString::number( parseErrorLine ), + QString::number( parseErrorColumn ), + parseErrorMessage ); + return; + } + + QDomElement rootE = doc.documentElement(); + if( rootE.tagName() != QLatin1String( "Updates" ) ) + { + setInvalidContentError(tr("root element %1 unexpected, should be \"Updates\"").arg(rootE.tagName())); + return; + } + + QDomNodeList childNodes = rootE.childNodes(); + for(int i=0; i<childNodes.count(); i++) + { + QDomNode childNode = childNodes.at(i); + QDomElement childE = childNode.toElement(); + if( childE.isNull() ) + continue; + + if( childE.tagName() == QLatin1String( "ApplicationName" ) ) + applicationName = childE.text(); + else if( childE.tagName() == QLatin1String( "ApplicationVersion" ) ) + applicationVersion = childE.text(); + else if( childE.tagName() == QLatin1String( "RequiredCompatLevel" ) ) + compatLevel = childE.text().toInt(); + else if( childE.tagName() == QLatin1String( "PackageUpdate" ) ) { + const bool res = parsePackageUpdateElement( childE ); + if (!res) { + //error handled in subroutine + return; + } + } else if( childE.tagName() == QLatin1String( "CompatUpdate" ) ) { + const bool res = parseCompatUpdateElement( childE ); + if (!res) { + //error handled in subroutine + return; + } + } + } + + if (applicationName.isEmpty()) + { + setInvalidContentError(tr("ApplicationName element is missing")); + return; + } + + if (applicationVersion.isEmpty()) + { + setInvalidContentError(tr("ApplicationVersion element is missing")); + return; + } + + error = UpdatesInfo::NoError; + errorMessage.clear(); +} + +bool KDUpdater::UpdatesInfo::UpdatesInfoData::parsePackageUpdateElement(const QDomElement & updateE) +{ + if( updateE.isNull() ) + return false; + + KDUpdater::UpdateInfo info; + info.type = KDUpdater::PackageUpdate; + + QDomNodeList childNodes = updateE.childNodes(); + for(int i=0; i<childNodes.count(); i++) + { + QDomNode childNode = childNodes.at(i); + QDomElement childE = childNode.toElement(); + if( childE.isNull() ) + continue; + + if( childE.tagName() == QLatin1String( "ReleaseNotes" ) ) { + info.data[childE.tagName()] = QUrl(childE.text()); + } + else if( childE.tagName() == QLatin1String( "UpdateFile" ) ) + { + KDUpdater::UpdateFileInfo ufInfo; + ufInfo.arch = childE.attribute(QLatin1String( "Arch" ), QLatin1String( "i386" )); + ufInfo.os = childE.attribute(QLatin1String( "OS" )); + ufInfo.compressedSize = childE.attribute( QLatin1String( "CompressedSize" ) ).toLongLong(); + ufInfo.uncompressedSize = childE.attribute( QLatin1String( "UncompressedSize" ) ).toLongLong(); + ufInfo.sha1sum = QByteArray::fromHex( childE.attribute( QLatin1String( "sha1sum" ) ).toAscii() ); + ufInfo.fileName = childE.text(); + info.updateFiles.append(ufInfo); + } + else if (childE.tagName() == QLatin1String("Licenses")) { + QHash<QString, QVariant> licenseHash; + const QDomNodeList licenseNodes = childE.childNodes(); + for (int i = 0; i < licenseNodes.count(); ++i) { + const QDomNode licenseNode = licenseNodes.at(i); + if (licenseNode.nodeName() == QLatin1String("License")) { + QDomElement element = licenseNode.toElement(); + licenseHash.insert(element.attributeNode(QLatin1String("name")).value(), + element.attributeNode(QLatin1String("file")).value()); + } + } + if (!licenseHash.isEmpty()) + info.data.insert(QLatin1String("Licenses"), licenseHash); + } + else { + info.data[childE.tagName()] = childE.text(); + } + } + + if (!info.data.contains( QLatin1String( "Name" ) )) + { + setInvalidContentError(tr("PackageUpdate element without Name")); + return false; + } + else if (!info.data.contains( QLatin1String( "Version" ) )) + { + setInvalidContentError(tr("PackageUpdate element without Version")); + return false; + } + else if (!info.data.contains( QLatin1String( "ReleaseDate" ) )) + { + setInvalidContentError(tr("PackageUpdate element without ReleaseDate")); + return false; + } + else if (info.updateFiles.isEmpty()) + { + setInvalidContentError(tr("PackageUpdate element without UpdateFile")); + return false; + } + + updateInfoList.append(info); + return true; +} + +bool KDUpdater::UpdatesInfo::UpdatesInfoData::parseCompatUpdateElement(const QDomElement & updateE) +{ + if( updateE.isNull() ) + return false; + + KDUpdater::UpdateInfo info; + info.type = KDUpdater::CompatUpdate; + + QDomNodeList childNodes = updateE.childNodes(); + for(int i=0; i<childNodes.count(); i++) + { + QDomNode childNode = childNodes.at(i); + QDomElement childE = childNode.toElement(); + if( childE.isNull() ) + continue; + + if( childE.tagName() == QLatin1String( "ReleaseNotes" ) ) { + info.data[childE.tagName()] = QUrl(childE.text()); + } + else if( childE.tagName() == QLatin1String( "UpdateFile" ) ) + { + KDUpdater::UpdateFileInfo ufInfo; + ufInfo.arch = childE.attribute(QLatin1String( "Arch" ), QLatin1String( "i386" )); + ufInfo.os = childE.attribute(QLatin1String( "OS" )); + ufInfo.fileName = childE.text(); + info.updateFiles.append(ufInfo); + } + else { + info.data[childE.tagName()] = childE.text(); + } + } + + if (!info.data.contains( QLatin1String( "CompatLevel" ) )) + { + setInvalidContentError(tr("CompatUpdate element without CompatLevel")); + return false; + } + + if (!info.data.contains( QLatin1String( "ReleaseDate" ) )) + { + setInvalidContentError(tr("CompatUpdate element without ReleaseDate")); + return false; + } + + if (info.updateFiles.isEmpty()) + { + setInvalidContentError(tr("CompatUpdate element without UpdateFile")); + return false; + } + + updateInfoList.append(info); + return true; +} + + +// +// KDUpdater::UpdatesInfo +// +KDUpdater::UpdatesInfo::UpdatesInfo() + : d(new KDUpdater::UpdatesInfo::UpdatesInfoData) +{ +} + +KDUpdater::UpdatesInfo::~UpdatesInfo() +{ +} + +bool KDUpdater::UpdatesInfo::isValid() const +{ + return d->error == NoError; +} + +QString KDUpdater::UpdatesInfo::errorString() const +{ + return d->errorMessage; +} + +void KDUpdater::UpdatesInfo::setFileName(const QString& updateXmlFile) +{ + if( d->updateXmlFile == updateXmlFile ) + return; + + d->applicationName.clear(); + d->applicationVersion.clear(); + d->updateInfoList.clear(); + + d->updateXmlFile = updateXmlFile; + d->parseFile( d->updateXmlFile ); +} + +QString KDUpdater::UpdatesInfo::fileName() const +{ + return d->updateXmlFile; +} + +QString KDUpdater::UpdatesInfo::applicationName() const +{ + return d->applicationName; +} + +QString KDUpdater::UpdatesInfo::applicationVersion() const +{ + return d->applicationVersion; +} + +int KDUpdater::UpdatesInfo::compatLevel() const +{ + return d->compatLevel; +} + +int KDUpdater::UpdatesInfo::updateInfoCount( int type) const +{ + if ( type == KDUpdater::AllUpdate ) { + return d->updateInfoList.count(); + } + int count = 0; + for ( int i=0; i<d->updateInfoList.count(); ++i ) { + if ( d->updateInfoList.at( i ).type == type ) + ++count; + } + return count; +} + +KDUpdater::UpdateInfo KDUpdater::UpdatesInfo::updateInfo(int index) const +{ + if(index < 0 || index >= d->updateInfoList.count()) + return KDUpdater::UpdateInfo(); + + return d->updateInfoList.at( index ); +} + +QList<KDUpdater::UpdateInfo> KDUpdater::UpdatesInfo::updatesInfo( int type, int compatLevel ) const +{ + QList<KDUpdater::UpdateInfo> list; + if ( compatLevel == -1 ) { + if ( type == KDUpdater::AllUpdate ) { + return d->updateInfoList; + } + for ( int i=0; i<d->updateInfoList.count(); ++i ) { + if ( d->updateInfoList.at( i ).type == type ) + list.append( d->updateInfoList.at( i ) ); + } + } + else { + for ( int i=0; i<d->updateInfoList.count(); ++i ) { + UpdateInfo updateInfo = d->updateInfoList.at( i ); + if ( updateInfo.type == type ) { + if ( updateInfo.type == CompatUpdate ) { + if ( updateInfo.data.value( QLatin1String( "CompatLevel" ) ) == compatLevel ) + list.append( updateInfo ); + } + else { + if ( updateInfo.data.value( QLatin1String( "RequiredCompatLevel" ) ) == compatLevel ) + list.append( updateInfo ); + } + } + } + } + return list; +} diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatesinfo_p.h b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatesinfo_p.h new file mode 100644 index 000000000..4179e3d8d --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatesinfo_p.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KD_UPDATER_UPDATE_PACKAGES_INFO_H +#define KD_UPDATER_UPDATE_PACKAGES_INFO_H + +#include "kdupdater.h" +#include <QSharedDataPointer> +#include <QString> +#include <QDate> +#include <QList> +#include <QStringList> +#include <QUrl> +#include <QMap> +#include <QVariant> + +// Classes and structures in this header file are for internal use only. +// They are not a part of the public API + +namespace KDUpdater +{ + struct UpdateFileInfo + { + UpdateFileInfo() + : compressedSize( 0 ), + uncompressedSize( 0 ) + { + } + QString arch; + QString os; + QString fileName; + QByteArray sha1sum; + quint64 compressedSize; + quint64 uncompressedSize; + }; + + struct UpdateInfo + { + int type; + QMap<QString, QVariant> data; + QList<UpdateFileInfo> updateFiles; + }; + + class UpdatesInfo + { + public: + enum Error + { + NoError=0, + NotYetReadError, + CouldNotReadUpdateInfoFileError, + InvalidXmlError, + InvalidContentError + }; + + UpdatesInfo(); + ~UpdatesInfo(); + + bool isValid() const; + QString errorString() const; + Error error() const; + + void setFileName(const QString& updateXmlFile); + QString fileName() const; + + QString applicationName() const; + QString applicationVersion() const; + int compatLevel() const; + + int updateInfoCount( int type=AllUpdate ) const; + UpdateInfo updateInfo(int index) const; + QList<UpdateInfo> updatesInfo( int type=AllUpdate, int compatLevel=-1 ) const; + + private: + struct UpdatesInfoData; + QSharedDataPointer<UpdatesInfoData> d; + }; +}; + +#endif diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatesourcesinfo.cpp b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatesourcesinfo.cpp new file mode 100644 index 000000000..14a765a5b --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatesourcesinfo.cpp @@ -0,0 +1,511 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "kdupdaterupdatesourcesinfo.h" +#include "kdupdaterapplication.h" + +#include <QDomElement> +#include <QDomDocument> +#include <QDomText> +#include <QDomCDATASection> +#include <QFileInfo> +#include <QFile> +#include <QTextStream> + + +/*! + \ingroup kdupdater + \class KDUpdater::UpdateSourcesInfo kdupdaterupdatesourcesinfo.h KDUpdaterUpdateSourcesInfo + \brief Provides access to information about the update sources set for the application. + + An update source is a repository that contains updates applicable for the application. + Applications can download updates from the update source and install them locally. + + Each application can have one or more update sources from which it can download updates. + Information about update source is stored in a file called UpdateSources.xml. This class helps + access and modify the UpdateSources.xml file. + + The complete file name of the UpdateSources.xml file can be specified via the \ref setFileName() + method. The class then parses the XML file and makes available information contained in + that XML file through an easy to use API. You can + + \li Get update sources information via the \ref updateSourceInfoCount() and \ref updateSourceInfo() + methods. + \li You can add/remove/change update source information via the \ref addUpdateSourceInfo(), + \ref removeUpdateSource(), \ref setUpdateSourceAt() methods. + + The class emits appropriate signals to inform listeners about changes in the update application. +*/ + +/*! \enum UpdateSourcesInfo::Error + * Error codes related to retrieving update sources + */ + +/*! \var UpdateSourcesInfo::Error UpdateSourcesInfo::NoError + * No error occurred + */ + +/*! \var UpdateSourcesInfo::Error UpdateSourcesInfo::NotYetReadError + * The package information was not parsed yet from the XML file + */ + +/*! \var UpdateSourcesInfo::Error UpdateSourcesInfo::CouldNotReadSourceFileError + * the specified update source file could not be read (does not exist or not readable) + */ + +/*! \var UpdateSourcesInfo::Error UpdateSourcesInfo::InvalidXmlError + * The source file contains invalid XML. + */ + +/*! \var UpdateSourcesInfo::Error UpdateSourcesInfo::InvalidContentError + * The source file contains valid XML, but does not match the expected format for source descriptions + */ + +/*! \var UpdateSourcesInfo::Error UpdateSourcesInfo::CouldNotSaveChangesError + * Changes made to the object could be saved back to the source file + */ + +namespace KDUpdater +{ +struct UpdateSourceInfoPriorityHigherThan +{ + bool operator()( const UpdateSourceInfo& lhs, const UpdateSourceInfo& rhs ) const + { + return lhs.priority > rhs.priority; + } +}; +} + +struct KDUpdater::UpdateSourcesInfo::UpdateSourcesInfoData +{ + UpdateSourcesInfoData( UpdateSourcesInfo* qq ) : + q( qq ), + error(UpdateSourcesInfo::NotYetReadError), + application(0), + modified(false) + {} + + UpdateSourcesInfo* q; + + QString errorMessage; + UpdateSourcesInfo::Error error; + KDUpdater::Application* application; + bool modified; + QString fileName; + QList<KDUpdater::UpdateSourceInfo> updateSourceInfoList; + + void addUpdateSourceFrom( const QDomElement & element ); + void addChildElement( QDomDocument & doc, QDomElement & parentE, const QString & tagName, const QString & text, bool htmlText=false ); + void setInvalidContentError( const QString& detail ); + void clearError(); + void saveChanges(); +}; + +void KDUpdater::UpdateSourcesInfo::UpdateSourcesInfoData::setInvalidContentError(const QString& detail) +{ + error = UpdateSourcesInfo::InvalidContentError; + errorMessage = tr("%1 contains invalid content: %2").arg(fileName, detail); +} + +void KDUpdater::UpdateSourcesInfo::UpdateSourcesInfoData::clearError() +{ + error = UpdateSourcesInfo::NoError; + errorMessage.clear(); +} + +/*! + \internal +*/ +KDUpdater::UpdateSourcesInfo::UpdateSourcesInfo(Application* application) + : QObject(application), + d( new KDUpdater::UpdateSourcesInfo::UpdateSourcesInfoData( this ) ) +{ + d->application = application; +} + +/*! + \internal +*/ +KDUpdater::UpdateSourcesInfo::~UpdateSourcesInfo() +{ + d->saveChanges(); + delete d; +} + +/*! + Returns a pointer to the update application for which this class manages update sources. +*/ +KDUpdater::Application* KDUpdater::UpdateSourcesInfo::application() const +{ + return d->application; +} + +/*! + \internal +*/ +bool KDUpdater::UpdateSourcesInfo::isValid() const +{ + return d->error == NoError; +} + +/*! + returns a human-readable description of the error + */ +QString KDUpdater::UpdateSourcesInfo::errorString() const +{ + return d->errorMessage; +} + +/*! + returns the last error + */ +KDUpdater::UpdateSourcesInfo::Error KDUpdater::UpdateSourcesInfo::error() const +{ + return d->error; +} + +bool KDUpdater::UpdateSourcesInfo::isModified() const +{ + return d->modified; +} + +void KDUpdater::UpdateSourcesInfo::setModified(bool modified) +{ + d->modified = modified; +} + +/*! + Sets the complete file name of the UpdateSources.xml file. The function also issues a call + to refresh() to reload package information from the XML file. + + \sa KDUpdater::Application::setUpdateSourcesXMLFileName() +*/ +void KDUpdater::UpdateSourcesInfo::setFileName(const QString& fileName) +{ + if( d->fileName == fileName ) + return; + + d->fileName = fileName; + refresh(); // load new file +} + +/*! + Returns the name of the UpdateSources.xml file that this class referred to. +*/ +QString KDUpdater::UpdateSourcesInfo::fileName() const +{ + return d->fileName; +} + +/*! + Returns the number of update source info structures contained in this class. +*/ +int KDUpdater::UpdateSourcesInfo::updateSourceInfoCount() const +{ + return d->updateSourceInfoList.count(); +} + +/*! + Returns the update source info structure at \c index. If an invalid index is passed + the function returns a dummy constructor. +*/ +KDUpdater::UpdateSourceInfo KDUpdater::UpdateSourcesInfo::updateSourceInfo(int index) const +{ + if( index < 0 || index >= d->updateSourceInfoList.count() ) + return KDUpdater::UpdateSourceInfo(); + + return d->updateSourceInfoList[index]; +} + +/*! + Adds an update source info to this class. Upon successful addition, the class emits a + \ref updateSourceInfoAdded() signal. +*/ +void KDUpdater::UpdateSourcesInfo::addUpdateSourceInfo(const KDUpdater::UpdateSourceInfo& info) +{ + if( d->updateSourceInfoList.contains( info ) ) + return; + d->updateSourceInfoList.push_back( info ); + qSort( d->updateSourceInfoList.begin(), d->updateSourceInfoList.end(), KDUpdater::UpdateSourceInfoPriorityHigherThan() ); + emit updateSourceInfoAdded(info); + d->modified = true; +} + +/*! + Removes an update source info from this class. Upon successful removal, the class emits a + \ref updateSourceInfoRemoved() signal. +*/ +void KDUpdater::UpdateSourcesInfo::removeUpdateSourceInfo(const KDUpdater::UpdateSourceInfo& info) +{ + if( !d->updateSourceInfoList.contains(info) ) + return; + d->updateSourceInfoList.removeAll(info); + emit updateSourceInfoRemoved(info); + d->modified = true; +} + +/*! + Removes an update source info at \index in this class. Upon successful removal, the class emits a + \ref updateSourceInfoRemoved() signal. +*/ +void KDUpdater::UpdateSourcesInfo::removeUpdateSourceInfoAt(int index) +{ + if( index < 0 || index >= d->updateSourceInfoList.count() ) + return; + KDUpdater::UpdateSourceInfo info = d->updateSourceInfoList[index]; + d->updateSourceInfoList.removeAt(index); + emit updateSourceInfoRemoved(info); + d->modified = true; +} + +/*! + Changes the update source info at \c index to \c info. If \c index is equal to the number of + source info structures in this class (\ref updateSourceInfoCount()) then \c info is appended; + otherwise the existing info at \c index will be changed. + + Depending on what the function does \ref updateSourceInfoAdded() or \ref updateSourceInfoChanged() + signal is emitted. +*/ +void KDUpdater::UpdateSourcesInfo::setUpdateSourceInfoAt(int index, const KDUpdater::UpdateSourceInfo& info) +{ + if( index < 0 || index > d->updateSourceInfoList.count() ) + return; + + if( index == d->updateSourceInfoList.count() ) + { + d->updateSourceInfoList.append(info); + emit updateSourceInfoAdded(info); + } + else + { + KDUpdater::UpdateSourceInfo oldInfo = d->updateSourceInfoList[index]; + if( info == oldInfo ) + return; + + d->updateSourceInfoList[index] = info; + emit updateSourceInfoChanged(info, oldInfo); + } + d->modified = true; +} + +/*! + This slot reloads the update source information from UpdateSources.xml. +*/ +void KDUpdater::UpdateSourcesInfo::refresh() +{ + d->saveChanges(); // save changes done in the previous file + d->updateSourceInfoList.clear(); + + QFile file( d->fileName ); + + // if the file does not exist then we just skip the reading + if( !file.exists() ) + { + d->clearError(); + emit reset(); + return; + } + + // Open the XML file + if( !file.open(QFile::ReadOnly) ) + { + d->errorMessage = tr("Could not read \"%1\"").arg(d->fileName); + d->error = CouldNotReadSourceFileError; + emit reset(); + return; + } + + QDomDocument doc; + QString parseErrorMessage; + int parseErrorLine; + int parseErrorColumn; + if( !doc.setContent( &file, &parseErrorMessage, &parseErrorLine, &parseErrorColumn ) ) + { + d->error = InvalidXmlError; + d->errorMessage = tr("XML Parse error in %1 at %2, %3: %4") + .arg( d->fileName, + QString::number( parseErrorLine ), + QString::number( parseErrorColumn ), + parseErrorMessage ); + emit reset(); + return; + } + + // Now parse the XML file. + QDomElement rootE = doc.documentElement(); + if( rootE.tagName() != QLatin1String( "UpdateSources" ) ) + { + d->setInvalidContentError(tr("Root element %1 unexpected, should be \"UpdateSources\"").arg(rootE.tagName())); + emit reset(); + return; + } + + QDomNodeList childNodes = rootE.childNodes(); + for(int i=0; i<childNodes.count(); i++) + { + QDomNode childNode = childNodes.item(i); + QDomElement childNodeE = childNode.toElement(); + if( childNodeE.isNull() ) + continue; + + if( childNodeE.tagName() == QLatin1String( "UpdateSource" ) ) + d->addUpdateSourceFrom(childNodeE); + } + + d->clearError(); + emit reset(); +} + +void KDUpdater::UpdateSourcesInfo::UpdateSourcesInfoData::saveChanges() +{ + if( !modified || fileName.isEmpty() ) + return; + + const bool hadSaveError = error == UpdateSourcesInfo::CouldNotSaveChangesError; + + QDomDocument doc; + + QDomElement rootE = doc.createElement( QLatin1String( "UpdateSources") ); + doc.appendChild(rootE); + + for(int i=0; i<updateSourceInfoList.count(); i++) + { + KDUpdater::UpdateSourceInfo info = updateSourceInfoList[i]; + + QDomElement infoE = doc.createElement( QLatin1String( "UpdateSource" ) ); + rootE.appendChild(infoE); + addChildElement(doc, infoE, QLatin1String( "Name" ), info.name); + addChildElement(doc, infoE, QLatin1String( "Title" ), info.title); + addChildElement(doc, infoE, QLatin1String( "Description" ), info.description, + (info.description.length() && info.description[0] == QLatin1Char( '<' ) ) ); + addChildElement(doc, infoE, QLatin1String( "Url" ), info.url.toString()); + } + + QFile file( fileName ); + if ( !file.open( QFile::WriteOnly ) ) + { + error = UpdateSourcesInfo::CouldNotSaveChangesError; + errorMessage = tr("Could not save changes to \"%1\": %2").arg(fileName, file.errorString()); + return; + } + + QTextStream stream( &file ); + doc.save( stream, 2 ); + stream.flush(); + file.close(); + + if ( file.error() != QFile::NoError ) + { + error = UpdateSourcesInfo::CouldNotSaveChangesError; + errorMessage = tr("Could not save changes to \"%1\": %2").arg(fileName, file.errorString()); + return; + } + + //if there was a write error before, clear the error, as the write was successful now + if ( hadSaveError ) + clearError(); + + modified = false; +} + +void KDUpdater::UpdateSourcesInfo::UpdateSourcesInfoData::addUpdateSourceFrom( const QDomElement & element ) +{ + if( element.tagName() != QLatin1String( "UpdateSource" ) ) + return; + + QDomNodeList childNodes = element.childNodes(); + if(!childNodes.count()) + return; + + KDUpdater::UpdateSourceInfo info; + + for(int i=0; i<childNodes.count(); i++) + { + QDomNode childNode = childNodes.item(i); + QDomElement childNodeE = childNode.toElement(); + if( childNodeE.isNull() ) + continue; + + if( childNodeE.tagName() == QLatin1String( "Name" ) ) + info.name = childNodeE.text(); + else if( childNodeE.tagName() == QLatin1String( "Title" ) ) + info.title = childNodeE.text(); + else if( childNodeE.tagName() == QLatin1String( "Description" ) ) + info.description = childNodeE.text(); + else if( childNodeE.tagName() == QLatin1String( "Url" ) ) + info.url = childNodeE.text(); + } + + this->updateSourceInfoList.append(info); +} + +void KDUpdater::UpdateSourcesInfo::UpdateSourcesInfoData::addChildElement( QDomDocument & doc, QDomElement & parentE, const QString & tagName, const QString & text, bool htmlText ) +{ + QDomElement childE = doc.createElement(tagName); + parentE.appendChild(childE); + + if( htmlText ) + { + QDomCDATASection textE = doc.createCDATASection(text); + childE.appendChild(textE); + } + else + { + QDomText textE = doc.createTextNode(text); + childE.appendChild(textE); + } +} + +/*! + \ingroup kdupdater + \struct KDUpdater::UpdateSourceInfo kdupdaterupdatesourcesinfo.h KDUpdaterUpdateSourcesInfo + \brief Describes a single update source + + An update source is a repository that contains updates applicable for the application. + This structure describes a single update source in terms of name, title, description, url and priority. +*/ + +/*! + \var QString KDUpdater::UpdateSourceInfo::name +*/ + +/*! + \var QString KDUpdater::UpdateSourceInfo::title +*/ + +/*! + \var QString KDUpdater::UpdateSourceInfo::description +*/ + +/*! + \var QUrl KDUpdater::UpdateSourceInfo::url +*/ + +/*! + \var QUrl KDUpdater::UpdateSourceInfo::priority +*/ + +bool KDUpdater::operator==( const UpdateSourceInfo & lhs, const UpdateSourceInfo & rhs ) +{ + return (lhs.name == rhs.name) && (lhs.title == rhs.title) && + (lhs.description == rhs.description) && (lhs.url == rhs.url); +} diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatesourcesinfo.h b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatesourcesinfo.h new file mode 100644 index 000000000..231002ee0 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatesourcesinfo.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KD_UPDATER_UPDATE_SOURCES_INFO_H +#define KD_UPDATER_UPDATE_SOURCES_INFO_H + +#include "kdupdater.h" + +#include <QObject> +#include <QVariant> +#include <QUrl> + +namespace KDUpdater +{ + class Application; + + struct KDTOOLS_UPDATER_EXPORT UpdateSourceInfo + { + UpdateSourceInfo() : priority(-1) { } + + QString name; + QString title; + QString description; + QUrl url; + int priority; + + }; + + KDTOOLS_UPDATER_EXPORT bool operator==( const UpdateSourceInfo & lhs, const UpdateSourceInfo & rhs ); + inline bool operator!= ( const UpdateSourceInfo & lhs, const UpdateSourceInfo & rhs ) { + return !operator==( lhs, rhs ); + } + + class KDTOOLS_UPDATER_EXPORT UpdateSourcesInfo : public QObject + { + Q_OBJECT + + public: + ~UpdateSourcesInfo(); + + enum Error + { + NoError=0, + NotYetReadError, + CouldNotReadSourceFileError, + InvalidXmlError, + InvalidContentError, + CouldNotSaveChangesError + }; + + Application* application() const; + + bool isValid() const; + QString errorString() const; + Error error() const; + + bool isModified() const; + void setModified(bool modified); + + void setFileName(const QString& fileName); + QString fileName() const; + + int updateSourceInfoCount() const; + UpdateSourceInfo updateSourceInfo(int index) const; + + void addUpdateSourceInfo(const UpdateSourceInfo& info); + void removeUpdateSourceInfo(const UpdateSourceInfo& info); + void removeUpdateSourceInfoAt(int index); + void setUpdateSourceInfoAt(int index, const UpdateSourceInfo& info); + + protected: + explicit UpdateSourcesInfo(Application* application); + + public Q_SLOTS: + void refresh(); + + Q_SIGNALS: + void reset(); + void updateSourceInfoAdded(const UpdateSourceInfo& info); + void updateSourceInfoRemoved(const UpdateSourceInfo& info); + void updateSourceInfoChanged(const UpdateSourceInfo& newInfo, + const UpdateSourceInfo& oldInfo); + + private: + friend class Application; + struct UpdateSourcesInfoData; + UpdateSourcesInfoData* d; + }; +} + +Q_DECLARE_METATYPE(KDUpdater::UpdateSourceInfo) + +#endif diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatesourcesview.cpp b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatesourcesview.cpp new file mode 100644 index 000000000..aec942614 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatesourcesview.cpp @@ -0,0 +1,375 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#include "kdupdaterupdatesourcesview.h" +#include "kdupdaterupdatesourcesinfo.h" +#include "ui_addupdatesourcedialog.h" + +#include <QMessageBox> +#include <QContextMenuEvent> +#include <QAction> +#include <QMenu> + +/*! + \ingroup kdupdater + \class KDUpdater::UpdateSourcesView kdupdaterupdatesourcesview.h KDUpdaterUpdateSourcesView + \brief A widget that helps view and/or edit \ref KDUpdater::UpdateSourcesInfo + + \ref KDUpdater::UpdateSourcesInfo, associated with \ref KDUpdater::Application, contains information + about all the update sources from which the application can download and install updates. + This widget helps view and edit update sources information. + + \image html updatesourcesview.jpg + + The widget provides the following slots for editing update sources information + \ref addNewSource() + \ref editCurrentSource() + \ref removeCurrentSource() + + You can include this widget within another form or dialog and connect to these slots which make + use of an inbuilt dialog box to add/edit update sources. Shown below is a screenshot of the + inbuilt dialog box. + + \image html editupdatesource.jpg + + Alternatively you can also use your own dialog box and directly update \ref KDUpdater::UpdateSourcesInfo. + This widget connects to \ref KDUpdater::UpdateSourcesInfo signals and ensures that the data it displays + is always kept updated. + + The widget provides a context menu using which you can add/remove/edit update sources. Shown below is a + screenshot of the context menu. + + \image html updatesourcesview_contextmenu.jpg +*/ + +struct KDUpdater::UpdateSourcesView::UpdateSourcesViewData +{ + UpdateSourcesViewData( UpdateSourcesView* qq ) : + q( qq ), + updateSourcesInfo(0) + {} + + UpdateSourcesView* q; + UpdateSourcesInfo* updateSourcesInfo; +}; + +/*! + Constructor +*/ +KDUpdater::UpdateSourcesView::UpdateSourcesView(QWidget* parent) + : QTreeWidget(parent), + d(new KDUpdater::UpdateSourcesView::UpdateSourcesViewData( this ) ) +{ + setColumnCount(3); + setHeaderLabels( QStringList() << tr("Name") << tr("Title") << tr("URL") ); + setRootIsDecorated(false); + + connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(editCurrentSource())); +} + +/*! + Destructor +*/ +KDUpdater::UpdateSourcesView::~UpdateSourcesView() +{ + delete d; +} + +/*! + Sets the \ref KDUpdater::UpdateSourcesInfo object whose information this widget should show. + + \code + KDUpdater::Application application; + + KDUpdater::UpdateSourcesView updatesView; + updatesView.setUpdateSourcesInfo( application.updateSourcesInfo() ); + updatesView.show(); + \endcode +*/ +void KDUpdater::UpdateSourcesView::setUpdateSourcesInfo(KDUpdater::UpdateSourcesInfo* info) +{ + if( d->updateSourcesInfo == info ) + return; + + if(d->updateSourcesInfo) + disconnect(d->updateSourcesInfo, 0, this, 0); + + d->updateSourcesInfo = info; + if(d->updateSourcesInfo) + { + connect(d->updateSourcesInfo, SIGNAL(reset()), this, SLOT(refresh())); + connect(d->updateSourcesInfo, SIGNAL(updateSourceInfoAdded(UpdateSourceInfo)), + this, SLOT(slotUpdateSourceInfoAdded(UpdateSourceInfo))); + connect(d->updateSourcesInfo, SIGNAL(updateSourceInfoRemoved(UpdateSourceInfo)), + this, SLOT(slotUpdateSourceInfoRemoved(UpdateSourceInfo))); + connect(d->updateSourcesInfo, SIGNAL(updateSourceInfoChanged(UpdateSourceInfo,UpdateSourceInfo)), + this, SLOT(slotUpdateSourceInfoChanged(UpdateSourceInfo,UpdateSourceInfo))); + } + + refresh(); +} + +/*! + Returns a pointer to the \ref KDUpdater::UpdateSourcesInfo object whose information this + widget is showing. +*/ +KDUpdater::UpdateSourcesInfo* KDUpdater::UpdateSourcesView::updateSourcesInfo() const +{ + return d->updateSourcesInfo; +} + +/*! + Returns the index of the currently selected update source in the widget. You can use this + index along with \ref KDUpdater::UpdateSourcesInfo::updateSourceInfo() method to get hold + of the update source info. +*/ +int KDUpdater::UpdateSourcesView::currentUpdateSourceInfoIndex() const +{ + if( !d->updateSourcesInfo ) + return -1; + + QTreeWidgetItem* item = this->currentItem(); + if( !item ) + return -1; + + int index = this->indexOfTopLevelItem(item); + if( index < 0 ) + return -1; + + return index; +} + +/*! + Call this slot to reload the updates information. By default this slot is connected to + \ref KDUpdater::UpdateSourcesInfo::reset() signal in \ref setUpdateSourcesInfo(). +*/ +void KDUpdater::UpdateSourcesView::refresh() +{ + this->clear(); + + if( !d->updateSourcesInfo ) + return; + + for(int i=0; i<d->updateSourcesInfo->updateSourceInfoCount(); i++) + { + KDUpdater::UpdateSourceInfo info = d->updateSourcesInfo->updateSourceInfo(i); + QTreeWidgetItem* item = new QTreeWidgetItem(this); + item->setText(0, info.name); + item->setText(1, info.title); + item->setText(2, info.url.toString()); + item->setData(0, Qt::UserRole, qVariantFromValue<KDUpdater::UpdateSourceInfo>(info)); + } + + resizeColumnToContents(0); +} + +/*! + Call this slot to make use of the in-built dialog box to add a new update source. Shown + below is a screenshot of the in-built dialog box. + + \image html addupdatesource.jpg +*/ +void KDUpdater::UpdateSourcesView::addNewSource() +{ + if( !d->updateSourcesInfo ) + return; + + QDialog dialog(this); + Ui::AddUpdateSourceDialog ui; + ui.setupUi(&dialog); + + while(1) + { + if( dialog.exec() == QDialog::Rejected ) + return; + + if(ui.txtName->text().isEmpty() || ui.txtUrl->text().isEmpty()) + { + QMessageBox::information(this, tr("Invalid Update Source Info"), + tr("A valid update source name and url has to be provided")); + continue; + } + + break; + } + + KDUpdater::UpdateSourceInfo newInfo; + newInfo.name = ui.txtName->text(); + newInfo.title = ui.txtTitle->text(); + newInfo.description = ui.txtDescription->toPlainText(); // FIXME: This should perhaps be toHtml + newInfo.url = QUrl(ui.txtUrl->text()); + + d->updateSourcesInfo->addUpdateSourceInfo(newInfo); +} + +/*! + Call this slot to delete the currently selected update source. +*/ +void KDUpdater::UpdateSourcesView::removeCurrentSource() +{ + if( !d->updateSourcesInfo ) + return; + + QTreeWidgetItem* item = this->currentItem(); + if( !item ) + return; + + int index = this->indexOfTopLevelItem(item); + if( index < 0 ) + return; + + d->updateSourcesInfo->removeUpdateSourceInfoAt(index); +} + +/*! + Call this slot to edit the currently selected update source, using the in-built edit + update source dialog box. Shown below is a screenshot of the edit update source dialog + box. + + \image html editupdatesource.jpg +*/ +void KDUpdater::UpdateSourcesView::editCurrentSource() +{ + if( !d->updateSourcesInfo ) + return; + + QTreeWidgetItem* item = this->currentItem(); + if( !item ) + return; + + int index = this->indexOfTopLevelItem(item); + if( index < 0 ) + return; + + KDUpdater::UpdateSourceInfo info = item->data(0, Qt::UserRole).value<KDUpdater::UpdateSourceInfo>(); + + QDialog dialog(this); + Ui::AddUpdateSourceDialog ui; + ui.setupUi(&dialog); + ui.txtName->setText(info.name); + ui.txtTitle->setText(info.title); + ui.txtDescription->setPlainText(info.description); // FIXME: This should perhaps be setHtml + ui.txtUrl->setText(info.url.toString()); + dialog.setWindowTitle(tr("Edit Update Source")); + + if( dialog.exec() == QDialog::Rejected ) + return; + + KDUpdater::UpdateSourceInfo newInfo; + newInfo.name = ui.txtName->text(); + newInfo.title = ui.txtTitle->text(); + newInfo.description = ui.txtDescription->toPlainText(); // FIXME: This should perhaps be setHtml + newInfo.url = QUrl(ui.txtUrl->text()); + + d->updateSourcesInfo->setUpdateSourceInfoAt(index, newInfo); +} + +/*! + \internal +*/ +void KDUpdater::UpdateSourcesView::slotUpdateSourceInfoAdded(const KDUpdater::UpdateSourceInfo &info) +{ + if( !d->updateSourcesInfo ) + return; + + QTreeWidgetItem* item = new QTreeWidgetItem(this); + item->setText(0, info.name); + item->setText(1, info.title); + item->setText(2, info.url.toString()); + item->setData(0, Qt::UserRole, qVariantFromValue<KDUpdater::UpdateSourceInfo>(info)); +} + +/*! + \internal +*/ +void KDUpdater::UpdateSourcesView::slotUpdateSourceInfoRemoved(const KDUpdater::UpdateSourceInfo &info) +{ + if( !d->updateSourcesInfo ) + return; + + QTreeWidgetItem* item = 0; + for(int i=0; i<topLevelItemCount(); i++) + { + item = topLevelItem(i); + KDUpdater::UpdateSourceInfo itemInfo = item->data(0, Qt::UserRole).value<KDUpdater::UpdateSourceInfo>(); + if(itemInfo == info) + break; + item = 0; + } + + if( !item ) + return; + + delete item; +} + +/*! + \internal +*/ +void KDUpdater::UpdateSourcesView::slotUpdateSourceInfoChanged (const KDUpdater::UpdateSourceInfo &newInfo, + const KDUpdater::UpdateSourceInfo &oldInfo) +{ + if( !d->updateSourcesInfo ) + return; + + QTreeWidgetItem* item = 0; + for(int i=0; i<topLevelItemCount(); i++) + { + item = topLevelItem(i); + KDUpdater::UpdateSourceInfo itemInfo = item->data(0, Qt::UserRole).value<KDUpdater::UpdateSourceInfo>(); + if(itemInfo == oldInfo) + break; + item = 0; + } + + if( !item ) + return; + + item->setText(0, newInfo.name); + item->setText(1, newInfo.title); + item->setText(2, newInfo.url.toString()); + item->setData(0, Qt::UserRole, qVariantFromValue<KDUpdater::UpdateSourceInfo>(newInfo)); +} + +/*! + \internal +*/ +void KDUpdater::UpdateSourcesView::contextMenuEvent(QContextMenuEvent* e) +{ + QTreeWidgetItem* item = this->itemAt( e->pos() ); + + QMenu menu; + QAction* addAction = menu.addAction(tr("&Add Source")); + QAction* editAction = item ? menu.addAction(tr("&Edit Source")) : 0; + QAction* remAction = item ? menu.addAction(tr("&Remove Source")) : 0; + + QAction* result = menu.exec( QCursor::pos() ); + if( !result ) + return; + + if( result == addAction ) + this->addNewSource(); + else if( result == remAction ) + this->removeCurrentSource(); + else if( result == editAction ) + this->editCurrentSource(); +} diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatesourcesview.h b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatesourcesview.h new file mode 100644 index 000000000..c2c840d72 --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatesourcesview.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved. +** +** This file is part of the KD Tools library. +** +** Licensees holding valid commercial KD Tools licenses may use this file in +** accordance with the KD Tools Commercial License Agreement provided with +** the Software. +** +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 and version 3 as published by the +** Free Software Foundation and appearing in the file LICENSE.LGPL included. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** Contact info@kdab.com if any conditions of this licensing are not +** clear to you. +** +**********************************************************************/ + +#ifndef KD_UPDATER_UPDATE_SOURCES_VIEW_H +#define KD_UPDATER_UPDATE_SOURCES_VIEW_H + +#include "kdupdater.h" +#include <QTreeWidget> + +namespace KDUpdater +{ + struct UpdateSourceInfo; + class UpdateSourcesInfo; + + class KDTOOLS_UPDATER_EXPORT UpdateSourcesView : public QTreeWidget + { + Q_OBJECT + + public: + explicit UpdateSourcesView(QWidget* parent=0); + ~UpdateSourcesView(); + + void setUpdateSourcesInfo(UpdateSourcesInfo* info); + UpdateSourcesInfo* updateSourcesInfo() const; + + int currentUpdateSourceInfoIndex() const; + + public Q_SLOTS: + void refresh(); + void addNewSource(); + void removeCurrentSource(); + void editCurrentSource(); + + protected: + void contextMenuEvent(QContextMenuEvent* e); + + private Q_SLOTS: + void slotUpdateSourceInfoAdded(const UpdateSourceInfo &info); + void slotUpdateSourceInfoRemoved(const UpdateSourceInfo &info); + void slotUpdateSourceInfoChanged(const UpdateSourceInfo &newInfo, + const UpdateSourceInfo &oldInfo); + + private: + struct UpdateSourcesViewData; + UpdateSourcesViewData* d; + }; +}; + +#endif diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/updatesdialog.ui b/installerbuilder/libinstaller/kdtools/KDUpdater/updatesdialog.ui new file mode 100644 index 000000000..52162109e --- /dev/null +++ b/installerbuilder/libinstaller/kdtools/KDUpdater/updatesdialog.ui @@ -0,0 +1,245 @@ +<ui version="4.0" > + <class>UpdatesDialog</class> + <widget class="QDialog" name="UpdatesDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>555</width> + <height>478</height> + </rect> + </property> + <property name="windowTitle" > + <string>Update</string> + </property> + <layout class="QGridLayout" name="gridLayout" > + <property name="sizeConstraint" > + <enum>QLayout::SetMinAndMaxSize</enum> + </property> + <item row="0" column="0" > + <widget class="QLabel" name="pixmapLabel" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Minimum" hsizetype="Minimum" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize" > + <size> + <width>96</width> + <height>96</height> + </size> + </property> + <property name="text" > + <string/> + </property> + <property name="alignment" > + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + </widget> + </item> + <item row="0" column="2" colspan="2" > + <widget class="QLabel" name="descriptionLabel" > + <property name="minimumSize" > + <size> + <width>400</width> + <height>96</height> + </size> + </property> + <property name="text" > + <string/> + </property> + <property name="textFormat" > + <enum>Qt::RichText</enum> + </property> + <property name="alignment" > + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + <property name="wordWrap" > + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="2" colspan="2" > + <layout class="QVBoxLayout" name="verticalLayout_2" > + <item> + <widget class="QWidget" native="1" name="releaseNotesGroup" > + <layout class="QVBoxLayout" name="verticalLayout" > + <property name="leftMargin" > + <number>0</number> + </property> + <property name="rightMargin" > + <number>0</number> + </property> + <item> + <widget class="QLabel" name="label" > + <property name="text" > + <string><b>Release Notes:</b></string> + </property> + <property name="textFormat" > + <enum>Qt::RichText</enum> + </property> + </widget> + </item> + <item> + <widget class="KDUPDATERVIEW" native="1" name="releaseNotesView" > + <property name="minimumSize" > + <size> + <width>400</width> + <height>200</height> + </size> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" native="1" name="packageSwitchBar" > + <layout class="QHBoxLayout" name="horizontalLayout" > + <property name="spacing" > + <number>0</number> + </property> + <property name="margin" > + <number>0</number> + </property> + <item> + <widget class="QCheckBox" name="packageUpdateCheckBox" > + <property name="text" > + <string>Update the current package</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>296</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QToolButton" name="previousPackageButton" > + <property name="text" > + <string/> + </property> + <property name="iconSize" > + <size> + <width>8</width> + <height>8</height> + </size> + </property> + <property name="autoRaise" > + <bool>true</bool> + </property> + <property name="arrowType" > + <enum>Qt::LeftArrow</enum> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="currentPackageLabel" > + <property name="text" > + <string>N</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>/</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="totalPackageLabel" > + <property name="text" > + <string>M</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="nextPackageButton" > + <property name="text" > + <string/> + </property> + <property name="iconSize" > + <size> + <width>8</width> + <height>8</height> + </size> + </property> + <property name="autoRaise" > + <bool>true</bool> + </property> + <property name="arrowType" > + <enum>Qt::RightArrow</enum> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </item> + <item row="2" column="3" > + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>KDUPDATERVIEW</class> + <extends>QWidget</extends> + <header>qwidget.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>UpdatesDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel" > + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>UpdatesDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel" > + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> |