diff options
-rw-r--r-- | src/libs/installer/binarycontent.cpp | 311 | ||||
-rw-r--r-- | src/libs/installer/binarycontent.h | 24 | ||||
-rw-r--r-- | src/libs/installer/packagemanagercore.cpp | 4 | ||||
-rw-r--r-- | src/libs/installer/packagemanagercore.h | 5 | ||||
-rw-r--r-- | src/libs/installer/packagemanagercore_p.cpp | 19 | ||||
-rw-r--r-- | src/libs/installer/packagemanagercore_p.h | 2 | ||||
-rw-r--r-- | src/libs/installer/productkeycheck.cpp | 4 | ||||
-rw-r--r-- | src/libs/installer/productkeycheck.h | 15 | ||||
-rw-r--r-- | src/sdk/installerbase.cpp | 546 | ||||
-rw-r--r-- | src/sdk/installerbase.h | 70 | ||||
-rw-r--r-- | src/sdk/main.cpp | 193 | ||||
-rw-r--r-- | src/sdk/sdk.pro | 14 | ||||
-rw-r--r-- | src/sdk/sdkapp.h | 87 | ||||
-rw-r--r-- | src/sdk/updatechecker.cpp | 114 | ||||
-rw-r--r-- | src/sdk/updatechecker.h | 11 | ||||
-rw-r--r-- | tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp | 6 | ||||
-rw-r--r-- | tools/devtool/main.cpp | 15 | ||||
-rw-r--r-- | tools/devtool/operationrunner.cpp | 3 | ||||
-rw-r--r-- | tools/devtool/operationrunner.h | 5 |
19 files changed, 666 insertions, 782 deletions
diff --git a/src/libs/installer/binarycontent.cpp b/src/libs/installer/binarycontent.cpp index 6280134d9..25032177b 100644 --- a/src/libs/installer/binarycontent.cpp +++ b/src/libs/installer/binarycontent.cpp @@ -41,16 +41,9 @@ #include "binarycontent.h" -#include "binaryformat.h" -#include "binaryformatenginehandler.h" #include "errors.h" #include "fileio.h" #include "fileutils.h" -#include "kdupdaterupdateoperationfactory.h" -#include "utils.h" - -#include <QFile> -#include <QResource> namespace QInstaller { @@ -101,124 +94,6 @@ qint64 BinaryContent::findMagicCookie(QFile *in, quint64 magicCookie) } /*! - \internal - Registers the resource found at \a segment within \a file into the Qt resource system. -*/ -static QByteArray addResourceFromBinary(QFile* file, const Range<qint64> &segment) -{ - if (segment.length() <= 0) - return 0; - - if (!file->seek(segment.start())) { - throw Error(QCoreApplication::translate("BinaryContent", - "Could not seek to in-binary resource. (offset: %1, length: %2)") - .arg(QString::number(segment.start()), QString::number(segment.length()))); - } - - QByteArray ba = QInstaller::retrieveData(file, segment.length()); - if (!QResource::registerResource((const uchar*) ba.constData(), QLatin1String(":/metadata"))) { - throw Error(QCoreApplication::translate("BinaryContent", - "Could not register in-binary resource.")); - } - return ba; -} - - -// -- Private - -class BinaryContent::Private : public QSharedData -{ -public: - Private(); - explicit Private(const QString &path); - Private(const Private &other); - ~Private(); - - qint64 m_magicMarker; - qint64 m_dataBlockStart; - - QSharedPointer<QFile> m_appBinary; - QSharedPointer<QFile> m_binaryDataFile; - - QList<Operation *> m_performedOperations; - QList<QPair<QString, QString> > m_performedOperationsData; - - QVector<QByteArray> m_resourceMappings; - QVector<Range<qint64> > m_metadataResourceSegments; -}; - - -BinaryContent::Private::Private() - : m_magicMarker(Q_INT64_C(0)) - , m_dataBlockStart(Q_INT64_C(0)) - , m_appBinary(0) - , m_binaryDataFile(0) -{} - -BinaryContent::Private::Private(const QString &path) - : m_magicMarker(Q_INT64_C(0)) - , m_dataBlockStart(Q_INT64_C(0)) - , m_appBinary(new QFile(path)) - , m_binaryDataFile(0) -{} - -BinaryContent::Private::Private(const Private &other) - : QSharedData(other) - , m_magicMarker(other.m_magicMarker) - , m_dataBlockStart(other.m_dataBlockStart) - , m_appBinary(other.m_appBinary) - , m_binaryDataFile(other.m_binaryDataFile) - , m_performedOperations(other.m_performedOperations) - , m_performedOperationsData(other.m_performedOperationsData) - , m_resourceMappings(other.m_resourceMappings) - , m_metadataResourceSegments(other.m_metadataResourceSegments) -{} - -BinaryContent::Private::~Private() -{ - foreach (const QByteArray &rccData, m_resourceMappings) - QResource::unregisterResource((const uchar*) rccData.constData(), QLatin1String(":/metadata")); - m_resourceMappings.clear(); -} - - -// -- BinaryContent - -BinaryContent::BinaryContent() - : d(new Private) -{} - -BinaryContent::~BinaryContent() -{} - -BinaryContent::BinaryContent(const QString &path) - : d(new Private(path)) -{} - -BinaryContent::BinaryContent(const BinaryContent &rhs) - : d(rhs.d) -{} - -BinaryContent &BinaryContent::operator=(const BinaryContent &rhs) -{ - if (this != &rhs) - d = rhs.d; - return *this; -} - -/*! - Reads binary content stored in the passed application binary. Maps the embedded resources into - memory and instantiates performed operations if available. -*/ -BinaryContent BinaryContent::readAndRegisterFromBinary(const QString &path) -{ - BinaryContent c = BinaryContent::readFromBinary(path); - c.registerEmbeddedQResources(); - c.registerPerformedOperations(); - return c; -} - -/*! * \class QInstaller::BinaryContent * * BinaryContent handles binary information embedded into executables. @@ -288,41 +163,6 @@ BinaryContent BinaryContent::readAndRegisterFromBinary(const QString &path) * \endverbatim */ -BinaryContent BinaryContent::readFromBinary(const QString &path) -{ - BinaryContent c(path); - - // Try to read the binary layout of the calling application. We need to figure out - // if we are in installer or an unistaller (maintenance, package manager, updater) binary. - QInstaller::openForRead(c.d->m_appBinary.data()); - quint64 cookiePos = findMagicCookie(c.d->m_appBinary.data(), BinaryContent::MagicCookie); - if (!c.d->m_appBinary->seek(cookiePos - sizeof(qint64))) { // seek to read the marker - throw Error(QCoreApplication::translate("BinaryContent", - "Could not seek to %1 to read the magic marker.").arg(cookiePos - sizeof(qint64))); - } - const qint64 magicMarker = QInstaller::retrieveInt64(c.d->m_appBinary.data()); - - if (magicMarker != MagicInstallerMarker) { - // We are not an installer, so we need to read the data from the .dat file. - - QFileInfo fi(path); - QString bundlePath; // On OSX it's not inside the bundle, deserves TODO. - if (QInstaller::isInBundle(fi.absoluteFilePath(), &bundlePath)) - fi.setFile(bundlePath); - - c.d->m_binaryDataFile.reset(new QFile(fi.absolutePath() + QLatin1Char('/') + fi.baseName() - + QLatin1String(".dat"))); - QInstaller::openForRead(c.d->m_binaryDataFile.data()); - readBinaryData(c, c.d->m_binaryDataFile, binaryLayout(c.d->m_binaryDataFile.data(), - BinaryContent::MagicCookieDat)); - } else { - // We are an installer, all data is appended to our binary itself. - readBinaryData(c, c.d->m_appBinary, binaryLayout(c.d->m_appBinary.data(), - BinaryContent::MagicCookie)); - } - return c; -} - /* static */ BinaryLayout BinaryContent::binaryLayout(QFile *file, quint64 magicCookie) { @@ -370,157 +210,6 @@ BinaryLayout BinaryContent::binaryLayout(QFile *file, quint64 magicCookie) return layout; } -/* static */ -void BinaryContent::readBinaryData(BinaryContent &content, const QSharedPointer<QFile> &file, - const BinaryLayout &layout) -{ - content.d->m_magicMarker = layout.magicMarker; - content.d->m_metadataResourceSegments = layout.metadataResourceSegments; - - const qint64 dataBlockStart = layout.endOfData - layout.dataBlockSize; - const qint64 operationsStart = layout.operationsStart + dataBlockStart; - if (!file->seek(operationsStart)) - throw Error(QCoreApplication::translate("BinaryContent", "Could not seek to operation list.")); - - const qint64 operationsCount = QInstaller::retrieveInt64(file.data()); - qDebug() << "Number of operations:" << operationsCount; - - for (int i = 0; i < operationsCount; ++i) { - const QString name = QInstaller::retrieveString(file.data()); - const QString data = QInstaller::retrieveString(file.data()); - content.d->m_performedOperationsData.append(qMakePair(name, data)); - } - - // seek to the position of the resource collections segment info - const qint64 resourceOffsetAndLengtSize = 2 * sizeof(qint64); - const qint64 resourceSectionSize = resourceOffsetAndLengtSize * layout.resourceCount; - qint64 offset = layout.endOfData - layout.indexSize - resourceSectionSize - - resourceOffsetAndLengtSize; - if (!file->seek(offset)) { - throw Error(QCoreApplication::translate("BinaryContent", - "Could not seek to read the resource collection segment info.")); - } - - offset = QInstaller::retrieveInt64(file.data()) + dataBlockStart; - if (!file->seek(offset)) { - throw Error(QCoreApplication::translate("BinaryContent", "Could not seek to start " - "position of resource collection block.")); - } - - ResourceCollectionManager collectionManager; - collectionManager.read(file, dataBlockStart); - BinaryFormatEngineHandler::instance()->registerResources(collectionManager.collections()); - - if (!QInstaller::isVerbose()) - return; - - const QList<ResourceCollection> collections = collectionManager.collections(); - qDebug() << "Number of resource collections loaded:" << collections.count(); - foreach (const ResourceCollection &collection, collections) { - const QList<QSharedPointer<Resource> > resources = collection.resources(); - qDebug() << collection.name().data() << "loaded..."; - QStringList resourcesWithSize; - foreach (const QSharedPointer<Resource> &resource, resources) { - resourcesWithSize.append(QString::fromLatin1("%1 - %2") - .arg(QString::fromUtf8(resource->name()), humanReadableSize(resource->size()))); - } - if (!resourcesWithSize.isEmpty()) { - qDebug() << " - " << resources.count() << "resources: " - << qPrintable(resourcesWithSize.join(QLatin1String("; "))); - } - } -} - -/*! - Registers already performed operations. -*/ -int BinaryContent::registerPerformedOperations() -{ - if (d->m_performedOperations.count() > 0) - return d->m_performedOperations.count(); - - for (int i = 0; i < d->m_performedOperationsData.count(); ++i) { - const QPair<QString, QString> opPair = d->m_performedOperationsData.at(i); - QScopedPointer<Operation> op(KDUpdater::UpdateOperationFactory::instance().create(opPair.first)); - if (op.isNull()) { - qWarning() << QString::fromLatin1("Failed to load unknown operation %1").arg(opPair.first); - continue; - } - - if (!op->fromXml(opPair.second)) { - qWarning() << "Failed to load XML for operation:" << opPair.first; - continue; - } - d->m_performedOperations.append(op.take()); - } - return d->m_performedOperations.count(); -} - -/*! - Returns the operations performed during installation. Returns an empty list if no operations - are instantiated, performed or the binary is the installer application. -*/ -OperationList BinaryContent::performedOperations() const -{ - return d->m_performedOperations; -} - -/*! - Returns the magic marker found in the binary. Returns 0 if no marker has been found. -*/ -qint64 BinaryContent::magicMarker() const -{ - return d->m_magicMarker; -} - -/*! - Registers the Qt resources embedded in this binary. -*/ -int BinaryContent::registerEmbeddedQResources() -{ - if (d->m_resourceMappings.count() > 0) - return d->m_resourceMappings.count(); - - const bool hasBinaryDataFile = !d->m_binaryDataFile.isNull(); - QFile *const data = hasBinaryDataFile ? d->m_binaryDataFile.data() : d->m_appBinary.data(); - if (data != 0 && !data->isOpen() && !data->open(QIODevice::ReadOnly)) { - throw Error(QCoreApplication::translate("BinaryContent", "Could not open binary %1: %2") - .arg(data->fileName(), data->errorString())); - } - - foreach (const Range<qint64> &i, d->m_metadataResourceSegments) - d->m_resourceMappings.append(addResourceFromBinary(data, i)); - - d->m_appBinary.clear(); - if (hasBinaryDataFile) - d->m_binaryDataFile.clear(); - - return d->m_resourceMappings.count(); -} - -/*! - Registers the passed file as default resource content. If the embedded resources are already - mapped into memory, it will replace the first with the new content. -*/ -void BinaryContent::registerAsDefaultQResource(const QString &path) -{ - QFile resource(path); - bool success = resource.open(QIODevice::ReadOnly); - if (success && (d->m_resourceMappings.count() > 0)) { - success = QResource::unregisterResource((const uchar*) d->m_resourceMappings.first() - .constData(), QLatin1String(":/metadata")); - if (success) - d->m_resourceMappings.remove(0); - } - - if (success) { - d->m_resourceMappings.prepend(addResourceFromBinary(&resource, - Range<qint64>::fromStartAndEnd(0, resource.size()))); - } else { - qWarning() << QString::fromLatin1("Could not register '%1' as default resource.").arg(path); - } -} - void BinaryContent::readBinaryContent(const QSharedPointer<QFile> &in, ResourceCollection *metaResources, QList<OperationBlob> *operations, ResourceCollectionManager *manager, qint64 *magicMarker, quint64 magicCookie) diff --git a/src/libs/installer/binarycontent.h b/src/libs/installer/binarycontent.h index edbf54f07..2efe3d0fc 100644 --- a/src/libs/installer/binarycontent.h +++ b/src/libs/installer/binarycontent.h @@ -69,6 +69,7 @@ struct BinaryLayout class INSTALLER_EXPORT BinaryContent { public: + // the marker to distinguish what kind of binary static const qint64 MagicInstallerMarker = 0x12023233UL; static const qint64 MagicUninstallerMarker = 0x12023234UL; @@ -79,24 +80,9 @@ public: static const quint64 MagicCookie = 0xc2630a1c99d668f8LL; // binary static const quint64 MagicCookieDat = 0xc2630a1c99d668f9LL; // data - BinaryContent(); - ~BinaryContent(); - - BinaryContent(const BinaryContent &rhs); - BinaryContent &operator=(const BinaryContent &rhs); - - static BinaryContent readFromBinary(const QString &path); - static BinaryContent readAndRegisterFromBinary(const QString &path); static qint64 findMagicCookie(QFile *file, quint64 magicCookie); static BinaryLayout binaryLayout(QFile *file, quint64 magicCookie); - int registerPerformedOperations(); - OperationList performedOperations() const; - - qint64 magicMarker() const; - int registerEmbeddedQResources(); - void registerAsDefaultQResource(const QString &path); - static void readBinaryContent(const QSharedPointer<QFile> &in, ResourceCollection *metaResources, QList<OperationBlob> *operations, @@ -110,14 +96,6 @@ public: const ResourceCollectionManager &manager, qint64 magicMarker, quint64 magicCookie); -private: - explicit BinaryContent(const QString &path); - static void readBinaryData(BinaryContent &content, const QSharedPointer<QFile> &file, - const BinaryLayout &layout); - -private: - class Private; - QSharedDataPointer<Private> d; }; } // namespace QInstaller diff --git a/src/libs/installer/packagemanagercore.cpp b/src/libs/installer/packagemanagercore.cpp index ad14a137e..6abfdb81c 100644 --- a/src/libs/installer/packagemanagercore.cpp +++ b/src/libs/installer/packagemanagercore.cpp @@ -749,8 +749,8 @@ PackageManagerCore::PackageManagerCore() qRegisterMetaType<QInstaller::PackageManagerCore::WizardPage>("QInstaller::PackageManagerCore::WizardPage"); } -PackageManagerCore::PackageManagerCore(qint64 magicmaker, const OperationList &performedOperations) - : d(new PackageManagerCorePrivate(this, magicmaker, performedOperations)) +PackageManagerCore::PackageManagerCore(qint64 magicmaker, const QList<OperationBlob> &operations) + : d(new PackageManagerCorePrivate(this, magicmaker, operations)) { qRegisterMetaType<QInstaller::PackageManagerCore::Status>("QInstaller::PackageManagerCore::Status"); qRegisterMetaType<QInstaller::PackageManagerCore::WizardPage>("QInstaller::PackageManagerCore::WizardPage"); diff --git a/src/libs/installer/packagemanagercore.h b/src/libs/installer/packagemanagercore.h index b3c5013cc..d8bcc86a6 100644 --- a/src/libs/installer/packagemanagercore.h +++ b/src/libs/installer/packagemanagercore.h @@ -41,6 +41,7 @@ #ifndef PACKAGEMANAGERCORE_H #define PACKAGEMANAGERCORE_H +#include "binaryformat.h" #include "repository.h" #include "qinstallerglobal.h" @@ -72,8 +73,8 @@ class INSTALLER_EXPORT PackageManagerCore : public QObject Q_PROPERTY(int status READ status NOTIFY statusChanged) public: - explicit PackageManagerCore(); - explicit PackageManagerCore(qint64 magicmaker, const OperationList &oldOperations = OperationList()); + PackageManagerCore(); + PackageManagerCore(qint64 magicmaker, const QList<OperationBlob> &ops); ~PackageManagerCore(); // status diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp index b2d7d7d83..c9cd458a6 100644 --- a/src/libs/installer/packagemanagercore_p.cpp +++ b/src/libs/installer/packagemanagercore_p.cpp @@ -203,7 +203,7 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core) } PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, qint64 magicInstallerMaker, - const OperationList &performedOperations) + const QList<OperationBlob> &performedOperations) : m_updateFinder(0) , m_updaterApplication(new DummyConfigurationInterface) , m_status(PackageManagerCore::Unfinished) @@ -212,7 +212,6 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, q , m_launchedAsRoot(AdminAuthorization::hasAdminRights()) , m_completeUninstall(false) , m_needToWriteMaintenanceTool(false) - , m_performedOperationsOld(performedOperations) , m_dependsOnLocalInstallerBinary(false) , m_core(core) , m_updates(false) @@ -228,6 +227,22 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, q , m_guiObject(0) , m_remoteFileEngineHandler(new RemoteFileEngineHandler) { + foreach (const OperationBlob &operation, performedOperations) { + QScopedPointer<QInstaller::Operation> op(KDUpdater::UpdateOperationFactory::instance() + .create(operation.name)); + if (op.isNull()) { + qWarning() << QString::fromLatin1("Failed to load unknown operation %1") + .arg(operation.name); + continue; + } + + if (!op->fromXml(operation.xml)) { + qWarning() << "Failed to load XML for operation:" << operation.name; + continue; + } + m_performedOperationsOld.append(op.take()); + } + // Creates and initializes a remote client, makes us get admin rights for QFile, QSettings // and QProcess operations. Init needs to called before we can get the real authorization key. int port = 30000 + qrand() % 1000; diff --git a/src/libs/installer/packagemanagercore_p.h b/src/libs/installer/packagemanagercore_p.h index 3b2655276..29da8cb54 100644 --- a/src/libs/installer/packagemanagercore_p.h +++ b/src/libs/installer/packagemanagercore_p.h @@ -106,7 +106,7 @@ public: explicit PackageManagerCorePrivate(PackageManagerCore *core); explicit PackageManagerCorePrivate(PackageManagerCore *core, qint64 magicInstallerMaker, - const OperationList &performedOperations); + const QList<OperationBlob> &performedOperations); ~PackageManagerCorePrivate(); static bool isProcessRunning(const QString &name, const QList<ProcessInfo> &processes); diff --git a/src/libs/installer/productkeycheck.cpp b/src/libs/installer/productkeycheck.cpp index 232dbb719..46a568625 100644 --- a/src/libs/installer/productkeycheck.cpp +++ b/src/libs/installer/productkeycheck.cpp @@ -30,6 +30,8 @@ #include "productkeycheck.h" #include "packagemanagercore.h" +namespace QInstaller { + class ProductKeyCheckPrivate { }; @@ -91,3 +93,5 @@ QList<int> ProductKeyCheck::registeredPages() const { return QList<int>(); } + +} // namespace QInstaller diff --git a/src/libs/installer/productkeycheck.h b/src/libs/installer/productkeycheck.h index 3df747f37..02e8a76a5 100644 --- a/src/libs/installer/productkeycheck.h +++ b/src/libs/installer/productkeycheck.h @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (c) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (c) 2013-2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of Installer Framework @@ -35,18 +35,18 @@ #include <QString> namespace QInstaller{ - class PackageManagerCore; - class Repository; -} - +class PackageManagerCore; class ProductKeyCheckPrivate; +class Repository; class INSTALLER_EXPORT ProductKeyCheck { + Q_DISABLE_COPY(ProductKeyCheck) + public: ~ProductKeyCheck(); static ProductKeyCheck *instance(); - void init(QInstaller::PackageManagerCore *core); + void init(PackageManagerCore *core); // was validLicense bool hasValidKey(); @@ -67,7 +67,8 @@ public: private: ProductKeyCheck(); ProductKeyCheckPrivate *const d; - Q_DISABLE_COPY(ProductKeyCheck) }; +} // namespace QInstaller + #endif // PRODUCTKEYCHECK_H diff --git a/src/sdk/installerbase.cpp b/src/sdk/installerbase.cpp index 73831bed1..4d13339cd 100644 --- a/src/sdk/installerbase.cpp +++ b/src/sdk/installerbase.cpp @@ -39,417 +39,263 @@ ** **************************************************************************/ -#include "console.h" +#include "constants.h" +#include "commandlineparser.h" +#include "installerbase.h" #include "installerbasecommons.h" -#include "sdkapp.h" #include "tabcontroller.h" -#include "updatechecker.h" -#include <binarycontent.h> +#include <binaryformatenginehandler.h> +#include <copydirectoryoperation.h> #include <errors.h> #include <init.h> +#include <kdupdaterupdateoperations.h> #include <messageboxhandler.h> +#include <packagemanagercore.h> +#include <qprocesswrapper.h> #include <productkeycheck.h> -#include <remoteserver.h> #include <settings.h> #include <utils.h> -#include <kdselfrestarter.h> #include <kdrunoncechecker.h> #include <kdupdaterfiledownloaderfactory.h> -#include <QCommandLineParser> #include <QDirIterator> -#include <QNetworkProxyFactory> +#include <QTemporaryFile> #include <QTranslator> #include <iostream> -#define QUOTE_(x) #x -#define QUOTE(x) QUOTE_(x) -#define VERSION "IFW Version: \"" QUOTE(IFW_VERSION) "\"" -#define BUILDDATE "Build date: " QUOTE(__DATE__) -#define SHA "Installer Framework SHA1: \"" QUOTE(_GIT_SHA1_) "\"" -static const char PLACEHOLDER[32] = "MY_InstallerCreateDateTime_MY"; - -using namespace QInstaller; - -static QStringList repositories(const QString &list) +InstallerBase::InstallerBase(int &argc, char *argv[]) + : SDKApp<QApplication>(argc, argv) + , m_core(0) { - const QStringList items = list.split(QLatin1Char(','), QString::SkipEmptyParts); - foreach (const QString &item, items) - qDebug() << "Adding custom repository:" << item; - return items; + QInstaller::init(); // register custom operations } -// -- main - -int main(int argc, char *argv[]) +InstallerBase::~InstallerBase() { -// increase maximum numbers of file descriptors -#if defined (Q_OS_OSX) - struct rlimit rl; - getrlimit(RLIMIT_NOFILE, &rl); - rl.rlim_cur = qMin((rlim_t)OPEN_MAX, rl.rlim_max); - setrlimit(RLIMIT_NOFILE, &rl); -#endif - - qsrand(QDateTime::currentDateTime().toTime_t()); - - QCommandLineParser parser; - QCommandLineOption help = parser.addHelpOption(); - - QCommandLineOption version(QLatin1String("version"), - QLatin1String("Displays version information.")); - parser.addOption(version); - - QCommandLineOption verbose(QStringList() << QLatin1String("v") << QLatin1String("verbose"), - QLatin1String("Verbose mode. Prints out more information.")); - parser.addOption(verbose); - - QCommandLineOption proxy(QLatin1String("proxy"), - QLatin1String("Use system proxy on Windows and OS X. This option has no effect on Linux.")); - parser.addOption(proxy); - - QCommandLineOption script(QLatin1String("script"), - QLatin1String("Execute the script given as argument."), QLatin1String("file")); - parser.addOption(script); - - QCommandLineOption checkUpdates(QLatin1String("checkupdates"), - QLatin1String("Check for updates and return an XML description.")); - parser.addOption(checkUpdates); - - QCommandLineOption updater(QLatin1String("updater"), - QLatin1String("Start application in updater mode.")); - parser.addOption(updater); - - QCommandLineOption pkgManager(QLatin1String("manage-packages"), - QLatin1String("Start application in package manager mode.")); - parser.addOption(pkgManager); - - QCommandLineOption noForce(QLatin1String("no-force-installations"), - QLatin1String("Allow deselection of components that are marked as forced.")); - parser.addOption(noForce); - - QCommandLineOption showVirtuals(QLatin1String("show-virtual-components"), - QLatin1String("Show virtual components in installer and package manager.")); - parser.addOption(showVirtuals); - - QCommandLineOption offlineRepo(QLatin1String("create-offline-repository"), QLatin1String( - "Create a local repository inside the installation directory. This option has no effect " - "on online installer's")); - parser.addOption(offlineRepo); - - QCommandLineOption addRepo(QLatin1String("addRepository"), - QLatin1String("Add a local or remote repository to the list of user defined repositories."), - QLatin1String("URI,...")); - parser.addOption(addRepo); - - QCommandLineOption addTmpRepo(QLatin1String("addTempRepository"), QLatin1String( - "Add a local or remote repository to the list of temporary available repositories."), - QLatin1String("URI,...")); - parser.addOption(addTmpRepo); - - QCommandLineOption setTmpRepo(QLatin1String("setTempRepository"), - QLatin1String("Set a local or remote repository as temporary repository, it is the only " - "one used during fetch.\nNote: URI must be prefixed with the protocol, i.e. file:///, " - "https://, http:// or ftp://."), QLatin1String("URI,...")); - parser.addOption(setTmpRepo); - - QCommandLineOption startServer(QLatin1String("startserver"), QLatin1String("Starts the " - "application as headless process waiting for commands to execute."), - QLatin1String("port,key")); - parser.addOption(startServer); - - parser.addPositionalArgument(QLatin1String("Key=Value"), - QLatin1String("Key Value pair to be set.")); - - // We need to start either a command line application or a GUI application. Since we - // fail doing so at least on Linux while parsing the argument using a core application - // object and later starting the GUI application, we now parse the arguments first. - parser.parse(QInstaller::parseCommandLineArgs(argc, argv)); - - QStringList mutually; - if (parser.isSet(checkUpdates)) - mutually << checkUpdates.names(); - if (parser.isSet(updater)) - mutually << updater.names(); - if (parser.isSet(pkgManager)) - mutually << pkgManager.names(); - - if (parser.isSet(help) || parser.isSet(version) || (mutually.count() > 1)) { - Console c; - QCoreApplication app(argc, argv); - - if (parser.isSet(version)) { - std::cout << VERSION << std::endl << BUILDDATE << std::endl << SHA << std::endl; - const QDateTime dateTime = QDateTime::fromString(QLatin1String(PLACEHOLDER), - QLatin1String("yyyy-MM-dd - HH:mm:ss")); - if (dateTime.isValid()) - std::cout << "Installer creation time: " << PLACEHOLDER << std::endl; - return EXIT_SUCCESS; - } - - if (mutually.count() > 1) { - std::cerr << qPrintable(QString::fromLatin1("The following options are mutually " - "exclusive: %1.").arg(mutually.join(QLatin1String(", ")))) << std::endl; - } + delete m_core; +} - std::cout << qPrintable(parser.helpText()) << std::endl; - return parser.isSet(help) ? EXIT_SUCCESS : EXIT_FAILURE; +int InstallerBase::run() +{ + KDRunOnceChecker runCheck(QLatin1String("lockmyApp1234865.lock")); + if (runCheck.isRunning(KDRunOnceChecker::ProcessList) + || runCheck.isRunning(KDRunOnceChecker::Lockfile)) { + QInstaller::MessageBoxHandler::information(0, QLatin1String("AlreadyRunning"), + QString::fromLatin1("Waiting for %1").arg(qAppName()), + QString::fromLatin1("Another %1 instance is already running. Wait " + "until it finishes, close it, or restart your system.").arg(qAppName())); + return EXIT_FAILURE; } - if (parser.isSet(startServer)) { - const QString argument = parser.value(startServer); - const QString port = argument.section(QLatin1Char(','), 0, 0); - const QString key = argument.section(QLatin1Char(','), 1, 1); - - QStringList missing; - if (port.isEmpty()) - missing << QLatin1String("Port"); - if (key.isEmpty()) - missing << QLatin1String("Key"); - - SDKApp<QCoreApplication> app(argc, argv); - if (missing.count()) { - Console c; - std::cerr << qPrintable(QString::fromLatin1("Missing argument(s) for option " - "'startserver': %2").arg(missing.join(QLatin1String(", ")))) << std::endl; - std::cout << qPrintable(parser.helpText()) << std::endl; - return EXIT_FAILURE; - } - - RemoteServer *server = new RemoteServer; - QObject::connect(server, SIGNAL(destroyed()), &app, SLOT(quit())); - server->init(port.toInt(), QHostAddress::LocalHost, Protocol::Mode::Release); - server->setAuthorizationKey(key); - server->start(); - return app.exec(); + QString fileName = datFile(binaryFile()); + quint64 cookie = QInstaller::BinaryContent::MagicCookieDat; + if (fileName.isEmpty()) { + fileName = binaryFile(); + cookie = QInstaller::BinaryContent::MagicCookie; } - try { - QScopedPointer<Console> console; - if (parser.isSet(verbose)) { - console.reset(new Console); - QInstaller::setVerbose(parser.isSet(verbose)); - } - - // On Windows we need the console window from above, we are a GUI application. - const QStringList unknownOptionNames = parser.unknownOptionNames(); - if (!unknownOptionNames.isEmpty()) { - const QString options = unknownOptionNames.join(QLatin1String(", ")); - std::cerr << "Unknown option: " << qPrintable(options) << std::endl; - } + QSharedPointer<QFile> binary(new QFile(fileName)); + QInstaller::openForRead(binary.data()); + + qint64 magicMarker; + QInstaller::ResourceCollection resources; + QInstaller::ResourceCollectionManager manager; + QList<QInstaller::OperationBlob> oldOperations; + QInstaller::BinaryContent::readBinaryContent(binary, &resources, &oldOperations, &manager, + &magicMarker, cookie); + + if (QInstaller::isVerbose()) { + using namespace std; + cout << "Language:" << QLocale().uiLanguages().value(0, + QLatin1String("No UI language set")).constData() << endl; + cout << "Arguments: " << arguments().join(QLatin1String(", ")).constData() << endl; + } - if (parser.isSet(proxy)) { - // Make sure we honor the system's proxy settings -#if defined(Q_OS_UNIX) && !defined(Q_OS_OSX) - QUrl proxyUrl(QString::fromLatin1(qgetenv("http_proxy"))); - if (proxyUrl.isValid()) { - QNetworkProxy proxy(QNetworkProxy::HttpProxy, proxyUrl.host(), proxyUrl.port(), - proxyUrl.userName(), proxyUrl.password()); - QNetworkProxy::setApplicationProxy(proxy); - } -#else - QNetworkProxyFactory::setUseSystemConfiguration(true); -#endif - } + registerMetaResources(resources); // the base class will unregister the resources + QInstaller::BinaryFormatEngineHandler::instance()->registerResources(manager.collections()); - if (parser.isSet(checkUpdates)) - return UpdateChecker().check(argc, argv); + if (QInstaller::isVerbose()) + dumpResourceTree(); - SDKApp<QApplication> app(argc, argv); - KDRunOnceChecker runCheck(QLatin1String("lockmyApp1234865.lock")); + // instantiate the installer we are actually going to use + m_core = new QInstaller::PackageManagerCore(magicMarker, oldOperations); + QInstaller::ProductKeyCheck::instance()->init(m_core); - if (runCheck.isRunning(KDRunOnceChecker::ProcessList) - || runCheck.isRunning(KDRunOnceChecker::Lockfile)) { - QInstaller::MessageBoxHandler::information(0, QLatin1String("AlreadyRunning"), - QString::fromLatin1("Waiting for %1").arg(qAppName()), - QString::fromLatin1("Another %1 instance is already running. Wait " - "until it finishes, close it, or restart your system.").arg(qAppName())); - return EXIT_FAILURE; - } + // We can close the binary file if we are an online installer or no installer at all, cause no + // embedded archives exist inside the component index. Keeps the .dat file unlocked on Windows. + if ((!m_core->isInstaller()) || (!m_core->isOfflineOnly())) + binary->close(); - const KDSelfRestarter restarter(argc, argv); - QInstaller::init(); // register custom operations + CommandLineParser parser; + parser.parse(arguments()); - if (QInstaller::isVerbose()) { - qDebug() << VERSION; - qDebug() << "Arguments:" << app.arguments(); - qDebug() << "Language: " << QLocale().uiLanguages().value(0, - QLatin1String("No UI language set")); - } + QString controlScript; + if (parser.isSet(QLatin1String(CommandLineOptions::Script))) { + controlScript = parser.value(QLatin1String(CommandLineOptions::Script)); + if (!QFileInfo(controlScript).exists()) + throw QInstaller::Error(QLatin1String("Script file does not exist.")); + } - BinaryContent content = BinaryContent::readAndRegisterFromBinary(app.binaryFile()); + if (parser.isSet(QLatin1String(CommandLineOptions::Proxy))) { + m_core->settings().setProxyType(QInstaller::Settings::SystemProxy); + KDUpdater::FileDownloaderFactory::instance().setProxyFactory(m_core->proxyFactory()); + } - // instantiate the installer we are actually going to use - PackageManagerCore core(content.magicMarker(), content.performedOperations()); - ProductKeyCheck::instance()->init(&core); + if (parser.isSet(QLatin1String(CommandLineOptions::ShowVirtualComponents))) { + QFont f; + f.setItalic(true); + QInstaller::PackageManagerCore::setVirtualComponentsFont(f); + QInstaller::PackageManagerCore::setVirtualComponentsVisible(true); + } - QString controlScript; - if (parser.isSet(script)) { - controlScript = parser.value(script); - if (!QFileInfo(controlScript).exists()) - throw Error(QLatin1String("Script file does not exist.")); - } + if (parser.isSet(QLatin1String(CommandLineOptions::Updater))) { + if (m_core->isInstaller()) + throw QInstaller::Error(QLatin1String("Cannot start installer binary as updater.")); + m_core->setUpdater(); + } - if (parser.isSet(proxy)) { - core.settings().setProxyType(QInstaller::Settings::SystemProxy); - KDUpdater::FileDownloaderFactory::instance().setProxyFactory(core.proxyFactory()); - } + if (parser.isSet(QLatin1String(CommandLineOptions::ManagePackages))) { + if (m_core->isInstaller()) + throw QInstaller::Error(QLatin1String("Cannot start installer binary as package manager.")); + m_core->setPackageManager(); + } - if (parser.isSet(showVirtuals)) { - QFont f; - f.setItalic(true); - PackageManagerCore::setVirtualComponentsFont(f); - PackageManagerCore::setVirtualComponentsVisible(true); - } + if (parser.isSet(QLatin1String(CommandLineOptions::AddRepository))) { + const QStringList repoList = repositories(parser + .value(QLatin1String(CommandLineOptions::AddRepository))); + if (repoList.isEmpty()) + throw QInstaller::Error(QLatin1String("Empty repository list for option 'addRepository'.")); + m_core->addUserRepositories(repoList); + } - if (parser.isSet(updater)) { - if (core.isInstaller()) - throw Error(QLatin1String("Cannot start installer binary as updater.")); - core.setUpdater(); - } + if (parser.isSet(QLatin1String(CommandLineOptions::AddTmpRepository))) { + const QStringList repoList = repositories(parser + .value(QLatin1String(CommandLineOptions::AddTmpRepository))); + if (repoList.isEmpty()) + throw QInstaller::Error(QLatin1String("Empty repository list for option 'addTempRepository'.")); + m_core->setTemporaryRepositories(repoList, false); + } - if (parser.isSet(pkgManager)) { - if (core.isInstaller()) - throw Error(QLatin1String("Cannot start installer binary as package manager.")); - core.setPackageManager(); - } + if (parser.isSet(QLatin1String(CommandLineOptions::SetTmpRepository))) { + const QStringList repoList = repositories(parser + .value(QLatin1String(CommandLineOptions::SetTmpRepository))); + if (repoList.isEmpty()) + throw QInstaller::Error(QLatin1String("Empty repository list for option 'setTempRepository'.")); + m_core->setTemporaryRepositories(repoList, true); + } - if (parser.isSet(addRepo)) { - const QStringList repoList = repositories(parser.value(addRepo)); - if (repoList.isEmpty()) - throw Error(QLatin1String("Empty repository list for option 'addRepository'.")); - core.addUserRepositories(repoList); + QInstaller::PackageManagerCore::setNoForceInstallation(parser + .isSet(QLatin1String(CommandLineOptions::NoForceInstallation))); + QInstaller::PackageManagerCore::setCreateLocalRepositoryFromBinary(parser + .isSet(QLatin1String(CommandLineOptions::CreateOfflineRepository))); + + QHash<QString, QString> params; + const QStringList positionalArguments = parser.positionalArguments(); + foreach (const QString &argument, positionalArguments) { + if (argument.contains(QLatin1Char('='))) { + const QString name = argument.section(QLatin1Char('='), 0, 0); + const QString value = argument.section(QLatin1Char('='), 1, 1); + params.insert(name, value); + m_core->setValue(name, value); } + } - if (parser.isSet(addTmpRepo)) { - const QStringList repoList = repositories(parser.value(addTmpRepo)); - if (repoList.isEmpty()) - throw Error(QLatin1String("Empty repository list for option 'addTempRepository'.")); - core.setTemporaryRepositories(repoList, false); + const QString directory = QLatin1String(":/translations"); + const QStringList translations = m_core->settings().translations(); + + // install the default Qt translator + QScopedPointer<QTranslator> translator(new QTranslator(QCoreApplication::instance())); + foreach (const QLocale locale, QLocale().uiLanguages()) { + // As there is no qt_en.qm, we simply end the search when the next + // preferred language is English. + if (locale.language() == QLocale::English) + break; + if (translator->load(locale, QLatin1String("qt"), QString::fromLatin1("_"), directory)) { + QCoreApplication::instance()->installTranslator(translator.take()); + break; } + } - if (parser.isSet(setTmpRepo)) { - const QStringList repoList = repositories(parser.value(setTmpRepo)); - if (repoList.isEmpty()) - throw Error(QLatin1String("Empty repository list for option 'setTempRepository'.")); - core.setTemporaryRepositories(repoList, true); - } + translator.reset(new QTranslator(QCoreApplication::instance())); + // install English translation as fallback so that correct license button text is used + if (translator->load(QLatin1String("en_us"), directory)) + QCoreApplication::instance()->installTranslator(translator.take()); - PackageManagerCore::setNoForceInstallation(parser.isSet(noForce)); - PackageManagerCore::setCreateLocalRepositoryFromBinary(parser.isSet(offlineRepo)); - - QHash<QString, QString> params; - const QStringList positionalArguments = parser.positionalArguments(); - foreach (const QString &argument, positionalArguments) { - if (argument.contains(QLatin1Char('='))) { - const QString name = argument.section(QLatin1Char('='), 0, 0); - const QString value = argument.section(QLatin1Char('='), 1, 1); - params.insert(name, value); - core.setValue(name, value); + if (translations.isEmpty()) { + translator.reset(new QTranslator(QCoreApplication::instance())); + foreach (const QLocale locale, QLocale().uiLanguages()) { + if (translator->load(locale, QLatin1String(""), QLatin1String(""), directory)) { + QCoreApplication::instance()->installTranslator(translator.take()); + break; } } - - // this needs to happen after we parse the arguments, but before we use the actual resources - const QString newDefaultResource = core.value(QString::fromLatin1("DefaultResourceReplacement")); - if (!newDefaultResource.isEmpty()) - content.registerAsDefaultQResource(newDefaultResource); - - if (QInstaller::isVerbose()) { - qDebug() << "Resource tree:"; - QDirIterator it(QLatin1String(":/"), QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden, - QDirIterator::Subdirectories); - while (it.hasNext()) { - const QString path = it.next(); - if (path.startsWith(QLatin1String(":/trolltech")) - || path.startsWith(QLatin1String(":/qt-project.org"))) { - continue; - } - qDebug() << " " << path.toUtf8().constData(); - } + } else { + foreach (const QString &translation, translations) { + translator.reset(new QTranslator(QCoreApplication::instance())); + if (translator->load(translation, QLatin1String(":/translations"))) + QCoreApplication::instance()->installTranslator(translator.take()); } + } - const QString directory = QLatin1String(":/translations"); - const QStringList translations = core.settings().translations(); + //create the wizard GUI + TabController controller(0); + controller.setManager(m_core); + controller.setManagerParams(params); + controller.setControlScript(controlScript); - // install the default Qt translator - QScopedPointer<QTranslator> translator(new QTranslator(&app)); - foreach (const QLocale locale, QLocale().uiLanguages()) { - // As there is no qt_en.qm, we simply end the search when the next - // preferred language is English. - if (locale.language() == QLocale::English) - break; - if (translator->load(locale, QLatin1String("qt"), QString::fromLatin1("_"), directory)) { - app.installTranslator(translator.take()); - break; - } - } + if (m_core->isInstaller()) + controller.setGui(new InstallerGui(m_core)); + else + controller.setGui(new MaintenanceGui(m_core)); - translator.reset(new QTranslator(&app)); - // install English translation as fallback so that correct license button text is used - if (translator->load(QLatin1String("en_us"), directory)) - app.installTranslator(translator.take()); - - if (translations.isEmpty()) { - translator.reset(new QTranslator(&app)); - foreach (const QLocale locale, QLocale().uiLanguages()) { - if (translator->load(locale, QLatin1String(""), QLatin1String(""), directory)) { - app.installTranslator(translator.take()); - break; - } - } - } else { - foreach (const QString &translation, translations) { - translator.reset(new QTranslator(&app)); - if (translator->load(translation, QLatin1String(":/translations"))) - app.installTranslator(translator.take()); - } - } + QInstaller::PackageManagerCore::Status status = + QInstaller::PackageManagerCore::Status(controller.init()); + if (status != QInstaller::PackageManagerCore::Success) + return status; - //create the wizard GUI - TabController controller(0); - controller.setManager(&core); - controller.setManagerParams(params); - controller.setControlScript(controlScript); + const int result = QCoreApplication::instance()->exec(); + if (result != 0) + return result; - if (core.isInstaller()) { - controller.setGui(new InstallerGui(&core)); - } else { - controller.setGui(new MaintenanceGui(&core)); - } + if (m_core->finishedWithSuccess()) + return QInstaller::PackageManagerCore::Success; - PackageManagerCore::Status status = PackageManagerCore::Status(controller.init()); - if (status != PackageManagerCore::Success) + status = m_core->status(); + switch (status) { + case QInstaller::PackageManagerCore::Success: return status; - const int result = app.exec(); - if (result != 0) - return result; + case QInstaller::PackageManagerCore::Canceled: + return status; - if (core.finishedWithSuccess()) - return PackageManagerCore::Success; + default: + break; + } + return QInstaller::PackageManagerCore::Failure; +} - status = core.status(); - switch (status) { - case PackageManagerCore::Success: - return status; - case PackageManagerCore::Canceled: - return status; +// -- private - default: - break; - } - return PackageManagerCore::Failure; - } catch(const Error &e) { - std::cerr << qPrintable(e.message()) << std::endl; - } catch (const std::exception &e) { - std::cerr << e.what() << std::endl; - } catch(...) { - std::cerr << "Unknown error, aborting." << std::endl; +void InstallerBase::dumpResourceTree() const +{ + std::cout << "Resource tree:" << std::endl; + QDirIterator it(QLatin1String(":/"), QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden, + QDirIterator::Subdirectories); + while (it.hasNext()) { + if (it.next().startsWith(QLatin1String(":/qt-project.org"))) + continue; + std::cout << " " << it.filePath().constData() << std::endl; } +} - return PackageManagerCore::Failure; +QStringList InstallerBase::repositories(const QString &list) const +{ + const QStringList items = list.split(QLatin1Char(','), QString::SkipEmptyParts); + foreach (const QString &item, items) + std::cout << "Adding custom repository:" << item.constData() << std::endl; + return items; } diff --git a/src/sdk/installerbase.h b/src/sdk/installerbase.h new file mode 100644 index 000000000..b241d9988 --- /dev/null +++ b/src/sdk/installerbase.h @@ -0,0 +1,70 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#ifndef INSTALLERBASE_H +#define INSTALLERBASE_H + +#include "sdkapp.h" + +namespace QInstaller { + class PackageManagerCore; +} + +class InstallerBase : public SDKApp<QApplication> +{ + Q_OBJECT + Q_DISABLE_COPY(InstallerBase) + +public: + InstallerBase(int &argc, char *argv[]); + ~InstallerBase(); + + int run(); + +private: + void dumpResourceTree() const; + QStringList repositories(const QString &list) const; + +private: + QInstaller::PackageManagerCore *m_core; +}; + +#endif // INSTALLERBASE_H diff --git a/src/sdk/main.cpp b/src/sdk/main.cpp new file mode 100644 index 000000000..5dc32b3bc --- /dev/null +++ b/src/sdk/main.cpp @@ -0,0 +1,193 @@ +/************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#include "console.h" +#include "constants.h" +#include "commandlineparser.h" +#include "installerbase.h" +#include "sdkapp.h" +#include "updatechecker.h" + +#include <errors.h> +#include <kdselfrestarter.h> +#include <remoteserver.h> +#include <utils.h> + +#include <QCommandLineParser> +#include <QDateTime> +#include <QHostAddress> +#include <QNetworkProxyFactory> + +#include <iostream> + +#define QUOTE_(x) #x +#define QUOTE(x) QUOTE_(x) +#define VERSION "IFW Version: \"" QUOTE(IFW_VERSION) "\"" +#define BUILDDATE "Build date: " QUOTE(__DATE__) +#define SHA "Installer Framework SHA1: \"" QUOTE(_GIT_SHA1_) "\"" +static const char PLACEHOLDER[32] = "MY_InstallerCreateDateTime_MY"; + +int main(int argc, char *argv[]) +{ + // increase maximum numbers of file descriptors +#if defined (Q_OS_OSX) + struct rlimit rl; + getrlimit(RLIMIT_NOFILE, &rl); + rl.rlim_cur = qMin((rlim_t) OPEN_MAX, rl.rlim_max); + setrlimit(RLIMIT_NOFILE, &rl); +#endif + + qsrand(QDateTime::currentDateTime().toTime_t()); + + // We need to start either a command line application or a GUI application. Since we + // fail doing so at least on Linux while parsing the argument using a core application + // object and later starting the GUI application, we now parse the arguments first. + CommandLineParser parser; + parser.parse(QInstaller::parseCommandLineArgs(argc, argv)); + + QStringList mutually; + if (parser.isSet(QLatin1String(CommandLineOptions::CheckUpdates))) + mutually << QLatin1String(CommandLineOptions::CheckUpdates); + if (parser.isSet(QLatin1String(CommandLineOptions::Updater))) + mutually << QLatin1String(CommandLineOptions::Updater); + if (parser.isSet(QLatin1String(CommandLineOptions::ManagePackages))) + mutually << QLatin1String(CommandLineOptions::ManagePackages); + + const bool help = parser.isSet(QLatin1String(CommandLineOptions::HelpShort)) + || parser.isSet(QLatin1String(CommandLineOptions::HelpLong)); + if (help || parser.isSet(QLatin1String(CommandLineOptions::Version)) || mutually.count()) { + Console c; + QCoreApplication app(argc, argv); + + if (parser.isSet(QLatin1String(CommandLineOptions::Version))) { + std::cout << VERSION << std::endl << BUILDDATE << std::endl << SHA << std::endl; + const QDateTime dateTime = QDateTime::fromString(QLatin1String(PLACEHOLDER), + QLatin1String("yyyy-MM-dd - HH:mm:ss")); + if (dateTime.isValid()) + std::cout << "Installer creation time: " << PLACEHOLDER << std::endl; + return EXIT_SUCCESS; + } + + if (mutually.count() > 1) { + std::cerr << qPrintable(QString::fromLatin1("The following options are mutually " + "exclusive: %1.").arg(mutually.join(QLatin1String(", ")))) << std::endl; + } + + std::cout << qPrintable(parser.helpText()) << std::endl; + return help ? EXIT_SUCCESS : EXIT_FAILURE; + } + + if (parser.isSet(QLatin1String(CommandLineOptions::StartServer))) { + const QString argument = parser.value(QLatin1String(CommandLineOptions::StartServer)); + const QString port = argument.section(QLatin1Char(','), 0, 0); + const QString key = argument.section(QLatin1Char(','), 1, 1); + + QStringList missing; + if (port.isEmpty()) + missing << QLatin1String("Port"); + if (key.isEmpty()) + missing << QLatin1String("Key"); + + SDKApp<QCoreApplication> app(argc, argv); + if (missing.count()) { + Console c; + std::cerr << qPrintable(QString::fromLatin1("Missing argument(s) for option " + "'startserver': %2").arg(missing.join(QLatin1String(", ")))) << std::endl; + std::cout << qPrintable(parser.helpText()) << std::endl; + return EXIT_FAILURE; + } + + QInstaller::RemoteServer *server = new QInstaller::RemoteServer; + QObject::connect(server, SIGNAL(destroyed()), &app, SLOT(quit())); + server->init(port.toInt(), QHostAddress::LocalHost, QInstaller::Protocol::Mode::Release); + server->setAuthorizationKey(key); + server->start(); + return app.exec(); + } + + try { + QScopedPointer<Console> console; + if (parser.isSet(QLatin1String(CommandLineOptions::VerboseShort)) + || parser.isSet(QLatin1String(CommandLineOptions::VerboseLong))) { + console.reset(new Console); + QInstaller::setVerbose(true); + } + + // On Windows we need the console window from above, we are a GUI application. + const QStringList unknownOptionNames = parser.unknownOptionNames(); + if (!unknownOptionNames.isEmpty()) { + const QString options = unknownOptionNames.join(QLatin1String(", ")); + std::cerr << "Unknown option: " << qPrintable(options) << std::endl; + } + + if (parser.isSet(QLatin1String(CommandLineOptions::Proxy))) { + // Make sure we honor the system's proxy settings +#if defined(Q_OS_UNIX) && !defined(Q_OS_OSX) + QUrl proxyUrl(QString::fromLatin1(qgetenv("http_proxy"))); + if (proxyUrl.isValid()) { + QNetworkProxy proxy(QNetworkProxy::HttpProxy, proxyUrl.host(), proxyUrl.port(), + proxyUrl.userName(), proxyUrl.password()); + QNetworkProxy::setApplicationProxy(proxy); + } +#else + QNetworkProxyFactory::setUseSystemConfiguration(true); +#endif + } + + if (parser.isSet(QLatin1String(CommandLineOptions::CheckUpdates))) + return UpdateChecker(argc, argv).check(); + + if (QInstaller::isVerbose()) + std::cout << VERSION << std::endl << BUILDDATE << std::endl << SHA << std::endl; + + const KDSelfRestarter restarter(argc, argv); + return InstallerBase(argc, argv).run(); + + } catch (const QInstaller::Error &e) { + std::cerr << qPrintable(e.message()) << std::endl; + } catch (const std::exception &e) { + std::cerr << e.what() << std::endl; + } catch (...) { + std::cerr << "Unknown exception caught." << std::endl; + } + + return EXIT_FAILURE; +} diff --git a/src/sdk/sdk.pro b/src/sdk/sdk.pro index 25aed1b1a..c13576518 100644 --- a/src/sdk/sdk.pro +++ b/src/sdk/sdk.pro @@ -112,13 +112,19 @@ HEADERS += \ settingsdialog.h \ console.h \ sdkapp.h \ - updatechecker.h - -SOURCES = installerbase.cpp \ + updatechecker.h \ + installerbase.h \ + constants.h \ + commandlineparser.h + +SOURCES = \ + main.cpp \ + installerbase.cpp \ tabcontroller.cpp \ installerbasecommons.cpp \ settingsdialog.cpp \ - updatechecker.cpp + updatechecker.cpp \ + commandlineparser.cpp win32 { # Force to overwrite the default manifest file with our own extended version. diff --git a/src/sdk/sdkapp.h b/src/sdk/sdkapp.h index f0095e3bf..a00e2d0e8 100644 --- a/src/sdk/sdkapp.h +++ b/src/sdk/sdkapp.h @@ -42,12 +42,16 @@ #ifndef SDKAPP_H #define SDKAPP_H -#include <QApplication> +#include <binarycontent.h> +#include <binaryformat.h> +#include <fileio.h> +#include <fileutils.h> -#ifdef Q_OS_OSX +#include <QApplication> +#include <QBuffer> #include <QDir> #include <QFileInfo> -#endif +#include <QResource> template<class T> class SDKApp : public T @@ -58,8 +62,14 @@ public: { } - ~SDKApp() + virtual ~SDKApp() { + using namespace QInstaller; + foreach (const QSharedPointer<Resource> &resource, resourceMappings.resources()) { + resource->open(); // ignore error here, either we opened it or it is opened + QResource::unregisterResource((const uchar *) resource->readAll().constData(), + QLatin1String(":/metadata")); + } } bool notify(QObject *receiver, QEvent *event) @@ -74,6 +84,15 @@ public: return false; } + /*! + Returns the installer/ maintenance tool binary. In case of an installer this will be the + installer binary itself, which contains the binary layout and the binary content. In case + of an maintenance tool, it will return a binary that has just a binary layout append. + + Note on OSX: For compatibility reason this function will return the a .dat file located + inside the resource folder in the application bundle, as on OSX the binary layout can not + be appended to the actual installer/ maintenance tool binary itself because of signing. + */ QString binaryFile() const { QString binaryFile = QCoreApplication::applicationFilePath(); @@ -84,10 +103,68 @@ public: QDir resourcePath(QFileInfo(binaryFile).dir()); resourcePath.cdUp(); resourcePath.cd(QLatin1String("Resources")); - binaryFile = resourcePath.filePath(QLatin1String("installer.dat")); + return resourcePath.filePath(QLatin1String("installer.dat")); #endif return binaryFile; } + + /*! + Returns the corresponding .dat file for a given installer/ maintenance tool binary or an + empty string if it fails to find one. + */ + QString datFile(const QString &binaryFile) const + { + QFile file(binaryFile); + QInstaller::openForRead(&file); + const quint64 cookiePos = QInstaller::BinaryContent::findMagicCookie(&file, + QInstaller::BinaryContent::MagicCookie); + if (!file.seek(cookiePos - sizeof(qint64))) // seek to read the marker + return QString(); // ignore error, we will fail later + + const qint64 magicMarker = QInstaller::retrieveInt64(&file); + if (magicMarker == QInstaller::BinaryContent::MagicUninstallerMarker) { + QFileInfo fi(binaryFile); + QString bundlePath; + if (QInstaller::isInBundle(fi.absoluteFilePath(), &bundlePath)) + fi.setFile(bundlePath); + return fi.absoluteDir().filePath(fi.baseName() + QLatin1String(".dat")); + } + return QString(); + } + + QInstaller::ResourceCollection registeredMetaResources() + { + return resourceMappings; + } + + void registerMetaResources(const QInstaller::ResourceCollection &resources) + { + foreach (const QSharedPointer<QInstaller::Resource> &resource, resources.resources()) { + const bool isOpen = resource->isOpen(); + if ((!isOpen) && (!resource->open())) + continue; + + if (!resource->seek(0)) + continue; + + const QByteArray ba = resource->readAll(); + if (ba.isEmpty()) + continue; + + if (QResource::registerResource((const uchar*) ba.data(), QLatin1String(":/metadata"))) { + using namespace QInstaller; + QSharedPointer<QBuffer> buffer(new QBuffer); + buffer->setData(ba); // set the buffers internal data + resourceMappings.appendResource(QSharedPointer<Resource>(new Resource(buffer))); + } + + if (!isOpen) // If we reach that point, either the resource was opened already... + resource->close(); // or we did open it and have to close it again. + } + } + +private: + QInstaller::ResourceCollection resourceMappings; }; #endif // SDKAPP_H diff --git a/src/sdk/updatechecker.cpp b/src/sdk/updatechecker.cpp index 60587942e..2ebe39b92 100644 --- a/src/sdk/updatechecker.cpp +++ b/src/sdk/updatechecker.cpp @@ -40,11 +40,9 @@ **************************************************************************/ #include "updatechecker.h" -#include "sdkapp.h" -#include <binarycontent.h> +#include <binaryformatenginehandler.h> #include <component.h> -#include <constants.h> #include <errors.h> #include <init.h> #include <kdrunoncechecker.h> @@ -55,57 +53,65 @@ #include <iostream> -int UpdateChecker::check(int argc, char *argv[]) +UpdateChecker::UpdateChecker(int &argc, char *argv[]) + : SDKApp<QCoreApplication>(argc, argv) { - try { - SDKApp<QCoreApplication> app(argc, argv); - - KDRunOnceChecker runCheck((QLatin1String("lockmyApp15021976.lock"))); - if (runCheck.isRunning(KDRunOnceChecker::Lockfile)) - throw QInstaller::Error(QLatin1String("An instance is already checking for updates.")); - - QInstaller::init(); // register custom operations - - const QInstaller::BinaryContent content = - QInstaller::BinaryContent::readAndRegisterFromBinary(app.binaryFile()); - if (content.magicMarker() != QInstaller::BinaryContent::MagicInstallerMarker) - throw QInstaller::Error(QLatin1String("Installers cannot check for updates.")); - - QInstaller::PackageManagerCore core(QInstaller::BinaryContent::MagicUpdaterMarker, content - .performedOperations()); - ProductKeyCheck::instance()->init(&core); - QInstaller::PackageManagerCore::setVirtualComponentsVisible(true); - - if (!core.fetchRemotePackagesTree()) - throw QInstaller::Error(core.error()); - - const QList<QInstaller::Component *> components = core.updaterComponents(); - if (components.isEmpty()) - throw QInstaller::Error(QLatin1String("There are currently no updates available.")); - - QDomDocument doc; - QDomElement root = doc.createElement(QLatin1String("updates")); - doc.appendChild(root); - - foreach (QInstaller::Component *component, components) { - QDomElement update = doc.createElement(QLatin1String("update")); - update.setAttribute(QLatin1String("name"), - component->value(QInstaller::scDisplayName)); - update.setAttribute(QLatin1String("version"), - component->value(QInstaller::scRemoteVersion)); - update.setAttribute(QLatin1String("size"), - component->value(QInstaller::scUncompressedSize)); - root.appendChild(update); - } - - std::cout << qPrintable(doc.toString(4)) << std::endl; - return EXIT_SUCCESS; - } catch (const QInstaller::Error &e) { - std::cerr << qPrintable(e.message()) << std::endl; - } catch (const std::exception &e) { - std::cerr << e.what() << std::endl; - } catch (...) { - std::cerr << "Unknown exception caught." << std::endl; + QInstaller::init(); // register custom operations +} + +int UpdateChecker::check() +{ + KDRunOnceChecker runCheck((QLatin1String("lockmyApp15021976.lock"))); + if (runCheck.isRunning(KDRunOnceChecker::Lockfile)) + throw QInstaller::Error(QLatin1String("An instance is already checking for updates.")); + + QString fileName = datFile(binaryFile()); + quint64 cookie = QInstaller::BinaryContent::MagicCookieDat; + if (fileName.isEmpty()) { + fileName = binaryFile(); + cookie = QInstaller::BinaryContent::MagicCookie; } - return EXIT_FAILURE; + + QSharedPointer<QFile> binary(new QFile(fileName)); + QInstaller::openForRead(binary.data()); + + qint64 magicMarker; + QInstaller::ResourceCollection resources; + QList<QInstaller::OperationBlob> operations; + QInstaller::ResourceCollectionManager manager; + QInstaller::BinaryContent::readBinaryContent(binary, &resources, &operations, &manager, + &magicMarker, cookie); + + if (magicMarker != QInstaller::BinaryContent::MagicInstallerMarker) + throw QInstaller::Error(QLatin1String("Installers cannot check for updates.")); + + registerMetaResources(resources); // the base class will unregister the resources + + // instantiate the installer we are actually going to use + QInstaller::PackageManagerCore core(QInstaller::BinaryContent::MagicUpdaterMarker, operations); + QInstaller::BinaryFormatEngineHandler::instance()->registerResources(manager.collections()); + QInstaller::PackageManagerCore::setVirtualComponentsVisible(true); + QInstaller::ProductKeyCheck::instance()->init(&core); + + if (!core.fetchRemotePackagesTree()) + throw QInstaller::Error(core.error()); + + const QList<QInstaller::Component *> components = core.updaterComponents(); + if (components.isEmpty()) + throw QInstaller::Error(QLatin1String("There are currently no updates available.")); + + QDomDocument doc; + QDomElement root = doc.createElement(QLatin1String("updates")); + doc.appendChild(root); + + foreach (QInstaller::Component *component, components) { + QDomElement update = doc.createElement(QLatin1String("update")); + update.setAttribute(QLatin1String("name"), component->value(QInstaller::scDisplayName)); + update.setAttribute(QLatin1String("version"), component->value(QInstaller::scRemoteVersion)); + update.setAttribute(QLatin1String("size"), component->value(QInstaller::scUncompressedSize)); + root.appendChild(update); + } + + std::cout << qPrintable(doc.toString(4)) << std::endl; + return EXIT_SUCCESS; } diff --git a/src/sdk/updatechecker.h b/src/sdk/updatechecker.h index 504e97604..19b34e29b 100644 --- a/src/sdk/updatechecker.h +++ b/src/sdk/updatechecker.h @@ -42,11 +42,16 @@ #ifndef UPDATECHECKER_H #define UPDATECHECKER_H -class UpdateChecker +#include "sdkapp.h" + +class UpdateChecker : public SDKApp<QCoreApplication> { + Q_OBJECT + Q_DISABLE_COPY(UpdateChecker) + public: - UpdateChecker() {} - int check(int argc, char *argv[]); + UpdateChecker(int &argc, char *argv[]); + int check(); }; #endif // UPDATECHECKER_H diff --git a/tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp b/tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp index e82162444..304f6376b 100644 --- a/tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp +++ b/tests/auto/installer/packagemanagercore/tst_packagemanagercore.cpp @@ -104,7 +104,8 @@ private slots: setIgnoreMessage(testDirectory); - PackageManagerCore core(QInstaller::BinaryContent::MagicInstallerMarker); + PackageManagerCore core(QInstaller::BinaryContent::MagicInstallerMarker, + QList<QInstaller::OperationBlob>()); // cancel the installer in error case core.autoRejectMessageBoxes(); core.appendRootComponent(new DummyComponent(&core)); @@ -132,7 +133,8 @@ private slots: setIgnoreMessage(testDirectory); - PackageManagerCore core(QInstaller::BinaryContent::MagicInstallerMarker); + PackageManagerCore core(QInstaller::BinaryContent::MagicInstallerMarker, + QList<QInstaller::OperationBlob>()); // cancel the installer in error case core.autoRejectMessageBoxes(); core.appendRootComponent(new DummyComponent(&core)); diff --git a/tools/devtool/main.cpp b/tools/devtool/main.cpp index d31870e67..0a9c677d2 100644 --- a/tools/devtool/main.cpp +++ b/tools/devtool/main.cpp @@ -143,23 +143,12 @@ int main(int argc, char *argv[]) if (!file->seek(operationsStart)) throw QInstaller::Error(QLatin1String("Could not seek to operation list.")); - QList<QInstaller::Operation *> performedOperations; + QList<QInstaller::OperationBlob> performedOperations; const qint64 operationsCount = QInstaller::retrieveInt64(file); for (int i = 0; i < operationsCount; ++i) { const QString name = QInstaller::retrieveString(file); const QString data = QInstaller::retrieveString(file); - - QScopedPointer<QInstaller::Operation> op(KDUpdater::UpdateOperationFactory::instance() - .create(name)); - if (op.isNull()) { - throw QInstaller::Error(QString::fromLatin1("Could not load unknown operation %1") - .arg(name)); - } - if (!op->fromXml(data)) { - throw QInstaller::Error(QString::fromLatin1("Could not load XML for operation: %1") - .arg(name)); - } - performedOperations.append(op.take()); + performedOperations.append(QInstaller::OperationBlob(name, data)); } // seek to the position of the resource collections segment info diff --git a/tools/devtool/operationrunner.cpp b/tools/devtool/operationrunner.cpp index 55c1160e2..100dcf2cc 100644 --- a/tools/devtool/operationrunner.cpp +++ b/tools/devtool/operationrunner.cpp @@ -49,7 +49,8 @@ #include <iostream> -OperationRunner::OperationRunner(qint64 magicMarker, const QInstaller::OperationList &oldOperations) +OperationRunner::OperationRunner(qint64 magicMarker, + const QList<QInstaller::OperationBlob> &oldOperations) : m_core(new QInstaller::PackageManagerCore(magicMarker, oldOperations)) { // We need a package manager core as some operations expect them to be available. diff --git a/tools/devtool/operationrunner.h b/tools/devtool/operationrunner.h index 73964c3a5..d4026dadc 100644 --- a/tools/devtool/operationrunner.h +++ b/tools/devtool/operationrunner.h @@ -42,11 +42,12 @@ #ifndef OPERATIONRUNNER_H #define OPERATIONRUNNER_H -#include <qinstallerglobal.h> +#include <binaryformat.h> #include <QObject> namespace QInstaller { + struct OperationBlob; class PackageManagerCore; } @@ -61,7 +62,7 @@ public: Undo }; - OperationRunner(qint64 magicMarker, const QInstaller::OperationList &oldOperations); + OperationRunner(qint64 magicMarker, const QList<QInstaller::OperationBlob> &oldOperations); ~OperationRunner(); int runOperation(QStringList arguments, RunMode mode); |