summaryrefslogtreecommitdiffstats
path: root/src/libs/installer
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/installer')
-rw-r--r--src/libs/installer/addqtcreatorarrayvalueoperation.cpp171
-rw-r--r--src/libs/installer/addqtcreatorarrayvalueoperation.h54
-rw-r--r--src/libs/installer/adminauthorization.cpp48
-rw-r--r--src/libs/installer/adminauthorization.h83
-rw-r--r--src/libs/installer/adminauthorization_mac.cpp120
-rw-r--r--src/libs/installer/adminauthorization_win.cpp103
-rw-r--r--src/libs/installer/adminauthorization_x11.cpp252
-rw-r--r--src/libs/installer/binaryformat.cpp1115
-rw-r--r--src/libs/installer/binaryformat.h248
-rw-r--r--src/libs/installer/binaryformatengine.cpp297
-rw-r--r--src/libs/installer/binaryformatengine.h78
-rw-r--r--src/libs/installer/binaryformatenginehandler.cpp116
-rw-r--r--src/libs/installer/binaryformatenginehandler.h66
-rw-r--r--src/libs/installer/component.cpp1207
-rw-r--r--src/libs/installer/component.h233
-rw-r--r--src/libs/installer/component_p.cpp359
-rw-r--r--src/libs/installer/component_p.h157
-rw-r--r--src/libs/installer/componentmodel.cpp528
-rw-r--r--src/libs/installer/componentmodel.h115
-rw-r--r--src/libs/installer/constants.h82
-rw-r--r--src/libs/installer/copydirectoryoperation.cpp159
-rw-r--r--src/libs/installer/copydirectoryoperation.h61
-rw-r--r--src/libs/installer/createdesktopentryoperation.cpp204
-rw-r--r--src/libs/installer/createdesktopentryoperation.h57
-rw-r--r--src/libs/installer/createlocalrepositoryoperation.cpp384
-rw-r--r--src/libs/installer/createlocalrepositoryoperation.h68
-rw-r--r--src/libs/installer/createshortcutoperation.cpp219
-rw-r--r--src/libs/installer/createshortcutoperation.h54
-rw-r--r--src/libs/installer/downloadarchivesjob.cpp340
-rw-r--r--src/libs/installer/downloadarchivesjob.h104
-rw-r--r--src/libs/installer/elevatedexecuteoperation.cpp276
-rw-r--r--src/libs/installer/elevatedexecuteoperation.h70
-rw-r--r--src/libs/installer/environmentvariablesoperation.cpp230
-rw-r--r--src/libs/installer/environmentvariablesoperation.h54
-rw-r--r--src/libs/installer/errors.h59
-rw-r--r--src/libs/installer/extractarchiveoperation.cpp149
-rw-r--r--src/libs/installer/extractarchiveoperation.h70
-rw-r--r--src/libs/installer/extractarchiveoperation_p.h231
-rw-r--r--src/libs/installer/fakestopprocessforupdateoperation.cpp129
-rw-r--r--src/libs/installer/fakestopprocessforupdateoperation.h54
-rw-r--r--src/libs/installer/fileutils.cpp507
-rw-r--r--src/libs/installer/fileutils.h114
-rw-r--r--src/libs/installer/fsengineclient.cpp818
-rw-r--r--src/libs/installer/fsengineclient.h75
-rw-r--r--src/libs/installer/fsengineserver.cpp595
-rw-r--r--src/libs/installer/fsengineserver.h62
-rw-r--r--src/libs/installer/getrepositoriesmetainfojob.cpp195
-rw-r--r--src/libs/installer/getrepositoriesmetainfojob.h96
-rw-r--r--src/libs/installer/getrepositorymetainfojob.cpp512
-rw-r--r--src/libs/installer/getrepositorymetainfojob.h113
-rw-r--r--src/libs/installer/globalsettingsoperation.cpp123
-rw-r--r--src/libs/installer/globalsettingsoperation.h59
-rw-r--r--src/libs/installer/init.cpp244
-rw-r--r--src/libs/installer/init.h44
-rw-r--r--src/libs/installer/installer.pro179
-rw-r--r--src/libs/installer/installer_global.h41
-rw-r--r--src/libs/installer/installiconsoperation.cpp281
-rw-r--r--src/libs/installer/installiconsoperation.h64
-rw-r--r--src/libs/installer/lazyplaintextedit.cpp85
-rw-r--r--src/libs/installer/lazyplaintextedit.h55
-rw-r--r--src/libs/installer/lib7z_facade.cpp1372
-rw-r--r--src/libs/installer/lib7z_facade.h242
-rw-r--r--src/libs/installer/licenseoperation.cpp119
-rw-r--r--src/libs/installer/licenseoperation.h54
-rw-r--r--src/libs/installer/linereplaceoperation.cpp112
-rw-r--r--src/libs/installer/linereplaceoperation.h54
-rw-r--r--src/libs/installer/macrelocateqt.cpp94
-rw-r--r--src/libs/installer/macrelocateqt.h55
-rw-r--r--src/libs/installer/macreplaceinstallnamesoperation.cpp273
-rw-r--r--src/libs/installer/macreplaceinstallnamesoperation.h67
-rw-r--r--src/libs/installer/messageboxhandler.cpp309
-rw-r--r--src/libs/installer/messageboxhandler.h131
-rw-r--r--src/libs/installer/minimumprogressoperation.cpp68
-rw-r--r--src/libs/installer/minimumprogressoperation.h61
-rw-r--r--src/libs/installer/operationrunner.cpp165
-rw-r--r--src/libs/installer/operationrunner.h60
-rw-r--r--src/libs/installer/packagemanagercore.cpp1845
-rw-r--r--src/libs/installer/packagemanagercore.h308
-rw-r--r--src/libs/installer/packagemanagercore_p.cpp2326
-rw-r--r--src/libs/installer/packagemanagercore_p.h260
-rw-r--r--src/libs/installer/packagemanagergui.cpp1983
-rw-r--r--src/libs/installer/packagemanagergui.h418
-rw-r--r--src/libs/installer/packagemanagerproxyfactory.cpp79
-rw-r--r--src/libs/installer/packagemanagerproxyfactory.h56
-rw-r--r--src/libs/installer/performinstallationform.cpp189
-rw-r--r--src/libs/installer/performinstallationform.h87
-rw-r--r--src/libs/installer/persistentsettings.cpp212
-rw-r--r--src/libs/installer/persistentsettings.h75
-rw-r--r--src/libs/installer/progresscoordinator.cpp275
-rw-r--r--src/libs/installer/progresscoordinator.h96
-rw-r--r--src/libs/installer/projectexplorer_export.h1
-rw-r--r--src/libs/installer/qinstallerglobal.h92
-rw-r--r--src/libs/installer/qprocesswrapper.cpp413
-rw-r--r--src/libs/installer/qprocesswrapper.h127
-rw-r--r--src/libs/installer/qsettingswrapper.cpp366
-rw-r--r--src/libs/installer/qsettingswrapper.h107
-rw-r--r--src/libs/installer/qtcreator_constants.h77
-rw-r--r--src/libs/installer/qtcreatorpersistentsettings.cpp227
-rw-r--r--src/libs/installer/qtcreatorpersistentsettings.h79
-rw-r--r--src/libs/installer/qtpatch.cpp233
-rw-r--r--src/libs/installer/qtpatch.h60
-rw-r--r--src/libs/installer/qtpatchoperation.cpp343
-rw-r--r--src/libs/installer/qtpatchoperation.h54
-rw-r--r--src/libs/installer/range.h88
-rw-r--r--src/libs/installer/registerdefaultdebuggeroperation.cpp160
-rw-r--r--src/libs/installer/registerdefaultdebuggeroperation.h54
-rw-r--r--src/libs/installer/registerdocumentationoperation.cpp153
-rw-r--r--src/libs/installer/registerdocumentationoperation.h54
-rw-r--r--src/libs/installer/registerfiletypeoperation.cpp207
-rw-r--r--src/libs/installer/registerfiletypeoperation.h54
-rw-r--r--src/libs/installer/registerqtoperation.cpp224
-rw-r--r--src/libs/installer/registerqtoperation.h54
-rw-r--r--src/libs/installer/registerqtv23operation.cpp231
-rw-r--r--src/libs/installer/registerqtv23operation.h54
-rw-r--r--src/libs/installer/registerqtv2operation.cpp201
-rw-r--r--src/libs/installer/registerqtv2operation.h54
-rw-r--r--src/libs/installer/registertoolchainoperation.cpp178
-rw-r--r--src/libs/installer/registertoolchainoperation.h72
-rw-r--r--src/libs/installer/replaceoperation.cpp106
-rw-r--r--src/libs/installer/replaceoperation.h54
-rw-r--r--src/libs/installer/repository.cpp213
-rw-r--r--src/libs/installer/repository.h97
-rw-r--r--src/libs/installer/resources/files-to-patch-linux72
-rw-r--r--src/libs/installer/resources/files-to-patch-linux-emb-arm70
-rw-r--r--src/libs/installer/resources/files-to-patch-macx61
-rw-r--r--src/libs/installer/resources/files-to-patch-windows60
-rw-r--r--src/libs/installer/resources/patch_file_lists.qrc7
-rw-r--r--src/libs/installer/selfrestartoperation.cpp88
-rw-r--r--src/libs/installer/selfrestartoperation.h54
-rw-r--r--src/libs/installer/setdemospathonqtoperation.cpp126
-rw-r--r--src/libs/installer/setdemospathonqtoperation.h54
-rw-r--r--src/libs/installer/setexamplespathonqtoperation.cpp127
-rw-r--r--src/libs/installer/setexamplespathonqtoperation.h54
-rw-r--r--src/libs/installer/setimportspathonqtcoreoperation.cpp151
-rw-r--r--src/libs/installer/setimportspathonqtcoreoperation.h54
-rw-r--r--src/libs/installer/setpathonqtcoreoperation.cpp175
-rw-r--r--src/libs/installer/setpathonqtcoreoperation.h54
-rw-r--r--src/libs/installer/setpluginpathonqtcoreoperation.cpp149
-rw-r--r--src/libs/installer/setpluginpathonqtcoreoperation.h54
-rw-r--r--src/libs/installer/setqtcreatorvalueoperation.cpp137
-rw-r--r--src/libs/installer/setqtcreatorvalueoperation.h54
-rw-r--r--src/libs/installer/settings.cpp523
-rw-r--r--src/libs/installer/settings.h141
-rw-r--r--src/libs/installer/simplemovefileoperation.cpp113
-rw-r--r--src/libs/installer/simplemovefileoperation.h61
-rw-r--r--src/libs/installer/templates.cpp176
-rw-r--r--src/libs/installer/updatecreatorsettingsfrom21to22operation.cpp325
-rw-r--r--src/libs/installer/updatecreatorsettingsfrom21to22operation.h54
-rw-r--r--src/libs/installer/updater.cpp97
-rw-r--r--src/libs/installer/updater.h55
-rw-r--r--src/libs/installer/updatesettings.cpp164
-rw-r--r--src/libs/installer/updatesettings.h85
-rw-r--r--src/libs/installer/utils.cpp341
-rw-r--r--src/libs/installer/utils.h92
-rw-r--r--src/libs/installer/zipjob.cpp206
-rw-r--r--src/libs/installer/zipjob.h100
156 files changed, 32732 insertions, 0 deletions
diff --git a/src/libs/installer/addqtcreatorarrayvalueoperation.cpp b/src/libs/installer/addqtcreatorarrayvalueoperation.cpp
new file mode 100644
index 000000000..23ca7a21a
--- /dev/null
+++ b/src/libs/installer/addqtcreatorarrayvalueoperation.cpp
@@ -0,0 +1,171 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "addqtcreatorarrayvalueoperation.h"
+
+#include "constants.h"
+#include "qtcreator_constants.h"
+#include "packagemanagercore.h"
+
+#include <QtCore/QSettings>
+#include <QtCore/QSet>
+
+using namespace QInstaller;
+
+static QString groupName(const QString &groupName)
+{
+ return groupName == QLatin1String("General") ? QString() : groupName;
+}
+
+AddQtCreatorArrayValueOperation::AddQtCreatorArrayValueOperation()
+{
+ setName(QLatin1String("AddQtCreatorArrayValue"));
+}
+
+void AddQtCreatorArrayValueOperation::backup()
+{
+}
+
+bool AddQtCreatorArrayValueOperation::performOperation()
+{
+ const QStringList args = arguments();
+
+ if (args.count() != 4) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, exactly 4 expected (group, "
+ "arrayname, key, value).").arg(name()).arg( arguments().count()));
+ return false;
+ }
+
+
+ PackageManagerCore *const core = qVariantValue<PackageManagerCore*>(value(QLatin1String("installer")));
+ if (!core) {
+ setError(UserDefinedError);
+ setErrorString(tr("Needed installer object in %1 operation is empty.").arg(name()));
+ return false;
+ }
+ const QString &rootInstallPath = core->value(scTargetDir);
+
+ QSettings settings(rootInstallPath + QLatin1String(QtCreatorSettingsSuffixPath),
+ QSettings::IniFormat);
+
+ const QString &group = groupName(args.at(0));
+ const QString &arrayName = args.at(1);
+ const QString &key = args.at(2);
+ const QString &value = args.at(3);
+
+ if (!group.isEmpty())
+ settings.beginGroup(group);
+
+ QList<QString> oldArrayValues;
+ int arraySize = settings.beginReadArray(arrayName);
+ for (int i = 0; i < arraySize; ++i) {
+ settings.setArrayIndex(i);
+ //if it is already there we have nothing todo
+ if (settings.value(key).toString() == value)
+ return true;
+ oldArrayValues.append(settings.value(key).toString());
+ }
+ settings.endArray();
+
+
+ settings.beginWriteArray(arrayName);
+
+ for (int i = 0; i < oldArrayValues.size(); ++i) {
+ settings.setArrayIndex(i);
+ settings.setValue(key, oldArrayValues.value(i));
+ }
+ settings.setArrayIndex(oldArrayValues.size()); //means next index after the last insert
+ settings.setValue(key, value);
+ settings.endArray();
+
+ settings.sync(); //be save ;)
+ setValue(QLatin1String("ArrayValueSet"), true);
+ return true;
+}
+
+bool AddQtCreatorArrayValueOperation::undoOperation()
+{
+ if (value(QLatin1String("ArrayValueSet")).isNull())
+ return true;
+ const QStringList args = arguments();
+
+ PackageManagerCore *const core = qVariantValue<PackageManagerCore*>(value(QLatin1String("installer")));
+ if (!core) {
+ setError(UserDefinedError);
+ setErrorString(tr("Needed installer object in %1 operation is empty.").arg(name()));
+ return false;
+ }
+ const QString &rootInstallPath = core->value(scTargetDir);
+
+ QSettings settings(rootInstallPath + QLatin1String(QtCreatorSettingsSuffixPath),
+ QSettings::IniFormat);
+
+ const QString &group = groupName(args.at(0));
+ const QString &arrayName = args.at(1);
+ const QString &key = args.at(2);
+ const QString &value = args.at(3);
+
+ if (!group.isEmpty())
+ settings.beginGroup(group);
+
+ QList<QString> oldArrayValues;
+ int arraySize = settings.beginReadArray(arrayName);
+ for (int i = 0; i < arraySize; ++i) {
+ settings.setArrayIndex(i);
+ if (settings.value(key).toString() != value)
+ oldArrayValues.append(settings.value(key).toString());
+ }
+ settings.endArray();
+
+
+ settings.beginWriteArray(arrayName);
+
+ for (int i = 0; i < oldArrayValues.size(); ++i) {
+ settings.setArrayIndex(i);
+ settings.setValue(key, oldArrayValues.value(i));
+ }
+ settings.endArray();
+
+ settings.sync(); //be save ;)
+ return true;
+}
+
+bool AddQtCreatorArrayValueOperation::testOperation()
+{
+ return true;
+}
+
+Operation *AddQtCreatorArrayValueOperation::clone() const
+{
+ return new AddQtCreatorArrayValueOperation();
+}
diff --git a/src/libs/installer/addqtcreatorarrayvalueoperation.h b/src/libs/installer/addqtcreatorarrayvalueoperation.h
new file mode 100644
index 000000000..156560422
--- /dev/null
+++ b/src/libs/installer/addqtcreatorarrayvalueoperation.h
@@ -0,0 +1,54 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef ADDQTCREATORVALUEOPERATION_H
+#define ADDQTCREATORVALUEOPERATION_H
+
+#include "qinstallerglobal.h"
+
+namespace QInstaller {
+
+class AddQtCreatorArrayValueOperation : public Operation
+{
+public:
+ AddQtCreatorArrayValueOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+};
+
+} // namespace QInstaller
+
+#endif // ADDQTCREATORVALUEOPERATION_H
diff --git a/src/libs/installer/adminauthorization.cpp b/src/libs/installer/adminauthorization.cpp
new file mode 100644
index 000000000..8b87d60c1
--- /dev/null
+++ b/src/libs/installer/adminauthorization.cpp
@@ -0,0 +1,48 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "adminauthorization.h"
+
+AdminAuthorizationBase::AdminAuthorizationBase()
+ : m_authorized(false)
+{
+}
+
+bool AdminAuthorizationBase::isAuthorized() const
+{
+ return m_authorized;
+}
+
+void AdminAuthorizationBase::setAuthorized()
+{
+ m_authorized = true;
+}
diff --git a/src/libs/installer/adminauthorization.h b/src/libs/installer/adminauthorization.h
new file mode 100644
index 000000000..96180a30d
--- /dev/null
+++ b/src/libs/installer/adminauthorization.h
@@ -0,0 +1,83 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef ADMINAUTHORIZATION_H
+#define ADMINAUTHORIZATION_H
+
+#include <QtCore/QObject>
+
+class AdminAuthorizationBase
+{
+protected:
+ AdminAuthorizationBase();
+
+public:
+ virtual ~AdminAuthorizationBase() {}
+
+ virtual bool authorize() = 0;
+ bool isAuthorized() const;
+
+protected:
+ void setAuthorized();
+
+private:
+ bool m_authorized;
+};
+
+class AdminAuthorization : public QObject, public AdminAuthorizationBase
+{
+ Q_OBJECT
+ Q_PROPERTY(bool authorized READ isAuthorized)
+
+public:
+ AdminAuthorization();
+#ifdef Q_OS_MAC
+ ~AdminAuthorization();
+#endif
+
+ bool execute(QWidget *dialogParent, const QString &programs, const QStringList &arguments);
+ static bool hasAdminRights();
+
+public Q_SLOTS:
+ bool authorize();
+
+Q_SIGNALS:
+ void authorized();
+
+#ifdef Q_OS_MAC
+private:
+ class Private;
+ Private *d;
+#endif
+};
+
+#endif // ADMINAUTHORIZATION_H
diff --git a/src/libs/installer/adminauthorization_mac.cpp b/src/libs/installer/adminauthorization_mac.cpp
new file mode 100644
index 000000000..ce0a17095
--- /dev/null
+++ b/src/libs/installer/adminauthorization_mac.cpp
@@ -0,0 +1,120 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "adminauthorization.h"
+
+#include <Security/Authorization.h>
+#include <Security/AuthorizationTags.h>
+
+#include <QtCore/QStringList>
+#include <QtCore/QVector>
+
+#include <unistd.h>
+
+
+// -- AdminAuthorization::Private
+
+class AdminAuthorization::Private
+{
+public:
+ Private() : auth(0) { }
+
+ AuthorizationRef auth;
+};
+
+
+// -- AdminAuthorization
+
+AdminAuthorization::AdminAuthorization()
+ : d(new Private)
+{
+ AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &d->auth);
+}
+
+AdminAuthorization::~AdminAuthorization()
+{
+ AuthorizationFree(d->auth, kAuthorizationFlagDestroyRights);
+ delete d;
+}
+
+bool AdminAuthorization::authorize()
+{
+ if (geteuid() == 0)
+ setAuthorized();
+
+ if (isAuthorized())
+ return true;
+
+ AuthorizationItem item;
+ item.name = kAuthorizationRightExecute;
+ item.valueLength = 0;
+ item.value = NULL;
+ item.flags = 0;
+
+ AuthorizationRights rights;
+ rights.count = 1;
+ rights.items = &item;
+
+ const AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed
+ | kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights;
+
+ const OSStatus result = AuthorizationCopyRights(d->auth, &rights, kAuthorizationEmptyEnvironment,
+ flags, 0);
+ if (result != errAuthorizationSuccess)
+ return false;
+
+ seteuid(0);
+ setAuthorized();
+ emit authorized();
+ return true;
+}
+
+bool AdminAuthorization::execute(QWidget *, const QString &program, const QStringList &arguments)
+{
+ QVector<char *> args;
+ QVector<QByteArray> utf8Args;
+ foreach (const QString &argument, arguments) {
+ utf8Args.push_back(argument.toUtf8());
+ args.push_back(utf8Args.last().data());
+ }
+ args.push_back(0);
+
+ const QByteArray utf8Program = program.toUtf8();
+ const OSStatus result = AuthorizationExecuteWithPrivileges(d->auth, utf8Program.data(),
+ kAuthorizationFlagDefaults, args.data(), 0);
+ return result == errAuthorizationSuccess;
+}
+
+bool AdminAuthorization::hasAdminRights()
+{
+ return geteuid() == 0;
+}
diff --git a/src/libs/installer/adminauthorization_win.cpp b/src/libs/installer/adminauthorization_win.cpp
new file mode 100644
index 000000000..7cfd6b3dc
--- /dev/null
+++ b/src/libs/installer/adminauthorization_win.cpp
@@ -0,0 +1,103 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#include "adminauthorization.h"
+
+#include "utils.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QStringList>
+#include <QtCore/QVector>
+
+#include <windows.h>
+
+AdminAuthorization::AdminAuthorization()
+{
+}
+
+bool AdminAuthorization::authorize()
+{
+ setAuthorized();
+ emit authorized();
+ return true;
+}
+
+bool AdminAuthorization::hasAdminRights()
+{
+ SID_IDENTIFIER_AUTHORITY authority = SECURITY_NT_AUTHORITY;
+ PSID adminGroup;
+ // Initialize SID.
+ if (!AllocateAndInitializeSid(&authority,
+ 2,
+ SECURITY_BUILTIN_DOMAIN_RID,
+ DOMAIN_ALIAS_RID_ADMINS,
+ 0, 0, 0, 0, 0, 0,
+ &adminGroup))
+ return false;
+
+ BOOL isInAdminGroup = FALSE;
+ if (!CheckTokenMembership(0, adminGroup, &isInAdminGroup))
+ isInAdminGroup = FALSE;
+
+ FreeSid(adminGroup);
+ return isInAdminGroup;
+}
+
+bool AdminAuthorization::execute(QWidget *, const QString &program, const QStringList &arguments)
+{
+ const QString file = QDir::toNativeSeparators(program);
+ const QString args = QInstaller::createCommandline(QString(), arguments);
+
+ const int len = GetShortPathNameW((wchar_t *)file.utf16(), 0, 0);
+ if (len == 0)
+ return false;
+ wchar_t *const buffer = new wchar_t[len];
+ GetShortPathName((wchar_t *)file.utf16(), buffer, len);
+
+ SHELLEXECUTEINFOW TempInfo = { 0 };
+ TempInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
+ TempInfo.fMask = 0;
+ TempInfo.hwnd = 0;
+ TempInfo.lpVerb = L"runas";
+ TempInfo.lpFile = buffer;
+ TempInfo.lpParameters = (wchar_t *)args.utf16();
+ TempInfo.lpDirectory = 0;
+ TempInfo.nShow = SW_NORMAL;
+
+
+ qDebug() << QString::fromLatin1(" starting elevated process %1 %2 with ::ShellExecuteExW( &TempInfo );"
+ ).arg(program, arguments.join(QLatin1String(" ")));
+ const bool result = ::ShellExecuteExW(&TempInfo);
+ qDebug() << QLatin1String("after starting elevated process");
+ delete[] buffer;
+ return result;
+}
diff --git a/src/libs/installer/adminauthorization_x11.cpp b/src/libs/installer/adminauthorization_x11.cpp
new file mode 100644
index 000000000..592f56ef7
--- /dev/null
+++ b/src/libs/installer/adminauthorization_x11.cpp
@@ -0,0 +1,252 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "adminauthorization.h"
+
+#include <QtCore/QFile>
+
+#include <QtGui/QApplication>
+#include <QtGui/QInputDialog>
+#include <QtGui/QMessageBox>
+
+#include <cstdlib>
+#include <unistd.h>
+#include <fcntl.h>
+
+#ifdef Q_OS_LINUX
+#include <linux/limits.h>
+#include <pty.h>
+#else
+#include <util.h>
+#endif
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <iostream>
+
+#define SU_COMMAND "/usr/bin/sudo"
+//#define SU_COMMAND "/bin/echo"
+
+AdminAuthorization::AdminAuthorization()
+{
+}
+
+bool AdminAuthorization::authorize()
+{
+ return true;
+}
+
+static QString getPassword(QWidget *)
+{
+ if (QApplication::type() == QApplication::GuiClient) {
+ bool ok = false;
+ const QString result = QInputDialog::getText(0, QObject::tr( "Authorization required" ), QObject::tr( "Enter your password to authorize for sudo:" ), QLineEdit::Password, QString(), &ok);
+ return ok ? result : QString();
+ } else {
+ std::cout << QObject::tr("Authorization required").toStdString() << std::endl;
+ std::cout << QObject::tr("Enter your password to authorize for sudo:").toStdString() << std::endl;
+ std::string password;
+ std::cin >> password;
+ return QString::fromStdString(password);
+ }
+}
+
+static void printError(QWidget *parent, const QString &value)
+{
+ if (QApplication::type() == QApplication::GuiClient)
+ QMessageBox::critical(parent, QObject::tr( "Error acquiring admin rights" ), value, QMessageBox::Ok, QMessageBox::Ok);
+ else
+ std::cout << value.toStdString() << std::endl;
+}
+
+bool AdminAuthorization::execute(QWidget *parent, const QString &program, const QStringList &arguments)
+{
+ // as we cannot pipe the password to su in QProcess, we need to setup a pseudo-terminal for it
+ int masterFD = -1;
+ int slaveFD = -1;
+ char ptsn[ PATH_MAX ];
+
+ if (::openpty(&masterFD, &slaveFD, ptsn, 0, 0))
+ return false;
+
+ masterFD = ::posix_openpt(O_RDWR | O_NOCTTY);
+ if (masterFD < 0)
+ return false;
+
+ const QByteArray ttyName = ::ptsname(masterFD);
+
+ if (::grantpt(masterFD)) {
+ ::close(masterFD);
+ return false;
+ }
+
+ ::revoke(ttyName);
+ ::unlockpt(masterFD);
+
+ slaveFD = ::open(ttyName, O_RDWR | O_NOCTTY);
+ if (slaveFD < 0) {
+ ::close(masterFD);
+ return false;
+ }
+
+ ::fcntl(masterFD, F_SETFD, FD_CLOEXEC);
+ ::fcntl(slaveFD, F_SETFD, FD_CLOEXEC);
+ int pipedData[2];
+ if (pipe(pipedData) != 0)
+ return false;
+
+ int flags = ::fcntl(pipedData[0], F_GETFD);
+ if (flags != -1)
+ ::fcntl(pipedData[0], F_SETFL, flags | O_NONBLOCK);
+
+ pid_t child = fork();
+
+ if (child < -1) {
+ ::close(masterFD);
+ ::close(slaveFD);
+ ::close(pipedData[0]);
+ ::close(pipedData[1]);
+ return false;
+ }
+
+ // parent process
+ else if (child > 0) {
+ ::close(slaveFD);
+ //close writing end of pipe
+ ::close(pipedData[1]);
+
+ QRegExp re(QLatin1String("[Pp]assword.*:"));
+ QByteArray errData;
+ flags = ::fcntl(masterFD, F_GETFD);
+// if (flags != -1)
+// ::fcntl(masterFD, F_SETFL, flags | O_NONBLOCK);
+ int bytes = 0;
+ int errBytes = 0;
+ char buf[1024];
+ while (bytes >= 0) {
+ int state;
+ if (::waitpid(child, &state, WNOHANG) == -1)
+ break;
+ bytes = ::read(masterFD, buf, 1023);
+ errBytes = ::read(pipedData[0], buf, 1023);
+ if (errBytes > 0)
+ errData.append(buf, errBytes);
+ if (bytes > 0) {
+ const QString line = QString::fromLatin1(buf, bytes);
+ if (re.indexIn(line) != -1) {
+ const QString password = getPassword(parent);
+ if (password == QString()) {
+ QByteArray pwd = password.toLatin1();
+ for (int i = 0; i < 3; ++i) {
+ ::write(masterFD, pwd.data(), pwd.length());
+ ::write(masterFD, "\n", 1);
+ }
+ return false;
+ }
+ QByteArray pwd = password.toLatin1();
+ ::write(masterFD, pwd.data(), pwd.length());
+ ::write(masterFD, "\n", 1);
+ ::read(masterFD, buf, pwd.length() + 1);
+ }
+ }
+ if (bytes == 0)
+ ::usleep(100000);
+ }
+ if (!errData.isEmpty()) {
+ printError(parent, QString::fromLocal8Bit(errData.constData()));
+ return false;
+ }
+
+ int status;
+ child = ::wait(&status);
+ const int exited = WIFEXITED(status);
+ const int exitStatus = WEXITSTATUS(status);
+ ::close(pipedData[1]);
+ if (exited)
+ return exitStatus == 0;
+
+ return false;
+ }
+
+ // child process
+ else {
+ ::close(pipedData[0]);
+ // Reset signal handlers
+ for (int sig = 1; sig < NSIG; ++sig)
+ signal(sig, SIG_DFL);
+ signal(SIGHUP, SIG_IGN);
+
+ ::setsid();
+
+ ::ioctl(slaveFD, TIOCSCTTY, 1);
+ int pgrp = ::getpid();
+ ::tcsetpgrp(slaveFD, pgrp);
+
+ ::dup2(slaveFD, 0);
+ ::dup2(slaveFD, 1);
+ ::dup2(pipedData[1], 2);
+
+ // close all file descriptors
+ struct rlimit rlp;
+ getrlimit(RLIMIT_NOFILE, &rlp);
+ for (int i = 3; i < static_cast<int>(rlp.rlim_cur); ++i)
+ ::close(i);
+
+ char **argp = (char **) ::malloc(arguments.count() + 4 * sizeof(char *));
+ QList<QByteArray> args;
+ args.push_back(SU_COMMAND);
+ args.push_back("-b");
+ args.push_back(program.toLocal8Bit());
+ for (QStringList::const_iterator it = arguments.begin(); it != arguments.end(); ++it)
+ args.push_back(it->toLocal8Bit());
+
+ int i = 0;
+ for (QList<QByteArray>::iterator it = args.begin(); it != args.end(); ++it, ++i)
+ argp[i] = it->data();
+ argp[i] = 0;
+
+ ::unsetenv("LANG");
+ ::unsetenv("LC_ALL");
+
+ ::execv(SU_COMMAND, argp);
+ _exit(0);
+ return false;
+ }
+}
+
+// has no guarantee to work
+bool AdminAuthorization::hasAdminRights()
+{
+ return getuid() == 0;
+}
diff --git a/src/libs/installer/binaryformat.cpp b/src/libs/installer/binaryformat.cpp
new file mode 100644
index 000000000..133df3690
--- /dev/null
+++ b/src/libs/installer/binaryformat.cpp
@@ -0,0 +1,1115 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "binaryformat.h"
+
+#include "errors.h"
+#include "fileutils.h"
+#include "lib7z_facade.h"
+#include "utils.h"
+#include "zipjob.h"
+
+#include <kdupdaterupdateoperationfactory.h>
+
+#include <QtCore/QResource>
+#include <QtCore/QTemporaryFile>
+
+#include <errno.h>
+
+using namespace QInstaller;
+using namespace QInstallerCreator;
+
+/*
+TRANSLATOR QInstallerCreator::Archive
+*/
+
+/*
+TRANSLATOR QInstallerCreator::Component
+*/
+
+static inline QByteArray &theBuffer(int size)
+{
+ static QByteArray b;
+ if (size > b.size())
+ b.resize(size);
+ return b;
+}
+
+void QInstaller::appendFileData(QIODevice *out, QIODevice *in)
+{
+ Q_ASSERT(!in->isSequential());
+ const qint64 size = in->size();
+ blockingCopy(in, out, size);
+}
+
+
+void QInstaller::retrieveFileData(QIODevice *out, QIODevice *in)
+{
+ qint64 size = QInstaller::retrieveInt64(in);
+ appendData(in, out, size);
+/* QByteArray &b = theBuffer(size);
+ blockingRead(in, b.data(), size);
+ blockingWrite(out, b.constData(), size);*/
+}
+
+void QInstaller::appendInt64(QIODevice *out, qint64 n)
+{
+ blockingWrite(out, reinterpret_cast<const char*>(&n), sizeof(n));
+}
+
+void QInstaller::appendInt64Range(QIODevice *out, const Range<qint64> &r)
+{
+ appendInt64(out, r.start());
+ appendInt64(out, r.length());
+}
+
+qint64 QInstaller::retrieveInt64(QIODevice *in)
+{
+ qint64 n = 0;
+ blockingRead(in, reinterpret_cast<char*>(&n), sizeof(n));
+ return n;
+}
+
+Range<qint64> QInstaller::retrieveInt64Range(QIODevice *in)
+{
+ const quint64 start = retrieveInt64(in);
+ const quint64 length = retrieveInt64(in);
+ return Range<qint64>::fromStartAndLength(start, length);
+}
+
+void QInstaller::appendData(QIODevice *out, QIODevice *in, qint64 size)
+{
+ while (size > 0) {
+ const qint64 nextSize = qMin(size, 16384LL);
+ QByteArray &b = theBuffer(nextSize);
+ blockingRead(in, b.data(), nextSize);
+ blockingWrite(out, b.constData(), nextSize);
+ size -= nextSize;
+ }
+}
+
+void QInstaller::appendString(QIODevice *out, const QString &str)
+{
+ appendByteArray(out, str.toUtf8());
+}
+
+void QInstaller::appendByteArray(QIODevice *out, const QByteArray &ba)
+{
+ appendInt64(out, ba.size());
+ blockingWrite(out, ba.constData(), ba.size());
+}
+
+void QInstaller::appendStringList(QIODevice *out, const QStringList &list)
+{
+ appendInt64(out, list.size());
+ foreach (const QString &s, list)
+ appendString(out, s);
+}
+
+void QInstaller::appendDictionary(QIODevice *out, const QHash<QString,QString> &dict)
+{
+ appendInt64(out, dict.size());
+ foreach (const QString &key, dict.keys()) {
+ appendString(out, key);
+ appendString(out, dict.value(key));
+ }
+}
+
+qint64 QInstaller::appendCompressedData(QIODevice *out, QIODevice *in, qint64 size)
+{
+ QByteArray ba;
+ ba.resize(size);
+ blockingRead(in, ba.data(), size);
+
+ QByteArray cba = qCompress(ba);
+ blockingWrite(out, cba, cba.size());
+ return cba.size();
+}
+
+QString QInstaller::retrieveString(QIODevice *in)
+{
+ const QByteArray b = retrieveByteArray(in);
+ return QString::fromUtf8(b);
+}
+
+QByteArray QInstaller::retrieveByteArray(QIODevice *in)
+{
+ QByteArray ba;
+ const qint64 n = retrieveInt64(in);
+ ba.resize(n);
+ blockingRead(in, ba.data(), n);
+ return ba;
+}
+
+QStringList QInstaller::retrieveStringList(QIODevice *in)
+{
+ QStringList list;
+ for (qint64 i = retrieveInt64(in); --i >= 0;)
+ list << retrieveString(in);
+ return list;
+}
+
+QHash<QString,QString> QInstaller::retrieveDictionary(QIODevice *in)
+{
+ QHash<QString,QString> dict;
+ for (qint64 i = retrieveInt64(in); --i >= 0;) {
+ QString key = retrieveString(in);
+ dict.insert(key, retrieveString(in));
+ }
+ return dict;
+}
+
+QByteArray QInstaller::retrieveData(QIODevice *in, qint64 size)
+{
+ QByteArray ba;
+ ba.resize(size);
+ blockingRead(in, ba.data(), size);
+ return ba;
+}
+
+QByteArray QInstaller::retrieveCompressedData(QIODevice *in, qint64 size)
+{
+ QByteArray ba;
+ ba.resize(size);
+ blockingRead(in, ba.data(), size);
+ return qUncompress(ba);
+}
+
+qint64 QInstaller::findMagicCookie(QFile *in, quint64 magicCookie)
+{
+ Q_ASSERT(in);
+ Q_ASSERT(in->isOpen());
+ Q_ASSERT(in->isReadable());
+ const qint64 oldPos = in->pos();
+ const qint64 MAX_SEARCH = 1024 * 1024; // stop searching after one MB
+ qint64 searched = 0;
+ try {
+ while (searched < MAX_SEARCH) {
+ const qint64 pos = in->size() - searched - sizeof(qint64);
+ if (pos < 0)
+ throw Error(QObject::tr("Searched whole file, no marker found"));
+ if (!in->seek(pos)) {
+ throw Error(QObject::tr("Could not seek to %1 in file %2: %3").arg(QString::number(pos),
+ in->fileName(), in->errorString()));
+ }
+ const quint64 num = static_cast<quint64>(retrieveInt64(in));
+ if (num == magicCookie) {
+ in->seek(oldPos);
+ return pos;
+ }
+ searched += 1;
+ }
+ throw Error(QObject::tr("No marker found, stopped after %1 bytes.").arg(QString::number(MAX_SEARCH)));
+ } catch (const Error& err) {
+ in->seek(oldPos);
+ throw err;
+ } catch (...) {
+ in->seek(oldPos);
+ throw Error(QObject::tr("No marker found, unknown exception caught."));
+ }
+ return -1; // never reached
+}
+
+/*!
+ Creates an archive providing the data in \a path.
+ \a path can be a path to a file or to a directory. If it's a file, it's considered to be
+ pre-zipped and gets delivered as it is. If it's a directory, it gets zipped by Archive.
+ */
+Archive::Archive(const QString &path)
+ : m_device(0),
+ m_isTempFile(false),
+ m_path(path),
+ m_name(QFileInfo(path).fileName().toUtf8())
+{
+}
+
+Archive::Archive(const QByteArray &identifier, const QByteArray &data)
+ : m_device(0),
+ m_isTempFile(true),
+ m_path(generateTemporaryFileName()),
+ m_name(identifier)
+{
+ QFile file(m_path);
+ file.open(QIODevice::WriteOnly);
+ file.write(data);
+}
+
+/*!
+ Creates an archive identified by \a identifier providing a data \a segment within a \a device.
+ */
+Archive::Archive(const QByteArray &identifier, const QSharedPointer<QFile> &device, const Range<qint64> &segment)
+ : m_device(device),
+ m_segment(segment),
+ m_isTempFile(false),
+ m_name(identifier)
+{
+}
+
+Archive::~Archive()
+{
+ if (isOpen())
+ close();
+ if (m_isTempFile)
+ QFile::remove(m_path);
+}
+
+/*!
+ Copies the archives contents to the path \a name.
+ If the archive is a zipped directory, \a name is treated as a directory. The archive gets extracted there.
+
+ If the archive is a plain file and \a name an existing directory, it gets created
+ with it's name. Otherwise it gets saved as \a name.
+ Note that if a file with the \a name already exists, copy() return false (i.e. Archive will not overwrite it).
+ */
+bool Archive::copy(const QString &name)
+{
+ const QFileInfo fileInfo(name);
+ if (isZippedDirectory()) {
+ if (fileInfo.exists() && !fileInfo.isDir())
+ return false;
+
+ errno = 0;
+ const QString absoluteFilePath = fileInfo.absoluteFilePath();
+ if (!fileInfo.exists() && !QDir().mkpath(absoluteFilePath)) {
+ setErrorString(tr("Could not create %1: %2").arg(name, QString::fromLocal8Bit(strerror(errno))));
+ return false;
+ }
+
+ if (isOpen())
+ close();
+ open(QIODevice::ReadOnly);
+
+ UnzipJob job;
+ job.setInputDevice(this);
+ job.setOutputPath(absoluteFilePath);
+ job.run();
+ } else {
+ if (isOpen())
+ close();
+ open(QIODevice::ReadOnly);
+
+ QFile target(fileInfo.isDir() ? QString::fromLatin1("%1/%2").arg(name)
+ .arg(QString::fromUtf8(m_name.data(), m_name.count())) : name);
+ if (target.exists())
+ return false;
+ target.open(QIODevice::WriteOnly);
+ blockingCopy(this, &target, size());
+ }
+ close();
+ return true;
+}
+
+/*!
+ \reimp
+ */
+bool Archive::seek(qint64 pos)
+{
+ if (m_inputFile.isOpen())
+ return m_inputFile.seek(pos) && QIODevice::seek(pos);
+ return QIODevice::seek(pos);
+}
+
+/*!
+ Returns true, if this archive was created by zipping a directory.
+ */
+bool Archive::isZippedDirectory() const
+{
+ if (m_device == 0) {
+ // easy, just check whether it's a dir
+ return QFileInfo(m_path).isDir();
+ }
+
+ // more complex, check the zip header magic
+ Archive* const arch = const_cast<Archive*> (this);
+
+ const bool notOpened = !isOpen();
+ if (notOpened)
+ arch->open(QIODevice::ReadOnly);
+ const qint64 p = pos();
+ arch->seek(0);
+
+ const QByteArray ba = arch->read(4);
+ const bool result = ba == QByteArray("\x50\x4b\x03\04");
+
+ arch->seek(p);
+ if (notOpened)
+ arch->close();
+ return result;
+}
+
+QByteArray Archive::name() const
+{
+ return m_name;
+}
+
+void Archive::setName(const QByteArray &name)
+{
+ m_name = name;
+}
+
+/*!
+ \reimpl
+ */
+void Archive::close()
+{
+ m_inputFile.close();
+ if (QFileInfo(m_path).isDir())
+ m_inputFile.remove();
+ QIODevice::close();
+}
+
+/*!
+ \reimp
+ */
+bool Archive::open(OpenMode mode)
+{
+ if (isOpen())
+ return false;
+
+ const bool writeOnly = (mode & QIODevice::WriteOnly) != QIODevice::NotOpen;
+ const bool append = (mode & QIODevice::Append) != QIODevice::NotOpen;
+
+ // no write support
+ if (writeOnly || append)
+ return false;
+
+ if (m_device != 0)
+ return QIODevice::open(mode);
+
+ const QFileInfo fi(m_path);
+ if (fi.isFile()) {
+ m_inputFile.setFileName(m_path);
+ if (!m_inputFile.open(mode)) {
+ setErrorString(tr("Could not open archive file %1 for reading.").arg(m_path));
+ return false;
+ }
+ setOpenMode(mode);
+ return true;
+ }
+
+ if (fi.isDir()) {
+ if (m_inputFile.fileName().isEmpty() || !m_inputFile.exists()) {
+ if (!createZippedFile())
+ return false;
+ }
+ Q_ASSERT(!m_inputFile.fileName().isEmpty());
+ if (!m_inputFile.open(mode))
+ return false;
+ setOpenMode(mode);
+ return true;
+ }
+
+ setErrorString(tr("Could not create archive from %1: Not a file.").arg(m_path));
+ return false;
+}
+
+bool Archive::createZippedFile()
+{
+ QTemporaryFile file;
+ file.setAutoRemove(false);
+ if (!file.open())
+ return false;
+
+ m_inputFile.setFileName(file.fileName());
+ file.close();
+ m_inputFile.open(QIODevice::ReadWrite);
+ try {
+ Lib7z::createArchive(&m_inputFile, QStringList() << m_path);
+ } catch(Lib7z::SevenZipException &e) {
+ m_inputFile.close();
+ setErrorString(e.message());
+ return false;
+ }
+
+ if (!Lib7z::isSupportedArchive(&m_inputFile)) {
+ m_inputFile.close();
+ setErrorString(tr("Error while packing directory at %1").arg(m_path));
+ return false;
+ }
+ m_inputFile.close();
+ return true;
+}
+
+/*!
+ \reimp
+ */
+qint64 Archive::size() const
+{
+ // if we got a device, we just pass the length of the segment
+ if (m_device != 0)
+ return m_segment.length();
+
+ const QFileInfo fi(m_path);
+ // if we got a regular file, we pass the size of the file
+ if (fi.isFile())
+ return fi.size();
+
+ if (fi.isDir()) {
+ if (m_inputFile.fileName().isEmpty() || !m_inputFile.exists()) {
+ if (!const_cast< Archive* >(this)->createZippedFile()) {
+ throw Error(QObject::tr("Cannot create zipped file for path %1: %2").arg(m_path,
+ errorString()));
+ }
+ }
+ Q_ASSERT(!m_inputFile.fileName().isEmpty());
+ return m_inputFile.size();
+ }
+ return 0;
+}
+
+/*!
+ \reimp
+ */
+qint64 Archive::readData(char* data, qint64 maxSize)
+{
+ if (m_device == 0)
+ return m_inputFile.read(data, maxSize);
+
+ const qint64 p = m_device->pos();
+ m_device->seek(m_segment.start() + pos());
+ const qint64 amountRead = m_device->read(data, qMin<quint64>(maxSize, m_segment.length() - pos()));
+ m_device->seek(p);
+ return amountRead;
+}
+
+/*!
+ \reimp
+ */
+qint64 Archive::writeData(const char* data, qint64 maxSize)
+{
+ Q_UNUSED(data);
+ Q_UNUSED(maxSize);
+ // should never be called, as we're read only
+ return -1;
+}
+
+QByteArray Component::name() const
+{
+ return m_name;
+}
+
+void Component::setName(const QByteArray &ba)
+{
+ m_name = ba;
+}
+
+Range<qint64> Component::binarySegment() const
+{
+ return m_binarySegment;
+}
+
+void Component::setBinarySegment(const Range<qint64> &r)
+{
+ m_binarySegment = r;
+}
+
+Component Component::readFromIndexEntry(const QSharedPointer<QFile> &in, qint64 offset)
+{
+ Component c;
+ c.m_name = retrieveByteArray(in.data());
+ c.m_binarySegment = retrieveInt64Range(in.data()).moved(offset);
+
+ c.readData(in, offset);
+
+ return c;
+}
+
+void Component::writeIndexEntry(QIODevice *out, qint64 positionOffset) const
+{
+ appendByteArray(out, m_name);
+ const Range<qint64> relative = m_binarySegment.moved(positionOffset);
+ appendInt64(out, binarySegment().start());
+ appendInt64(out, binarySegment().length());
+}
+
+void Component::writeData(QIODevice *out, qint64 offset) const
+{
+ const qint64 dataBegin = out->pos() + offset;
+
+ appendInt64(out, m_archives.count());
+
+ qint64 start = out->pos() + offset;
+
+ // Why 16 + 16? This is 24, not 32???
+ const int foo = 3 * sizeof(qint64);
+ // add 16 + 16 + number of name characters for each archive (the size of the table)
+ foreach (const QSharedPointer<Archive> &archive, m_archives)
+ start += foo + archive->name().count();
+
+ QList<qint64> starts;
+ foreach (const QSharedPointer<Archive> &archive, m_archives) {
+ appendByteArray(out, archive->name());
+ starts.push_back(start);
+ appendInt64Range(out, Range<qint64>::fromStartAndLength(start, archive->size()));
+ start += archive->size();
+ }
+
+ foreach (const QSharedPointer<Archive> &archive, m_archives) {
+ if (!archive->open(QIODevice::ReadOnly)) {
+ throw Error(tr("Could not open archive %1: %2").arg(QLatin1String(archive->name()),
+ archive->errorString()));
+ }
+
+ const qint64 expectedStart = starts.takeFirst();
+ const qint64 actualStart = out->pos() + offset;
+ Q_UNUSED(expectedStart);
+ Q_UNUSED(actualStart);
+ Q_ASSERT(expectedStart == actualStart);
+ blockingCopy(archive.data(), out, archive->size());
+ }
+
+ m_binarySegment = Range<qint64>::fromStartAndEnd(dataBegin, out->pos() + offset);
+}
+
+void Component::readData(const QSharedPointer<QFile> &in, qint64 offset)
+{
+ const qint64 pos = in->pos();
+
+ in->seek(m_binarySegment.start());
+ const qint64 count = retrieveInt64(in.data());
+
+ QVector<QByteArray> names;
+ QVector<Range<qint64> > ranges;
+ for (int i = 0; i < count; ++i) {
+ names.push_back(retrieveByteArray(in.data()));
+ ranges.push_back(retrieveInt64Range(in.data()).moved(offset));
+ }
+
+ for (int i = 0; i < ranges.count(); ++i)
+ m_archives.append(QSharedPointer<Archive>(new Archive(names.at(i), in, ranges.at(i))));
+
+ in->seek(pos);
+}
+
+QString Component::dataDirectory() const
+{
+ return m_dataDirectory;
+}
+
+void Component::setDataDirectory(const QString &path)
+{
+ m_dataDirectory = path;
+}
+
+bool Component::operator<(const Component& other) const
+{
+ if (m_name != other.name())
+ return m_name < other.m_name;
+ return m_binarySegment < other.m_binarySegment;
+}
+
+bool Component::operator==(const Component& other) const
+{
+ return m_name == other.m_name && m_binarySegment == other.m_binarySegment;
+}
+
+/*!
+ Destroys this component.
+ */
+Component::~Component()
+{
+}
+
+/*!
+ Appends \a archive to this component. The component takes ownership of \a archive.
+ */
+void Component::appendArchive(const QSharedPointer<Archive>& archive)
+{
+ Q_ASSERT(archive);
+ archive->setParent(0);
+ m_archives.push_back(archive);
+}
+
+/*!
+ Returns the archives associated with this component.
+ */
+QVector<QSharedPointer<Archive> > Component::archives() const
+{
+ return m_archives;
+}
+
+QSharedPointer<Archive> Component::archiveByName(const QByteArray &name) const
+{
+ foreach (const QSharedPointer<Archive>& i, m_archives) {
+ if (i->name() == name)
+ return i;
+ }
+ return QSharedPointer<Archive>();
+}
+
+
+// -- ComponentIndex
+
+ComponentIndex::ComponentIndex()
+{
+}
+
+ComponentIndex ComponentIndex::read(const QSharedPointer<QFile> &dev, qint64 offset)
+{
+ ComponentIndex result;
+ const qint64 size = retrieveInt64(dev.data());
+ for (int i = 0; i < size; ++i)
+ result.insertComponent(Component::readFromIndexEntry(dev, offset));
+ retrieveInt64(dev.data());
+ return result;
+}
+
+void ComponentIndex::writeIndex(QIODevice *out, qint64 offset) const
+{
+ // Q: why do we write the size twice?
+ // A: for us to be able to read it beginning from the end of the file as well
+ appendInt64(out, componentCount());
+ foreach (const Component& i, components())
+ i.writeIndexEntry(out, offset);
+ appendInt64(out, componentCount());
+}
+
+void ComponentIndex::writeComponentData(QIODevice *out, qint64 offset) const
+{
+ appendInt64(out, componentCount());
+
+ foreach (const Component &component, m_components)
+ component.writeData(out, offset);
+}
+
+Component ComponentIndex::componentByName(const QByteArray &id) const
+{
+ return m_components.value(id);
+}
+
+void ComponentIndex::insertComponent(const Component& c)
+{
+ m_components.insert(c.name(), c);
+}
+
+void ComponentIndex::removeComponent(const QByteArray &name)
+{
+ m_components.remove(name);
+}
+
+QVector<Component> ComponentIndex::components() const
+{
+ return m_components.values().toVector();
+}
+
+int ComponentIndex::componentCount() const
+{
+ return m_components.size();
+}
+
+
+static QVector<QByteArray> sResourceVec;
+/*!
+ \internal
+ Registers the resource found at \a segment within \a file into the Qt resource system.
+ */
+static const uchar* addResourceFromBinary(QFile* file, const Range<qint64> &segment)
+{
+ if (segment.length() <= 0)
+ return 0;
+
+ if (!file->seek(segment.start())) {
+ throw Error(QObject::tr("Could not seek to in-binary resource. (offset: %1, length: %2)")
+ .arg(QString::number(segment.start()), QString::number(segment.length())));
+ }
+ sResourceVec.append(retrieveData(file, segment.length()));
+
+ if (!QResource::registerResource((const uchar*)(sResourceVec.last().constData()),
+ QLatin1String(":/metadata"))) {
+ throw Error(QObject::tr("Could not register in-binary resource."));
+ }
+ return (const uchar*)(sResourceVec.last().constData());
+}
+
+
+// -- BinaryContentPrivate
+
+BinaryContentPrivate::BinaryContentPrivate(const QString &path)
+ : m_magicMarker(0)
+ , m_dataBlockStart(0)
+ , m_appBinary(new QFile(path))
+ , m_binaryDataFile(0)
+ , m_binaryFormatEngineHandler(m_componentIndex)
+{
+}
+
+BinaryContentPrivate::BinaryContentPrivate(const BinaryContentPrivate &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)
+ , m_componentIndex(other.m_componentIndex)
+ , m_binaryFormatEngineHandler(other.m_binaryFormatEngineHandler)
+{
+}
+
+BinaryContentPrivate::~BinaryContentPrivate()
+{
+ foreach (const uchar *rccData, m_resourceMappings)
+ QResource::unregisterResource(rccData);
+ sResourceVec.clear();
+ m_resourceMappings.clear();
+}
+
+
+// -- BinaryContent
+
+BinaryContent::BinaryContent(const QString &path)
+ : d(new BinaryContentPrivate(path))
+{
+}
+
+BinaryContent::~BinaryContent()
+{
+}
+
+/*!
+ Reads binary content stored in the current application binary. Maps the embedded resources into memory
+ and instantiates performed operations if available.
+*/
+BinaryContent BinaryContent::readAndRegisterFromApplicationFile()
+{
+ BinaryContent c = BinaryContent::readFromApplicationFile();
+ c.registerEmbeddedQResources();
+ c.registerPerformedOperations();
+ return c;
+}
+
+/*!
+ 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;
+}
+
+/*!
+ Reads binary content stored in the current application binary.
+*/
+BinaryContent BinaryContent::readFromApplicationFile()
+{
+ return BinaryContent::readFromBinary(QCoreApplication::applicationFilePath());;
+}
+
+/*!
+ * \class QInstaller::BinaryContent
+ *
+ * BinaryContent handles binary information embedded into executables.
+ * Qt resources as well as component information can be stored.
+ *
+ * Explanation of the binary blob at the end of the installer or separate data file:
+ *
+ * \verbatim
+ * Meta data segment 0
+ * Meta data segment ...
+ * Meta data segment n
+ * ------------------------------------------------------
+ * Component data segment 0
+ * Component data segment ..
+ * Component data segment n
+ * ------------------------------------------------------
+ * Component index segment
+ * ------------------------------------------------------
+ * quint64 offset of component index segment
+ * quint64 length of component index segment
+ * ------------------------------------------------------
+ * qint64 offset of meta data segment 0
+ * qint64 length of meta data segment 0
+ * qint64 offset of meta data segment ..
+ * qint64 length of meta data segment ..
+ * qint64 offset of meta data segment n
+ * qint64 length of meta data segment n
+ * ------------------------------------------------------
+ * operations start offest
+ * operations end
+ * quint64 embedded resource count
+ * quint64 data block size
+ * quint64 Magic marker
+ * quint64 Magic cookie (0xc2 0x63 0x0a 0x1c 0x99 0xd6 0x68 0xf8)
+ * <eof>
+ *
+ * All offsets are addresses relative to the end of the file.
+ *
+ * Meta data segments are stored as Qt resources, which must be "mounted"
+ * via QResource::registerResource()
+ *
+ * Component index segment:
+ * quint64 number of index entries
+ * QString identifier of component 0
+ * quint64 offset of component data segment 0
+ * quint64 length of component data segment 0
+ * QString identifier of component ..
+ * quint64 offset of component data segment ..
+ * quint64 length of component data segment ..
+ * QString identifier of component n
+ * quint64 offset of component data segment n
+ * quint64 length of component data segment n
+ * quint64 number of index entries
+ *
+ * Component data segment:
+ * quint64 number of archives in this component
+ * QString name of archive 0
+ * quint64 offset of archive 0
+ * quint64 length of archive 0
+ * QString name of archive ..
+ * quint64 offset of archive ..
+ * quint64 length of archive ..
+ * QString name of archive n
+ * quint64 offset of archive n
+ * quint64 length of archive n
+ * Archive 0
+ * Archive ..
+ * Archive n
+ * \endverbatim
+ */
+
+BinaryContent BinaryContent::readFromBinary(const QString &path)
+{
+ BinaryContent c(path);
+ if (!c.d->m_appBinary->open(QIODevice::ReadOnly))
+ throw Error(QObject::tr("Could not open binary %1: %2").arg(path, c.d->m_appBinary->errorString()));
+
+ // check for supported binary, will throw if we can't find a marker
+ const BinaryLayout layout = readBinaryLayout(c.d->m_appBinary.data(),
+ findMagicCookie(c.d->m_appBinary.data(), QInstaller::MagicCookie));
+
+ bool retry = true;
+ if (layout.magicMarker != MagicInstallerMarker) {
+ QString binaryDataPath = path;
+ QFileInfo fi(path + QLatin1String("/../../.."));
+ if (QFileInfo(fi.absoluteFilePath()).isBundle())
+ binaryDataPath = fi.absoluteFilePath();
+ fi.setFile(binaryDataPath);
+
+ c.d->m_binaryDataFile = QSharedPointer<QFile>(new QFile(fi.absolutePath() + QLatin1Char('/')
+ + fi.baseName() + QLatin1String(".dat")));
+ if (c.d->m_binaryDataFile->exists() && c.d->m_binaryDataFile->open(QIODevice::ReadOnly)) {
+ // check for supported binary data file, will throw if we can't find a marker
+ try {
+ const qint64 cookiePos = findMagicCookie(c.d->m_binaryDataFile.data(),
+ QInstaller::MagicCookieDat);
+ const BinaryLayout binaryLayout = readBinaryLayout(c.d->m_binaryDataFile.data(), cookiePos);
+ readBinaryData(c, c.d->m_binaryDataFile, binaryLayout);
+ retry = false;
+ } catch (const Error &error) {
+ // this seems to be an unsupported dat file, try to read from original binary
+ c.d->m_binaryDataFile.clear();
+ qDebug() << error.message();
+ }
+ } else {
+ c.d->m_binaryDataFile.clear();
+ }
+ }
+
+ if (retry)
+ readBinaryData(c, c.d->m_appBinary, layout);
+
+ return c;
+}
+
+/* static */
+BinaryLayout BinaryContent::readBinaryLayout(QIODevice *const file, qint64 cookiePos)
+{
+ const qint64 indexSize = 5 * sizeof(qint64);
+ if (!file->seek(cookiePos - indexSize))
+ throw Error(QObject::tr("Could not seek to binary layout section."));
+
+ BinaryLayout layout;
+ layout.operationsStart = retrieveInt64(file);
+ layout.operationsEnd = retrieveInt64(file);
+ layout.resourceCount = retrieveInt64(file);
+ layout.dataBlockSize = retrieveInt64(file);
+ layout.magicMarker = retrieveInt64(file);
+ layout.magicCookie = retrieveInt64(file);
+ layout.indexSize = indexSize + sizeof(qint64);
+ layout.endOfData = file->pos();
+
+ qDebug() << "Operations start:" << layout.operationsStart;
+ qDebug() << "Operations end:" << layout.operationsEnd;
+ qDebug() << "Resource count:" << layout.resourceCount;
+ qDebug() << "Data block size:" << layout.dataBlockSize;
+ qDebug() << "Magic marker:" << layout.magicMarker;
+ qDebug() << "Magic cookie:" << layout.magicCookie;
+ qDebug() << "Index size:" << layout.indexSize;
+ qDebug() << "End of data:" << layout.endOfData;
+
+ const qint64 resourceOffsetAndLengtSize = 2 * sizeof(qint64);
+ const qint64 dataBlockStart = layout.endOfData - layout.dataBlockSize;
+ for (int i = 0; i < layout.resourceCount; ++i) {
+ if (!file->seek(layout.endOfData - layout.indexSize - resourceOffsetAndLengtSize * (i + 1)))
+ throw Error(QObject::tr("Could not seek to metadata index."));
+
+ const qint64 metadataResourceOffset = retrieveInt64(file);
+ const qint64 metadataResourceLength = retrieveInt64(file);
+ layout.metadataResourceSegments.append(Range<qint64>::fromStartAndLength(metadataResourceOffset
+ + dataBlockStart, metadataResourceLength));
+ }
+
+ 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(QObject::tr("Could not seek to operation list."));
+
+ const qint64 operationsCount = retrieveInt64(file.data());
+ qDebug() << "Number of operations:" << operationsCount;
+
+ for (int i = 0; i < operationsCount; ++i) {
+ const QString name = retrieveString(file.data());
+ const QString data = retrieveString(file.data());
+ content.d->m_performedOperationsData.append(qMakePair(name, data));
+ }
+
+ // seek to the position of the component index
+ const qint64 resourceOffsetAndLengtSize = 2 * sizeof(qint64);
+ const qint64 resourceSectionSize = resourceOffsetAndLengtSize * layout.resourceCount;
+ if (!file->seek(layout.endOfData - layout.indexSize - resourceSectionSize - resourceOffsetAndLengtSize))
+ throw Error(QObject::tr("Could not seek to component index information."));
+
+ const qint64 compIndexStart = retrieveInt64(file.data()) + dataBlockStart;
+ if (!file->seek(compIndexStart))
+ throw Error(QObject::tr("Could not seek to component index."));
+
+ content.d->m_componentIndex = QInstallerCreator::ComponentIndex::read(file, dataBlockStart);
+ content.d->m_binaryFormatEngineHandler.setComponentIndex(content.d->m_componentIndex);
+
+ if (isVerbose()) {
+ const QVector<QInstallerCreator::Component> components = content.d->m_componentIndex.components();
+ qDebug() << "Number of components loaded:" << components.count();
+ foreach (const QInstallerCreator::Component &component, components) {
+ const QVector<QSharedPointer<Archive> > archives = component.archives();
+ qDebug() << component.name().data() << "loaded...";
+ QStringList archivesWithSize;
+ foreach (const QSharedPointer<Archive> &archive, archives) {
+ QString archiveWithSize(QLatin1String("%1 - %2 Bytes"));
+ archiveWithSize = archiveWithSize.arg(QString::fromLocal8Bit(archive->name()),
+ QString::number(archive->size()));
+ archivesWithSize.append(archiveWithSize);
+ }
+ if (!archivesWithSize.isEmpty()) {
+ qDebug() << " - " << archives.count() << "archives: "
+ << qPrintable(archivesWithSize.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));
+ Q_ASSERT_X(!op.isNull(), __FUNCTION__, QString::fromLatin1("Invalid operation name: %1.")
+ .arg(opPair.first).toLatin1());
+
+ 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->isOpen() && !data->open(QIODevice::ReadOnly)) {
+ throw Error(QObject::tr("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();
+}
+
+/*!
+ Returns the binary component index as read from the file.
+*/
+QInstallerCreator::ComponentIndex BinaryContent::componentIndex() const
+{
+ return d->m_componentIndex;
+}
diff --git a/src/libs/installer/binaryformat.h b/src/libs/installer/binaryformat.h
new file mode 100644
index 000000000..e2e1f6a0f
--- /dev/null
+++ b/src/libs/installer/binaryformat.h
@@ -0,0 +1,248 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef BINARYFORMAT_H
+#define BINARYFORMAT_H
+
+#include "binaryformatenginehandler.h"
+#include "range.h"
+#include "qinstallerglobal.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QFile>
+#include <QtCore/QHash>
+#include <QtCore/QStack>
+#include <QtCore/QVector>
+#include <QtCore/QSharedPointer>
+
+namespace QInstaller {
+ static const qint64 MagicInstallerMarker = 0x12023233UL;
+ static const qint64 MagicUninstallerMarker = 0x12023234UL;
+
+ static const qint64 MagicUpdaterMarker = 0x12023235UL;
+ static const qint64 MagicPackageManagerMarker = 0x12023236UL;
+
+ // this cookie is put at the end of the file to determine whether we have data
+ static const quint64 MagicCookie = 0xc2630a1c99d668f8LL;
+ static const quint64 MagicCookieDat = 0xc2630a1c99d668f9LL;
+
+ qint64 INSTALLER_EXPORT findMagicCookie(QFile *file, quint64 magicCookie = MagicCookie);
+ void INSTALLER_EXPORT appendFileData(QIODevice *out, QIODevice *in);
+ void INSTALLER_EXPORT appendInt64(QIODevice *out, qint64 n);
+ void INSTALLER_EXPORT appendInt64Range(QIODevice *out, const Range<qint64> &r);
+ void INSTALLER_EXPORT appendData(QIODevice *out, QIODevice *in, qint64 size);
+ void INSTALLER_EXPORT appendByteArray(QIODevice *out, const QByteArray &ba);
+ void INSTALLER_EXPORT appendString(QIODevice *out, const QString &str);
+ void INSTALLER_EXPORT appendStringList(QIODevice *out, const QStringList &list);
+ void INSTALLER_EXPORT appendDictionary(QIODevice *out, const QHash<QString,QString> &dict);
+ qint64 INSTALLER_EXPORT appendCompressedData(QIODevice *out, QIODevice *in, qint64 size);
+
+ void INSTALLER_EXPORT retrieveFileData(QIODevice *out, QIODevice *in);
+ qint64 INSTALLER_EXPORT retrieveInt64(QIODevice *in);
+ Range<qint64> INSTALLER_EXPORT retrieveInt64Range(QIODevice *in);
+ QByteArray INSTALLER_EXPORT retrieveByteArray(QIODevice *in);
+ QString INSTALLER_EXPORT retrieveString(QIODevice *in);
+ QStringList INSTALLER_EXPORT retrieveStringList(QIODevice *in);
+ QHash<QString,QString> INSTALLER_EXPORT retrieveDictionary(QIODevice *in);
+ QByteArray INSTALLER_EXPORT retrieveData(QIODevice *in, qint64 size);
+ QByteArray INSTALLER_EXPORT retrieveCompressedData(QIODevice *in, qint64 size);
+}
+
+namespace QInstallerCreator {
+class Component;
+
+class INSTALLER_EXPORT Archive : public QIODevice
+{
+ Q_OBJECT
+public:
+ explicit Archive(const QString &path);
+ Archive(const QByteArray &name, const QByteArray &data);
+ Archive(const QByteArray &name, const QSharedPointer<QFile> &device, const Range<qint64> &segment);
+ ~Archive();
+
+ bool open(OpenMode mode);
+ void close();
+
+ bool seek(qint64 pos);
+ qint64 size() const;
+
+ bool createZippedFile();
+ bool isZippedDirectory() const;
+ bool copy(const QString &name);
+
+ QByteArray name() const;
+ void setName(const QByteArray &name);
+
+protected:
+ qint64 readData(char *data, qint64 maxSize);
+ qint64 writeData(const char *data, qint64 maxSize);
+
+ Range< qint64 > binarySegment() const;
+
+private:
+ //used when when reading from the installer
+ QSharedPointer<QFile> m_device;
+ const Range<qint64> m_segment;
+
+ //used when creating the installer, archive input file
+ QFile m_inputFile;
+ const bool m_isTempFile;
+ const QString m_path;
+ QByteArray m_name;
+};
+
+class INSTALLER_EXPORT Component
+{
+ Q_DECLARE_TR_FUNCTIONS(Component)
+
+public:
+ virtual ~Component();
+
+ static Component readFromIndexEntry(const QSharedPointer<QFile> &dev, qint64 offset);
+ void writeIndexEntry(QIODevice *dev, qint64 offset) const;
+
+ void writeData(QIODevice *dev, qint64 positionOffset) const;
+ void readData(const QSharedPointer<QFile> &dev, qint64 offset);
+
+ QByteArray name() const;
+ void setName(const QByteArray &ba);
+
+ QString dataDirectory() const;
+ void setDataDirectory(const QString &path);
+
+ Range<qint64> binarySegment() const;
+ void setBinarySegment(const Range<qint64> &r);
+
+ void appendArchive(const QSharedPointer<Archive> &archive);
+ QSharedPointer<Archive> archiveByName(const QByteArray &name) const;
+ QVector< QSharedPointer<Archive> > archives() const;
+
+ bool operator<(const Component &other) const;
+ bool operator==(const Component &other) const;
+
+private:
+ QByteArray m_name;
+ QVector<QSharedPointer<Archive> > m_archives;
+ mutable Range<qint64> m_binarySegment;
+ QString m_dataDirectory;
+};
+
+
+class INSTALLER_EXPORT ComponentIndex
+{
+public:
+ ComponentIndex();
+ static ComponentIndex read(const QSharedPointer<QFile> &dev, qint64 offset);
+ void writeIndex(QIODevice *dev, qint64 offset) const;
+ void writeComponentData(QIODevice *dev, qint64 offset) const;
+ Component componentByName(const QByteArray &name) const;
+ void insertComponent(const Component &name);
+ void removeComponent(const QByteArray &name);
+ QVector<Component> components() const;
+ int componentCount() const;
+
+private:
+ QHash<QByteArray, Component> m_components;
+};
+}
+
+namespace QInstaller {
+
+struct BinaryLayout
+{
+ QVector<Range<qint64> > metadataResourceSegments;
+ qint64 operationsStart;
+ qint64 operationsEnd;
+ qint64 resourceCount;
+ qint64 dataBlockSize;
+ qint64 magicMarker;
+ quint64 magicCookie;
+ qint64 indexSize;
+ qint64 endOfData;
+};
+
+class BinaryContentPrivate : public QSharedData
+{
+public:
+ BinaryContentPrivate(const QString &path);
+ BinaryContentPrivate(const BinaryContentPrivate &other);
+ ~BinaryContentPrivate();
+
+ 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<const uchar *> m_resourceMappings;
+ QVector<Range<qint64> > m_metadataResourceSegments;
+
+ QInstallerCreator::ComponentIndex m_componentIndex;
+ QInstallerCreator::BinaryFormatEngineHandler m_binaryFormatEngineHandler;
+};
+
+class INSTALLER_EXPORT BinaryContent
+{
+ explicit BinaryContent(const QString &path);
+
+public:
+ virtual ~BinaryContent();
+
+ static BinaryContent readAndRegisterFromApplicationFile();
+ static BinaryContent readAndRegisterFromBinary(const QString &path);
+
+ static BinaryContent readFromApplicationFile();
+ static BinaryContent readFromBinary(const QString &path);
+
+ static BinaryLayout readBinaryLayout(QIODevice *const file, qint64 cookiePos);
+
+ int registerPerformedOperations();
+ OperationList performedOperations() const;
+
+ qint64 magicMarker() const;
+ int registerEmbeddedQResources();
+ QInstallerCreator::ComponentIndex componentIndex() const;
+
+private:
+ static void readBinaryData(BinaryContent &content, const QSharedPointer<QFile> &file,
+ const BinaryLayout &layout);
+
+private:
+ QSharedDataPointer<BinaryContentPrivate> d;
+};
+
+}
+
+#endif // BINARYFORMAT_H
diff --git a/src/libs/installer/binaryformatengine.cpp b/src/libs/installer/binaryformatengine.cpp
new file mode 100644
index 000000000..05affaf4b
--- /dev/null
+++ b/src/libs/installer/binaryformatengine.cpp
@@ -0,0 +1,297 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "binaryformatengine.h"
+
+using namespace QInstallerCreator;
+
+namespace {
+
+class StringListIterator : public QAbstractFileEngineIterator
+{
+public:
+ StringListIterator( const QStringList &list, QDir::Filters filters, const QStringList &nameFilters)
+ : QAbstractFileEngineIterator(filters, nameFilters),
+ list(list),
+ index(-1)
+ {
+ }
+
+ bool hasNext() const
+ {
+ return index < list.size() - 1;
+ }
+
+ QString next()
+ {
+ if(!hasNext())
+ return QString();
+ ++index;
+ return currentFilePath();
+ }
+
+ QString currentFileName() const
+ {
+ return index < 0 ? QString() : list[index];
+ }
+
+private:
+ const QStringList list;
+ int index;
+};
+
+} // anon namespace
+
+BinaryFormatEngine::BinaryFormatEngine(const ComponentIndex &index, const QString &fileName)
+ : m_index(index)
+ , m_hasComponent(false)
+ , m_hasArchive(false)
+ , m_archive(0)
+{
+ setArchive(fileName);
+}
+
+BinaryFormatEngine::~BinaryFormatEngine()
+{
+}
+
+void BinaryFormatEngine::setArchive(const QString &file)
+{
+ m_fileNamePath = file;
+
+ static const QChar sep = QLatin1Char('/');
+ static const QString prefix = QLatin1String("installer://");
+ Q_ASSERT(file.toLower().startsWith(prefix));
+
+ // cut the prefix
+ QString path = file.mid(prefix.length());
+ while (path.endsWith(sep))
+ path.chop(1);
+
+ QString arch;
+ const QString comp = path.section(sep, 0, 0);
+ m_hasComponent = !comp.isEmpty();
+ m_hasArchive = path.contains(sep);
+ if (m_hasArchive)
+ arch = path.section(sep, 1, 1);
+
+ m_component = m_index.componentByName(comp.toUtf8());
+ m_archive = m_component.archiveByName(arch.toUtf8());
+}
+
+/**
+ * \reimp
+ */
+void BinaryFormatEngine::setFileName(const QString &file)
+{
+ setArchive(file);
+}
+
+/**
+ * \reimp
+ */
+bool BinaryFormatEngine::close()
+{
+ if (m_archive == 0)
+ return false;
+
+ const bool result = m_archive->isOpen();
+ m_archive->close();
+ return result;
+}
+
+/**
+ * \reimp
+ */
+bool BinaryFormatEngine::open(QIODevice::OpenMode mode)
+{
+ return m_archive == 0 ? false : m_archive->open(mode);
+}
+
+/**
+ * \reimp
+ */
+qint64 BinaryFormatEngine::pos() const
+{
+ return m_archive == 0 ? 0 : m_archive->pos();
+}
+
+/**
+ * \reimp
+ */
+qint64 BinaryFormatEngine::read(char *data, qint64 maxlen)
+{
+ return m_archive == 0 ? -1 : m_archive->read(data, maxlen);
+}
+
+/**
+ * \reimp
+ */
+bool BinaryFormatEngine::seek(qint64 offset)
+{
+ return m_archive == 0 ? false : m_archive->seek(offset);
+}
+
+/**
+ * \reimp
+ */
+QString BinaryFormatEngine::fileName(FileName file) const
+{
+ switch(file) {
+ case BaseName:
+ return m_fileNamePath.section(QChar::fromLatin1('/'), -1, -1, QString::SectionSkipEmpty);
+ case PathName:
+ case AbsolutePathName:
+ case CanonicalPathName:
+ return m_fileNamePath.section(QChar::fromLatin1('/'), 0, -2, QString::SectionSkipEmpty);
+ case DefaultName:
+ case AbsoluteName:
+ case CanonicalName:
+ return m_fileNamePath;
+ default:
+ return QString();
+ }
+}
+
+/**
+ * \reimp
+ */
+bool BinaryFormatEngine::copy(const QString &newName)
+{
+ if (QFile::exists(newName))
+ return false;
+
+ QFile target(newName);
+ if (!target.open(QIODevice::WriteOnly))
+ return false;
+
+ qint64 bytesLeft = size();
+ if (!open(QIODevice::ReadOnly))
+ return false;
+
+ char data[4096];
+ while(bytesLeft > 0) {
+ const qint64 len = qMin<qint64>(bytesLeft, 4096);
+ const qint64 bytesRead = read(data, len);
+ if (bytesRead != len) {
+ close();
+ return false;
+ }
+ const qint64 bytesWritten = target.write(data, len);
+ if (bytesWritten != len) {
+ close();
+ return false;
+ }
+ bytesLeft -= len;
+ }
+ close();
+
+ return true;
+}
+
+/**
+ * \reimp
+ */
+QAbstractFileEngine::FileFlags BinaryFormatEngine::fileFlags(FileFlags type) const
+{
+ FileFlags result;
+ if ((type & FileType) && m_archive != 0)
+ result |= FileType;
+ if ((type & DirectoryType) && !m_hasArchive)
+ result |= DirectoryType;
+ if ((type & ExistsFlag) && m_hasArchive && m_archive != 0)
+ result |= ExistsFlag;
+ if ((type & ExistsFlag) && !m_hasArchive && !m_component.name().isEmpty())
+ result |= ExistsFlag;
+
+ return result;
+}
+
+/**
+ * \reimp
+ */
+QAbstractFileEngineIterator *BinaryFormatEngine::beginEntryList(QDir::Filters filters, const QStringList &filterNames)
+{
+ const QStringList entries = entryList(filters, filterNames);
+ return new StringListIterator(entries, filters, filterNames);
+}
+
+/**
+ * \reimp
+ */
+QStringList BinaryFormatEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const
+{
+ if (m_hasArchive)
+ return QStringList();
+
+ QStringList result;
+
+ if (m_hasComponent && (filters & QDir::Files)) {
+ const QVector< QSharedPointer<Archive> > archives = m_component.archives();
+ foreach (const QSharedPointer<Archive> &i, archives)
+ result.push_back(QString::fromUtf8(i->name()));
+ }
+ else if (!m_hasComponent && (filters & QDir::Dirs)) {
+ const QVector<Component> components = m_index.components();
+ foreach (const Component &i, components)
+ result.push_back(QString::fromUtf8(i.name()));
+ }
+
+ if (filterNames.isEmpty())
+ return result;
+
+ QList<QRegExp> regexps;
+ foreach (const QString &i, filterNames)
+ regexps.push_back(QRegExp(i, Qt::CaseInsensitive, QRegExp::Wildcard));
+
+ QStringList entries;
+ foreach (const QString &i, result) {
+ bool matched = false;
+ foreach (const QRegExp &reg, regexps) {
+ matched = reg.exactMatch(i);
+ if (matched)
+ break;
+ }
+ if (matched)
+ entries.push_back(i);
+ }
+
+ return entries;
+}
+
+/**
+ * \reimp
+ */
+qint64 BinaryFormatEngine::size() const
+{
+ return m_archive == 0 ? 0 : m_archive->size();
+}
diff --git a/src/libs/installer/binaryformatengine.h b/src/libs/installer/binaryformatengine.h
new file mode 100644
index 000000000..d5ac18a45
--- /dev/null
+++ b/src/libs/installer/binaryformatengine.h
@@ -0,0 +1,78 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef BINARYFORMATENGINE_H
+#define BINARYFORMATENGINE_H
+
+#include <QAbstractFileEngine>
+
+#include "binaryformat.h"
+
+namespace QInstallerCreator {
+
+class BinaryFormatEngine : public QAbstractFileEngine
+{
+public:
+ BinaryFormatEngine(const ComponentIndex &index, const QString &fileName);
+ ~BinaryFormatEngine();
+
+ void setFileName(const QString &file);
+
+ Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames);
+
+ bool copy(const QString &newName);
+ bool close();
+ bool open(QIODevice::OpenMode mode);
+ qint64 pos() const;
+ qint64 read(char *data, qint64 maxlen);
+ bool seek(qint64 offset);
+ qint64 size() const;
+
+ QString fileName(FileName file = DefaultName) const;
+ FileFlags fileFlags(FileFlags type = FileInfoAll) const;
+ QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const;
+
+protected:
+ void setArchive(const QString &file);
+
+private:
+ const ComponentIndex m_index;
+ bool m_hasComponent;
+ bool m_hasArchive;
+ Component m_component;
+ QSharedPointer<Archive> m_archive;
+ QString m_fileNamePath;
+};
+
+} // namespace QInstallerCreator
+
+#endif
diff --git a/src/libs/installer/binaryformatenginehandler.cpp b/src/libs/installer/binaryformatenginehandler.cpp
new file mode 100644
index 000000000..2b1bbcad4
--- /dev/null
+++ b/src/libs/installer/binaryformatenginehandler.cpp
@@ -0,0 +1,116 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "binaryformatenginehandler.h"
+#include "binaryformatengine.h"
+#include "binaryformat.h"
+
+#include <QDebug>
+#include <QFile>
+#include <QFSFileEngine>
+
+using namespace QInstallerCreator;
+
+static BinaryFormatEngineHandler *s_instance = 0;
+
+
+class BinaryFormatEngineHandler::Private
+{
+public:
+ Private(const ComponentIndex &i)
+ : index(i)
+ {
+ }
+
+ ComponentIndex index;
+};
+
+BinaryFormatEngineHandler::BinaryFormatEngineHandler(const ComponentIndex &index)
+ : d(new Private(index))
+{
+ s_instance = this;
+}
+
+BinaryFormatEngineHandler::BinaryFormatEngineHandler(const BinaryFormatEngineHandler &other)
+ : QAbstractFileEngineHandler(),
+ d(new Private(other.d->index))
+{
+ s_instance = this;
+}
+
+BinaryFormatEngineHandler::~BinaryFormatEngineHandler()
+{
+ if (s_instance == this)
+ s_instance = 0;
+ delete d;
+}
+
+void BinaryFormatEngineHandler::setComponentIndex(const ComponentIndex &index)
+{
+ d->index = index;
+}
+
+QAbstractFileEngine *BinaryFormatEngineHandler::create(const QString &fileName) const
+{
+ return fileName.startsWith(QLatin1String("installer://"), Qt::CaseInsensitive ) ? new BinaryFormatEngine(d->index, fileName) : 0;
+}
+
+BinaryFormatEngineHandler *BinaryFormatEngineHandler::instance()
+{
+ return s_instance;
+}
+
+void BinaryFormatEngineHandler::registerArchive(const QString &pathName, const QString &archive)
+{
+ static const QChar sep = QChar::fromLatin1('/');
+ static const QString prefix = QString::fromLatin1("installer://");
+ Q_ASSERT(pathName.toLower().startsWith(prefix));
+
+ // cut the prefix
+ QString path = pathName.mid(prefix.length());
+ while (path.endsWith(sep))
+ path.chop(1);
+
+ const QString comp = path.section(sep, 0, 0);
+ const QString archiveName = path.section(sep, 1, 1);
+
+ Component c = d->index.componentByName(comp.toUtf8());
+ if (c.name().isEmpty())
+ c.setName(comp.toUtf8());
+
+ QList< QSharedPointer<Archive> > registered;
+ QSharedPointer<Archive> newArchive(new Archive(archive));
+ newArchive->setName(archiveName.toUtf8());
+ registered.push_back(newArchive);
+ c.appendArchive(newArchive);
+ d->index.insertComponent(c);
+}
diff --git a/src/libs/installer/binaryformatenginehandler.h b/src/libs/installer/binaryformatenginehandler.h
new file mode 100644
index 000000000..82269eda9
--- /dev/null
+++ b/src/libs/installer/binaryformatenginehandler.h
@@ -0,0 +1,66 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef BINARYFORMATENGINEHANDLER_H
+#define BINARYFORMATENGINEHANDLER_H
+
+#include "installer_global.h"
+
+#include <QtCore/QAbstractFileEngineHandler>
+
+
+namespace QInstallerCreator {
+
+class ComponentIndex;
+
+class INSTALLER_EXPORT BinaryFormatEngineHandler : public QAbstractFileEngineHandler
+{
+public:
+ explicit BinaryFormatEngineHandler(const ComponentIndex &index);
+ BinaryFormatEngineHandler(const BinaryFormatEngineHandler &other);
+ ~BinaryFormatEngineHandler();
+ QAbstractFileEngine *create(const QString &fileName) const;
+
+ void setComponentIndex(const ComponentIndex &index);
+
+ static BinaryFormatEngineHandler *instance();
+
+ void registerArchive(const QString &fileName, const QString &path);
+
+private:
+ class Private;
+ Private *const d;
+};
+
+}
+
+#endif
diff --git a/src/libs/installer/component.cpp b/src/libs/installer/component.cpp
new file mode 100644
index 000000000..4f8345836
--- /dev/null
+++ b/src/libs/installer/component.cpp
@@ -0,0 +1,1207 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#include "component.h"
+
+#include "errors.h"
+#include "fileutils.h"
+#include "fsengineclient.h"
+#include "lib7z_facade.h"
+#include "packagemanagercore.h"
+#include "qinstallerglobal.h"
+#include "messageboxhandler.h"
+
+#include <kdupdaterupdatesourcesinfo.h>
+#include <kdupdaterupdateoperationfactory.h>
+
+#include <QtCore/QDirIterator>
+#include <QtCore/QTranslator>
+
+#include <QtGui/QApplication>
+
+#include <QtUiTools/QUiLoader>
+
+#include <algorithm>
+
+using namespace QInstaller;
+
+static const QLatin1String scScript("Script");
+static const QLatin1String scDefault("Default");
+static const QLatin1String scAutoDependOn("AutoDependOn");
+static const QLatin1String scVirtual("Virtual");
+static const QLatin1String scInstalled("Installed");
+static const QLatin1String scUpdateText("UpdateText");
+static const QLatin1String scUninstalled("Uninstalled");
+static const QLatin1String scCurrentState("CurrentState");
+static const QLatin1String scForcedInstallation("ForcedInstallation");
+
+static QRegExp scCommaRegExp(QLatin1String("\\b(,|, )\\b"));
+
+/*!
+ \class QInstaller::Component
+ Component describes a component within the installer.
+*/
+
+/*!
+ Constructor. Creates a new Component inside of \a installer.
+*/
+Component::Component(PackageManagerCore *core)
+ : d(new ComponentPrivate(core, this))
+{
+ setPrivate(d);
+
+ connect(this, SIGNAL(valueChanged(QString, QString)), this, SLOT(updateModelData(QString, QString)));
+}
+
+/*!
+ Destroys the Component.
+*/
+Component::~Component()
+{
+ if (parentComponent() != 0)
+ d->m_parentComponent->d->m_allChildComponents.removeAll(this);
+
+ //why can we delete all create operations if the component gets destroyed
+ if (!d->m_newlyInstalled)
+ qDeleteAll(d->m_operations);
+
+ //we need to copy the list, because we are changing it with removeAll at top
+ //(this made the iterators broken in the past)
+ QList<Component*> copiedChildrenList = d->m_allChildComponents;
+ copiedChildrenList.detach(); //this makes it a real copy
+
+ qDeleteAll(copiedChildrenList);
+ delete d;
+ d = 0;
+}
+
+void Component::loadDataFromPackage(const LocalPackage &package)
+{
+ setValue(scName, package.name);
+ // pixmap ???
+ setValue(scDisplayName, package.title);
+ setValue(scDescription, package.description);
+ setValue(scVersion, package.version);
+ setValue(scInheritVersion, package.inheritVersionFrom);
+ setValue(scInstalledVersion, package.version);
+ setValue(QLatin1String("LastUpdateDate"), package.lastUpdateDate.toString());
+ setValue(QLatin1String("InstallDate"), package.installDate.toString());
+ setValue(scUncompressedSize, QString::number(package.uncompressedSize));
+
+ QString dependstr;
+ foreach (const QString &val, package.dependencies)
+ dependstr += val + QLatin1String(",");
+
+ if (package.dependencies.count() > 0)
+ dependstr.chop(1);
+ setValue(scDependencies, dependstr);
+
+ setValue(scForcedInstallation, package.forcedInstallation ? scTrue : scFalse);
+ if (package.forcedInstallation & !PackageManagerCore::noForceInstallation()) {
+ setEnabled(false);
+ setCheckable(false);
+ setCheckState(Qt::Checked);
+ }
+ setValue(scVirtual, package.virtualComp ? scTrue : scFalse);
+ setValue(scCurrentState, scInstalled);
+}
+
+void Component::loadDataFromPackage(const Package &package)
+{
+ Q_ASSERT(&package);
+ Q_ASSERT(!package.name().isEmpty());
+
+ setValue(scName, package.data(scName).toString());
+ setValue(scDisplayName, package.data(scDisplayName).toString());
+ setValue(scDescription, package.data(scDescription).toString());
+ setValue(scDefault, package.data(scDefault).toString());
+ setValue(scAutoDependOn, package.data(scAutoDependOn).toString());
+ setValue(scCompressedSize, QString::number(package.compressedSize()));
+ setValue(scUncompressedSize, QString::number(package.uncompressedSize()));
+ setValue(scRemoteVersion, package.data(scRemoteVersion).toString());
+ setValue(scInheritVersion, package.data(scInheritVersion).toString());
+ setValue(scDependencies, package.data(scDependencies).toString());
+ setValue(scDownloadableArchives, package.data(scDownloadableArchives).toString());
+ setValue(scVirtual, package.data(scVirtual).toString());
+ setValue(scSortingPriority, package.data(scSortingPriority).toString());
+
+ setValue(scEssential, package.data(scEssential).toString());
+ setValue(scUpdateText, package.data(scUpdateText).toString());
+ setValue(scNewComponent, package.data(scNewComponent).toString());
+ setValue(scRequiresAdminRights, package.data(scRequiresAdminRights).toString());
+
+ setValue(scScript, package.data(scScript).toString());
+ setValue(scReplaces, package.data(scReplaces).toString());
+ setValue(scReleaseDate, package.data(scReleaseDate).toString());
+
+ QString forced = package.data(scForcedInstallation, scFalse).toString().toLower();
+ if (PackageManagerCore::noForceInstallation())
+ forced = scFalse;
+ setValue(scForcedInstallation, forced);
+ if (forced == scTrue) {
+ setEnabled(false);
+ setCheckable(false);
+ setCheckState(Qt::Checked);
+ }
+
+ setLocalTempPath(QInstaller::pathFromUrl(package.sourceInfo().url));
+ const QStringList uis = package.data(QLatin1String("UserInterfaces")).toString().split(scCommaRegExp,
+ QString::SkipEmptyParts);
+ if (!uis.isEmpty())
+ loadUserInterfaces(QDir(QString::fromLatin1("%1/%2").arg(localTempPath(), name())), uis);
+
+ const QStringList qms = package.data(QLatin1String("Translations")).toString().split(scCommaRegExp,
+ QString::SkipEmptyParts);
+ if (!qms.isEmpty())
+ loadTranslations(QDir(QString::fromLatin1("%1/%2").arg(localTempPath(), name())), qms);
+
+ QHash<QString, QVariant> licenseHash = package.data(QLatin1String("Licenses")).toHash();
+ if (!licenseHash.isEmpty())
+ loadLicenses(QString::fromLatin1("%1/%2/").arg(localTempPath(), name()), licenseHash);
+}
+
+quint64 Component::updateUncompressedSize()
+{
+ quint64 size = 0;
+
+ if (isSelected())
+ size = (quint64)value(scUncompressedSize).toDouble();
+
+ foreach (Component* comp, d->m_allChildComponents)
+ size += comp->updateUncompressedSize();
+
+ setValue(scUncompressedSizeSum, QString::number(size));
+ setData(uncompressedSize(), UncompressedSize);
+
+ return size;
+}
+
+QString Component::uncompressedSize() const
+{
+ double size = value(scUncompressedSizeSum).toDouble();
+ if (size < 1000.0)
+ return tr("%L1 Bytes").arg(size);
+ size /= 1024.0;
+ if (size < 1000.0)
+ return tr("%L1 kBytes").arg(size, 0, 'f', 2);
+ size /= 1024.0;
+ if (size < 1000.0)
+ return tr("%L1 MBytes").arg(size, 0, 'f', 2);
+ size /= 1024.0;
+
+ return tr("%L1 GBytes").arg(size, 0, 'f', 2);
+}
+
+void Component::markAsPerformedInstallation()
+{
+ d->m_newlyInstalled = true;
+}
+
+/*!
+ \property Component::removeBeforeUpdate
+ Specifies whether this component gets removed by the installer system before it gets updated. Get this
+ property's value by using %removeBeforeUpdate(), and set it using %setRemoveBeforeUpdate(). The default
+ value is true.
+*/
+bool Component::removeBeforeUpdate() const
+{
+ return d->m_removeBeforeUpdate;
+}
+
+void Component::setRemoveBeforeUpdate(bool removeBeforeUpdate)
+{
+ d->m_removeBeforeUpdate = removeBeforeUpdate;
+}
+
+/*
+ Returns a key/value based hash of all variables set for this component.
+*/
+QHash<QString,QString> Component::variables() const
+{
+ return d->m_vars;
+}
+
+/*!
+ Returns the value of variable name \a key.
+ If \a key is not known yet, \a defaultValue is returned.
+*/
+QString Component::value(const QString &key, const QString &defaultValue) const
+{
+ return d->m_vars.value(key, defaultValue);
+}
+
+/*!
+ Sets the value of the variable with \a key to \a value.
+*/
+void Component::setValue(const QString &key, const QString &value)
+{
+ if (d->m_vars.value(key) == value)
+ return;
+
+ if (key == scName)
+ d->m_componentName = value;
+
+ d->m_vars[key] = value;
+ emit valueChanged(key, value);
+}
+
+/*!
+ Returns the installer this component belongs to.
+*/
+PackageManagerCore *Component::packageManagerCore() const
+{
+ return d->m_core;
+}
+
+/*!
+ Returns the parent of this component. If this component is com.nokia.sdk.qt, its
+ parent is com.nokia.sdk, as far as this exists.
+*/
+Component *Component::parentComponent() const
+{
+ return d->m_parentComponent;
+}
+
+/*!
+ Appends \a component as a child of this component. If \a component already has a parent,
+ it is removed from the previous parent.
+*/
+void Component::appendComponent(Component *component)
+{
+ if (!component->isVirtual()) {
+ d->m_childComponents.append(component);
+ std::sort(d->m_childComponents.begin(), d->m_childComponents.end(), Component::SortingPriorityLessThan());
+ } else {
+ d->m_virtualChildComponents.append(component);
+ }
+
+ d->m_allChildComponents = d->m_childComponents + d->m_virtualChildComponents;
+ if (Component *parent = component->parentComponent())
+ parent->removeComponent(component);
+ component->d->m_parentComponent = this;
+ setTristate(d->m_childComponents.count() > 0);
+}
+
+/*!
+ Removes \a component if it is a child of this component. The component object still exists after the
+ function returns. It's up to the caller to delete the passed \a component.
+*/
+void Component::removeComponent(Component *component)
+{
+ if (component->parentComponent() == this) {
+ component->d->m_parentComponent = 0;
+ d->m_childComponents.removeAll(component);
+ d->m_virtualChildComponents.removeAll(component);
+ d->m_allChildComponents = d->m_childComponents + d->m_virtualChildComponents;
+ }
+}
+
+/*!
+ Returns a list of child components. If \a recursive is set to true, the returned list
+ contains not only the direct children, but all ancestors. Note: The returned list does include ALL
+ children, non virtual components as well as virtual components.
+*/
+QList<Component*> Component::childComponents(bool recursive, RunMode runMode) const
+{
+ QList<Component*> result;
+ if (runMode == UpdaterMode)
+ return result;
+
+ if (!recursive)
+ return d->m_allChildComponents;
+
+ foreach (Component *component, d->m_allChildComponents) {
+ result.append(component);
+ result += component->childComponents(true, runMode);
+ }
+ return result;
+}
+
+/*!
+ Contains this component's name (unique identifier).
+*/
+QString Component::name() const
+{
+ return d->m_componentName;
+}
+
+/*!
+ Contains this component's display name (as visible to the user).
+*/
+QString Component::displayName() const
+{
+ return value(scDisplayName);
+}
+
+void Component::loadComponentScript()
+{
+ const QString script = value(scScript);
+ if (!localTempPath().isEmpty() && !script.isEmpty())
+ loadComponentScript(QString::fromLatin1("%1/%2/%3").arg(localTempPath(), name(), script));
+}
+
+/*!
+ Loads the script at \a fileName into this component's script engine. The installer and all its
+ components as well as other useful stuff are being exported into the script.
+ Read \link componentscripting Component Scripting \endlink for details.
+ \throws Error when either the script at \a fileName couldn't be opened, or the QScriptEngine
+ couldn't evaluate the script.
+*/
+void Component::loadComponentScript(const QString &fileName)
+{
+ QFile file(fileName);
+ if (!file.open(QIODevice::ReadOnly)) {
+ throw Error(tr("Could not open the requested script file at %1: %2.").arg(fileName, file.errorString()));
+ }
+
+ d->scriptEngine()->evaluate(QLatin1String(file.readAll()), fileName);
+ if (d->scriptEngine()->hasUncaughtException()) {
+ throw Error(tr("Exception while loading the component script: %1")
+ .arg(uncaughtExceptionString(d->scriptEngine()/*, QFileInfo(file).absoluteFilePath()*/)));
+ }
+
+ const QList<Component*> components = d->m_core->availableComponents();
+ QScriptValue comps = d->scriptEngine()->newArray(components.count());
+ for (int i = 0; i < components.count(); ++i)
+ comps.setProperty(i, d->scriptEngine()->newQObject(components[i]));
+
+ d->scriptEngine()->globalObject().property(QLatin1String("installer"))
+ .setProperty(QLatin1String("components"), comps);
+
+ QScriptValue comp = d->scriptEngine()->evaluate(QLatin1String("Component"), fileName);
+ if (!d->scriptEngine()->hasUncaughtException()) {
+ d->m_scriptComponent = comp;
+ d->m_scriptComponent.construct();
+ }
+
+ //evaluate("Component") and construct can have an exception
+ if (d->scriptEngine()->hasUncaughtException()) {
+ throw Error(tr("Exception while loading the component script: %1")
+ .arg(uncaughtExceptionString(d->scriptEngine(), QFileInfo(file).absoluteFilePath())));
+ }
+
+ emit loaded();
+ languageChanged();
+
+ //Solves a freeze seen on updater/ package manger restart.
+ QCoreApplication::processEvents();
+}
+
+/*!
+ \internal
+ Calls the script method \link retranslateUi() \endlink, if any. This is done whenever a
+ QTranslator file is being loaded.
+*/
+void Component::languageChanged()
+{
+ callScriptMethod(QLatin1String("retranslateUi"));
+}
+
+/*!
+ Tries to call the method with \a name within the script and returns the result. If the method
+ doesn't exist, an invalid result is returned. If the method has an uncaught exception, its
+ string representation is thrown as an Error exception.
+
+ \note The method is not called, if the current script context is the same method, to avoid
+ infinite recursion.
+*/
+QScriptValue Component::callScriptMethod(const QString &methodName, const QScriptValueList &arguments) const
+{
+ if (!d->m_unexistingScriptMethods.value(methodName, true))
+ return QScriptValue();
+
+ // don't allow such a recursion
+ if (d->scriptEngine()->currentContext()->backtrace().first().startsWith(methodName))
+ return QScriptValue();
+
+ QScriptValue method = d->m_scriptComponent.property(QString::fromLatin1("prototype"))
+ .property(methodName);
+ if (!method.isValid()) // this marks the method to be called not any longer
+ d->m_unexistingScriptMethods[methodName] = false;
+
+ const QScriptValue result = method.call(d->m_scriptComponent, arguments);
+ if (!result.isValid())
+ return result;
+
+ if (d->scriptEngine()->hasUncaughtException())
+ throw Error(uncaughtExceptionString(d->scriptEngine()/*, name()*/));
+
+ return result;
+}
+
+/*!
+ Loads the translations matching the name filters \a qms inside \a directory. Only translations
+ with a \link QFileInfo::baseName() baseName \endlink matching the current locales \link
+ QLocale::name() name \endlink are loaded.
+ Read \ref componenttranslation for details.
+*/
+void Component::loadTranslations(const QDir &directory, const QStringList &qms)
+{
+ QDirIterator it(directory.path(), qms, QDir::Files);
+ while (it.hasNext()) {
+ const QString filename = it.next();
+ if (QFileInfo(filename).baseName().toLower() != QLocale().name().toLower())
+ continue;
+
+ QScopedPointer<QTranslator> translator(new QTranslator(this));
+ if (!translator->load(filename))
+ throw Error(tr("Could not open the requested translation file at %1").arg(filename));
+ qApp->installTranslator(translator.take());
+ }
+}
+
+/*!
+ Loads the user interface files matching the name filters \a uis inside \a directory. The loaded
+ interface can be accessed via userInterfaces by using the class name set in the ui file.
+ Read \ref componentuserinterfaces for details.
+*/
+void Component::loadUserInterfaces(const QDir &directory, const QStringList &uis)
+{
+ if (QApplication::type() == QApplication::Tty)
+ return;
+
+ QDirIterator it(directory.path(), uis, QDir::Files);
+ while (it.hasNext()) {
+ QFile file(it.next());
+ if (!file.open(QIODevice::ReadOnly)) {
+ throw Error(tr("Could not open the requested UI file at %1: %2").arg(it.fileName(),
+ file.errorString()));
+ }
+
+ static QUiLoader loader;
+ loader.setTranslationEnabled(true);
+ loader.setLanguageChangeEnabled(true);
+ QWidget *const w = loader.load(&file, MessageBoxHandler::currentBestSuitParent());
+ d->m_userInterfaces.insert(w->objectName(), w);
+ }
+}
+
+/*!
+ Loads the text of the Licenses contained in the licenseHash.
+ This is saved into a new hash containing the filename and the text of that file.
+*/
+void Component::loadLicenses(const QString &directory, const QHash<QString, QVariant> &licenseHash)
+{
+ QHash<QString, QVariant>::const_iterator it;
+ for (it = licenseHash.begin(); it != licenseHash.end(); ++it) {
+ const QString &fileName = it.value().toString();
+ QFileInfo fileInfo(fileName);
+ QFile file(QString::fromLatin1("%1%2_%3.%4").arg(directory, fileInfo.baseName(),
+ QLocale().name().toLower(), fileInfo.completeSuffix()));
+ if (!file.open(QIODevice::ReadOnly)) {
+ // No translated license, use untranslated file
+ qDebug("Unable to open translated license file. Using untranslated fallback.");
+ file.setFileName(directory + fileName);
+ if (!file.open(QIODevice::ReadOnly)) {
+ throw Error(tr("Could not open the requested license file at %1: %2").arg(fileName,
+ file.errorString()));
+ }
+ }
+ d->m_licenses.insert(it.key(), qMakePair(fileName, QTextStream(&file).readAll()));
+ }
+}
+
+/*!
+ Contains a list of all user interface class names known to this component.
+*/
+QStringList Component::userInterfaces() const
+{
+ return d->m_userInterfaces.keys();
+}
+
+QHash<QString, QPair<QString, QString> > Component::licenses() const
+{
+ return d->m_licenses;
+}
+
+/*!
+ Returns the QWidget created for \a name or 0 if the widget already has been deleted or cannot be found.
+*/
+QWidget *Component::userInterface(const QString &name) const
+{
+ return d->m_userInterfaces.value(name).data();
+}
+
+/*!
+ Creates all operations needed to install this component's \a path. \a path is a full qualified
+ filename including the component's name. This methods gets called from
+ Component::createOperationsForArchive. You can override this method by providing a method with
+ the same name in the component script.
+
+ \note RSA signature files are omitted by this method.
+ \note If you call this method from a script, it won't call the scripts method with the same name.
+
+ The default implementation is recursively creating Copy and Mkdir operations for all files
+ and folders within \a path.
+*/
+void Component::createOperationsForPath(const QString &path)
+{
+ const QFileInfo fi(path);
+
+ // don't copy over a checksum file
+ if (fi.suffix() == QLatin1String("sha1") && QFileInfo(fi.dir(), fi.completeBaseName()).exists())
+ return;
+
+ // the script can override this method
+ if (callScriptMethod(QLatin1String("createOperationsForPath"), QScriptValueList() << path).isValid())
+ return;
+
+ QString target;
+ static const QString zipPrefix = QString::fromLatin1("7z://installer://");
+ // if the path is an archive, remove the archive file name from the target path
+ if (path.startsWith(zipPrefix)) {
+ target = path.mid(zipPrefix.length() + name().length() + 1); // + 1 for the /
+ const int nextSlash = target.indexOf(QLatin1Char('/'));
+ if (nextSlash != -1)
+ target = target.mid(nextSlash);
+ else
+ target.clear();
+ target.prepend(QLatin1String("@TargetDir@"));
+ } else {
+ static const QString prefix = QString::fromLatin1("installer://");
+ target = QString::fromLatin1("@TargetDir@%1").arg(path.mid(prefix.length() + name().length()));
+ }
+
+ if (fi.isFile()) {
+ static const QString copy = QString::fromLatin1("Copy");
+ addOperation(copy, fi.filePath(), target);
+ } else if (fi.isDir()) {
+ qApp->processEvents();
+ static const QString mkdir = QString::fromLatin1("Mkdir");
+ addOperation(mkdir, target);
+
+ QDirIterator it(fi.filePath());
+ while (it.hasNext())
+ createOperationsForPath(it.next());
+ }
+}
+
+/*!
+ Creates all operations needed to install this component's \a archive. This method gets called
+ from Component::createOperations. You can override this method by providing a method with the
+ same name in the component script.
+
+ \note If you call this method from a script, it won't call the scripts method with the same name.
+
+ The default implementation calls createOperationsForPath for everything contained in the archive.
+ If \a archive is a compressed archive known to the installer system, an Extract operation is
+ created, instead.
+*/
+void Component::createOperationsForArchive(const QString &archive)
+{
+ // the script can override this method
+ if (callScriptMethod(QLatin1String("createOperationsForArchive"), QScriptValueList() << archive).isValid())
+ return;
+
+ const QFileInfo fi(QString::fromLatin1("installer://%1/%2").arg(name(), archive));
+ const bool isZip = Lib7z::isSupportedArchive(fi.filePath());
+
+ if (isZip) {
+ // archives get completely extracted per default (if the script isn't doing other stuff)
+ addOperation(QLatin1String("Extract"), fi.filePath(), QLatin1String("@TargetDir@"));
+ } else {
+ createOperationsForPath(fi.filePath());
+ }
+}
+
+void Component::beginInstallation()
+{
+ // the script can override this method
+ if (callScriptMethod(QLatin1String("beginInstallation")).isValid()) {
+ return;
+ }
+}
+
+
+/*!
+ Creates all operations needed to install this component.
+ You can override this method by providing a method with the same name in the component script.
+
+ \note If you call this method from a script, it won't call the scripts method with the same name.
+
+ The default implementation calls createOperationsForArchive for all archives in this component.
+*/
+void Component::createOperations()
+{
+ // the script can override this method
+ if (callScriptMethod(QLatin1String("createOperations")).isValid()) {
+ d->m_operationsCreated = true;
+ return;
+ }
+
+ foreach (const QString &archive, archives())
+ createOperationsForArchive(archive);
+
+ d->m_operationsCreated = true;
+}
+
+/*!
+ Registers the file or directory at \a path for being removed when this component gets uninstalled.
+ In case of a directory, this will be recursive. If \a wipe is set to true, the directory will
+ also be deleted if it contains changes done by the user after installation.
+*/
+void Component::registerPathForUninstallation(const QString &path, bool wipe)
+{
+ d->m_pathesForUninstallation.append(qMakePair(path, wipe));
+}
+
+/*!
+ Returns the list of paths previously registered for uninstallation with
+ #registerPathForUninstallation.
+*/
+QList<QPair<QString, bool> > Component::pathesForUninstallation() const
+{
+ return d->m_pathesForUninstallation;
+}
+
+/*!
+ Contains the names of all archives known to this component. This does not contain archives added
+ with #addDownloadableArchive.
+*/
+QStringList Component::archives() const
+{
+ return QDir(QString::fromLatin1("installer://%1/").arg(name())).entryList();
+}
+
+/*!
+ Adds the archive \a path to this component. This can only be called when this component was
+ downloaded from an online repository. When adding \a path, it will be downloaded from the
+ repository when the installation starts.
+
+ Read \ref sec_repogen for details. \sa fromOnlineRepository
+*/
+void Component::addDownloadableArchive(const QString &path)
+{
+ Q_ASSERT(isFromOnlineRepository());
+
+ const QString versionPrefix = value(scRemoteVersion);
+ qDebug() << "addDownloadable" << path;
+ d->m_downloadableArchives.append(versionPrefix + path);
+}
+
+/*!
+ Removes the archive \a path previously added via addDownloadableArchive from this component.
+ This can only be called when this component was downloaded from an online repository.
+
+ Read \ref sec_repogen for details.
+*/
+void Component::removeDownloadableArchive(const QString &path)
+{
+ Q_ASSERT(isFromOnlineRepository());
+ d->m_downloadableArchives.removeAll(path);
+}
+
+/*!
+ Returns the archives to be downloaded from the online repository before installation.
+*/
+QStringList Component::downloadableArchives() const
+{
+ return d->m_downloadableArchives;
+}
+
+/*!
+ Adds a request for quitting the process @p process before installing/updating/uninstalling the
+ component.
+*/
+void Component::addStopProcessForUpdateRequest(const QString &process)
+{
+ d->m_stopProcessForUpdateRequests.append(process);
+}
+
+/*!
+ Removes the request for quitting the process @p process again.
+*/
+void Component::removeStopProcessForUpdateRequest(const QString &process)
+{
+ d->m_stopProcessForUpdateRequests.removeAll(process);
+}
+
+/*!
+ Convenience: Add/remove request depending on @p requested (add if @p true, remove if @p false).
+*/
+void Component::setStopProcessForUpdateRequest(const QString &process, bool requested)
+{
+ if (requested)
+ addStopProcessForUpdateRequest(process);
+ else
+ removeStopProcessForUpdateRequest(process);
+}
+
+/*!
+ The list of processes this component needs to be closed before installing/updating/uninstalling
+*/
+QStringList Component::stopProcessForUpdateRequests() const
+{
+ return d->m_stopProcessForUpdateRequests;
+}
+
+/*!
+ Returns the operations needed to install this component. If autoCreateOperations is true,
+ createOperations is called, if no operations have been auto-created yet.
+*/
+OperationList Component::operations() const
+{
+ if (d->m_autoCreateOperations && !d->m_operationsCreated) {
+ const_cast<Component*>(this)->createOperations();
+
+ if (!d->m_minimumProgressOperation) {
+ d->m_minimumProgressOperation = KDUpdater::UpdateOperationFactory::instance()
+ .create(QLatin1String("MinimumProgress"));
+ d->m_operations.append(d->m_minimumProgressOperation);
+ }
+
+ if (!d->m_licenses.isEmpty()) {
+ d->m_licenseOperation = KDUpdater::UpdateOperationFactory::instance()
+ .create(QLatin1String("License"));
+ d->m_licenseOperation->setValue(QLatin1String("installer"), QVariant::fromValue(d->m_core));
+
+ QVariantMap licenses;
+ const QList<QPair<QString, QString> > values = d->m_licenses.values();
+ for (int i = 0; i < values.count(); ++i)
+ licenses.insert(values.at(i).first, values.at(i).second);
+ d->m_licenseOperation->setValue(QLatin1String("licenses"), licenses);
+ d->m_operations.append(d->m_licenseOperation);
+ }
+ }
+ return d->m_operations;
+}
+
+/*!
+ Adds \a operation to the list of operations needed to install this component.
+*/
+void Component::addOperation(Operation *operation)
+{
+ d->m_operations.append(operation);
+ if (FSEngineClientHandler::instance().isActive())
+ operation->setValue(QLatin1String("admin"), true);
+}
+
+/*!
+ Adds \a operation to the list of operations needed to install this component. \a operation
+ is executed with elevated rights.
+*/
+void Component::addElevatedOperation(Operation *operation)
+{
+ if (value(scRequiresAdminRights, scFalse) != scTrue) {
+ qWarning() << QString::fromLatin1("component %1 uses addElevatedOperation in the script, but it doesn't"
+ "have the needed RequiresAdminRights tag").arg(name());
+ }
+ addOperation(operation);
+ operation->setValue(QLatin1String("admin"), true);
+}
+
+bool Component::operationsCreatedSuccessfully() const
+{
+ return d->m_operationsCreatedSuccessfully;
+}
+
+Operation *Component::createOperation(const QString &operation, const QString &parameter1,
+ const QString &parameter2, const QString &parameter3, const QString &parameter4, const QString &parameter5,
+ const QString &parameter6, const QString &parameter7, const QString &parameter8, const QString &parameter9,
+ const QString &parameter10)
+{
+ Operation *op = KDUpdater::UpdateOperationFactory::instance().create(operation);
+ if (op == 0) {
+ const QMessageBox::StandardButton button =
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("OperationDoesNotExistError"), tr("Error"), tr("Error: Operation %1 does not exist")
+ .arg(operation), QMessageBox::Abort | QMessageBox::Ignore);
+ if (button == QMessageBox::Abort)
+ d->m_operationsCreatedSuccessfully = false;
+ return op;
+ }
+
+ if (op->name() == QLatin1String("Delete"))
+ op->setValue(QLatin1String("performUndo"), false);
+ op->setValue(QLatin1String("installer"), qVariantFromValue(d->m_core));
+
+ QStringList arguments;
+ if (!parameter1.isNull())
+ arguments.append(parameter1);
+ if (!parameter2.isNull())
+ arguments.append(parameter2);
+ if (!parameter3.isNull())
+ arguments.append(parameter3);
+ if (!parameter4.isNull())
+ arguments.append(parameter4);
+ if (!parameter5.isNull())
+ arguments.append(parameter5);
+ if (!parameter6.isNull())
+ arguments.append(parameter6);
+ if (!parameter7.isNull())
+ arguments.append(parameter7);
+ if (!parameter8.isNull())
+ arguments.append(parameter8);
+ if (!parameter9.isNull())
+ arguments.append(parameter9);
+ if (!parameter10.isNull())
+ arguments.append(parameter10);
+ op->setArguments(d->m_core->replaceVariables(arguments));
+
+ return op;
+}
+/*!
+ Creates and adds an installation operation for \a operation. Add any number of \a parameter1,
+ \a parameter2, \a parameter3, \a parameter4, \a parameter5 and \a parameter6. The contents of
+ the parameters get variables like "@TargetDir@" replaced with their values, if contained.
+ \sa installeroperations
+*/
+bool Component::addOperation(const QString &operation, const QString &parameter1, const QString &parameter2,
+ const QString &parameter3, const QString &parameter4, const QString &parameter5, const QString &parameter6,
+ const QString &parameter7, const QString &parameter8, const QString &parameter9, const QString &parameter10)
+{
+ if (Operation *op = createOperation(operation, parameter1, parameter2, parameter3, parameter4, parameter5,
+ parameter6, parameter7, parameter8, parameter9, parameter10)) {
+ addOperation(op);
+ return true;
+ }
+
+ return false;
+}
+
+/*!
+ Creates and adds an installation operation for \a operation. Add any number of \a parameter1,
+ \a parameter2, \a parameter3, \a parameter4, \a parameter5 and \a parameter6. The contents of
+ the parameters get variables like "@TargetDir@" replaced with their values, if contained.
+ \a operation is executed with elevated rights.
+ \sa installeroperations
+*/
+bool Component::addElevatedOperation(const QString &operation, const QString &parameter1,
+ const QString &parameter2, const QString &parameter3, const QString &parameter4, const QString &parameter5,
+ const QString &parameter6, const QString &parameter7, const QString &parameter8, const QString &parameter9,
+ const QString &parameter10)
+{
+ if (Operation *op = createOperation(operation, parameter1, parameter2, parameter3, parameter4, parameter5,
+ parameter6, parameter7, parameter8, parameter9, parameter10)) {
+ addElevatedOperation(op);
+ return true;
+ }
+
+ return false;
+}
+
+/*!
+ Specifies whether operations should be automatically created when the installation starts. This
+ would be done by calling #createOperations. If you set this to false, it's completely up to the
+ component's script to create all operations.
+*/
+bool Component::autoCreateOperations() const
+{
+ return d->m_autoCreateOperations;
+}
+
+void Component::setAutoCreateOperations(bool autoCreateOperations)
+{
+ d->m_autoCreateOperations = autoCreateOperations;
+}
+
+bool Component::isVirtual() const
+{
+ return value(scVirtual, scFalse).toLower() == scTrue;
+}
+
+/*!
+ \property Component::selected
+ Specifies whether this component is selected for installation. Get this property's value by using
+ %isSelected(), and set it using %setSelected().
+*/
+bool Component::isSelected() const
+{
+ return checkState() != Qt::Unchecked;
+}
+
+bool Component::forcedInstallation() const
+{
+ return value(scForcedInstallation, scFalse).toLower() == scTrue;
+}
+
+/*!
+ Marks the component for installation. Emits the selectedChanged() signal if the check state changes.
+*/
+void Component::setSelected(bool selected)
+{
+ Q_UNUSED(selected)
+ qDebug() << Q_FUNC_INFO << QString::fromLatin1("on \"%1\" is deprecated!!!").arg(d->m_componentName);
+}
+
+void Component::addDependency(const QString &newDependency)
+{
+ QString oldDependencies = value(scDependencies);
+ if (oldDependencies.isEmpty())
+ setValue(scDependencies, newDependency);
+ else
+ setValue(scDependencies, oldDependencies + QLatin1String(", ") + newDependency);
+}
+
+
+/*!
+ Contains this component dependencies.
+ Read \ref componentdependencies for details.
+*/
+QStringList Component::dependencies() const
+{
+ return value(scDependencies).split(scCommaRegExp, QString::SkipEmptyParts);
+}
+
+QStringList Component::autoDependencies() const
+{
+ QStringList autoDependencyStringList =
+ value(scAutoDependOn).split(scCommaRegExp, QString::SkipEmptyParts);
+ autoDependencyStringList.removeAll(QLatin1String("script"));
+ return autoDependencyStringList;
+}
+
+/*!
+ Set's the components state to installed.
+*/
+void Component::setInstalled()
+{
+ setValue(scCurrentState, scInstalled);
+}
+
+/*!
+ Determines if the component comes as an auto dependency. Returns true if the component needs to be
+ installed.
+*/
+bool Component::isAutoDependOn(const QSet<QString> &componentsToInstall) const
+{
+ // If there is no auto depend on value or the value is empty, we have nothing todo. The component does
+ // not need to be installed as an auto dependency.
+ QStringList autoDependOnList = autoDependencies();
+ if (autoDependOnList.isEmpty())
+ return false;
+
+ // The script can override this method and determines if the component needs to be installed.
+ if (autoDependOnList.first().compare(QLatin1String("script"), Qt::CaseInsensitive) == 0) {
+ QScriptValue valueFromScript;
+ try {
+ valueFromScript = callScriptMethod(QLatin1String("isAutoDependOn"));
+ } catch (const Error &error) {
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("isAutoDependOnError"), tr("Can't resolve isAutoDependOn in %1"
+ ).arg(name()), error.message());
+ return false;
+ }
+
+ if (valueFromScript.isValid())
+ return valueFromScript.toBool();
+ qDebug() << "value from script is not valid";
+ return false;
+ }
+
+ QSet<QString> components = componentsToInstall;
+ const QStringList installedPackages = d->m_core->localInstalledPackages().keys();
+ foreach (const QString &name, installedPackages)
+ components.insert(name);
+
+ foreach (const QString &component, components) {
+ autoDependOnList.removeAll(component);
+ if (autoDependOnList.isEmpty()) {
+ // If all components in the isAutoDependOn field are already installed or selected for
+ // installation, this component needs to be installed as well.
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*!
+ Determines if the component is a default one.
+*/
+bool Component::isDefault() const
+{
+ // the script can override this method
+ if (value(scDefault).compare(QLatin1String("script"), Qt::CaseInsensitive) == 0) {
+ QScriptValue valueFromScript;
+ try {
+ valueFromScript = callScriptMethod(QLatin1String("isDefault"));
+ } catch (const Error &error) {
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("isDefaultError"), tr("Can't resolve isDefault in %1").arg(name()),
+ error.message());
+ return false;
+ }
+ if (valueFromScript.isValid())
+ return valueFromScript.toBool();
+ qDebug() << "value from script is not valid";
+ return false;
+ }
+
+ return value(scDefault).compare(scTrue, Qt::CaseInsensitive) == 0;
+}
+
+/*!
+ Determines if the component is installed.
+*/
+bool Component::isInstalled() const
+{
+ return scInstalled == value(scCurrentState);
+}
+
+/*!
+ Determines if the user wants to install the component
+*/
+bool Component::installationRequested() const
+{
+ return !isInstalled() && isSelected();
+}
+
+/*!
+ Sets a flag that the core found an update
+*/
+void Component::setUpdateAvailable(bool isUpdateAvailable)
+{
+ d->m_updateIsAvailable = isUpdateAvailable;
+}
+
+/*!
+ Determines if the user wants to install the update for this component
+*/
+bool Component::updateRequested()
+{
+ return d->m_updateIsAvailable && isSelected();
+}
+
+/*!
+ Returns true if that component will be changed (update/installation/uninstallation)
+*/
+bool Component::componentChangeRequested()
+{
+ return updateRequested() || installationRequested() || uninstallationRequested();
+}
+
+
+/*!
+ Sets the component state to uninstalled.
+*/
+void Component::setUninstalled()
+{
+ setValue(scCurrentState, scUninstalled);
+}
+
+/*!
+ Determines if the component is uninstalled.
+*/
+bool Component::isUninstalled() const
+{
+ return scUninstalled == value(scCurrentState);
+}
+
+/*!
+ Determines if the user wants to uninstall the component.
+*/
+bool Component::uninstallationRequested() const
+{
+ if (packageManagerCore()->isUpdater())
+ return false;
+ return isInstalled() && !isSelected();
+}
+
+/*!
+ \property Component::fromOnlineRepository
+
+ Determines whether this component has been loaded from an online repository. Get this property's
+ value by using %isFromOnlineRepository. \sa addDownloadableArchive
+*/
+bool Component::isFromOnlineRepository() const
+{
+ return !repositoryUrl().isEmpty();
+}
+
+/*!
+ Contains the repository Url this component is downloaded from.
+ When this component is not downloaded from an online repository, returns an empty #QUrl.
+*/
+QUrl Component::repositoryUrl() const
+{
+ return d->m_repositoryUrl;
+}
+
+/*!
+ Sets this components #repositoryUrl.
+*/
+void Component::setRepositoryUrl(const QUrl &url)
+{
+ d->m_repositoryUrl = url;
+}
+
+
+QString Component::localTempPath() const
+{
+ return d->m_localTempPath;
+}
+
+void Component::setLocalTempPath(const QString &tempLocalPath)
+{
+ d->m_localTempPath = tempLocalPath;
+}
+
+void Component::updateModelData(const QString &key, const QString &data)
+{
+ if (key == scVirtual) {
+ if (data.toLower() == scTrue)
+ setData(d->m_core->virtualComponentsFont(), Qt::FontRole);
+ }
+
+ if (key == scRemoteDisplayVersion)
+ setData(data, RemoteDisplayVersion);
+
+ if (key == scDisplayName)
+ setData(data, Qt::DisplayRole);
+
+ if (key == scDisplayVersion)
+ setData(data, LocalDisplayVersion);
+
+ if (key == scUncompressedSize)
+ setData(uncompressedSize(), UncompressedSize);
+
+ const QString &updateInfo = value(scUpdateText);
+ if (!d->m_core->isUpdater() || updateInfo.isEmpty()) {
+ setData(QLatin1String("<html><body>") + value(scDescription) + QLatin1String("</body></html>"),
+ Qt::ToolTipRole);
+ } else {
+ setData(value(scDescription) + QLatin1String("<br><br>") + tr("Update Info: ") + updateInfo, Qt::ToolTipRole);
+ }
+}
+
+
+QDebug QInstaller::operator<<(QDebug dbg, Component *component)
+{
+ dbg << "component: " << component->name() << "\n";
+ dbg << "\tisSelected: \t" << component->isSelected() << "\n";
+ dbg << "\tisInstalled: \t" << component->isInstalled() << "\n";
+ dbg << "\tisUninstalled: \t" << component->isUninstalled() << "\n";
+ dbg << "\tupdateRequested: \t" << component->updateRequested() << "\n";
+ dbg << "\tinstallationRequested: \t" << component->installationRequested() << "\n";
+ dbg << "\tuninstallationRequested: \t" << component->uninstallationRequested() << "\n";
+ return dbg;
+}
diff --git a/src/libs/installer/component.h b/src/libs/installer/component.h
new file mode 100644
index 000000000..14d16722c
--- /dev/null
+++ b/src/libs/installer/component.h
@@ -0,0 +1,233 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef COMPONENT_H
+#define COMPONENT_H
+
+#include "constants.h"
+#include "component_p.h"
+#include "qinstallerglobal.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QMetaType>
+#include <QtCore/QObject>
+#include <QtCore/QUrl>
+
+#include <QtScript/QScriptable>
+#include <QtScript/QScriptValueList>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+
+namespace KDUpdater {
+ class Update;
+ struct PackageInfo;
+}
+
+namespace QInstaller {
+
+class PackageManagerCore;
+
+class INSTALLER_EXPORT Component : public QObject, public QScriptable, public ComponentModelHelper
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(Component)
+
+ Q_PROPERTY(QString name READ name)
+ Q_PROPERTY(QString displayName READ displayName)
+ Q_PROPERTY(bool selected READ isSelected WRITE setSelected)
+ Q_PROPERTY(bool autoCreateOperations READ autoCreateOperations WRITE setAutoCreateOperations)
+ Q_PROPERTY(QStringList archives READ archives)
+ Q_PROPERTY(QStringList userInterfaces READ userInterfaces)
+ Q_PROPERTY(QStringList dependencies READ dependencies)
+ Q_PROPERTY(QStringList autoDependencies READ autoDependencies)
+ Q_PROPERTY(bool fromOnlineRepository READ isFromOnlineRepository)
+ Q_PROPERTY(QUrl repositoryUrl READ repositoryUrl)
+ Q_PROPERTY(bool removeBeforeUpdate READ removeBeforeUpdate WRITE setRemoveBeforeUpdate)
+ Q_PROPERTY(bool default READ isDefault)
+ Q_PROPERTY(bool installed READ isInstalled)
+ Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
+
+public:
+ explicit Component(PackageManagerCore *core);
+ ~Component();
+
+ struct SortingPriorityLessThan
+ {
+ bool operator() (const Component *lhs, const Component *rhs) const
+ {
+ return lhs->value(scSortingPriority).toInt() < rhs->value(scSortingPriority).toInt();
+ }
+ };
+
+ void loadDataFromPackage(const Package &package);
+ void loadDataFromPackage(const LocalPackage &package);
+
+ QHash<QString, QString> variables() const;
+ Q_INVOKABLE void setValue(const QString &key, const QString &value);
+ Q_INVOKABLE QString value(const QString &key, const QString &defaultValue = QString()) const;
+
+ QStringList archives() const;
+ PackageManagerCore *packageManagerCore() const;
+
+ Component *parentComponent() const;
+ void appendComponent(Component *component);
+ void removeComponent(Component *component);
+ QList<Component*> childComponents(bool recursive, RunMode runMode) const;
+
+ void loadComponentScript();
+
+ //move this to private
+ void loadComponentScript(const QString &fileName);
+ void loadTranslations(const QDir &directory, const QStringList &qms);
+ void loadUserInterfaces(const QDir &directory, const QStringList &uis);
+ void loadLicenses(const QString &directory, const QHash<QString, QVariant> &hash);
+ void markAsPerformedInstallation();
+
+ QStringList userInterfaces() const;
+ QHash<QString, QPair<QString, QString> > licenses() const;
+ Q_INVOKABLE QWidget *userInterface(const QString &name) const;
+ Q_INVOKABLE virtual void beginInstallation();
+ Q_INVOKABLE virtual void createOperations();
+ Q_INVOKABLE virtual void createOperationsForArchive(const QString &archive);
+ Q_INVOKABLE virtual void createOperationsForPath(const QString &path);
+
+ Q_INVOKABLE QList<QPair<QString, bool> > pathesForUninstallation() const;
+ Q_INVOKABLE void registerPathForUninstallation(const QString &path, bool wipe = false);
+
+ OperationList operations() const;
+
+ void addOperation(Operation *operation);
+ Q_INVOKABLE bool addOperation(const QString &operation, const QString &parameter1 = QString(),
+ const QString &parameter2 = QString(), const QString &parameter3 = QString(),
+ const QString &parameter4 = QString(), const QString &parameter5 = QString(),
+ const QString &parameter6 = QString(), const QString &parameter7 = QString(),
+ const QString &parameter8 = QString(), const QString &parameter9 = QString(),
+ const QString &parameter10 = QString());
+
+ void addElevatedOperation(Operation *operation);
+ Q_INVOKABLE bool addElevatedOperation(const QString &operation,
+ const QString &parameter1 = QString(), const QString &parameter2 = QString(),
+ const QString &parameter3 = QString(), const QString &parameter4 = QString(),
+ const QString &parameter5 = QString(), const QString &parameter6 = QString(),
+ const QString &parameter7 = QString(), const QString &parameter8 = QString(),
+ const QString &parameter9 = QString(), const QString &parameter10 = QString());
+
+ QStringList downloadableArchives() const;
+ Q_INVOKABLE void addDownloadableArchive(const QString &path);
+ Q_INVOKABLE void removeDownloadableArchive(const QString &path);
+
+ QStringList stopProcessForUpdateRequests() const;
+ Q_INVOKABLE void addStopProcessForUpdateRequest(const QString &process);
+ Q_INVOKABLE void removeStopProcessForUpdateRequest(const QString &process);
+ Q_INVOKABLE void setStopProcessForUpdateRequest(const QString &process, bool requested);
+
+ QString name() const;
+ QString displayName() const;
+ QString uncompressedSize() const;
+ quint64 updateUncompressedSize();
+
+ QUrl repositoryUrl() const;
+ void setRepositoryUrl(const QUrl &url);
+
+ bool removeBeforeUpdate() const;
+ void setRemoveBeforeUpdate(bool removeBeforeUpdate);
+
+ Q_INVOKABLE void addDependency(const QString &newDependency);
+ QStringList dependencies() const;
+ QStringList autoDependencies() const;
+
+ void languageChanged();
+ QString localTempPath() const;
+
+ bool autoCreateOperations() const;
+ bool operationsCreatedSuccessfully() const;
+
+ Q_INVOKABLE bool isDefault() const;
+ Q_INVOKABLE bool isAutoDependOn(const QSet<QString> &componentsToInstall) const;
+
+ Q_INVOKABLE void setInstalled();
+ Q_INVOKABLE bool isInstalled() const;
+ Q_INVOKABLE bool installationRequested() const;
+
+ Q_INVOKABLE void setUninstalled();
+ Q_INVOKABLE bool isUninstalled() const;
+ Q_INVOKABLE bool uninstallationRequested() const;
+
+ Q_INVOKABLE bool isFromOnlineRepository() const;
+
+ Q_INVOKABLE void setUpdateAvailable(bool isUpdateAvailable);
+ Q_INVOKABLE bool updateRequested();
+
+ Q_INVOKABLE bool componentChangeRequested();
+
+
+ bool isVirtual() const;
+ bool isSelected() const;
+ bool forcedInstallation() const;
+
+public Q_SLOTS:
+ void setSelected(bool selected);
+ void setAutoCreateOperations(bool autoCreateOperations);
+
+Q_SIGNALS:
+ void loaded();
+ void selectedChanged(bool selected);
+ void valueChanged(const QString &key, const QString &value);
+
+protected:
+ QScriptValue callScriptMethod(const QString &name,
+ const QScriptValueList &parameters = QScriptValueList()) const;
+
+private Q_SLOTS:
+ void updateModelData(const QString &key, const QString &value);
+
+private:
+ void setLocalTempPath(const QString &tempPath);
+
+ Operation *createOperation(const QString &operation, const QString &parameter1 = QString(),
+ const QString &parameter2 = QString(), const QString &parameter3 = QString(),
+ const QString &parameter4 = QString(), const QString &parameter5 = QString(),
+ const QString &parameter6 = QString(), const QString &parameter7 = QString(),
+ const QString &parameter8 = QString(), const QString &parameter9 = QString(),
+ const QString &parameter10 = QString());
+
+private:
+ ComponentPrivate *d;
+};
+
+QDebug operator<<(QDebug dbg, Component *component);
+
+} // namespace QInstaller
+
+Q_DECLARE_METATYPE(QInstaller::Component*)
+
+#endif // COMPONENT_H
diff --git a/src/libs/installer/component_p.cpp b/src/libs/installer/component_p.cpp
new file mode 100644
index 000000000..4294ac8a8
--- /dev/null
+++ b/src/libs/installer/component_p.cpp
@@ -0,0 +1,359 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "component_p.h"
+
+#include "component.h"
+#include "messageboxhandler.h"
+#include "packagemanagercore.h"
+
+#include <QtGui/QApplication>
+#include <QtGui/QDesktopServices>
+
+
+namespace QInstaller {
+
+// -- ComponentPrivate
+
+ComponentPrivate::ComponentPrivate(PackageManagerCore *core, Component *qq)
+ : q(qq),
+ m_core(core),
+ m_parentComponent(0),
+ m_licenseOperation(0),
+ m_minimumProgressOperation(0),
+ m_newlyInstalled (false),
+ m_operationsCreated(false),
+ m_removeBeforeUpdate(true),
+ m_autoCreateOperations(true),
+ m_operationsCreatedSuccessfully(true),
+ m_updateIsAvailable(false),
+ m_scriptEngine(0)
+{
+}
+
+ComponentPrivate::~ComponentPrivate()
+{
+ // Before we can delete the added widgets, they need to be removed from the wizard first.
+ QMap<QString, QPointer<QWidget> >::const_iterator it;
+ foreach (const QString &widgetName, m_userInterfaces.keys()) {
+ m_core->removeWizardPage(q, widgetName);
+ m_core->removeWizardPageItem(q, widgetName);
+ }
+
+ // Use QPointer here instead of raw pointers. This is a requirement that needs to be met cause possible
+ // Ui elements get added during component script run and might be destroyed by the package manager gui
+ // before the actual component gets destroyed. Avoids a possible delete call on a dangling pointer.
+ foreach (const QPointer<QWidget> widget, m_userInterfaces)
+ delete widget.data();
+}
+
+QScriptEngine *ComponentPrivate::scriptEngine()
+{
+ if (m_scriptEngine != 0)
+ return m_scriptEngine;
+
+
+ m_scriptEngine = new QScriptEngine(q);
+
+ // register translation stuff
+ m_scriptEngine->installTranslatorFunctions();
+
+ // register QMessageBox::StandardButton enum in the script connection
+ registerMessageBox(m_scriptEngine);
+
+ // register QDesktopServices in the script connection
+ QScriptValue desktopServices = m_scriptEngine->newArray();
+ setProperty(desktopServices, QLatin1String("DesktopLocation"), QDesktopServices::DesktopLocation);
+ setProperty(desktopServices, QLatin1String("DocumentsLocation"), QDesktopServices::DocumentsLocation);
+ setProperty(desktopServices, QLatin1String("FontsLocation"), QDesktopServices::FontsLocation);
+ setProperty(desktopServices, QLatin1String("ApplicationsLocation"), QDesktopServices::ApplicationsLocation);
+ setProperty(desktopServices, QLatin1String("MusicLocation"), QDesktopServices::MusicLocation);
+ setProperty(desktopServices, QLatin1String("MoviesLocation"), QDesktopServices::MoviesLocation);
+ setProperty(desktopServices, QLatin1String("PicturesLocation"), QDesktopServices::PicturesLocation);
+ setProperty(desktopServices, QLatin1String("TempLocation"), QDesktopServices::TempLocation);
+ setProperty(desktopServices, QLatin1String("HomeLocation"), QDesktopServices::HomeLocation);
+ setProperty(desktopServices, QLatin1String("DataLocation"), QDesktopServices::DataLocation);
+ setProperty(desktopServices, QLatin1String("CacheLocation"), QDesktopServices::CacheLocation);
+
+ desktopServices.setProperty(QLatin1String("openUrl"), m_scriptEngine->newFunction(qDesktopServicesOpenUrl));
+ desktopServices.setProperty(QLatin1String("displayName"),
+ m_scriptEngine->newFunction(qDesktopServicesDisplayName));
+ desktopServices.setProperty(QLatin1String("storageLocation"),
+ m_scriptEngine->newFunction(qDesktopServicesStorageLocation));
+
+ // register ::WizardPage enum in the script connection
+ QScriptValue qinstaller = m_scriptEngine->newArray();
+ setProperty(qinstaller, QLatin1String("Introduction"), PackageManagerCore::Introduction);
+ setProperty(qinstaller, QLatin1String("LicenseCheck"), PackageManagerCore::LicenseCheck);
+ setProperty(qinstaller, QLatin1String("TargetDirectory"), PackageManagerCore::TargetDirectory);
+ setProperty(qinstaller, QLatin1String("ComponentSelection"), PackageManagerCore::ComponentSelection);
+ setProperty(qinstaller, QLatin1String("StartMenuSelection"), PackageManagerCore::StartMenuSelection);
+ setProperty(qinstaller, QLatin1String("ReadyForInstallation"), PackageManagerCore::ReadyForInstallation);
+ setProperty(qinstaller, QLatin1String("PerformInstallation"), PackageManagerCore::PerformInstallation);
+ setProperty(qinstaller, QLatin1String("InstallationFinished"), PackageManagerCore::InstallationFinished);
+ setProperty(qinstaller, QLatin1String("End"), PackageManagerCore::End);
+
+ // register ::Status enum in the script connection
+ setProperty(qinstaller, QLatin1String("Success"), PackageManagerCore::Success);
+ setProperty(qinstaller, QLatin1String("Failure"), PackageManagerCore::Failure);
+ setProperty(qinstaller, QLatin1String("Running"), PackageManagerCore::Running);
+ setProperty(qinstaller, QLatin1String("Canceled"), PackageManagerCore::Canceled);
+
+ // maybe used by old scripts
+ setProperty(qinstaller, QLatin1String("InstallerFailed"), PackageManagerCore::Failure);
+ setProperty(qinstaller, QLatin1String("InstallerSucceeded"), PackageManagerCore::Success);
+ setProperty(qinstaller, QLatin1String("InstallerUnfinished"), PackageManagerCore::Unfinished);
+ setProperty(qinstaller, QLatin1String("InstallerCanceledByUser"), PackageManagerCore::Canceled);
+
+ QScriptValue installerObject = m_scriptEngine->newQObject(m_core);
+ installerObject.setProperty(QLatin1String("componentByName"), m_scriptEngine
+ ->newFunction(qInstallerComponentByName, 1));
+
+ m_scriptEngine->globalObject().setProperty(QLatin1String("QInstaller"), qinstaller);
+ m_scriptEngine->globalObject().setProperty(QLatin1String("installer"), installerObject);
+ m_scriptEngine->globalObject().setProperty(QLatin1String("QDesktopServices"), desktopServices);
+ m_scriptEngine->globalObject().setProperty(QLatin1String("component"), m_scriptEngine->newQObject(q));
+
+ return m_scriptEngine;
+}
+
+void ComponentPrivate::setProperty(QScriptValue &scriptValue, const QString &propertyName, int value)
+{
+ scriptValue.setProperty(propertyName, m_scriptEngine->newVariant(value));
+}
+
+
+// -- ComponentModelHelper
+
+ComponentModelHelper::ComponentModelHelper()
+{
+ setCheckState(Qt::Unchecked);
+ setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable);
+}
+
+/*!
+ Returns the number of child components. Depending if virtual components are visible or not the count might
+ differ from what one will get if calling Component::childComponents(...).count().
+*/
+int ComponentModelHelper::childCount() const
+{
+ if (m_componentPrivate->m_core->virtualComponentsVisible())
+ return m_componentPrivate->m_allChildComponents.count();
+ return m_componentPrivate->m_childComponents.count();
+}
+
+/*!
+ Returns the index of this component as seen from it's parent.
+*/
+int ComponentModelHelper::indexInParent() const
+{
+ int index = 0;
+ if (Component *parent = m_componentPrivate->m_parentComponent->parentComponent())
+ index = parent->childComponents(false, AllMode).indexOf(m_componentPrivate->m_parentComponent);
+ return (index >= 0 ? index : 0);
+}
+
+/*!
+ Returns all children and whose children depending if virtual components are visible or not.
+*/
+QList<Component*> ComponentModelHelper::childs() const
+{
+ QList<Component*> *components = &m_componentPrivate->m_childComponents;
+ if (m_componentPrivate->m_core->virtualComponentsVisible())
+ components = &m_componentPrivate->m_allChildComponents;
+
+ QList<Component*> result;
+ foreach (Component *component, *components) {
+ result.append(component);
+ result += component->childs();
+ }
+ return result;
+}
+
+/*!
+ Returns the component at index position in the list. Index must be a valid position in
+ the list (i.e., index >= 0 && index < childCount()). Otherwise it returns 0.
+*/
+Component *ComponentModelHelper::childAt(int index) const
+{
+ if (index >= 0 && index < childCount()) {
+ if (m_componentPrivate->m_core->virtualComponentsVisible())
+ return m_componentPrivate->m_allChildComponents.value(index, 0);
+ return m_componentPrivate->m_childComponents.value(index, 0);
+ }
+ return 0;
+}
+
+/*!
+ Determines if the components installations status can be changed. The default value is true.
+*/
+bool ComponentModelHelper::isEnabled() const
+{
+ return (flags() & Qt::ItemIsEnabled) != 0;
+}
+
+/*!
+ Enables oder disables ability to change the components installations status.
+*/
+void ComponentModelHelper::setEnabled(bool enabled)
+{
+ changeFlags(enabled, Qt::ItemIsEnabled);
+}
+
+/*!
+ Returns whether the component is tristate; that is, if it's checkable with three separate states.
+ The default value is false.
+*/
+bool ComponentModelHelper::isTristate() const
+{
+ return (flags() & Qt::ItemIsTristate) != 0;
+}
+
+/*!
+ Sets whether the component is tristate. If tristate is true, the component is checkable with three
+ separate states; otherwise, the component is checkable with two states.
+
+ (Note that this also requires that the component is checkable; see isCheckable().)
+*/
+void ComponentModelHelper::setTristate(bool tristate)
+{
+ changeFlags(tristate, Qt::ItemIsTristate);
+}
+
+/*!
+ Returns whether the component is user-checkable. The default value is true.
+*/
+bool ComponentModelHelper::isCheckable() const
+{
+ return (flags() & Qt::ItemIsUserCheckable) != 0;
+}
+
+/*!
+ Sets whether the component is user-checkable. If checkable is true, the component can be checked by the
+ user; otherwise, the user cannot check the component. The delegate will render a checkable component
+ with a check box next to the component's text.
+*/
+void ComponentModelHelper::setCheckable(bool checkable)
+{
+ if (checkable && !isCheckable()) {
+ // make sure there's data for the check state role
+ if (!data(Qt::CheckStateRole).isValid())
+ setData(Qt::Unchecked, Qt::CheckStateRole);
+ }
+ changeFlags(checkable, Qt::ItemIsUserCheckable);
+}
+
+/*!
+ Returns whether the component is selectable by the user. The default value is true.
+*/
+bool ComponentModelHelper::isSelectable() const
+{
+ return (flags() & Qt::ItemIsSelectable) != 0;
+}
+
+/*!
+ Sets whether the component is selectable. If selectable is true, the component can be selected by the
+ user; otherwise, the user cannot select the component.
+*/
+void ComponentModelHelper::setSelectable(bool selectable)
+{
+ changeFlags(selectable, Qt::ItemIsSelectable);
+}
+
+/*!
+ Returns the item flags for the component. The item flags determine how the user can interact with the
+ component.
+*/
+Qt::ItemFlags ComponentModelHelper::flags() const
+{
+ QVariant variant = data(Qt::UserRole - 1);
+ if (!variant.isValid())
+ return (Qt::ItemIsEnabled | Qt::ItemIsSelectable| Qt::ItemIsUserCheckable);
+ return Qt::ItemFlags(variant.toInt());
+}
+
+/*!
+ Sets the item flags for the component to flags. The item flags determine how the user can interact with
+ the component. This is often used to disable an component.
+*/
+void ComponentModelHelper::setFlags(Qt::ItemFlags flags)
+{
+ setData(int(flags), Qt::UserRole - 1);
+}
+
+/*!
+ Returns the checked state of the component.
+*/
+Qt::CheckState ComponentModelHelper::checkState() const
+{
+ return Qt::CheckState(qvariant_cast<int>(data(Qt::CheckStateRole)));
+}
+
+/*!
+ Sets the check state of the component to be state.
+*/
+void ComponentModelHelper::setCheckState(Qt::CheckState state)
+{
+ setData(state, Qt::CheckStateRole);
+}
+
+/*!
+ Returns the component's data for the given role, or an invalid QVariant if there is no data for role.
+*/
+QVariant ComponentModelHelper::data(int role) const
+{
+ return m_values.value((role == Qt::EditRole ? Qt::DisplayRole : role), QVariant());
+}
+
+/*!
+ Sets the component's data for the given role to the specified value.
+*/
+void ComponentModelHelper::setData(const QVariant &value, int role)
+{
+ m_values.insert((role == Qt::EditRole ? Qt::DisplayRole : role), value);
+}
+
+// -- protected
+
+void ComponentModelHelper::setPrivate(ComponentPrivate *componentPrivate)
+{
+ m_componentPrivate = componentPrivate;
+}
+
+// -- private
+
+void ComponentModelHelper::changeFlags(bool enable, Qt::ItemFlags itemFlags)
+{
+ setFlags(enable ? flags() |= itemFlags : flags() &= ~itemFlags);
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/component_p.h b/src/libs/installer/component_p.h
new file mode 100644
index 000000000..6cdbdcf80
--- /dev/null
+++ b/src/libs/installer/component_p.h
@@ -0,0 +1,157 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef COMPONENT_P_H
+#define COMPONENT_P_H
+
+#include "qinstallerglobal.h"
+
+#include <QtCore/QPointer>
+#include <QtCore/QStringList>
+#include <QtCore/QUrl>
+
+#include <QtScript/QScriptEngine>
+
+namespace QInstaller {
+
+class Component;
+class PackageManagerCore;
+
+class ComponentPrivate
+{
+ QInstaller::Component* const q;
+
+public:
+ explicit ComponentPrivate(PackageManagerCore *core, Component *qq);
+ ~ComponentPrivate();
+
+ QScriptEngine *scriptEngine();
+
+ void setProperty(QScriptValue &scriptValue, const QString &propertyName, int value);
+
+ PackageManagerCore *m_core;
+ Component *m_parentComponent;
+ OperationList m_operations;
+ Operation *m_licenseOperation;
+ Operation *m_minimumProgressOperation;
+
+ bool m_newlyInstalled;
+ bool m_operationsCreated;
+ bool m_removeBeforeUpdate;
+ bool m_autoCreateOperations;
+ bool m_operationsCreatedSuccessfully;
+ bool m_updateIsAvailable;
+
+ QString m_componentName;
+ QUrl m_repositoryUrl;
+ QString m_localTempPath;
+ QScriptValue m_scriptComponent;
+ QHash<QString, QString> m_vars;
+ QList<Component*> m_childComponents;
+ QList<Component*> m_allChildComponents;
+ QList<Component*> m_virtualChildComponents;
+ QStringList m_downloadableArchives;
+ QStringList m_stopProcessForUpdateRequests;
+ QHash<QString, bool> m_unexistingScriptMethods;
+ QMap<QString, QPointer<QWidget> > m_userInterfaces;
+
+ // < display name, < file name, file content > >
+ QHash<QString, QPair<QString, QString> > m_licenses;
+ QList<QPair<QString, bool> > m_pathesForUninstallation;
+
+private:
+ QScriptEngine* m_scriptEngine;
+};
+
+
+// -- ComponentModelHelper
+
+class ComponentModelHelper
+{
+public:
+ enum Roles {
+ LocalDisplayVersion = Qt::UserRole + 1,
+ RemoteDisplayVersion = LocalDisplayVersion + 1,
+ UncompressedSize = RemoteDisplayVersion + 1
+ };
+
+ enum Column {
+ NameColumn = 0,
+ InstalledVersionColumn,
+ NewVersionColumn,
+ UncompressedSizeColumn
+ };
+
+ explicit ComponentModelHelper();
+
+ int childCount() const;
+ int indexInParent() const;
+
+ QList<Component*> childs() const;
+ Component* childAt(int index) const;
+
+ bool isEnabled() const;
+ void setEnabled(bool enabled);
+
+ bool isTristate() const;
+ void setTristate(bool tristate);
+
+ bool isCheckable() const;
+ void setCheckable(bool checkable);
+
+ bool isSelectable() const;
+ void setSelectable(bool selectable);
+
+ Qt::ItemFlags flags() const;
+ void setFlags(Qt::ItemFlags flags);
+
+ Qt::CheckState checkState() const;
+ void setCheckState(Qt::CheckState state);
+
+ QVariant data(int role = Qt::UserRole + 1) const;
+ void setData(const QVariant &value, int role = Qt::UserRole + 1);
+
+protected:
+ void setPrivate(ComponentPrivate *componentPrivate);
+
+private:
+ void changeFlags(bool enable, Qt::ItemFlags itemFlags);
+
+private:
+ QHash<int, QVariant> m_values;
+
+ ComponentPrivate *m_componentPrivate;
+};
+
+} // namespace QInstaller
+
+#endif // COMPONENT_P_H
diff --git a/src/libs/installer/componentmodel.cpp b/src/libs/installer/componentmodel.cpp
new file mode 100644
index 000000000..ec61ee84e
--- /dev/null
+++ b/src/libs/installer/componentmodel.cpp
@@ -0,0 +1,528 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "componentmodel.h"
+
+#include "component.h"
+#include "packagemanagercore.h"
+
+namespace QInstaller {
+
+/*!
+ \fn void defaultCheckStateChanged(bool changed)
+
+ This signal is emitted whenever the default check state of a model is changed. The \a changed value
+ indicates whether the model has it's initial checked state or some components changed it's checked state.
+*/
+
+/*!
+ \fn void checkStateChanged(const QModelIndex &index)
+
+ This signal is emitted whenever the default check state of a component is changed. The \a index value
+ indicates the QModelIndex representation of the component as seen from the model.
+*/
+
+
+/*!
+ Constructs an component model with the given number of \a columns and \a core as parent.
+*/
+ComponentModel::ComponentModel(int columns, PackageManagerCore *core)
+ : QAbstractItemModel(core)
+ , m_core(core)
+ , m_rootIndex(0)
+{
+ m_headerData.insert(0, columns, QVariant());
+
+ connect(this, SIGNAL(modelReset()), this, SLOT(slotModelReset()));
+ connect(this, SIGNAL(checkStateChanged(QModelIndex)), this, SLOT(slotCheckStateChanged(QModelIndex)));
+}
+
+/*!
+ Destroys the component model.
+*/
+ComponentModel::~ComponentModel()
+{
+}
+
+/*!
+ Returns the number of items under the given \a parent. When the parent is valid it means that rowCount is
+ returning the number of items of parent.
+*/
+int ComponentModel::rowCount(const QModelIndex &parent) const
+{
+ if (Component *component = componentFromIndex(parent))
+ return component->childCount();
+ return m_rootComponentList.count();
+}
+
+/*!
+ Returns the number of columns of the given \a parent.
+*/
+int ComponentModel::columnCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent)
+ return m_headerData.size();
+}
+
+/*!
+ Returns the parent of the child item with the given \a parent. If the item has no parent, an invalid
+ QModelIndex is returned.
+*/
+QModelIndex ComponentModel::parent(const QModelIndex &child) const
+{
+ if (!child.isValid())
+ return QModelIndex();
+
+ if (Component *childComponent = componentFromIndex(child)) {
+ if (Component *parent = childComponent->parentComponent()) {
+ if (!m_rootComponentList.contains(parent))
+ return createIndex(parent->indexInParent(), 0, parent);
+ return createIndex(child.row(), 0, parent);
+ }
+ }
+
+ return QModelIndex();
+}
+
+/*!
+ Returns the index of the item in the model specified by the given \a row, \a column and \a parent index.
+*/
+QModelIndex ComponentModel::index(int row, int column, const QModelIndex &parent) const
+{
+ if (parent.isValid() && (row >= rowCount(parent) || column >= columnCount()))
+ return QModelIndex();
+
+ if (Component *parentComponent = componentFromIndex(parent)) {
+ if (Component *childComponent = parentComponent->childAt(row))
+ return createIndex(row, column, childComponent);
+ } else if (row < m_rootComponentList.count()) {
+ return createIndex(row, column, m_rootComponentList.at(row));
+ }
+
+ return QModelIndex();
+}
+
+/*!
+ Returns the data stored under the given \a role for the item referred to by the \a index.
+
+ \note An \bold invalid QVariant is returned if the given index is invalid. \bold Qt::CheckStateRole is
+ only supported for the first column of the model. \bold Qt::EditRole, \bold Qt::DisplayRole and \bold
+ Qt::ToolTipRole are specifically handled for columns greater than the first column and translate to \bold
+ Qt::UserRole \bold + \bold index.column().
+
+*/
+QVariant ComponentModel::data(const QModelIndex &index, int role) const
+{
+ if (Component *component = componentFromIndex(index)) {
+ if (index.column() > 0) {
+ if (role == Qt::CheckStateRole)
+ return QVariant();
+ if (role == Qt::EditRole || role == Qt::DisplayRole || role == Qt::ToolTipRole)
+ return component->data(Qt::UserRole + index.column());
+ }
+ return component->data(role);
+ }
+ return QVariant();
+}
+
+/*!
+ Sets the \a role data for the item at \a index to \a value. Returns true if successful; otherwise returns
+ false. The dataChanged() signal is emitted if the data was successfully set. The checkStateChanged() and
+ defaultCheckStateChanged() signal are emitted in addition if the check state of the item is set.
+*/
+bool ComponentModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (!index.isValid())
+ return false;
+
+ Component *component = componentFromIndex(index);
+ if (!component)
+ return false;
+
+ component->setData(value, role);
+
+ emit dataChanged(index, index);
+ if (role == Qt::CheckStateRole) {
+ emit checkStateChanged(index);
+ foreach (Component* comp, m_rootComponentList) {
+ comp->updateUncompressedSize();
+ }
+ }
+
+ return true;
+}
+
+/*!
+ Returns the data for the given \a role and \a section in the header with the specified \a orientation.
+ An \bold invalid QVariant is returned if \a section is out of bounds, \a orientation is not Qt::Horizontal
+ or \a role is anything else than Qt::DisplayRole.
+*/
+QVariant ComponentModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (section >= 0 && section < columnCount() && orientation == Qt::Horizontal && role == Qt::DisplayRole)
+ return m_headerData.at(section);
+ return QVariant();
+}
+
+/*!
+ Sets the data for the given \a role and \a section in the header with the specified \a orientation to the
+ \a value supplied. Returns true if the header's data was updated; otherwise returns false. The
+ headerDataChanged() signal is emitted if the data was successfully set.
+
+ \note Only \bold Qt::Horizontal orientation is supported.
+*/
+bool ComponentModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role)
+{
+ if (section >= 0 && section < columnCount() && orientation == Qt::Horizontal
+ && (role == Qt::DisplayRole || role == Qt::EditRole)) {
+ m_headerData.replace(section, value);
+ emit headerDataChanged(orientation, section, section);
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Returns the item flags for the given \a index.
+
+ The class implementation returns a combination of flags that enables the item (Qt::ItemIsEnabled), allows
+ it to be selected (Qt::ItemIsSelectable) and to be checked (Qt::ItemIsUserCheckable).
+*/
+Qt::ItemFlags ComponentModel::flags(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return Qt::NoItemFlags;
+
+ if (Component *component = componentFromIndex(index))
+ return component->flags();
+
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
+}
+
+/*!
+ Set's the passed \a rootComponents to be list of currently shown components. The model is repopulated and
+ the individual component checked state is used to show the check mark in front of the visual component
+ representation. The modelAboutToBeReset() and modelReset() signals are emitted.
+*/
+void ComponentModel::setRootComponents(QList<Component*> rootComponents)
+{
+ beginResetModel();
+
+ m_indexByNameCache.clear();
+ m_rootComponentList.clear();
+ m_initialCheckedSet.clear();
+ m_currentCheckedSet.clear();
+
+ m_rootIndex = 0;
+ m_rootComponentList = rootComponents;
+
+ endResetModel();
+}
+
+/*!
+ Appends the passed \a rootComponents to the currently shown list of components. The model is repopulated
+ and the individual component checked state is used to show the check mark in front of the visual component
+ representation. Already changed check states on the previous model are preserved. The modelAboutToBeReset()
+ and modelReset() signals are emitted.
+*/
+void ComponentModel::appendRootComponents(QList<Component*> rootComponents)
+{
+ beginResetModel();
+
+ m_indexByNameCache.clear();
+
+ m_rootIndex = m_rootComponentList.count() - 1;
+ m_rootComponentList += rootComponents;
+
+ endResetModel();
+}
+
+/*!
+ Returns a pointer to the PackageManagerCore this model belongs to.
+*/
+PackageManagerCore *ComponentModel::packageManagerCore() const
+{
+ return m_core;
+}
+
+/*!
+ Returns true if no changes to the components checked state have been done, otherwise returns false.
+*/
+bool ComponentModel::defaultCheckState() const
+{
+ return m_initialCheckedSet == m_currentCheckedSet;
+}
+
+/*!
+ Returns true if this model has checked components, otherwise returns false.
+*/
+bool ComponentModel::hasCheckedComponents() const
+{
+ return !m_currentCheckedSet.isEmpty();
+}
+
+/*!
+ Returns a list of checked components.
+*/
+QList<Component*> ComponentModel::checkedComponents() const
+{
+ QList<Component*> list;
+ foreach (const QString &name, m_currentCheckedSet)
+ list.append(componentFromIndex(indexFromComponentName(name)));
+ return list;
+}
+
+/*!
+ Translates between a given component \a name and it's associated QModelIndex. Returns the QModelIndex that
+ represents the component or an invalid QModelIndex if the component does not exist in the model.
+*/
+QModelIndex ComponentModel::indexFromComponentName(const QString &name) const
+{
+ if (m_indexByNameCache.isEmpty()) {
+ for (int i = 0; i < m_rootComponentList.count(); ++i)
+ updateCache(index(i, 0, QModelIndex()));
+ }
+ return m_indexByNameCache.value(name, QModelIndex());
+}
+
+/*!
+ Translates between a given QModelIndex \a index and it's associated Component. Returns the Component if
+ the index is valid or 0 if an invalid QModelIndex is given.
+*/
+Component *ComponentModel::componentFromIndex(const QModelIndex &index) const
+{
+ if (index.isValid())
+ return static_cast<Component*>(index.internalPointer());
+ return 0;
+}
+
+// -- public slots
+
+/*!
+ Invoking this slot results in an checked state for every component the has a visual representation in the
+ model. Note that components are not changed if they are not checkable. The checkStateChanged() and
+ defaultCheckStateChanged() signal are emitted.
+*/
+void ComponentModel::selectAll()
+{
+ m_currentCheckedSet = m_currentCheckedSet.unite(select(Qt::Checked));
+ emit defaultCheckStateChanged(m_initialCheckedSet != m_currentCheckedSet);
+}
+
+/*!
+ Invoking this slot results in an unchecked state for every component the has a visual representation in
+ the model. Note that components are not changed if they are not checkable. The checkStateChanged() and
+ defaultCheckStateChanged() signal are emitted.
+*/
+void ComponentModel::deselectAll()
+{
+ m_currentCheckedSet = m_currentCheckedSet.subtract(select(Qt::Unchecked));
+ emit defaultCheckStateChanged(m_initialCheckedSet != m_currentCheckedSet);
+}
+
+/*!
+ Invoking this slot results in an checked state for every component the has a visual representation in the
+ model when the model was setup during setRootComponents() or appendRootComponents(). Note that components
+ are not changed it they are not checkable. The checkStateChanged() and defaultCheckStateChanged() signal
+ are emitted.
+*/
+void ComponentModel::selectDefault()
+{
+ m_currentCheckedSet = m_currentCheckedSet.subtract(select(Qt::Unchecked));
+ foreach (const QString &name, m_initialCheckedSet)
+ setData(indexFromComponentName(name), Qt::Checked, Qt::CheckStateRole);
+ emit defaultCheckStateChanged(m_initialCheckedSet != m_currentCheckedSet);
+}
+
+// -- private slots
+
+void ComponentModel::slotModelReset()
+{
+ QList<QInstaller::Component*> components = m_rootComponentList;
+ if (m_core->runMode() == QInstaller::AllMode) {
+ for (int i = m_rootIndex; i < m_rootComponentList.count(); ++i)
+ components.append(m_rootComponentList.at(i)->childs());
+ }
+
+ foreach (Component *child, components) {
+ if (child->checkState() == Qt::Checked && !child->isTristate())
+ m_initialCheckedSet.insert(child->name());
+ }
+ m_currentCheckedSet += m_initialCheckedSet;
+
+ if (m_core->runMode() == QInstaller::AllMode)
+ select(Qt::Unchecked);
+
+ foreach (const QString &name, m_currentCheckedSet)
+ setData(indexFromComponentName(name), Qt::Checked, Qt::CheckStateRole);
+
+}
+
+static Qt::CheckState verifyPartiallyChecked(Component *component)
+{
+ int checked = 0;
+ int unchecked = 0;
+ int virtualChilds = 0;
+
+ const int count = component->childCount();
+ for (int i = 0; i < count; ++i) {
+ Component *const child = component->childAt(i);
+ if (!child->isVirtual()) {
+ switch (component->childAt(i)->checkState()) {
+ case Qt::Checked: {
+ ++checked;
+ } break;
+ case Qt::Unchecked: {
+ ++unchecked;
+ } break;
+ default:
+ break;
+ }
+ } else {
+ ++virtualChilds;
+ }
+ }
+
+ if ((checked + virtualChilds) == count)
+ return Qt::Checked;
+
+ if ((unchecked + virtualChilds) == count)
+ return Qt::Unchecked;
+
+ return Qt::PartiallyChecked;
+}
+
+void ComponentModel::slotCheckStateChanged(const QModelIndex &index)
+{
+ Component *component = componentFromIndex(index);
+ if (!component)
+ return;
+
+ if (component->checkState() == Qt::Checked && !component->isTristate())
+ m_currentCheckedSet.insert(component->name());
+ else if (component->checkState() == Qt::Unchecked && !component->isTristate())
+ m_currentCheckedSet.remove(component->name());
+ emit defaultCheckStateChanged(m_initialCheckedSet != m_currentCheckedSet);
+
+ if (component->isVirtual())
+ return;
+
+ const Qt::CheckState state = component->checkState();
+ if (component->isTristate()) {
+ if (state == Qt::PartiallyChecked) {
+ component->setCheckState(verifyPartiallyChecked(component));
+ return;
+ }
+
+ QModelIndexList notCheckable;
+ foreach (Component *child, component->childs()) {
+ const QModelIndex &idx = indexFromComponentName(child->name());
+ if (child->isCheckable()) {
+ if (child->checkState() != state && !child->isVirtual())
+ setData(idx, state, Qt::CheckStateRole);
+ } else {
+ notCheckable.append(idx);
+ }
+ }
+
+ if (state == Qt::Unchecked && !notCheckable.isEmpty()) {
+ foreach (const QModelIndex &idx, notCheckable)
+ setData(idx, idx.data(Qt::CheckStateRole), Qt::CheckStateRole);
+ }
+ } else {
+ QList<Component*> parents;
+ while (0 != component->parentComponent()) {
+ parents.append(component->parentComponent());
+ component = parents.last();
+ }
+
+ foreach (Component *parent, parents) {
+ if (parent->isCheckable()) {
+ const QModelIndex &idx = indexFromComponentName(parent->name());
+ if (parent->checkState() == Qt::PartiallyChecked) {
+ setData(idx, verifyPartiallyChecked(parent), Qt::CheckStateRole);
+ } else {
+ setData(idx, Qt::PartiallyChecked, Qt::CheckStateRole);
+ }
+ }
+ }
+ }
+}
+
+// -- private
+
+QSet<QString> ComponentModel::select(Qt::CheckState state)
+{
+ QSet<QString> changed;
+ for (int i = 0; i < m_rootComponentList.count(); ++i) {
+ QSet<QString> tmp;
+ QList<Component*> children = m_rootComponentList.at(i)->childs();
+ children.prepend(m_rootComponentList.at(i)); // we need to take the root item into account as well
+ foreach (Component *child, children) {
+ if (child->isCheckable() && !child->isTristate() && child->checkState() != state) {
+ tmp.insert(child->name());
+ child->setCheckState(state);
+ }
+ }
+ if (!tmp.isEmpty()) {
+ changed += tmp;
+ setData(index(i, 0, QModelIndex()), state, Qt::CheckStateRole);
+ }
+ }
+ return changed;
+}
+
+void ComponentModel::updateCache(const QModelIndex &parent) const
+{
+ const QModelIndexList &list = collectComponents(parent);
+ foreach (const QModelIndex &index, list) {
+ if (Component *component = componentFromIndex(index))
+ m_indexByNameCache.insert(component->name(), index);
+ }
+ m_indexByNameCache.insert((static_cast<Component*> (parent.internalPointer()))->name(), parent);
+}
+
+QModelIndexList ComponentModel::collectComponents(const QModelIndex &parent) const
+{
+ QModelIndexList list;
+ for (int i = 0; i < rowCount(parent) ; ++i) {
+ const QModelIndex &next = index(i, 0, parent);
+ if (Component *component = componentFromIndex(next)) {
+ if (component->childCount() > 0)
+ list += collectComponents(next);
+ }
+ list.append(next);
+ }
+ return list;
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/componentmodel.h b/src/libs/installer/componentmodel.h
new file mode 100644
index 000000000..6dd6b634a
--- /dev/null
+++ b/src/libs/installer/componentmodel.h
@@ -0,0 +1,115 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef COMPONENTMODEL_H
+#define COMPONENTMODEL_H
+
+#include "qinstallerglobal.h"
+
+#include <QtCore/QAbstractItemModel>
+#include <QtCore/QList>
+#include <QtCore/QSet>
+#include <QtCore/QVector>
+
+namespace QInstaller {
+
+class Component;
+class PackageManagerCore;
+
+class INSTALLER_EXPORT ComponentModel : public QAbstractItemModel
+{
+ Q_OBJECT
+
+public:
+ explicit ComponentModel(int columns, PackageManagerCore *core = 0);
+ ~ComponentModel();
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+
+ QModelIndex parent(const QModelIndex &child) const;
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
+
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+ bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value,
+ int role = Qt::EditRole);
+
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+
+ void setRootComponents(QList<Component*> rootComponents);
+ void appendRootComponents(QList<Component*> rootComponents);
+
+ PackageManagerCore *packageManagerCore() const;
+
+ bool defaultCheckState() const;
+ bool hasCheckedComponents() const;
+ QList<Component*> checkedComponents() const;
+
+ QModelIndex indexFromComponentName(const QString &name) const;
+ Component* componentFromIndex(const QModelIndex &index) const;
+
+public Q_SLOTS:
+ void selectAll();
+ void deselectAll();
+ void selectDefault();
+
+Q_SIGNALS:
+ void defaultCheckStateChanged(bool changed);
+ void checkStateChanged(const QModelIndex &index);
+
+private Q_SLOTS:
+ void slotModelReset();
+ void slotCheckStateChanged(const QModelIndex &index);
+
+private:
+ QSet<QString> select(Qt::CheckState state);
+ void updateCache(const QModelIndex &parent) const;
+ QModelIndexList collectComponents(const QModelIndex &parent) const;
+
+private:
+ PackageManagerCore *m_core;
+
+ int m_rootIndex;
+ QVector<QVariant> m_headerData;
+ QSet<QString> m_initialCheckedSet;
+ QSet<QString> m_currentCheckedSet;
+ QList<Component*> m_rootComponentList;
+
+ mutable QMap<QString, QPersistentModelIndex> m_indexByNameCache;
+};
+
+} // namespace QInstaller
+
+#endif // COMPONENTMODEL_H
diff --git a/src/libs/installer/constants.h b/src/libs/installer/constants.h
new file mode 100644
index 000000000..ea512ed76
--- /dev/null
+++ b/src/libs/installer/constants.h
@@ -0,0 +1,82 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef CONSTANTS_H
+#define CONSTANTS_H
+
+#include <QtCore/QString>
+
+namespace QInstaller {
+
+// constants used throughout several classes
+static const QLatin1String scTrue("true");
+static const QLatin1String scFalse("false");
+
+static const QLatin1String scName("Name");
+static const QLatin1String scVersion("Version");
+static const QLatin1String scRemoteVersion("Version");
+static const QLatin1String scDisplayVersion("DisplayVersion");
+static const QLatin1String scRemoteDisplayVersion("RemoteDisplayVersion");
+static const QLatin1String scInheritVersion("inheritVersionFrom");
+static const QLatin1String scReplaces("Replaces");
+static const QLatin1String scDownloadableArchives("DownloadableArchives");
+static const QLatin1String scEssential("Essential");
+static const QLatin1String scTargetDir("TargetDir");
+static const QLatin1String scReleaseDate("ReleaseDate");
+static const QLatin1String scDescription("Description");
+static const QLatin1String scDisplayName("DisplayName");
+static const QLatin1String scDependencies("Dependencies");
+static const QLatin1String scNewComponent("NewComponent");
+static const QLatin1String scRepositories("Repositories");
+static const QLatin1String scCompressedSize("CompressedSize");
+static const QLatin1String scInstalledVersion("InstalledVersion");
+static const QLatin1String scUncompressedSize("UncompressedSize");
+static const QLatin1String scUncompressedSizeSum("UncompressedSizeSum");
+static const QLatin1String scRequiresAdminRights("RequiresAdminRights");
+
+// constants used throughout the components class
+static const QLatin1String scVirtual("Virtual");
+static const QLatin1String scSortingPriority("SortingPriority");
+
+// constants used throughout the settings and package manager core class
+static const QLatin1String scTitle("Title");
+static const QLatin1String scPublisher("Publisher");
+static const QLatin1String scRunProgram("RunProgram");
+static const QLatin1String scStartMenuDir("StartMenuDir");
+static const QLatin1String scRemoveTargetDir("RemoveTargetDir");
+static const QLatin1String scRunProgramDescription("RunProgramDescription");
+static const QLatin1String scTargetConfigurationFile("TargetConfigurationFile");
+static const QLatin1String scAllowNonAsciiCharacters("AllowNonAsciiCharacters");
+
+}
+
+#endif // CONSTANTS_H
diff --git a/src/libs/installer/copydirectoryoperation.cpp b/src/libs/installer/copydirectoryoperation.cpp
new file mode 100644
index 000000000..f5e7692f5
--- /dev/null
+++ b/src/libs/installer/copydirectoryoperation.cpp
@@ -0,0 +1,159 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "copydirectoryoperation.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QDirIterator>
+#include <QtCore/QFileInfo>
+
+using namespace QInstaller;
+
+class AutoPush
+{
+public:
+ AutoPush(CopyDirectoryOperation *op)
+ : m_op(op) {}
+ ~AutoPush() { m_op->setValue(QLatin1String("files"), m_files); }
+
+ QStringList m_files;
+ CopyDirectoryOperation *m_op;
+};
+
+/*
+TRANSLATOR QInstaller::CopyDirectoryOperation
+*/
+
+CopyDirectoryOperation::CopyDirectoryOperation()
+{
+ setName(QLatin1String("CopyDirectory"));
+}
+
+void CopyDirectoryOperation::backup()
+{
+}
+
+bool CopyDirectoryOperation::performOperation()
+{
+ const QStringList args = arguments();
+ if (args.count() != 2) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, 2 expected.").arg(name())
+ .arg(args.count()));
+ return false;
+ }
+ const QString sourcePath = args.at(0);
+ const QString targetPath = args.at(1);
+
+ const QFileInfo sourceInfo(sourcePath);
+ const QFileInfo targetInfo(targetPath);
+ if (!sourceInfo.exists() || !sourceInfo.isDir() || !targetInfo.exists() || !targetInfo.isDir()) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: Directories are invalid: %1 %2").arg(name())
+ .arg(sourcePath).arg(targetPath));
+ return false;
+ }
+
+ const QDir sourceDir = sourceInfo.absoluteDir();
+ const QDir targetDir = targetInfo.absoluteDir();
+
+ AutoPush autoPush(this);
+ QDirIterator it(sourceInfo.absoluteFilePath(), QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden,
+ QDirIterator::Subdirectories);
+ while (it.hasNext()) {
+ const QString itemName = it.next();
+ const QFileInfo itemInfo(sourceDir.absoluteFilePath(itemName));
+ const QString relativePath = sourceDir.relativeFilePath(itemName);
+ if (itemInfo.isSymLink()) {
+ // Check if symlink target is inside copied directory
+ const QString linkTarget = itemInfo.symLinkTarget();
+ if (linkTarget.startsWith(sourceDir.absolutePath())) {
+ // create symlink to copied location
+ const QString linkTargetRelative = sourceDir.relativeFilePath(linkTarget);
+ QFile(targetDir.absoluteFilePath(linkTargetRelative))
+ .link(targetDir.absoluteFilePath(relativePath));
+ } else {
+ // create symlink pointing to original location
+ QFile(linkTarget).link(targetDir.absoluteFilePath(relativePath));
+ }
+ // add file entry
+ autoPush.m_files.prepend(targetDir.absoluteFilePath(relativePath));
+ emit outputTextChanged(autoPush.m_files.first());
+ } else if (itemInfo.isDir()) {
+ if (!targetDir.mkpath(targetDir.absoluteFilePath(relativePath))) {
+ setError(InvalidArguments);
+ setErrorString(tr("Could not create %0").arg(targetDir.absoluteFilePath(relativePath)));
+ return false;
+ }
+ } else {
+ if (!QFile::copy(sourceDir.absoluteFilePath(itemName), targetDir.absoluteFilePath(relativePath))) {
+ setError(InvalidArguments);
+ setErrorString(tr("Could not copy %0 to %1").arg(sourceDir.absoluteFilePath(itemName))
+ .arg(targetDir.absoluteFilePath(relativePath)));
+ return false;
+ }
+ autoPush.m_files.prepend(targetDir.absoluteFilePath(relativePath));
+ emit outputTextChanged(autoPush.m_files.first());
+ }
+ }
+ return true;
+}
+
+bool CopyDirectoryOperation::undoOperation()
+{
+ Q_ASSERT(arguments().count() == 2);
+
+ QDir dir;
+ const QStringList files = value(QLatin1String("files")).toStringList();
+ foreach (const QString &file, files) {
+ if (!QFile::remove(file)) {
+ setError(InvalidArguments);
+ setErrorString(tr("Could not remove %0").arg(file));
+ return false;
+ }
+ dir.rmpath(QFileInfo(file).absolutePath());
+ emit outputTextChanged(file);
+ }
+
+ setValue(QLatin1String("files"), QStringList());
+ return true;
+}
+
+bool CopyDirectoryOperation::testOperation()
+{
+ return true;
+}
+
+Operation *CopyDirectoryOperation::clone() const
+{
+ return new CopyDirectoryOperation();
+}
diff --git a/src/libs/installer/copydirectoryoperation.h b/src/libs/installer/copydirectoryoperation.h
new file mode 100644
index 000000000..5b2067cc4
--- /dev/null
+++ b/src/libs/installer/copydirectoryoperation.h
@@ -0,0 +1,61 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef COPYDIRECTORYOPERATION_H
+#define COPYDIRECTORYOPERATION_H
+
+#include "qinstallerglobal.h"
+
+#include <QtCore/QObject>
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT CopyDirectoryOperation : public QObject, public Operation
+{
+ Q_OBJECT
+
+public:
+ CopyDirectoryOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+
+Q_SIGNALS:
+ void outputTextChanged(const QString &progress);
+};
+
+}
+
+#endif
diff --git a/src/libs/installer/createdesktopentryoperation.cpp b/src/libs/installer/createdesktopentryoperation.cpp
new file mode 100644
index 000000000..feda777cc
--- /dev/null
+++ b/src/libs/installer/createdesktopentryoperation.cpp
@@ -0,0 +1,204 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#include "createdesktopentryoperation.h"
+
+#include "errors.h"
+#include "fileutils.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QTextStream>
+#include <QtCore/QProcess>
+#if QT_VERSION >= 0x040600
+# include <QtCore/QProcessEnvironment>
+#endif
+
+using namespace QInstaller;
+
+QString CreateDesktopEntryOperation::absoluteFileName()
+{
+ const QString filename = arguments().first();
+
+ // give filename is already absolute
+ if (QFileInfo(filename).isAbsolute())
+ return filename;
+
+ // we're not searching for the first time, let's re-use the old value
+ if (hasValue(QLatin1String("directory")))
+ return QDir(value(QLatin1String("directory")).toString()).absoluteFilePath(filename);
+
+ const QProcessEnvironment env;
+ QStringList XDG_DATA_DIRS = env.value(QLatin1String("XDG_DATA_DIRS")).split(QLatin1Char(':'),
+ QString::SkipEmptyParts);
+ QStringList XDG_DATA_HOME = env.value(QLatin1String("XDG_DATA_HOME")).split(QLatin1Char(':'),
+ QString::SkipEmptyParts);
+
+ XDG_DATA_DIRS.push_back(QLatin1String("/usr/share")); // default path
+ XDG_DATA_HOME.push_back(QDir::home().absoluteFilePath(QLatin1String(".local/share"))); // default path
+
+ const QStringList directories = XDG_DATA_DIRS + XDG_DATA_HOME;
+ QString directory;
+ for (QStringList::const_iterator it = directories.begin(); it != directories.end(); ++it) {
+ if (it->isEmpty())
+ continue;
+
+ directory = QDir(*it).absoluteFilePath(QLatin1String("applications"));
+ QDir dir(directory);
+
+ // let's see wheter this dir exists or we're able to create it
+ if (!dir.exists() && !QDir().mkpath(directory))
+ continue;
+
+ // we just try wheter we're able to open the file in ReadWrite
+ QFile file(QDir(directory).absoluteFilePath(filename));
+ const bool existed = file.exists();
+ if (!file.open(QIODevice::ReadWrite))
+ continue;
+ file.close();
+ if (!existed)
+ file.remove();
+ break;
+ }
+
+ if (!QDir(directory).exists())
+ QDir().mkpath(directory);
+
+ setValue(QLatin1String("directory"), directory);
+
+ return QDir(directory).absoluteFilePath(filename);
+}
+
+/*
+TRANSLATOR QInstaller::CreateDesktopEntryOperation
+*/
+
+CreateDesktopEntryOperation::CreateDesktopEntryOperation()
+{
+ setName(QLatin1String("CreateDesktopEntry"));
+}
+
+CreateDesktopEntryOperation::~CreateDesktopEntryOperation()
+{
+ deleteFileNowOrLater(value(QLatin1String("backupOfExistingDesktopEntry")).toString());
+}
+
+void CreateDesktopEntryOperation::backup()
+{
+ const QString filename = absoluteFileName();
+ if (!QFile::exists(filename))
+ return;
+
+ try {
+ setValue(QLatin1String("backupOfExistingDesktopEntry"), generateTemporaryFileName(filename));
+ } catch (const QInstaller::Error &e) {
+ setErrorString(e.message());
+ return;
+ }
+
+ if (!QFile::copy(filename, value(QLatin1String("backupOfExistingDesktopEntry")).toString()))
+ setErrorString(QObject::tr("Could not backup file %1").arg(filename));
+}
+
+bool CreateDesktopEntryOperation::performOperation()
+{
+ const QStringList args = arguments();
+ if (args.count() != 2) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, 2 expected.").arg(name()).arg(args
+ .count()));
+ return false;
+ }
+
+ const QString filename = absoluteFileName();
+ const QString &values = args[1];
+
+ if (QFile::exists(filename) && !deleteFileNowOrLater(filename)) {
+ setError(UserDefinedError);
+ setErrorString(tr("Failed to overwrite %1").arg(filename));
+ return false;
+ }
+
+ QFile file(filename);
+ if(!file.open(QIODevice::WriteOnly)) {
+ setError(UserDefinedError);
+ setErrorString(tr("Could not write Desktop Entry at %1").arg(filename));
+ return false;
+ }
+
+ QFile::setPermissions(filename, QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::ReadGroup
+ | QFile::ReadOther);
+
+ QTextStream stream(&file);
+ stream.setCodec("UTF-8");
+ stream << QLatin1String("[Desktop Entry]") << endl;
+ stream << QLatin1String("Encoding=UTF-8") << endl;
+
+ // Type=Application\nExec=qtcreator\nPath=...
+ const QStringList pairs = values.split(QLatin1Char('\n'));
+ for (QStringList::const_iterator it = pairs.begin(); it != pairs.end(); ++it)
+ stream << *it << endl;
+
+ return true;
+}
+
+bool CreateDesktopEntryOperation::undoOperation()
+{
+ const QString filename = absoluteFileName();
+
+ // first remove the link
+ if (!deleteFileNowOrLater(filename)) {
+ setErrorString(QObject::tr("Could not delete file %1").arg(filename));
+ return false;
+ }
+
+ if (!hasValue(QLatin1String("backupOfExistingDesktopEntry")))
+ return true;
+
+ const QString backupOfExistingDesktopEntry = value(QLatin1String("backupOfExistingDesktopEntry")).toString();
+ const bool success = QFile::copy(backupOfExistingDesktopEntry, filename)
+ && deleteFileNowOrLater(backupOfExistingDesktopEntry);
+ if (!success)
+ setErrorString(QObject::tr("Could not restore backup file into %1").arg(filename));
+
+ return success;
+}
+
+bool CreateDesktopEntryOperation::testOperation()
+{
+ return true;
+}
+
+Operation *CreateDesktopEntryOperation::clone() const
+{
+ return new CreateDesktopEntryOperation();
+}
diff --git a/src/libs/installer/createdesktopentryoperation.h b/src/libs/installer/createdesktopentryoperation.h
new file mode 100644
index 000000000..b87e317f7
--- /dev/null
+++ b/src/libs/installer/createdesktopentryoperation.h
@@ -0,0 +1,57 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef CREATEDESKTOPENTRYOPERATION_H
+#define CREATEDESKTOPENTRYOPERATION_H
+
+#include "qinstallerglobal.h"
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT CreateDesktopEntryOperation : public Operation
+{
+public:
+ CreateDesktopEntryOperation();
+ ~CreateDesktopEntryOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation* clone() const;
+
+ QString absoluteFileName();
+};
+
+}
+
+#endif
diff --git a/src/libs/installer/createlocalrepositoryoperation.cpp b/src/libs/installer/createlocalrepositoryoperation.cpp
new file mode 100644
index 000000000..a0527f432
--- /dev/null
+++ b/src/libs/installer/createlocalrepositoryoperation.cpp
@@ -0,0 +1,384 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#include "createlocalrepositoryoperation.h"
+
+#include "binaryformat.h"
+#include "errors.h"
+#include "fileutils.h"
+#include "copydirectoryoperation.h"
+#include "lib7z_facade.h"
+#include "packagemanagercore.h"
+
+#include "kdupdaterupdateoperations.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QDirIterator>
+
+#include <cerrno>
+
+namespace QInstaller {
+
+
+// -- AutoHelper
+
+class AutoHelper
+{
+public:
+ AutoHelper(CreateLocalRepositoryOperation *op)
+ : m_op(op)
+ {
+ }
+
+ virtual ~AutoHelper()
+ {
+ m_op->emitFullProgress();
+ m_op->setValue(QLatin1String("files"), m_files);
+ }
+
+ QStringList m_files;
+ CreateLocalRepositoryOperation *m_op;
+};
+
+
+// statics
+
+namespace Static {
+
+static void fixPermissions(const QString &repoPath)
+{
+ QDirIterator it(repoPath, QDirIterator::Subdirectories);
+ while (it.hasNext() && !it.next().isEmpty()) {
+ if (!it.fileInfo().isFile())
+ continue;
+
+ if (!QFile::setPermissions(it.filePath(), QFile::ReadOwner | QFile::WriteOwner
+ | QFile::ReadUser | QFile::WriteUser | QFile::ReadGroup | QFile::ReadOther)) {
+ throw Error(CreateLocalRepositoryOperation::tr("Could not set file permissions %1!")
+ .arg(it.filePath()));
+ }
+ }
+}
+
+static void removeDirectory(const QString &path, AutoHelper *const helper)
+{
+ QInstaller::removeDirectory(path);
+ QStringList files = helper->m_files.filter(path);
+ foreach (const QString &file, files)
+ helper->m_files.removeAll(file);
+}
+
+static void removeFiles(const QString &path, AutoHelper *const helper)
+{
+ const QFileInfoList entries = QDir(path).entryInfoList(QDir::AllEntries | QDir::Hidden);
+ foreach (const QFileInfo &fi, entries) {
+ if (fi.isSymLink() || fi.isFile()) {
+ QFile f(fi.filePath());
+ if (!f.remove())
+ throw Error(QObject::tr("Could not remove file %1: %2").arg(f.fileName(), f.errorString()));
+ helper->m_files.removeAll(f.fileName());
+ }
+ }
+}
+
+static QString createArchive(const QString repoPath, const QString &sourceDir, const QString &version
+ , AutoHelper *const helper)
+{
+ const QString fileName = QString::fromLatin1("/%1meta.7z").arg(version);
+
+ QFile archive(repoPath + fileName);
+ QInstaller::openForWrite(&archive, archive.fileName());
+ Lib7z::createArchive(&archive, QStringList() << sourceDir);
+ removeFiles(sourceDir, helper); // cleanup the files we compressed
+ if (!archive.rename(sourceDir + fileName)) {
+ throw Error(CreateLocalRepositoryOperation::tr("Could not move file %1 to %2. Error: %3").arg(archive
+ .fileName(), sourceDir + fileName, archive.errorString()));
+ }
+ return archive.fileName();
+}
+
+} // namespace Statics
+
+
+// -- CreateLocalRepositoryOperation
+
+CreateLocalRepositoryOperation::CreateLocalRepositoryOperation()
+{
+ setName(QLatin1String("CreateLocalRepository"));
+}
+
+void CreateLocalRepositoryOperation::backup()
+{
+}
+
+bool CreateLocalRepositoryOperation::performOperation()
+{
+ AutoHelper helper(this);
+ emit progressChanged(0.0);
+
+ const QStringList args = arguments();
+
+ if (args.count() != 2) {
+ emit progressChanged(1.0);
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, 2 expected.").arg(name())
+ .arg(args.count()));
+ return false;
+ }
+
+ QString repoPath;
+ try {
+ QString binaryPath = QFileInfo(args.at(0)).absoluteFilePath();
+ // Note the "/" at the end, important to make copy directory operation behave well
+ repoPath = QFileInfo(args.at(1)).absoluteFilePath() + QLatin1String("/repository/");
+
+ // if we're running as installer and install into an existing target, remove possible previous repos
+ PackageManagerCore *core = qVariantValue<PackageManagerCore*>(value(QLatin1String("installer")));
+ if (core && core->isOfflineOnly() && QFile::exists(repoPath)) {
+ Static::fixPermissions(repoPath);
+ QInstaller::removeDirectory(repoPath);
+ }
+
+ // create the local repository target dir
+ KDUpdater::MkdirOperation mkDirOp;
+ mkDirOp.setArguments(QStringList() << repoPath);
+ mkDirOp.backup();
+ if (!mkDirOp.performOperation()) {
+ setError(mkDirOp.error());
+ setErrorString(mkDirOp.errorString());
+ return false;
+ }
+ setValue(QLatin1String("createddir"), mkDirOp.value(QLatin1String("createddir")));
+
+ // copy the whole meta data into local repository
+ CopyDirectoryOperation copyDirOp;
+ copyDirOp.setArguments(QStringList() << QLatin1String(":/metadata/") << repoPath);
+ connect(&copyDirOp, SIGNAL(outputTextChanged(QString)), this, SIGNAL(outputTextChanged(QString)));
+
+ const bool success = copyDirOp.performOperation();
+ helper.m_files = copyDirOp.value(QLatin1String("files")).toStringList();
+ if (!success) {
+ setError(copyDirOp.error());
+ setErrorString(copyDirOp.errorString());
+ return false;
+ }
+
+ emit progressChanged(0.25);
+
+ // we need to fix the folder and file permissions here, as copying from read only resource file
+ // systems sets all permissions to a completely bogus value...
+ Static::fixPermissions(repoPath);
+
+ // open the updates xml file we previously copied
+ QFile updatesXml(repoPath + QLatin1String("Updates.xml"));
+ if (!updatesXml.exists() || !updatesXml.open(QIODevice::ReadOnly))
+ throw QInstaller::Error(tr("Could not open file: %1").arg(updatesXml.fileName()));
+
+ // read the content of the updates xml
+ QString error;
+ QDomDocument doc;
+ if (!doc.setContent(&updatesXml, &error))
+ throw QInstaller::Error(tr("Could not read: %1. Error: %2").arg(updatesXml.fileName(), error));
+
+ // build for each available package a name - version mapping
+ QHash<QString, QString> versionMap;
+ const QDomElement root = doc.documentElement();
+ const QDomNodeList rootChildNodes = root.childNodes();
+ for (int i = 0; i < rootChildNodes.count(); ++i) {
+ const QDomElement element = rootChildNodes.at(i).toElement();
+ if (element.isNull())
+ continue;
+
+ QString name, version;
+ if (element.tagName() == QLatin1String("PackageUpdate")) {
+ const QDomNodeList elementChildNodes = element.childNodes();
+ for (int j = 0; j < elementChildNodes.count(); ++j) {
+ const QDomElement e = elementChildNodes.at(j).toElement();
+ if (e.tagName() == QLatin1String("Name"))
+ name = e.text();
+ else if (e.tagName() == QLatin1String("Version"))
+ version = e.text();
+ }
+ versionMap.insert(name, version);
+ }
+ }
+
+ emit progressChanged(0.50);
+
+ QSharedPointer<QFile> file(new QFile(binaryPath));
+ if (!file->open(QIODevice::ReadOnly)) {
+ throw QInstaller::Error(tr("Could not open file: %1. Error: %2").arg(file->fileName(),
+ file->errorString()));
+ }
+
+ // start to read the binary layout
+ BinaryLayout bl = BinaryContent::readBinaryLayout(file.data(), findMagicCookie(file.data(),
+ QInstaller::MagicCookie));
+
+ // calculate the offset of the component index start inside the binary
+ const qint64 resourceOffsetAndLengtSize = 2 * sizeof(qint64);
+ const qint64 resourceSectionSize = resourceOffsetAndLengtSize * bl.resourceCount;
+ file->seek(bl.endOfData - bl.indexSize - resourceSectionSize - resourceOffsetAndLengtSize);
+
+ const qint64 dataBlockStart = bl.endOfData - bl.dataBlockSize;
+ file->seek(retrieveInt64(file.data()) + dataBlockStart);
+ QInstallerCreator::ComponentIndex componentIndex = QInstallerCreator::ComponentIndex::read(file,
+ dataBlockStart);
+
+ QDirIterator it(repoPath, QDirIterator::Subdirectories);
+ while (it.hasNext() && !it.next().isEmpty()) {
+ if (it.fileInfo().isDir()) {
+ const QString fileName = it.fileName();
+ const QString absoluteTargetPath = QDir(repoPath).absoluteFilePath(fileName);
+
+ // zip the meta files that come with the offline installer
+ if (versionMap.contains(fileName)) {
+ helper.m_files.prepend(Static::createArchive(repoPath, absoluteTargetPath,
+ versionMap.value(fileName), &helper));
+ versionMap.remove(fileName);
+ emit outputTextChanged(helper.m_files.first());
+ }
+
+ // copy the 7z files that are inside the component index into the target
+ QInstallerCreator::Component c = componentIndex.componentByName(fileName.toUtf8());
+ if (c.archives().count()) {
+ QVector<QSharedPointer<QInstallerCreator::Archive> > archives = c.archives();
+ foreach (const QSharedPointer<QInstallerCreator::Archive> &a, archives) {
+ if (!a->open(QIODevice::ReadOnly))
+ continue;
+
+ QFile target(absoluteTargetPath + QDir::separator() + QString::fromUtf8(a->name()));
+ QInstaller::openForWrite(&target, target.fileName());
+ QInstaller::blockingCopy(a.data(), &target, a->size());
+ helper.m_files.prepend(target.fileName());
+ emit outputTextChanged(helper.m_files.first());
+ }
+ }
+ }
+ }
+
+ emit progressChanged(0.75);
+
+ QDir repo(repoPath);
+ if (!versionMap.isEmpty()) {
+ // offline installers might miss possible old components
+ foreach (const QString &dir, versionMap.keys()) {
+ const QString missingDir = repoPath + dir;
+ if (!repo.mkpath(missingDir))
+ throw QInstaller::Error(tr("Could not create target dir: %1.").arg(missingDir));
+ helper.m_files.prepend(Static::createArchive(repoPath, missingDir, versionMap.value(dir)
+ , &helper));
+ emit outputTextChanged(helper.m_files.first());
+ }
+ }
+
+ try {
+ // remove these, if we fail it doesn't hurt
+ Static::removeDirectory(QDir::cleanPath(repoPath + QLatin1String("/installer-config")),
+ &helper);
+ Static::removeDirectory(QDir::cleanPath(repoPath + QLatin1String("/config")), &helper);
+ const QStringList files = repo.entryList(QStringList() << QLatin1String("*.qrc"), QDir::Files);
+ foreach (const QString &file, files) {
+ if (repo.remove(file))
+ helper.m_files.removeAll(QDir::cleanPath(repoPath + file));
+ }
+ } catch (...) {}
+ setValue(QLatin1String("local-repo"), repoPath);
+ } catch (const Lib7z::SevenZipException &e) {
+ setError(UserDefinedError);
+ setErrorString(e.message());
+ return false;
+ } catch (const QInstaller::Error &e) {
+ setError(UserDefinedError);
+ setErrorString(e.message());
+ return false;
+ } catch (...) {
+ setError(UserDefinedError);
+ setErrorString(tr("Unknown exception caught: %1.").arg(QLatin1String(Q_FUNC_INFO)));
+ return false;
+ }
+ return true;
+}
+
+bool CreateLocalRepositoryOperation::undoOperation()
+{
+ Q_ASSERT(arguments().count() == 2);
+
+ AutoHelper _(this);
+ emit progressChanged(0.0);
+
+ QDir dir;
+ const QStringList files = value(QLatin1String("files")).toStringList();
+ foreach (const QString &file, files) {
+ emit outputTextChanged(tr("Removing file: %0").arg(file));
+ if (!QFile::remove(file)) {
+ setError(InvalidArguments);
+ setErrorString(tr("Could not remove %0.").arg(file));
+ return false;
+ }
+ dir.rmpath(QFileInfo(file).absolutePath());
+ }
+ setValue(QLatin1String("files"), QStringList());
+
+ QDir createdDir = QDir(value(QLatin1String("createddir")).toString());
+ if (createdDir == QDir::root() || !createdDir.exists())
+ return true;
+
+ QFile::remove(createdDir.path() + QLatin1String("/.DS_Store"));
+ QFile::remove(createdDir.path() + QLatin1String("/Thumbs.db"));
+
+ errno = 0;
+ const bool result = QDir::root().rmdir(createdDir.path());
+ if (!result) {
+ setError(UserDefinedError, tr("Cannot remove directory %1: %2").arg(createdDir.path(),
+ QLatin1String(strerror(errno))));
+ }
+ setValue(QLatin1String("files"), QStringList());
+
+ return result;
+}
+
+bool CreateLocalRepositoryOperation::testOperation()
+{
+ return true;
+}
+
+Operation *CreateLocalRepositoryOperation::clone() const
+{
+ return new CreateLocalRepositoryOperation();
+}
+
+void CreateLocalRepositoryOperation::emitFullProgress()
+{
+ emit progressChanged(1.0);
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/createlocalrepositoryoperation.h b/src/libs/installer/createlocalrepositoryoperation.h
new file mode 100644
index 000000000..928e2959e
--- /dev/null
+++ b/src/libs/installer/createlocalrepositoryoperation.h
@@ -0,0 +1,68 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef CREATELOCALREPOSITORYOPERATION_H
+#define CREATELOCALREPOSITORYOPERATION_H
+
+#include "qinstallerglobal.h"
+
+#include <QtCore/QObject>
+
+namespace QInstaller {
+
+class AutoHelper;
+
+class INSTALLER_EXPORT CreateLocalRepositoryOperation : public QObject, public Operation
+{
+ Q_OBJECT
+ friend class AutoHelper;
+
+public:
+ CreateLocalRepositoryOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+
+signals:
+ void progressChanged(double progress);
+ void outputTextChanged(const QString &message);
+
+private:
+ void emitFullProgress();
+};
+
+} // namespace QInstaller
+
+#endif // CREATELOCALREPOSITORYOPERATION_H
diff --git a/src/libs/installer/createshortcutoperation.cpp b/src/libs/installer/createshortcutoperation.cpp
new file mode 100644
index 000000000..bd0608233
--- /dev/null
+++ b/src/libs/installer/createshortcutoperation.cpp
@@ -0,0 +1,219 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#include "createshortcutoperation.h"
+
+#include "errors.h"
+#include "fileutils.h"
+
+#include "kdupdaterapplication.h"
+#include "kdupdaterpackagesinfo.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+#include <QtCore/QTemporaryFile>
+
+#include <algorithm>
+#include <cerrno>
+
+#ifdef Q_WS_WIN
+# include <windows.h>
+# include <shlobj.h>
+#endif
+
+using namespace QInstaller;
+
+static bool createLink(QString fileName, QString linkName, QString workingDir, QString arguments = QString())
+{
+#ifdef Q_WS_WIN
+ bool ret = false;
+ fileName = QDir::toNativeSeparators(fileName);
+ linkName = QDir::toNativeSeparators(linkName);
+ if (workingDir.isEmpty())
+ workingDir = QFileInfo(fileName).absolutePath();
+ workingDir = QDir::toNativeSeparators(workingDir);
+
+ //### assume that they add .lnk
+
+ IShellLink *psl;
+ bool neededCoInit = false;
+
+ HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
+
+ if (hres == CO_E_NOTINITIALIZED) { // COM was not initialized
+ neededCoInit = true;
+ CoInitialize(NULL);
+ hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
+ }
+
+ if (SUCCEEDED(hres)) {
+ hres = psl->SetPath((wchar_t *)fileName.utf16());
+ if (SUCCEEDED(hres) && !arguments.isNull())
+ hres = psl->SetArguments((wchar_t*)arguments.utf16());
+ if (SUCCEEDED(hres)) {
+ hres = psl->SetWorkingDirectory((wchar_t *)workingDir.utf16());
+ if (SUCCEEDED(hres)) {
+ IPersistFile *ppf;
+ hres = psl->QueryInterface(IID_IPersistFile, (void **)&ppf);
+ if (SUCCEEDED(hres)) {
+ hres = ppf->Save((wchar_t*)linkName.utf16(), TRUE);
+ if (SUCCEEDED(hres))
+ ret = true;
+ ppf->Release();
+ }
+ }
+ }
+ psl->Release();
+ }
+
+ if (neededCoInit)
+ CoUninitialize();
+
+ return ret;
+#else
+ Q_UNUSED(workingDir)
+ Q_UNUSED(arguments)
+ return QFile::link(fileName, linkName);
+#endif
+}
+
+/*
+TRANSLATOR QInstaller::CreateShortcutOperation
+*/
+
+CreateShortcutOperation::CreateShortcutOperation()
+{
+ setName(QLatin1String("CreateShortcut"));
+}
+
+static bool isWorkingDirOption(const QString &s)
+{
+ return s.startsWith(QLatin1String("workingDirectory="));
+}
+
+static QString takeWorkingDirArgument(QStringList &args)
+{
+ QString workingDir;
+ // if the args contain an option in the form "workingDirectory=...", find it and consume it
+ QStringList::iterator wdiropt = std::find_if(args.begin(), args.end(), isWorkingDirOption);
+ if (wdiropt != args.end()) {
+ workingDir = wdiropt->mid(QString::fromLatin1("workingDirectory=").size());
+ args.erase(wdiropt);
+ }
+ return workingDir;
+}
+
+void CreateShortcutOperation::backup()
+{
+}
+
+bool CreateShortcutOperation::performOperation()
+{
+ QStringList args = arguments();
+ const QString workingDir = takeWorkingDirArgument(args);
+
+ if (args.count() != 2 && args.count() != 3) {
+ setError(InvalidArguments);
+ setErrorString(QObject::tr("Invalid arguments: %1 arguments given, 2 or 3 expected (optional: "
+ "\"workingDirectory=...\").").arg(args.count()));
+ return false;
+ }
+
+ const QString& linkTarget = args.at(0);
+ const QString& linkLocation = args.at(1);
+ const QString targetArguments = args.value(2); //used value because it could be not existing
+
+ const QString linkPath = QFileInfo(linkLocation).absolutePath();
+
+ const bool linkPathAlreadyExists = QDir(linkPath).exists();
+ const bool created = linkPathAlreadyExists || QDir::root().mkpath(linkPath);
+
+ if (!created) {
+ setError(UserDefinedError);
+ setErrorString(tr("Could not create folder %1: %2.").arg(QDir::toNativeSeparators(linkPath),
+ QLatin1String(strerror(errno))));
+ return false;
+ }
+
+
+ //remove a possible existing older one
+ QString errorString;
+ if (QFile::exists(linkLocation) && !deleteFileNowOrLater(linkLocation, &errorString)) {
+ setError(UserDefinedError);
+ setErrorString(QObject::tr("Failed to overwrite %1: %2").arg(QDir::toNativeSeparators(linkLocation),
+ errorString));
+ return false;
+ }
+
+ const bool linked = createLink(linkTarget, linkLocation, workingDir, targetArguments);
+ if (!linked) {
+ setError(UserDefinedError);
+ setErrorString(tr("Could not create link %1: %2").arg(QDir::toNativeSeparators(linkLocation),
+ qt_error_string()));
+ return false;
+ }
+ return true;
+}
+
+bool CreateShortcutOperation::undoOperation()
+{
+ const QStringList args = arguments();
+
+ const QString& linkLocation = args.at(1);
+
+ // first remove the link
+ if (!deleteFileNowOrLater(linkLocation))
+ qDebug() << "Can't delete:" << linkLocation;
+
+ const QString linkPath = QFileInfo(linkLocation).absolutePath();
+
+ QStringList pathParts = QString(linkPath).remove(QDir::homePath()).split(QLatin1String("/"));
+ for (int i = pathParts.count(); i > 0; --i) {
+ QString possibleToDeleteDir = QDir::homePath() + QStringList(pathParts.mid(0, i)).join(QLatin1String("/"));
+ removeSystemGeneratedFiles(possibleToDeleteDir);
+ if (!possibleToDeleteDir.isEmpty() && QDir().rmdir(possibleToDeleteDir))
+ qDebug() << "Deleted directory:" << possibleToDeleteDir;
+ else
+ break;
+ }
+
+ return true;
+}
+
+bool CreateShortcutOperation::testOperation()
+{
+ return true;
+}
+
+Operation *CreateShortcutOperation::clone() const
+{
+ return new CreateShortcutOperation();
+}
diff --git a/src/libs/installer/createshortcutoperation.h b/src/libs/installer/createshortcutoperation.h
new file mode 100644
index 000000000..ab8a6b096
--- /dev/null
+++ b/src/libs/installer/createshortcutoperation.h
@@ -0,0 +1,54 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef CREATESHORTCUTOPERATION_H
+#define CREATESHORTCUTOPERATION_H
+
+#include "qinstallerglobal.h"
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT CreateShortcutOperation : public Operation
+{
+public:
+ CreateShortcutOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+};
+
+}
+
+#endif
diff --git a/src/libs/installer/downloadarchivesjob.cpp b/src/libs/installer/downloadarchivesjob.cpp
new file mode 100644
index 000000000..f33108442
--- /dev/null
+++ b/src/libs/installer/downloadarchivesjob.cpp
@@ -0,0 +1,340 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#include "downloadarchivesjob.h"
+
+#include "binaryformatenginehandler.h"
+#include "component.h"
+#include "messageboxhandler.h"
+#include "packagemanagercore.h"
+
+#include "kdupdaterfiledownloader.h"
+#include "kdupdaterfiledownloaderfactory.h"
+
+#include <QtCore/QFile>
+#include <QtCore/QTimerEvent>
+
+using namespace QInstaller;
+using namespace KDUpdater;
+
+
+/*!
+ Creates a new DownloadArchivesJob with \a parent.
+*/
+DownloadArchivesJob::DownloadArchivesJob(PackageManagerCore *core)
+ : KDJob(core),
+ m_core(core),
+ m_downloader(0),
+ m_archivesDownloaded(0),
+ m_archivesToDownloadCount(0),
+ m_canceled(false),
+ m_lastFileProgress(0),
+ m_progressChangedTimerId(0)
+{
+ setCapabilities(Cancelable);
+}
+
+/*!
+ Destroys the DownloadArchivesJob.
+ All temporary files get deleted.
+*/
+DownloadArchivesJob::~DownloadArchivesJob()
+{
+ foreach (const QString &fileName, m_temporaryFiles) {
+ QFile file(fileName);
+ if (file.exists() && !file.remove())
+ qWarning("Could not delete file %s: %s", qPrintable(fileName), qPrintable(file.errorString()));
+ }
+
+ if (m_downloader)
+ m_downloader->deleteLater();
+}
+
+/*!
+ Sets the archives to download. The first value of each pair contains the file name to register
+ the file in the installer's internal file system, the second one the source url.
+*/
+void DownloadArchivesJob::setArchivesToDownload(const QList<QPair<QString, QString> > &archives)
+{
+ m_archivesToDownload = archives;
+ m_archivesToDownloadCount = archives.count();
+}
+
+/*!
+ \reimp
+*/
+void DownloadArchivesJob::doStart()
+{
+ m_archivesDownloaded = 0;
+ fetchNextArchiveHash();
+}
+
+/*!
+ \reimp
+*/
+void DownloadArchivesJob::doCancel()
+{
+ m_canceled = true;
+ if (m_downloader != 0)
+ m_downloader->cancelDownload();
+}
+
+void DownloadArchivesJob::fetchNextArchiveHash()
+{
+ if (m_core->testChecksum()) {
+ if (m_canceled) {
+ finishWithError(tr("Canceled"));
+ return;
+ }
+
+ if (m_archivesToDownload.isEmpty()) {
+ emitFinished();
+ return;
+ }
+
+ if (m_downloader)
+ m_downloader->deleteLater();
+
+ m_downloader = setupDownloader(QLatin1String(".sha1"));
+ if (!m_downloader) {
+ m_archivesToDownload.removeFirst();
+ QMetaObject::invokeMethod(this, "fetchNextArchiveHash", Qt::QueuedConnection);
+ return;
+ }
+
+ connect(m_downloader, SIGNAL(downloadCompleted()), this, SLOT(finishedHashDownload()),
+ Qt::QueuedConnection);
+ m_downloader->download();
+ } else {
+ QMetaObject::invokeMethod(this, "fetchNextArchive", Qt::QueuedConnection);
+ }
+}
+
+void DownloadArchivesJob::finishedHashDownload()
+{
+ Q_ASSERT(m_downloader != 0);
+
+ const QString tempFile = m_downloader->downloadedFileName();
+ QFile sha1HashFile(tempFile);
+ if (sha1HashFile.open(QFile::ReadOnly))
+ m_currentHash = sha1HashFile.readAll();
+ else
+ finishWithError(tr("Downloading hash signature failed."));
+
+ m_temporaryFiles.insert(tempFile);
+
+ fetchNextArchive();
+}
+
+/*!
+ Fetches the next archive and registers it in the installer.
+*/
+void DownloadArchivesJob::fetchNextArchive()
+{
+ if (m_canceled) {
+ finishWithError(tr("Canceled"));
+ return;
+ }
+
+ if (m_archivesToDownload.isEmpty()) {
+ emitFinished();
+ return;
+ }
+
+ if (m_downloader != 0)
+ m_downloader->deleteLater();
+
+ m_downloader = setupDownloader();
+ if (!m_downloader) {
+ m_archivesToDownload.removeFirst();
+ QMetaObject::invokeMethod(this, "fetchNextArchive", Qt::QueuedConnection);
+ return;
+ }
+
+ emit progressChanged(double(m_archivesDownloaded) / m_archivesToDownloadCount);
+ connect(m_downloader, SIGNAL(downloadProgress(double)), this, SLOT(emitDownloadProgress(double)));
+ connect(m_downloader, SIGNAL(downloadCompleted()), this, SLOT(registerFile()), Qt::QueuedConnection);
+
+ m_downloader->download();
+}
+
+/*!
+ Emits the global download progress during a single download in a lazy way (uses a timer to reduce to
+ much processChanged).
+*/
+void DownloadArchivesJob::emitDownloadProgress(double progress)
+{
+ m_lastFileProgress = progress;
+ if (!m_progressChangedTimerId)
+ m_progressChangedTimerId = startTimer(5);
+}
+
+/*!
+ This is used to reduce the progressChanged signals.
+*/
+void DownloadArchivesJob::timerEvent(QTimerEvent *event)
+{
+ if (event->timerId() == m_progressChangedTimerId) {
+ killTimer(m_progressChangedTimerId);
+ m_progressChangedTimerId = 0;
+ emit progressChanged((double(m_archivesDownloaded) + m_lastFileProgress) / m_archivesToDownloadCount);
+ }
+}
+
+/*!
+ Registers the just downloaded file in the intaller's file system.
+*/
+void DownloadArchivesJob::registerFile()
+{
+ Q_ASSERT(m_downloader != 0);
+
+ ++m_archivesDownloaded;
+ if (m_progressChangedTimerId) {
+ killTimer(m_progressChangedTimerId);
+ m_progressChangedTimerId = 0;
+ emit progressChanged(double(m_archivesDownloaded) / m_archivesToDownloadCount);
+ }
+
+ const QString tempFile = m_downloader->downloadedFileName();
+ if (m_core->testChecksum()) {
+ QFile archiveFile(tempFile);
+ if (archiveFile.open(QFile::ReadOnly)) {
+ static QByteArray buffer(1024 * 1024, '\0');
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ while (true) {
+ const qint64 numRead = archiveFile.read(buffer.data(), buffer.size());
+ if (numRead <= 0)
+ break;
+ hash.addData(buffer.constData(), numRead);
+ }
+
+ const QByteArray archiveHash = hash.result().toHex();
+ if ((archiveHash != m_currentHash) && (!m_canceled)) {
+ //TODO: Maybe we should try to download the file again automatically
+ const QMessageBox::Button res =
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("DownloadError"), tr("Download Error"), tr("Hash verification while "
+ "downloading failed. This is a temporary error, please retry."),
+ QMessageBox::Retry | QMessageBox::Cancel, QMessageBox::Cancel);
+
+ if (res == QMessageBox::Cancel) {
+ finishWithError(tr("Could not verify Hash"));
+ return;
+ }
+
+ fetchNextArchiveHash();
+ return;
+ }
+ } else {
+ finishWithError(tr("Could not open %1").arg(tempFile));
+ }
+ }
+
+ m_temporaryFiles.insert(tempFile);
+ const QPair<QString, QString> pair = m_archivesToDownload.takeFirst();
+ QInstallerCreator::BinaryFormatEngineHandler::instance()->registerArchive(pair.first, tempFile);
+
+ fetchNextArchiveHash();
+}
+
+void DownloadArchivesJob::downloadCanceled()
+{
+ emitFinishedWithError(KDJob::Canceled, m_downloader->errorString());
+}
+
+void DownloadArchivesJob::downloadFailed(const QString &error)
+{
+ if (m_canceled)
+ return;
+
+ const QMessageBox::StandardButton b =
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("archiveDownloadError"), tr("Download Error"), tr("Could not download archive: %1 : %2")
+ .arg(m_archivesToDownload.first().second, error), QMessageBox::Retry | QMessageBox::Cancel);
+
+ if (b == QMessageBox::Retry)
+ QMetaObject::invokeMethod(this, "fetchNextArchiveHash", Qt::QueuedConnection);
+ else
+ downloadCanceled();
+}
+
+void DownloadArchivesJob::finishWithError(const QString &error)
+{
+ const FileDownloader *const dl = dynamic_cast<const FileDownloader*> (sender());
+ const QString msg = tr("Could not fetch archives: %1\nError while loading %2");
+ if (dl != 0)
+ emitFinishedWithError(QInstaller::DownloadError, msg.arg(error, dl->url().toString()));
+ else
+ emitFinishedWithError(QInstaller::DownloadError, msg.arg(error, m_downloader->url().toString()));
+}
+
+KDUpdater::FileDownloader *DownloadArchivesJob::setupDownloader(const QString &prefix)
+{
+ KDUpdater::FileDownloader *downloader = 0;
+ const QFileInfo fi = QFileInfo(m_archivesToDownload.first().first);
+ const Component *const component = m_core->componentByName(QFileInfo(fi.path()).fileName());
+ if (component) {
+ const QUrl url(m_archivesToDownload.first().second + prefix);
+ const QString &scheme = url.scheme();
+ downloader = FileDownloaderFactory::instance().create(scheme, this);
+
+ if (downloader) {
+ downloader->setUrl(url);
+ downloader->setAutoRemoveDownloadedFile(false);
+
+ QAuthenticator auth;
+ auth.setUser(component->value(QLatin1String("username")));
+ auth.setPassword(component->value(QLatin1String("password")));
+ downloader->setAuthenticator(auth);
+
+ connect(downloader, SIGNAL(downloadCanceled()), this, SLOT(downloadCanceled()));
+ connect(downloader, SIGNAL(downloadAborted(QString)), this, SLOT(downloadFailed(QString)),
+ Qt::QueuedConnection);
+ connect(downloader, SIGNAL(downloadStatus(QString)), this, SIGNAL(downloadStatusChanged(QString)));
+
+ if (scheme == QLatin1String("http") || scheme == QLatin1String("ftp") ||
+ scheme == QLatin1String("file")) {
+ downloader->setDownloadedFileName(component->localTempPath() + QLatin1String("/")
+ + component->name() + QLatin1String("/") + fi.fileName() + prefix);
+ }
+
+ QString message = tr("Downloading archive hash for component: %1");
+ if (prefix.isEmpty())
+ message = tr("Downloading archive for component: %1");
+ emit outputTextChanged(message.arg(component->displayName()));
+ } else {
+ emit outputTextChanged(tr("Scheme not supported: %1 (%2)").arg(scheme, url.toString()));
+ }
+ } else {
+ emit outputTextChanged(tr("Could not find component for: %1.").arg(QFileInfo(fi.path()).fileName()));
+ }
+ return downloader;
+}
diff --git a/src/libs/installer/downloadarchivesjob.h b/src/libs/installer/downloadarchivesjob.h
new file mode 100644
index 000000000..1c0c9b272
--- /dev/null
+++ b/src/libs/installer/downloadarchivesjob.h
@@ -0,0 +1,104 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef DOWNLOADARCHIVESJOB_H
+#define DOWNLOADARCHIVESJOB_H
+
+#include <kdjob.h>
+
+#include <QtCore/QPair>
+#include <QtCore/QSet>
+
+QT_BEGIN_NAMESPACE
+class QTimerEvent;
+QT_END_NAMESPACE
+
+namespace KDUpdater {
+ class FileDownloader;
+}
+
+namespace QInstaller {
+
+class MessageBoxHandler;
+class PackageManagerCore;
+
+class DownloadArchivesJob : public KDJob
+{
+ Q_OBJECT
+
+public:
+ explicit DownloadArchivesJob(PackageManagerCore *core = 0);
+ ~DownloadArchivesJob();
+
+ void setArchivesToDownload(const QList<QPair<QString, QString> > &archives);
+
+Q_SIGNALS:
+ void progressChanged(double progress);
+ void outputTextChanged(const QString &progress);
+ void downloadStatusChanged(const QString &status);
+
+protected:
+ void doStart();
+ void doCancel();
+ void timerEvent(QTimerEvent *event);
+
+protected Q_SLOTS:
+ void registerFile();
+ void downloadCanceled();
+ void downloadFailed(const QString &error);
+ void finishWithError(const QString &error);
+ void fetchNextArchive();
+ void fetchNextArchiveHash();
+ void finishedHashDownload();
+ void emitDownloadProgress(double progress);
+
+private:
+ KDUpdater::FileDownloader *setupDownloader(const QString &prefix = QString());
+
+private:
+ PackageManagerCore *m_core;
+ KDUpdater::FileDownloader *m_downloader;
+
+ int m_archivesDownloaded;
+ int m_archivesToDownloadCount;
+ QList<QPair<QString, QString> > m_archivesToDownload;
+
+ bool m_canceled;
+ QSet<QString> m_temporaryFiles;
+ QByteArray m_currentHash;
+ double m_lastFileProgress;
+ int m_progressChangedTimerId;
+};
+
+} // namespace QInstaller
+
+#endif // DOWNLOADARCHIVESJOB_H
diff --git a/src/libs/installer/elevatedexecuteoperation.cpp b/src/libs/installer/elevatedexecuteoperation.cpp
new file mode 100644
index 000000000..1e1539222
--- /dev/null
+++ b/src/libs/installer/elevatedexecuteoperation.cpp
@@ -0,0 +1,276 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "elevatedexecuteoperation.h"
+
+#include "environment.h"
+#include "qprocesswrapper.h"
+
+#include <QtCore/QThread>
+#include <QtCore/QProcessEnvironment>
+#include <QtCore/QDebug>
+
+using namespace QInstaller;
+
+class ElevatedExecuteOperation::Private
+{
+public:
+ explicit Private(ElevatedExecuteOperation *qq)
+ : q(qq), process(0), showStandardError(false)
+ {
+ }
+
+private:
+ ElevatedExecuteOperation *const q;
+
+public:
+ void readProcessOutput();
+ bool run(const QStringList &arguments);
+
+ QProcessWrapper *process;
+ bool showStandardError;
+};
+
+ElevatedExecuteOperation::ElevatedExecuteOperation()
+ : d(new Private(this))
+{
+ // this operation has to "overwrite" the Execute operation from KDUpdater
+ setName(QLatin1String("Execute"));
+}
+
+ElevatedExecuteOperation::~ElevatedExecuteOperation()
+{
+ delete d;
+}
+
+bool ElevatedExecuteOperation::performOperation()
+{
+ // This operation receives only one argument. It is the complete
+ // command line of the external program to execute.
+ if (arguments().isEmpty()) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %1: %2 arguments given, at least 1 expected.").arg(name(),
+ QString::number(arguments().count())));
+ return false;
+ }
+ QStringList args;
+ foreach (const QString &argument, arguments()) {
+ if (argument!=QLatin1String("UNDOEXECUTE"))
+ args.append(argument);
+ else
+ break; //we don't need the UNDOEXECUTE args here
+ }
+
+ return d->run(args);
+}
+
+bool ElevatedExecuteOperation::Private::run(const QStringList &arguments)
+{
+ QStringList args = arguments;
+ QString workingDirectory;
+ QStringList filteredWorkingDirectoryArgs = args.filter(QLatin1String("workingdirectory="),
+ Qt::CaseInsensitive);
+ if (!filteredWorkingDirectoryArgs.isEmpty()) {
+ QString workingDirectoryArgument = filteredWorkingDirectoryArgs.at(0);
+ workingDirectory = workingDirectoryArgument;
+ workingDirectory.replace(QLatin1String("workingdirectory="), QString(), Qt::CaseInsensitive);
+ args.removeAll(workingDirectoryArgument);
+ }
+
+
+ if (args.last().endsWith(QLatin1String("showStandardError"))) {
+ showStandardError = true;
+ args.pop_back();
+ }
+
+ 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.constBegin(); it != numbers.constEnd(); ++it)
+ allowedExitCodes.push_back(it->toInt());
+ args.pop_front();
+ } else {
+ allowedExitCodes.push_back(0);
+ }
+
+ const QString callstr = args.join(QLatin1String(" "));
+
+ // unix style: when there's an ampersand after the command, it's started detached
+ if (args.count() >= 2 && args.last() == QLatin1String("&")) {
+ args.pop_back();
+ const bool success = QProcessWrapper::startDetached(args.front(), args.mid(1));
+ if (!success) {
+ q->setError(UserDefinedError);
+ q->setErrorString(tr("Execution failed: Could not start detached: \"%1\"").arg(callstr));
+ }
+ return success;
+ }
+
+ process = new QProcessWrapper();
+ if (!workingDirectory.isEmpty()) {
+ process->setWorkingDirectory(workingDirectory);
+ qDebug() << "ElevatedExecuteOperation setWorkingDirectory:" << workingDirectory;
+ }
+
+ QProcessEnvironment penv;
+ // there is no way to serialize a QProcessEnvironment properly other than per mangled QStringList:
+ // (i.e. no other way to list all keys)
+ process->setEnvironment(KDUpdater::Environment::instance().applyTo(penv).toStringList());
+
+ if (showStandardError)
+ process->setProcessChannelMode(QProcessWrapper::MergedChannels);
+
+ connect(q, SIGNAL(cancelProcess()), process, SLOT(cancel()));
+
+ //we still like the none blocking possibility to perform this operation without threads
+ QEventLoop loop;
+ if (QThread::currentThread() == qApp->thread()) {
+ QObject::connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit()));
+ }
+ //readProcessOutput should only called from this current Thread -> Qt::DirectConnection
+ QObject::connect(process, SIGNAL(readyRead()), q, SLOT(readProcessOutput()), Qt::DirectConnection);
+#ifdef Q_OS_WIN
+ if (args.count() == 1) {
+ process->setNativeArguments(args.front());
+ qDebug() << "ElevatedExecuteOperation setNativeArguments to start:" << args.front();
+ process->start(QString(), QStringList());
+ } else
+#endif
+ {
+ process->start(args.front(), args.mid(1));
+ }
+ qDebug() << args.front() << "started, arguments:" << QStringList(args.mid(1)).join(QLatin1String(" "));
+
+ bool success = false;
+ //we still like the none blocking possibility to perform this operation without threads
+ if (QThread::currentThread() == qApp->thread()) {
+ success = process->waitForStarted();
+ } else {
+ success = process->waitForFinished(-1);
+ }
+
+ bool returnValue = true;
+ if (!success) {
+ q->setError(UserDefinedError);
+ //TODO: pass errorString() through the wrapper */
+ q->setErrorString(tr("Execution failed: Could not start: \"%1\"").arg(callstr));
+ returnValue = false;
+ }
+
+ if (QThread::currentThread() == qApp->thread()) {
+ if (process->state() != QProcessWrapper::NotRunning) {
+ loop.exec();
+ }
+ readProcessOutput();
+ }
+
+ q->setValue(QLatin1String("ExitCode"), process->exitCode());
+
+ if (process->exitStatus() == QProcessWrapper::CrashExit) {
+ q->setError(UserDefinedError);
+ q->setErrorString(tr("Execution failed(Crash): \"%1\"").arg(callstr));
+ returnValue = false;
+ }
+
+ if (!allowedExitCodes.contains(process->exitCode())) {
+ q->setError(UserDefinedError);
+ q->setErrorString(tr("Execution failed(Unexpected exit code: %1): \"%2\"")
+ .arg(QString::number(process->exitCode()), callstr));
+ returnValue = false;
+ }
+
+ Q_ASSERT(process);
+ process->deleteLater();
+ process = 0;
+
+ return returnValue;
+}
+
+/*!
+ Cancels the ElevatedExecuteOperation. This methods tries to terminate the process
+ gracefully by calling QProcessWrapper::terminate. After 10 seconds, the process gets killed.
+ */
+void ElevatedExecuteOperation::cancelOperation()
+{
+ emit cancelProcess();
+}
+
+void ElevatedExecuteOperation::Private::readProcessOutput()
+{
+ Q_ASSERT(process);
+ Q_ASSERT(QThread::currentThread() == process->thread());
+ if (QThread::currentThread() != process->thread()) {
+ qDebug() << Q_FUNC_INFO << "can only be called from the same thread as the process is.";
+ }
+ const QByteArray output = process->readAll();
+ if (!output.isEmpty()) {
+ qDebug() << output;
+ emit q->outputTextChanged(QString::fromLocal8Bit(output));
+ }
+}
+
+
+bool ElevatedExecuteOperation::undoOperation()
+{
+ QStringList args;
+ bool found = false;
+ foreach (const QString &argument, arguments()) {
+ if (found)
+ args.append(argument);
+ else
+ found = argument == QLatin1String("UNDOEXECUTE");
+ }
+ if (args.isEmpty())
+ return true;
+
+ return d->run(args);
+}
+
+bool ElevatedExecuteOperation::testOperation()
+{
+ // TODO
+ return true;
+}
+
+Operation *ElevatedExecuteOperation::clone() const
+{
+ return new ElevatedExecuteOperation;
+}
+
+void ElevatedExecuteOperation::backup()
+{
+}
+
+
+#include "moc_elevatedexecuteoperation.cpp"
diff --git a/src/libs/installer/elevatedexecuteoperation.h b/src/libs/installer/elevatedexecuteoperation.h
new file mode 100644
index 000000000..db9575380
--- /dev/null
+++ b/src/libs/installer/elevatedexecuteoperation.h
@@ -0,0 +1,70 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef ELEVATEDEXECUTEOPERATION_H
+#define ELEVATEDEXECUTEOPERATION_H
+
+#include "qinstallerglobal.h"
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT ElevatedExecuteOperation : public QObject, public Operation
+{
+ Q_OBJECT
+
+public:
+ ElevatedExecuteOperation();
+ ~ElevatedExecuteOperation();
+
+ virtual void backup();
+ virtual bool performOperation();
+ virtual bool undoOperation();
+ virtual bool testOperation();
+ virtual Operation *clone() const;
+
+Q_SIGNALS:
+ void cancelProcess();
+ void outputTextChanged(const QString &text);
+
+public Q_SLOTS:
+ void cancelOperation();
+
+private:
+ Q_PRIVATE_SLOT(d, void readProcessOutput())
+
+ class Private;
+ Private *d;
+};
+
+} // namespace
+
+#endif
diff --git a/src/libs/installer/environmentvariablesoperation.cpp b/src/libs/installer/environmentvariablesoperation.cpp
new file mode 100644
index 000000000..c71a250c9
--- /dev/null
+++ b/src/libs/installer/environmentvariablesoperation.cpp
@@ -0,0 +1,230 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "environmentvariablesoperation.h"
+#include "qsettingswrapper.h"
+
+#include <stdlib.h>
+
+#include "environment.h"
+
+#ifdef Q_WS_WIN
+# include <windows.h>
+#endif
+
+using namespace QInstaller;
+using namespace KDUpdater;
+
+/*
+TRANSLATOR QInstaller::EnvironmentVariablesOperation
+*/
+
+EnvironmentVariableOperation::EnvironmentVariableOperation()
+{
+ setName(QLatin1String("EnvironmentVariable"));
+}
+
+void EnvironmentVariableOperation::backup()
+{
+}
+
+#ifdef Q_WS_WIN
+static bool broadcastChange() {
+ // Use SendMessageTimeout to Broadcast a message to the whole system to update settings of all
+ // running applications. This is needed to activate the changes done above without logout+login.
+ // Note that cmd.exe does not respond to any WM_SETTINGCHANGE messages...
+ DWORD aResult = 0;
+ LRESULT sendresult = SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE,
+ 0, (LPARAM) "Environment", SMTO_BLOCK | SMTO_ABORTIFHUNG, 5000, &aResult);
+ if (sendresult == 0 || aResult != 0) {
+ qWarning("Failed to broadcast a WM_SETTINGCHANGE message\n");
+ return false;
+ }
+
+ return true;
+}
+#endif
+
+namespace {
+
+template <typename SettingsType>
+UpdateOperation::Error writeSetting(const QString &regPath,
+ const QString &name,
+ const QString &value,
+ QString *errorString,
+ QString *oldValue)
+{
+ oldValue->clear();
+ SettingsType registry(regPath, QSettingsWrapper::NativeFormat);
+ if (!registry.isWritable()) {
+ *errorString = QObject::tr("Registry path %1 is not writable").arg(regPath);
+ return UpdateOperation::UserDefinedError;
+ }
+
+ // remember old value for undo
+ *oldValue = registry.value(name).toString();
+
+ // set the new value
+ registry.setValue(name, value);
+ registry.sync();
+
+ if (registry.status() != QSettingsWrapper::NoError) {
+ *errorString = QObject::tr("Could not write to registry path %1").arg(regPath);
+ return UpdateOperation::UserDefinedError;
+ }
+
+ return UpdateOperation::NoError;
+}
+
+template <typename SettingsType>
+UpdateOperation::Error undoSetting(const QString &regPath,
+ const QString &name,
+ const QString &value,
+ const QString &oldValue,
+ QString *errorString)
+{
+ QString actual;
+ {
+ SettingsType registry(regPath, QSettingsWrapper::NativeFormat);
+ actual = registry.value(name).toString();
+ }
+ if (actual != value) //key changed, don't undo
+ return UpdateOperation::UserDefinedError;
+ QString dontcare;
+ return writeSetting<SettingsType>(regPath, name, oldValue, errorString, &dontcare);
+}
+
+} // namespace
+
+bool EnvironmentVariableOperation::performOperation()
+{
+ if (arguments().count() < 2 || arguments().count() > 4) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, 2-3 expected.")
+ .arg(name()).arg(arguments().count()));
+ return false;
+ }
+
+ const QString name = arguments().at(0);
+ const QString value = arguments().at(1);
+ bool isPersistent = false;
+
+#ifdef Q_WS_WIN
+ isPersistent = arguments().count() >= 3 ? arguments().at(2) == QLatin1String("true") : true;
+ const bool isSystemWide = arguments().count() >= 4 ? arguments().at(3) == QLatin1String("true") : false;
+ QString oldvalue;
+ if (isPersistent) {
+ const QString regPath = isSystemWide ? QLatin1String("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet"
+ "\\Control\\Session Manager\\Environment") : QLatin1String("HKEY_CURRENT_USER\\Environment");
+
+ // write the name=value pair to the global environment
+ QString errorString;
+
+ Error err = NoError;
+
+ err = isSystemWide
+ ? writeSetting<QSettingsWrapper>(regPath, name, value, &errorString, &oldvalue)
+ : writeSetting<QSettingsWrapper>(regPath, name, value, &errorString, &oldvalue);
+ if (err != NoError) {
+ setError(err);
+ setErrorString(errorString);
+ return false;
+ }
+ const bool bret = broadcastChange();
+ Q_UNUSED(bret); // this is not critical, so fall-through
+ setValue(QLatin1String("oldvalue"), oldvalue);
+ return true;
+ }
+#endif
+ Q_ASSERT(!isPersistent);
+ Q_UNUSED(isPersistent)
+
+ setValue(QLatin1String("oldvalue"), Environment::instance().value(name));
+ Environment::instance().setTemporaryValue(name, value);
+
+ return true;
+}
+
+bool EnvironmentVariableOperation::undoOperation()
+{
+ if (arguments().count() < 2 || arguments().count() > 4)
+ return false;
+
+ const QString name = arguments().at(0);
+ const QString value = arguments().at(1);
+ const QString oldvalue = this->value(QLatin1String("oldvalue")).toString();
+
+#ifdef Q_WS_WIN
+ const bool isPersistent = arguments().count() >= 3 ? arguments().at(2) == QLatin1String("true") : true;
+#else
+ const bool isPersistent = false;
+#endif
+
+ if (!isPersistent) {
+ const QString actual = Environment::instance().value(name);
+ const bool doUndo = actual == value;
+ if (doUndo)
+ Environment::instance().setTemporaryValue(name, oldvalue);
+ return doUndo;
+ }
+
+#ifdef Q_WS_WIN
+ const bool isSystemWide = arguments().count() >= 4 ? arguments().at(3) == QLatin1String("true") : false;
+
+ const QString regPath = isSystemWide ? QLatin1String("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\"
+ "Control\\Session Manager\\Environment") : QLatin1String("HKEY_CURRENT_USER\\Environment");
+
+ QString errorString;
+
+ const Error err = isSystemWide
+ ? undoSetting<QSettingsWrapper>(regPath, name, value, oldvalue, &errorString)
+ : undoSetting<QSettingsWrapper>(regPath, name, value, oldvalue, &errorString);
+
+ if (err != NoError) {
+ setError(err);
+ setErrorString(errorString);
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+bool EnvironmentVariableOperation::testOperation()
+{
+ return true;
+}
+
+Operation *EnvironmentVariableOperation::clone() const
+{
+ return new EnvironmentVariableOperation();
+}
diff --git a/src/libs/installer/environmentvariablesoperation.h b/src/libs/installer/environmentvariablesoperation.h
new file mode 100644
index 000000000..c8bc98c07
--- /dev/null
+++ b/src/libs/installer/environmentvariablesoperation.h
@@ -0,0 +1,54 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef ENVIRONMENTVARIABLESOPERATION_H
+#define ENVIRONMENTVARIABLESOPERATION_H
+
+#include "qinstallerglobal.h"
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT EnvironmentVariableOperation : public Operation
+{
+public:
+ EnvironmentVariableOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+};
+
+}
+
+#endif
diff --git a/src/libs/installer/errors.h b/src/libs/installer/errors.h
new file mode 100644
index 000000000..09ad52b14
--- /dev/null
+++ b/src/libs/installer/errors.h
@@ -0,0 +1,59 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef ERRORS_H
+#define ERRORS_H
+
+#include <QtCore/QDebug>
+#include <QtCore/QString>
+
+#include <stdexcept>
+
+namespace QInstaller {
+
+class Error : public std::runtime_error
+{
+public:
+ explicit Error(const QString &message)
+ : std::runtime_error(message.toStdString())
+ , m_message (message) { qWarning() << "Error-Exception:" << message; }
+ virtual ~Error() throw() {}
+
+ QString message() const { return m_message; }
+
+private:
+ QString m_message;
+};
+
+}
+
+#endif // ERRORS_H
diff --git a/src/libs/installer/extractarchiveoperation.cpp b/src/libs/installer/extractarchiveoperation.cpp
new file mode 100644
index 000000000..f07a91d5a
--- /dev/null
+++ b/src/libs/installer/extractarchiveoperation.cpp
@@ -0,0 +1,149 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "extractarchiveoperation.h"
+#include "extractarchiveoperation_p.h"
+
+#include <QtCore/QEventLoop>
+#include <QtCore/QThread>
+#include <QtCore/QThreadPool>
+
+using namespace QInstaller;
+
+
+ExtractArchiveOperation::ExtractArchiveOperation()
+{
+ setName(QLatin1String("Extract"));
+}
+
+void ExtractArchiveOperation::backup()
+{
+ // we need to backup on the fly...
+}
+
+bool ExtractArchiveOperation::performOperation()
+{
+ const QStringList args = arguments();
+ if (args.count() != 2) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, 2 expected.").arg(name()).arg(args
+ .count()));
+ return false;
+ }
+
+ const QString archivePath = args.first();
+ const QString targetDir = args.at(1);
+
+ Receiver receiver;
+ Callback callback;
+
+ // usually we have to connect it as queued connection but then some blocking work is in the main thread
+ connect(&callback, SIGNAL(progressChanged(QString)), this, SLOT(slotProgressChanged(QString)),
+ Qt::DirectConnection);
+
+ if (PackageManagerCore *core = this->value(QLatin1String("installer")).value<PackageManagerCore*>()) {
+ connect(core, SIGNAL(statusChanged(QInstaller::PackageManagerCore::Status)), &callback,
+ SLOT(statusChanged(QInstaller::PackageManagerCore::Status)), Qt::QueuedConnection);
+ }
+
+ //Runnable is derived from QRunable which will be deleted by the ThreadPool -> no parent is needed
+ Runnable *runnable = new Runnable(archivePath, targetDir, &callback);
+ connect(runnable, SIGNAL(finished(bool,QString)), &receiver, SLOT(runnableFinished(bool,QString)),
+ Qt::QueuedConnection);
+
+ QEventLoop loop;
+ connect(&receiver, SIGNAL(finished()), &loop, SLOT(quit()));
+ if (QThreadPool::globalInstance()->tryStart(runnable)) {
+ loop.exec();
+ } else {
+ // in case there is no availabe thread we should call it directly this is more a hack
+ runnable->run();
+ receiver.runnableFinished(true, QString());
+ }
+
+ typedef QPair<QString, QString> StringPair;
+ QVector<StringPair> backupFiles = callback.backupFiles;
+
+ //TODO use backups for rollback, too? doesn't work for uninstallation though
+
+ //delete all backups we can delete right now, remember the rest
+ foreach (const StringPair &i, backupFiles)
+ deleteFileNowOrLater(i.second);
+
+ if (!receiver.success) {
+ setError(UserDefinedError);
+ setErrorString(receiver.errorString);
+ return false;
+ }
+ return true;
+}
+
+bool ExtractArchiveOperation::undoOperation()
+{
+ Q_ASSERT(arguments().count() == 2);
+ //const QString archivePath = arguments().first();
+ //const QString targetDir = arguments().last();
+
+ const QStringList files = value(QLatin1String("files")).toStringList();
+
+ WorkerThread *const thread = new WorkerThread(this, files);
+ connect(thread, SIGNAL(outputTextChanged(QString)), this, SIGNAL(outputTextChanged(QString)),
+ Qt::QueuedConnection);
+
+ QEventLoop loop;
+ connect(thread, SIGNAL(finished()), &loop, SLOT(quit()), Qt::QueuedConnection);
+ thread->start();
+ loop.exec();
+ thread->deleteLater();
+ return true;
+}
+
+bool ExtractArchiveOperation::testOperation()
+{
+ return true;
+}
+
+Operation *ExtractArchiveOperation::clone() const
+{
+ return new ExtractArchiveOperation();
+}
+
+/*!
+ This slot is direct connected to the caller so please don't call it from another thread in the same time.
+*/
+void ExtractArchiveOperation::slotProgressChanged(const QString &filename)
+{
+ QStringList files = value(QLatin1String("files")).toStringList();
+ files.prepend(filename);
+ setValue(QLatin1String("files"), files);
+ emit outputTextChanged(filename);
+}
diff --git a/src/libs/installer/extractarchiveoperation.h b/src/libs/installer/extractarchiveoperation.h
new file mode 100644
index 000000000..822a43510
--- /dev/null
+++ b/src/libs/installer/extractarchiveoperation.h
@@ -0,0 +1,70 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef EXTRACTARCHIVEOPERATION_H
+#define EXTRACTARCHIVEOPERATION_H
+
+#include "qinstallerglobal.h"
+
+#include <QtCore/QObject>
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT ExtractArchiveOperation : public QObject, public Operation
+{
+ Q_OBJECT
+ friend class WorkerThread;
+
+public:
+ ExtractArchiveOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+
+Q_SIGNALS:
+ void outputTextChanged(const QString &progress);
+
+private Q_SLOTS:
+ void slotProgressChanged(const QString &progress);
+
+private:
+ class Callback;
+ class Runnable;
+ class Receiver;
+};
+
+}
+
+#endif
diff --git a/src/libs/installer/extractarchiveoperation_p.h b/src/libs/installer/extractarchiveoperation_p.h
new file mode 100644
index 000000000..25c38e61e
--- /dev/null
+++ b/src/libs/installer/extractarchiveoperation_p.h
@@ -0,0 +1,231 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#ifndef EXTRACTARCHIVEOPERATION_P_H
+#define EXTRACTARCHIVEOPERATION_P_H
+
+#include "extractarchiveoperation.h"
+
+#include "fileutils.h"
+#include "lib7z_facade.h"
+#include "packagemanagercore.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QPair>
+#include <QtCore/QThread>
+#include <QtCore/QVector>
+
+namespace QInstaller {
+
+class WorkerThread : public QThread
+{
+ Q_OBJECT
+public:
+ WorkerThread(ExtractArchiveOperation *op, const QStringList &files, QObject *parent = 0)
+ : QThread(parent)
+ , m_files(files)
+ , m_op(op)
+ {
+ }
+
+ void run()
+ {
+ ExtractArchiveOperation *const op = m_op;//dynamic_cast< ExtractArchiveOperation* >(parent());
+ Q_ASSERT(op != 0);
+
+ foreach (const QString &file, m_files) {
+ const QFileInfo fi(file);
+ emit outputTextChanged(file);
+ if (fi.isFile() || fi.isSymLink()) {
+ op->deleteFileNowOrLater(fi.absoluteFilePath());
+ } else if (fi.isDir()) {
+ const QDir d = fi.dir();
+ removeSystemGeneratedFiles(file);
+ d.rmdir(file); // directory may not exist
+ }
+ }
+ }
+
+Q_SIGNALS:
+ void outputTextChanged(const QString &filename);
+
+private:
+ QStringList m_files;
+ ExtractArchiveOperation *m_op;
+};
+
+
+class ExtractArchiveOperation::Callback : public QObject, public Lib7z::ExtractCallback
+{
+ Q_OBJECT
+
+public:
+ HRESULT state;
+ bool createBackups;
+ QVector<QPair<QString, QString> > backupFiles;
+
+ Callback() : state(S_OK), createBackups(true) {}
+
+Q_SIGNALS:
+ void progressChanged(const QString &filename);
+
+public Q_SLOTS:
+ void statusChanged(QInstaller::PackageManagerCore::Status status)
+ {
+ switch(status) {
+ case PackageManagerCore::Canceled:
+ state = E_ABORT;
+ break;
+ case PackageManagerCore::Failure:
+ state = E_FAIL;
+ break;
+ case PackageManagerCore::Unfinished: // fall through
+ case PackageManagerCore::Success:
+ case PackageManagerCore::Running:
+ //state = S_OK;
+ break;
+ }
+ }
+
+protected:
+ void setCurrentFile(const QString &filename)
+ {
+ emit progressChanged(QDir::toNativeSeparators(filename));
+ }
+
+ static QString generateBackupName(const QString &fn)
+ {
+ const QString bfn = fn + QLatin1String(".tmpUpdate");
+ QString res = bfn;
+ int i = 0;
+ while (QFile::exists(res))
+ res = bfn + QString::fromLatin1(".%1").arg(i++);
+ return res;
+ }
+
+ bool prepareForFile(const QString &filename)
+ {
+ if (!createBackups)
+ return true;
+ if (!QFile::exists(filename))
+ return true;
+ const QString backup = generateBackupName(filename);
+ QFile f(filename);
+ const bool renamed = f.rename(backup);
+ if (f.exists() && !renamed) {
+ qCritical("Could not rename %s to %s: %s", qPrintable(filename), qPrintable(backup),
+ qPrintable(f.errorString()));
+ return false;
+ }
+ backupFiles.push_back(qMakePair(filename, backup));
+ return true;
+ }
+
+ HRESULT setCompleted(quint64 /*completed*/, quint64 /*total*/)
+ {
+ return state;
+ }
+};
+
+class ExtractArchiveOperation::Runnable : public QObject, public QRunnable
+{
+ Q_OBJECT
+
+public:
+ Runnable(const QString &archivePath_, const QString &targetDir_, ExtractArchiveOperation::Callback *callback_)
+ : QObject()
+ , QRunnable()
+ , archivePath(archivePath_)
+ , targetDir(targetDir_)
+ , callback(callback_) {}
+
+ void run()
+ {
+ QFile archive(archivePath);
+ if (!archive.open(QIODevice::ReadOnly)) {
+
+ emit finished(false, tr("Could not open %1 for reading: %2.").arg(archivePath, archive.errorString()));
+ return;
+ }
+
+ try {
+ Lib7z::extractArchive(&archive, targetDir, callback);
+ emit finished(true, QString());
+ } catch (const Lib7z::SevenZipException& e) {
+#ifdef Q_WS_WIN
+ emit finished(false, tr("Error while extracting %1: %2. (Maybe the target dir(%3) is blocked by "
+ "another process.)").arg(archivePath, e.message(), targetDir));
+#else
+ emit finished(false, tr("Error while extracting %1: %2.").arg(archivePath, e.message()));
+#endif
+ } catch (...) {
+ emit finished(false, tr("Unknown exception caught while extracting %1.").arg(archivePath));
+ }
+ }
+
+Q_SIGNALS:
+ void finished(bool success, const QString &errorString);
+
+private:
+ const QString archivePath;
+ const QString targetDir;
+ ExtractArchiveOperation::Callback *const callback;
+};
+
+
+class ExtractArchiveOperation::Receiver : public QObject
+{
+ Q_OBJECT
+public:
+ explicit Receiver(QObject *parent = 0)
+ : QObject(parent)
+ , success(false) {}
+
+public Q_SLOTS:
+ void runnableFinished(bool ok, const QString &msg)
+ {
+ success = ok;
+ errorString = msg;
+ emit finished();
+ }
+
+Q_SIGNALS:
+ void finished();
+
+public:
+ bool success;
+ QString errorString;
+};
+
+}
+
+#endif // EXTRACTARCHIVEOPERATION_P_H
diff --git a/src/libs/installer/fakestopprocessforupdateoperation.cpp b/src/libs/installer/fakestopprocessforupdateoperation.cpp
new file mode 100644
index 000000000..9bfd6f35e
--- /dev/null
+++ b/src/libs/installer/fakestopprocessforupdateoperation.cpp
@@ -0,0 +1,129 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "fakestopprocessforupdateoperation.h"
+
+#include <kdsysinfo.h>
+#include <QtCore/QDir>
+
+#include <algorithm>
+
+using namespace KDUpdater;
+
+/*!
+ Copied from QInstaller with some adjustments
+ Return true, if a process with \a name is running. On Windows, the comparision is case-insensitive.
+*/
+static bool isProcessRunning(const QString &name, const QList<ProcessInfo> &processes)
+{
+ for (QList<ProcessInfo>::const_iterator it = processes.constBegin(); it != processes.constEnd(); ++it) {
+ if (it->name.isEmpty())
+ continue;
+
+#ifndef Q_WS_WIN
+ if (it->name == name)
+ return true;
+ const QFileInfo fi(it->name);
+ if (fi.fileName() == name || fi.baseName() == name)
+ return true;
+#else
+ if (it->name.toLower() == name.toLower())
+ return true;
+ if (it->name.toLower() == QDir::toNativeSeparators(name.toLower()))
+ return true;
+ const QFileInfo fi(it->name);
+ if (fi.fileName().toLower() == name.toLower() || fi.baseName().toLower() == name.toLower())
+ return true;
+#endif
+ }
+ return false;
+}
+
+static QStringList checkRunningProcessesFromList(const QStringList &processList)
+{
+ const QList<ProcessInfo> allProcesses = runningProcesses();
+ QStringList stillRunningProcesses;
+ foreach (const QString &process, processList) {
+ if (!process.isEmpty() && isProcessRunning(process, allProcesses))
+ stillRunningProcesses.append(process);
+ }
+ return stillRunningProcesses;
+}
+
+using namespace QInstaller;
+
+FakeStopProcessForUpdateOperation::FakeStopProcessForUpdateOperation()
+{
+ setName(QLatin1String("FakeStopProcessForUpdate"));
+}
+
+void FakeStopProcessForUpdateOperation::backup()
+{
+}
+
+bool FakeStopProcessForUpdateOperation::performOperation()
+{
+ return true;
+}
+
+bool FakeStopProcessForUpdateOperation::undoOperation()
+{
+ setError(KDUpdater::UpdateOperation::NoError);
+ if (arguments().size() != 1) {
+ setError(KDUpdater::UpdateOperation::InvalidArguments, QObject::tr("Number of arguments does not "
+ "match : one is required"));
+ return false;
+ }
+
+ QStringList processList = arguments()[0].split(QLatin1String(","), QString::SkipEmptyParts);
+ qSort(processList);
+ processList.erase(std::unique(processList.begin(), processList.end()), processList.end());
+ if (!processList.isEmpty()) {
+ const QStringList processes = checkRunningProcessesFromList(processList);
+ if (!processes.isEmpty()) {
+ setError(KDUpdater::UpdateOperation::UserDefinedError, tr("These processes should be stopped to "
+ "continue:\n\n%1").arg(QDir::toNativeSeparators(processes.join(QLatin1String("\n")))));
+ }
+ return false;
+ }
+ return true;
+}
+
+bool FakeStopProcessForUpdateOperation::testOperation()
+{
+ return true;
+}
+
+Operation *FakeStopProcessForUpdateOperation::clone() const
+{
+ return new FakeStopProcessForUpdateOperation();
+}
diff --git a/src/libs/installer/fakestopprocessforupdateoperation.h b/src/libs/installer/fakestopprocessforupdateoperation.h
new file mode 100644
index 000000000..ab380aa3f
--- /dev/null
+++ b/src/libs/installer/fakestopprocessforupdateoperation.h
@@ -0,0 +1,54 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef FAKESTOPPROCESSFORUPDATEOPERATION_H
+#define FAKESTOPPROCESSFORUPDATEOPERATION_H
+
+#include "qinstallerglobal.h"
+
+namespace QInstaller {
+
+class FakeStopProcessForUpdateOperation : public Operation
+{
+public:
+ FakeStopProcessForUpdateOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+};
+
+}
+
+#endif // FAKESTOPPROCESSFORUPDATEOPERATION_H
diff --git a/src/libs/installer/fileutils.cpp b/src/libs/installer/fileutils.cpp
new file mode 100644
index 000000000..b07b5b08f
--- /dev/null
+++ b/src/libs/installer/fileutils.cpp
@@ -0,0 +1,507 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#include "fileutils.h"
+
+#include <errors.h>
+
+#include <QtCore/QDateTime>
+#include <QtCore/QDir>
+#include <QtCore/QDirIterator>
+#include <QtCore/QEventLoop>
+#include <QtCore/QTemporaryFile>
+#include <QtCore/QThread>
+#include <QtCore/QUrl>
+
+#include <errno.h>
+
+using namespace QInstaller;
+
+
+// -- TempDirDeleter
+
+TempDirDeleter::TempDirDeleter(const QString &path)
+{
+ m_paths.insert(path);
+}
+
+TempDirDeleter::TempDirDeleter(const QStringList &paths)
+ : m_paths(paths.toSet())
+{
+}
+
+TempDirDeleter::~TempDirDeleter()
+{
+ releaseAndDeleteAll();
+}
+
+QStringList TempDirDeleter::paths() const
+{
+ return m_paths.toList();
+}
+
+void TempDirDeleter::add(const QString &path)
+{
+ m_paths.insert(path);
+}
+
+void TempDirDeleter::add(const QStringList &paths)
+{
+ m_paths += paths.toSet();
+}
+
+void TempDirDeleter::releaseAll()
+{
+ m_paths.clear();
+}
+
+void TempDirDeleter::release(const QString &path)
+{
+ m_paths.remove(path);
+}
+
+void TempDirDeleter::passAndReleaseAll(TempDirDeleter &tdd)
+{
+ tdd.m_paths = m_paths;
+ releaseAll();
+}
+
+void TempDirDeleter::passAndRelease(TempDirDeleter &tdd, const QString &path)
+{
+ tdd.add(path);
+ release(path);
+}
+
+void TempDirDeleter::releaseAndDeleteAll()
+{
+ foreach (const QString &path, m_paths)
+ releaseAndDelete(path);
+}
+
+void TempDirDeleter::releaseAndDelete(const QString &path)
+{
+ if (m_paths.contains(path)) {
+ try {
+ m_paths.remove(path);
+ removeDirectory(path);
+ } catch (const Error &e) {
+ qCritical() << Q_FUNC_INFO << "Exception caught:" << e.message();
+ } catch (...) {
+ qCritical() << Q_FUNC_INFO << "Unknown exception caught.";
+ }
+ }
+}
+
+
+// -- read, write operations
+
+bool QInstaller::isLocalUrl(const QUrl &url)
+{
+ return url.scheme().isEmpty() || url.scheme().toLower() == QLatin1String("file");
+}
+
+QString QInstaller::pathFromUrl(const QUrl &url)
+{
+ if (isLocalUrl(url))
+ return url.toLocalFile();
+ const QString str = url.toString();
+ if (url.scheme() == QLatin1String("resource"))
+ return str.mid(QString::fromLatin1("resource").length());
+ return str;
+}
+
+void QInstaller::openForRead(QIODevice *dev, const QString &name)
+{
+ Q_ASSERT(dev);
+ if (!dev->open(QIODevice::ReadOnly))
+ throw Error(QObject::tr("Cannot open file %1 for reading: %2").arg(name, dev->errorString()));
+}
+
+void QInstaller::openForWrite(QIODevice *dev, const QString &name)
+{
+ Q_ASSERT(dev);
+ if (!dev->open(QIODevice::WriteOnly))
+ throw Error(QObject::tr("Cannot open file %1 for writing: %2").arg(name, dev->errorString()));
+}
+
+void QInstaller::openForAppend(QIODevice *dev, const QString &name)
+{
+ Q_ASSERT(dev);
+ if (!dev->open(QIODevice::ReadWrite | QIODevice::Append))
+ throw Error(QObject::tr("Cannot open file %1 for writing: %2").arg(name, dev->errorString()));
+}
+
+qint64 QInstaller::blockingWrite(QIODevice *out, const char *buffer, qint64 size)
+{
+ qint64 left = size;
+ while (left > 0) {
+ const qint64 n = out->write(buffer, left);
+ if (n < 0) {
+ throw Error(QObject::tr("Write failed after %1 bytes: %2").arg(QString::number(size-left),
+ out->errorString()));
+ }
+ left -= n;
+ }
+ return size;
+}
+
+qint64 QInstaller::blockingWrite(QIODevice *out, const QByteArray &ba)
+{
+ return blockingWrite(out, ba.constData(), ba.size());
+}
+
+qint64 QInstaller::blockingRead(QIODevice *in, char *buffer, qint64 size)
+{
+ if (in->atEnd())
+ return 0;
+ qint64 left = size;
+ while (left > 0) {
+ const qint64 n = in->read(buffer, left);
+ if (n < 0) {
+ throw Error(QObject::tr("Read failed after %1 bytes: %2").arg(QString::number(size-left),
+ in->errorString()));
+ }
+ left -= n;
+ buffer += n;
+ }
+ return size;
+}
+
+void QInstaller::blockingCopy(QIODevice *in, QIODevice *out, qint64 size)
+{
+ static const qint64 blockSize = 4096;
+ QByteArray ba(blockSize, '\0');
+ qint64 actual = qMin(blockSize, size);
+ while (actual > 0) {
+ blockingRead(in, ba.data(), actual);
+ blockingWrite(out, ba.constData(), actual);
+ size -= actual;
+ actual = qMin(blockSize, size);
+ }
+}
+
+void QInstaller::removeFiles(const QString &path, bool ignoreErrors)
+{
+ const QFileInfoList entries = QDir(path).entryInfoList(QDir::AllEntries | QDir::Hidden);
+ foreach (const QFileInfo &fi, entries) {
+ if (fi.isSymLink() || fi.isFile()) {
+ QFile f(fi.filePath());
+ if (!f.remove() && !ignoreErrors)
+ throw Error(QObject::tr("Could not remove file %1: %2").arg(f.fileName(), f.errorString()));
+ }
+ }
+}
+
+void QInstaller::removeDirectory(const QString &path, bool ignoreErrors)
+{
+ if (path.isEmpty()) // QDir("") points to the working directory! We never want to remove that one.
+ return;
+
+ QStringList dirs;
+ QDirIterator it(path, QDir::NoDotAndDotDot | QDir::Dirs | QDir::NoSymLinks | QDir::Hidden,
+ QDirIterator::Subdirectories);
+ while (it.hasNext()) {
+ it.next();
+ dirs.prepend(it.filePath());
+ removeFiles(dirs.at(0), ignoreErrors);
+ }
+
+ QDir d;
+ dirs.append(path);
+ removeFiles(path, ignoreErrors);
+ foreach (const QString &dir, dirs) {
+ errno = 0;
+ if (d.exists(path) && !d.rmdir(dir) && !ignoreErrors)
+ throw Error(QObject::tr("Could not remove folder %1: %2").arg(dir, QLatin1String(strerror(errno))));
+ }
+}
+
+/*!
+ \internal
+ */
+class RemoveDirectoryThread : public QThread
+{
+public:
+ explicit RemoveDirectoryThread(const QString &path, bool ignoreErrors = false, QObject *parent = 0)
+ : QThread(parent),
+ p(path),
+ ignore(ignoreErrors)
+ {
+ }
+
+ const QString &error() const
+ {
+ return err;
+ }
+
+protected:
+ /*!
+ \reimp
+ */
+ void run()
+ {
+ try {
+ removeDirectory(p, ignore);
+ } catch (const Error &e) {
+ err = e.message();
+ }
+ }
+
+private:
+ QString err;
+ const QString p;
+ const bool ignore;
+};
+
+void QInstaller::removeDirectoryThreaded(const QString &path, bool ignoreErrors)
+{
+ RemoveDirectoryThread thread(path, ignoreErrors);
+ QEventLoop loop;
+ QObject::connect(&thread, SIGNAL(finished()), &loop, SLOT(quit()));
+ thread.start();
+ loop.exec();
+ if (!thread.error().isEmpty())
+ throw Error(thread.error());
+}
+
+void QInstaller::removeSystemGeneratedFiles(const QString &path)
+{
+ if (path.isEmpty())
+ return;
+#if defined Q_WS_MAC
+ QFile::remove(path + QLatin1String("/.DS_Store"));
+#elif defined Q_WS_WIN
+ QFile::remove(path + QLatin1String("/Thumbs.db"));
+#endif
+}
+
+void QInstaller::copyDirectoryContents(const QString &sourceDir, const QString &targetDir)
+{
+ qDebug() << "Copying" << sourceDir << "to" << targetDir;
+ Q_ASSERT(QFileInfo(sourceDir).isDir());
+ Q_ASSERT(!QFileInfo(targetDir).exists() || QFileInfo(targetDir).isDir());
+ if (!QDir().mkpath(targetDir))
+ throw Error(QObject::tr("Could not create folder %1").arg(targetDir));
+
+ QDirIterator it(sourceDir, QDir::NoDotAndDotDot | QDir::AllEntries);
+ while (it.hasNext()) {
+ const QFileInfo i(it.next());
+ if (i.isDir()) {
+ copyDirectoryContents(QDir(sourceDir).absoluteFilePath(i.fileName()),
+ QDir(targetDir).absoluteFilePath(i.fileName()));
+ } else {
+ QFile f(i.filePath());
+ const QString target = QDir(targetDir).absoluteFilePath(i.fileName());
+ if (!f.copy(target)) {
+ throw Error(QObject::tr("Could not copy file from %1 to %2: %3").arg(f.fileName(), target,
+ f.errorString()));
+ }
+ }
+ }
+}
+
+void QInstaller::moveDirectoryContents(const QString &sourceDir, const QString &targetDir)
+{
+ qDebug() << "Moving" << sourceDir << "to" << targetDir;
+ Q_ASSERT(QFileInfo(sourceDir).isDir());
+ Q_ASSERT(!QFileInfo(targetDir).exists() || QFileInfo(targetDir).isDir());
+ if (!QDir().mkpath(targetDir))
+ throw Error(QObject::tr("Could not create folder %1").arg(targetDir));
+
+ QDirIterator it(sourceDir, QDir::NoDotAndDotDot | QDir::AllEntries);
+ while (it.hasNext()) {
+ const QFileInfo i(it.next());
+ if (i.isDir()) {
+ moveDirectoryContents(QDir(sourceDir).absoluteFilePath(i.fileName()),
+ QDir(targetDir).absoluteFilePath(i.fileName()));
+ } else {
+ QFile f(i.filePath());
+ const QString target = QDir(targetDir).absoluteFilePath(i.fileName());
+ if (!f.rename(target)) {
+ throw Error(QObject::tr("Could not move file from %1 to %2: %3").arg(f.fileName(), target,
+ f.errorString()));
+ }
+ }
+ }
+}
+
+void QInstaller::mkdir(const QString &path)
+{
+ errno = 0;
+ if (!QDir().mkdir(QFileInfo(path).absoluteFilePath())) {
+ throw Error(QObject::tr("Could not create folder %1: %2").arg(path,
+ QString::fromLocal8Bit(strerror(errno))));
+ }
+}
+
+void QInstaller::mkpath(const QString &path)
+{
+ errno = 0;
+ if (!QDir().mkpath(QFileInfo(path).absoluteFilePath())) {
+ throw Error(QObject::tr("Could not create folder %1: %2").arg(path,
+ QString::fromLocal8Bit(strerror(errno))));
+ }
+}
+
+QString QInstaller::generateTemporaryFileName(const QString &templ)
+{
+ if (templ.isEmpty()) {
+ QTemporaryFile f;
+ if (!f.open())
+ throw Error(QObject::tr("Could not open temporary file: %1").arg(f.errorString()));
+ return f.fileName();
+ }
+
+ static const QString characters = QLatin1String("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890");
+ QString suffix;
+ qsrand(qrand() * QDateTime::currentDateTime().toTime_t());
+ for (int i = 0; i < 5; ++i)
+ suffix += characters[qrand() % characters.length()];
+
+ const QString tmp = QLatin1String("%1.tmp.%2.%3");
+ int count = 1;
+ while (QFile::exists(tmp.arg(templ, suffix).arg(count)))
+ ++count;
+
+ QFile f(tmp.arg(templ, suffix).arg(count));
+ if (!f.open(QIODevice::WriteOnly))
+ throw Error(QObject::tr("Could not open temporary file for template %1: %2").arg(templ, f.errorString()));
+ f.remove();
+ return f.fileName();
+}
+
+QString QInstaller::createTemporaryDirectory(const QString &templ)
+{
+ const QString t = QDir::tempPath() + QLatin1String("/") + templ + QLatin1String("XXXXXX");
+ QTemporaryFile f(t);
+ if (!f.open())
+ throw Error(QObject::tr("Could not create temporary folder for template %1: %2").arg(t, f.errorString()));
+ const QString path = f.fileName() + QLatin1String("meta");
+ qDebug() << "Creating meta data directory at" << path;
+
+ QInstaller::mkpath(path);
+ return path;
+}
+
+#ifdef Q_WS_WIN
+#include <windows.h>
+
+#pragma pack(push)
+#pragma pack(2)
+
+typedef struct {
+ BYTE bWidth; // Width, in pixels, of the image
+ BYTE bHeight; // Height, in pixels, of the image
+ BYTE bColorCount; // Number of colors in image (0 if >=8bpp)
+ BYTE bReserved; // Reserved
+ WORD wPlanes; // Color Planes
+ WORD wBitCount; // Bits per pixel
+ DWORD dwBytesInRes; // how many bytes in this resource?
+ DWORD dwImageOffset; // the ID
+} ICONDIRENTRY;
+
+typedef struct {
+ WORD idReserved; // Reserved (must be 0)
+ WORD idType; // Resource type (1 for icons)
+ WORD idCount; // How many images?
+ ICONDIRENTRY idEntries[1]; // The entries for each image
+} ICONDIR;
+
+typedef struct {
+ BYTE bWidth; // Width, in pixels, of the image
+ BYTE bHeight; // Height, in pixels, of the image
+ BYTE bColorCount; // Number of colors in image (0 if >=8bpp)
+ BYTE bReserved; // Reserved
+ WORD wPlanes; // Color Planes
+ WORD wBitCount; // Bits per pixel
+ DWORD dwBytesInRes; // how many bytes in this resource?
+ WORD nID; // the ID
+} GRPICONDIRENTRY, *LPGRPICONDIRENTRY;
+
+typedef struct {
+ WORD idReserved; // Reserved (must be 0)
+ WORD idType; // Resource type (1 for icons)
+ WORD idCount; // How many images?
+ GRPICONDIRENTRY idEntries[1]; // The entries for each image
+} GRPICONDIR, *LPGRPICONDIR;
+
+
+#pragma pack(pop)
+
+void QInstaller::setApplicationIcon(const QString &application, const QString &icon)
+{
+ wchar_t* const path = new wchar_t[application.length() + 1];
+ QDir::toNativeSeparators(application).toWCharArray(path);
+ path[application.length()] = 0;
+
+ HANDLE updateRes = BeginUpdateResource(path, false);
+ delete[] path;
+
+ QFile iconFile(icon);
+ if (!iconFile.open(QIODevice::ReadOnly))
+ return;
+
+ QByteArray temp = iconFile.readAll();
+
+ ICONDIR* ig = reinterpret_cast< ICONDIR* >(temp.data());
+
+ DWORD newSize = sizeof(GRPICONDIR) + sizeof(GRPICONDIRENTRY) * (ig->idCount - 1);
+ GRPICONDIR* newDir = reinterpret_cast< GRPICONDIR* >(new char[newSize]);
+ newDir->idReserved = ig->idReserved;
+ newDir->idType = ig->idType;
+ newDir->idCount = ig->idCount;
+
+ for (int i = 0; i < ig->idCount; ++i) {
+ char* temp1 = temp.data() + ig->idEntries[i].dwImageOffset;
+ DWORD size1 = ig->idEntries[i].dwBytesInRes;
+
+ newDir->idEntries[i].bWidth = ig->idEntries[i].bWidth;
+ newDir->idEntries[i].bHeight = ig->idEntries[i].bHeight;
+ newDir->idEntries[i].bColorCount = ig->idEntries[i].bColorCount;
+ newDir->idEntries[i].bReserved = ig->idEntries[i].bReserved;
+ newDir->idEntries[i].wPlanes = ig->idEntries[i].wPlanes;
+ newDir->idEntries[i].wBitCount = ig->idEntries[i].wBitCount;
+ newDir->idEntries[i].dwBytesInRes = ig->idEntries[i].dwBytesInRes;
+ newDir->idEntries[i].nID = i + 1;
+
+ UpdateResource(updateRes, RT_ICON, MAKEINTRESOURCE(i + 1),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), temp1, size1);
+ }
+
+ UpdateResource(updateRes, RT_GROUP_ICON, L"IDI_ICON1", MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), newDir
+ , newSize);
+
+ delete [] newDir;
+
+ EndUpdateResource(updateRes, false);
+}
+
+#endif
diff --git a/src/libs/installer/fileutils.h b/src/libs/installer/fileutils.h
new file mode 100644
index 000000000..4e634a210
--- /dev/null
+++ b/src/libs/installer/fileutils.h
@@ -0,0 +1,114 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework**
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).*
+**
+** Contact: Nokia Corporation qt-info@nokia.com**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception version
+** 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you are unsure which license is appropriate for your use, please contact
+** (qt-info@nokia.com).
+**
+**************************************************************************/
+#ifndef QINSTALLER_FILEUTILS_H
+#define QINSTALLER_FILEUTILS_H
+
+#include "installer_global.h"
+
+#include <QtCore/QSet>
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+
+QT_BEGIN_NAMESPACE
+class QByteArray;
+class QIODevice;
+class QUrl;
+QT_END_NAMESPACE
+
+namespace QInstaller {
+class INSTALLER_EXPORT TempDirDeleter
+{
+public:
+ explicit TempDirDeleter(const QString &path);
+ explicit TempDirDeleter(const QStringList &paths = QStringList());
+ ~TempDirDeleter();
+
+ QStringList paths() const;
+
+ void add(const QString &path);
+ void add(const QStringList &paths);
+
+ void releaseAll();
+ void release(const QString &path);
+ void passAndReleaseAll(TempDirDeleter &tdd);
+ void passAndRelease(TempDirDeleter &tdd, const QString &path);
+
+ void releaseAndDeleteAll();
+ void releaseAndDelete(const QString &path);
+
+private:
+ Q_DISABLE_COPY(TempDirDeleter)
+ QSet<QString> m_paths;
+};
+
+ void INSTALLER_EXPORT openForRead(QIODevice *dev, const QString &name);
+ void INSTALLER_EXPORT openForWrite(QIODevice *dev, const QString &name);
+ void INSTALLER_EXPORT openForAppend(QIODevice *dev, const QString &name);
+
+ qint64 INSTALLER_EXPORT blockingRead(QIODevice *in, char *buffer, qint64 size);
+ void INSTALLER_EXPORT blockingCopy(QIODevice *in, QIODevice *out, qint64 size);
+ qint64 INSTALLER_EXPORT blockingWrite(QIODevice *out, const char *buffer, qint64 size);
+ qint64 INSTALLER_EXPORT blockingWrite(QIODevice *out, const QByteArray& ba);
+
+ /*!
+ Removes the directory at \a path recursively.
+ @param path The directory to remove
+ @param ignoreErrors if @p true, errors will be silently ignored. Otherwise an exception will be thrown
+ if removing fails.
+
+ @throws QInstaller::Error if the directory cannot be removed and ignoreErrors is @p false
+ */
+ void INSTALLER_EXPORT removeFiles(const QString &path, bool ignoreErrors = false);
+ void INSTALLER_EXPORT removeDirectory(const QString &path, bool ignoreErrors = false);
+ void INSTALLER_EXPORT removeDirectoryThreaded(const QString &path, bool ignoreErrors = false);
+ void INSTALLER_EXPORT removeSystemGeneratedFiles(const QString &path);
+
+ /*!
+ Creates a temporary directory
+ @throws QInstaller::Error if creating the temporary directory fails
+ */
+ QString INSTALLER_EXPORT createTemporaryDirectory(const QString &templ=QString());
+
+ QString INSTALLER_EXPORT generateTemporaryFileName(const QString &templ=QString());
+
+ void INSTALLER_EXPORT moveDirectoryContents(const QString &sourceDir, const QString &targetDir);
+ void INSTALLER_EXPORT copyDirectoryContents(const QString &sourceDir, const QString &targetDir);
+
+ bool INSTALLER_EXPORT isLocalUrl(const QUrl &url);
+ QString INSTALLER_EXPORT pathFromUrl(const QUrl &url);
+
+ void INSTALLER_EXPORT mkdir(const QString &path);
+ void INSTALLER_EXPORT mkpath(const QString &path);
+
+#ifdef Q_WS_WIN
+ /*!
+ Sets the .ico file at \a icon as application icon for \a application.
+ */
+ void INSTALLER_EXPORT setApplicationIcon(const QString &application, const QString &icon);
+#endif
+}
+
+#endif // QINSTALLER_FILEUTILS_H
diff --git a/src/libs/installer/fsengineclient.cpp b/src/libs/installer/fsengineclient.cpp
new file mode 100644
index 000000000..1858d7408
--- /dev/null
+++ b/src/libs/installer/fsengineclient.cpp
@@ -0,0 +1,818 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "fsengineclient.h"
+
+#include "adminauthorization.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QMutex>
+#include <QtCore/QProcess>
+#include <QtCore/QThread>
+#include <QtCore/QTimer>
+#include <QtCore/QUuid>
+
+#include <QtNetwork/QHostAddress>
+#include <QtNetwork/QTcpSocket>
+
+
+// -- StillAliveThread
+
+/*!
+ This thread convinces the watchdog in the running server that the client has not crashed yet.
+*/
+class StillAliveThread : public QThread
+{
+ Q_OBJECT
+public:
+ void run()
+ {
+ QTimer stillAliveTimer;
+ connect(&stillAliveTimer, SIGNAL(timeout()), this, SLOT(stillAlive()));
+ stillAliveTimer.start(1000);
+ exec();
+ }
+
+public Q_SLOTS:
+ void stillAlive()
+ {
+ if (!FSEngineClientHandler::instance().isServerRunning())
+ return;
+
+ // in case of the server not running, this will simply fail
+ QTcpSocket socket;
+ FSEngineClientHandler::instance().connect(&socket);
+ }
+};
+
+
+// -- FSEngineClient
+
+class FSEngineClient : public QAbstractFileEngine
+{
+public:
+ FSEngineClient();
+ ~FSEngineClient();
+
+ bool atEnd() const;
+ Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames);
+ bool caseSensitive() const;
+ bool close();
+ bool copy(const QString &newName);
+ QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const;
+ QFile::FileError error() const;
+ QString errorString() const;
+ bool extension(Extension extension, const ExtensionOption *option = 0, ExtensionReturn *output = 0);
+ FileFlags fileFlags(FileFlags type = FileInfoAll) const;
+ QString fileName(FileName file = DefaultName) const;
+ bool flush();
+ int handle() const;
+ bool isRelativePath() const;
+ bool isSequential() const;
+ bool link(const QString &newName);
+ bool mkdir(const QString &dirName, bool createParentDirectories) const;
+ bool open(QIODevice::OpenMode mode);
+ QString owner(FileOwner owner) const;
+ uint ownerId(FileOwner owner) const;
+ qint64 pos() const;
+ qint64 read(char *data, qint64 maxlen);
+ qint64 readLine(char *data, qint64 maxlen);
+ bool remove();
+ bool rename(const QString &newName);
+ bool rmdir(const QString &dirName, bool recurseParentDirectories) const;
+ bool seek(qint64 offset);
+ void setFileName(const QString &fileName);
+ bool setPermissions(uint perms);
+ bool setSize(qint64 size);
+ qint64 size() const;
+ bool supportsExtension(Extension extension) const;
+ qint64 write(const char *data, qint64 len);
+
+private:
+ template<typename T> T returnWithType() const;
+ template<typename T> T returnWithCastedType() const;
+
+private:
+ friend class FSEngineClientHandler;
+
+ mutable QTcpSocket *socket;
+ mutable QDataStream stream;
+};
+
+template<typename T> T FSEngineClient::returnWithType() const
+{
+ socket->flush();
+ if (!socket->bytesAvailable())
+ socket->waitForReadyRead();
+ quint32 test;
+ stream >> test;
+
+ T result;
+ stream >> result;
+ return result;
+}
+
+template<typename T> T FSEngineClient::returnWithCastedType() const
+{
+ socket->flush();
+ if (!socket->bytesAvailable())
+ socket->waitForReadyRead();
+ quint32 test;
+ stream >> test;
+
+ int result;
+ stream >> result;
+ return static_cast<T>(result);
+}
+
+/*!
+ \internal
+*/
+class FSEngineClientIterator : public QAbstractFileEngineIterator
+{
+public:
+ FSEngineClientIterator(QDir::Filters filters, const QStringList &nameFilters, const QStringList &files)
+ : QAbstractFileEngineIterator(filters, nameFilters),
+ entries(files),
+ index(-1)
+ {
+ }
+
+ /*!
+ \reimp
+ */
+ bool hasNext() const
+ {
+ return index < entries.size() - 1;
+ }
+
+ /*!
+ \reimp
+ */
+ QString next()
+ {
+ if (!hasNext())
+ return QString();
+ ++index;
+ return currentFilePath();
+ }
+
+ /*!
+ \reimp
+ */
+ QString currentFileName() const
+ {
+ return entries.at(index);
+ }
+
+private:
+ const QStringList entries;
+ int index;
+};
+
+FSEngineClient::FSEngineClient()
+ : socket(new QTcpSocket)
+{
+ FSEngineClientHandler::instance().connect(socket);
+ stream.setDevice(socket);
+ stream.setVersion(QDataStream::Qt_4_2);
+}
+
+FSEngineClient::~FSEngineClient()
+{
+ if (QThread::currentThread() == socket->thread()) {
+ socket->close();
+ delete socket;
+ } else {
+ socket->deleteLater();
+ }
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::atEnd() const
+{
+ stream << QString::fromLatin1("QFSFileEngine::atEnd");
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+QAbstractFileEngine::Iterator* FSEngineClient::beginEntryList(QDir::Filters filters,
+ const QStringList &filterNames)
+{
+ QStringList entries = entryList(filters, filterNames);
+ entries.removeAll(QString());
+ return new FSEngineClientIterator(filters, filterNames, entries);
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::caseSensitive() const
+{
+ stream << QString::fromLatin1("QFSFileEngine::caseSensitive");
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::close()
+{
+ stream << QString::fromLatin1("QFSFileEngine::close");
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::copy(const QString &newName)
+{
+ stream << QString::fromLatin1("QFSFileEngine::copy");
+ stream << newName;
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+QStringList FSEngineClient::entryList(QDir::Filters filters, const QStringList &filterNames) const
+{
+ stream << QString::fromLatin1("QFSFileEngine::entryList");
+ stream << static_cast<int>(filters);
+ stream << filterNames;
+ return returnWithType<QStringList>();
+}
+
+/*!
+ \reimp
+*/
+QFile::FileError FSEngineClient::error() const
+{
+ stream << QString::fromLatin1("QFSFileEngine::error");
+ return returnWithCastedType<QFile::FileError>();
+}
+
+/*!
+ \reimp
+*/
+QString FSEngineClient::errorString() const
+{
+ stream << QString::fromLatin1("QFSFileEngine::errorString");
+ return returnWithType<QString>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output)
+{
+ Q_UNUSED(extension)
+ Q_UNUSED(option)
+ Q_UNUSED(output)
+ return false;
+}
+
+/*!
+ \reimp
+*/
+QAbstractFileEngine::FileFlags FSEngineClient::fileFlags(FileFlags type) const
+{
+ stream << QString::fromLatin1("QFSFileEngine::fileFlags");
+ stream << static_cast<int>(type);
+ return returnWithCastedType<QAbstractFileEngine::FileFlags>();
+}
+
+/*!
+ \reimp
+*/
+QString FSEngineClient::fileName(FileName file) const
+{
+ stream << QString::fromLatin1("QFSFileEngine::fileName");
+ stream << static_cast<int>(file);
+ return returnWithType<QString>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::flush()
+{
+ stream << QString::fromLatin1("QFSFileEngine::flush");
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+int FSEngineClient::handle() const
+{
+ stream << QString::fromLatin1("QFSFileEngine::handle");
+ return returnWithType<int>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::isRelativePath() const
+{
+ stream << QString::fromLatin1("QFSFileEngine::isRelativePath");
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::isSequential() const
+{
+ stream << QString::fromLatin1("QFSFileEngine::isSequential");
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::link(const QString &newName)
+{
+ stream << QString::fromLatin1("QFSFileEngine::link");
+ stream << newName;
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::mkdir(const QString &dirName, bool createParentDirectories) const
+{
+ stream << QString::fromLatin1("QFSFileEngine::mkdir");
+ stream << dirName;
+ stream << createParentDirectories;
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::open(QIODevice::OpenMode mode)
+{
+ stream << QString::fromLatin1("QFSFileEngine::open");
+ stream << static_cast<int>(mode);
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+QString FSEngineClient::owner(FileOwner owner) const
+{
+ stream << QString::fromLatin1("QFSFileEngine::owner");
+ stream << static_cast<int>(owner);
+ return returnWithType<QString>();
+}
+
+/*!
+ \reimp
+*/
+uint FSEngineClient::ownerId(FileOwner owner) const
+{
+ stream << QString::fromLatin1("QFSFileEngine::ownerId");
+ stream << static_cast<int>(owner);
+ return returnWithType<uint>();
+}
+
+/*!
+ \reimp
+*/
+qint64 FSEngineClient::pos() const
+{
+ stream << QString::fromLatin1("QFSFileEngine::pos");
+ return returnWithType<qint64>();
+}
+
+/*!
+ \reimp
+*/
+qint64 FSEngineClient::read(char *data, qint64 maxlen)
+{
+ stream << QString::fromLatin1("QFSFileEngine::read");
+ stream << maxlen;
+ socket->flush();
+ if (!socket->bytesAvailable())
+ socket->waitForReadyRead();
+ quint32 size;
+ stream >> size;
+ qint64 result;
+ stream >> result;
+ qint64 read = 0;
+ while (read < result) {
+ if (!socket->bytesAvailable())
+ socket->waitForReadyRead();
+ read += socket->read(data + read, result - read);
+ }
+ return result;
+}
+
+/*!
+ \reimp
+*/
+qint64 FSEngineClient::readLine(char *data, qint64 maxlen)
+{
+ stream << QString::fromLatin1("QFSFileEngine::readLine");
+ stream << maxlen;
+ socket->flush();
+ if (!socket->bytesAvailable())
+ socket->waitForReadyRead();
+ quint32 size;
+ stream >> size;
+ qint64 result;
+ stream >> result;
+ qint64 read = 0;
+ while (read < result) {
+ if (!socket->bytesAvailable())
+ socket->waitForReadyRead();
+ read += socket->read(data + read, result - read);
+ }
+ return result;
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::remove()
+{
+ stream << QString::fromLatin1("QFSFileEngine::remove");
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::rename(const QString &newName)
+{
+ stream << QString::fromLatin1("QFSFileEngine::rename");
+ stream << newName;
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::rmdir(const QString &dirName, bool recurseParentDirectories) const
+{
+ stream << QString::fromLatin1("QFSFileEngine::rmdir");
+ stream << dirName;
+ stream << recurseParentDirectories;
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::seek(qint64 offset)
+{
+ stream << QString::fromLatin1("QFSFileEngine::seek");
+ stream << offset;
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+void FSEngineClient::setFileName(const QString &fileName)
+{
+ stream << QString::fromLatin1("QFSFileEngine::setFileName");
+ stream << fileName;
+
+ socket->flush();
+ if (!socket->bytesAvailable())
+ socket->waitForReadyRead();
+ quint32 test;
+ stream >> test;
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::setPermissions(uint perms)
+{
+ stream << QString::fromLatin1("QFSFileEngine::setPermissions");
+ stream << perms;
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::setSize(qint64 size)
+{
+ stream << QString::fromLatin1("QFSFileEngine::setSize");
+ stream << size;
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+qint64 FSEngineClient::size() const
+{
+ stream << QString::fromLatin1("QFSFileEngine::size");
+ return returnWithType<qint64>();
+}
+
+/*!
+ \reimp
+*/
+bool FSEngineClient::supportsExtension(Extension extension) const
+{
+ stream << QString::fromLatin1("QFSFileEngine::supportsExtension");
+ stream << static_cast<int>(extension);
+ return returnWithType<bool>();
+}
+
+/*!
+ \reimp
+*/
+qint64 FSEngineClient::write(const char *data, qint64 len)
+{
+ stream << QString::fromLatin1("QFSFileEngine::write");
+ stream << len;
+ qint64 written = 0;
+ while (written < len) {
+ written += socket->write(data, len - written);
+ socket->waitForBytesWritten();
+ }
+ return returnWithType<qint64>();
+}
+
+class FSEngineClientHandler::Private
+{
+public:
+ Private()
+ : mutex(QMutex::Recursive),
+ port(0),
+ startServerAsAdmin(false),
+ serverStarted(false),
+ serverStarting(false),
+ active(false),
+ thread(new StillAliveThread)
+ {
+ thread->moveToThread(thread);
+ }
+
+ void maybeStartServer();
+ void maybeStopServer();
+
+ QMutex mutex;
+ QHostAddress address;
+ quint16 port;
+ QString socket;
+ bool startServerAsAdmin;
+ bool serverStarted;
+ bool serverStarting;
+ bool active;
+ QString serverCommand;
+ QStringList serverArguments;
+ QString key;
+
+ StillAliveThread *const thread;
+};
+
+/*!
+ Creates a new FSEngineClientHandler with no connection.
+*/
+FSEngineClientHandler::FSEngineClientHandler()
+ : d(new Private)
+{
+ //don't do this in the Private ctor as createUuid() accesses QFileEngine, which accesses this
+ // half-constructed handler -> Crash (KDNDK-248)
+ d->key = QUuid::createUuid().toString();
+}
+
+void FSEngineClientHandler::enableTestMode()
+{
+ d->key = QLatin1String("testAuthorizationKey");
+ d->serverStarted = true;
+}
+
+void FSEngineClientHandler::init(quint16 port, const QHostAddress &a)
+{
+ d->address = a;
+ d->port = port;
+ d->thread->start();
+}
+
+bool FSEngineClientHandler::connect(QTcpSocket *socket)
+{
+ int tries = 3;
+ while (tries > 0) {
+ socket->connectToHost(d->address, d->port);
+ if (!socket->waitForConnected(10000)) {
+ if (static_cast<QAbstractSocket::SocketError>(socket->error()) != QAbstractSocket::UnknownSocketError)
+ --tries;
+ qApp->processEvents();
+ continue;
+ }
+
+ QDataStream stream(socket);
+ stream << QString::fromLatin1("authorize");
+ stream << d->key;
+ socket->flush();
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Destroys the FSEngineClientHandler. If the handler started a server instance, it gets shut down.
+*/
+FSEngineClientHandler::~FSEngineClientHandler()
+{
+ QMetaObject::invokeMethod(d->thread, "quit");
+ //d->maybeStopServer();
+ delete d;
+}
+
+/*!
+ Returns a previously created FSEngineClientHandler instance.
+*/
+FSEngineClientHandler &FSEngineClientHandler::instance()
+{
+ static FSEngineClientHandler instance;
+ return instance;
+}
+
+/*!
+ Returns a created authorization key which is sent to the server when connecting via the "authorize"
+ command after the server was started.
+*/
+QString FSEngineClientHandler::authorizationKey() const
+{
+ return d->key;
+}
+
+/*!
+ Sets \a command as the command to be executed to startup the server. If \a startAsAdmin is set,
+ it is executed with admin privilegies.
+*/
+void FSEngineClientHandler::setStartServerCommand(const QString &command, bool startAsAdmin)
+{
+ setStartServerCommand(command, QStringList(), startAsAdmin);
+}
+
+/*!
+ Sets \a command as the command to be executed to startup the server. If \a startAsAdmin is set, it is
+ executed with admin privilegies. A list of \a arguments is passed to the process.
+*/
+void FSEngineClientHandler::setStartServerCommand(const QString &command, const QStringList &arguments,
+ bool startAsAdmin)
+{
+ d->maybeStopServer();
+
+ d->startServerAsAdmin = startAsAdmin;
+ d->serverCommand = command;
+ d->serverArguments = arguments;
+}
+
+/*!
+ \reimp
+*/
+QAbstractFileEngine* FSEngineClientHandler::create(const QString &fileName) const
+{
+ if (d->serverStarting || !d->active)
+ return 0;
+
+ d->maybeStartServer();
+
+ static QRegExp re(QLatin1String("^[a-z0-9]*://.*$"));
+ if (re.exactMatch(fileName)) // stuff like installer:// 7z:// and so on
+ return 0;
+
+ if (fileName.isEmpty() || fileName.startsWith(QLatin1String(":")))
+ return 0; // empty filename or Qt resource
+
+ FSEngineClient *const client = new FSEngineClient;
+ // authorize
+ client->stream << QString::fromLatin1("authorize");
+ client->stream << d->key;
+ client->socket->flush();
+
+ client->setFileName(fileName);
+ return client;
+}
+
+/*!
+ Sets the FSEngineClientHandler to \a active. I.e. to actually return FSEngineClients if asked for.
+*/
+void FSEngineClientHandler::setActive(bool active)
+{
+ d->active = active;
+ if (active) {
+ d->maybeStartServer();
+ d->active = d->serverStarted;
+ }
+}
+
+/*!
+ Returns, wheter this FSEngineClientHandler is active or not.
+*/
+bool FSEngineClientHandler::isActive() const
+{
+ return d->active;
+}
+
+/*!
+ Returns true, when the server already has been started.
+*/
+bool FSEngineClientHandler::isServerRunning() const
+{
+ return d->serverStarted;
+}
+
+/*!
+ \internal
+ Starts the server if a command was set and it isn't already started.
+*/
+void FSEngineClientHandler::Private::maybeStartServer()
+{
+ if (serverStarted || serverCommand.isEmpty())
+ return;
+
+ const QMutexLocker ml(&mutex);
+ if (serverStarted)
+ return;
+
+ serverStarting = true;
+
+ if (startServerAsAdmin) {
+ AdminAuthorization auth;
+ serverStarted = auth.authorize() && auth.execute(0, serverCommand, serverArguments);
+ } else {
+ serverStarted = QProcess::startDetached(serverCommand, serverArguments);
+ }
+
+ // now wait for the socket to arrive
+ QTcpSocket s;
+ while (serverStarting && serverStarted) {
+ if (FSEngineClientHandler::instance().connect(&s))
+ serverStarting = false;
+ }
+ serverStarting = false;
+}
+
+/*!
+ Stops the server if it was started before.
+*/
+void FSEngineClientHandler::Private::maybeStopServer()
+{
+ if (!serverStarted)
+ return;
+
+ const QMutexLocker ml(&mutex);
+ if (!serverStarted)
+ return;
+
+ QTcpSocket s;
+ if (FSEngineClientHandler::instance().connect(&s)) {
+ QDataStream stream(&s);
+ stream.setVersion(QDataStream::Qt_4_2);
+ stream << QString::fromLatin1("authorize");
+ stream << key;
+ stream << QString::fromLatin1("shutdown");
+ s.flush();
+ }
+ serverStarted = false;
+}
+
+#include "fsengineclient.moc"
diff --git a/src/libs/installer/fsengineclient.h b/src/libs/installer/fsengineclient.h
new file mode 100644
index 000000000..b974234ec
--- /dev/null
+++ b/src/libs/installer/fsengineclient.h
@@ -0,0 +1,75 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef FSENGINECLIENT_H
+#define FSENGINECLIENT_H
+
+#include "installer_global.h"
+
+#include <QtCore/QAbstractFileEngineHandler>
+
+#include <QtNetwork/QHostAddress>
+
+QT_BEGIN_NAMESPACE
+class QTcpSocket;
+QT_END_NAMESPACE
+
+class INSTALLER_EXPORT FSEngineClientHandler : public QAbstractFileEngineHandler
+{
+public:
+ static FSEngineClientHandler& instance();
+
+ QAbstractFileEngine* create(const QString &fileName) const;
+ void init(quint16 port, const QHostAddress &a = QHostAddress::LocalHost);
+
+ bool connect(QTcpSocket *socket);
+
+ bool isActive() const;
+ void setActive(bool active);
+
+ void enableTestMode();
+ bool isServerRunning() const;
+ QString authorizationKey() const;
+
+ void setStartServerCommand(const QString &command, bool startAsAdmin = false);
+ void setStartServerCommand(const QString &command, const QStringList &arguments, bool startAsAdmin = false);
+
+protected:
+ FSEngineClientHandler();
+ ~FSEngineClientHandler();
+
+private:
+ class Private;
+ Private *d;
+};
+
+#endif
diff --git a/src/libs/installer/fsengineserver.cpp b/src/libs/installer/fsengineserver.cpp
new file mode 100644
index 000000000..19539334c
--- /dev/null
+++ b/src/libs/installer/fsengineserver.cpp
@@ -0,0 +1,595 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#include "fsengineserver.h"
+
+#include "utils.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QFSFileEngine>
+#include <QtCore/QProcess>
+#include <QtCore/QSettings>
+#include <QtCore/QStringList>
+#include <QtCore/QThread>
+
+#include <QtNetwork/QTcpSocket>
+
+typedef int descriptor_t;
+
+#ifdef Q_WS_WIN
+# include <windows.h>
+#endif
+
+bool startDetached(const QString &program, const QStringList &args, const QString &workingDirectory,
+ qint64 *pid)
+{
+#ifdef Q_WS_WIN
+ 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
+ };
+
+ const QString arguments = QInstaller::createCommandline(program, args);
+ const bool success = CreateProcess(0, const_cast<wchar_t *>(static_cast<const wchar_t *>(arguments.utf16())),
+ 0, 0, FALSE, CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE,
+ 0, (wchar_t*)workingDirectory.utf16(),
+ &startupInfo, &pinfo);
+
+ if (success) {
+ CloseHandle(pinfo.hThread);
+ CloseHandle(pinfo.hProcess);
+ if (pid)
+ *pid = pinfo.dwProcessId;
+ }
+
+ return success;
+#else
+ return QProcess::startDetached(program, args, workingDirectory, pid);
+#endif
+}
+
+
+class QProcessSignalReceiver : public QObject
+{
+ Q_OBJECT
+
+public:
+ QProcessSignalReceiver(QObject *parent = 0)
+ : QObject(parent)
+ {
+ connect(parent, SIGNAL(finished(int, QProcess::ExitStatus)), this,
+ SLOT(processFinished(int, QProcess::ExitStatus)));
+ connect(parent, SIGNAL(error(QProcess::ProcessError)), this,
+ SLOT(processError(QProcess::ProcessError)));
+ connect(parent, SIGNAL(readyRead()), this, SLOT(processReadyRead()));
+ connect(parent, SIGNAL(started()), this, SLOT(processStarted()));
+ connect(parent, SIGNAL(stateChanged(QProcess::ProcessState)), this,
+ SLOT(processStateChanged(QProcess::ProcessState)));
+ }
+
+ QList<QVariant> receivedSignals;
+
+private Q_SLOTS:
+ void processError(QProcess::ProcessError error);
+ void processFinished(int exitCode, QProcess::ExitStatus exitStatus);
+ void processReadyRead();
+ void processStarted();
+ void processStateChanged(QProcess::ProcessState newState);
+};
+
+/*!
+ \internal
+*/
+class FSEngineConnectionThread : public QThread
+{
+ Q_OBJECT
+public:
+ FSEngineConnectionThread(descriptor_t socketDescriptor, QObject *parent)
+ : QThread(parent),
+ descriptor(socketDescriptor),
+ settings(0),
+ process(0),
+ signalReceiver(0)
+ {}
+
+protected:
+ void run();
+
+private:
+ QByteArray handleCommand(const QString &command);
+
+ QFSFileEngine engine;
+ const descriptor_t descriptor;
+ QDataStream receivedStream;
+ QSettings *settings;
+
+ QProcess *process;
+ QProcessSignalReceiver *signalReceiver;
+};
+
+
+FSEngineServer::FSEngineServer(quint16 port, QObject *parent)
+ : QTcpServer(parent)
+{
+ listen(QHostAddress::LocalHost, port);
+ connect(&watchdog, SIGNAL(timeout()), qApp, SLOT(quit()));
+ watchdog.setSingleShot(true);
+ watchdog.setInterval(30000);
+ watchdog.start();
+}
+
+FSEngineServer::FSEngineServer(const QHostAddress &address, quint16 port, QObject *parent)
+ : QTcpServer(parent)
+{
+ listen(address, port);
+ connect(&watchdog, SIGNAL(timeout()), qApp, SLOT(quit()));
+ watchdog.setSingleShot(true);
+ watchdog.setInterval(30000);
+ watchdog.start();
+}
+
+/*!
+ Destroys the FSEngineServer.
+*/
+FSEngineServer::~FSEngineServer()
+{
+ const QList<QThread *> threads = findChildren<QThread *>();
+ foreach (QThread *thread, threads)
+ thread->wait();
+}
+
+/*!
+ \reimp
+*/
+void FSEngineServer::incomingConnection(int socketDescriptor)
+{
+ qApp->processEvents();
+ QThread *const thread = new FSEngineConnectionThread(socketDescriptor, this);
+ connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
+ thread->start();
+ watchdog.start();
+}
+
+void FSEngineServer::enableTestMode()
+{
+ setAuthorizationKey(QLatin1String("testAuthorizationKey"));
+ //we don't want to kill the server,
+ //maybe we should introduce a call where the client can kill the server
+ watchdog.disconnect();
+}
+
+
+/*!
+ Sets the authorization key this server is asking the clients for to \a authorizationKey.
+*/
+void FSEngineServer::setAuthorizationKey(const QString &authorizationKey)
+{
+ key = authorizationKey;
+}
+
+QString FSEngineServer::authorizationKey() const
+{
+ return key;
+}
+
+void QProcessSignalReceiver::processError(QProcess::ProcessError error)
+{
+ receivedSignals.push_back(QLatin1String("error"));
+ receivedSignals.push_back(static_cast<int> (error));
+}
+
+void QProcessSignalReceiver::processFinished(int exitCode, QProcess::ExitStatus exitStatus)
+{
+ receivedSignals.push_back(QLatin1String("finished"));
+ receivedSignals.push_back(exitCode);
+ receivedSignals.push_back(static_cast<int> (exitStatus));
+}
+
+void QProcessSignalReceiver::processStarted()
+{
+ receivedSignals.push_back(QLatin1String("started"));
+}
+
+void QProcessSignalReceiver::processReadyRead()
+{
+ receivedSignals.push_back(QLatin1String("readyRead"));
+}
+
+void QProcessSignalReceiver::processStateChanged(QProcess::ProcessState newState)
+{
+ receivedSignals.push_back(QLatin1String("stateChanged"));
+ receivedSignals.push_back(static_cast<int>(newState));
+}
+
+/*!
+ \reimp
+*/
+void FSEngineConnectionThread::run()
+{
+ QTcpSocket socket;
+ socket.setSocketDescriptor(descriptor);
+
+ receivedStream.setDevice(&socket);
+ receivedStream.setVersion(QDataStream::Qt_4_2);
+
+ bool authorized = false;
+
+ while (static_cast<QAbstractSocket::SocketState>(socket.state()) == QAbstractSocket::ConnectedState) {
+ if (!socket.bytesAvailable() && !socket.waitForReadyRead(250))
+ continue;
+
+ QString command;
+ receivedStream >> command;
+
+ if (authorized && command == QLatin1String("shutdown")) {
+ // this is a graceful shutdown
+ socket.close();
+ parent()->deleteLater();
+ return;
+ } else if (command == QLatin1String("authorize")) {
+ QString k;
+ receivedStream >> k;
+ if (k != dynamic_cast<FSEngineServer*> (parent())->authorizationKey()) {
+ // this is closing the connection... auth failed
+ socket.close();
+ return;
+ }
+ authorized = true;
+ } else if (authorized) {
+ if (command.isEmpty())
+ continue;
+ const QByteArray result = handleCommand(command);
+ receivedStream << static_cast<quint32> (result.size());
+ if (!result.isEmpty())
+ receivedStream.writeRawData(result.data(), result.size());
+ } else {
+ // authorization failed, connection not wanted
+ socket.close();
+ return;
+ }
+ }
+}
+
+static QDataStream &operator<<(QDataStream &stream, const QSettings::Status &status)
+{
+ return stream << static_cast<int>(status);
+}
+
+/*!
+ Handles \a command and returns a QByteArray which has the result streamed into it.
+*/
+QByteArray FSEngineConnectionThread::handleCommand(const QString &command)
+{
+ QByteArray block;
+ QDataStream returnStream(&block, QIODevice::WriteOnly);
+ returnStream.setVersion(QDataStream::Qt_4_2);
+
+ // first, QSettings handling
+ if (command == QLatin1String("createQSettings")) {
+ QString fileName;
+ receivedStream >> fileName;
+ settings = new QSettings(fileName, QSettings::NativeFormat);
+ } else if (command == QLatin1String("destroyQSettings")) {
+ delete settings;
+ settings = 0;
+ } else if (command == QLatin1String("QSettings::allKeys")) {
+ returnStream << settings->allKeys();
+ } else if (command == QLatin1String("QSettings::beginGroup")) {
+ QString prefix;
+ receivedStream >> prefix;
+ settings->beginGroup(prefix);
+ } else if (command == QLatin1String("QSettings::beginReadArray")) {
+ QString prefix;
+ int size;
+ receivedStream >> prefix;
+ receivedStream >> size;
+ settings->beginWriteArray(prefix, size);
+ } else if (command == QLatin1String("QSettings::beginWriteArray")) {
+ QString prefix;
+ receivedStream >> prefix;
+ returnStream << settings->beginReadArray(prefix);
+ } else if (command == QLatin1String("QSettings::childGroups")) {
+ returnStream << settings->childGroups();
+ } else if (command == QLatin1String("QSettings::childKeys")) {
+ returnStream << settings->childKeys();
+ } else if (command == QLatin1String("QSettings::clear")) {
+ settings->clear();
+ } else if (command == QLatin1String("QSettings::contains")) {
+ QString key;
+ receivedStream >> key;
+ returnStream << settings->contains(key);
+ } else if (command == QLatin1String("QSettings::endArray")) {
+ settings->endArray();
+ } else if (command == QLatin1String("QSettings::endGroup")) {
+ settings->endGroup();
+ } else if (command == QLatin1String("QSettings::fallbacksEnabled")) {
+ returnStream << settings->fallbacksEnabled();
+ } else if (command == QLatin1String("QSettings::fileName")) {
+ returnStream << settings->fileName();
+ } else if (command == QLatin1String("QSettings::group")) {
+ returnStream << settings->group();
+ } else if (command == QLatin1String("QSettings::isWritable")) {
+ returnStream << settings->isWritable();
+ } else if (command == QLatin1String("QSettings::remove")) {
+ QString key;
+ receivedStream >> key;
+ settings->remove(key);
+ } else if (command == QLatin1String("QSettings::setArrayIndex")) {
+ int i;
+ receivedStream >> i;
+ settings->setArrayIndex(i);
+ } else if (command == QLatin1String("QSettings::setFallbacksEnabled")) {
+ bool b;
+ receivedStream >> b;
+ settings->setFallbacksEnabled(b);
+ } else if (command == QLatin1String("QSettings::status")) {
+ returnStream << settings->status();
+ } else if (command == QLatin1String("QSettings::sync")) {
+ settings->sync();
+ } else if (command == QLatin1String("QSettings::setValue")) {
+ QString key;
+ QVariant value;
+ receivedStream >> key;
+ receivedStream >> value;
+ settings->setValue(key, value);
+ } else if (command == QLatin1String("QSettings::value")) {
+ QString key;
+ QVariant defaultValue;
+ receivedStream >> key;
+ receivedStream >> defaultValue;
+ returnStream << settings->value(key, defaultValue);
+ }
+
+ // from here, QProcess handling
+ else if (command == QLatin1String("createQProcess")) {
+ process = new QProcess;
+ signalReceiver = new QProcessSignalReceiver(process);
+ } else if (command == QLatin1String("destroyQProcess")) {
+ signalReceiver->receivedSignals.clear();
+ process->deleteLater();
+ process = 0;
+ } else if (command == QLatin1String("getQProcessSignals")) {
+ returnStream << signalReceiver->receivedSignals;
+ signalReceiver->receivedSignals.clear();
+ qApp->processEvents();
+ } else if (command == QLatin1String("QProcess::closeWriteChannel")) {
+ process->closeWriteChannel();
+ } else if (command == QLatin1String("QProcess::exitCode")) {
+ returnStream << process->exitCode();
+ } else if (command == QLatin1String("QProcess::exitStatus")) {
+ returnStream << static_cast<int> (process->exitStatus());
+ } else if (command == QLatin1String("QProcess::kill")) {
+ process->kill();
+ } else if (command == QLatin1String("QProcess::readAll")) {
+ returnStream << process->readAll();
+ } else if (command == QLatin1String("QProcess::readAllStandardOutput")) {
+ returnStream << process->readAllStandardOutput();
+ } else if (command == QLatin1String("QProcess::startDetached")) {
+ QString program;
+ QStringList arguments;
+ QString workingDirectory;
+ receivedStream >> program;
+ receivedStream >> arguments;
+ receivedStream >> workingDirectory;
+ qint64 pid;
+ const bool result = startDetached(program, arguments, workingDirectory, &pid);
+ returnStream << qMakePair< bool, qint64> (result, pid);
+ } else if (command == QLatin1String("QProcess::setWorkingDirectory")) {
+ QString dir;
+ receivedStream >> dir;
+ process->setWorkingDirectory(dir);
+ } else if (command == QLatin1String("QProcess::setEnvironment")) {
+ QStringList env;
+ receivedStream >> env;
+ process->setEnvironment(env);
+ } else if (command == QLatin1String("QProcess::start")) {
+ QString program;
+ QStringList arguments;
+ int mode;
+ receivedStream >> program;
+ receivedStream >> arguments;
+ receivedStream >> mode;
+ process->start(program, arguments, static_cast<QIODevice::OpenMode> (mode));
+ } else if (command == QLatin1String("QProcess::state")) {
+ returnStream << static_cast<int> (process->state());
+ } else if (command == QLatin1String("QProcess::terminate")) {
+ process->terminate();
+ } else if (command == QLatin1String("QProcess::waitForFinished")) {
+ int msecs;
+ receivedStream >> msecs;
+ returnStream << process->waitForFinished(msecs);
+ } else if (command == QLatin1String("QProcess::waitForStarted")) {
+ int msecs;
+ receivedStream >> msecs;
+ returnStream << process->waitForStarted(msecs);
+ } else if (command == QLatin1String("QProcess::workingDirectory")) {
+ returnStream << process->workingDirectory();
+ } else if (command == QLatin1String("QProcess::write")) {
+ QByteArray byteArray;
+ receivedStream >> byteArray;
+ returnStream << process->write(byteArray);
+ } else if (command == QLatin1String("QProcess::readChannel")) {
+ returnStream << static_cast<int> (process->readChannel());
+ } else if (command == QLatin1String("QProcess::setReadChannel")) {
+ int processChannel;
+ receivedStream >> processChannel;
+ process->setReadChannel(static_cast<QProcess::ProcessChannel>(processChannel));
+ } else if (command == QLatin1String("QProcess::write")) {
+ QByteArray byteArray;
+ receivedStream >> byteArray;
+ returnStream << process->write(byteArray);
+ }
+
+ // from here, QFSEngine handling
+ else if (command == QLatin1String("QFSFileEngine::atEnd")) {
+ returnStream << engine.atEnd();
+ } else if (command == QLatin1String("QFSFileEngine::caseSensitive")) {
+ returnStream << engine.caseSensitive();
+ } else if (command == QLatin1String("QFSFileEngine::close")) {
+ returnStream << engine.close();
+ } else if (command == QLatin1String("QFSFileEngine::copy")) {
+ QString newName;
+ receivedStream >> newName;
+ returnStream << engine.copy(newName);
+ } else if (command == QLatin1String("QFSFileEngine::entryList")) {
+ int filters;
+ QStringList filterNames;
+ receivedStream >> filters;
+ receivedStream >> filterNames;
+ returnStream << engine.entryList(static_cast<QDir::Filters> (filters), filterNames);
+ } else if (command == QLatin1String("QFSFileEngine::error")) {
+ returnStream << static_cast<int> (engine.error());
+ } else if (command == QLatin1String("QFSFileEngine::errorString")) {
+ returnStream << engine.errorString();
+ }
+ // extension
+ else if (command == QLatin1String("QFSFileEngine::fileFlags")) {
+ int flags;
+ receivedStream >> flags;
+ returnStream << static_cast<int>(engine.fileFlags(static_cast<QAbstractFileEngine::FileFlags>(flags)));
+ } else if (command == QLatin1String("QFSFileEngine::fileName")) {
+ int file;
+ receivedStream >> file;
+ returnStream << engine.fileName(static_cast<QAbstractFileEngine::FileName> (file));
+ } else if (command == QLatin1String("QFSFileEngine::flush")) {
+ returnStream << engine.flush();
+ } else if (command == QLatin1String("QFSFileEngine::handle")) {
+ returnStream << engine.handle();
+ } else if (command == QLatin1String("QFSFileEngine::isRelativePath")) {
+ returnStream << engine.isRelativePath();
+ } else if (command == QLatin1String("QFSFileEngine::isSequential")) {
+ returnStream << engine.isSequential();
+ } else if (command == QLatin1String("QFSFileEngine::link")) {
+ QString newName;
+ receivedStream >> newName;
+ returnStream << engine.link(newName);
+ } else if (command == QLatin1String("QFSFileEngine::mkdir")) {
+ QString dirName;
+ bool createParentDirectories;
+ receivedStream >> dirName;
+ receivedStream >> createParentDirectories;
+ returnStream << engine.mkdir(dirName, createParentDirectories);
+ } else if (command == QLatin1String("QFSFileEngine::open")) {
+ int openMode;
+ receivedStream >> openMode;
+ returnStream << engine.open(static_cast<QIODevice::OpenMode> (openMode));
+ } else if (command == QLatin1String("QFSFileEngine::owner")) {
+ int owner;
+ receivedStream >> owner;
+ returnStream << engine.owner(static_cast<QAbstractFileEngine::FileOwner> (owner));
+ } else if (command == QLatin1String("QFSFileEngine::ownerId")) {
+ int owner;
+ receivedStream >> owner;
+ returnStream << engine.ownerId(static_cast<QAbstractFileEngine::FileOwner> (owner));
+ } else if (command == QLatin1String("QFSFileEngine::pos")) {
+ returnStream << engine.pos();
+ } else if (command == QLatin1String("QFSFileEngine::read")) {
+ qint64 maxlen;
+ receivedStream >> maxlen;
+ QByteArray ba(maxlen, '\0');
+ const qint64 result = engine.read(ba.data(), maxlen);
+ returnStream << result;
+ int written = 0;
+ while (written < result)
+ written += returnStream.writeRawData(ba.data() + written, result - written);
+ } else if (command == QLatin1String("QFSFileEngine::readLine")) {
+ qint64 maxlen;
+ receivedStream >> maxlen;
+ QByteArray ba(maxlen, '\0');
+ const qint64 result = engine.readLine(ba.data(), maxlen);
+ returnStream << result;
+ int written = 0;
+ while (written < result)
+ written += returnStream.writeRawData(ba.data() + written, result - written);
+ } else if (command == QLatin1String("QFSFileEngine::remove")) {
+ returnStream << engine.remove();
+ } else if (command == QLatin1String("QFSFileEngine::rename")) {
+ QString newName;
+ receivedStream >> newName;
+ returnStream << engine.rename(newName);
+ } else if (command == QLatin1String("QFSFileEngine::rmdir")) {
+ QString dirName;
+ bool recurseParentDirectories;
+ receivedStream >> dirName;
+ receivedStream >> recurseParentDirectories;
+ returnStream << engine.rmdir(dirName, recurseParentDirectories);
+ } else if (command == QLatin1String("QFSFileEngine::seek")) {
+ quint64 offset;
+ receivedStream >> offset;
+ returnStream << engine.seek(offset);
+ } else if (command == QLatin1String("QFSFileEngine::setFileName")) {
+ QString fileName;
+ receivedStream >> fileName;
+ engine.setFileName(fileName);
+ } else if (command == QLatin1String("QFSFileEngine::setPermissions")) {
+ uint perms;
+ receivedStream >> perms;
+ returnStream << engine.setPermissions(perms);
+ } else if (command == QLatin1String("QFSFileEngine::setSize")) {
+ qint64 size;
+ receivedStream >> size;
+ returnStream << engine.setSize(size);
+ } else if (command == QLatin1String("QFSFileEngine::size")) {
+ returnStream << engine.size();
+ } else if (command == QLatin1String("QFSFileEngine::supportsExtension")) {
+ int extension;
+ receivedStream >> extension;
+ //returnStream << engine.supportsExtension(static_cast<QAbstractFileEngine::Extension> (extension));
+ returnStream << false;
+ } else if (command == QLatin1String("QFSFileEngine::write")) {
+ qint64 length;
+ receivedStream >> length;
+ qint64 read = 0;
+ qint64 written = 0;
+ QByteArray buffer(65536, '\0');
+ while (read < length) {
+ if (!receivedStream.device()->bytesAvailable())
+ receivedStream.device()->waitForReadyRead(-1);
+ const qint64 r = receivedStream.readRawData(buffer.data(), qMin(length - read,
+ static_cast<qint64> (buffer.length())));
+ read += r;
+ qint64 w = 0;
+ while (w < r)
+ w += engine.write(buffer.data(), r);
+ written += r;
+ }
+ returnStream << written;
+ } else if (!command.isEmpty()) {
+ qDebug() << "unknown command:" << command;
+ }
+
+ return block;
+}
+
+#include "fsengineserver.moc"
+#include "moc_fsengineserver.cpp"
diff --git a/src/libs/installer/fsengineserver.h b/src/libs/installer/fsengineserver.h
new file mode 100644
index 000000000..8ba72a85d
--- /dev/null
+++ b/src/libs/installer/fsengineserver.h
@@ -0,0 +1,62 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef FSENGINESERVER_H
+#define FSENGINESERVER_H
+
+#include "installer_global.h"
+
+#include <QtCore/QTimer>
+#include <QtNetwork/QTcpServer>
+
+class INSTALLER_EXPORT FSEngineServer : public QTcpServer
+{
+ Q_OBJECT
+
+public:
+ explicit FSEngineServer(quint16 port, QObject *parent = 0);
+ FSEngineServer(const QHostAddress &address, quint16 port, QObject *parent = 0);
+ ~FSEngineServer();
+
+ void enableTestMode();
+ void setAuthorizationKey(const QString &key);
+ QString authorizationKey() const;
+
+protected:
+ void incomingConnection(int socketDescriptor);
+
+private:
+ QString key;
+ QTimer watchdog;
+};
+
+#endif
diff --git a/src/libs/installer/getrepositoriesmetainfojob.cpp b/src/libs/installer/getrepositoriesmetainfojob.cpp
new file mode 100644
index 000000000..1e7092129
--- /dev/null
+++ b/src/libs/installer/getrepositoriesmetainfojob.cpp
@@ -0,0 +1,195 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "getrepositoriesmetainfojob.h"
+
+#include "getrepositorymetainfojob.h"
+#include "packagemanagercore_p.h"
+#include "qinstallerglobal.h"
+
+#include <QtCore/QDebug>
+
+using namespace KDUpdater;
+using namespace QInstaller;
+
+
+// -- GetRepositoriesMetaInfoJob
+
+GetRepositoriesMetaInfoJob::GetRepositoriesMetaInfoJob(PackageManagerCorePrivate *corePrivate)
+ : KDJob(corePrivate),
+ m_canceled(false),
+ m_silentRetries(3),
+ m_haveIgnoredError(false),
+ m_corePrivate(corePrivate)
+{
+ setCapabilities(Cancelable);
+}
+
+QStringList GetRepositoriesMetaInfoJob::temporaryDirectories() const
+{
+ return m_repositoryByTemporaryDirectory.keys();
+}
+
+QStringList GetRepositoriesMetaInfoJob::releaseTemporaryDirectories() const
+{
+ m_tempDirDeleter.releaseAll();
+ return m_repositoryByTemporaryDirectory.keys();
+}
+
+Repository GetRepositoriesMetaInfoJob::repositoryForTemporaryDirectory(const QString &tmpDir) const
+{
+ return m_repositoryByTemporaryDirectory.value(tmpDir);
+}
+
+int GetRepositoriesMetaInfoJob::numberOfRetrievedRepositories() const
+{
+ return m_repositoryByTemporaryDirectory.size();
+}
+
+int GetRepositoriesMetaInfoJob::silentRetries() const
+{
+ return m_silentRetries;
+}
+
+void GetRepositoriesMetaInfoJob::setSilentRetries(int retries)
+{
+ m_silentRetries = retries;
+}
+
+void GetRepositoriesMetaInfoJob::reset()
+{
+ m_canceled = false;
+ m_silentRetries = 3;
+ m_errorString.clear();
+ m_haveIgnoredError = false;
+
+ m_repositories.clear();
+ m_tempDirDeleter.releaseAndDeleteAll();
+ m_repositoryByTemporaryDirectory.clear();
+
+ setError(KDJob::NoError);
+ setErrorString(QString());
+ setCapabilities(Cancelable);
+}
+
+bool GetRepositoriesMetaInfoJob::isCanceled() const
+{
+ return m_canceled;
+}
+
+// -- private Q_SLOTS
+
+void GetRepositoriesMetaInfoJob::doStart()
+{
+ if ((m_corePrivate->isInstaller() && !m_corePrivate->isOfflineOnly())
+ || (m_corePrivate->isUpdater() || m_corePrivate->isPackageManager())) {
+ foreach (const Repository &repo, m_corePrivate->m_settings.repositories()) {
+ if (repo.isEnabled())
+ m_repositories += repo;
+ }
+ }
+
+ fetchNextRepo();
+}
+
+void GetRepositoriesMetaInfoJob::doCancel()
+{
+ m_canceled = true;
+ if (m_job)
+ m_job->cancel();
+}
+
+void GetRepositoriesMetaInfoJob::fetchNextRepo()
+{
+ if (m_job) {
+ m_job->deleteLater();
+ m_job = 0;
+ }
+
+ if (m_canceled) {
+ emitFinishedWithError(KDJob::Canceled, m_errorString);
+ return;
+ }
+
+ if (m_repositories.isEmpty()) {
+ if (m_haveIgnoredError)
+ emitFinishedWithError(QInstaller::UserIgnoreError, m_errorString);
+ else
+ emitFinished();
+ return;
+ }
+
+ m_job = new GetRepositoryMetaInfoJob(m_corePrivate, this);
+ connect(m_job, SIGNAL(finished(KDJob*)), this, SLOT(jobFinished(KDJob*)));
+ connect(m_job, SIGNAL(infoMessage(KDJob*, QString)), this, SIGNAL(infoMessage(KDJob*, QString)));
+
+ m_job->setSilentRetries(silentRetries());
+ m_job->setRepository(m_repositories.takeLast());
+ m_job->start();
+}
+
+void GetRepositoriesMetaInfoJob::jobFinished(KDJob *j)
+{
+ const GetRepositoryMetaInfoJob *const job = qobject_cast<const GetRepositoryMetaInfoJob *>(j);
+ Q_ASSERT(job);
+
+ if (job->error() != KDJob::NoError && !job->temporaryDirectory().isEmpty()) {
+ try {
+ removeDirectory(job->temporaryDirectory());
+ } catch (...) {
+ }
+ }
+
+ if (job->error() == KDJob::Canceled
+ || (job->error() >= KDJob::UserDefinedError && job->error() < QInstaller::UserIgnoreError)) {
+ emit infoMessage(j, job->errorString());
+ qDebug() << job->errorString();
+ emitFinishedWithError(job->error(), job->errorString());
+ return;
+ }
+
+ if (job->error() == QInstaller::UserIgnoreError) {
+ m_haveIgnoredError = true;
+ m_errorString = job->errorString();
+ } else {
+ const QString &tmpdir = job->releaseTemporaryDirectory();
+ job->m_tempDirDeleter.passAndRelease(m_tempDirDeleter, tmpdir);
+ m_repositoryByTemporaryDirectory.insert(tmpdir, job->repository());
+ }
+
+ if (job->error() == QInstaller::RepositoryUpdatesReceived) {
+ reset();
+ QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection);
+ } else {
+ QMetaObject::invokeMethod(this, "fetchNextRepo", Qt::QueuedConnection);
+ }
+}
diff --git a/src/libs/installer/getrepositoriesmetainfojob.h b/src/libs/installer/getrepositoriesmetainfojob.h
new file mode 100644
index 000000000..e44032b48
--- /dev/null
+++ b/src/libs/installer/getrepositoriesmetainfojob.h
@@ -0,0 +1,96 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#ifndef GETREPOSITORIESMETAINFOJOB_H
+#define GETREPOSITORIESMETAINFOJOB_H
+
+#include "fileutils.h"
+#include "installer_global.h"
+#include "repository.h"
+
+#include "kdjob.h"
+
+#include <QtCore/QList>
+#include <QtCore/QPointer>
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+
+namespace KDUpdater {
+ class FileDownloader;
+}
+
+namespace QInstaller {
+
+class GetRepositoryMetaInfoJob;
+class PackageManagerCorePrivate;
+
+class INSTALLER_EXPORT GetRepositoriesMetaInfoJob : public KDJob
+{
+ Q_OBJECT
+
+public:
+ explicit GetRepositoriesMetaInfoJob(PackageManagerCorePrivate *corePrivate);
+
+ QStringList temporaryDirectories() const;
+ QStringList releaseTemporaryDirectories() const;
+ Repository repositoryForTemporaryDirectory(const QString &tmpDir) const;
+
+ int numberOfRetrievedRepositories() const;
+
+ int silentRetries() const;
+ void setSilentRetries(int retries);
+
+ void reset();
+ bool isCanceled() const;
+
+private Q_SLOTS:
+ /* reimp */ void doStart();
+ /* reimp */ void doCancel();
+
+ void fetchNextRepo();
+ void jobFinished(KDJob*);
+
+private:
+ bool m_canceled;
+ int m_silentRetries;
+ bool m_haveIgnoredError;
+ PackageManagerCorePrivate *m_corePrivate;
+
+ QString m_errorString;
+ QList<Repository> m_repositories;
+ mutable TempDirDeleter m_tempDirDeleter;
+ QPointer<GetRepositoryMetaInfoJob> m_job;
+ QHash<QString, Repository> m_repositoryByTemporaryDirectory;
+};
+
+} // namespace QInstaller
+
+#endif // GETREPOSITORIESMETAINFOJOB_H
diff --git a/src/libs/installer/getrepositorymetainfojob.cpp b/src/libs/installer/getrepositorymetainfojob.cpp
new file mode 100644
index 000000000..a72a861a0
--- /dev/null
+++ b/src/libs/installer/getrepositorymetainfojob.cpp
@@ -0,0 +1,512 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#include "getrepositorymetainfojob.h"
+
+#include "constants.h"
+#include "errors.h"
+#include "lib7z_facade.h"
+#include "messageboxhandler.h"
+#include "packagemanagercore_p.h"
+#include "qinstallerglobal.h"
+
+#include "kdupdaterfiledownloader.h"
+#include "kdupdaterfiledownloaderfactory.h"
+
+#include <QtCore/QFile>
+#include <QtCore/QTimer>
+#include <QtCore/QUrl>
+
+#include <QtGui/QMessageBox>
+
+#include <QtNetwork/QAuthenticator>
+
+#include <QtXml/QDomDocument>
+#include <QtXml/QDomElement>
+
+using namespace KDUpdater;
+using namespace QInstaller;
+
+
+// -- GetRepositoryMetaInfoJob::ZipRunnable
+
+class GetRepositoryMetaInfoJob::ZipRunnable : public QObject, public QRunnable
+{
+ Q_OBJECT
+
+public:
+ ZipRunnable(const QString &archive, const QString &targetDir, QPointer<FileDownloader> downloader)
+ : QObject()
+ , QRunnable()
+ , m_archive(archive)
+ , m_targetDir(targetDir)
+ , m_downloader(downloader)
+ {}
+
+ ~ZipRunnable()
+ {
+ if (m_downloader)
+ m_downloader->deleteLater();
+ }
+
+ void run()
+ {
+ QFile archive(m_archive);
+ if (archive.open(QIODevice::ReadOnly)) {
+ try {
+ Lib7z::extractArchive(&archive, m_targetDir);
+ if (!archive.remove()) {
+ qWarning("Could not delete file %s: %s", qPrintable(m_archive),
+ qPrintable(archive.errorString()));
+ }
+ emit finished(true, QString());
+ } catch (const Lib7z::SevenZipException& e) {
+ emit finished(false, tr("Error while extracting %1. Error: %2").arg(m_archive, e.message()));
+ } catch (...) {
+ emit finished(false, tr("Unknown exception caught while extracting %1.").arg(m_archive));
+ }
+ } else {
+ emit finished(false, tr("Could not open %1 for reading. Error: %2").arg(m_archive,
+ archive.errorString()));
+ }
+ }
+
+Q_SIGNALS:
+ void finished(bool success, const QString &errorString);
+
+private:
+ const QString m_archive;
+ const QString m_targetDir;
+ QPointer<FileDownloader> m_downloader;
+};
+
+
+// -- GetRepositoryMetaInfoJob
+
+GetRepositoryMetaInfoJob::GetRepositoryMetaInfoJob(PackageManagerCorePrivate *corePrivate, QObject *parent)
+ : KDJob(parent),
+ m_canceled(false),
+ m_silentRetries(3),
+ m_retriesLeft(m_silentRetries),
+ m_downloader(0),
+ m_waitForDone(false),
+ m_corePrivate(corePrivate)
+{
+ setCapabilities(Cancelable);
+}
+
+GetRepositoryMetaInfoJob::~GetRepositoryMetaInfoJob()
+{
+ if (m_downloader)
+ m_downloader->deleteLater();
+}
+
+Repository GetRepositoryMetaInfoJob::repository() const
+{
+ return m_repository;
+}
+
+void GetRepositoryMetaInfoJob::setRepository(const Repository &r)
+{
+ m_repository = r;
+ qDebug() << "Setting repository with URL:" << r.url().toString();
+}
+
+int GetRepositoryMetaInfoJob::silentRetries() const
+{
+ return m_silentRetries;
+}
+
+void GetRepositoryMetaInfoJob::setSilentRetries(int retries)
+{
+ m_silentRetries = retries;
+}
+
+void GetRepositoryMetaInfoJob::doStart()
+{
+ m_retriesLeft = m_silentRetries;
+ startUpdatesXmlDownload();
+}
+
+void GetRepositoryMetaInfoJob::doCancel()
+{
+ m_canceled = true;
+ if (m_downloader)
+ m_downloader->cancelDownload();
+}
+
+void GetRepositoryMetaInfoJob::finished(int error, const QString &errorString)
+{
+ m_waitForDone = true;
+ m_threadPool.waitForDone();
+ (error > KDJob::NoError) ? emitFinishedWithError(error, errorString) : emitFinished();
+}
+
+QString GetRepositoryMetaInfoJob::temporaryDirectory() const
+{
+ return m_temporaryDirectory;
+}
+
+QString GetRepositoryMetaInfoJob::releaseTemporaryDirectory() const
+{
+ m_tempDirDeleter.releaseAll();
+ return m_temporaryDirectory;
+}
+
+// Updates.xml download
+
+void GetRepositoryMetaInfoJob::startUpdatesXmlDownload()
+{
+ if (m_downloader) {
+ m_downloader->deleteLater();
+ m_downloader = 0;
+ }
+
+ const QUrl url = m_repository.url();
+ if (url.isEmpty()) {
+ finished(QInstaller::InvalidUrl, tr("Empty repository URL."));
+ return;
+ }
+
+ if (!url.isValid()) {
+ finished(QInstaller::InvalidUrl, tr("Invalid repository URL: %1").arg(url.toString()));
+ return;
+ }
+
+ m_downloader = FileDownloaderFactory::instance().create(url.scheme(), this);
+ if (!m_downloader) {
+ finished(QInstaller::InvalidUrl, tr("URL scheme not supported: %1 (%2)").arg(url.scheme(),
+ url.toString()));
+ return;
+ }
+
+ // append a random string to avoid proxy caches
+ m_downloader->setUrl(QUrl(url.toString() + QString::fromLatin1("/Updates.xml?")
+ .append(QString::number(qrand() * qrand()))));
+
+ QAuthenticator auth;
+ auth.setUser(m_repository.username());
+ auth.setPassword(m_repository.password());
+ m_downloader->setAuthenticator(auth);
+
+ m_downloader->setAutoRemoveDownloadedFile(false);
+ connect(m_downloader, SIGNAL(downloadCompleted()), this, SLOT(updatesXmlDownloadFinished()));
+ connect(m_downloader, SIGNAL(downloadCanceled()), this, SLOT(updatesXmlDownloadCanceled()));
+ connect(m_downloader, SIGNAL(downloadAborted(QString)), this,
+ SLOT(updatesXmlDownloadError(QString)), Qt::QueuedConnection);
+ m_downloader->download();
+}
+
+void GetRepositoryMetaInfoJob::updatesXmlDownloadCanceled()
+{
+ finished(KDJob::Canceled, m_downloader->errorString());
+}
+
+void GetRepositoryMetaInfoJob::updatesXmlDownloadFinished()
+{
+ emit infoMessage(this, tr("Retrieving component meta information..."));
+
+ const QString fn = m_downloader->downloadedFileName();
+ Q_ASSERT(!fn.isEmpty());
+ Q_ASSERT(QFile::exists(fn));
+
+ try {
+ m_temporaryDirectory = createTemporaryDirectory(QLatin1String("remoterepo"));
+ m_tempDirDeleter.add(m_temporaryDirectory);
+ } catch (const QInstaller::Error& e) {
+ finished(QInstaller::ExtractionError, e.message());
+ return;
+ }
+
+ QFile updatesFile(fn);
+ if (!updatesFile.rename(m_temporaryDirectory + QLatin1String("/Updates.xml"))) {
+ finished(QInstaller::DownloadError, tr("Could not move Updates.xml to target location. Error: %1")
+ .arg(updatesFile.errorString()));
+ return;
+ }
+
+ if (!updatesFile.open(QIODevice::ReadOnly)) {
+ finished(QInstaller::DownloadError, tr("Could not open Updates.xml for reading. Error: %1")
+ .arg(updatesFile.errorString()));
+ return;
+ }
+
+ QString err;
+ QDomDocument doc;
+ if (!doc.setContent(&updatesFile, &err)) {
+ const QString msg = tr("Could not fetch a valid version of Updates.xml from repository: %1. "
+ "Error: %2").arg(m_repository.url().toString(), err);
+
+ const QMessageBox::StandardButton b =
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("updatesXmlDownloadError"), tr("Download Error"), msg, QMessageBox::Cancel);
+
+ if (b == QMessageBox::Cancel || b == QMessageBox::NoButton) {
+ finished(KDJob::Canceled, msg);
+ return;
+ }
+ }
+
+ emit infoMessage(this, tr("Parsing component meta information..."));
+
+ const QDomElement root = doc.documentElement();
+ // search for additional repositories that we might need to check
+ const QDomNode repositoryUpdate = root.firstChildElement(QLatin1String("RepositoryUpdate"));
+ if (!repositoryUpdate.isNull()) {
+ QHash<QString, QPair<Repository, Repository> > repositoryUpdates;
+ const QDomNodeList children = repositoryUpdate.toElement().childNodes();
+ for (int i = 0; i < children.count(); ++i) {
+ const QDomElement el = children.at(i).toElement();
+ if (!el.isNull() && el.tagName() == QLatin1String("Repository")) {
+ const QString action = el.attribute(QLatin1String("action"));
+ if (action == QLatin1String("add")) {
+ // add a new repository to the defaults list
+ Repository repository(el.attribute(QLatin1String("url")), true);
+ repository.setUsername(el.attribute(QLatin1String("username")));
+ repository.setPassword(el.attribute(QLatin1String("password")));
+ repositoryUpdates.insertMulti(action, qMakePair(repository, Repository()));
+
+ qDebug() << "Repository to add:" << repository.url().toString();
+ } else if (action == QLatin1String("remove")) {
+ // remove possible default repositories using the given server url
+ Repository repository(el.attribute(QLatin1String("url")), true);
+ repositoryUpdates.insertMulti(action, qMakePair(repository, Repository()));
+
+ qDebug() << "Repository to remove:" << repository.url().toString();
+ } else if (action == QLatin1String("replace")) {
+ // replace possible default repositories using the given server url
+ Repository oldRepository(el.attribute(QLatin1String("oldUrl")), true);
+ Repository newRepository(el.attribute(QLatin1String("newUrl")), true);
+ newRepository.setUsername(el.attribute(QLatin1String("username")));
+ newRepository.setPassword(el.attribute(QLatin1String("password")));
+
+ // store the new repository and the one old it replaces
+ repositoryUpdates.insertMulti(action, qMakePair(newRepository, oldRepository));
+ qDebug() << "Replace repository:" << oldRepository.url().toString() << "with:"
+ << newRepository.url().toString();
+ } else {
+ qDebug() << "Invalid additional repositories action set in Updates.xml fetched from:"
+ << m_repository.url().toString() << "Line:" << el.lineNumber();
+ }
+ }
+ }
+
+ if (!repositoryUpdates.isEmpty()) {
+ if (m_corePrivate->m_settings.updateDefaultRepositories(repositoryUpdates)
+ == Settings::UpdatesApplied) {
+ if (m_corePrivate->isUpdater() || m_corePrivate->isPackageManager())
+ m_corePrivate->writeMaintenanceConfigFiles();
+ finished(QInstaller::RepositoryUpdatesReceived, tr("Repository updates received."));
+ return;
+ }
+ }
+ }
+
+ const QDomNodeList children = root.childNodes();
+ for (int i = 0; i < children.count(); ++i) {
+ const QDomElement el = children.at(i).toElement();
+ if (el.isNull())
+ continue;
+ if (el.tagName() == QLatin1String("PackageUpdate")) {
+ const QDomNodeList c2 = el.childNodes();
+ for (int j = 0; j < c2.count(); ++j) {
+ if (c2.at(j).toElement().tagName() == scName)
+ m_packageNames << c2.at(j).toElement().text();
+ else if (c2.at(j).toElement().tagName() == scRemoteVersion)
+ m_packageVersions << c2.at(j).toElement().text();
+ else if (c2.at(j).toElement().tagName() == QLatin1String("SHA1"))
+ m_packageHash << c2.at(j).toElement().text();
+ }
+ }
+ }
+
+ setTotalAmount(m_packageNames.count() + 1);
+ setProcessedAmount(1);
+ emit infoMessage(this, tr("Finished updating component meta information..."));
+
+ if (m_packageNames.isEmpty())
+ finished(KDJob::NoError);
+ else
+ fetchNextMetaInfo();
+}
+
+void GetRepositoryMetaInfoJob::updatesXmlDownloadError(const QString &err)
+{
+ if (m_retriesLeft <= 0) {
+ const QString msg = tr("Could not fetch Updates.xml from repository: %1. Error: %2")
+ .arg(m_repository.url().toString(), err);
+
+ QMessageBox::StandardButtons buttons = QMessageBox::Retry | QMessageBox::Cancel;
+ const QMessageBox::StandardButton b =
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("updatesXmlDownloadError"), tr("Download Error"), msg, buttons);
+
+ if (b == QMessageBox::Cancel || b == QMessageBox::NoButton) {
+ finished(KDJob::Canceled, msg);
+ return;
+ }
+ }
+
+ m_retriesLeft--;
+ QTimer::singleShot(1500, this, SLOT(startUpdatesXmlDownload()));
+}
+
+// meta data download
+
+void GetRepositoryMetaInfoJob::fetchNextMetaInfo()
+{
+ emit infoMessage(this, tr("Retrieving component information from remote repository..."));
+
+ if (m_canceled) {
+ finished(KDJob::Canceled, m_downloader->errorString());
+ return;
+ }
+
+ if (m_packageNames.isEmpty() && m_currentPackageName.isEmpty()) {
+ finished(KDJob::NoError);
+ return;
+ }
+
+ QString next = m_currentPackageName;
+ QString nextVersion = m_currentPackageVersion;
+ if (next.isEmpty()) {
+ m_retriesLeft = m_silentRetries;
+ next = m_packageNames.takeLast();
+ nextVersion = m_packageVersions.takeLast();
+ }
+
+ qDebug() << "fetching metadata of" << next << "in version" << nextVersion;
+
+ bool online = true;
+ if (m_repository.url().scheme().isEmpty())
+ online = false;
+
+ const QString repoUrl = m_repository.url().toString();
+ const QUrl url = QString::fromLatin1("%1/%2/%3meta.7z").arg(repoUrl, next,
+ online ? nextVersion : QString());
+ m_downloader = FileDownloaderFactory::instance().create(url.scheme(), this);
+
+ if (!m_downloader) {
+ m_currentPackageName.clear();
+ m_currentPackageVersion.clear();
+ qWarning() << "Scheme not supported: " << url.toString();
+ QMetaObject::invokeMethod(this, "fetchNextMetaInfo", Qt::QueuedConnection);
+ return;
+ }
+
+ m_currentPackageName = next;
+ m_currentPackageVersion = nextVersion;
+ m_downloader->setUrl(url);
+ m_downloader->setAutoRemoveDownloadedFile(true);
+
+ QAuthenticator auth;
+ auth.setUser(m_repository.username());
+ auth.setPassword(m_repository.password());
+ m_downloader->setAuthenticator(auth);
+
+ connect(m_downloader, SIGNAL(downloadCanceled()), this, SLOT(metaDownloadCanceled()));
+ connect(m_downloader, SIGNAL(downloadCompleted()), this, SLOT(metaDownloadFinished()));
+ connect(m_downloader, SIGNAL(downloadAborted(QString)), this, SLOT(metaDownloadError(QString)),
+ Qt::QueuedConnection);
+
+ m_downloader->download();
+}
+
+void GetRepositoryMetaInfoJob::metaDownloadCanceled()
+{
+ finished(KDJob::Canceled, m_downloader->errorString());
+}
+
+void GetRepositoryMetaInfoJob::metaDownloadFinished()
+{
+ const QString fn = m_downloader->downloadedFileName();
+ Q_ASSERT(!fn.isEmpty());
+
+ QFile arch(fn);
+ if (!arch.open(QIODevice::ReadOnly)) {
+ finished(QInstaller::ExtractionError, tr("Could not open meta info archive: %1. Error: %2").arg(fn,
+ arch.errorString()));
+ return;
+ }
+
+ if (!m_packageHash.isEmpty()) {
+ // verify file hash
+ QByteArray expectedFileHash = m_packageHash.back().toLatin1();
+ QByteArray archContent = arch.readAll();
+ QByteArray realFileHash = QString::fromLatin1(QCryptographicHash::hash(archContent,
+ QCryptographicHash::Sha1).toHex()).toLatin1();
+ if (expectedFileHash != realFileHash) {
+ emit infoMessage(this, tr("The hash of one component does not match the expected one."));
+ metaDownloadError(tr("Bad hash."));
+ return;
+ }
+ m_packageHash.removeLast();
+ }
+ arch.close();
+ m_currentPackageName.clear();
+
+ ZipRunnable *runnable = new ZipRunnable(fn, m_temporaryDirectory, m_downloader);
+ connect(runnable, SIGNAL(finished(bool,QString)), this, SLOT(unzipFinished(bool,QString)));
+ m_threadPool.start(runnable);
+
+ if (!m_waitForDone)
+ fetchNextMetaInfo();
+}
+
+void GetRepositoryMetaInfoJob::metaDownloadError(const QString &err)
+{
+ if (m_retriesLeft <= 0) {
+ const QString msg = tr("Could not download meta information for component: %1. Error: %2")
+ .arg(m_currentPackageName, err);
+
+ QMessageBox::StandardButtons buttons = QMessageBox::Retry | QMessageBox::Cancel;
+ const QMessageBox::StandardButton b =
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("updatesXmlDownloadError"), tr("Download Error"), msg, buttons);
+
+ if (b == QMessageBox::Cancel || b == QMessageBox::NoButton) {
+ finished(KDJob::Canceled, msg);
+ return;
+ }
+ }
+
+ m_retriesLeft--;
+ QTimer::singleShot(1500, this, SLOT(fetchNextMetaInfo()));
+}
+
+void GetRepositoryMetaInfoJob::unzipFinished(bool ok, const QString &error)
+{
+ if (!ok)
+ finished(QInstaller::ExtractionError, error);
+}
+
+#include "getrepositorymetainfojob.moc"
+#include "moc_getrepositorymetainfojob.cpp"
diff --git a/src/libs/installer/getrepositorymetainfojob.h b/src/libs/installer/getrepositorymetainfojob.h
new file mode 100644
index 000000000..aec94603d
--- /dev/null
+++ b/src/libs/installer/getrepositorymetainfojob.h
@@ -0,0 +1,113 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#ifndef GETREPOSITORYMETAINFOJOB_H
+#define GETREPOSITORYMETAINFOJOB_H
+
+#include "fileutils.h"
+#include "installer_global.h"
+#include "repository.h"
+
+#include "kdjob.h"
+
+#include <QtCore/QPointer>
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+#include <QtCore/QThreadPool>
+
+namespace KDUpdater {
+ class FileDownloader;
+}
+
+namespace QInstaller {
+
+class GetRepositoriesMetaInfoJob;
+class PackageManagerCorePrivate;
+
+class INSTALLER_EXPORT GetRepositoryMetaInfoJob : public KDJob
+{
+ Q_OBJECT
+ class ZipRunnable;
+ friend class QInstaller::GetRepositoriesMetaInfoJob;
+
+public:
+ explicit GetRepositoryMetaInfoJob(PackageManagerCorePrivate *corePrivate, QObject *parent = 0);
+ ~GetRepositoryMetaInfoJob();
+
+ Repository repository() const;
+ void setRepository(const Repository &r);
+
+ int silentRetries() const;
+ void setSilentRetries(int retries);
+
+ QString temporaryDirectory() const;
+ QString releaseTemporaryDirectory() const;
+
+private:
+ /* reimp */ void doStart();
+ /* reimp */ void doCancel();
+ void finished(int error, const QString &errorString = QString());
+
+private Q_SLOTS:
+ void startUpdatesXmlDownload();
+ void updatesXmlDownloadCanceled();
+ void updatesXmlDownloadFinished();
+ void updatesXmlDownloadError(const QString &error);
+
+ void fetchNextMetaInfo();
+ void metaDownloadCanceled();
+ void metaDownloadFinished();
+ void metaDownloadError(const QString &error);
+
+ void unzipFinished(bool status, const QString &error);
+
+private:
+ bool m_canceled;
+ int m_silentRetries;
+ int m_retriesLeft;
+ Repository m_repository;
+ QStringList m_packageNames;
+ QStringList m_packageVersions;
+ QStringList m_packageHash;
+ QPointer<KDUpdater::FileDownloader> m_downloader;
+ QString m_currentPackageName;
+ QString m_currentPackageVersion;
+ QString m_temporaryDirectory;
+ mutable TempDirDeleter m_tempDirDeleter;
+
+ bool m_waitForDone;
+ QThreadPool m_threadPool;
+ PackageManagerCorePrivate *m_corePrivate;
+};
+
+} // namespace QInstaller
+
+#endif // GETREPOSITORYMETAINFOJOB_H
diff --git a/src/libs/installer/globalsettingsoperation.cpp b/src/libs/installer/globalsettingsoperation.cpp
new file mode 100644
index 000000000..4fe68abae
--- /dev/null
+++ b/src/libs/installer/globalsettingsoperation.cpp
@@ -0,0 +1,123 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "globalsettingsoperation.h"
+#include "qsettingswrapper.h"
+
+using namespace QInstaller;
+
+GlobalSettingsOperation::GlobalSettingsOperation()
+{
+ setName(QLatin1String("GlobalConfig"));
+}
+
+void GlobalSettingsOperation::backup()
+{
+}
+
+bool GlobalSettingsOperation::performOperation()
+{
+ QString key, value;
+ QScopedPointer<QSettingsWrapper> settings(setup(&key, &value, arguments()));
+ if (settings.isNull())
+ return false;
+
+ if (!settings->isWritable()) {
+ setError(UserDefinedError);
+ setErrorString(tr("Settings are not writable"));
+ return false;
+ }
+
+ const QVariant oldValue = settings->value(key);
+ settings->setValue(key, value);
+ settings->sync();
+
+ if (settings->status() != QSettingsWrapper::NoError) {
+ setError(UserDefinedError);
+ setErrorString(tr("Failed to write settings"));
+ return false;
+ }
+
+ setValue(QLatin1String("oldvalue"), oldValue);
+ return true;
+}
+
+bool GlobalSettingsOperation::undoOperation()
+{
+ QString key, val;
+ QScopedPointer<QSettingsWrapper> settings(setup(&key, &val, arguments()));
+ if (settings.isNull())
+ return false;
+
+ // be sure it's still our value and nobody changed it in between
+ const QVariant oldValue = value(QLatin1String("oldvalue"));
+ if (settings->value(key) == val) {
+ // restore the previous state
+ if (oldValue.isNull())
+ settings->remove(key);
+ else
+ settings->setValue(key, oldValue);
+ }
+
+ return true;
+}
+
+bool GlobalSettingsOperation::testOperation()
+{
+ return true;
+}
+
+Operation *GlobalSettingsOperation::clone() const
+{
+ return new GlobalSettingsOperation();
+}
+
+QSettingsWrapper *GlobalSettingsOperation::setup(QString *key, QString *value, const QStringList &arguments)
+{
+ if (arguments.count() == 4) {
+ const QString &company = arguments.at(0);
+ const QString &application = arguments.at(1);
+ *key = arguments.at(2);
+ *value = arguments.at(3);
+ return new QSettingsWrapper(company, application);
+ } else if (arguments.count() == 3) {
+ const QString &filename = arguments.at(0);
+ *key = arguments.at(1);
+ *value = arguments.at(2);
+ return new QSettingsWrapper(filename, QSettingsWrapper::NativeFormat);
+ }
+
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in 0%: %1 arguments given, at least 3 expected.")
+ .arg(name()).arg(arguments.count()));
+ return 0;
+}
diff --git a/src/libs/installer/globalsettingsoperation.h b/src/libs/installer/globalsettingsoperation.h
new file mode 100644
index 000000000..f222bdff5
--- /dev/null
+++ b/src/libs/installer/globalsettingsoperation.h
@@ -0,0 +1,59 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef GLOBALSETTINGSOPERATION_H
+#define GLOBALSETTINGSOPERATION_H
+
+#include "qinstallerglobal.h"
+
+class QSettingsWrapper;
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT GlobalSettingsOperation : public Operation
+{
+public:
+ GlobalSettingsOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+
+private:
+ QSettingsWrapper *setup(QString *key, QString *value, const QStringList &args);
+};
+
+} // namespace QInstaller
+
+#endif // GLOBALSETTINGSOPERATION_H
diff --git a/src/libs/installer/init.cpp b/src/libs/installer/init.cpp
new file mode 100644
index 000000000..5a464684a
--- /dev/null
+++ b/src/libs/installer/init.cpp
@@ -0,0 +1,244 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#include "init.h"
+
+#include "createshortcutoperation.h"
+#include "createdesktopentryoperation.h"
+#include "createlocalrepositoryoperation.h"
+#include "extractarchiveoperation.h"
+#include "globalsettingsoperation.h"
+#include "environmentvariablesoperation.h"
+#include "registerfiletypeoperation.h"
+#include "selfrestartoperation.h"
+#include "installiconsoperation.h"
+#include "elevatedexecuteoperation.h"
+#include "fakestopprocessforupdateoperation.h"
+
+//added for NDK
+#include "copydirectoryoperation.h"
+#include "qtpatchoperation.h"
+#include "setdemospathonqtoperation.h"
+#include "setexamplespathonqtoperation.h"
+#include "setpluginpathonqtcoreoperation.h"
+#include "setimportspathonqtcoreoperation.h"
+#include "setpathonqtcoreoperation.h"
+#include "replaceoperation.h"
+#include "licenseoperation.h"
+#include "linereplaceoperation.h"
+#include "registerdocumentationoperation.h"
+#include "registerqtoperation.h"
+#include "registerqtv2operation.h"
+#include "registerqtv23operation.h"
+#include "setqtcreatorvalueoperation.h"
+#include "addqtcreatorarrayvalueoperation.h"
+#include "simplemovefileoperation.h"
+#include "registertoolchainoperation.h"
+#include "registerdefaultdebuggeroperation.h"
+#include "updatecreatorsettingsfrom21to22operation.h"
+
+#include "minimumprogressoperation.h"
+
+#ifdef Q_OS_MAC
+# include "macreplaceinstallnamesoperation.h"
+#endif // Q_OS_MAC
+
+#include "utils.h"
+
+#include "kdupdaterupdateoperation.h"
+#include "kdupdaterupdateoperationfactory.h"
+#include "kdupdaterfiledownloader.h"
+#include "kdupdaterfiledownloaderfactory.h"
+
+#include <QtPlugin>
+#include <QNetworkProxyFactory>
+
+#include <unix/C/7zCrc.h>
+
+namespace NArchive {
+namespace NBz2 { void registerArcBZip2(); }
+namespace NGz { void registerArcGZip(); }
+namespace NLzma { namespace NLzmaAr { void registerArcLzma(); } }
+namespace NLzma { namespace NLzma86Ar { void registerArcLzma86(); } }
+namespace NSplit { void registerArcSplit(); }
+namespace NXz { void registerArcxz(); }
+namespace NZ { void registerArcZ(); }
+}
+
+void registerArc7z();
+void registerArcCab();
+void registerArcTar();
+void registerArcZip();
+
+void registerCodecBCJ2();
+void registerCodecBCJ();
+void registerCodecBCJ();
+void registerCodecByteSwap();
+void registerCodecBZip2();
+void registerCodecCopy();
+void registerCodecDeflate64();
+void registerCodecDeflate();
+void registerCodecDelta();
+void registerCodecLZMA2();
+void registerCodecLZMA();
+void registerCodecPPMD();
+void registerCodec7zAES();
+
+using namespace NArchive;
+using namespace KDUpdater;
+using namespace QInstaller;
+
+static void initArchives()
+{
+ NBz2::registerArcBZip2();
+ NGz::registerArcGZip();
+ NLzma::NLzmaAr::registerArcLzma();
+ NLzma::NLzma86Ar::registerArcLzma86();
+ NSplit::registerArcSplit();
+ NXz::registerArcxz();
+ NZ::registerArcZ();
+ registerArc7z();
+ registerArcCab();
+ registerArcTar();
+ registerArcZip();
+
+ registerCodecBCJ2();
+ registerCodecBCJ();
+ registerCodecBCJ();
+ registerCodecByteSwap();
+ registerCodecBZip2();
+ registerCodecCopy();
+ registerCodecDeflate64();
+ registerCodecDeflate();
+ registerCodecDelta();
+ registerCodecLZMA2();
+ registerCodecLZMA();
+ registerCodecPPMD();
+ registerCodec7zAES();
+
+ CrcGenerateTable();
+}
+
+static void initResources()
+{
+ Q_INIT_RESOURCE(patch_file_lists);
+#if defined(USE_STATIC_SQLITE_PLUGIN)
+ Q_IMPORT_PLUGIN(qsqlite); // RegisterDocumentationOperation needs this
+#endif
+}
+
+static void messageHandler(QtMsgType type, const char *msg)
+{
+ QByteArray ba(msg);
+ // last character is a space from qDebug
+ if (ba.endsWith(' '))
+ ba.chop(1);
+
+ // remove quotes if the whole message is surrounded with them
+ if (ba.startsWith('"') && ba.endsWith('"'))
+ ba = ba.mid(1, ba.length()-2);
+
+ // prepend the message type, skip QtDebugMsg
+ switch (type) {
+ case QtWarningMsg: {
+ ba.prepend("Warning: ");
+ } break;
+ case QtCriticalMsg: {
+ ba.prepend("Critical: ");
+ } break;
+ case QtFatalMsg: {
+ ba.prepend("Fatal: ");
+ } break;
+ default:
+ break;
+ }
+
+ verbose() << ba.constData() << std::endl;
+ if (type == QtFatalMsg) {
+ QtMsgHandler oldMsgHandler = qInstallMsgHandler(0);
+ qt_message_output(type, msg);
+ qInstallMsgHandler(oldMsgHandler);
+ }
+}
+
+void QInstaller::init()
+{
+ ::initResources();
+
+ UpdateOperationFactory &factory = UpdateOperationFactory::instance();
+ factory.registerUpdateOperation<CreateShortcutOperation>(QLatin1String("CreateShortcut"));
+ factory.registerUpdateOperation<CreateDesktopEntryOperation>(QLatin1String("CreateDesktopEntry"));
+ factory.registerUpdateOperation<CreateLocalRepositoryOperation>(QLatin1String("CreateLocalRepository"));
+ factory.registerUpdateOperation<ExtractArchiveOperation>(QLatin1String("Extract"));
+ factory.registerUpdateOperation<GlobalSettingsOperation>(QLatin1String("GlobalConfig"));
+ factory.registerUpdateOperation<EnvironmentVariableOperation>(QLatin1String( "EnvironmentVariable"));
+ factory.registerUpdateOperation<RegisterFileTypeOperation>(QLatin1String("RegisterFileType"));
+ factory.registerUpdateOperation<SelfRestartOperation>(QLatin1String("SelfRestart"));
+ factory.registerUpdateOperation<InstallIconsOperation>(QLatin1String("InstallIcons"));
+ factory.registerUpdateOperation<ElevatedExecuteOperation>(QLatin1String("Execute"));
+ factory.registerUpdateOperation<FakeStopProcessForUpdateOperation>(QLatin1String("FakeStopProcessForUpdate"));
+
+ // added for NDK
+ factory.registerUpdateOperation<SimpleMoveFileOperation>(QLatin1String("SimpleMoveFile"));
+ factory.registerUpdateOperation<CopyDirectoryOperation>(QLatin1String("CopyDirectory"));
+ factory.registerUpdateOperation<RegisterDocumentationOperation>(QLatin1String("RegisterDocumentation"));
+ factory.registerUpdateOperation<RegisterQtInCreatorOperation>(QLatin1String("RegisterQtInCreator"));
+ factory.registerUpdateOperation<RegisterQtInCreatorV2Operation>(QLatin1String("RegisterQtInCreatorV2"));
+ factory.registerUpdateOperation<RegisterQtInCreatorV23Operation>(QLatin1String("RegisterQtInCreatorV23"));
+ factory.registerUpdateOperation<RegisterToolChainOperation>(QLatin1String("RegisterToolChain") );
+ factory.registerUpdateOperation<RegisterDefaultDebuggerOperation>(QLatin1String( "RegisterDefaultDebugger"));
+ factory.registerUpdateOperation<SetDemosPathOnQtOperation>(QLatin1String("SetDemosPathOnQt"));
+ factory.registerUpdateOperation<SetExamplesPathOnQtOperation>(QLatin1String("SetExamplesPathOnQt"));
+ factory.registerUpdateOperation<SetPluginPathOnQtCoreOperation>(QLatin1String("SetPluginPathOnQtCore"));
+ factory.registerUpdateOperation<SetImportsPathOnQtCoreOperation>(QLatin1String("SetImportsPathOnQtCore"));
+ factory.registerUpdateOperation<SetPathOnQtCoreOperation>(QLatin1String("SetPathOnQtCore"));
+ factory.registerUpdateOperation<SetQtCreatorValueOperation>(QLatin1String("SetQtCreatorValue"));
+ factory.registerUpdateOperation<AddQtCreatorArrayValueOperation>(QLatin1String("AddQtCreatorArrayValue"));
+ factory.registerUpdateOperation<QtPatchOperation>(QLatin1String("QtPatch"));
+ factory.registerUpdateOperation<ReplaceOperation>(QLatin1String("Replace"));
+ factory.registerUpdateOperation<LineReplaceOperation>(QLatin1String( "LineReplace" ) );
+ factory.registerUpdateOperation<UpdateCreatorSettingsFrom21To22Operation>(QLatin1String("UpdateCreatorSettingsFrom21To22"));
+
+ factory.registerUpdateOperation<MinimumProgressOperation>(QLatin1String("MinimumProgress"));
+ factory.registerUpdateOperation<LicenseOperation>(QLatin1String("License"));
+
+ FileDownloaderFactory::setFollowRedirects(true);
+
+#ifdef Q_OS_MAC
+ factory.registerUpdateOperation<MacReplaceInstallNamesOperation>(QLatin1String("ReplaceInstallNames"));
+#endif // Q_OS_MAC
+
+ // load 7z stuff, if we're a static lib
+ ::initArchives();
+
+ // qDebug -> verbose()
+ qInstallMsgHandler(messageHandler);
+}
diff --git a/src/libs/installer/init.h b/src/libs/installer/init.h
new file mode 100644
index 000000000..ae15f90dd
--- /dev/null
+++ b/src/libs/installer/init.h
@@ -0,0 +1,44 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef QINSTALLER_INIT_H
+#define QINSTALLER_INIT_H
+
+#include "installer_global.h"
+
+namespace QInstaller {
+
+void INSTALLER_EXPORT init();
+
+}
+
+#endif // QINSTALLER_INIT_H
diff --git a/src/libs/installer/installer.pro b/src/libs/installer/installer.pro
new file mode 100644
index 000000000..5ec75d589
--- /dev/null
+++ b/src/libs/installer/installer.pro
@@ -0,0 +1,179 @@
+TEMPLATE = lib
+TARGET = installer
+DEPENDPATH += . ..
+INCLUDEPATH += . ..
+
+include(../7zip/7zip.pri)
+include(../kdtools/kdtools.pri)
+include(../../../installerfw.pri)
+
+DESTDIR = $$IFW_LIB_PATH
+DLLDESTDIR = $$IFW_APP_PATH
+
+DEFINES += BUILD_LIB_INSTALLER
+
+QT += script \
+ network \
+ xml
+
+HEADERS += packagemanagercore.h \
+ packagemanagercore_p.h \
+ packagemanagergui.h \
+ binaryformat.h \
+ binaryformatengine.h \
+ binaryformatenginehandler.h \
+ repository.h \
+ zipjob.h \
+ utils.h \
+ errors.h \
+ component.h \
+ componentmodel.h \
+ qinstallerglobal.h \
+ qtpatch.h \
+ persistentsettings.h \
+ projectexplorer_export.h \
+ qtpatchoperation.h \
+ setpathonqtcoreoperation.h \
+ setdemospathonqtoperation.h \
+ setexamplespathonqtoperation.h \
+ setpluginpathonqtcoreoperation.h \
+ setimportspathonqtcoreoperation.h \
+ replaceoperation.h \
+ linereplaceoperation.h \
+ registerdocumentationoperation.h \
+ registerqtoperation.h \
+ registertoolchainoperation.h \
+ registerqtv2operation.h \
+ registerqtv23operation.h \
+ setqtcreatorvalueoperation.h \
+ addqtcreatorarrayvalueoperation.h \
+ copydirectoryoperation.h \
+ simplemovefileoperation.h \
+ extractarchiveoperation.h \
+ extractarchiveoperation_p.h \
+ globalsettingsoperation.h \
+ createshortcutoperation.h \
+ createdesktopentryoperation.h \
+ registerfiletypeoperation.h \
+ environmentvariablesoperation.h \
+ installiconsoperation.h \
+ selfrestartoperation.h \
+ settings.h \
+ getrepositorymetainfojob.h \
+ downloadarchivesjob.h \
+ init.h \
+ updater.h \
+ operationrunner.h \
+ updatesettings.h \
+ adminauthorization.h \
+ fsengineclient.h \
+ fsengineserver.h \
+ elevatedexecuteoperation.h \
+ fakestopprocessforupdateoperation.h \
+ lazyplaintextedit.h \
+ progresscoordinator.h \
+ minimumprogressoperation.h \
+ performinstallationform.h \
+ messageboxhandler.h \
+ getrepositoriesmetainfojob.h \
+ licenseoperation.h \
+ component_p.h \
+ qtcreator_constants.h \
+ qtcreatorpersistentsettings.h \
+ registerdefaultdebuggeroperation.h \
+ updatecreatorsettingsfrom21to22operation.h \
+ qprocesswrapper.h \
+ qsettingswrapper.h \
+ constants.h \
+ packagemanagerproxyfactory.h \
+ createlocalrepositoryoperation.h \
+ lib7z_facade.h
+
+SOURCES += packagemanagercore.cpp \
+ packagemanagercore_p.cpp \
+ packagemanagergui.cpp \
+ binaryformat.cpp \
+ binaryformatengine.cpp \
+ binaryformatenginehandler.cpp \
+ repository.cpp \
+ zipjob.cpp \
+ fileutils.cpp \
+ utils.cpp \
+ component.cpp \
+ componentmodel.cpp \
+ qtpatch.cpp \
+ persistentsettings.cpp \
+ qtpatchoperation.cpp \
+ setpathonqtcoreoperation.cpp \
+ setdemospathonqtoperation.cpp \
+ setexamplespathonqtoperation.cpp \
+ setpluginpathonqtcoreoperation.cpp \
+ setimportspathonqtcoreoperation.cpp \
+ replaceoperation.cpp \
+ linereplaceoperation.cpp \
+ registerdocumentationoperation.cpp \
+ registerqtoperation.cpp \
+ registertoolchainoperation.cpp \
+ registerqtv2operation.cpp \
+ registerqtv23operation.cpp \
+ setqtcreatorvalueoperation.cpp \
+ addqtcreatorarrayvalueoperation.cpp \
+ copydirectoryoperation.cpp \
+ simplemovefileoperation.cpp \
+ extractarchiveoperation.cpp \
+ globalsettingsoperation.cpp \
+ createshortcutoperation.cpp \
+ createdesktopentryoperation.cpp \
+ registerfiletypeoperation.cpp \
+ environmentvariablesoperation.cpp \
+ installiconsoperation.cpp \
+ selfrestartoperation.cpp \
+ getrepositorymetainfojob.cpp \
+ downloadarchivesjob.cpp \
+ init.cpp \
+ updater.cpp \
+ operationrunner.cpp \
+ updatesettings.cpp \
+ adminauthorization.cpp \
+ fsengineclient.cpp \
+ fsengineserver.cpp \
+ elevatedexecuteoperation.cpp \
+ fakestopprocessforupdateoperation.cpp \
+ lazyplaintextedit.cpp \
+ progresscoordinator.cpp \
+ minimumprogressoperation.cpp \
+ performinstallationform.cpp \
+ messageboxhandler.cpp \
+ getrepositoriesmetainfojob.cpp \
+ licenseoperation.cpp \
+ component_p.cpp \
+ qtcreatorpersistentsettings.cpp \
+ registerdefaultdebuggeroperation.cpp \
+ updatecreatorsettingsfrom21to22operation.cpp \
+ qprocesswrapper.cpp \
+ templates.cpp \
+ qsettingswrapper.cpp \
+ settings.cpp \
+ packagemanagerproxyfactory.cpp \
+ createlocalrepositoryoperation.cpp \
+ lib7z_facade.cpp
+
+RESOURCES += resources/patch_file_lists.qrc
+
+macx {
+ HEADERS += macrelocateqt.h \
+ macreplaceinstallnamesoperation.h
+ SOURCES += adminauthorization_mac.cpp \
+ macrelocateqt.cpp \
+ macreplaceinstallnamesoperation.cpp
+}
+
+unix:!macx:SOURCES += adminauthorization_x11.cpp
+
+win32 {
+ SOURCES += adminauthorization_win.cpp
+ LIBS += -loleaut32 -lUser32 # 7zip
+ LIBS += advapi32.lib psapi.lib # kdtools
+ LIBS += -lole32 # createshortcutoperation
+ CONFIG(shared, static|shared):LIBS += -lshell32
+}
diff --git a/src/libs/installer/installer_global.h b/src/libs/installer/installer_global.h
new file mode 100644
index 000000000..51091cd49
--- /dev/null
+++ b/src/libs/installer/installer_global.h
@@ -0,0 +1,41 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework**
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).*
+**
+** Contact: Nokia Corporation qt-info@nokia.com**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception version
+** 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you are unsure which license is appropriate for your use, please contact
+** (qt-info@nokia.com).
+**
+**************************************************************************/
+#ifndef INSTALLER_GLOBAL_H
+#define INSTALLER_GLOBAL_H
+
+#include <QtCore/QtGlobal>
+
+#ifdef LIB_INSTALLER_SHARED
+#ifdef BUILD_LIB_INSTALLER
+#define INSTALLER_EXPORT Q_DECL_EXPORT
+#else
+#define INSTALLER_EXPORT Q_DECL_IMPORT
+#endif
+#else
+#define INSTALLER_EXPORT
+#endif
+
+#endif //INSTALLER_GLOBAL_H
diff --git a/src/libs/installer/installiconsoperation.cpp b/src/libs/installer/installiconsoperation.cpp
new file mode 100644
index 000000000..c2fee33eb
--- /dev/null
+++ b/src/libs/installer/installiconsoperation.cpp
@@ -0,0 +1,281 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#include "installiconsoperation.h"
+
+#include "fileutils.h"
+#include "packagemanagercore.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QDirIterator>
+
+#if QT_VERSION >= 0x040600
+# include <QProcessEnvironment>
+#else
+# include <QProcess>
+#endif
+
+using namespace QInstaller;
+
+QString InstallIconsOperation::targetDirectory()
+{
+ // we're not searching for the first time, let's re-use the old value
+ if (hasValue(QLatin1String("targetdirectory")))
+ return value(QLatin1String("targetdirectory")).toString();
+
+#if QT_VERSION >= 0x040600
+ const QProcessEnvironment env;
+ QStringList XDG_DATA_DIRS = env.value(QLatin1String("XDG_DATA_DIRS")).split(QLatin1Char(':'),
+ QString::SkipEmptyParts);
+#else
+ QStringList XDG_DATA_DIRS;
+ const QStringList env = QProcess::systemEnvironment();
+ for (QStringList::const_iterator it = env.begin(); it != env.end(); ++it) {
+ if (it->startsWith(QLatin1String("XDG_DATA_DIRS=")))
+ XDG_DATA_DIRS = it->mid(it->indexOf(QLatin1Char('=')) + 1).split(QLatin1Char(':'));
+ }
+#endif
+
+ XDG_DATA_DIRS.push_back(QLatin1String("/usr/share/pixmaps")); // default path
+ XDG_DATA_DIRS.push_back(QDir::home().absoluteFilePath(QLatin1String(".local/share/icons"))); // default path
+ XDG_DATA_DIRS.push_back(QDir::home().absoluteFilePath(QLatin1String(".icons"))); // default path
+
+ QString directory;
+ const QStringList& directories = XDG_DATA_DIRS;
+ for (QStringList::const_iterator it = directories.begin(); it != directories.end(); ++it) {
+ if (it->isEmpty())
+ continue;
+
+ // our default dirs are correct, XDG_DATA_DIRS set via env need "icon" at the end
+ if ((it + 1 == directories.end()) || (it + 2 == directories.end()) || (it + 3 == directories.end()))
+ directory = QDir(*it).absolutePath();
+ else
+ directory = QDir(*it).absoluteFilePath(QLatin1String("icons"));
+
+ QDir dir(directory);
+ // let's see if this dir exists or we're able to create it
+ if (!dir.exists() && !QDir().mkpath(directory))
+ continue;
+
+ // we just try if we're able to open the file in ReadWrite
+ QFile file(QDir(directory).absoluteFilePath(QLatin1String("tmpfile")));
+ const bool existed = file.exists();
+ if (!file.open(QIODevice::ReadWrite))
+ continue;
+
+ file.close();
+ if (!existed)
+ file.remove();
+ break;
+ }
+
+ if (!QDir(directory).exists())
+ QDir().mkpath(directory);
+
+ setValue(QLatin1String("directory"), directory);
+ return directory;
+}
+
+InstallIconsOperation::InstallIconsOperation()
+{
+ setName(QLatin1String("InstallIcons"));
+}
+
+InstallIconsOperation::~InstallIconsOperation()
+{
+ const QStringList backupFiles = value(QLatin1String("backupfiles")).toStringList();
+ for (QStringList::const_iterator it = backupFiles.begin(); it != backupFiles.end(); it += 2) {
+ const QString& backup = *(it + 1);
+ deleteFileNowOrLater(backup);
+ }
+}
+
+void InstallIconsOperation::backup()
+{
+ // we backup on the fly
+}
+
+bool InstallIconsOperation::performOperation()
+{
+ const QStringList args = arguments();
+ if (args.count() != 1) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, 1 expected.").arg(name()).arg(args
+ .count()));
+ return false;
+ }
+
+ const QString source = args.first();
+ if (source.isEmpty()) {
+ setError(InvalidArguments);
+ setErrorString(QObject::tr("Invalid Argument: source folder must not be empty."));
+ return false;
+ }
+
+ const QDir sourceDir = QDir(source);
+ const QDir targetDir = QDir(targetDirectory());
+
+ QStringList files;
+ QStringList backupFiles;
+ QStringList createdDirectories;
+
+ PackageManagerCore *const core = qVariantValue<PackageManagerCore*>(value(QLatin1String("installer")));
+
+ // iterate a second time to get the actual work done
+ QDirIterator it(sourceDir.path(), QDir::Dirs | QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot,
+ QDirIterator::Subdirectories);
+ while (it.hasNext()) {
+ qApp->processEvents();
+
+ const int status = core->status();
+ if (status == PackageManagerCore::Canceled || status == PackageManagerCore::Failure)
+ return true;
+
+ const QString source = it.next();
+ const QString target = targetDir.absoluteFilePath(sourceDir.relativeFilePath(source));
+
+ emit outputTextChanged(target);
+
+ const QFileInfo fi = it.fileInfo();
+ if (!fi.isDir()) {
+ if (QFile(target).exists()) {
+ // first backup...
+ const QString backup = generateTemporaryFileName(target + QLatin1String("XXXXXX"));
+ QFile bf(target);
+ if (!bf.copy(backup)) {
+ setError(UserDefinedError);
+ setErrorString(QObject::tr("Could not backup file %1: %2").arg(target, bf.errorString()));
+ undoOperation();
+ return false;
+ }
+
+ backupFiles.push_back(target);
+ backupFiles.push_back(backup);
+ setValue(QLatin1String("backupfiles"), backupFiles);
+
+ // then delete it
+ QString errStr;
+ if (!deleteFileNowOrLater(target, &errStr)) {
+ setError(UserDefinedError);
+ setErrorString(QObject::tr("Failed to overwrite %1: %2").arg(target, errStr));
+ undoOperation();
+ return false;
+ }
+
+ }
+
+ // copy the file to its new location
+ QFile cf(source);
+ if (!cf.copy(target)) {
+ setError(UserDefinedError);
+ setErrorString(QObject::tr("Failed to copy file %1: %2").arg(target, cf.errorString()));
+ undoOperation();
+ return false;
+ }
+ deleteFileNowOrLater(source);
+ files.push_back(source);
+ files.push_back(target);
+ setValue(QLatin1String("files"), files);
+ } else if (fi.isDir() && !QDir(target).exists()) {
+ if (!QDir().mkpath(target)) {
+ setErrorString(QObject::tr("Could not create folder at %1: %2").arg(target, qt_error_string()));
+ undoOperation();
+ return false;
+ }
+ createdDirectories.push_front(target);
+ setValue(QLatin1String("createddirectories"), createdDirectories);
+ }
+ }
+
+ // this should work now if not, it's not _that_ problematic...
+ try {
+ removeDirectory(source);
+ } catch(...) {
+ }
+ return true;
+}
+
+bool InstallIconsOperation::undoOperation()
+{
+ bool success = true;
+
+ // first copy back all files to their origin
+ const QStringList files = value(QLatin1String("files")).toStringList();
+ for (QStringList::const_iterator it = files.begin(); it != files.end(); it += 2) {
+ qApp->processEvents();
+
+ const QString& source = *it;
+ const QString& target = *(it + 1);
+
+ // first make sure the "source" path is valid
+ QDir().mkpath(QFileInfo(source).absolutePath());
+
+ // now copy target to source (feels weird, I know...)
+ success = QFile::copy(target, source) && success;
+ // and remove target
+ success = QFile::remove(target) && success;
+ }
+
+ // then copy back and remove all backuped files
+ const QStringList backupFiles = value(QLatin1String("backupfiles")).toStringList();
+ for (QStringList::const_iterator it = backupFiles.begin(); it != backupFiles.end(); it += 2) {
+ const QString& target = *it;
+ const QString& backup = *(it + 1);
+
+ // remove the target
+ if (QFile::exists(target))
+ success = deleteFileNowOrLater(target) && success;
+ // then copy the backup onto the target
+ success = QFile::copy(backup, target) && success;
+ // finally remove the backp
+ success = deleteFileNowOrLater(backup) && success;
+ }
+
+ // then remove all directories created by us
+ const QStringList createdDirectories = value(QLatin1String("createddirectories")).toStringList();
+ for (QStringList::const_iterator it = createdDirectories.begin(); it != createdDirectories.end(); ++it) {
+ const QDir dir(*it);
+ removeSystemGeneratedFiles(dir.absolutePath());
+ success = QDir::root().rmdir(dir.path());
+ }
+
+ return success;
+}
+
+bool InstallIconsOperation::testOperation()
+{
+ return true;
+}
+
+Operation *InstallIconsOperation::clone() const
+{
+ return new InstallIconsOperation();
+}
diff --git a/src/libs/installer/installiconsoperation.h b/src/libs/installer/installiconsoperation.h
new file mode 100644
index 000000000..52faaabc7
--- /dev/null
+++ b/src/libs/installer/installiconsoperation.h
@@ -0,0 +1,64 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef INSTALLICONSOPERATION_H
+#define INSTALLICONSOPERATION_H
+
+#include "qinstallerglobal.h"
+
+#include <QtCore/QObject>
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT InstallIconsOperation : public QObject, public Operation
+{
+ Q_OBJECT
+public:
+ InstallIconsOperation();
+ ~InstallIconsOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+
+Q_SIGNALS:
+ void outputTextChanged(const QString &progress);
+
+private:
+ QString targetDirectory();
+};
+
+}
+
+#endif
diff --git a/src/libs/installer/lazyplaintextedit.cpp b/src/libs/installer/lazyplaintextedit.cpp
new file mode 100644
index 000000000..b9b868775
--- /dev/null
+++ b/src/libs/installer/lazyplaintextedit.cpp
@@ -0,0 +1,85 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "lazyplaintextedit.h"
+
+#include <QScrollBar>
+
+const int INTERVAL = 20;
+
+LazyPlainTextEdit::LazyPlainTextEdit(QWidget *parent) :
+ QPlainTextEdit(parent), m_timerId(0)
+{
+}
+
+void LazyPlainTextEdit::timerEvent(QTimerEvent *event)
+{
+ if (event->timerId() == m_timerId) {
+ killTimer(m_timerId);
+ m_timerId = 0;
+ m_chachedOutput.chop(1); //removes the last \n
+ if (!m_chachedOutput.isEmpty())
+ appendPlainText(m_chachedOutput);
+ horizontalScrollBar()->setValue( 0 );
+ m_chachedOutput.clear();
+ }
+}
+
+void LazyPlainTextEdit::append(const QString &text)
+{
+ m_chachedOutput.append(text + QLatin1String("\n"));
+ if (isVisible() && m_timerId == 0)
+ m_timerId = startTimer(INTERVAL);
+}
+
+void LazyPlainTextEdit::clear()
+{
+ if (m_timerId) {
+ killTimer(m_timerId);
+ m_timerId = 0;
+ m_chachedOutput.clear();
+ }
+ QPlainTextEdit::clear();
+}
+
+
+void LazyPlainTextEdit::setVisible(bool visible)
+{
+ if (m_timerId) {
+ killTimer(m_timerId);
+ m_timerId = 0;
+ }
+ if (visible) {
+ m_timerId = startTimer(INTERVAL);
+ }
+ QPlainTextEdit::setVisible(visible);
+}
diff --git a/src/libs/installer/lazyplaintextedit.h b/src/libs/installer/lazyplaintextedit.h
new file mode 100644
index 000000000..40477deaf
--- /dev/null
+++ b/src/libs/installer/lazyplaintextedit.h
@@ -0,0 +1,55 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef LAZYPLAINTEXTEDIT_H
+#define LAZYPLAINTEXTEDIT_H
+
+#include <QPlainTextEdit>
+
+class LazyPlainTextEdit : public QPlainTextEdit
+{
+ Q_OBJECT
+public:
+ explicit LazyPlainTextEdit(QWidget *parent = 0);
+
+public slots:
+ void append(const QString &text);
+ virtual void clear();
+ virtual void setVisible ( bool visible );
+protected:
+ void timerEvent(QTimerEvent *event);
+private:
+ QString m_chachedOutput;
+ int m_timerId;
+};
+
+#endif // LAZYPLAINTEXTEDIT_H
diff --git a/src/libs/installer/lib7z_facade.cpp b/src/libs/installer/lib7z_facade.cpp
new file mode 100644
index 000000000..c679572fd
--- /dev/null
+++ b/src/libs/installer/lib7z_facade.cpp
@@ -0,0 +1,1372 @@
+#include "lib7z_facade.h"
+
+#include "errors.h"
+#include "fileutils.h"
+
+#ifndef Q_OS_WIN
+# include "StdAfx.h"
+#endif
+
+#include "Common/MyInitGuid.h"
+
+#include "Common/CommandLineParser.h"
+#include "Common/IntToString.h"
+#include "Common/MyException.h"
+#include "Common/StdOutStream.h"
+#include "Common/StringConvert.h"
+#include "Common/StringToInt.h"
+
+#include "Windows/Defs.h"
+#include "Windows/Error.h"
+#include "Windows/FileDir.h"
+#include "Windows/FileName.h"
+
+#include "7zip/ICoder.h"
+#include "7zip/IPassword.h"
+
+#include "7zip/UI/Common/ArchiveCommandLine.h"
+#include "7zip/UI/Common/ExitCode.h"
+#include "7zip/UI/Common/Extract.h"
+#include "7zip/UI/Common/Update.h"
+#include "7zip/UI/Common/ArchiveExtractCallback.h"
+#include "7zip/UI/Common/LoadCodecs.h"
+#include "7zip/UI/Common/PropIDUtils.h"
+
+#include "Windows/Defs.h"
+#include "Windows/Error.h"
+#include "Windows/FileDir.h"
+#include "Windows/FileName.h"
+#include "Windows/PropVariant.h"
+#include "Windows/PropVariantConversions.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+#include <QtCore/QIODevice>
+#include <QtCore/QMutex>
+#include <QtCore/QMutexLocker>
+#include <QtCore/QPointer>
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+#include <QtCore/QTemporaryFile>
+
+#ifdef _MSC_VER
+#pragma warning(disable:4297)
+#endif
+
+#ifdef Q_OS_WIN
+
+#include <time.h>
+#define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000 /* trick for Unix */
+#define S_IFMT 00170000
+#define S_IFLNK 0120000
+#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+
+typedef BOOL (WINAPI *CREATEHARDLINK)(LPCSTR dst, LPCSTR str, LPSECURITY_ATTRIBUTES sa);
+
+bool CreateHardLinkWrapper(const QString &dest, const QString &file)
+{
+ static HMODULE module = 0;
+ static CREATEHARDLINK proc = 0;
+
+ if (module == 0)
+ module = LoadLibrary(L"kernel32.dll");
+ if (module == 0)
+ return false;
+ if (proc == 0)
+ proc = (CREATEHARDLINK) GetProcAddress(module, "CreateHardLinkA");
+ if (proc == 0)
+ return false;
+ QString target = file;
+ if (!QFileInfo(file).isAbsolute())
+ {
+ target = QFileInfo(dest).dir().absoluteFilePath(file);
+ }
+ const QString link = QDir::toNativeSeparators(dest);
+ const QString existingFile = QDir::toNativeSeparators(target);
+ return proc(link.toLocal8Bit(), existingFile.toLocal8Bit(), 0);
+}
+
+#else
+#include <sys/stat.h>
+#endif
+
+#include <iostream>
+#include <memory>
+
+#include <cassert>
+
+using namespace Lib7z;
+using namespace NWindows;
+
+namespace {
+/**
+* RAII class to create a directory (tryCreate()) and delete it on destruction unless released.
+*/
+struct DirectoryGuard {
+ explicit DirectoryGuard(const QString &path)
+ : m_path(path),
+ m_created(false),
+ m_released(false)
+ {
+ const QRegExp re(QLatin1String("\\\\|/"));
+ const QLatin1String sep("/");
+ m_path.replace(re, sep);
+ }
+
+ ~DirectoryGuard()
+ {
+ if (!m_created || m_released)
+ return;
+ QDir dir(m_path);
+ if (!dir.rmdir(m_path))
+ qWarning() << "Could not delete directory " << m_path;
+ }
+
+ /**
+ * Tries to create the directorie structure.
+ * Returns a list of every directory created.
+ */
+ QStringList tryCreate() {
+ if (m_path.isEmpty())
+ return QStringList();
+
+ const QFileInfo fi(m_path);
+ if (fi.exists() && fi.isDir())
+ return QStringList();
+ if (fi.exists() && !fi.isDir())
+ throw SevenZipException(QObject::tr("Path exists but is not a folder: %1").arg(m_path));
+
+ QStringList created;
+
+ QDir toCreate(m_path);
+ while (!toCreate.exists())
+ {
+ QString p = toCreate.absolutePath();
+ created.push_front(p);
+ p = p.section(QLatin1Char('/'), 0, -2);
+ toCreate = QDir(p);
+ }
+
+ QDir dir(m_path);
+ m_created = dir.mkpath(m_path);
+ if (!m_created)
+ throw SevenZipException(QObject::tr("Could not create folder: %1").arg(m_path));
+
+ return created;
+ }
+
+ void release() {
+ m_released = true;
+ }
+
+ QString m_path;
+ bool m_created;
+ bool m_released;
+};
+}
+
+static void throwIfNotOK(HRESULT result, const QString &msg)
+{
+ if (result != S_OK)
+ throw SevenZipException(msg);
+}
+
+static UString QString2UString(const QString &str)
+{
+ return str.toStdWString().c_str();
+}
+
+static QString UString2QString(const UString& str)
+{
+ return QString::fromStdWString(static_cast<const wchar_t*>(str));
+}
+
+static QString generateTempFileName()
+{
+ QTemporaryFile tmp;
+ if (!tmp.open())
+ throw SevenZipException(QObject::tr("Could not create temporary file"));
+ return QDir::toNativeSeparators(tmp.fileName());
+}
+
+/*
+static QStringList UStringVector2QStringList(const UStringVector& vec)
+{
+QStringList res;
+for (int i = 0; i < vec.Size(); ++i)
+res += UString2QString(vec[i]);
+return res;
+}
+*/
+
+static NCOM::CPropVariant readProperty(IInArchive* archive, int index, int propId)
+{
+ NCOM::CPropVariant prop;
+ throwIfNotOK(archive->GetProperty(index, propId, &prop), QObject::tr("Could not retrieve property %1 "
+ "for item %2").arg(QString::number(propId), QString::number(index)));
+ return prop;
+}
+
+static bool IsFileTimeZero(const FILETIME *lpFileTime)
+{
+ return (lpFileTime->dwLowDateTime == 0) && (lpFileTime->dwHighDateTime == 0);
+}
+
+static bool IsDST(const QDateTime& datetime = QDateTime())
+{
+ const time_t seconds = static_cast< time_t >(datetime.isValid() ? datetime.toTime_t()
+ : QDateTime::currentDateTime().toTime_t());
+ const tm* const t = localtime(&seconds);
+ return t->tm_isdst;
+}
+
+static
+ QDateTime getDateTimeProperty(IInArchive* archive, int index, int propId, const QDateTime& defaultValue)
+{
+ const NCOM::CPropVariant prop = readProperty(archive, index, propId);
+ if (prop.vt != VT_FILETIME) {
+ throw SevenZipException(QObject::tr("Property %1 for item %2 not of type VT_FILETIME but %3")
+ .arg(QString::number(propId), QString::number(index), QString::number(prop.vt)));
+ }
+ if (IsFileTimeZero(&prop.filetime))
+ return defaultValue;
+
+ FILETIME localFileTime;
+ if (!FileTimeToLocalFileTime(&prop.filetime, &localFileTime))
+ throw SevenZipException(QObject::tr("Could not convert file time to local time"));
+
+ SYSTEMTIME st;
+ if (!BOOLToBool(FileTimeToSystemTime(&localFileTime, &st)))
+ throw SevenZipException(QObject::tr("Could not convert local file time to system time"));
+
+ const QDate date(st.wYear, st.wMonth, st.wDay);
+ const QTime time(st.wHour, st.wMinute, st.wSecond);
+ QDateTime result(date, time);
+
+ // fix daylight saving time
+ const bool dst = IsDST();
+ if (dst != IsDST(result))
+ result = result.addSecs(dst ? -3600 : 3600);
+
+ return result;
+}
+
+static quint64 getUInt64Property(IInArchive* archive, int index, int propId, quint64 defaultValue=0)
+{
+ const NCOM::CPropVariant prop = readProperty(archive, index, propId);
+ if (prop.vt == VT_EMPTY)
+ return defaultValue;
+ return static_cast<quint64>(ConvertPropVariantToUInt64(prop));
+}
+
+static quint32 getUInt32Property(IInArchive* archive, int index, int propId, quint32 defaultValue=0)
+{
+ const NCOM::CPropVariant prop = readProperty(archive, index, propId);
+ if (prop.vt == VT_EMPTY)
+ return defaultValue;
+ return static_cast< quint32 >(prop.ulVal);
+}
+
+static QFile::Permissions getPermissions(IInArchive* archive, int index, bool* hasPermissions = 0)
+{
+ quint32 attributes = getUInt32Property(archive, index, kpidAttrib, 0);
+ QFile::Permissions permissions = 0;
+ if (attributes & FILE_ATTRIBUTE_UNIX_EXTENSION) {
+ if (hasPermissions != 0)
+ *hasPermissions = true;
+ // filter the unix permissions
+ attributes = (attributes >> 16) & 0777;
+ permissions |= static_cast< QFile::Permissions >((attributes & 0700) << 2); // owner rights
+ permissions |= static_cast< QFile::Permissions >((attributes & 0070) << 1); // group
+ permissions |= static_cast< QFile::Permissions >((attributes & 0007) << 0); // and world rights
+ } else if (hasPermissions != 0) {
+ *hasPermissions = false;
+ }
+ return permissions;
+}
+
+namespace Lib7z {
+class QIODeviceSequentialOutStream : public ISequentialOutStream, public CMyUnknownImp
+{
+public:
+ MY_UNKNOWN_IMP
+ explicit QIODeviceSequentialOutStream(QIODevice* device);
+ ~QIODeviceSequentialOutStream();
+
+ /* reimp */ STDMETHOD(Write)(const void* data, UInt32 size, UInt32* processedSize);
+
+private:
+ QPointer<QIODevice> m_device;
+ const bool closeOnDestroy;
+};
+
+QIODeviceSequentialOutStream::QIODeviceSequentialOutStream(QIODevice* device)
+ : ISequentialOutStream(),
+ CMyUnknownImp(),
+ m_device(device),
+ closeOnDestroy(!device->isOpen())
+{
+ assert(m_device);
+ if (closeOnDestroy)
+ m_device->open(QIODevice::WriteOnly);
+}
+
+QIODeviceSequentialOutStream::~QIODeviceSequentialOutStream()
+{
+ if (closeOnDestroy)
+ {
+ m_device->close();
+ delete m_device;
+ }
+}
+
+HRESULT QIODeviceSequentialOutStream::Write(const void* data, UInt32 size, UInt32* processedSize)
+{
+ if (!m_device) {
+ if (processedSize)
+ *processedSize = 0;
+ return E_FAIL;
+ }
+ if (closeOnDestroy && !m_device->isOpen()) {
+ const bool opened = m_device->open(QIODevice::WriteOnly);
+ if (!opened) {
+ if (processedSize)
+ *processedSize = 0;
+ return E_FAIL;
+ }
+ }
+
+ const qint64 written = m_device->write(reinterpret_cast<const char*>(data), size);
+ if (processedSize)
+ *processedSize = written;
+ return written >= 0 ? S_OK : E_FAIL;
+}
+
+class QIODeviceInStream : public IInStream, public CMyUnknownImp
+{
+public:
+ MY_UNKNOWN_IMP
+
+ explicit QIODeviceInStream(QIODevice* device) : IInStream(), CMyUnknownImp(), m_device(device)
+ {
+ assert(m_device);
+ assert(!m_device->isSequential());
+ }
+
+ /* reimp */ STDMETHOD(Read)(void* data, UInt32 size, UInt32* processedSize)
+ {
+ assert(m_device);
+ assert(m_device->isReadable());
+ const qint64 actual = m_device->read(reinterpret_cast<char*>(data), size);
+ Q_ASSERT(actual != 0 || m_device->atEnd());
+ if (processedSize)
+ *processedSize = actual;
+ return actual >= 0 ? S_OK : E_FAIL;
+ }
+
+ /* reimp */ STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64* newPosition)
+ {
+ assert(m_device);
+ assert(!m_device->isSequential());
+ assert(m_device->isReadable());
+ if (seekOrigin > STREAM_SEEK_END)
+ return STG_E_INVALIDFUNCTION;
+ UInt64 np = 0;
+ switch(seekOrigin) {
+ case STREAM_SEEK_SET:
+ np = offset;
+ break;
+ case STREAM_SEEK_CUR:
+ np = m_device->pos() + offset;
+ break;
+ case STREAM_SEEK_END:
+ np = m_device->size() + offset;
+ break;
+ default:
+ return STG_E_INVALIDFUNCTION;
+ }
+
+ np = qBound(static_cast<UInt64>(0), np, static_cast<UInt64>(m_device->size() - 1));
+ const bool ok = m_device->seek(np);
+ if (newPosition)
+ *newPosition = np;
+ return ok ? S_OK : E_FAIL;
+ }
+
+private:
+ QPointer<QIODevice> m_device;
+};
+}
+
+File::File()
+ : permissions(0)
+ , uncompressedSize(0)
+ , compressedSize(0)
+ , isDirectory(false)
+{
+}
+
+QVector<File> File::subtreeInPreorder() const
+{
+ QVector<File> res;
+ res += *this;
+ Q_FOREACH (const File& child, children)
+ res += child.subtreeInPreorder();
+ return res;
+}
+
+bool File::operator<(const File& other) const
+{
+ if (path != other.path)
+ return path < other.path;
+ if (mtime != other.mtime)
+ return mtime < other.mtime;
+ if (uncompressedSize != other.uncompressedSize)
+ return uncompressedSize < other.uncompressedSize;
+ if (compressedSize != other.compressedSize)
+ return compressedSize < other.compressedSize;
+ if (isDirectory != other.isDirectory)
+ return !isDirectory;
+ if (permissions != other.permissions)
+ return permissions < other.permissions;
+ return false;
+}
+
+bool File::operator==(const File& other) const
+{
+ return mtime == other.mtime
+ && path == other.path
+ && uncompressedSize == other.uncompressedSize
+ && compressedSize == other.compressedSize
+ && isDirectory == other.isDirectory
+ && children == other.children
+ && (permissions == other.permissions
+ || permissions == static_cast< QFile::Permissions >(-1)
+ || other.permissions == static_cast< QFile::Permissions >(-1));
+}
+
+QByteArray Lib7z::formatKeyValuePairs(const QVariantList& l)
+{
+ assert(l.size() % 2 == 0);
+ QByteArray res;
+ for (QVariantList::ConstIterator it = l.constBegin(); it != l.constEnd(); ++it) {
+ if (!res.isEmpty())
+ res += ", ";
+ res += qPrintable(it->toString()) + QByteArray(" = ");
+ ++it;
+ res += qPrintable(it->toString());
+ }
+ return res;
+}
+
+class Job::Private
+{
+public:
+ Private()
+ : error(Lib7z::NoError)
+ {}
+
+ int error;
+ QString errorString;
+};
+
+Job::Job(QObject* parent)
+ : QObject(parent)
+ , QRunnable()
+ , d(new Private)
+{
+}
+
+Job::~Job()
+{
+ delete d;
+}
+
+void Job::emitResult()
+{
+ emit finished(this);
+}
+
+void Job::setError(int code)
+{
+ d->error = code;
+}
+
+void Job::setErrorString(const QString &str)
+{
+ d->errorString = str;
+}
+
+void Job::emitProgress(qint64 completed, qint64 total)
+{
+ emit progress(completed, total);
+}
+
+int Job::error() const
+{
+ return d->error;
+}
+
+bool Job::hasError() const
+{
+ return d->error != NoError;
+}
+
+void Job::run()
+{
+ doStart();
+}
+
+QString Job::errorString() const
+{
+ return d->errorString;
+}
+
+void Job::start()
+{
+ QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection);
+}
+
+class ListArchiveJob::Private
+{
+public:
+ QPointer<QIODevice> archive;
+ QVector<File> files;
+};
+
+ListArchiveJob::ListArchiveJob(QObject* parent)
+ : Job(parent)
+ , d(new Private)
+{
+}
+
+ListArchiveJob::~ListArchiveJob()
+{
+ delete d;
+}
+
+QIODevice* ListArchiveJob::archive() const
+{
+ return d->archive;
+}
+
+void ListArchiveJob::setArchive(QIODevice* device)
+{
+ d->archive = device;
+}
+
+QVector<File> ListArchiveJob::index() const
+{
+ return d->files;
+}
+
+class OpenArchiveInfo
+{
+private:
+ OpenArchiveInfo(QIODevice* device)
+ : codecs(new CCodecs)
+ {
+ throwIfNotOK(codecs->Load(), QObject::tr("Could not load codecs"));
+
+ if (!codecs->FindFormatForArchiveType(L"", formatIndices))
+ throw SevenZipException(QObject::tr("Could not retrieve default format"));
+
+ stream = new QIODeviceInStream(device);
+ throwIfNotOK(archiveLink.Open2(codecs.data(), formatIndices, false, stream, UString(), 0),
+ QObject::tr("Could not open archive"));
+ if (archiveLink.Arcs.Size() == 0)
+ throw SevenZipException(QObject::tr("No CArc found"));
+
+ m_cleaner = new OpenArchiveInfoCleaner();
+ m_cleaner->moveToThread(device->thread());
+ QObject::connect(device, SIGNAL(destroyed(QObject*)), m_cleaner, SLOT(deviceDestroyed(QObject*)));
+ }
+
+public:
+ ~OpenArchiveInfo()
+ {
+ m_cleaner->deleteLater();
+ }
+
+ static OpenArchiveInfo* value(QIODevice* device)
+ {
+ QMutexLocker _(&m_mutex);
+ if (!instances.contains(device))
+ instances.insert(device, new OpenArchiveInfo(device));
+ return instances.value(device);
+ }
+
+ static OpenArchiveInfo* take(QIODevice *device)
+ {
+ QMutexLocker _(&m_mutex);
+ if (instances.contains(device))
+ return instances.take(device);
+ return 0;
+ }
+
+ CArchiveLink archiveLink;
+
+private:
+ CIntVector formatIndices;
+ CMyComPtr<IInStream> stream;
+ QScopedPointer<CCodecs> codecs;
+ OpenArchiveInfoCleaner *m_cleaner;
+
+ static QMutex m_mutex;
+ static QMap< QIODevice*, OpenArchiveInfo* > instances;
+};
+
+QMutex OpenArchiveInfo::m_mutex;
+QMap< QIODevice*, OpenArchiveInfo* > OpenArchiveInfo::instances;
+
+void OpenArchiveInfoCleaner::deviceDestroyed(QObject* dev)
+{
+ QIODevice* device = static_cast<QIODevice*>(dev);
+ Q_ASSERT(device);
+ delete OpenArchiveInfo::take(device);
+}
+
+QVector<File> Lib7z::listArchive(QIODevice* archive)
+{
+ assert(archive);
+ try {
+ const OpenArchiveInfo* const openArchive = OpenArchiveInfo::value(archive);
+
+ QVector<File> flat;
+
+ for (int i = 0; i < openArchive->archiveLink.Arcs.Size(); ++i) {
+ const CArc& arc = openArchive->archiveLink.Arcs[i];
+ IInArchive* const arch = arc.Archive;
+
+ UInt32 numItems = 0;
+ throwIfNotOK(arch->GetNumberOfItems(&numItems), QObject::tr("Could not retrieve number of items "
+ "in archive"));
+
+ flat.reserve(flat.size() + numItems);
+ for (uint item = 0; item < numItems; ++item) {
+ UString s;
+ throwIfNotOK(arc.GetItemPath(item, s), QObject::tr("Could not retrieve path of archive item "
+ "%1").arg(item));
+ File f;
+ f.archiveIndex.setX(i);
+ f.archiveIndex.setY(item);
+ f.path = UString2QString(s).replace(QLatin1Char('\\'), QLatin1Char('/'));
+ IsArchiveItemFolder(arch, item, f.isDirectory);
+ f.permissions = getPermissions(arch, item);
+ f.mtime = getDateTimeProperty(arch, item, kpidMTime, QDateTime());
+ f.uncompressedSize = getUInt64Property(arch, item, kpidSize, 0);
+ f.compressedSize = getUInt64Property(arch, item, kpidPackSize, 0);
+ flat.push_back(f);
+ qApp->processEvents();
+ }
+ }
+ return flat;
+ } catch (const SevenZipException& e) {
+ throw e;
+ } catch (const char *err) {
+ throw SevenZipException(err);
+ } catch (...) {
+ throw SevenZipException(QObject::tr("Unknown exception caught (%1)")
+ .arg(QString::fromLatin1(Q_FUNC_INFO)));
+ }
+ return QVector<File>(); // never reached
+}
+
+void ListArchiveJob::doStart()
+{
+ try {
+ if (!d->archive)
+ throw SevenZipException(tr("Could not list archive: QIODevice already destroyed."));
+ d->files = listArchive(d->archive);
+ } catch (const SevenZipException& e) {
+ setError(Failed);
+ setErrorString(e.message());
+ } catch (...) {
+ setError(Failed);
+ setErrorString(QObject::tr("Unknown exception caught (%1)").arg(QObject::tr("Failed")));
+ }
+ emitResult();
+}
+
+class Lib7z::ExtractCallbackImpl : public IArchiveExtractCallback, public CMyUnknownImp
+{
+public:
+ MY_UNKNOWN_IMP
+ explicit ExtractCallbackImpl(ExtractCallback* qq)
+ : q(qq),
+ currentIndex(0),
+ arc(0),
+ total(0),
+ completed(0),
+ device(0)
+ {
+ }
+
+ void setTarget(QIODevice* dev)
+ {
+ device = dev;
+ }
+
+ void setTarget(const QString &targetDirectory)
+ {
+ targetDir = targetDirectory;
+ }
+
+ // this method will be called by CFolderOutStream::OpenFile to stream via
+ // CDecoder::CodeSpec extracted content to an output stream.
+ /* reimp */ STDMETHOD(GetStream)(UInt32 index, ISequentialOutStream** outStream, Int32 askExtractMode)
+ {
+ Q_UNUSED(askExtractMode)
+ *outStream = 0;
+ if (device != 0) {
+ CMyComPtr<ISequentialOutStream> stream = new QIODeviceSequentialOutStream(device);
+ *outStream = stream.Detach();
+ return S_OK;
+ } else if (!targetDir.isEmpty()) {
+ assert(arc);
+
+ currentIndex = index;
+
+ UString s;
+ throwIfNotOK(arc->GetItemPath(index, s), QObject::tr("Could not retrieve path of archive item "
+ "%1").arg(index));
+ const QString path = UString2QString(s).replace(QLatin1Char('\\'), QLatin1Char('/'));
+
+ const QFileInfo fi(QString::fromLatin1("%1/%2").arg(targetDir, path));
+ DirectoryGuard guard(fi.absolutePath());
+ const QStringList directories = guard.tryCreate();
+
+ bool isDir = false;
+ IsArchiveItemFolder(arc->Archive, index, isDir);
+ if (isDir)
+ QDir(fi.absolutePath()).mkdir(fi.fileName());
+
+ // this makes sure that all directories created get removed as well
+ for (QStringList::const_iterator it = directories.begin(); it != directories.end(); ++it)
+ q->setCurrentFile(*it);
+
+ if (!isDir && !q->prepareForFile(fi.absoluteFilePath()))
+ return E_FAIL;
+
+ q->setCurrentFile(fi.absoluteFilePath());
+
+ if (!isDir) {
+ CMyComPtr< ISequentialOutStream > stream = new QIODeviceSequentialOutStream(new QFile(fi
+ .absoluteFilePath()));
+ *outStream = stream;
+ stream.Detach();
+ }
+
+ guard.release();
+ return S_OK;
+ }
+ return E_FAIL;
+ }
+
+ /* reimp */ STDMETHOD(PrepareOperation)(Int32 askExtractMode)
+ {
+ Q_UNUSED(askExtractMode)
+ return S_OK;
+ }
+
+ /* reimp */ STDMETHOD(SetOperationResult)(Int32 resultEOperationResult)
+ {
+ Q_UNUSED(resultEOperationResult)
+
+ if (!targetDir.isEmpty()) {
+ bool hasPerm;
+ const QFile::Permissions permissions = getPermissions(arc->Archive, currentIndex, &hasPerm);
+ if (hasPerm) {
+ UString s;
+ throwIfNotOK(arc->GetItemPath(currentIndex, s), QObject::tr("Could not retrieve path of "
+ "archive item %1").arg(currentIndex));
+ const QString path = UString2QString(s).replace(QLatin1Char('\\'), QLatin1Char('/'));
+ const QFileInfo fi(QString::fromLatin1("%1/%2").arg(targetDir, path));
+ QFile::setPermissions(fi.absoluteFilePath(), permissions);
+
+ // do we have a symlink?
+ const quint32 attributes = getUInt32Property(arc->Archive, currentIndex, kpidAttrib, 0);
+ struct stat stat_info;
+ stat_info.st_mode = attributes >> 16;
+ if (S_ISLNK(stat_info.st_mode)) {
+ QFile f(fi.absoluteFilePath());
+ f.open(QIODevice::ReadOnly);
+ const QByteArray path = f.readAll();
+ f.close();
+ f.remove();
+#ifdef Q_OS_WIN
+ if (!CreateHardLinkWrapper(fi.absoluteFilePath(), QLatin1String(path))) {
+ throw SevenZipException(QObject::tr("Could not create file system link at %1")
+ .arg(fi.absoluteFilePath()));
+ }
+#else
+ if (!QFile::link(QString::fromLatin1(path), fi.absoluteFilePath())) {
+ throw SevenZipException(QObject::tr("Could not create softlink at %1")
+ .arg(fi.absoluteFilePath()));
+ }
+#endif
+ }
+ }
+ }
+
+ return S_OK;
+ }
+
+ /* reimp */ STDMETHOD(SetTotal)(UInt64 t)
+ {
+ total = t;
+ return S_OK;
+ }
+
+ /* reimp */ STDMETHOD(SetCompleted)(const UInt64* c)
+ {
+ completed = *c;
+ if (total > 0)
+ return q->setCompleted(completed, total);
+ return S_OK;
+ }
+
+ void setArchive(const CArc* archive)
+ {
+ arc = archive;
+ }
+
+private:
+ ExtractCallback* const q;
+ UInt32 currentIndex;
+ const CArc* arc;
+ UInt64 total;
+ UInt64 completed;
+ QPointer<QIODevice> device;
+ QString targetDir;
+};
+
+
+class Lib7z::ExtractCallbackPrivate
+{
+public:
+ explicit ExtractCallbackPrivate(ExtractCallback* qq)
+ {
+ impl = new ExtractCallbackImpl(qq);
+ }
+
+ CMyComPtr<ExtractCallbackImpl> impl;
+};
+
+ExtractCallback::ExtractCallback()
+ : d(new ExtractCallbackPrivate(this))
+{
+}
+
+ExtractCallback::~ExtractCallback()
+{
+ delete d;
+}
+
+ExtractCallbackImpl* ExtractCallback::impl()
+{
+ return d->impl;
+}
+
+const ExtractCallbackImpl* ExtractCallback::impl() const
+{
+ return d->impl;
+}
+
+void ExtractCallback::setTarget(QIODevice* dev)
+{
+ d->impl->setTarget(dev);
+}
+
+void ExtractCallback::setTarget(const QString &dir)
+{
+ d->impl->setTarget(dir);
+}
+
+HRESULT ExtractCallback::setCompleted(quint64, quint64)
+{
+ return S_OK;
+}
+
+void ExtractCallback::setCurrentFile(const QString&)
+{
+}
+
+bool ExtractCallback::prepareForFile(const QString&)
+{
+ return true;
+}
+
+class Lib7z::ExtractCallbackJobImpl : public ExtractCallback
+{
+public:
+ explicit ExtractCallbackJobImpl(ExtractItemJob* j)
+ : ExtractCallback()
+ , job(j)
+ {}
+
+private:
+ /* reimp */ HRESULT setCompleted(quint64 c, quint64 t)
+ {
+ emit job->progress(c, t);
+ return S_OK;
+ }
+
+ ExtractItemJob* const job;
+};
+
+class Lib7z::UpdateCallbackImpl : public IUpdateCallbackUI2, public CMyUnknownImp
+{
+public:
+ MY_UNKNOWN_IMP
+ explicit UpdateCallbackImpl(UpdateCallback* qq)
+ : q(qq)
+ {
+ }
+ virtual ~UpdateCallbackImpl()
+ {
+ }
+ /**
+ * \reimp
+ */
+ HRESULT SetTotal(UInt64)
+ {
+ return S_OK;
+ }
+ /**
+ * \reimp
+ */
+ HRESULT SetCompleted(const UInt64*)
+ {
+ return S_OK;
+ }
+ HRESULT SetRatioInfo(const UInt64*, const UInt64*)
+ {
+ return S_OK;
+ }
+ HRESULT CheckBreak()
+ {
+ return S_OK;
+ }
+ HRESULT Finilize()
+ {
+ return S_OK;
+ }
+ HRESULT SetNumFiles(UInt64)
+ {
+ return S_OK;
+ }
+ HRESULT GetStream(const wchar_t*, bool)
+ {
+ return S_OK;
+ }
+ HRESULT OpenFileError(const wchar_t*, DWORD)
+ {
+ return S_OK;
+ }
+ HRESULT CryptoGetTextPassword2(Int32* passwordIsDefined, OLECHAR** password)
+ {
+ *password = 0;
+ *passwordIsDefined = false;
+ return S_OK;
+ }
+ HRESULT CryptoGetTextPassword(OLECHAR**)
+ {
+ return E_NOTIMPL;
+ }
+ HRESULT OpenResult(const wchar_t*, LONG)
+ {
+ return S_OK;
+ }
+ HRESULT StartScanning()
+ {
+ return S_OK;
+ }
+ HRESULT ScanProgress(UInt64, UInt64, const wchar_t*)
+ {
+ return S_OK;
+ }
+ HRESULT CanNotFindError(const wchar_t*, DWORD)
+ {
+ return S_OK;
+ }
+ HRESULT FinishScanning()
+ {
+ return S_OK;
+ }
+ HRESULT StartArchive(const wchar_t*, bool)
+ {
+ return S_OK;
+ }
+ HRESULT FinishArchive()
+ {
+ return S_OK;
+ }
+
+ /**
+ * \reimp
+ */
+ HRESULT SetOperationResult(Int32)
+ {
+ // TODO!
+ return S_OK;
+ }
+ void setSource(const QStringList &dir)
+ {
+ sourceDir = dir;
+ }
+ void setTarget(QIODevice* archive)
+ {
+ target = archive;
+ }
+
+private:
+ UpdateCallback* const q;
+
+ QIODevice* target;
+ QStringList sourceDir;
+};
+
+class Lib7z::UpdateCallbackPrivate
+{
+public:
+ explicit UpdateCallbackPrivate(UpdateCallback* qq)
+ {
+ m_impl = new UpdateCallbackImpl(qq);
+ }
+
+ UpdateCallbackImpl* impl()
+ {
+ return m_impl;
+ }
+
+private:
+ CMyComPtr< UpdateCallbackImpl > m_impl;
+};
+
+UpdateCallback::UpdateCallback()
+ : d(new UpdateCallbackPrivate(this))
+{
+}
+
+UpdateCallback::~UpdateCallback()
+{
+ delete d;
+}
+
+UpdateCallbackImpl* UpdateCallback::impl()
+{
+ return d->impl();
+}
+
+void UpdateCallback::setSource(const QStringList &dir)
+{
+ d->impl()->setSource(dir);
+}
+
+void UpdateCallback::setTarget(QIODevice* target)
+{
+ d->impl()->setTarget(target);
+}
+
+class ExtractItemJob::Private
+{
+public:
+ Private(ExtractItemJob* qq)
+ : q(qq),
+ target(0),
+ callback(new ExtractCallbackJobImpl(q))
+ {
+ }
+
+ ExtractItemJob* q;
+ File item;
+ QPointer<QIODevice> archive;
+ QString targetDirectory;
+ QIODevice* target;
+ ExtractCallback* callback;
+};
+
+ExtractItemJob::ExtractItemJob(QObject* parent)
+ : Job(parent)
+ , d(new Private(this))
+{
+}
+
+ExtractItemJob::~ExtractItemJob()
+{
+ delete d;
+}
+
+File ExtractItemJob::item() const
+{
+ return d->item;
+}
+
+void ExtractItemJob::setItem(const File& item)
+{
+ d->item = item;
+}
+
+QIODevice* ExtractItemJob::archive() const
+{
+ return d->archive;
+}
+
+void ExtractItemJob::setArchive(QIODevice* archive)
+{
+ d->archive = archive;
+}
+
+QString ExtractItemJob::targetDirectory() const
+{
+ return d->targetDirectory;
+}
+
+void ExtractItemJob::setTargetDirectory(const QString &dir)
+{
+ d->targetDirectory = dir;
+ d->target = 0;
+}
+
+void ExtractItemJob::setTarget(QIODevice* dev)
+{
+ d->target = dev;
+}
+
+void Lib7z::createArchive(QIODevice* archive, const QStringList &sourceDirectories, UpdateCallback* callback)
+{
+ assert(archive);
+
+ std::auto_ptr< UpdateCallback > dummyCallback(callback ? 0 : new UpdateCallback);
+ if (!callback)
+ callback = dummyCallback.get();
+
+ try
+ {
+ std::auto_ptr< CCodecs > codecs(new CCodecs);
+ throwIfNotOK(codecs->Load(), QObject::tr("Could not load codecs"));
+
+ CIntVector formatIndices;
+
+ if (!codecs.get()->FindFormatForArchiveType(L"", formatIndices))
+ throw SevenZipException(QObject::tr("Could not retrieve default format"));
+
+ // yes this is crap, but there seems to be no streaming solution to this...
+
+ const QString tempFile = generateTempFileName();
+
+ NWildcard::CCensor censor;
+ foreach(QString dir, sourceDirectories) {
+ const UString sourceDirectoryPath = QString2UString(QDir::toNativeSeparators(dir));
+ if (UString2QString(sourceDirectoryPath) != QDir::toNativeSeparators(dir))
+ throw UString2QString(sourceDirectoryPath).toLatin1().data();
+ censor.AddItem(true, sourceDirectoryPath, true);
+ }
+
+ CUpdateOptions options;
+ CArchivePath archivePath;
+ archivePath.ParseFromPath(QString2UString(tempFile));
+ CUpdateArchiveCommand command;
+ command.ArchivePath = archivePath;
+ command.ActionSet = NUpdateArchive::kAddActionSet;
+ options.Commands.Add(command);
+ options.ArchivePath = archivePath;
+ options.MethodMode.FormatIndex = codecs->FindFormatForArchiveType(L"7z");
+
+ CUpdateErrorInfo errorInfo;
+
+ callback->setTarget(archive);
+ callback->setSource(sourceDirectories);
+ const HRESULT res = UpdateArchive(codecs.get(), censor, options, errorInfo, 0, callback->impl());
+ if (res != S_OK || !QFile::exists(tempFile))
+ throw SevenZipException(QObject::tr("Could not create archive %1").arg(tempFile));
+ {
+ //TODO remove temp file even if one the following throws
+ QFile file(tempFile);
+ QInstaller::openForRead(&file, tempFile);
+ QInstaller::blockingCopy(&file, archive, file.size());
+ }
+ QFile file(tempFile);
+ if (!file.remove()) {
+ qWarning("%s: Could not remove temporary file %s: %s", Q_FUNC_INFO, qPrintable(tempFile),
+ qPrintable(file.errorString()));
+ }
+ } catch (const char *err) {
+ std::cout << err << std::endl;
+ throw SevenZipException(err);
+ } catch (const QInstaller::Error &err) {
+ throw SevenZipException(err.message());
+ } catch (...) {
+ throw SevenZipException(QObject::tr("Unknown exception caught (%1)")
+ .arg(QString::fromLatin1(Q_FUNC_INFO)));
+ }
+}
+
+void Lib7z::extractArchive(QIODevice* archive, const File& item, QIODevice* target, ExtractCallback* callback)
+{
+ assert(archive);
+ assert(target);
+
+ std::auto_ptr<ExtractCallback> dummyCallback(callback ? 0 : new ExtractCallback);
+ if (!callback)
+ callback = dummyCallback.get();
+
+ try {
+ const OpenArchiveInfo* const openArchive = OpenArchiveInfo::value(archive);
+
+ const int arcIdx = item.archiveIndex.x();
+ if (arcIdx < 0 || arcIdx >= openArchive->archiveLink.Arcs.Size()) {
+ throw SevenZipException(QObject::tr("CArc index %1 out of bounds [0, %2]")
+ .arg(openArchive->archiveLink.Arcs.Size() - 1));
+ }
+ const CArc& arc = openArchive->archiveLink.Arcs[arcIdx];
+ IInArchive* const parchive = arc.Archive;
+
+ const UInt32 itemIdx = item.archiveIndex.y();
+ UInt32 numItems = 0;
+ throwIfNotOK(parchive->GetNumberOfItems(&numItems), QObject::tr("Could not retrieve number of items "
+ "in archive"));
+ if (itemIdx >= numItems) {
+ throw SevenZipException(QObject::tr("Item index %1 out of bounds [0, %2]").arg(itemIdx)
+ .arg(numItems - 1));
+ }
+
+ UString s;
+ throwIfNotOK(arc.GetItemPath(itemIdx, s), QObject::tr("Could not retrieve path of archive item %1")
+ .arg(itemIdx));
+ assert(item.path == UString2QString(s).replace(QLatin1Char('\\'), QLatin1Char('/')));
+
+ callback->setTarget(target);
+ const LONG extractResult = parchive->Extract(&itemIdx, 1, /*testmode=*/1, callback->impl());
+ //TODO: how to interpret result?
+ throwIfNotOK(extractResult, QObject::tr("Extracting %1 failed.").arg(item.path));
+ } catch (const char *err) {
+ throw SevenZipException(err);
+ } catch (...) {
+ throw SevenZipException(QObject::tr("Unknown exception caught (%1)")
+ .arg(QString::fromLatin1(Q_FUNC_INFO)));
+ }
+}
+
+void Lib7z::extractArchive(QIODevice* archive, const File& item, const QString &targetDirectory,
+ ExtractCallback* callback)
+{
+ assert(archive);
+
+ std::auto_ptr<ExtractCallback> dummyCallback(callback ? 0 : new ExtractCallback);
+ if (!callback)
+ callback = dummyCallback.get();
+
+ QFileInfo fi(targetDirectory + QLatin1String("/") + item.path);
+ DirectoryGuard outDir(fi.absolutePath());
+ outDir.tryCreate();
+ QFile out(fi.absoluteFilePath());
+ if (!out.open(QIODevice::WriteOnly)) { //TODO use tmp file
+ throw SevenZipException(QObject::tr("Could not create output file for writing: %1")
+ .arg(fi.absoluteFilePath()));
+ }
+ if (item.permissions)
+ out.setPermissions(item.permissions);
+ callback->setTarget(&out);
+ extractArchive(archive, item, &out, callback);
+ outDir.release();
+}
+
+void Lib7z::extractArchive(QIODevice* archive, const QString &targetDirectory, ExtractCallback* callback)
+{
+ assert(archive);
+
+ std::auto_ptr<ExtractCallback> dummyCallback(callback ? 0 : new ExtractCallback);
+ if (!callback)
+ callback = dummyCallback.get();
+
+ callback->setTarget(targetDirectory);
+
+ const QFileInfo fi(targetDirectory);
+ DirectoryGuard outDir(fi.absolutePath());
+ outDir.tryCreate();
+
+ const OpenArchiveInfo* const openArchive = OpenArchiveInfo::value(archive);
+
+ for (int a = 0; a < openArchive->archiveLink.Arcs.Size(); ++a)
+ {
+ const CArc& arc = openArchive->archiveLink.Arcs[a];
+ IInArchive* const arch = arc.Archive;
+ callback->impl()->setArchive(&arc);
+ const LONG extractResult = arch->Extract(0, static_cast< UInt32 >(-1), false, callback->impl());
+ //TODO is it possible to get a more detailed error?
+ throwIfNotOK(extractResult, QObject::tr("Extraction failed."));
+ }
+
+ outDir.release();
+}
+
+bool Lib7z::isSupportedArchive(const QString &archive)
+{
+ QFile file(archive);
+ if (!file.open(QIODevice::ReadOnly))
+ return false;
+
+ return isSupportedArchive(&file);
+}
+
+bool Lib7z::isSupportedArchive(QIODevice* archive)
+{
+ assert(archive);
+ assert(!archive->isSequential());
+ const qint64 initialPos = archive->pos();
+ try {
+ std::auto_ptr<CCodecs> codecs(new CCodecs);
+ throwIfNotOK(codecs->Load(), QObject::tr("Could not load codecs"));
+
+ CIntVector formatIndices;
+
+ if (!codecs.get()->FindFormatForArchiveType(L"", formatIndices))
+ throw SevenZipException(QObject::tr("Could not retrieve default format"));
+
+ CArchiveLink archiveLink;
+ //CMyComPtr is needed, otherwise it crashes in OpenStream()
+ const CMyComPtr<IInStream> stream = new QIODeviceInStream(archive);
+
+ const HRESULT result = archiveLink.Open2(codecs.get(), formatIndices, /*stdInMode*/false, stream,
+ UString(), 0);
+
+ archive->seek(initialPos);
+ return result == S_OK;
+ } catch (const SevenZipException& e) {
+ archive->seek(initialPos);
+ throw e;
+ } catch (const char *err) {
+ archive->seek(initialPos);
+ throw SevenZipException(err);
+ } catch (...) {
+ archive->seek(initialPos);
+ throw SevenZipException(QObject::tr("Unknown exception caught (%1)")
+ .arg(QString::fromLatin1(Q_FUNC_INFO)));
+ }
+ return false; // never reached
+}
+
+void ExtractItemJob::doStart()
+{
+ try {
+ if (!d->archive)
+ throw SevenZipException(tr("Could not list archive: QIODevice not set or already destroyed."));
+ if (d->target)
+ extractArchive(d->archive, d->item, d->target, d->callback);
+ else if (!d->item.path.isEmpty())
+ extractArchive(d->archive, d->item, d->targetDirectory, d->callback);
+ else
+ extractArchive(d->archive, d->targetDirectory, d->callback);
+ } catch (const SevenZipException& e) {
+ setError(Failed);
+ setErrorString(e.message());
+ } catch (...) {
+ setError(Failed);
+ setErrorString(QObject::tr("Unknown exception caught (%1)").arg(QObject::tr("Failed")));
+ }
+ emitResult();
+}
diff --git a/src/libs/installer/lib7z_facade.h b/src/libs/installer/lib7z_facade.h
new file mode 100644
index 000000000..58774df11
--- /dev/null
+++ b/src/libs/installer/lib7z_facade.h
@@ -0,0 +1,242 @@
+#ifndef LIB7Z_FACADE_H
+#define LIB7Z_FACADE_H
+
+#include "installer_global.h"
+
+#include "Common/MyWindows.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDateTime>
+#include <QtCore/QFile>
+#include <QtCore/QPoint>
+#include <QtCore/QRunnable>
+#include <QtCore/QString>
+#include <QtCore/QVector>
+#include <QtCore/QVariant>
+
+#include <stdexcept>
+#include <string>
+
+QT_BEGIN_NAMESPACE
+class QStringList;
+template <typename T> class QVector;
+QT_END_NAMESPACE
+
+namespace Lib7z {
+
+ class INSTALLER_EXPORT SevenZipException : public std::runtime_error {
+ public:
+ explicit SevenZipException( const QString& msg ) : std::runtime_error( msg.toStdString() ), m_message( msg ) {}
+ explicit SevenZipException( const char* msg ) : std::runtime_error( msg ), m_message( QString::fromLocal8Bit( msg ) ) {}
+ explicit SevenZipException( const std::string& msg ) : std::runtime_error( msg ), m_message( QString::fromLocal8Bit( msg.c_str() ) ) {}
+
+ ~SevenZipException() throw() {}
+ QString message() const { return m_message; }
+ private:
+ QString m_message;
+ };
+
+ class INSTALLER_EXPORT File {
+ public:
+ File();
+ QVector<File> subtreeInPreorder() const;
+
+ bool operator<( const File& other ) const;
+ bool operator==( const File& other ) const;
+
+ QFile::Permissions permissions;
+ QString path;
+ QString name;
+ QDateTime mtime;
+ quint64 uncompressedSize;
+ quint64 compressedSize;
+ bool isDirectory;
+ QVector<File> children;
+ QPoint archiveIndex;
+ };
+
+ class ExtractCallbackPrivate;
+ class ExtractCallbackImpl;
+
+ class ExtractCallback {
+ friend class ::Lib7z::ExtractCallbackImpl;
+ public:
+ ExtractCallback();
+ virtual ~ExtractCallback();
+
+ void setTarget( QIODevice* archive );
+ void setTarget( const QString& dir );
+
+ protected:
+ /**
+ * Reimplement to prepare for file @p filename to be extracted, e.g. by renaming existing files.
+ * @return @p true if the preparation was successful and extraction can be continued.
+ * If @p false is returned, the extraction will be aborted. Default implementation returns @p true.
+ */
+ virtual bool prepareForFile( const QString& filename );
+ virtual void setCurrentFile( const QString& filename );
+ virtual HRESULT setCompleted( quint64 completed, quint64 total );
+
+ public: //for internal use
+ const ExtractCallbackImpl* impl() const;
+ ExtractCallbackImpl* impl();
+
+ private:
+ ExtractCallbackPrivate* const d;
+ };
+
+ class UpdateCallbackPrivate;
+ class UpdateCallbackImpl;
+
+ class UpdateCallback
+ {
+ friend class ::Lib7z::UpdateCallbackImpl;
+ public:
+ UpdateCallback();
+ virtual ~UpdateCallback();
+
+ void setTarget( QIODevice* archive );
+ void setSource( const QStringList& dir );
+
+ virtual UpdateCallbackImpl* impl();
+
+ private:
+ UpdateCallbackPrivate* const d;
+ };
+
+ class OpenArchiveInfoCleaner : public QObject {
+ Q_OBJECT
+ public:
+ OpenArchiveInfoCleaner() {}
+ private Q_SLOTS:
+ void deviceDestroyed(QObject*);
+ };
+
+ /*
+ * @throws Lib7z::SevenZipException
+ */
+ void INSTALLER_EXPORT extractArchive( QIODevice* archive, const File& item, QIODevice* out, ExtractCallback* callback=0 );
+
+ /*
+ * @throws Lib7z::SevenZipException
+ */
+ void INSTALLER_EXPORT extractArchive( QIODevice* archive, const File& item, const QString& targetDirectory, ExtractCallback* callback=0 );
+
+ /*
+ * @throws Lib7z::SevenZipException
+ */
+ void INSTALLER_EXPORT extractArchive( QIODevice* archive, const QString& targetDirectory, ExtractCallback* callback=0 );
+
+ /*
+ * @thows Lib7z::SevenZipException
+ */
+ void INSTALLER_EXPORT createArchive( QIODevice* archive, const QStringList& sourceDirectory, UpdateCallback* callback = 0 );
+
+ /*
+ * @throws Lib7z::SevenZipException
+ */
+ QVector<File> INSTALLER_EXPORT listArchive( QIODevice* archive );
+
+ /*
+ * @throws Lib7z::SevenZipException
+ */
+ bool INSTALLER_EXPORT isSupportedArchive( QIODevice* archive );
+
+ /*
+ * @throws Lib7z::SevenZipException
+ */
+ bool INSTALLER_EXPORT isSupportedArchive( const QString& archive );
+
+
+
+ enum Error {
+ NoError=0,
+ Failed=1,
+ UserDefinedError=128
+ };
+
+ class ExtractCallbackJobImpl;
+
+ class INSTALLER_EXPORT Job : public QObject, public QRunnable {
+ friend class ::Lib7z::ExtractCallbackJobImpl;
+ Q_OBJECT
+ public:
+
+ explicit Job( QObject* parent=0 );
+ ~Job();
+ void start();
+ int error() const;
+ bool hasError() const;
+ QString errorString() const;
+
+ /* reimp */ void run();
+
+ protected:
+ void emitResult();
+ void setError( int code );
+ void setErrorString( const QString& err );
+ void emitProgress( qint64 completed, qint64 total );
+
+ Q_SIGNALS:
+ void finished( Lib7z::Job* job );
+ void progress( qint64 completed, qint64 total );
+
+ private Q_SLOTS:
+ virtual void doStart() = 0;
+
+ private:
+ class Private;
+ Private* const d;
+ };
+
+ class INSTALLER_EXPORT ListArchiveJob : public Job {
+ Q_OBJECT
+ public:
+
+ explicit ListArchiveJob( QObject* parent=0 );
+ ~ListArchiveJob();
+
+ QIODevice* archive() const;
+ void setArchive( QIODevice* archive );
+
+ QVector<File> index() const;
+
+ private:
+ /* reimp */ void doStart();
+
+ private:
+ class Private;
+ Private* const d;
+ };
+
+ class INSTALLER_EXPORT ExtractItemJob : public Job {
+ Q_OBJECT
+ friend class ::Lib7z::ExtractCallback;
+ public:
+
+ explicit ExtractItemJob( QObject* parent=0 );
+ ~ExtractItemJob();
+
+ File item() const;
+ void setItem( const File& item );
+
+ QIODevice* archive() const;
+ void setArchive( QIODevice* archive );
+
+ QString targetDirectory() const;
+ void setTargetDirectory( const QString& dir );
+
+ void setTarget( QIODevice* dev );
+
+ private:
+ /* reimp */ void doStart();
+
+ private:
+ class Private;
+ Private* const d;
+ };
+
+ QByteArray INSTALLER_EXPORT formatKeyValuePairs( const QVariantList& l );
+}
+
+#endif // LIB7Z_FACADE_H
diff --git a/src/libs/installer/licenseoperation.cpp b/src/libs/installer/licenseoperation.cpp
new file mode 100644
index 000000000..d002afdb4
--- /dev/null
+++ b/src/libs/installer/licenseoperation.cpp
@@ -0,0 +1,119 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "licenseoperation.h"
+
+#include "packagemanagercore.h"
+#include "settings.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QTextStream>
+
+using namespace QInstaller;
+
+LicenseOperation::LicenseOperation()
+{
+ setName(QLatin1String("License"));
+}
+
+void LicenseOperation::backup()
+{
+}
+
+bool LicenseOperation::performOperation()
+{
+ QVariantMap licenses = value(QLatin1String("licenses")).toMap();
+ if (licenses.isEmpty()) {
+ setError(UserDefinedError);
+ setErrorString(tr("No license files found to copy."));
+ return false;
+ }
+
+ PackageManagerCore *const core = qVariantValue<PackageManagerCore*>(value(QLatin1String("installer")));
+ if (!core) {
+ setError( UserDefinedError );
+ setErrorString(tr("Needed installer object in %1 operation is empty.").arg(name()));
+ return false;
+ }
+
+ QString targetDir = QString::fromLatin1("%1/%2").arg(core->value(scTargetDir),
+ QLatin1String("Licenses"));
+
+ QDir dir;
+ dir.mkpath(targetDir);
+ setArguments(QStringList(targetDir));
+
+ for (QVariantMap::const_iterator it = licenses.begin(); it != licenses.end(); ++it) {
+ QFile file(targetDir + QDir::separator() + it.key());
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+ setError(UserDefinedError);
+ setErrorString(tr("Can not write license file: %1.").arg(targetDir + QDir::separator()
+ + it.key()));
+ return false;
+ }
+
+ QTextStream stream(&file);
+ stream << it.value().toString();
+ }
+
+ return true;
+}
+
+bool LicenseOperation::undoOperation()
+{
+ QVariantMap licenses = value(QLatin1String("licenses")).toMap();
+ if (licenses.isEmpty()) {
+ setError(UserDefinedError);
+ setErrorString(tr("No license files found to delete."));
+ return false;
+ }
+
+ QString targetDir = arguments().value(0);
+ for (QVariantMap::const_iterator it = licenses.begin(); it != licenses.end(); ++it)
+ QFile::remove(targetDir + QDir::separator() + it.key());
+
+ QDir dir;
+ dir.rmdir(targetDir);
+
+ return true;
+}
+
+bool LicenseOperation::testOperation()
+{
+ return true;
+}
+
+Operation *LicenseOperation::clone() const
+{
+ return new LicenseOperation();
+}
diff --git a/src/libs/installer/licenseoperation.h b/src/libs/installer/licenseoperation.h
new file mode 100644
index 000000000..324ca4e57
--- /dev/null
+++ b/src/libs/installer/licenseoperation.h
@@ -0,0 +1,54 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef LICENSEOPERATION_H
+#define LICENSEOPERATION_H
+
+#include "qinstallerglobal.h"
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT LicenseOperation : public Operation
+{
+public:
+ LicenseOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation* clone() const;
+};
+
+} // namespace QInstaller
+
+#endif //LICENSEOPERATION_H
diff --git a/src/libs/installer/linereplaceoperation.cpp b/src/libs/installer/linereplaceoperation.cpp
new file mode 100644
index 000000000..94a8fb910
--- /dev/null
+++ b/src/libs/installer/linereplaceoperation.cpp
@@ -0,0 +1,112 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "linereplaceoperation.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QTextStream>
+
+using namespace QInstaller;
+
+LineReplaceOperation::LineReplaceOperation()
+{
+ setName(QLatin1String("LineReplace"));
+}
+
+void LineReplaceOperation::backup()
+{
+}
+
+bool LineReplaceOperation::performOperation()
+{
+ const QStringList args = arguments();
+
+ // Arguments:
+ // 1. filename
+ // 2. startsWith Search-String
+ // 3. Replace-Line-String
+ if (args.count() != 3) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, exactly 3 expected.").arg(name()).arg(args
+ .count()));
+ return false;
+ }
+ const QString fileName = args.at(0);
+ const QString searchString = args.at(1);
+ const QString replaceString = args.at(2);
+
+ QFile file(fileName);
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ setError(UserDefinedError);
+ setErrorString(QObject::tr("Failed to open %1 for reading.").arg(fileName));
+ return false;
+ }
+
+ QString replacement;
+ QTextStream stream(&file);
+ while (!stream.atEnd()) {
+ const QString line = stream.readLine();
+ if (line.trimmed().startsWith(searchString))
+ replacement.append(replaceString + QLatin1String("\n"));
+ else
+ replacement.append(line + QLatin1String("\n"));
+ }
+ file.close();
+
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ setError(UserDefinedError);
+ setErrorString(QObject::tr("Failed to open %1 for writing.").arg(fileName));
+ return false;
+ }
+
+ stream.setDevice(&file);
+ stream << replacement;
+ file.close();
+
+ return true;
+}
+
+bool LineReplaceOperation::undoOperation()
+{
+ return true;
+}
+
+bool LineReplaceOperation::testOperation()
+{
+ return true;
+}
+
+Operation *LineReplaceOperation::clone() const
+{
+ return new LineReplaceOperation();
+}
diff --git a/src/libs/installer/linereplaceoperation.h b/src/libs/installer/linereplaceoperation.h
new file mode 100644
index 000000000..e385169bb
--- /dev/null
+++ b/src/libs/installer/linereplaceoperation.h
@@ -0,0 +1,54 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef LINEREPLACEOPERATION_H
+#define LINEREPLACEOPERATION_H
+
+#include "qinstallerglobal.h"
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT LineReplaceOperation : public Operation
+{
+public:
+ LineReplaceOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+};
+
+} // namespace
+
+#endif // LINEREPLACEOPERATION_H
diff --git a/src/libs/installer/macrelocateqt.cpp b/src/libs/installer/macrelocateqt.cpp
new file mode 100644
index 000000000..98f8153c5
--- /dev/null
+++ b/src/libs/installer/macrelocateqt.cpp
@@ -0,0 +1,94 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "macrelocateqt.h"
+#include "macreplaceinstallnamesoperation.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QFile>
+
+
+using namespace QInstaller;
+
+Relocator::Relocator()
+{
+}
+
+bool Relocator::apply(const QString &qtInstallDir, const QString &targetDir)
+{
+// Relocator::apply(/Users/rakeller/QtSDKtest2/Simulator/Qt/gcc)
+// Relocator uses indicator: /QtSDKtest2operation 'QtPatch' with arguments: 'mac; /Users/rakeller/QtSDKtest2/Simulator/Qt/gcc' failed: Error while relocating Qt: "ReplaceInsta
+ if (qtInstallDir.isEmpty()) {
+ m_errorMessage = QLatin1String("qtInstallDir can't be empty");
+ return false;
+ }
+ if (targetDir.isEmpty()) {
+ m_errorMessage = QLatin1String("targetDir can't be empty");
+ return false;
+ }
+ qDebug() << Q_FUNC_INFO << qtInstallDir;
+
+ m_errorMessage.clear();
+ m_installDir.clear();
+
+ m_installDir = targetDir;
+ if (!m_installDir.endsWith(QLatin1Char('/')))
+ m_installDir.append(QLatin1Char('/'));
+ if (!QFile::exists(qtInstallDir + QLatin1String("/bin/qmake"))) {
+ m_errorMessage = QLatin1String("This is not a Qt installation directory.");
+ return false;
+ }
+
+ QString indicator = qtInstallDir;
+ indicator = indicator.replace(targetDir, QString());
+ //to get realy only the first subdirectory as an indicator like the old behaviour was till Mobility don't use this qt patch hack
+ indicator = indicator.left(indicator.indexOf(QLatin1String("/"), 1));
+
+ qDebug() << "Relocator uses indicator:" << indicator;
+ QString replacement = targetDir;
+
+
+ MacReplaceInstallNamesOperation operation;
+ QStringList arguments;
+ arguments << indicator
+ << replacement
+ << qtInstallDir + QLatin1String("/plugins")
+ << qtInstallDir + QLatin1String("/lib")
+ << qtInstallDir + QLatin1String("/imports")
+ << qtInstallDir + QLatin1String("/bin");
+
+ operation.setArguments(arguments);
+ operation.performOperation();
+
+ m_errorMessage = operation.errorString();
+ return m_errorMessage.isNull();
+}
diff --git a/src/libs/installer/macrelocateqt.h b/src/libs/installer/macrelocateqt.h
new file mode 100644
index 000000000..769cb11aa
--- /dev/null
+++ b/src/libs/installer/macrelocateqt.h
@@ -0,0 +1,55 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef RELOCATOR_H
+#define RELOCATOR_H
+
+#include <QString>
+
+namespace QInstaller {
+
+class Relocator
+{
+public:
+ Relocator();
+
+ bool apply(const QString &qtInstallDir, const QString &targetDir);
+ QString errorMessage() const { return m_errorMessage; }
+
+private:
+ QString m_errorMessage;
+ QString m_installDir;
+};
+
+} // namespace QInstaller
+
+#endif // RELOCATOR_H
diff --git a/src/libs/installer/macreplaceinstallnamesoperation.cpp b/src/libs/installer/macreplaceinstallnamesoperation.cpp
new file mode 100644
index 000000000..69797b040
--- /dev/null
+++ b/src/libs/installer/macreplaceinstallnamesoperation.cpp
@@ -0,0 +1,273 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "macreplaceinstallnamesoperation.h"
+
+#include "qprocesswrapper.h"
+
+#include <QtCore/QBuffer>
+#include <QtCore/QDebug>
+#include <QtCore/QDirIterator>
+
+
+using namespace QInstaller;
+
+MacReplaceInstallNamesOperation::MacReplaceInstallNamesOperation()
+{
+ setName(QLatin1String("ReplaceInstallNames"));
+}
+
+void MacReplaceInstallNamesOperation::backup()
+{
+}
+
+bool MacReplaceInstallNamesOperation::performOperation()
+{
+ // Arguments:
+ // 1. indicator to find the original build directory,
+ // means the path till this will be used to replace it with 2.
+ // 2. new/current target install directory(the replacement)
+ // 3. directory containing frameworks
+ // 4. other directory containing frameworks
+ // 5. other directory containing frameworks
+ // 6. ...
+
+ if (arguments().count() < 3) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, 3 expected.").arg(name())
+ .arg(arguments().count()));
+ return false;
+ }
+
+ QString indicator = arguments().at(0);
+ QString installationDir = arguments().at(1);
+ QStringList searchDirList = arguments().mid(2);
+ foreach (const QString &searchDir, searchDirList) {
+ if (!apply(indicator, installationDir, searchDir))
+ return false;
+ }
+
+ return true;
+}
+
+bool MacReplaceInstallNamesOperation::undoOperation()
+{
+ return true;
+}
+
+bool MacReplaceInstallNamesOperation::testOperation()
+{
+ return true;
+}
+
+Operation *MacReplaceInstallNamesOperation::clone() const
+{
+ return new MacReplaceInstallNamesOperation;
+}
+
+bool MacReplaceInstallNamesOperation::apply(const QString &indicator, const QString &installationDir,
+ const QString &searchDir)
+{
+ m_indicator = indicator;
+ m_installationDir = installationDir;
+
+ QStringList alreadyPatchedFrameworks;
+ QLatin1String frameworkSuffix(".framework");
+
+ QDirIterator dirIterator(searchDir, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
+ while (dirIterator.hasNext()) {
+ QString fileName = dirIterator.next();
+
+ //check that we don't do anything for already patched framework pathes
+ if (fileName.contains(frameworkSuffix)) {
+ QString alreadyPatchedSearchString = fileName.left(fileName.lastIndexOf(frameworkSuffix))
+ + frameworkSuffix;
+ if (alreadyPatchedFrameworks.contains(alreadyPatchedSearchString)) {
+ continue;
+ }
+ }
+ if (dirIterator.fileInfo().isDir() && fileName.endsWith(frameworkSuffix)) {
+ relocateFramework(fileName);
+ alreadyPatchedFrameworks.append(fileName);
+ }
+ else if (dirIterator.fileInfo().isDir())
+ continue;
+ else if (fileName.endsWith(QLatin1String(".dylib")))
+ relocateBinary(fileName);
+ else if (dirIterator.fileInfo().isExecutable() && !fileName.endsWith(QLatin1String(".h"))
+ && !fileName.endsWith(QLatin1String(".cpp")) && !fileName.endsWith(QLatin1String(".pro"))
+ && !fileName.endsWith(QLatin1String(".pri"))) {
+ //the endsWith check are here because there were wrongly commited files in the repositories
+ relocateBinary(fileName);
+ }
+ }
+
+ return error() == NoError;
+}
+
+void MacReplaceInstallNamesOperation::extractExecutableInfo(const QString &fileName, QString &frameworkId,
+ QStringList &frameworks, QString &originalBuildDir)
+{
+ qDebug() << "Relocator calling otool -l for" << fileName;
+ QProcessWrapper otool;
+ otool.start(QLatin1String("otool"), QStringList() << QLatin1String("-l") << fileName);
+ if (!otool.waitForStarted()) {
+ setError(UserDefinedError, tr("Can't invoke otool. Is Xcode installed?"));
+ return;
+ }
+ otool.waitForFinished();
+ enum State {
+ State_Start,
+ State_LC_ID_DYLIB,
+ State_LC_LOAD_DYLIB
+ };
+ State state = State_Start;
+ QByteArray outputData = otool.readAllStandardOutput();
+ QBuffer output(&outputData);
+ output.open(QBuffer::ReadOnly);
+ while (!output.atEnd()) {
+ QString line = QString::fromLocal8Bit(output.readLine());
+ line = line.trimmed();
+ if (line.startsWith(QLatin1String("cmd "))) {
+ line.remove(0, 4);
+ if (line == QLatin1String("LC_LOAD_DYLIB"))
+ state = State_LC_LOAD_DYLIB;
+ else if (line == QLatin1String("LC_ID_DYLIB"))
+ state = State_LC_ID_DYLIB;
+ else
+ state = State_Start;
+ } else if (state == State_LC_LOAD_DYLIB && line.startsWith(QLatin1String("name "))) {
+ line.remove(0, 5);
+ int idx = line.indexOf(QLatin1String("(offset"));
+ if (idx > 0)
+ line.truncate(idx);
+ line = line.trimmed();
+ frameworks.append(line);
+ } else if (state == State_LC_ID_DYLIB && line.startsWith(QLatin1String("name "))) {
+ line.remove(0, 5);
+ int idx = line.indexOf(QLatin1String("(offset"));
+ if (idx > 0)
+ line.truncate(idx);
+ line = line.trimmed();
+ frameworkId = line;
+
+ originalBuildDir = frameworkId;
+ idx = originalBuildDir.indexOf(m_indicator);
+ if (idx < 0) {
+ originalBuildDir.clear();
+ } else {
+ originalBuildDir.truncate(idx);
+ }
+ if (originalBuildDir.endsWith(QLatin1Char('/')))
+ originalBuildDir.chop(1);
+ qDebug() << "originalBuildDir is:" << originalBuildDir;
+ }
+ }
+ qDebug() << "END - Relocator calling otool -l for" << fileName;
+}
+
+void MacReplaceInstallNamesOperation::relocateBinary(const QString &fileName)
+{
+ QString frameworkId;
+ QStringList frameworks;
+ QString originalBuildDir;
+ extractExecutableInfo(fileName, frameworkId, frameworks, originalBuildDir);
+
+ qDebug() << QString::fromLatin1("got following informations(fileName: %1, frameworkId: %2, frameworks: %3,"
+ "orginalBuildDir: %4)").arg(fileName, frameworkId, frameworks.join(QLatin1String("|")), originalBuildDir);
+
+ QStringList args;
+ if (frameworkId.contains(m_indicator) || QFileInfo(frameworkId).fileName() == frameworkId) {
+ args << QLatin1String("-id") << fileName << fileName;
+ if (!execCommand(QLatin1String("install_name_tool"), args))
+ return;
+ }
+
+
+ foreach (const QString &fw, frameworks) {
+ if (originalBuildDir.isEmpty() && fw.contains(m_indicator)) {
+ originalBuildDir = fw.left(fw.indexOf(m_indicator));
+ }
+ if (originalBuildDir.isEmpty() || !fw.contains(originalBuildDir))
+ continue;
+ QString newPath = fw;
+ newPath.replace(originalBuildDir, m_installationDir);
+
+ args.clear();
+ args << QLatin1String("-change") << fw << newPath << fileName;
+ if (!execCommand(QLatin1String("install_name_tool"), args))
+ return;
+ }
+}
+
+void MacReplaceInstallNamesOperation::relocateFramework(const QString &directoryName)
+{
+ QFileInfo fi(directoryName);
+ QString frameworkName = fi.baseName();
+
+ QString absoluteVersionDirectory = directoryName + QLatin1String("/Versions/Current");
+ if (QFileInfo(absoluteVersionDirectory).isSymLink()) {
+ absoluteVersionDirectory = QFileInfo(absoluteVersionDirectory).symLinkTarget();
+ }
+
+ fi.setFile(absoluteVersionDirectory + QDir::separator() + frameworkName);
+ if (fi.exists()) {
+ QString fileName = fi.isSymLink() ? fi.symLinkTarget() : fi.absoluteFilePath();
+ relocateBinary(fileName);
+ }
+
+ fi.setFile(absoluteVersionDirectory + QDir::separator() + frameworkName + QLatin1String("_debug"));
+ if (fi.exists()) {
+ QString fileName = fi.isSymLink() ? fi.symLinkTarget() : fi.absoluteFilePath();
+ relocateBinary(fileName);
+ }
+}
+
+bool MacReplaceInstallNamesOperation::execCommand(const QString &cmd, const QStringList &args)
+{
+ qDebug() << Q_FUNC_INFO << cmd << " " << args;
+
+ QProcessWrapper process;
+ process.start(cmd, args);
+ if (!process.waitForStarted()) {
+ setError(UserDefinedError, tr("Can't start process %0.").arg(cmd));
+ return false;
+ }
+ process.waitForFinished();
+ if (process.exitCode() != 0) {
+ QString errorMessage = QLatin1String("Command %1 failed.\nArguments: %2\nOutput: %3\n");
+ setError(UserDefinedError, errorMessage.arg(cmd, args.join(QLatin1String(" ")),
+ QString::fromLocal8Bit(process.readAll())));
+ return false;
+ }
+ return true;
+}
diff --git a/src/libs/installer/macreplaceinstallnamesoperation.h b/src/libs/installer/macreplaceinstallnamesoperation.h
new file mode 100644
index 000000000..29f226de6
--- /dev/null
+++ b/src/libs/installer/macreplaceinstallnamesoperation.h
@@ -0,0 +1,67 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef MACREPLACEINSTALLNAMEOPERATION_H
+#define MACREPLACEINSTALLNAMEOPERATION_H
+
+#include "qinstallerglobal.h"
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT MacReplaceInstallNamesOperation : public Operation
+{
+public:
+ MacReplaceInstallNamesOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+
+ bool apply(const QString &oldString, const QString &newString, const QString &frameworkDir);
+
+private:
+ void extractExecutableInfo(const QString &fileName, QString &frameworkId, QStringList &frameworks,
+ QString &originalBuildDir);
+ void relocateFramework(const QString &directoryName);
+ void relocateBinary(const QString &fileName);
+ bool execCommand(const QString &cmd, const QStringList &args);
+
+private:
+ QString m_indicator;
+ QString m_installationDir;
+};
+
+} // namespace QInstaller
+
+#endif // MACREPLACEINSTALLNAMEOPERATION_H
diff --git a/src/libs/installer/messageboxhandler.cpp b/src/libs/installer/messageboxhandler.cpp
new file mode 100644
index 000000000..a50590382
--- /dev/null
+++ b/src/libs/installer/messageboxhandler.cpp
@@ -0,0 +1,309 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "messageboxhandler.h"
+
+#include <QtCore/QDebug>
+
+#include <QtGui/QApplication>
+#include <QtGui/QPushButton>
+#include <QtGui/QDialogButtonBox>
+
+#include <QtScript/QScriptEngine>
+#include <QtScript/QScriptValue>
+
+QScriptValue QInstaller::registerMessageBox(QScriptEngine *scriptEngine)
+{
+ // register QMessageBox::StandardButton enum in the script connection
+ QScriptValue messageBox = scriptEngine->newQObject(MessageBoxHandler::instance());
+ messageBox.setProperty(QLatin1String("Ok"),
+ scriptEngine->newVariant(static_cast<int>(QMessageBox::Yes)));
+ messageBox.setProperty(QLatin1String("Open"),
+ scriptEngine->newVariant(static_cast<int>(QMessageBox::Open)));
+ messageBox.setProperty(QLatin1String("Save"),
+ scriptEngine->newVariant(static_cast<int>(QMessageBox::Save)));
+ messageBox.setProperty(QLatin1String("Cancel"),
+ scriptEngine->newVariant(static_cast<int>(QMessageBox::Cancel)));
+ messageBox.setProperty(QLatin1String("Close"),
+ scriptEngine->newVariant(static_cast<int>(QMessageBox::Close)));
+ messageBox.setProperty(QLatin1String("Discard"),
+ scriptEngine->newVariant(static_cast<int>(QMessageBox::Discard)));
+ messageBox.setProperty(QLatin1String("Apply"),
+ scriptEngine->newVariant(static_cast<int>(QMessageBox::Apply)));
+ messageBox.setProperty(QLatin1String("Reset"),
+ scriptEngine->newVariant(static_cast<int>(QMessageBox::Reset)));
+ messageBox.setProperty(QLatin1String("RestoreDefaults"),
+ scriptEngine->newVariant(static_cast<int>(QMessageBox::RestoreDefaults)));
+ messageBox.setProperty(QLatin1String("Help"),
+ scriptEngine->newVariant(static_cast<int>(QMessageBox::Help)));
+ messageBox.setProperty(QLatin1String("SaveAll"),
+ scriptEngine->newVariant(static_cast<int>(QMessageBox::SaveAll)));
+ messageBox.setProperty(QLatin1String("Yes"),
+ scriptEngine->newVariant(static_cast<int>(QMessageBox::Yes)));
+ messageBox.setProperty(QLatin1String("YesToAll"),
+ scriptEngine->newVariant(static_cast<int>(QMessageBox::YesToAll)));
+ messageBox.setProperty(QLatin1String("No"),
+ scriptEngine->newVariant(static_cast<int>(QMessageBox::No)));
+ messageBox.setProperty(QLatin1String("NoToAll"),
+ scriptEngine->newVariant(static_cast<int>(QMessageBox::NoToAll)));
+ messageBox.setProperty(QLatin1String("Abort"),
+ scriptEngine->newVariant(static_cast<int>(QMessageBox::Abort)));
+ messageBox.setProperty(QLatin1String("Retry"),
+ scriptEngine->newVariant(static_cast<int>(QMessageBox::Retry)));
+ messageBox.setProperty(QLatin1String("Ignore"),
+ scriptEngine->newVariant(static_cast<int>(QMessageBox::Ignore)));
+ messageBox.setProperty(QLatin1String("NoButton"),
+ scriptEngine->newVariant(static_cast<int>(QMessageBox::NoButton)));
+ scriptEngine->globalObject().setProperty(QLatin1String("QMessageBox"), messageBox);
+
+ return messageBox;
+}
+
+using namespace QInstaller;
+
+template <typename T>
+static QList<T> reversed(const QList<T> &list)
+{
+ qFatal("This seems to be broken, check this!!!!");
+ // TODO: Figure out what should happen here. See setDefaultAction(...).
+#if 1
+ // Note: This does not what the function name implies???
+ QList<T> res = list;
+ qCopyBackward(list.begin(), list.end(), res.end());
+ return res;
+#else
+ // Note: This does what the function name implies, but we need to check if this is what we want.
+ QList<T> res = list;
+ std::reverse(res.begin(), res.end());
+ return res;
+#endif
+}
+
+
+// -- MessageBoxHandler
+
+MessageBoxHandler *MessageBoxHandler::m_instance = 0;
+
+MessageBoxHandler::MessageBoxHandler(QObject *parent)
+ : QObject(parent)
+ , m_defaultAction(MessageBoxHandler::AskUser)
+{
+}
+
+MessageBoxHandler *MessageBoxHandler::instance()
+{
+ if (m_instance == 0)
+ m_instance = new MessageBoxHandler(qApp);
+ return m_instance;
+}
+
+QWidget *MessageBoxHandler::currentBestSuitParent()
+{
+ if (QApplication::type() == QApplication::Tty) {
+ return 0;
+ }
+
+ if (qApp->activeModalWidget())
+ return qApp->activeModalWidget();
+
+ return qApp->activeWindow();
+}
+
+void MessageBoxHandler::setDefaultAction(DefaultAction defaultAction)
+{
+ if (m_defaultAction == defaultAction)
+ return;
+ m_defaultAction = defaultAction;
+
+ m_buttonOrder.clear();
+ if (m_defaultAction != AskUser) {
+ m_buttonOrder << QMessageBox::YesToAll << QMessageBox::Yes << QMessageBox::Ok << QMessageBox::Apply
+ << QMessageBox::SaveAll << QMessageBox::Save <<QMessageBox::Retry << QMessageBox::Ignore
+ << QMessageBox::Help << QMessageBox::RestoreDefaults << QMessageBox::Reset << QMessageBox::Open
+ << QMessageBox::Cancel << QMessageBox::Close << QMessageBox::Abort << QMessageBox::Discard
+ << QMessageBox::No << QMessageBox::NoToAll;
+ }
+
+ if (m_defaultAction == Reject) {
+ // If we want to reject everything, we need the lowest button. For example, if Cancel is existing it
+ // could use Cancel, but if Close is existing it will use Close.
+ m_buttonOrder = reversed(m_buttonOrder);
+ }
+}
+
+void MessageBoxHandler::setAutomaticAnswer(const QString &identifier, QMessageBox::StandardButton answer)
+{
+ m_automaticAnswers.insert(identifier, answer);
+}
+
+// -- static
+
+QMessageBox::StandardButton MessageBoxHandler::critical(QWidget *parent, const QString &identifier,
+ const QString &title, const QString &text, QMessageBox::StandardButtons buttons,
+ QMessageBox::StandardButton button)
+{
+ return instance()->showMessageBox(criticalType, parent, identifier, title, text, buttons, button);
+}
+
+QMessageBox::StandardButton MessageBoxHandler::information(QWidget *parent, const QString &identifier,
+ const QString &title, const QString &text, QMessageBox::StandardButtons buttons,
+ QMessageBox::StandardButton button)
+{
+ return instance()->showMessageBox(informationType, parent, identifier, title, text, buttons, button);
+}
+
+QMessageBox::StandardButton MessageBoxHandler::question(QWidget *parent, const QString &identifier,
+ const QString &title, const QString &text, QMessageBox::StandardButtons buttons,
+ QMessageBox::StandardButton button)
+{
+ return instance()->showMessageBox(questionType, parent, identifier, title, text, buttons, button);
+}
+
+QMessageBox::StandardButton MessageBoxHandler::warning(QWidget *parent, const QString &identifier,
+ const QString &title, const QString &text, QMessageBox::StandardButtons buttons,
+ QMessageBox::StandardButton button)
+{
+ return instance()->showMessageBox(warningType, parent, identifier, title, text, buttons, button);
+}
+
+// -- invokable
+
+int MessageBoxHandler::critical(const QString &identifier, const QString &title, const QString &text,
+ QMessageBox::StandardButtons buttons, QMessageBox::StandardButton button) const
+{
+ return showMessageBox(criticalType, currentBestSuitParent(), identifier, title, text, buttons, button);
+}
+
+int MessageBoxHandler::information(const QString &identifier, const QString &title, const QString &text,
+ QMessageBox::StandardButtons buttons, QMessageBox::StandardButton button) const
+{
+ return showMessageBox(informationType, currentBestSuitParent(), identifier, title, text, buttons, button);
+}
+
+int MessageBoxHandler::question(const QString &identifier, const QString &title, const QString &text,
+ QMessageBox::StandardButtons buttons, QMessageBox::StandardButton button) const
+{
+ return showMessageBox(questionType, currentBestSuitParent(), identifier, title, text, buttons, button);
+}
+
+int MessageBoxHandler::warning(const QString &identifier, const QString &title, const QString &text,
+ QMessageBox::StandardButtons buttons, QMessageBox::StandardButton button) const
+{
+ return showMessageBox(warningType, currentBestSuitParent(), identifier, title, text, buttons, button);
+}
+
+// -- private
+
+QMessageBox::StandardButton MessageBoxHandler::autoReply(QMessageBox::StandardButtons buttons) const
+{
+ if (buttons == QMessageBox::NoButton)
+ return QMessageBox::NoButton;
+
+ foreach (const QMessageBox::StandardButton &currentButton, m_buttonOrder) {
+ if ((buttons & currentButton) != 0)
+ return currentButton;
+ }
+ Q_ASSERT(!"the list must have all possible buttons");
+ return QMessageBox::NoButton;
+}
+
+static QMessageBox::StandardButton showNewMessageBox(QWidget *parent, QMessageBox::Icon icon,
+ const QString &title, const QString &text, QMessageBox::StandardButtons buttons,
+ QMessageBox::StandardButton defaultButton)
+{
+ QMessageBox msgBox(icon, title, text, QMessageBox::NoButton, parent);
+ QDialogButtonBox *buttonBox = msgBox.findChild<QDialogButtonBox *>();
+ Q_ASSERT(buttonBox != 0);
+
+ uint mask = QMessageBox::FirstButton;
+ while (mask <= QMessageBox::LastButton) {
+ uint sb = buttons & mask;
+ mask <<= 1;
+ if (!sb)
+ continue;
+ QPushButton *button = msgBox.addButton((QMessageBox::StandardButton)sb);
+ // Choose the first accept role as the default
+ if (msgBox.defaultButton())
+ continue;
+ if ((defaultButton == QMessageBox::NoButton
+ && buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole)
+ || (defaultButton != QMessageBox::NoButton && sb == uint(defaultButton))) {
+ msgBox.setDefaultButton(button);
+ }
+ }
+#if defined(Q_WS_MAC)
+ msgBox.setWindowModality(Qt::WindowModal);
+#endif
+ if (msgBox.exec() == -1)
+ return QMessageBox::Cancel;
+ return msgBox.standardButton(msgBox.clickedButton());
+}
+
+QMessageBox::StandardButton MessageBoxHandler::showMessageBox(MessageType messageType, QWidget *parent,
+ const QString &identifier, const QString &title, const QString &text, QMessageBox::StandardButtons buttons,
+ QMessageBox::StandardButton defaultButton) const
+{
+ static QHash<MessageType, QString> messageTypeHash;
+ if (messageTypeHash.isEmpty()) {
+ messageTypeHash.insert(criticalType, QLatin1String("critical"));
+ messageTypeHash.insert(informationType, QLatin1String("information"));
+ messageTypeHash.insert(questionType, QLatin1String("question"));
+ messageTypeHash.insert(warningType, QLatin1String("warning"));
+ };
+
+ qDebug() << QString::fromLatin1("created %1 message box %2: '%3', %4").arg(messageTypeHash
+ .value(messageType),identifier, title, text);
+
+ if (QApplication::type() == QApplication::Tty)
+ return defaultButton;
+
+ if (m_automaticAnswers.contains(identifier))
+ return m_automaticAnswers.value(identifier);
+
+ if (m_defaultAction == AskUser) {
+ switch (messageType) {
+ case criticalType:
+ return showNewMessageBox(parent, QMessageBox::Critical, title, text, buttons, defaultButton);
+ case informationType:
+ return showNewMessageBox(parent, QMessageBox::Information, title, text, buttons, defaultButton);
+ case questionType:
+ return showNewMessageBox(parent, QMessageBox::Question, title, text, buttons, defaultButton);
+ case warningType:
+ return showNewMessageBox(parent, QMessageBox::Warning, title, text, buttons, defaultButton);
+ }
+ } else {
+ return autoReply(buttons);
+ }
+
+ Q_ASSERT_X(false, Q_FUNC_INFO, "Something went really wrong.");
+ return defaultButton;
+}
diff --git a/src/libs/installer/messageboxhandler.h b/src/libs/installer/messageboxhandler.h
new file mode 100644
index 000000000..367459b6f
--- /dev/null
+++ b/src/libs/installer/messageboxhandler.h
@@ -0,0 +1,131 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef QINSTALLER_MESSAGEBOXHANDLER_H
+#define QINSTALLER_MESSAGEBOXHANDLER_H
+
+#include <installer_global.h>
+
+#include <QtCore/QHash>
+#include <QtCore/QObject>
+
+#include <QtGui/QMessageBox>
+
+#include <QtScript/QScriptable>
+
+namespace QInstaller {
+
+QScriptValue registerMessageBox(QScriptEngine *scriptEngine);
+
+class INSTALLER_EXPORT MessageBoxHandler : public QObject, private QScriptable
+{
+ Q_OBJECT
+
+public:
+ enum DefaultAction {
+ AskUser,
+ Accept,
+ Reject
+ };
+
+ enum MessageType{
+ criticalType,
+ informationType,
+ questionType,
+ warningType
+ };
+
+ static MessageBoxHandler *instance();
+ static QWidget *currentBestSuitParent();
+
+ void setDefaultAction(DefaultAction defaultAction);
+ void setAutomaticAnswer(const QString &identifier, QMessageBox::StandardButton answer);
+
+ static QMessageBox::StandardButton critical(QWidget *parent, const QString &identifier,
+ const QString &title, const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok,
+ QMessageBox::StandardButton button = QMessageBox::NoButton);
+
+ static QMessageBox::StandardButton information(QWidget *parent, const QString &identifier,
+ const QString &title, const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok,
+ QMessageBox::StandardButton button=QMessageBox::NoButton);
+
+ static QMessageBox::StandardButton question(QWidget *parent, const QString &identifier,
+ const QString &title, const QString &text,
+ QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No,
+ QMessageBox::StandardButton button = QMessageBox::NoButton);
+
+ static QMessageBox::StandardButton warning(QWidget *parent, const QString &identifier,
+ const QString &title, const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok,
+ QMessageBox::StandardButton button = QMessageBox::NoButton);
+
+ Q_INVOKABLE int critical(const QString &identifier, const QString &title, const QString &text,
+ QMessageBox::StandardButtons buttons = QMessageBox::Ok,
+ QMessageBox::StandardButton button = QMessageBox::NoButton) const;
+
+ Q_INVOKABLE int information(const QString &identifier, const QString &title, const QString &text,
+ QMessageBox::StandardButtons buttons = QMessageBox::Ok,
+ QMessageBox::StandardButton button = QMessageBox::NoButton) const;
+
+ Q_INVOKABLE int question(const QString &identifier, const QString &title, const QString &text,
+ QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No,
+ QMessageBox::StandardButton button = QMessageBox::NoButton) const;
+
+ Q_INVOKABLE int warning(const QString &identifier, const QString &title, const QString &text,
+ QMessageBox::StandardButtons buttons = QMessageBox::Ok,
+ QMessageBox::StandardButton button = QMessageBox::NoButton) const;
+
+private Q_SLOTS:
+ //this removes the slot from the script area
+ virtual void deleteLater() {
+ QObject::deleteLater();
+ }
+
+private:
+ explicit MessageBoxHandler(QObject *parent);
+
+ QMessageBox::StandardButton autoReply(QMessageBox::StandardButtons buttons) const;
+ QMessageBox::StandardButton showMessageBox(MessageType messageType, QWidget *parent,
+ const QString &identifier, const QString &title, const QString &text,
+ QMessageBox::StandardButtons buttons = QMessageBox::Ok,
+ QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) const;
+
+private:
+ static MessageBoxHandler *m_instance;
+
+ DefaultAction m_defaultAction;
+ QList<QMessageBox::Button> m_buttonOrder;
+ QHash<QString, QMessageBox::StandardButton> m_automaticAnswers;
+};
+
+}
+
+#endif // QINSTALLER_MESSAGEBOXHANDLER_H
diff --git a/src/libs/installer/minimumprogressoperation.cpp b/src/libs/installer/minimumprogressoperation.cpp
new file mode 100644
index 000000000..bc4b02fc3
--- /dev/null
+++ b/src/libs/installer/minimumprogressoperation.cpp
@@ -0,0 +1,68 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "minimumprogressoperation.h"
+
+using namespace QInstaller;
+
+MinimumProgressOperation::MinimumProgressOperation()
+{
+ //this shouldn't be call able by script, but we need a name for the binary format
+ setName(QLatin1String("MinimumProgress"));
+}
+
+void MinimumProgressOperation::backup()
+{
+}
+
+bool MinimumProgressOperation::performOperation()
+{
+ progressChanged(1);
+ return true;
+}
+
+bool MinimumProgressOperation::undoOperation()
+{
+ progressChanged(1);
+ return true;
+}
+
+bool MinimumProgressOperation::testOperation()
+{
+ return true;
+}
+
+Operation *MinimumProgressOperation::clone() const
+{
+ return new MinimumProgressOperation();
+}
+
diff --git a/src/libs/installer/minimumprogressoperation.h b/src/libs/installer/minimumprogressoperation.h
new file mode 100644
index 000000000..c945de951
--- /dev/null
+++ b/src/libs/installer/minimumprogressoperation.h
@@ -0,0 +1,61 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef MINIMUMPROGRESSOPERATION_H
+#define MINIMUMPROGRESSOPERATION_H
+
+#include "qinstallerglobal.h"
+
+#include <QtCore/QObject>
+
+namespace QInstaller {
+
+class MinimumProgressOperation : public QObject, public Operation
+{
+ Q_OBJECT
+
+public:
+ MinimumProgressOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+
+signals:
+ void progressChanged(double progress);
+};
+
+} // namespace QInstaller
+
+#endif // MINIMUMPROGRESSOPERATION_H
diff --git a/src/libs/installer/operationrunner.cpp b/src/libs/installer/operationrunner.cpp
new file mode 100644
index 000000000..26e7e6b02
--- /dev/null
+++ b/src/libs/installer/operationrunner.cpp
@@ -0,0 +1,165 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#include "operationrunner.h"
+
+#include "binaryformat.h"
+#include "component.h"
+#include "errors.h"
+#include "init.h"
+#include "packagemanagercore.h"
+#include "utils.h"
+
+#include "kdupdaterupdateoperation.h"
+#include "kdupdaterupdateoperationfactory.h"
+
+#include <iostream>
+
+namespace {
+class OutputHandler : public QObject
+{
+ Q_OBJECT
+
+public slots:
+ void drawItToCommandLine(const QString &outPut)
+ {
+ std::cout << qPrintable(outPut) << std::endl;
+ }
+};
+}
+
+using namespace QInstaller;
+using namespace QInstallerCreator;
+
+OperationRunner::OperationRunner()
+ : m_core(0)
+{
+ QInstaller::init();
+}
+
+OperationRunner::~OperationRunner()
+{
+ delete m_core;
+}
+
+bool OperationRunner::init()
+{
+ try {
+ BinaryContent content = BinaryContent::readAndRegisterFromApplicationFile();
+ m_core = new PackageManagerCore(content.magicMarker(), content.performedOperations());
+ } catch (const Error &e) {
+ std::cerr << qPrintable(e.message()) << std::endl;
+ return false;
+ } catch (...) {
+ return false;
+ }
+
+ return true;
+}
+
+void OperationRunner::setVerbose(bool verbose)
+{
+ QInstaller::setVerbose(verbose);
+}
+
+int OperationRunner::runOperation(const QStringList &arguments)
+{
+ if (!init()) {
+ qDebug() << "Could not init the package manager core - without this not all operations are working "
+ "as expected.";
+ }
+
+ bool isPerformType = arguments.contains(QLatin1String("--runoperation"));
+ bool isUndoType = arguments.contains(QLatin1String("--undooperation"));
+
+ if ((!isPerformType && !isUndoType) || (isPerformType && isUndoType)) {
+ std::cerr << "wrong arguments are used, cannot run this operation";
+ return EXIT_FAILURE;
+ }
+
+ QStringList argumentList;
+
+ if (isPerformType)
+ argumentList = arguments.mid(arguments.indexOf(QLatin1String("--runoperation")) + 1);
+ else
+ argumentList = arguments.mid(arguments.indexOf(QLatin1String("--undooperation")) + 1);
+
+
+ try {
+ const QString operationName = argumentList.takeFirst();
+ KDUpdater::UpdateOperation* const operation = KDUpdater::UpdateOperationFactory::instance()
+ .create(operationName);
+ if (!operation) {
+ std::cerr << "Can not find the operation: " << qPrintable(operationName) << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ OutputHandler myOutPutHandler;
+ QObject *const operationObject = dynamic_cast<QObject *>(operation);
+ if (operationObject != 0) {
+ const QMetaObject *const mo = operationObject->metaObject();
+ if (mo->indexOfSignal(QMetaObject::normalizedSignature("outputTextChanged(QString)")) > -1) {
+ QObject::connect(operationObject, SIGNAL(outputTextChanged(QString)),
+ &myOutPutHandler, SLOT(drawItToCommandLine(QString)));
+ }
+ }
+
+ operation->setValue(QLatin1String("installer"), QVariant::fromValue(m_core));
+
+ operation->setArguments(argumentList);
+
+ bool readyPerformed = false;
+ if (isPerformType)
+ readyPerformed = operation->performOperation();
+
+ if (isUndoType)
+ readyPerformed = operation->undoOperation();
+
+ std::cout << "========================================" << std::endl;
+ if (readyPerformed) {
+ std::cout << "Operation was successfully performed." << std::endl;
+ } else {
+ std::cerr << "There was a problem while performing the operation: "
+ << qPrintable(operation->errorString()) << std::endl;
+ return EXIT_FAILURE;
+ }
+ } catch (const QInstaller::Error &e) {
+ std::cerr << qPrintable(e.message()) << std::endl;
+ return EXIT_FAILURE;
+ } catch (...) {
+ std::cerr << "Caught unknown exception" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
+
+#include "operationrunner.moc"
diff --git a/src/libs/installer/operationrunner.h b/src/libs/installer/operationrunner.h
new file mode 100644
index 000000000..9d3056e16
--- /dev/null
+++ b/src/libs/installer/operationrunner.h
@@ -0,0 +1,60 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef OPERATIONRUNNER_H
+#define OPERATIONRUNNER_H
+
+#include "installer_global.h"
+
+QT_FORWARD_DECLARE_CLASS(QStringList)
+
+namespace QInstaller {
+
+class PackageManagerCore;
+
+class INSTALLER_EXPORT OperationRunner
+{
+public:
+ explicit OperationRunner();
+ ~OperationRunner();
+
+ bool init();
+ void setVerbose(bool verbose);
+ int runOperation(const QStringList &arguments);
+
+private:
+ PackageManagerCore *m_core;
+};
+
+} // namespace QInstaller
+
+#endif
diff --git a/src/libs/installer/packagemanagercore.cpp b/src/libs/installer/packagemanagercore.cpp
new file mode 100644
index 000000000..8b871c61e
--- /dev/null
+++ b/src/libs/installer/packagemanagercore.cpp
@@ -0,0 +1,1845 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#include "packagemanagercore.h"
+
+#include "adminauthorization.h"
+#include "binaryformat.h"
+#include "component.h"
+#include "downloadarchivesjob.h"
+#include "errors.h"
+#include "fsengineclient.h"
+#include "getrepositoriesmetainfojob.h"
+#include "messageboxhandler.h"
+#include "packagemanagercore_p.h"
+#include "packagemanagerproxyfactory.h"
+#include "progresscoordinator.h"
+#include "qinstallerglobal.h"
+#include "qprocesswrapper.h"
+#include "qsettingswrapper.h"
+#include "settings.h"
+#include "utils.h"
+
+#include <QtCore/QTemporaryFile>
+
+#include <QtGui/QDesktopServices>
+
+#include <QtScript/QScriptEngine>
+#include <QtScript/QScriptContext>
+
+#include "kdsysinfo.h"
+#include "kdupdaterupdateoperationfactory.h"
+
+#ifdef Q_OS_WIN
+# include "qt_windows.h"
+#endif
+
+using namespace QInstaller;
+
+static QFont sVirtualComponentsFont;
+
+static bool sNoForceInstallation = false;
+static bool sVirtualComponentsVisible = false;
+
+static QScriptValue checkArguments(QScriptContext *context, int amin, int amax)
+{
+ if (context->argumentCount() < amin || context->argumentCount() > amax) {
+ if (amin != amax) {
+ return context->throwError(QObject::tr("Invalid arguments: %1 arguments given, %2 to "
+ "%3 expected.").arg(QString::number(context->argumentCount()),
+ QString::number(amin), QString::number(amax)));
+ }
+ return context->throwError(QObject::tr("Invalid arguments: %1 arguments given, %2 expected.")
+ .arg(QString::number(context->argumentCount()), QString::number(amin)));
+ }
+ return QScriptValue();
+}
+
+static bool componentMatches(const Component *component, const QString &name,
+ const QString &version = QString())
+{
+ if (name.isEmpty() || component->name() != name)
+ return false;
+
+ if (version.isEmpty())
+ return true;
+
+ // can be remote or local version
+ return PackageManagerCore::versionMatches(component->value(scVersion), version);
+}
+
+Component *PackageManagerCore::subComponentByName(const QInstaller::PackageManagerCore *installer,
+ const QString &name, const QString &version, Component *check)
+{
+ if (name.isEmpty())
+ return 0;
+
+ if (check != 0 && componentMatches(check, name, version))
+ return check;
+
+ if (installer->runMode() == AllMode) {
+ QList<Component*> rootComponents;
+ if (check == 0)
+ rootComponents = installer->rootComponents();
+ else
+ rootComponents = check->childComponents(false, AllMode);
+
+ foreach (QInstaller::Component *component, rootComponents) {
+ Component *const result = subComponentByName(installer, name, version, component);
+ if (result != 0)
+ return result;
+ }
+ } else {
+ const QList<Component*> updaterComponents = installer->updaterComponents()
+ + installer->d->m_updaterComponentsDeps;
+ foreach (QInstaller::Component *component, updaterComponents) {
+ if (componentMatches(component, name, version))
+ return component;
+ }
+ }
+ return 0;
+}
+
+/*!
+ Scriptable version of PackageManagerCore::componentByName(QString).
+ \sa PackageManagerCore::componentByName
+ */
+QScriptValue QInstaller::qInstallerComponentByName(QScriptContext *context, QScriptEngine *engine)
+{
+ const QScriptValue check = checkArguments(context, 1, 1);
+ if (check.isError())
+ return check;
+
+ // well... this is our "this" pointer
+ PackageManagerCore *const core = dynamic_cast<PackageManagerCore*>(engine->globalObject()
+ .property(QLatin1String("installer")).toQObject());
+
+ const QString name = context->argument(0).toString();
+ return engine->newQObject(core->componentByName(name));
+}
+
+QScriptValue QInstaller::qDesktopServicesOpenUrl(QScriptContext *context, QScriptEngine *engine)
+{
+ Q_UNUSED(engine);
+ const QScriptValue check = checkArguments(context, 1, 1);
+ if (check.isError())
+ return check;
+ QString url = context->argument(0).toString();
+ url.replace(QLatin1String("\\\\"), QLatin1String("/"));
+ url.replace(QLatin1String("\\"), QLatin1String("/"));
+ return QDesktopServices::openUrl(QUrl::fromUserInput(url));
+}
+
+QScriptValue QInstaller::qDesktopServicesDisplayName(QScriptContext *context, QScriptEngine *engine)
+{
+ Q_UNUSED(engine);
+ const QScriptValue check = checkArguments(context, 1, 1);
+ if (check.isError())
+ return check;
+ const QDesktopServices::StandardLocation location =
+ static_cast< QDesktopServices::StandardLocation >(context->argument(0).toInt32());
+ return QDesktopServices::displayName(location);
+}
+
+QScriptValue QInstaller::qDesktopServicesStorageLocation(QScriptContext *context, QScriptEngine *engine)
+{
+ Q_UNUSED(engine);
+ const QScriptValue check = checkArguments(context, 1, 1);
+ if (check.isError())
+ return check;
+ const QDesktopServices::StandardLocation location =
+ static_cast< QDesktopServices::StandardLocation >(context->argument(0).toInt32());
+ return QDesktopServices::storageLocation(location);
+}
+
+QString QInstaller::uncaughtExceptionString(QScriptEngine *scriptEngine, const QString &context)
+{
+ QString error(QLatin1String("\n\n%1\n\nBacktrace:\n\t%2"));
+ if (!context.isEmpty())
+ error.prepend(context);
+
+ return error.arg(scriptEngine->uncaughtException().toString(), scriptEngine->uncaughtExceptionBacktrace()
+ .join(QLatin1String("\n\t")));
+}
+
+
+/*!
+ \class QInstaller::PackageManagerCore
+ PackageManagerCore forms the core of the installation, update, maintenance and un-installation system.
+ */
+
+/*!
+ \enum QInstaller::PackageManagerCore::WizardPage
+ WizardPage is used to number the different pages known to the Installer GUI.
+ */
+
+/*!
+ \var QInstaller::PackageManagerCore::Introduction
+ I ntroduction page.
+ */
+
+/*!
+ \var QInstaller::PackageManagerCore::LicenseCheck
+ License check page
+ */
+/*!
+ \var QInstaller::PackageManagerCore::TargetDirectory
+ Target directory selection page
+ */
+/*!
+ \var QInstaller::PackageManagerCore::ComponentSelection
+ %Component selection page
+ */
+/*!
+ \var QInstaller::PackageManagerCore::StartMenuSelection
+ Start menu directory selection page - Microsoft Windows only
+ */
+/*!
+ \var QInstaller::PackageManagerCore::ReadyForInstallation
+ "Ready for Installation" page
+ */
+/*!
+ \var QInstaller::PackageManagerCore::PerformInstallation
+ Page shown while performing the installation
+ */
+/*!
+ \var QInstaller::PackageManagerCore::InstallationFinished
+ Page shown when the installation was finished
+ */
+/*!
+ \var QInstaller::PackageManagerCore::End
+ Non-existing page - this value has to be used if you want to insert a page after \a InstallationFinished
+ */
+
+void PackageManagerCore::writeUninstaller()
+{
+ if (d->m_needToWriteUninstaller) {
+ try {
+ d->writeUninstaller(d->m_performedOperationsOld + d->m_performedOperationsCurrentSession);
+
+ bool gainedAdminRights = false;
+ QTemporaryFile tempAdminFile(d->targetDir()
+ + QLatin1String("/testjsfdjlkdsjflkdsjfldsjlfds") + QString::number(qrand() % 1000));
+ if (!tempAdminFile.open() || !tempAdminFile.isWritable()) {
+ gainAdminRights();
+ gainedAdminRights = true;
+ }
+ d->m_updaterApplication.packagesInfo()->writeToDisk();
+ if (gainedAdminRights)
+ dropAdminRights();
+ d->m_needToWriteUninstaller = false;
+ } catch (const Error &error) {
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("WriteError"), tr("Error writing Uninstaller"), error.message(),
+ QMessageBox::Ok, QMessageBox::Ok);
+ }
+ }
+}
+
+void PackageManagerCore::reset(const QHash<QString, QString> &params)
+{
+ d->m_completeUninstall = false;
+ d->m_forceRestart = false;
+ d->m_status = PackageManagerCore::Unfinished;
+ d->m_installerBaseBinaryUnreplaced.clear();
+ d->m_vars.clear();
+ d->m_vars = params;
+ d->initialize();
+}
+
+/*!
+ Sets the uninstallation to be \a complete. If \a complete is false, only components deselected
+ by the user will be uninstalled. This option applies only on uninstallation.
+ */
+void PackageManagerCore::setCompleteUninstallation(bool complete)
+{
+ d->m_completeUninstall = complete;
+}
+
+void PackageManagerCore::cancelMetaInfoJob()
+{
+ if (d->m_repoMetaInfoJob)
+ d->m_repoMetaInfoJob->cancel();
+}
+
+void PackageManagerCore::componentsToInstallNeedsRecalculation()
+{
+ d->m_componentsToInstallCalculated = false;
+}
+
+void PackageManagerCore::autoAcceptMessageBoxes()
+{
+ MessageBoxHandler::instance()->setDefaultAction(MessageBoxHandler::Accept);
+}
+
+void PackageManagerCore::autoRejectMessageBoxes()
+{
+ MessageBoxHandler::instance()->setDefaultAction(MessageBoxHandler::Reject);
+}
+
+void PackageManagerCore::setMessageBoxAutomaticAnswer(const QString &identifier, int button)
+{
+ MessageBoxHandler::instance()->setAutomaticAnswer(identifier,
+ static_cast<QMessageBox::Button>(button));
+}
+
+quint64 size(QInstaller::Component *component, const QString &value)
+{
+ if (!component->isSelected() || component->isInstalled())
+ return quint64(0);
+ return component->value(value).toLongLong();
+}
+
+quint64 PackageManagerCore::requiredDiskSpace() const
+{
+ quint64 result = 0;
+
+ foreach (QInstaller::Component *component, rootComponents())
+ result += component->updateUncompressedSize();
+
+ return result;
+}
+
+quint64 PackageManagerCore::requiredTemporaryDiskSpace() const
+{
+ quint64 result = 0;
+
+ foreach (QInstaller::Component *component, orderedComponentsToInstall())
+ result += size(component, scCompressedSize);
+
+ return result;
+}
+
+/*!
+ Returns the count of archives that will be downloaded.
+*/
+int PackageManagerCore::downloadNeededArchives(double partProgressSize)
+{
+ Q_ASSERT(partProgressSize >= 0 && partProgressSize <= 1);
+
+ QList<QPair<QString, QString> > archivesToDownload;
+ QList<Component*> neededComponents = orderedComponentsToInstall();
+ foreach (Component *component, neededComponents) {
+ // collect all archives to be downloaded
+ const QStringList toDownload = component->downloadableArchives();
+ foreach (const QString &versionFreeString, toDownload) {
+ archivesToDownload.push_back(qMakePair(QString::fromLatin1("installer://%1/%2")
+ .arg(component->name(), versionFreeString), QString::fromLatin1("%1/%2/%3")
+ .arg(component->repositoryUrl().toString(), component->name(), versionFreeString)));
+ }
+ }
+
+ if (archivesToDownload.isEmpty())
+ return 0;
+
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nDownloading packages..."));
+
+ // don't have it on the stack, since it keeps the temporary files
+ DownloadArchivesJob *const archivesJob = new DownloadArchivesJob(this);
+ archivesJob->setAutoDelete(false);
+ archivesJob->setArchivesToDownload(archivesToDownload);
+ connect(this, SIGNAL(installationInterrupted()), archivesJob, SLOT(cancel()));
+ connect(archivesJob, SIGNAL(outputTextChanged(QString)), ProgressCoordinator::instance(),
+ SLOT(emitLabelAndDetailTextChanged(QString)));
+ connect(archivesJob, SIGNAL(downloadStatusChanged(QString)), ProgressCoordinator::instance(),
+ SIGNAL(downloadStatusChanged(QString)));
+
+ ProgressCoordinator::instance()->registerPartProgress(archivesJob, SIGNAL(progressChanged(double)),
+ partProgressSize);
+
+ archivesJob->start();
+ archivesJob->waitForFinished();
+
+ if (archivesJob->error() == KDJob::Canceled)
+ interrupt();
+ else if (archivesJob->error() != KDJob::NoError)
+ throw Error(archivesJob->errorString());
+
+ if (d->statusCanceledOrFailed())
+ throw Error(tr("Installation canceled by user"));
+ ProgressCoordinator::instance()->emitDownloadStatus(tr("All downloads finished."));
+
+ return archivesToDownload.count();
+}
+
+void PackageManagerCore::installComponent(Component *component, double progressOperationSize)
+{
+ Q_ASSERT(progressOperationSize);
+
+ d->setStatus(PackageManagerCore::Running);
+ try {
+ d->installComponent(component, progressOperationSize);
+ d->setStatus(PackageManagerCore::Success);
+ } catch (const Error &error) {
+ if (status() != PackageManagerCore::Canceled) {
+ d->setStatus(PackageManagerCore::Failure);
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("installationError"), tr("Error"), error.message());
+ }
+ }
+}
+
+/*!
+ If a component marked as important was installed during update
+ process true is returned.
+*/
+bool PackageManagerCore::needsRestart() const
+{
+ return d->m_forceRestart;
+}
+
+void PackageManagerCore::rollBackInstallation()
+{
+ emit titleMessageChanged(tr("Cancelling the Installer"));
+
+ //this unregisters all operation progressChanged connects
+ ProgressCoordinator::instance()->setUndoMode();
+ const int progressOperationCount = d->countProgressOperations(d->m_performedOperationsCurrentSession);
+ const double progressOperationSize = double(1) / progressOperationCount;
+
+ //re register all the undo operations with the new size to the ProgressCoordninator
+ foreach (Operation *const operation, d->m_performedOperationsCurrentSession) {
+ QObject *const operationObject = dynamic_cast<QObject*> (operation);
+ if (operationObject != 0) {
+ const QMetaObject* const mo = operationObject->metaObject();
+ if (mo->indexOfSignal(QMetaObject::normalizedSignature("progressChanged(double)")) > -1) {
+ ProgressCoordinator::instance()->registerPartProgress(operationObject,
+ SIGNAL(progressChanged(double)), progressOperationSize);
+ }
+ }
+ }
+
+ KDUpdater::PackagesInfo &packages = *d->m_updaterApplication.packagesInfo();
+ while (!d->m_performedOperationsCurrentSession.isEmpty()) {
+ try {
+ Operation *const operation = d->m_performedOperationsCurrentSession.takeLast();
+ const bool becameAdmin = !d->m_FSEngineClientHandler->isActive()
+ && operation->value(QLatin1String("admin")).toBool() && gainAdminRights();
+
+ PackageManagerCorePrivate::performOperationThreaded(operation, PackageManagerCorePrivate::Undo);
+
+ const QString componentName = operation->value(QLatin1String("component")).toString();
+ if (!componentName.isEmpty()) {
+ Component *component = componentByName(componentName);
+ if (!component)
+ component = d->componentsToReplace(runMode()).value(componentName).second;
+ if (component) {
+ component->setUninstalled();
+ packages.removePackage(component->name());
+ }
+ }
+
+ if (becameAdmin)
+ dropAdminRights();
+ } catch (const Error &e) {
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("ElevationError"), tr("Authentication Error"), tr("Some components "
+ "could not be removed completely because admin rights could not be acquired: %1.")
+ .arg(e.message()));
+ } catch (...) {
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("unknown"),
+ tr("Unknown error."), tr("Some components could not be removed completely because an unknown "
+ "error happened."));
+ }
+ }
+ packages.writeToDisk();
+}
+
+bool PackageManagerCore::isFileExtensionRegistered(const QString &extension) const
+{
+ QSettingsWrapper settings(QLatin1String("HKEY_CLASSES_ROOT"), QSettingsWrapper::NativeFormat);
+ return settings.value(QString::fromLatin1(".%1/Default").arg(extension)).isValid();
+}
+
+
+// -- QInstaller
+
+/*!
+ Used by operation runner to get a fake installer, can be removed if installerbase can do what operation
+ runner does.
+*/
+PackageManagerCore::PackageManagerCore()
+ : d(new PackageManagerCorePrivate(this))
+{
+}
+
+PackageManagerCore::PackageManagerCore(qint64 magicmaker, const OperationList &performedOperations)
+ : d(new PackageManagerCorePrivate(this, magicmaker, performedOperations))
+{
+ qRegisterMetaType<QInstaller::PackageManagerCore::Status>("QInstaller::PackageManagerCore::Status");
+ qRegisterMetaType<QInstaller::PackageManagerCore::WizardPage>("QInstaller::PackageManagerCore::WizardPage");
+
+ d->initialize();
+}
+
+PackageManagerCore::~PackageManagerCore()
+{
+ if (!isUninstaller() && !(isInstaller() && status() == PackageManagerCore::Canceled)) {
+ QDir targetDir(value(scTargetDir));
+ QString logFileName = targetDir.absoluteFilePath(value(QLatin1String("LogFileName"),
+ QLatin1String("InstallationLog.txt")));
+ QInstaller::VerboseWriter::instance()->setOutputStream(logFileName);
+ }
+ delete d;
+}
+
+/* static */
+QFont PackageManagerCore::virtualComponentsFont()
+{
+ return sVirtualComponentsFont;
+}
+
+/* static */
+void PackageManagerCore::setVirtualComponentsFont(const QFont &font)
+{
+ sVirtualComponentsFont = font;
+}
+
+/* static */
+bool PackageManagerCore::virtualComponentsVisible()
+{
+ return sVirtualComponentsVisible;
+}
+
+/* static */
+void PackageManagerCore::setVirtualComponentsVisible(bool visible)
+{
+ sVirtualComponentsVisible = visible;
+}
+
+/* static */
+bool PackageManagerCore::noForceInstallation()
+{
+ return sNoForceInstallation;
+}
+
+/* static */
+void PackageManagerCore::setNoForceInstallation(bool value)
+{
+ sNoForceInstallation = value;
+}
+
+RunMode PackageManagerCore::runMode() const
+{
+ return isUpdater() ? UpdaterMode : AllMode;
+}
+
+bool PackageManagerCore::fetchLocalPackagesTree()
+{
+ d->setStatus(Running);
+
+ if (!isPackageManager()) {
+ d->setStatus(Failure, tr("Application not running in Package Manager mode!"));
+ return false;
+ }
+
+ LocalPackagesHash installedPackages = d->localInstalledPackages();
+ if (installedPackages.isEmpty()) {
+ if (status() != Failure)
+ d->setStatus(Failure, tr("No installed packages found."));
+ return false;
+ }
+
+ emit startAllComponentsReset();
+
+ d->clearAllComponentLists();
+ QHash<QString, QInstaller::Component*> components;
+
+ const QStringList &keys = installedPackages.keys();
+ foreach (const QString &key, keys) {
+ QScopedPointer<QInstaller::Component> component(new QInstaller::Component(this));
+ component->loadDataFromPackage(installedPackages.value(key));
+ const QString &name = component->name();
+ if (components.contains(name)) {
+ qCritical("Could not register component! Component with identifier %s already registered.",
+ qPrintable(name));
+ continue;
+ }
+ components.insert(name, component.take());
+ }
+
+ if (!d->buildComponentTree(components, false))
+ return false;
+
+ updateDisplayVersions(scDisplayVersion);
+
+ emit finishAllComponentsReset();
+ d->setStatus(Success);
+
+ return true;
+}
+
+LocalPackagesHash PackageManagerCore::localInstalledPackages()
+{
+ return d->localInstalledPackages();
+}
+
+void PackageManagerCore::networkSettingsChanged()
+{
+ cancelMetaInfoJob();
+
+ d->m_updates = false;
+ d->m_repoFetched = false;
+ d->m_updateSourcesAdded = false;
+
+ if (d->isUpdater() || d->isPackageManager())
+ d->writeMaintenanceConfigFiles();
+ KDUpdater::FileDownloaderFactory::instance().setProxyFactory(proxyFactory());
+
+ emit coreNetworkSettingsChanged();
+}
+
+KDUpdater::FileDownloaderProxyFactory *PackageManagerCore::proxyFactory() const
+{
+ if (d->m_proxyFactory)
+ return d->m_proxyFactory->clone();
+ return new PackageManagerProxyFactory(this);
+}
+
+void PackageManagerCore::setProxyFactory(KDUpdater::FileDownloaderProxyFactory *factory)
+{
+ delete d->m_proxyFactory;
+ d->m_proxyFactory = factory;
+ KDUpdater::FileDownloaderFactory::instance().setProxyFactory(proxyFactory());
+}
+
+PackagesList PackageManagerCore::remotePackages()
+{
+ return d->remotePackages();
+}
+
+bool PackageManagerCore::fetchRemotePackagesTree()
+{
+ d->setStatus(Running);
+
+ if (isUninstaller()) {
+ d->setStatus(Failure, tr("Application running in Uninstaller mode!"));
+ return false;
+ }
+
+ const LocalPackagesHash installedPackages = d->localInstalledPackages();
+ if (!isInstaller() && status() == Failure)
+ return false;
+
+ if (!d->fetchMetaInformationFromRepositories())
+ return false;
+
+ if (!d->addUpdateResourcesFromRepositories(true))
+ return false;
+
+ const PackagesList &packages = d->remotePackages();
+ if (packages.isEmpty())
+ return false;
+
+ bool success = false;
+ if (runMode() == AllMode)
+ success = fetchAllPackages(packages, installedPackages);
+ else {
+ success = fetchUpdaterPackages(packages, installedPackages);
+ }
+
+ updateDisplayVersions(scRemoteDisplayVersion);
+
+ if (success && !d->statusCanceledOrFailed())
+ d->setStatus(Success);
+ return success;
+}
+
+/*!
+ Adds the widget with objectName() \a name registered by \a component as a new page
+ into the installer's GUI wizard. The widget is added before \a page.
+ \a page has to be a value of \ref QInstaller::PackageManagerCore::WizardPage "WizardPage".
+*/
+bool PackageManagerCore::addWizardPage(Component *component, const QString &name, int page)
+{
+ if (QWidget* const widget = component->userInterface(name)) {
+ emit wizardPageInsertionRequested(widget, static_cast<WizardPage>(page));
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Removes the widget with objectName() \a name previously added to the installer's wizard
+ by \a component.
+*/
+bool PackageManagerCore::removeWizardPage(Component *component, const QString &name)
+{
+ if (QWidget* const widget = component->userInterface(name)) {
+ emit wizardPageRemovalRequested(widget);
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Sets the visibility of the default page with id \a page to \a visible, i.e.
+ removes or adds it from/to the wizard. This works only for pages which have been
+ in the installer when it was started.
+ */
+bool PackageManagerCore::setDefaultPageVisible(int page, bool visible)
+{
+ emit wizardPageVisibilityChangeRequested(visible, page);
+ return true;
+}
+
+/*!
+ Adds the widget with objectName() \a name registered by \a component as an GUI element
+ into the installer's GUI wizard. The widget is added on \a page.
+ \a page has to be a value of \ref QInstaller::PackageManagerCore::WizardPage "WizardPage".
+*/
+bool PackageManagerCore::addWizardPageItem(Component *component, const QString &name, int page)
+{
+ if (QWidget* const widget = component->userInterface(name)) {
+ emit wizardWidgetInsertionRequested(widget, static_cast<WizardPage>(page));
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Removes the widget with objectName() \a name previously added to the installer's wizard
+ by \a component.
+*/
+bool PackageManagerCore::removeWizardPageItem(Component *component, const QString &name)
+{
+ if (QWidget* const widget = component->userInterface(name)) {
+ emit wizardWidgetRemovalRequested(widget);
+ return true;
+ }
+ return false;
+}
+
+void PackageManagerCore::addUserRepositories(const QSet<Repository> &repositories)
+{
+ d->m_settings.addUserRepositories(repositories);
+}
+
+/*!
+ Sets additional repository for this instance of the installer or updater.
+ Will be removed after invoking it again.
+*/
+void PackageManagerCore::setTemporaryRepositories(const QSet<Repository> &repositories, bool replace)
+{
+ d->m_settings.setTemporaryRepositories(repositories, replace);
+}
+
+/*!
+ Checks if the downloader should try to download sha1 checksums for archives.
+*/
+bool PackageManagerCore::testChecksum() const
+{
+ return d->m_testChecksum;
+}
+
+/*!
+ Defines if the downloader should try to download sha1 checksums for archives.
+*/
+void PackageManagerCore::setTestChecksum(bool test)
+{
+ d->m_testChecksum = test;
+}
+
+/*!
+ Returns the number of components in the list for installer and package manager mode. Might return 0 in
+ case the engine has only been run in updater mode or no components have been fetched.
+*/
+int PackageManagerCore::rootComponentCount() const
+{
+ return d->m_rootComponents.size();
+}
+
+/*!
+ Returns the component at index position \a i in the components list. \a i must be a valid index
+ position in the list (i.e., 0 <= i < rootComponentCount()).
+*/
+Component *PackageManagerCore::rootComponent(int i) const
+{
+ return d->m_rootComponents.value(i, 0);
+}
+
+/*!
+ Returns a list of root components if run in installer or package manager mode. Might return an empty list
+ in case the engine has only been run in updater mode or no components have been fetched.
+*/
+QList<Component*> PackageManagerCore::rootComponents() const
+{
+ return d->m_rootComponents;
+}
+
+/*!
+ Appends a component as root component to the internal storage for installer or package manager components.
+ To append a component as a child to an already existing component, use Component::appendComponent(). Emits
+ the componentAdded() signal.
+*/
+void PackageManagerCore::appendRootComponent(Component *component)
+{
+ d->m_rootComponents.append(component);
+ emit componentAdded(component);
+}
+
+/*!
+ Returns the number of components in the list for updater mode. Might return 0 in case the engine has only
+ been run in installer or package manager mode or no components have been fetched.
+*/
+int PackageManagerCore::updaterComponentCount() const
+{
+ return d->m_updaterComponents.size();
+}
+
+/*!
+ Returns the component at index position \a i in the updates component list. \a i must be a valid index
+ position in the list (i.e., 0 <= i < updaterComponentCount()).
+*/
+Component *PackageManagerCore::updaterComponent(int i) const
+{
+ return d->m_updaterComponents.value(i, 0);
+}
+
+/*!
+ Returns a list of components if run in updater mode. Might return an empty list in case the engine has only
+ been run in installer or package manager mode or no components have been fetched.
+*/
+QList<Component*> PackageManagerCore::updaterComponents() const
+{
+ return d->m_updaterComponents;
+}
+
+/*!
+ Appends a component to the internal storage for updater components. Emits the componentAdded() signal.
+*/
+void PackageManagerCore::appendUpdaterComponent(Component *component)
+{
+ component->setUpdateAvailable(true);
+ d->m_updaterComponents.append(component);
+ emit componentAdded(component);
+}
+
+/*!
+ Returns a list of all available components found during a fetch. Note that depending on the run mode the
+ returned list might have different values. In case of updater mode, components scheduled for an
+ update as well as all possible dependencies are returned.
+*/
+QList<Component*> PackageManagerCore::availableComponents() const
+{
+ if (isUpdater())
+ return d->m_updaterComponents + d->m_updaterComponentsDeps + d->m_updaterDependencyReplacements;
+
+ QList<Component*> result = d->m_rootComponents;
+ foreach (QInstaller::Component *component, d->m_rootComponents)
+ result += component->childComponents(true, AllMode);
+ return result + d->m_rootDependencyReplacements;
+}
+
+/*!
+ Returns a component matching \a name. \a name can also contains a version requirement.
+ E.g. "com.nokia.sdk.qt" returns any component with that name, "com.nokia.sdk.qt->=4.5" requires
+ the returned component to have at least version 4.5.
+ If no component matches the requirement, 0 is returned.
+*/
+Component *PackageManagerCore::componentByName(const QString &name) const
+{
+ if (name.isEmpty())
+ return 0;
+
+ if (name.contains(QChar::fromLatin1('-'))) {
+ // the last part is considered to be the version, then
+ const QString version = name.section(QLatin1Char('-'), 1);
+ return subComponentByName(this, name.section(QLatin1Char('-'), 0, 0), version);
+ }
+
+ return subComponentByName(this, name);
+}
+
+/*!
+ Calculates an ordered list of components to install based on the current run mode. Also auto installed
+ dependencies are resolved.
+*/
+bool PackageManagerCore::calculateComponentsToInstall() const
+{
+ if (!d->m_componentsToInstallCalculated) {
+ d->clearComponentsToInstall();
+ QList<Component*> components;
+ if (runMode() == UpdaterMode) {
+ foreach (Component *component, updaterComponents()) {
+ if (component->updateRequested())
+ components.append(component);
+ }
+ } else if (runMode() == AllMode) {
+ // relevant means all components which are not replaced
+ QList<Component*> relevantComponents = rootComponents();
+ foreach (QInstaller::Component *component, rootComponents())
+ relevantComponents += component->childComponents(true, AllMode);
+ foreach (Component *component, relevantComponents) {
+ // ask for all components which will be installed to get all dependencies
+ // even dependencies wich are changed without an increased version
+ if (component->installationRequested() || (component->isInstalled() && !component->uninstallationRequested()))
+ components.append(component);
+ }
+ }
+
+ d->m_componentsToInstallCalculated = d->appendComponentsToInstall(components);
+ }
+ return d->m_componentsToInstallCalculated;
+}
+
+/*!
+ Returns a list of ordered components to install. The list can be empty.
+*/
+QList<Component*> PackageManagerCore::orderedComponentsToInstall() const
+{
+ return d->m_orderedComponentsToInstall;
+}
+
+/*!
+ Calculates a list of components to uninstall based on the current run mode. Auto installed dependencies
+ are resolved as well.
+*/
+bool PackageManagerCore::calculateComponentsToUninstall() const
+{
+ if (runMode() == UpdaterMode)
+ return true;
+
+ // hack to avoid removeing needed dependencies
+ QSet<Component*> componentsToInstall = d->m_orderedComponentsToInstall.toSet();
+
+ QList<Component*> components;
+ foreach (Component *component, availableComponents()) {
+ if (component->uninstallationRequested() && !componentsToInstall.contains(component))
+ components.append(component);
+ }
+
+
+ d->m_componentsToUninstall.clear();
+ return d->appendComponentsToUninstall(components);
+}
+
+/*!
+ Returns a list of components to uninstall. The list can be empty.
+*/
+QList<Component *> PackageManagerCore::componentsToUninstall() const
+{
+ return d->m_componentsToUninstall.toList();
+}
+
+QString PackageManagerCore::componentsToInstallError() const
+{
+ return d->m_componentsToInstallError;
+}
+
+/*!
+ Returns the reason why the component needs to be installed. Reasons can be: The component was scheduled
+ for installation, the component was added as a dependency for an other component or added as an automatic
+ dependency.
+*/
+QString PackageManagerCore::installReason(Component *component) const
+{
+ return d->installReason(component);
+}
+
+/*!
+ Returns a list of components that dependend on \a component. The list can be empty. Note: Auto
+ installed dependencies are not resolved.
+*/
+QList<Component*> PackageManagerCore::dependees(const Component *_component) const
+{
+ QList<Component*> dependees;
+ const QList<Component*> components = availableComponents();
+ if (!_component || components.isEmpty())
+ return dependees;
+
+ const QLatin1Char dash('-');
+ foreach (Component *component, components) {
+ const QStringList &dependencies = component->dependencies();
+ foreach (const QString &dependency, dependencies) {
+ // the last part is considered to be the version then
+ const QString name = dependency.contains(dash) ? dependency.section(dash, 0, 0) : dependency;
+ const QString version = dependency.contains(dash) ? dependency.section(dash, 1) : QString();
+ if (componentMatches(_component, name, version))
+ dependees.append(component);
+ }
+ }
+ return dependees;
+}
+
+/*!
+ Returns a list of dependencies for \a component. If there's a dependency which cannot be fulfilled,
+ \a missingComponents will contain the missing components. Note: Auto installed dependencies are not
+ resolved.
+*/
+QList<Component*> PackageManagerCore::dependencies(const Component *component, QStringList &missingComponents) const
+{
+ QList<Component*> result;
+ foreach (const QString &dependency, component->dependencies()) {
+ Component *component = componentByName(dependency);
+ if (component)
+ result.append(component);
+ else
+ missingComponents.append(dependency);
+ }
+ return result;
+}
+
+Settings &PackageManagerCore::settings() const
+{
+ return d->m_settings;
+}
+
+/*!
+ This method tries to gain admin rights. On success, it returns true.
+*/
+bool PackageManagerCore::gainAdminRights()
+{
+ if (AdminAuthorization::hasAdminRights())
+ return true;
+
+ d->m_FSEngineClientHandler->setActive(true);
+ if (!d->m_FSEngineClientHandler->isActive())
+ throw Error(QObject::tr("Error while elevating access rights."));
+ return true;
+}
+
+/*!
+ This method drops gained admin rights.
+*/
+void PackageManagerCore::dropAdminRights()
+{
+ d->m_FSEngineClientHandler->setActive(false);
+}
+
+/*!
+ Return true, if a process with \a name is running. On Windows, the comparison
+ is case-insensitive.
+*/
+bool PackageManagerCore::isProcessRunning(const QString &name) const
+{
+ return PackageManagerCorePrivate::isProcessRunning(name, runningProcesses());
+}
+
+/*!
+ Executes a program.
+
+ \param program The program that should be executed.
+ \param arguments Optional list of arguments.
+ \param stdIn Optional stdin the program reads.
+ \return If the command could not be executed, an empty QList, otherwise the output of the
+ command as first item, the return code as second item.
+ \note On Unix, the output is just the output to stdout, not to stderr.
+*/
+
+QList<QVariant> PackageManagerCore::execute(const QString &program, const QStringList &arguments,
+ const QString &stdIn) const
+{
+ QEventLoop loop;
+ QProcessWrapper process;
+
+ QString adjustedProgram = replaceVariables(program);
+ QStringList adjustedArguments;
+ foreach (const QString &argument, arguments)
+ adjustedArguments.append(replaceVariables(argument));
+ QString adjustedStdIn = replaceVariables(stdIn);
+
+ connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), &loop, SLOT(quit()));
+ process.start(adjustedProgram, adjustedArguments,
+ adjustedStdIn.isNull() ? QIODevice::ReadOnly : QIODevice::ReadWrite);
+
+ if (!process.waitForStarted())
+ return QList< QVariant >();
+
+ if (!adjustedStdIn.isNull()) {
+ process.write(adjustedStdIn.toLatin1());
+ process.closeWriteChannel();
+ }
+
+ if (process.state() != QProcessWrapper::NotRunning)
+ loop.exec();
+
+ return QList<QVariant>() << QString::fromLatin1(process.readAllStandardOutput()) << process.exitCode();
+}
+
+/*!
+ Executes a program.
+
+ \param program The program that should be executed.
+ \param arguments Optional list of arguments.
+ \return If the command could not be executed, an false will be returned
+*/
+
+bool PackageManagerCore::executeDetached(const QString &program, const QStringList &arguments) const
+{
+ QString adjustedProgram = replaceVariables(program);
+ QStringList adjustedArguments;
+ foreach (const QString &argument, arguments)
+ adjustedArguments.append(replaceVariables(argument));
+ return QProcess::startDetached(adjustedProgram, adjustedArguments);
+}
+
+
+/*!
+ Returns an environment variable.
+*/
+QString PackageManagerCore::environmentVariable(const QString &name) const
+{
+#ifdef Q_WS_WIN
+ const LPCWSTR n = (LPCWSTR) name.utf16();
+ LPTSTR buff = (LPTSTR) malloc(4096 * sizeof(TCHAR));
+ DWORD getenvret = GetEnvironmentVariable(n, buff, 4096);
+ const QString actualValue = getenvret != 0
+ ? QString::fromUtf16((const unsigned short *) buff) : QString();
+ free(buff);
+ return actualValue;
+#else
+ const char *pPath = name.isEmpty() ? 0 : getenv(name.toLatin1());
+ return pPath ? QLatin1String(pPath) : QString();
+#endif
+}
+
+/*!
+ Instantly performs an operation \a name with \a arguments.
+ \sa Component::addOperation
+*/
+bool PackageManagerCore::performOperation(const QString &name, const QStringList &arguments)
+{
+ QScopedPointer<Operation> op(KDUpdater::UpdateOperationFactory::instance().create(name));
+ if (!op.data())
+ return false;
+
+ op->setArguments(replaceVariables(arguments));
+ op->backup();
+ if (!PackageManagerCorePrivate::performOperationThreaded(op.data())) {
+ PackageManagerCorePrivate::performOperationThreaded(op.data(), PackageManagerCorePrivate::Undo);
+ return false;
+ }
+ return true;
+}
+
+/*!
+ Returns true when \a version matches the \a requirement.
+ \a requirement can be a fixed version number or it can be prefix by the comparators '>', '>=',
+ '<', '<=' and '='.
+*/
+bool PackageManagerCore::versionMatches(const QString &version, const QString &requirement)
+{
+ QRegExp compEx(QLatin1String("([<=>]+)(.*)"));
+ const QString comparator = compEx.exactMatch(requirement) ? compEx.cap(1) : QLatin1String("=");
+ const QString ver = compEx.exactMatch(requirement) ? compEx.cap(2) : requirement;
+
+ const bool allowEqual = comparator.contains(QLatin1Char('='));
+ const bool allowLess = comparator.contains(QLatin1Char('<'));
+ const bool allowMore = comparator.contains(QLatin1Char('>'));
+
+ if (allowEqual && version == ver)
+ return true;
+
+ if (allowLess && KDUpdater::compareVersion(ver, version) > 0)
+ return true;
+
+ if (allowMore && KDUpdater::compareVersion(ver, version) < 0)
+ return true;
+
+ return false;
+}
+
+/*!
+ Finds a library named \a name in \a paths.
+ If \a paths is empty, it gets filled with platform dependent default paths.
+ The resulting path is stored in \a library.
+ This method can be used by scripts to check external dependencies.
+*/
+QString PackageManagerCore::findLibrary(const QString &name, const QStringList &paths)
+{
+ QStringList findPaths = paths;
+#if defined(Q_WS_WIN)
+ return findPath(QString::fromLatin1("%1.lib").arg(name), findPaths);
+#else
+ if (findPaths.isEmpty()) {
+ findPaths.push_back(QLatin1String("/lib"));
+ findPaths.push_back(QLatin1String("/usr/lib"));
+ findPaths.push_back(QLatin1String("/usr/local/lib"));
+ findPaths.push_back(QLatin1String("/opt/local/lib"));
+ }
+#if defined(Q_WS_MAC)
+ const QString dynamic = findPath(QString::fromLatin1("lib%1.dylib").arg(name), findPaths);
+#else
+ const QString dynamic = findPath(QString::fromLatin1("lib%1.so*").arg(name), findPaths);
+#endif
+ if (!dynamic.isEmpty())
+ return dynamic;
+ return findPath(QString::fromLatin1("lib%1.a").arg(name), findPaths);
+#endif
+}
+
+/*!
+ Tries to find a file name \a name in one of \a paths.
+ The resulting path is stored in \a path.
+ This method can be used by scripts to check external dependencies.
+*/
+QString PackageManagerCore::findPath(const QString &name, const QStringList &paths)
+{
+ foreach (const QString &path, paths) {
+ const QDir dir(path);
+ const QStringList entries = dir.entryList(QStringList() << name, QDir::Files | QDir::Hidden);
+ if (entries.isEmpty())
+ continue;
+
+ return dir.absoluteFilePath(entries.first());
+ }
+ return QString();
+}
+
+/*!
+ Sets the "installerbase" binary to use when writing the package manager/uninstaller.
+ Set this if an update to installerbase is available.
+ If not set, the executable segment of the running un/installer will be used.
+*/
+void PackageManagerCore::setInstallerBaseBinary(const QString &path)
+{
+ d->m_installerBaseBinaryUnreplaced = path;
+}
+
+/*!
+ Returns the installer value for \a key. If \a key is not known to the system, \a defaultValue is
+ returned. Additionally, on Windows, \a key can be a registry key.
+*/
+QString PackageManagerCore::value(const QString &key, const QString &defaultValue) const
+{
+#ifdef Q_WS_WIN
+ if (!d->m_vars.contains(key)) {
+ static const QRegExp regex(QLatin1String("\\\\|/"));
+ const QString filename = key.section(regex, 0, -2);
+ const QString regKey = key.section(regex, -1);
+ const QSettingsWrapper registry(filename, QSettingsWrapper::NativeFormat);
+ if (!filename.isEmpty() && !regKey.isEmpty() && registry.contains(regKey))
+ return registry.value(regKey).toString();
+ }
+#else
+ if (key == scTargetDir) {
+ const QString dir = d->m_vars.value(key, defaultValue);
+ if (dir.startsWith(QLatin1String("~/")))
+ return QDir::home().absoluteFilePath(dir.mid(2));
+ return dir;
+ }
+#endif
+ return d->m_vars.value(key, defaultValue);
+}
+
+/*!
+ Sets the installer value for \a key to \a value.
+*/
+void PackageManagerCore::setValue(const QString &key, const QString &value)
+{
+ if (d->m_vars.value(key) == value)
+ return;
+
+ d->m_vars.insert(key, value);
+ emit valueChanged(key, value);
+}
+
+/*!
+ Returns true, when the installer contains a value for \a key.
+*/
+bool PackageManagerCore::containsValue(const QString &key) const
+{
+ return d->m_vars.contains(key);
+}
+
+void PackageManagerCore::setSharedFlag(const QString &key, bool value)
+{
+ d->m_sharedFlags.insert(key, value);
+}
+
+bool PackageManagerCore::sharedFlag(const QString &key) const
+{
+ return d->m_sharedFlags.value(key, false);
+}
+
+bool PackageManagerCore::isVerbose() const
+{
+ return QInstaller::isVerbose();
+}
+
+void PackageManagerCore::setVerbose(bool on)
+{
+ QInstaller::setVerbose(on);
+}
+
+PackageManagerCore::Status PackageManagerCore::status() const
+{
+ return PackageManagerCore::Status(d->m_status);
+}
+
+QString PackageManagerCore::error() const
+{
+ return d->m_error;
+}
+
+/*!
+ Returns true if at least one complete installation/update was successful, even if the user cancelled the
+ newest installation process.
+*/
+bool PackageManagerCore::finishedWithSuccess() const
+{
+ return d->m_status == PackageManagerCore::Success || d->m_needToWriteUninstaller;
+}
+
+void PackageManagerCore::interrupt()
+{
+ setCanceled();
+ emit installationInterrupted();
+}
+
+void PackageManagerCore::setCanceled()
+{
+ cancelMetaInfoJob();
+ d->setStatus(PackageManagerCore::Canceled);
+}
+
+/*!
+ Replaces all variables within \a str by their respective values and returns the result.
+*/
+QString PackageManagerCore::replaceVariables(const QString &str) const
+{
+ return d->replaceVariables(str);
+}
+
+/*!
+ \overload
+ Replaces all variables in any of \a str by their respective values and returns the results.
+*/
+QStringList PackageManagerCore::replaceVariables(const QStringList &str) const
+{
+ QStringList result;
+ foreach (const QString &s, str)
+ result.push_back(d->replaceVariables(s));
+
+ return result;
+}
+
+/*!
+ \overload
+ Replaces all variables within \a ba by their respective values and returns the result.
+*/
+QByteArray PackageManagerCore::replaceVariables(const QByteArray &ba) const
+{
+ return d->replaceVariables(ba);
+}
+
+/*!
+ Returns the path to the installer binary.
+*/
+QString PackageManagerCore::installerBinaryPath() const
+{
+ return d->installerBinaryPath();
+}
+
+/*!
+ Returns true when this is the installer running.
+*/
+bool PackageManagerCore::isInstaller() const
+{
+ return d->isInstaller();
+}
+
+/*!
+ Returns true if this is an offline-only installer.
+*/
+bool PackageManagerCore::isOfflineOnly() const
+{
+ return d->isOfflineOnly();
+}
+
+void PackageManagerCore::setUninstaller()
+{
+ d->m_magicBinaryMarker = QInstaller::MagicUninstallerMarker;
+}
+
+/*!
+ Returns true when this is the uninstaller running.
+*/
+bool PackageManagerCore::isUninstaller() const
+{
+ return d->isUninstaller();
+}
+
+void PackageManagerCore::setUpdater()
+{
+ d->m_magicBinaryMarker = QInstaller::MagicUpdaterMarker;
+}
+
+/*!
+ Returns true when this is neither an installer nor an uninstaller running.
+ Must be an updater, then.
+*/
+bool PackageManagerCore::isUpdater() const
+{
+ return d->isUpdater();
+}
+
+void PackageManagerCore::setPackageManager()
+{
+ d->m_magicBinaryMarker = QInstaller::MagicPackageManagerMarker;
+}
+
+/*!
+ Returns true when this is the package manager running.
+*/
+bool PackageManagerCore::isPackageManager() const
+{
+ return d->isPackageManager();
+}
+
+/*!
+ Runs the installer. Returns true on success, false otherwise.
+*/
+bool PackageManagerCore::runInstaller()
+{
+ try {
+ d->runInstaller();
+ return true;
+ } catch (...) {
+ return false;
+ }
+}
+
+/*!
+ Runs the uninstaller. Returns true on success, false otherwise.
+*/
+bool PackageManagerCore::runUninstaller()
+{
+ try {
+ d->runUninstaller();
+ return true;
+ } catch (...) {
+ return false;
+ }
+}
+
+/*!
+ Runs the package updater. Returns true on success, false otherwise.
+*/
+bool PackageManagerCore::runPackageUpdater()
+{
+ try {
+ d->runPackageUpdater();
+ return true;
+ } catch (...) {
+ return false;
+ }
+}
+
+/*!
+ \internal
+ Calls languangeChanged on all components.
+*/
+void PackageManagerCore::languageChanged()
+{
+ foreach (Component *component, availableComponents())
+ component->languageChanged();
+}
+
+/*!
+ Runs the installer, un-installer, updater or package manager, depending on the type of this binary.
+*/
+bool PackageManagerCore::run()
+{
+ try {
+ if (isInstaller())
+ d->runInstaller();
+ else if (isUninstaller())
+ d->runUninstaller();
+ else if (isPackageManager() || isUpdater())
+ d->runPackageUpdater();
+ return true;
+ } catch (const Error &err) {
+ qDebug() << "Caught Installer Error:" << err.message();
+ return false;
+ }
+}
+
+/*!
+ Returns the path name of the uninstaller binary.
+*/
+QString PackageManagerCore::uninstallerName() const
+{
+ return d->uninstallerName();
+}
+
+bool PackageManagerCore::updateComponentData(struct Data &data, Component *component)
+{
+ try {
+ // check if we already added the component to the available components list
+ const QString name = data.package->data(scName).toString();
+ if (data.components->contains(name)) {
+ qCritical("Could not register component! Component with identifier %s already registered.",
+ qPrintable(name));
+ return false;
+ }
+
+ component->setUninstalled();
+ const QString localPath = component->localTempPath();
+ if (isVerbose()) {
+ static QString lastLocalPath;
+ if (lastLocalPath != localPath)
+ qDebug() << "Url is:" << localPath;
+ lastLocalPath = localPath;
+ }
+
+ if (d->m_repoMetaInfoJob) {
+ const Repository &repo = d->m_repoMetaInfoJob->repositoryForTemporaryDirectory(localPath);
+ component->setRepositoryUrl(repo.url());
+ component->setValue(QLatin1String("username"), repo.username());
+ component->setValue(QLatin1String("password"), repo.password());
+ }
+
+ // add downloadable archive from xml
+ const QStringList downloadableArchives = data.package->data(scDownloadableArchives).toString()
+ .split(QRegExp(QLatin1String("\\b(,|, )\\b")), QString::SkipEmptyParts);
+
+ if (component->isFromOnlineRepository()) {
+ foreach (const QString downloadableArchive, downloadableArchives)
+ component->addDownloadableArchive(downloadableArchive);
+ }
+
+ const QStringList componentsToReplace = data.package->data(scReplaces).toString()
+ .split(QRegExp(QLatin1String("\\b(,|, )\\b")), QString::SkipEmptyParts);
+
+ if (!componentsToReplace.isEmpty()) {
+ // Store the component (this is a component that replaces others) and all components that
+ // this one will replace.
+ data.replacementToExchangeables.insert(component, componentsToReplace);
+ }
+
+ if (isInstaller()) {
+ // Running as installer means no component is installed, we do not need to check if the
+ // replacement needs to be marked as installed, just return.
+ return true;
+ }
+
+ if (data.installedPackages->contains(name)) {
+ // The replacement is already installed, we can mark it as installed and skip the search for
+ // a possible component to replace that might be installed (to mark the replacement as installed).
+ component->setInstalled();
+ component->setValue(scInstalledVersion, data.installedPackages->value(name).version);
+ return true;
+ }
+
+ // The replacement is not yet installed, check all components to replace for there install state.
+ foreach (const QString &componentName, componentsToReplace) {
+ if (data.installedPackages->contains(componentName)) {
+ // We found a replacement that is installed.
+ if (isPackageManager()) {
+ // Mark the replacement component as installed as well. Only do this in package manager
+ // mode, otherwise it would not show up in the updaters component list.
+ component->setInstalled();
+ component->setValue(scInstalledVersion, data.installedPackages->value(componentName).version);
+ break; // Break as soon as we know we found an installed component this one replaces.
+ }
+ }
+ }
+ } catch (...) {
+ return false;
+ }
+
+ return true;
+}
+
+void PackageManagerCore::storeReplacedComponents(QHash<QString, Component *> &components, const struct Data &data)
+{
+ QHash<Component*, QStringList>::const_iterator it = data.replacementToExchangeables.constBegin();
+ // remember all components that got a replacement, required for uninstall
+ for (; it != data.replacementToExchangeables.constEnd(); ++it) {
+ foreach (const QString &componentName, it.value()) {
+ Component *component = components.take(componentName);
+ // if one component has a replaces which is not existing in the current component list anymore
+ // just ignore it
+ if (!component) {
+ // This case can happen when in installer mode, but should not occur when updating
+ if (isUpdater())
+ qWarning() << componentName << "- Does not exist in the repositories anymore.";
+ continue;
+ }
+ if (!component && !d->componentsToReplace(data.runMode).contains(componentName)) {
+ component = new Component(this);
+ component->setValue(scName, componentName);
+ } else {
+ component->loadComponentScript();
+ d->replacementDependencyComponents(data.runMode).append(component);
+ }
+ d->componentsToReplace(data.runMode).insert(componentName, qMakePair(it.key(), component));
+ }
+ }
+}
+
+bool PackageManagerCore::fetchAllPackages(const PackagesList &remotes, const LocalPackagesHash &locals)
+{
+ emit startAllComponentsReset();
+
+ d->clearAllComponentLists();
+ QHash<QString, QInstaller::Component*> components;
+
+ Data data;
+ data.runMode = AllMode;
+ data.components = &components;
+ data.installedPackages = &locals;
+
+ foreach (Package *const package, remotes) {
+ if (d->statusCanceledOrFailed())
+ return false;
+
+ QScopedPointer<QInstaller::Component> component(new QInstaller::Component(this));
+ data.package = package;
+ component->loadDataFromPackage(*package);
+ if (updateComponentData(data, component.data())) {
+ const QString name = component->name();
+ components.insert(name, component.take());
+ }
+ }
+
+ foreach (const QString &key, locals.keys()) {
+ QScopedPointer<QInstaller::Component> component(new QInstaller::Component(this));
+ component->loadDataFromPackage(locals.value(key));
+ const QString &name = component->name();
+ if (!components.contains(name))
+ components.insert(name, component.take());
+ }
+
+ // store all components that got a replacement
+ storeReplacedComponents(components, data);
+
+ if (!d->buildComponentTree(components, true))
+ return false;
+
+ emit finishAllComponentsReset();
+ return true;
+}
+
+bool PackageManagerCore::fetchUpdaterPackages(const PackagesList &remotes, const LocalPackagesHash &locals)
+{
+ emit startUpdaterComponentsReset();
+
+ d->clearUpdaterComponentLists();
+ QHash<QString, QInstaller::Component *> components;
+
+ Data data;
+ data.runMode = UpdaterMode;
+ data.components = &components;
+ data.installedPackages = &locals;
+
+ bool foundEssentialUpdate = false;
+ LocalPackagesHash installedPackages = locals;
+ QStringList replaceMes;
+
+ foreach (Package *const update, remotes) {
+ if (d->statusCanceledOrFailed())
+ return false;
+
+ QScopedPointer<QInstaller::Component> component(new QInstaller::Component(this));
+ data.package = update;
+ component->loadDataFromPackage(*update);
+ if (updateComponentData(data, component.data())) {
+ // Keep a reference so we can resolve dependencies during update.
+ d->m_updaterComponentsDeps.append(component.take());
+
+// const QString isNew = update->data(scNewComponent).toString();
+// if (isNew.toLower() != scTrue)
+// continue;
+
+ const QString &name = d->m_updaterComponentsDeps.last()->name();
+ const QString replaces = data.package->data(scReplaces).toString();
+ installedPackages.take(name); // remove from local installed packages
+
+ bool isValidUpdate = locals.contains(name);
+ if (!isValidUpdate && !replaces.isEmpty()) {
+ const QStringList possibleNames = replaces.split(QRegExp(QLatin1String("\\b(,|, )\\b")),
+ QString::SkipEmptyParts);
+ foreach (const QString &possibleName, possibleNames) {
+ if (locals.contains(possibleName)) {
+ isValidUpdate = true;
+ replaceMes << possibleName;
+ }
+ }
+ }
+
+ if (!isValidUpdate)
+ continue; // Update for not installed package found, skip it.
+
+ const LocalPackage &localPackage = locals.value(name);
+ const QString updateVersion = update->data(scRemoteVersion).toString();
+ if (KDUpdater::compareVersion(updateVersion, localPackage.version) <= 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.
+ const QDate updateDate = update->data(scReleaseDate).toDate();
+ if (localPackage.lastUpdateDate > updateDate)
+ continue;
+
+ if (update->data(scEssential, scFalse).toString().toLower() == scTrue)
+ foundEssentialUpdate = true;
+
+ // this is not a dependency, it is a real update
+ components.insert(name, d->m_updaterComponentsDeps.takeLast());
+ }
+ }
+
+ QHash<QString, QInstaller::Component *> localReplaceMes;
+ foreach (const QString &key, installedPackages.keys()) {
+ QInstaller::Component *component = new QInstaller::Component(this);
+ component->loadDataFromPackage(installedPackages.value(key));
+ d->m_updaterComponentsDeps.append(component);
+ // Keep a list of local components that should be replaced
+ if (replaceMes.contains(component->name()))
+ localReplaceMes.insert(component->name(), component);
+ }
+
+ // store all components that got a replacement, but do not modify the components list
+ storeReplacedComponents(localReplaceMes.unite(components), data);
+
+ try {
+ if (!components.isEmpty()) {
+ // load the scripts and append all components w/o parent to the direct list
+ foreach (QInstaller::Component *component, components) {
+ if (d->statusCanceledOrFailed())
+ return false;
+
+ component->loadComponentScript();
+ component->setCheckState(Qt::Checked);
+ appendUpdaterComponent(component);
+ }
+
+ // after everything is set up, check installed components
+ foreach (QInstaller::Component *component, d->m_updaterComponentsDeps) {
+ if (d->statusCanceledOrFailed())
+ return false;
+ // even for possible dependency we need to load the script for example to get archives
+ component->loadComponentScript();
+ if (component->isInstalled()) {
+ // since we do not put them into the model, which would force a update of e.g. tri state
+ // components, we have to check all installed components ourselves
+ component->setCheckState(Qt::Checked);
+ }
+ }
+
+ if (foundEssentialUpdate) {
+ foreach (QInstaller::Component *component, components) {
+ if (d->statusCanceledOrFailed())
+ return false;
+
+ component->setCheckable(false);
+ component->setSelectable(false);
+ if (component->value(scEssential, scFalse).toLower() == scFalse) {
+ // non essential updates are disabled, not checkable and unchecked
+ component->setEnabled(false);
+ component->setCheckState(Qt::Unchecked);
+ } else {
+ // essential updates are enabled, still not checkable but checked
+ component->setEnabled(true);
+ }
+ }
+ }
+ } else {
+ // we have no updates, no need to store possible dependencies
+ d->clearUpdaterComponentLists();
+ }
+ } catch (const Error &error) {
+ d->clearUpdaterComponentLists();
+ emit finishUpdaterComponentsReset();
+ d->setStatus(Failure, error.message());
+
+ // TODO: make sure we remove all message boxes inside the library at some point.
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"),
+ tr("Error"), error.message());
+ return false;
+ }
+
+ emit finishUpdaterComponentsReset();
+ return true;
+}
+
+void PackageManagerCore::resetComponentsToUserCheckedState()
+{
+ d->resetComponentsToUserCheckedState();
+}
+
+void PackageManagerCore::updateDisplayVersions(const QString &displayKey)
+{
+ QHash<QString, QInstaller::Component *> components;
+ const QList<QInstaller::Component *> componentList = availableComponents();
+ foreach (QInstaller::Component *component, componentList)
+ components[component->name()] = component;
+
+ // set display version for all components in list
+ const QStringList &keys = components.keys();
+ foreach (const QString &key, keys) {
+ QHash<QString, bool> visited;
+ if (components.value(key)->isInstalled()) {
+ components.value(key)->setValue(scDisplayVersion,
+ findDisplayVersion(key, components, scInstalledVersion, visited));
+ }
+ visited.clear();
+ const QString displayVersionRemote = findDisplayVersion(key, components, scRemoteVersion, visited);
+ if (displayVersionRemote.isEmpty())
+ components.value(key)->setValue(displayKey, tr("invalid"));
+ else
+ components.value(key)->setValue(displayKey, displayVersionRemote);
+ }
+
+}
+
+QString PackageManagerCore::findDisplayVersion(const QString &componentName,
+ const QHash<QString, Component *> &components, const QString &versionKey, QHash<QString, bool> &visited)
+{
+ if (!components.contains(componentName))
+ return QString();
+ const QString replaceWith = components.value(componentName)->value(scInheritVersion);
+ visited[componentName] = true;
+
+ if (replaceWith.isEmpty())
+ return components.value(componentName)->value(versionKey);
+
+ if (visited.contains(replaceWith)) // cycle
+ return QString();
+
+ return findDisplayVersion(replaceWith, components, versionKey, visited);
+}
+
+bool PackageManagerCore::createLocalRepositoryFromBinary() const
+{
+ return d->m_createLocalRepositoryFromBinary;
+}
+
+void PackageManagerCore::setCreateLocalRepositoryFromBinary(bool create)
+{
+ if (!isOfflineOnly())
+ return;
+ d->m_createLocalRepositoryFromBinary = create;
+}
diff --git a/src/libs/installer/packagemanagercore.h b/src/libs/installer/packagemanagercore.h
new file mode 100644
index 000000000..4e02b3ba3
--- /dev/null
+++ b/src/libs/installer/packagemanagercore.h
@@ -0,0 +1,308 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#ifndef PACKAGEMANAGERCORE_H
+#define PACKAGEMANAGERCORE_H
+
+#include "repository.h"
+#include "qinstallerglobal.h"
+
+#include <QtCore/QHash>
+#include <QtCore/QObject>
+#include <QtCore/QStringList>
+#include <QtCore/QVector>
+
+namespace KDUpdater {
+ class FileDownloaderProxyFactory;
+}
+
+namespace QInstaller {
+
+class Component;
+class PackageManagerCorePrivate;
+class Settings;
+
+// -- PackageManagerCore
+
+class INSTALLER_EXPORT PackageManagerCore : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(PackageManagerCore)
+
+ Q_ENUMS(Status WizardPage)
+ Q_PROPERTY(int status READ status NOTIFY statusChanged)
+
+public:
+ explicit PackageManagerCore();
+ explicit PackageManagerCore(qint64 magicmaker, const OperationList &oldOperations = OperationList());
+ ~PackageManagerCore();
+
+ // status
+ enum Status {
+ Success = EXIT_SUCCESS,
+ Failure = EXIT_FAILURE,
+ Running,
+ Canceled,
+ Unfinished
+ };
+ Status status() const;
+ QString error() const;
+
+ enum WizardPage {
+ Introduction = 0x1000,
+ TargetDirectory = 0x2000,
+ ComponentSelection = 0x3000,
+ LicenseCheck = 0x4000,
+ StartMenuSelection = 0x5000,
+ ReadyForInstallation = 0x6000,
+ PerformInstallation = 0x7000,
+ InstallationFinished = 0x8000,
+ End = 0xffff
+ };
+
+ static QFont virtualComponentsFont();
+ static void setVirtualComponentsFont(const QFont &font);
+
+ static bool virtualComponentsVisible();
+ static void setVirtualComponentsVisible(bool visible);
+
+ static bool noForceInstallation();
+ static void setNoForceInstallation(bool value);
+
+ bool fetchLocalPackagesTree();
+ LocalPackagesHash localInstalledPackages();
+
+ void networkSettingsChanged();
+ KDUpdater::FileDownloaderProxyFactory *proxyFactory() const;
+ void setProxyFactory(KDUpdater::FileDownloaderProxyFactory *factory);
+
+ PackagesList remotePackages();
+ bool fetchRemotePackagesTree();
+
+ bool run();
+ RunMode runMode() const;
+ void reset(const QHash<QString, QString> &params);
+
+ Q_INVOKABLE QList<QVariant> execute(const QString &program,
+ const QStringList &arguments = QStringList(), const QString &stdIn = QString()) const;
+ Q_INVOKABLE bool executeDetached(const QString &program,
+ const QStringList &arguments = QStringList()) const;
+ Q_INVOKABLE QString environmentVariable(const QString &name) const;
+
+ Q_INVOKABLE bool performOperation(const QString &name, const QStringList &arguments);
+
+ Q_INVOKABLE static bool versionMatches(const QString &version, const QString &requirement);
+
+ Q_INVOKABLE static QString findLibrary(const QString &name, const QStringList &paths = QStringList());
+ Q_INVOKABLE static QString findPath(const QString &name, const QStringList &paths = QStringList());
+
+ Q_INVOKABLE void setInstallerBaseBinary(const QString &path);
+
+ // parameter handling
+ Q_INVOKABLE bool containsValue(const QString &key) const;
+ Q_INVOKABLE void setValue(const QString &key, const QString &value);
+ Q_INVOKABLE QString value(const QString &key, const QString &defaultValue = QString()) const;
+
+ //a way to have global flags share able from a component script to another one
+ Q_INVOKABLE bool sharedFlag(const QString &key) const;
+ Q_INVOKABLE void setSharedFlag(const QString &key, bool value = true);
+
+ QString replaceVariables(const QString &str) const;
+ QByteArray replaceVariables(const QByteArray &str) const;
+ QStringList replaceVariables(const QStringList &str) const;
+
+ void writeUninstaller();
+ QString uninstallerName() const;
+ QString installerBinaryPath() const;
+
+ bool testChecksum() const;
+ void setTestChecksum(bool test);
+
+ void addUserRepositories(const QSet<Repository> &repositories);
+ void setTemporaryRepositories(const QSet<Repository> &repositories, bool replace = false);
+
+ Q_INVOKABLE void autoAcceptMessageBoxes();
+ Q_INVOKABLE void autoRejectMessageBoxes();
+ Q_INVOKABLE void setMessageBoxAutomaticAnswer(const QString &identifier, int button);
+
+ Q_INVOKABLE bool isFileExtensionRegistered(const QString &extension) const;
+
+public:
+ // component handling
+ int rootComponentCount() const;
+ Component *rootComponent(int i) const;
+ QList<Component*> rootComponents() const;
+ void appendRootComponent(Component *components);
+
+ Q_INVOKABLE int updaterComponentCount() const;
+ Component *updaterComponent(int i) const;
+ QList<Component*> updaterComponents() const;
+ void appendUpdaterComponent(Component *components);
+
+ QList<Component*> availableComponents() const;
+ Component *componentByName(const QString &identifier) const;
+
+ bool calculateComponentsToInstall() const;
+ QList<Component*> orderedComponentsToInstall() const;
+
+ bool calculateComponentsToUninstall() const;
+ QList<Component*> componentsToUninstall() const;
+
+ QString componentsToInstallError() const;
+ QString installReason(Component *component) const;
+
+ QList<Component*> dependees(const Component *component) const;
+ QList<Component*> dependencies(const Component *component, QStringList &missingComponents) const;
+
+ // convenience
+ Q_INVOKABLE bool isInstaller() const;
+ Q_INVOKABLE bool isOfflineOnly() const;
+
+ Q_INVOKABLE void setUninstaller();
+ Q_INVOKABLE bool isUninstaller() const;
+
+ Q_INVOKABLE void setUpdater();
+ Q_INVOKABLE bool isUpdater() const;
+
+ Q_INVOKABLE void setPackageManager();
+ Q_INVOKABLE bool isPackageManager() const;
+
+ bool isVerbose() const;
+ void setVerbose(bool on);
+
+ Q_INVOKABLE bool gainAdminRights();
+ Q_INVOKABLE void dropAdminRights();
+
+ Q_INVOKABLE quint64 requiredDiskSpace() const;
+ Q_INVOKABLE quint64 requiredTemporaryDiskSpace() const;
+
+ Q_INVOKABLE bool isProcessRunning(const QString &name) const;
+
+ Settings &settings() const;
+
+ Q_INVOKABLE bool addWizardPage(QInstaller::Component *component, const QString &name, int page);
+ Q_INVOKABLE bool removeWizardPage(QInstaller::Component *component, const QString &name);
+ Q_INVOKABLE bool addWizardPageItem(QInstaller::Component *component, const QString &name, int page);
+ Q_INVOKABLE bool removeWizardPageItem(QInstaller::Component *component, const QString &name);
+ Q_INVOKABLE bool setDefaultPageVisible(int page, bool visible);
+
+ void rollBackInstallation();
+
+ int downloadNeededArchives(double partProgressSize);
+ void installComponent(Component *component, double progressOperationSize);
+
+ bool needsRestart() const;
+ bool finishedWithSuccess() const;
+
+ Q_INVOKABLE bool createLocalRepositoryFromBinary() const;
+ Q_INVOKABLE void setCreateLocalRepositoryFromBinary(bool create);
+
+public Q_SLOTS:
+ bool runInstaller();
+ bool runUninstaller();
+ bool runPackageUpdater();
+ void interrupt();
+ void setCanceled();
+ void languageChanged();
+ void setCompleteUninstallation(bool complete);
+ void cancelMetaInfoJob();
+ void componentsToInstallNeedsRecalculation();
+
+Q_SIGNALS:
+ void componentAdded(QInstaller::Component *comp);
+ void rootComponentsAdded(QList<QInstaller::Component*> components);
+ void updaterComponentsAdded(QList<QInstaller::Component*> components);
+ void componentsAboutToBeCleared();
+ void valueChanged(const QString &key, const QString &value);
+ void statusChanged(QInstaller::PackageManagerCore::Status);
+ void currentPageChanged(int page);
+ void finishButtonClicked();
+
+ void metaJobInfoMessage(const QString &message);
+
+ void startAllComponentsReset();
+ void finishAllComponentsReset();
+
+ void startUpdaterComponentsReset();
+ void finishUpdaterComponentsReset();
+
+ void installationStarted();
+ void installationInterrupted();
+ void installationFinished();
+ void updateFinished();
+ void uninstallationStarted();
+ void uninstallationFinished();
+ void titleMessageChanged(const QString &title);
+
+ void wizardPageInsertionRequested(QWidget *widget, QInstaller::PackageManagerCore::WizardPage page);
+ void wizardPageRemovalRequested(QWidget *widget);
+ void wizardWidgetInsertionRequested(QWidget *widget, QInstaller::PackageManagerCore::WizardPage page);
+ void wizardWidgetRemovalRequested(QWidget *widget);
+ void wizardPageVisibilityChangeRequested(bool visible, int page);
+
+ void setAutomatedPageSwitchEnabled(bool request);
+ void coreNetworkSettingsChanged();
+
+private:
+ struct Data {
+ RunMode runMode;
+ Package *package;
+ QHash<QString, Component*> *components;
+ const LocalPackagesHash *installedPackages;
+ QHash<Component*, QStringList> replacementToExchangeables;
+ };
+
+ bool updateComponentData(struct Data &data, QInstaller::Component *component);
+ void storeReplacedComponents(QHash<QString, Component*> &components, const struct Data &data);
+ bool fetchAllPackages(const PackagesList &remotePackages, const LocalPackagesHash &localPackages);
+ bool fetchUpdaterPackages(const PackagesList &remotePackages, const LocalPackagesHash &localPackages);
+
+ static Component *subComponentByName(const QInstaller::PackageManagerCore *installer, const QString &name,
+ const QString &version = QString(), Component *check = 0);
+
+ void updateDisplayVersions(const QString &displayKey);
+ QString findDisplayVersion(const QString &componentName, const QHash<QString, QInstaller::Component*> &components,
+ const QString& versionKey, QHash<QString, bool> &visited);
+private:
+ PackageManagerCorePrivate *const d;
+ friend class PackageManagerCorePrivate;
+
+private:
+ // remove once we deprecate isSelected, setSelected etc...
+ friend class ComponentSelectionPage;
+ void resetComponentsToUserCheckedState();
+};
+
+}
+
+Q_DECLARE_METATYPE(QInstaller::PackageManagerCore*)
+
+#endif // PACKAGEMANAGERCORE_H
diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp
new file mode 100644
index 000000000..765db4e4e
--- /dev/null
+++ b/src/libs/installer/packagemanagercore_p.cpp
@@ -0,0 +1,2326 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#include "packagemanagercore_p.h"
+
+#include "adminauthorization.h"
+#include "binaryformat.h"
+#include "component.h"
+#include "errors.h"
+#include "fileutils.h"
+#include "fsengineclient.h"
+#include "messageboxhandler.h"
+#include "packagemanagercore.h"
+#include "progresscoordinator.h"
+#include "qprocesswrapper.h"
+#include "qsettingswrapper.h"
+
+#include "kdsavefile.h"
+#include "kdselfrestarter.h"
+#include "kdupdaterfiledownloaderfactory.h"
+#include "kdupdaterupdatesourcesinfo.h"
+#include "kdupdaterupdateoperationfactory.h"
+#include "kdupdaterupdatefinder.h"
+
+#include <QtCore/QtConcurrentRun>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDir>
+#include <QtCore/QDirIterator>
+#include <QtCore/QFuture>
+#include <QtCore/QFutureWatcher>
+#include <QtCore/QTemporaryFile>
+
+#include <QtXml/QXmlStreamReader>
+#include <QtXml/QXmlStreamWriter>
+
+#include <errno.h>
+
+namespace QInstaller {
+
+static bool runOperation(Operation *op, PackageManagerCorePrivate::OperationType type)
+{
+ switch (type) {
+ case PackageManagerCorePrivate::Backup:
+ op->backup();
+ return true;
+ case PackageManagerCorePrivate::Perform:
+ return op->performOperation();
+ case PackageManagerCorePrivate::Undo:
+ return op->undoOperation();
+ default:
+ Q_ASSERT(!"unexpected operation type");
+ }
+ return false;
+}
+
+/*!
+ \internal
+ Creates and initializes a FSEngineClientHandler -> makes us get admin rights for QFile operations
+*/
+static FSEngineClientHandler *sClientHandlerInstance = 0;
+static FSEngineClientHandler *initFSEngineClientHandler()
+{
+ if (sClientHandlerInstance == 0) {
+ sClientHandlerInstance = &FSEngineClientHandler::instance();
+
+ // Initialize the created FSEngineClientHandler instance.
+ const int port = 30000 + qrand() % 1000;
+ sClientHandlerInstance->init(port);
+ sClientHandlerInstance->setStartServerCommand(QCoreApplication::applicationFilePath(),
+ QStringList() << QLatin1String("--startserver") << QString::number(port)
+ << sClientHandlerInstance->authorizationKey(), true);
+ }
+ return sClientHandlerInstance;
+}
+
+static QStringList checkRunningProcessesFromList(const QStringList &processList)
+{
+ const QList<ProcessInfo> allProcesses = runningProcesses();
+ QStringList stillRunningProcesses;
+ foreach (const QString &process, processList) {
+ if (!process.isEmpty() && PackageManagerCorePrivate::isProcessRunning(process, allProcesses))
+ stillRunningProcesses.append(process);
+ }
+ return stillRunningProcesses;
+}
+
+static void deferredRename(const QString &oldName, const QString &newName, bool restart = false)
+{
+#ifdef Q_OS_WIN
+ QStringList arguments;
+ {
+ QTemporaryFile f(QDir::temp().absoluteFilePath(QLatin1String("deferredrenameXXXXXX.vbs")));
+ openForWrite(&f, f.fileName());
+ f.setAutoRemove(false);
+
+ arguments << QDir::toNativeSeparators(f.fileName()) << QDir::toNativeSeparators(oldName)
+ << QDir::toNativeSeparators(QFileInfo(oldName).dir().absoluteFilePath(QFileInfo(newName)
+ .fileName()));
+
+ QTextStream batch(&f);
+ batch << "Set fso = WScript.CreateObject(\"Scripting.FileSystemObject\")\n";
+ batch << "Set tmp = WScript.CreateObject(\"WScript.Shell\")\n";
+ batch << QString::fromLatin1("file = \"%1\"\n").arg(arguments[2]);
+ batch << "on error resume next\n";
+
+ batch << "while fso.FileExists(file)\n";
+ batch << " fso.DeleteFile(file)\n";
+ batch << " WScript.Sleep(1000)\n";
+ batch << "wend\n";
+ batch << QString::fromLatin1("fso.MoveFile \"%1\", file\n").arg(arguments[1]);
+ if (restart)
+ batch << QString::fromLatin1("tmp.exec \"%1 --updater\"\n").arg(arguments[2]);
+ batch << "fso.DeleteFile(WScript.ScriptFullName)\n";
+ }
+
+ QProcessWrapper::startDetached(QLatin1String("cscript"), QStringList() << QLatin1String("//Nologo")
+ << arguments[0]);
+#else
+ QFile::remove(newName);
+ QFile::rename(oldName, newName);
+ KDSelfRestarter::setRestartOnQuit(restart);
+#endif
+}
+
+
+// -- PackageManagerCorePrivate
+
+PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core)
+ : m_updateFinder(0)
+ , m_FSEngineClientHandler(0)
+ , m_core(core)
+ , m_repoMetaInfoJob(0)
+ , m_updates(false)
+ , m_repoFetched(false)
+ , m_updateSourcesAdded(false)
+ , m_componentsToInstallCalculated(false)
+ , m_proxyFactory(0)
+ , m_createLocalRepositoryFromBinary(false)
+{
+}
+
+PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, qint64 magicInstallerMaker,
+ const OperationList &performedOperations)
+ : m_updateFinder(0)
+ , m_FSEngineClientHandler(initFSEngineClientHandler())
+ , m_status(PackageManagerCore::Unfinished)
+ , m_forceRestart(false)
+ , m_testChecksum(false)
+ , m_launchedAsRoot(AdminAuthorization::hasAdminRights())
+ , m_completeUninstall(false)
+ , m_needToWriteUninstaller(false)
+ , m_performedOperationsOld(performedOperations)
+ , m_core(core)
+ , m_repoMetaInfoJob(0)
+ , m_updates(false)
+ , m_repoFetched(false)
+ , m_updateSourcesAdded(false)
+ , m_magicBinaryMarker(magicInstallerMaker)
+ , m_componentsToInstallCalculated(false)
+ , m_proxyFactory(0)
+ , m_createLocalRepositoryFromBinary(false)
+{
+ connect(this, SIGNAL(installationStarted()), m_core, SIGNAL(installationStarted()));
+ connect(this, SIGNAL(installationFinished()), m_core, SIGNAL(installationFinished()));
+ connect(this, SIGNAL(uninstallationStarted()), m_core, SIGNAL(uninstallationStarted()));
+ connect(this, SIGNAL(uninstallationFinished()), m_core, SIGNAL(uninstallationFinished()));
+}
+
+PackageManagerCorePrivate::~PackageManagerCorePrivate()
+{
+ clearAllComponentLists();
+ clearUpdaterComponentLists();
+ clearComponentsToInstall();
+
+ qDeleteAll(m_ownedOperations);
+ qDeleteAll(m_performedOperationsOld);
+ qDeleteAll(m_performedOperationsCurrentSession);
+
+ // check for fake installer case
+ if (m_FSEngineClientHandler)
+ m_FSEngineClientHandler->setActive(false);
+
+ delete m_updateFinder;
+ delete m_proxyFactory;
+}
+
+/*!
+ Return true, if a process with \a name is running. On Windows, comparison is case-insensitive.
+*/
+/* static */
+bool PackageManagerCorePrivate::isProcessRunning(const QString &name,
+ const QList<ProcessInfo> &processes)
+{
+ QList<ProcessInfo>::const_iterator it;
+ for (it = processes.constBegin(); it != processes.constEnd(); ++it) {
+ if (it->name.isEmpty())
+ continue;
+
+#ifndef Q_WS_WIN
+ if (it->name == name)
+ return true;
+ const QFileInfo fi(it->name);
+ if (fi.fileName() == name || fi.baseName() == name)
+ return true;
+#else
+ if (it->name.toLower() == name.toLower())
+ return true;
+ if (it->name.toLower() == QDir::toNativeSeparators(name.toLower()))
+ return true;
+ const QFileInfo fi(it->name);
+ if (fi.fileName().toLower() == name.toLower() || fi.baseName().toLower() == name.toLower())
+ return true;
+#endif
+ }
+ return false;
+}
+
+/* static */
+bool PackageManagerCorePrivate::performOperationThreaded(Operation *operation, OperationType type)
+{
+ QFutureWatcher<bool> futureWatcher;
+ const QFuture<bool> future = QtConcurrent::run(runOperation, operation, type);
+
+ QEventLoop loop;
+ loop.connect(&futureWatcher, SIGNAL(finished()), SLOT(quit()), Qt::QueuedConnection);
+ futureWatcher.setFuture(future);
+
+ if (!future.isFinished())
+ loop.exec();
+
+ return future.result();
+}
+
+QString PackageManagerCorePrivate::targetDir() const
+{
+ return m_core->value(scTargetDir);
+}
+
+QString PackageManagerCorePrivate::configurationFileName() const
+{
+ return m_core->value(scTargetConfigurationFile, QLatin1String("components.xml"));
+}
+
+QString PackageManagerCorePrivate::componentsXmlPath() const
+{
+ return QDir::toNativeSeparators(QDir(QDir::cleanPath(targetDir()))
+ .absoluteFilePath(configurationFileName()));
+}
+
+bool PackageManagerCorePrivate::buildComponentTree(QHash<QString, Component*> &components, bool loadScript)
+{
+ try {
+ // append all components to their respective parents
+ QHash<QString, Component*>::const_iterator it;
+ for (it = components.begin(); it != components.end(); ++it) {
+ if (statusCanceledOrFailed())
+ return false;
+
+ QString id = it.key();
+ QInstaller::Component *component = it.value();
+ while (!id.isEmpty() && component->parentComponent() == 0) {
+ id = id.section(QLatin1Char('.'), 0, -2);
+ if (components.contains(id))
+ components[id]->appendComponent(component);
+ }
+ }
+
+ // append all components w/o parent to the direct list
+ foreach (QInstaller::Component *component, components) {
+ if (statusCanceledOrFailed())
+ return false;
+
+ if (component->parentComponent() == 0)
+ m_core->appendRootComponent(component);
+ }
+
+ // after everything is set up, load the scripts
+ foreach (QInstaller::Component *component, components) {
+ if (statusCanceledOrFailed())
+ return false;
+
+ if (loadScript)
+ component->loadComponentScript();
+
+ // set the checked state for all components without child (means without tristate)
+ if (component->isCheckable() && !component->isTristate()) {
+ if (component->isDefault() && isInstaller())
+ component->setCheckState(Qt::Checked);
+ else if (component->isInstalled())
+ component->setCheckState(Qt::Checked);
+ }
+ }
+ } catch (const Error &error) {
+ clearAllComponentLists();
+ emit m_core->finishAllComponentsReset();
+ setStatus(PackageManagerCore::Failure, error.message());
+
+ // TODO: make sure we remove all message boxes inside the library at some point.
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"),
+ tr("Error"), error.message());
+ return false;
+ }
+ return true;
+}
+
+void PackageManagerCorePrivate::clearAllComponentLists()
+{
+ qDeleteAll(m_rootComponents);
+ m_rootComponents.clear();
+
+ m_rootDependencyReplacements.clear();
+
+ const QList<QPair<Component*, Component*> > list = m_componentsToReplaceAllMode.values();
+ for (int i = 0; i < list.count(); ++i)
+ delete list.at(i).second;
+ m_componentsToReplaceAllMode.clear();
+ m_componentsToInstallCalculated = false;
+}
+
+void PackageManagerCorePrivate::clearUpdaterComponentLists()
+{
+ QSet<Component*> usedComponents =
+ QSet<Component*>::fromList(m_updaterComponents + m_updaterComponentsDeps);
+
+ const QList<QPair<Component*, Component*> > list = m_componentsToReplaceUpdaterMode.values();
+ for (int i = 0; i < list.count(); ++i) {
+ if (usedComponents.contains(list.at(i).second))
+ qWarning() << "a replacement was already in the list - is that correct?";
+ else
+ usedComponents.insert(list.at(i).second);
+ }
+
+ qDeleteAll(usedComponents);
+
+ m_updaterComponents.clear();
+ m_updaterComponentsDeps.clear();
+
+ m_updaterDependencyReplacements.clear();
+
+ m_componentsToReplaceUpdaterMode.clear();
+ m_componentsToInstallCalculated = false;
+}
+
+QList<Component *> &PackageManagerCorePrivate::replacementDependencyComponents(RunMode mode)
+{
+ return mode == AllMode ? m_rootDependencyReplacements : m_updaterDependencyReplacements;
+}
+
+QHash<QString, QPair<Component*, Component*> > &PackageManagerCorePrivate::componentsToReplace(RunMode mode)
+{
+ return mode == AllMode ? m_componentsToReplaceAllMode : m_componentsToReplaceUpdaterMode;
+}
+
+void PackageManagerCorePrivate::clearComponentsToInstall()
+{
+ m_visitedComponents.clear();
+ m_toInstallComponentIds.clear();
+ m_componentsToInstallError.clear();
+ m_orderedComponentsToInstall.clear();
+ m_toInstallComponentIdReasonHash.clear();
+}
+
+bool PackageManagerCorePrivate::appendComponentsToInstall(const QList<Component *> &components)
+{
+ if (components.isEmpty()) {
+ qDebug() << "components list is empty in" << Q_FUNC_INFO;
+ return true;
+ }
+
+ QList<Component*> relevantComponentForAutoDependOn;
+ if (isUpdater())
+ relevantComponentForAutoDependOn = m_updaterComponents + m_updaterComponentsDeps;
+ else {
+ foreach (QInstaller::Component *component, m_rootComponents)
+ relevantComponentForAutoDependOn += component->childComponents(true, AllMode);
+ }
+
+ QList<Component*> notAppendedComponents; // for example components with unresolved dependencies
+ foreach (Component *component, components){
+ if (m_toInstallComponentIds.contains(component->name())) {
+ QString errorMessage = QString::fromLatin1("Recursion detected component(%1) already added with "
+ "reason: \"%2\"").arg(component->name(), installReason(component));
+ qDebug() << qPrintable(errorMessage);
+ m_componentsToInstallError.append(errorMessage);
+ Q_ASSERT_X(!m_toInstallComponentIds.contains(component->name()), Q_FUNC_INFO,
+ qPrintable(errorMessage));
+ return false;
+ }
+
+ if (component->dependencies().isEmpty())
+ realAppendToInstallComponents(component);
+ else
+ notAppendedComponents.append(component);
+ }
+
+ foreach (Component *component, notAppendedComponents) {
+ if (!appendComponentToInstall(component))
+ return false;
+ }
+
+ QList<Component *> foundAutoDependOnList;
+ // All regular dependencies are resolved. Now we are looking for auto depend on components.
+ foreach (Component *component, relevantComponentForAutoDependOn) {
+ // If a components is already installed or is scheduled for installation, no need to check for
+ // auto depend installation.
+ if ((!component->isInstalled() || component->updateRequested())
+ && !m_toInstallComponentIds.contains(component->name())) {
+ // If we figure out a component requests auto installation, keep it to resolve their deps as
+ // well.
+ if (component->isAutoDependOn(m_toInstallComponentIds)) {
+ foundAutoDependOnList.append(component);
+ insertInstallReason(component, tr("Component(s) added as automatic dependencies"));
+ }
+ }
+ }
+
+ if (!foundAutoDependOnList.isEmpty())
+ return appendComponentsToInstall(foundAutoDependOnList);
+ return true;
+}
+
+bool PackageManagerCorePrivate::appendComponentToInstall(Component *component)
+{
+ QSet<QString> allDependencies = component->dependencies().toSet();
+
+ foreach (const QString &dependencyComponentName, allDependencies) {
+ //componentByName return 0 if dependencyComponentName contains a version which is not available
+ Component *dependencyComponent = m_core->componentByName(dependencyComponentName);
+ if (dependencyComponent == 0) {
+ QString errorMessage;
+ if (!dependencyComponent)
+ errorMessage = QString::fromLatin1("Can't find missing dependency (%1) for %2.");
+ errorMessage = errorMessage.arg(dependencyComponentName, component->name());
+ qDebug() << qPrintable(errorMessage);
+ m_componentsToInstallError.append(errorMessage);
+ Q_ASSERT_X(false, Q_FUNC_INFO, qPrintable(errorMessage));
+ return false;
+ }
+
+ if ((!dependencyComponent->isInstalled() || dependencyComponent->updateRequested())
+ && !m_toInstallComponentIds.contains(dependencyComponent->name())) {
+ if (m_visitedComponents.value(component).contains(dependencyComponent)) {
+ QString errorMessage = QString::fromLatin1("Recursion detected component(%1) already "
+ "added with reason: \"%2\"").arg(component->name(), installReason(component));
+ qDebug() << qPrintable(errorMessage);
+ m_componentsToInstallError = errorMessage;
+ Q_ASSERT_X(!m_visitedComponents.value(component).contains(dependencyComponent), Q_FUNC_INFO,
+ qPrintable(errorMessage));
+ return false;
+ }
+ m_visitedComponents[component].insert(dependencyComponent);
+
+ // add needed dependency components to the next run
+ insertInstallReason(dependencyComponent, tr("Added as dependency for %1.").arg(component->name()));
+
+ if (!appendComponentToInstall(dependencyComponent))
+ return false;
+ }
+ }
+
+ if (!m_toInstallComponentIds.contains(component->name())) {
+ realAppendToInstallComponents(component);
+ insertInstallReason(component, tr("Component(s) that have resolved Dependencies"));
+ }
+ return true;
+}
+
+QString PackageManagerCorePrivate::installReason(Component *component)
+{
+ const QString reason = m_toInstallComponentIdReasonHash.value(component->name());
+ if (reason.isEmpty())
+ return tr("Selected Component(s) without Dependencies");
+ return m_toInstallComponentIdReasonHash.value(component->name());
+}
+
+
+void PackageManagerCorePrivate::initialize()
+{
+ m_coreCheckedHash.clear();
+ m_componentsToInstallCalculated = false;
+ m_createLocalRepositoryFromBinary = false;
+
+ // first set some common variables that may used e.g. as placeholder
+ // in some of the settings variables or in a script or...
+ m_vars.insert(QLatin1String("rootDir"), QDir::rootPath());
+ m_vars.insert(QLatin1String("homeDir"), QDir::homePath());
+ m_vars.insert(scTargetConfigurationFile, QLatin1String("components.xml"));
+
+#ifdef Q_WS_WIN
+ m_vars.insert(QLatin1String("os"), QLatin1String("win"));
+#elif defined(Q_WS_MAC)
+ m_vars.insert(QLatin1String("os"), QLatin1String("mac"));
+#elif defined(Q_WS_X11)
+ m_vars.insert(QLatin1String("os"), QLatin1String("x11"));
+#elif defined(Q_WS_QWS)
+ m_vars.insert(QLatin1String("os"), QLatin1String("Qtopia"));
+#else
+ // TODO: add more platforms as needed...
+#endif
+
+ try {
+ m_settings = Settings(Settings::fromFileAndPrefix(QLatin1String(":/metadata/installer-config/config.xml"),
+ QLatin1String(":/metadata/installer-config/")));
+ } catch (const Error &e) {
+ qCritical("Could not parse Config: %s", qPrintable(e.message()));
+ // TODO: try better error handling
+ return;
+ }
+
+ // fill the variables defined in the settings
+ m_vars.insert(QLatin1String("ProductName"), m_settings.applicationName());
+ m_vars.insert(QLatin1String("ProductVersion"), m_settings.applicationVersion());
+ m_vars.insert(scTitle, m_settings.title());
+ m_vars.insert(scPublisher, m_settings.publisher());
+ m_vars.insert(QLatin1String("Url"), m_settings.url());
+ m_vars.insert(scStartMenuDir, m_settings.startMenuDir());
+ m_vars.insert(scTargetConfigurationFile, m_settings.configurationFileName());
+ m_vars.insert(QLatin1String("LogoPixmap"), m_settings.logo());
+ m_vars.insert(QLatin1String("LogoSmallPixmap"), m_settings.logoSmall());
+ m_vars.insert(QLatin1String("WatermarkPixmap"), m_settings.watermark());
+
+ m_vars.insert(scRunProgram, replaceVariables(m_settings.runProgram()));
+ const QString desc = m_settings.runProgramDescription();
+ if (!desc.isEmpty())
+ m_vars.insert(scRunProgramDescription, desc);
+#ifdef Q_WS_X11
+ if (m_launchedAsRoot)
+ m_vars.insert(scTargetDir, replaceVariables(m_settings.adminTargetDir()));
+ else
+#endif
+ m_vars.insert(scTargetDir, replaceVariables(m_settings.targetDir()));
+ m_vars.insert(scRemoveTargetDir, replaceVariables(m_settings.removeTargetDir()));
+
+ QSettingsWrapper creatorSettings(QSettingsWrapper::IniFormat, QSettingsWrapper::UserScope,
+ QLatin1String("Nokia"), QLatin1String("QtCreator"));
+ QFileInfo info(creatorSettings.fileName());
+ if (info.exists()) {
+ m_vars.insert(QLatin1String("QtCreatorSettingsFile"), info.absoluteFilePath());
+ QDir settingsDirectory = info.absoluteDir();
+ if (settingsDirectory.exists(QLatin1String("qtversion.xml"))) {
+ m_vars.insert(QLatin1String("QtCreatorSettingsQtVersionFile"),
+ settingsDirectory.absoluteFilePath(QLatin1String("qtversion.xml")));
+ }
+ if (settingsDirectory.exists(QLatin1String("toolChains.xml"))) {
+ m_vars.insert(QLatin1String("QtCreatorSettingsToolchainsFile"),
+ settingsDirectory.absoluteFilePath(QLatin1String("toolChains.xml")));
+ }
+ }
+
+ if (!m_core->isInstaller()) {
+#ifdef Q_WS_MAC
+ readMaintenanceConfigFiles(QCoreApplication::applicationDirPath() + QLatin1String("/../../.."));
+#else
+ readMaintenanceConfigFiles(QCoreApplication::applicationDirPath());
+#endif
+ }
+
+ foreach (Operation *currentOperation, m_performedOperationsOld)
+ currentOperation->setValue(QLatin1String("installer"), QVariant::fromValue(m_core));
+
+ disconnect(this, SIGNAL(installationStarted()), ProgressCoordinator::instance(), SLOT(reset()));
+ connect(this, SIGNAL(installationStarted()), ProgressCoordinator::instance(), SLOT(reset()));
+ disconnect(this, SIGNAL(uninstallationStarted()), ProgressCoordinator::instance(), SLOT(reset()));
+ connect(this, SIGNAL(uninstallationStarted()), ProgressCoordinator::instance(), SLOT(reset()));
+
+ m_updaterApplication.updateSourcesInfo()->setFileName(QString());
+ KDUpdater::PackagesInfo &packagesInfo = *m_updaterApplication.packagesInfo();
+ packagesInfo.setFileName(componentsXmlPath());
+ if (packagesInfo.applicationName().isEmpty())
+ packagesInfo.setApplicationName(m_settings.applicationName());
+ if (packagesInfo.applicationVersion().isEmpty())
+ packagesInfo.setApplicationVersion(m_settings.applicationVersion());
+
+ if (isInstaller()) {
+ m_updaterApplication.addUpdateSource(m_settings.applicationName(), m_settings.applicationName(),
+ QString(), QUrl(QLatin1String("resource://metadata/")), 0);
+ m_updaterApplication.updateSourcesInfo()->setModified(false);
+ }
+
+ if (!m_repoMetaInfoJob) {
+ m_repoMetaInfoJob = new GetRepositoriesMetaInfoJob(this);
+ m_repoMetaInfoJob->setAutoDelete(false);
+ connect(m_repoMetaInfoJob, SIGNAL(infoMessage(KDJob*, QString)), this, SLOT(infoMessage(KDJob*,
+ QString)));
+ }
+ KDUpdater::FileDownloaderFactory::instance().setProxyFactory(m_core->proxyFactory());
+}
+
+bool PackageManagerCorePrivate::isOfflineOnly() const
+{
+ if (!isInstaller())
+ return false;
+
+ QSettingsWrapper confInternal(QLatin1String(":/config/config-internal.ini"), QSettingsWrapper::IniFormat);
+ return confInternal.value(QLatin1String("offlineOnly"), false).toBool();
+}
+
+QString PackageManagerCorePrivate::installerBinaryPath() const
+{
+ return qApp->applicationFilePath();
+}
+
+bool PackageManagerCorePrivate::isInstaller() const
+{
+ return m_magicBinaryMarker == MagicInstallerMarker;
+}
+
+bool PackageManagerCorePrivate::isUninstaller() const
+{
+ return m_magicBinaryMarker == MagicUninstallerMarker;
+}
+
+bool PackageManagerCorePrivate::isUpdater() const
+{
+ return m_magicBinaryMarker == MagicUpdaterMarker;
+}
+
+bool PackageManagerCorePrivate::isPackageManager() const
+{
+ return m_magicBinaryMarker == MagicPackageManagerMarker;
+}
+
+bool PackageManagerCorePrivate::statusCanceledOrFailed() const
+{
+ return m_status == PackageManagerCore::Canceled || m_status == PackageManagerCore::Failure;
+}
+
+void PackageManagerCorePrivate::setStatus(int status, const QString &error)
+{
+ m_error = error;
+ if (!error.isEmpty())
+ qDebug() << m_error;
+ if (m_status != status) {
+ m_status = status;
+ emit m_core->statusChanged(PackageManagerCore::Status(m_status));
+ }
+}
+
+QString PackageManagerCorePrivate::replaceVariables(const QString &str) const
+{
+ static const QChar at = QLatin1Char('@');
+ QString res;
+ int pos = 0;
+ while (true) {
+ const int pos1 = str.indexOf(at, pos);
+ if (pos1 == -1)
+ break;
+ const int pos2 = str.indexOf(at, pos1 + 1);
+ if (pos2 == -1)
+ break;
+ res += str.mid(pos, pos1 - pos);
+ const QString name = str.mid(pos1 + 1, pos2 - pos1 - 1);
+ res += m_core->value(name);
+ pos = pos2 + 1;
+ }
+ res += str.mid(pos);
+ return res;
+}
+
+QByteArray PackageManagerCorePrivate::replaceVariables(const QByteArray &ba) const
+{
+ static const QChar at = QLatin1Char('@');
+ QByteArray res;
+ int pos = 0;
+ while (true) {
+ const int pos1 = ba.indexOf(at, pos);
+ if (pos1 == -1)
+ break;
+ const int pos2 = ba.indexOf(at, pos1 + 1);
+ if (pos2 == -1)
+ break;
+ res += ba.mid(pos, pos1 - pos);
+ const QString name = QString::fromLocal8Bit(ba.mid(pos1 + 1, pos2 - pos1 - 1));
+ res += m_core->value(name).toLocal8Bit();
+ pos = pos2 + 1;
+ }
+ res += ba.mid(pos);
+ return res;
+}
+
+/*!
+ \internal
+ Creates an update operation owned by the installer, not by any component.
+ */
+Operation *PackageManagerCorePrivate::createOwnedOperation(const QString &type)
+{
+ m_ownedOperations.append(KDUpdater::UpdateOperationFactory::instance().create(type));
+ return m_ownedOperations.last();
+}
+
+/*!
+ \internal
+ Removes \a operation from the operations owned by the installer, returns the very same operation if the
+ operation was found, otherwise 0.
+ */
+Operation *PackageManagerCorePrivate::takeOwnedOperation(Operation *operation)
+{
+ if (!m_ownedOperations.contains(operation))
+ return 0;
+
+ m_ownedOperations.removeAll(operation);
+ return operation;
+}
+
+QString PackageManagerCorePrivate::uninstallerName() const
+{
+ QString filename = m_settings.uninstallerName();
+#if defined(Q_WS_MAC)
+ if (QFileInfo(QCoreApplication::applicationDirPath() + QLatin1String("/../..")).isBundle())
+ filename += QLatin1String(".app/Contents/MacOS/") + filename;
+#elif defined(Q_OS_WIN)
+ filename += QLatin1String(".exe");
+#endif
+ return QString::fromLatin1("%1/%2").arg(targetDir()).arg(filename);
+}
+
+static QNetworkProxy readProxy(QXmlStreamReader &reader)
+{
+ QNetworkProxy proxy(QNetworkProxy::HttpProxy);
+ while (reader.readNextStartElement()) {
+ if (reader.name() == QLatin1String("Host"))
+ proxy.setHostName(reader.readElementText());
+ else if (reader.name() == QLatin1String("Port"))
+ proxy.setPort(reader.readElementText().toInt());
+ else if (reader.name() == QLatin1String("Username"))
+ proxy.setUser(reader.readElementText());
+ else if (reader.name() == QLatin1String("Password"))
+ proxy.setPassword(reader.readElementText());
+ else
+ reader.skipCurrentElement();
+ }
+ return proxy;
+}
+
+static QSet<Repository> readRepositories(QXmlStreamReader &reader, bool isDefault)
+{
+ QSet<Repository> set;
+ while (reader.readNextStartElement()) {
+ if (reader.name() == QLatin1String("Repository")) {
+ Repository repo(QString(), isDefault);
+ while (reader.readNextStartElement()) {
+ if (reader.name() == QLatin1String("Host"))
+ repo.setUrl(reader.readElementText());
+ else if (reader.name() == QLatin1String("Username"))
+ repo.setUsername(reader.readElementText());
+ else if (reader.name() == QLatin1String("Password"))
+ repo.setPassword(reader.readElementText());
+ else if (reader.name() == QLatin1String("Enabled"))
+ repo.setEnabled(bool(reader.readElementText().toInt()));
+ else
+ reader.skipCurrentElement();
+ }
+ set.insert(repo);
+ } else {
+ reader.skipCurrentElement();
+ }
+ }
+ return set;
+}
+
+void PackageManagerCorePrivate::writeMaintenanceConfigFiles()
+{
+ // write current state (variables) to the uninstaller ini file
+ const QString iniPath = targetDir() + QLatin1Char('/') + m_settings.uninstallerIniFile();
+
+ QVariantHash vars;
+ QSettingsWrapper cfg(iniPath, QSettingsWrapper::IniFormat);
+ foreach (const QString &key, m_vars.keys()) {
+ if (key != scRunProgramDescription && key != scRunProgram)
+ vars.insert(key, m_vars.value(key));
+ }
+ cfg.setValue(QLatin1String("Variables"), vars);
+
+ QVariantList repos;
+ foreach (const Repository &repo, m_settings.defaultRepositories())
+ repos.append(QVariant().fromValue(repo));
+ cfg.setValue(QLatin1String("DefaultRepositories"), repos);
+
+ cfg.sync();
+ if (cfg.status() != QSettingsWrapper::NoError) {
+ const QString reason = cfg.status() == QSettingsWrapper::AccessError ? tr("Access error")
+ : tr("Format error");
+ throw Error(tr("Could not write installer configuration to %1: %2").arg(iniPath, reason));
+ }
+
+ QFile file(targetDir() + QLatin1Char('/') + QLatin1String("network.xml"));
+ if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+ QXmlStreamWriter writer(&file);
+ writer.setCodec("UTF-8");
+ writer.setAutoFormatting(true);
+ writer.writeStartDocument();
+
+ writer.writeStartElement(QLatin1String("Network"));
+ writer.writeTextElement(QLatin1String("ProxyType"), QString::number(m_settings.proxyType()));
+ writer.writeStartElement(QLatin1String("Ftp"));
+ const QNetworkProxy &ftpProxy = m_settings.ftpProxy();
+ writer.writeTextElement(QLatin1String("Host"), ftpProxy.hostName());
+ writer.writeTextElement(QLatin1String("Port"), QString::number(ftpProxy.port()));
+ writer.writeTextElement(QLatin1String("Username"), ftpProxy.user());
+ writer.writeTextElement(QLatin1String("Password"), ftpProxy.password());
+ writer.writeEndElement();
+ writer.writeStartElement(QLatin1String("Http"));
+ const QNetworkProxy &httpProxy = m_settings.httpProxy();
+ writer.writeTextElement(QLatin1String("Host"), httpProxy.hostName());
+ writer.writeTextElement(QLatin1String("Port"), QString::number(httpProxy.port()));
+ writer.writeTextElement(QLatin1String("Username"), httpProxy.user());
+ writer.writeTextElement(QLatin1String("Password"), httpProxy.password());
+ writer.writeEndElement();
+
+ writer.writeStartElement(QLatin1String("Repositories"));
+ foreach (const Repository &repo, m_settings.userRepositories()) {
+ writer.writeStartElement(QLatin1String("Repository"));
+ writer.writeTextElement(QLatin1String("Host"), repo.url().toString());
+ writer.writeTextElement(QLatin1String("Username"), repo.username());
+ writer.writeTextElement(QLatin1String("Password"), repo.password());
+ writer.writeTextElement(QLatin1String("Enabled"), QString::number(repo.isEnabled()));
+ writer.writeEndElement();
+ }
+ writer.writeEndElement();
+ writer.writeEndElement();
+ }
+}
+
+void PackageManagerCorePrivate::readMaintenanceConfigFiles(const QString &targetDir)
+{
+ QSettingsWrapper cfg(targetDir + QLatin1Char('/') + m_settings.uninstallerIniFile(),
+ QSettingsWrapper::IniFormat);
+ const QVariantHash vars = cfg.value(QLatin1String("Variables")).toHash();
+ for (QHash<QString, QVariant>::ConstIterator it = vars.constBegin(); it != vars.constEnd(); ++it)
+ m_vars.insert(it.key(), it.value().toString());
+
+ QSet<Repository> repos;
+ const QVariantList variants = cfg.value(QLatin1String("DefaultRepositories")).toList();
+ foreach (const QVariant &variant, variants)
+ repos.insert(variant.value<Repository>());
+ if (!repos.isEmpty())
+ m_settings.setDefaultRepositories(repos);
+
+ QFile file(targetDir + QLatin1String("/network.xml"));
+ if (!file.open(QIODevice::ReadOnly))
+ return;
+
+ QXmlStreamReader reader(&file);
+ while (!reader.atEnd()) {
+ switch (reader.readNext()) {
+ case QXmlStreamReader::StartElement: {
+ if (reader.name() == QLatin1String("Network")) {
+ while (reader.readNextStartElement()) {
+ const QStringRef name = reader.name();
+ if (name == QLatin1String("Ftp")) {
+ m_settings.setFtpProxy(readProxy(reader));
+ } else if (name == QLatin1String("Http")) {
+ m_settings.setHttpProxy(readProxy(reader));
+ } else if (reader.name() == QLatin1String("Repositories")) {
+ m_settings.addUserRepositories(readRepositories(reader, false));
+ } else if (name == QLatin1String("ProxyType")) {
+ m_settings.setProxyType(Settings::ProxyType(reader.readElementText().toInt()));
+ } else {
+ reader.skipCurrentElement();
+ }
+ }
+ }
+ } break;
+
+ case QXmlStreamReader::Invalid: {
+ qDebug() << reader.errorString();
+ } break;
+
+ default:
+ break;
+ }
+ }
+}
+
+void PackageManagerCorePrivate::callBeginInstallation(const QList<Component*> &componentList)
+{
+ foreach (Component *component, componentList)
+ component->beginInstallation();
+}
+
+void PackageManagerCorePrivate::stopProcessesForUpdates(const QList<Component*> &components)
+{
+ QStringList processList;
+ foreach (const Component *component, components)
+ processList << m_core->replaceVariables(component->stopProcessForUpdateRequests());
+
+ qSort(processList);
+ processList.erase(std::unique(processList.begin(), processList.end()), processList.end());
+ if (processList.isEmpty())
+ return;
+
+ while (true) {
+ const QList<ProcessInfo> allProcesses = runningProcesses(); // FIXME: Unused?
+ const QStringList processes = checkRunningProcessesFromList(processList);
+ if (processes.isEmpty())
+ return;
+
+ const QMessageBox::StandardButton button =
+ MessageBoxHandler::warning(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("stopProcessesForUpdates"), tr("Stop Processes"), tr("These processes "
+ "should be stopped to continue:\n\n%1").arg(QDir::toNativeSeparators(processes
+ .join(QLatin1String("\n")))), QMessageBox::Retry | QMessageBox::Ignore
+ | QMessageBox::Cancel, QMessageBox::Retry);
+ if (button == QMessageBox::Ignore)
+ return;
+ if (button == QMessageBox::Cancel) {
+ m_core->setCanceled();
+ throw Error(tr("Installation canceled by user"));
+ }
+ }
+}
+
+int PackageManagerCorePrivate::countProgressOperations(const OperationList &operations)
+{
+ int operationCount = 0;
+ foreach (Operation *operation, operations) {
+ if (QObject *operationObject = dynamic_cast<QObject*> (operation)) {
+ const QMetaObject *const mo = operationObject->metaObject();
+ if (mo->indexOfSignal(QMetaObject::normalizedSignature("progressChanged(double)")) > -1)
+ operationCount++;
+ }
+ }
+ return operationCount;
+}
+
+int PackageManagerCorePrivate::countProgressOperations(const QList<Component*> &components)
+{
+ int operationCount = 0;
+ foreach (Component *component, components)
+ operationCount += countProgressOperations(component->operations());
+
+ return operationCount;
+}
+
+void PackageManagerCorePrivate::connectOperationToInstaller(Operation *const operation, double operationPartSize)
+{
+ Q_ASSERT(operationPartSize);
+ QObject *const operationObject = dynamic_cast< QObject*> (operation);
+ if (operationObject != 0) {
+ const QMetaObject *const mo = operationObject->metaObject();
+ if (mo->indexOfSignal(QMetaObject::normalizedSignature("outputTextChanged(QString)")) > -1) {
+ connect(operationObject, SIGNAL(outputTextChanged(QString)), ProgressCoordinator::instance(),
+ SLOT(emitDetailTextChanged(QString)));
+ }
+
+ if (mo->indexOfSlot(QMetaObject::normalizedSignature("cancelOperation()")) > -1)
+ connect(m_core, SIGNAL(installationInterrupted()), operationObject, SLOT(cancelOperation()));
+
+ if (mo->indexOfSignal(QMetaObject::normalizedSignature("progressChanged(double)")) > -1) {
+ ProgressCoordinator::instance()->registerPartProgress(operationObject,
+ SIGNAL(progressChanged(double)), operationPartSize);
+ }
+ }
+}
+
+Operation *PackageManagerCorePrivate::createPathOperation(const QFileInfo &fileInfo,
+ const QString &componentName)
+{
+ const bool isDir = fileInfo.isDir();
+ // create an operation with the dir/ file as target, it will get deleted on undo
+ Operation *operation = createOwnedOperation(QLatin1String(isDir ? "Mkdir" : "Copy"));
+ if (isDir)
+ operation->setValue(QLatin1String("createddir"), fileInfo.absoluteFilePath());
+ operation->setValue(QLatin1String("component"), componentName);
+ operation->setArguments(isDir ? QStringList() << fileInfo.absoluteFilePath()
+ : QStringList() << QString() << fileInfo.absoluteFilePath());
+ return operation;
+}
+
+/*!
+ This creates fake operations which remove stuff which was registered for uninstallation afterwards
+*/
+void PackageManagerCorePrivate::registerPathesForUninstallation(
+ const QList<QPair<QString, bool> > &pathesForUninstallation, const QString &componentName)
+{
+ if (pathesForUninstallation.isEmpty())
+ return;
+
+ QList<QPair<QString, bool> >::const_iterator it;
+ for (it = pathesForUninstallation.begin(); it != pathesForUninstallation.end(); ++it) {
+ const bool wipe = it->second;
+ const QString path = replaceVariables(it->first);
+
+ const QFileInfo fi(path);
+ // create a copy operation with the file as target -> it will get deleted on undo
+ Operation *op = createPathOperation(fi, componentName);
+ if (fi.isDir())
+ op->setValue(QLatin1String("forceremoval"), wipe ? scTrue : scFalse);
+ addPerformed(takeOwnedOperation(op));
+
+ // get recursive afterwards
+ if (fi.isDir() && !wipe) {
+ QDirIterator dirIt(path, QDir::Hidden | QDir::AllEntries | QDir::System
+ | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
+ while (dirIt.hasNext()) {
+ dirIt.next();
+ op = createPathOperation(dirIt.fileInfo(), componentName);
+ addPerformed(takeOwnedOperation(op));
+ }
+ }
+ }
+}
+
+void PackageManagerCorePrivate::writeUninstallerBinary(QFile *const input, qint64 size, bool writeBinaryLayout)
+{
+ QString uninstallerRenamedName = uninstallerName() + QLatin1String(".new");
+ qDebug() << "Writing uninstaller:" << uninstallerRenamedName;
+
+ KDSaveFile out(uninstallerRenamedName);
+ openForWrite(&out, out.fileName()); // throws an exception in case of error
+
+ if (!input->seek(0))
+ throw Error(QObject::tr("Failed to seek in file %1: %2").arg(input->fileName(), input->errorString()));
+
+ appendData(&out, input, size);
+ if (writeBinaryLayout) {
+ appendInt64(&out, 0); // resource count
+ appendInt64(&out, 4 * sizeof(qint64)); // data block size
+ appendInt64(&out, QInstaller::MagicUninstallerMarker);
+ appendInt64(&out, QInstaller::MagicCookie);
+ }
+ out.setPermissions(out.permissions() | QFile::WriteUser | QFile::ReadGroup | QFile::ReadOther
+ | QFile::ExeOther | QFile::ExeGroup | QFile::ExeUser);
+
+ if (!out.commit(KDSaveFile::OverwriteExistingFile))
+ throw Error(tr("Could not write uninstaller to %1: %2").arg(out.fileName(), out.errorString()));
+}
+
+void PackageManagerCorePrivate::writeUninstallerBinaryData(QIODevice *output, QFile *const input,
+ const OperationList &performedOperations, const BinaryLayout &layout)
+{
+ const qint64 dataBlockStart = output->pos();
+
+ QVector<Range<qint64> >resourceSegments;
+ foreach (const Range<qint64> &segment, layout.metadataResourceSegments) {
+ input->seek(segment.start());
+ resourceSegments.append(Range<qint64>::fromStartAndLength(output->pos(), segment.length()));
+ appendData(output, input, segment.length());
+ }
+
+ const qint64 operationsStart = output->pos();
+ appendInt64(output, performedOperations.count());
+ foreach (Operation *operation, performedOperations) {
+ // the installer can't be put into XML, remove it first
+ operation->clearValue(QLatin1String("installer"));
+
+ appendString(output, operation->name());
+ appendString(output, operation->toXml().toString());
+
+ // for the ui not to get blocked
+ qApp->processEvents();
+ }
+ appendInt64(output, performedOperations.count());
+ const qint64 operationsEnd = output->pos();
+
+ // we don't save any component-indexes.
+ const qint64 numComponents = 0;
+ appendInt64(output, numComponents); // for the indexes
+ // we don't save any components.
+ const qint64 compIndexStart = output->pos();
+ appendInt64(output, numComponents); // and 2 times number of components,
+ appendInt64(output, numComponents); // one before and one after the components
+ const qint64 compIndexEnd = output->pos();
+
+ appendInt64Range(output, Range<qint64>::fromStartAndEnd(compIndexStart, compIndexEnd)
+ .moved(-dataBlockStart));
+ foreach (const Range<qint64> segment, resourceSegments)
+ appendInt64Range(output, segment.moved(-dataBlockStart));
+ appendInt64Range(output, Range<qint64>::fromStartAndEnd(operationsStart, operationsEnd)
+ .moved(-dataBlockStart));
+ appendInt64(output, layout.resourceCount);
+ //data block size, from end of .exe to end of file
+ appendInt64(output, output->pos() + 3 * sizeof(qint64) - dataBlockStart);
+ appendInt64(output, MagicUninstallerMarker);
+}
+
+void PackageManagerCorePrivate::writeUninstaller(OperationList performedOperations)
+{
+ bool gainedAdminRights = false;
+ QTemporaryFile tempAdminFile(targetDir() + QLatin1String("/testjsfdjlkdsjflkdsjfldsjlfds")
+ + QString::number(qrand() % 1000));
+ if (!tempAdminFile.open() || !tempAdminFile.isWritable()) {
+ m_core->gainAdminRights();
+ gainedAdminRights = true;
+ }
+
+ const QString targetAppDirPath = QFileInfo(uninstallerName()).path();
+ if (!QDir().exists(targetAppDirPath)) {
+ // create the directory containing the uninstaller (like a bundle structor, on Mac...)
+ Operation *op = createOwnedOperation(QLatin1String("Mkdir"));
+ op->setArguments(QStringList() << targetAppDirPath);
+ performOperationThreaded(op, Backup);
+ performOperationThreaded(op);
+ performedOperations.append(takeOwnedOperation(op));
+ }
+
+ writeMaintenanceConfigFiles();
+
+#ifdef Q_WS_MAC
+ // if it is a bundle, we need some stuff in it...
+ const QString sourceAppDirPath = QCoreApplication::applicationDirPath();
+ if (isInstaller() && QFileInfo(sourceAppDirPath + QLatin1String("/../..")).isBundle()) {
+ Operation *op = createOwnedOperation(QLatin1String("Copy"));
+ op->setArguments(QStringList() << (sourceAppDirPath + QLatin1String("/../PkgInfo"))
+ << (targetAppDirPath + QLatin1String("/../PkgInfo")));
+ performOperationThreaded(op, Backup);
+ performOperationThreaded(op);
+
+ // copy Info.plist to target directory
+ op = createOwnedOperation(QLatin1String("Copy"));
+ op->setArguments(QStringList() << (sourceAppDirPath + QLatin1String("/../Info.plist"))
+ << (targetAppDirPath + QLatin1String("/../Info.plist")));
+ performOperationThreaded(op, Backup);
+ performOperationThreaded(op);
+
+ // patch the Info.plist after copying it
+ QFile sourcePlist(sourceAppDirPath + QLatin1String("/../Info.plist"));
+ openForRead(&sourcePlist, sourcePlist.fileName());
+ QFile targetPlist(targetAppDirPath + QLatin1String("/../Info.plist"));
+ openForWrite(&targetPlist, targetPlist.fileName());
+
+ QTextStream in(&sourcePlist);
+ QTextStream out(&targetPlist);
+ const QString before = QLatin1String("<string>") + QFileInfo(QCoreApplication::applicationFilePath())
+ .baseName() + QLatin1String("</string>");
+ const QString after = QLatin1String("<string>") + QFileInfo(uninstallerName()).baseName()
+ + QLatin1String("</string>");
+ while (!in.atEnd())
+ out << in.readLine().replace(before, after) << endl;
+
+ // copy qt_menu.nib if it exists
+ op = createOwnedOperation(QLatin1String("Mkdir"));
+ op->setArguments(QStringList() << (targetAppDirPath + QLatin1String("/../Resources/qt_menu.nib")));
+ if (!op->performOperation()) {
+ qDebug() << "ERROR in Mkdir operation:" << op->errorString();
+ }
+
+ op = createOwnedOperation(QLatin1String("CopyDirectory"));
+ op->setArguments(QStringList() << (sourceAppDirPath + QLatin1String("/../Resources/qt_menu.nib"))
+ << (targetAppDirPath + QLatin1String("/../Resources/qt_menu.nib")));
+ performOperationThreaded(op);
+
+ op = createOwnedOperation(QLatin1String("Mkdir"));
+ op->setArguments(QStringList() << (QFileInfo(targetAppDirPath).path() + QLatin1String("/Resources")));
+ performOperationThreaded(op, Backup);
+ performOperationThreaded(op);
+
+ // copy application icons if it exists
+ const QString icon = QFileInfo(QCoreApplication::applicationFilePath()).baseName()
+ + QLatin1String(".icns");
+ op = createOwnedOperation(QLatin1String("Copy"));
+ op->setArguments(QStringList() << (sourceAppDirPath + QLatin1String("/../Resources/") + icon)
+ << (targetAppDirPath + QLatin1String("/../Resources/") + icon));
+ performOperationThreaded(op, Backup);
+ performOperationThreaded(op);
+
+ // finally, copy everything within Frameworks and plugins
+ op = createOwnedOperation(QLatin1String("CopyDirectory"));
+ op->setArguments(QStringList() << (sourceAppDirPath + QLatin1String("/../Frameworks"))
+ << (targetAppDirPath + QLatin1String("/../Frameworks")));
+ performOperationThreaded(op);
+
+ op = createOwnedOperation(QLatin1String("CopyDirectory"));
+ op->setArguments(QStringList() << (sourceAppDirPath + QLatin1String("/../plugins"))
+ << (targetAppDirPath + QLatin1String("/../plugins")));
+ performOperationThreaded(op);
+ }
+#endif
+
+ try {
+ // 1 - check if we have a installer base replacement
+ // |--- if so, write out the new tool and remove the replacement
+ // |--- remember to restart and that we need to replace the original binary
+ //
+ // 2 - if we do not have a replacement, try to open the binary data file as input
+ // |--- try to read the binary layout
+ // |--- on error (see 2.1)
+ // |--- remember we might to append uncompressed resource data (see 3)
+ // |--- set the installer or maintenance binary as input to take over binary data
+ // |--- in case we did not have a replacement, write out an new maintenance tool binary
+ // |--- remember that we need to replace the original binary
+ //
+ // 3 - open a new binary data file
+ // |--- try to write out the binary data based on the loaded input file (see 2)
+ // |--- on error (see 3.1)
+ // |--- if we wrote a new maintenance tool, take this as output - if not, write out a new
+ // one and set it as output file, remember we did this
+ // |--- append the binary data based on the loaded input file (see 2), make sure we force
+ // uncompressing the resource section if we read from a binary data file (see 4.1).
+ //
+ // 4 - force a deferred rename on the .dat file (see 4.1)
+ // 5 - force a deferred rename on the maintenance file (see 5.1)
+
+ // 2.1 - Error cases are: no data file (in fact we are the installer or an old installation),
+ // could not find the data file magic cookie (unknown .dat file), failed to read binary
+ // layout (mostly likely the resource section or we couldn't seek inside the file)
+ //
+ // 3.1 - most likely the commit operation will fail
+ // 4.1 - if 3 failed, this makes sure the .dat file will get removed and on the next run all
+ // binary data is read from the maintenance tool, otherwise it get replaced be the new one
+ // 5.1 - this will only happen -if- we wrote out a new binary
+
+ bool newBinaryWritten = false;
+ bool replacementExists = false;
+ const QString installerBaseBinary = m_core->replaceVariables(m_installerBaseBinaryUnreplaced);
+ if (!installerBaseBinary.isEmpty() && QFileInfo(installerBaseBinary).exists()) {
+ qDebug() << "Got a replacement installer base binary:" << installerBaseBinary;
+
+ QFile replacementBinary(installerBaseBinary);
+ try {
+ openForRead(&replacementBinary, replacementBinary.fileName());
+ writeUninstallerBinary(&replacementBinary, replacementBinary.size(), true);
+
+ m_forceRestart = true;
+ newBinaryWritten = true;
+ replacementExists = true;
+ } catch (const Error &error) {
+ qDebug() << error.message();
+ }
+
+ if (!replacementBinary.remove()) {
+ // Is there anything more sensible we can do with this error? I think not. It's not serious
+ // enough for throwing/ aborting the process.
+ qDebug() << QString::fromLatin1("Could not remove installer base binary (%1) after updating "
+ "the uninstaller: %2").arg(installerBaseBinary, replacementBinary.errorString());
+ }
+ m_installerBaseBinaryUnreplaced.clear();
+ }
+
+ QFile input;
+ BinaryLayout layout;
+ const QString dataFile = targetDir() + QLatin1Char('/') + m_settings.uninstallerName()
+ + QLatin1String(".dat");
+ try {
+ if (isInstaller()) {
+ throw Error(tr("Found a binary data file, but we are the installer and we should read the "
+ "binary resource from our very own binary!"));
+ }
+ input.setFileName(dataFile);
+ openForRead(&input, input.fileName());
+ layout = BinaryContent::readBinaryLayout(&input, findMagicCookie(&input, MagicCookieDat));
+ } catch (const Error &/*error*/) {
+ input.setFileName(isInstaller() ? installerBinaryPath() : uninstallerName());
+ openForRead(&input, input.fileName());
+ layout = BinaryContent::readBinaryLayout(&input, findMagicCookie(&input, MagicCookie));
+ if (!newBinaryWritten) {
+ newBinaryWritten = true;
+ writeUninstallerBinary(&input, layout.endOfData - layout.dataBlockSize, true);
+ }
+ }
+
+ try {
+ KDSaveFile file(dataFile + QLatin1String(".new"));
+ openForWrite(&file, file.fileName());
+ writeUninstallerBinaryData(&file, &input, performedOperations, layout);
+ appendInt64(&file, MagicCookieDat);
+ file.setPermissions(file.permissions() | QFile::WriteUser | QFile::ReadGroup
+ | QFile::ReadOther);
+ if (!file.commit(KDSaveFile::OverwriteExistingFile)) {
+ throw Error(tr("Could not write uninstaller binary data to %1: %2").arg(file.fileName(),
+ file.errorString()));
+ }
+ } catch (const Error &/*error*/) {
+ if (!newBinaryWritten) {
+ newBinaryWritten = true;
+ QFile tmp(isInstaller() ? installerBinaryPath() : uninstallerName());
+ openForRead(&tmp, tmp.fileName());
+ BinaryLayout tmpLayout = BinaryContent::readBinaryLayout(&tmp, findMagicCookie(&tmp, MagicCookie));
+ writeUninstallerBinary(&tmp, tmpLayout.endOfData - tmpLayout.dataBlockSize, false);
+ }
+
+ QFile file(uninstallerName() + QLatin1String(".new"));
+ openForAppend(&file, file.fileName());
+ file.seek(file.size());
+ writeUninstallerBinaryData(&file, &input, performedOperations, layout);
+ appendInt64(&file, MagicCookie);
+ }
+ input.close();
+ deferredRename(dataFile + QLatin1String(".new"), dataFile, false);
+
+ if (newBinaryWritten) {
+ const bool restart = replacementExists && isUpdater() && (!statusCanceledOrFailed());
+ deferredRename(uninstallerName() + QLatin1String(".new"), uninstallerName(), restart);
+ qDebug() << "Maintenance tool restart:" << (restart ? "true." : "false.");
+ }
+ } catch (const Error &err) {
+ setStatus(PackageManagerCore::Failure);
+ if (gainedAdminRights)
+ m_core->dropAdminRights();
+ m_needToWriteUninstaller = false;
+ throw err;
+ }
+
+ if (gainedAdminRights)
+ m_core->dropAdminRights();
+
+ commitSessionOperations();
+
+ m_needToWriteUninstaller = false;
+}
+
+QString PackageManagerCorePrivate::registerPath() const
+{
+#ifdef Q_OS_WIN
+ QString productName = m_vars.value(QLatin1String("ProductName"));
+ if (productName.isEmpty())
+ throw Error(tr("ProductName should be set"));
+
+ QString path = QLatin1String("HKEY_CURRENT_USER");
+ if (m_vars.value(QLatin1String("AllUsers")) == scTrue)
+ path = QLatin1String("HKEY_LOCAL_MACHINE");
+
+ return path + QLatin1String("\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\")
+ + productName;
+#endif
+ return QString();
+}
+
+void PackageManagerCorePrivate::runInstaller()
+{
+ bool adminRightsGained = false;
+ try {
+ setStatus(PackageManagerCore::Running);
+ emit installationStarted(); //resets also the ProgressCoordninator
+
+ //to have some progress for writeUninstaller
+ ProgressCoordinator::instance()->addReservePercentagePoints(1);
+
+ static const QLatin1String sep("/");
+ const QString target = QDir::cleanPath(targetDir().replace(QRegExp(QLatin1String("\\\\|/")), sep));
+ if (target.isEmpty())
+ throw Error(tr("Variable 'TargetDir' not set."));
+
+ if (!QDir(target).exists()) {
+ const QString &pathToTarget = target.mid(0, target.lastIndexOf(sep));
+ if (!QDir(pathToTarget).exists()) {
+ Operation *pathToTargetOp = createOwnedOperation(QLatin1String("Mkdir"));
+ pathToTargetOp->setArguments(QStringList() << pathToTarget);
+ if (!performOperationThreaded(pathToTargetOp)) {
+ adminRightsGained = m_core->gainAdminRights();
+ if (!performOperationThreaded(pathToTargetOp))
+ throw Error(pathToTargetOp->errorString());
+ }
+ }
+ } else if (QDir(target).exists()) {
+ QTemporaryFile tempAdminFile(target + QLatin1String("/adminrights"));
+ if (!tempAdminFile.open() || !tempAdminFile.isWritable())
+ adminRightsGained = m_core->gainAdminRights();
+ }
+
+ // add the operation to create the target directory
+ Operation *mkdirOp = createOwnedOperation(QLatin1String("Mkdir"));
+ mkdirOp->setArguments(QStringList() << target);
+ mkdirOp->setValue(QLatin1String("forceremoval"), true);
+ mkdirOp->setValue(QLatin1String("uninstall-only"), true);
+
+ performOperationThreaded(mkdirOp, Backup);
+ if (!performOperationThreaded(mkdirOp)) {
+ // if we cannot create the target dir, we try to activate the admin rights
+ adminRightsGained = m_core->gainAdminRights();
+ if (!performOperationThreaded(mkdirOp))
+ throw Error(mkdirOp->errorString());
+ }
+ const QString remove = m_core->value(scRemoveTargetDir);
+ if (QVariant(remove).toBool())
+ addPerformed(takeOwnedOperation(mkdirOp));
+
+ // to show that there was some work
+ ProgressCoordinator::instance()->addManualPercentagePoints(1);
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("Preparing the installation..."));
+
+ const QList<Component*> componentsToInstall = m_core->orderedComponentsToInstall();
+ qDebug() << "Install size:" << componentsToInstall.size() << "components";
+
+ if (!adminRightsGained) {
+ foreach (Component *component, m_core->orderedComponentsToInstall()) {
+ if (component->value(scRequiresAdminRights, scFalse) == scFalse)
+ continue;
+
+ m_core->gainAdminRights();
+ m_core->dropAdminRights();
+ break;
+ }
+ }
+
+ const double downloadPartProgressSize = double(1) / double(3);
+ double componentsInstallPartProgressSize = double(2) / double(3);
+ const int downloadedArchivesCount = m_core->downloadNeededArchives(downloadPartProgressSize);
+
+ // if there was no download we have the whole progress for installing components
+ if (!downloadedArchivesCount)
+ componentsInstallPartProgressSize = double(1);
+
+ // Force an update on the components xml as the install dir might have changed.
+ KDUpdater::PackagesInfo &info = *m_updaterApplication.packagesInfo();
+ info.setFileName(componentsXmlPath());
+ // Clear the packages as we might install into an already existing installation folder.
+ info.clearPackageInfoList();
+ // also update the application name and version, might be set from a script as well
+ info.setApplicationName(m_core->value(QLatin1String("ProductName"), m_settings.applicationName()));
+ info.setApplicationVersion(m_core->value(QLatin1String("ProductVersion"),
+ m_settings.applicationVersion()));
+
+ callBeginInstallation(componentsToInstall);
+ stopProcessesForUpdates(componentsToInstall);
+
+ const int progressOperationCount = countProgressOperations(componentsToInstall)
+ + (m_createLocalRepositoryFromBinary ? 1 : 0); // add one more operation as we support progress
+ double progressOperationSize = componentsInstallPartProgressSize / progressOperationCount;
+
+ foreach (Component *component, componentsToInstall)
+ installComponent(component, progressOperationSize, adminRightsGained);
+
+ if (m_createLocalRepositoryFromBinary) {
+ emit m_core->titleMessageChanged(tr("Creating local repository"));
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QString());
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("Creating local repository"));
+
+ Operation *createRepo = createOwnedOperation(QLatin1String("CreateLocalRepository"));
+ if (createRepo) {
+ createRepo->setValue(QLatin1String("uninstall-only"), true);
+ createRepo->setValue(QLatin1String("installer"), QVariant::fromValue(m_core));
+ createRepo->setArguments(QStringList() << QCoreApplication::applicationFilePath() << target);
+
+ connectOperationToInstaller(createRepo, progressOperationSize);
+
+ bool success = performOperationThreaded(createRepo);
+ if (!success) {
+ adminRightsGained = m_core->gainAdminRights();
+ success = performOperationThreaded(createRepo);
+ }
+
+ if (success) {
+ QSet<Repository> repos;
+ foreach (Repository repo, m_settings.defaultRepositories()) {
+ repo.setEnabled(false);
+ repos.insert(repo);
+ }
+ repos.insert(Repository(QUrl::fromUserInput(createRepo
+ ->value(QLatin1String("local-repo")).toString()), true));
+ m_settings.setDefaultRepositories(repos);
+ addPerformed(takeOwnedOperation(createRepo));
+ } else {
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("installationError"), tr("Error"), createRepo->errorString());
+ createRepo->undoOperation();
+ }
+ }
+ }
+
+ emit m_core->titleMessageChanged(tr("Creating Uninstaller"));
+
+ writeUninstaller(m_performedOperationsOld + m_performedOperationsCurrentSession);
+ registerUninstaller();
+
+ // fake a possible wrong value to show a full progress bar
+ const int progress = ProgressCoordinator::instance()->progressInPercentage();
+ if (progress < 100)
+ ProgressCoordinator::instance()->addManualPercentagePoints(100 - progress);
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nInstallation finished!"));
+
+ if (adminRightsGained)
+ m_core->dropAdminRights();
+ setStatus(PackageManagerCore::Success);
+ emit installationFinished();
+ } catch (const Error &err) {
+ if (m_core->status() != PackageManagerCore::Canceled) {
+ setStatus(PackageManagerCore::Failure);
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("installationError"), tr("Error"), err.message());
+ qDebug() << "ROLLING BACK operations=" << m_performedOperationsCurrentSession.count();
+ }
+
+ m_core->rollBackInstallation();
+
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nInstallation aborted!"));
+ if (adminRightsGained)
+ m_core->dropAdminRights();
+ emit installationFinished();
+
+ throw;
+ }
+}
+
+void PackageManagerCorePrivate::runPackageUpdater()
+{
+ bool adminRightsGained = false;
+ try {
+ if (m_completeUninstall) {
+ runUninstaller();
+ return;
+ }
+
+ setStatus(PackageManagerCore::Running);
+ emit installationStarted(); //resets also the ProgressCoordninator
+
+ //to have some progress for the cleanup/write component.xml step
+ ProgressCoordinator::instance()->addReservePercentagePoints(1);
+
+ const QString packagesXml = componentsXmlPath();
+ // check if we need admin rights and ask before the action happens
+ if (!QFileInfo(installerBinaryPath()).isWritable() || !QFileInfo(packagesXml).isWritable())
+ adminRightsGained = m_core->gainAdminRights();
+
+ const QList<Component *> componentsToInstall = m_core->orderedComponentsToInstall();
+ qDebug() << "Install size:" << componentsToInstall.size() << "components";
+
+ bool updateAdminRights = false;
+ if (!adminRightsGained) {
+ foreach (Component *component, componentsToInstall) {
+ if (component->value(scRequiresAdminRights, scFalse) == scFalse)
+ continue;
+
+ updateAdminRights = true;
+ break;
+ }
+ }
+
+ OperationList undoOperations;
+ OperationList nonRevertedOperations;
+ QHash<QString, Component *> componentsByName;
+
+ // build a list of undo operations based on the checked state of the component
+ foreach (Operation *operation, m_performedOperationsOld) {
+ const QString &name = operation->value(QLatin1String("component")).toString();
+ Component *component = componentsByName.value(name, 0);
+ if (!component)
+ component = m_core->componentByName(name);
+ if (component)
+ componentsByName.insert(name, component);
+
+ if (isUpdater()) {
+ // We found the component, the component is not scheduled for update, the dependency solver
+ // did not add the component as install dependency and there is no replacement, keep it.
+ if ((component && !component->updateRequested() && !componentsToInstall.contains(component)
+ && !m_componentsToReplaceUpdaterMode.contains(name))) {
+ nonRevertedOperations.append(operation);
+ continue;
+ }
+
+ // There is a replacement, but the replacement is not scheduled for update, keep it as well.
+ if (m_componentsToReplaceUpdaterMode.contains(name)
+ && !m_componentsToReplaceUpdaterMode.value(name).first->updateRequested()) {
+ nonRevertedOperations.append(operation);
+ continue;
+ }
+ } else if (isPackageManager()) {
+ // We found the component, the component is still checked and the dependency solver did not
+ // add the component as install dependency, keep it.
+ if (component && component->isSelected() && !componentsToInstall.contains(component)) {
+ nonRevertedOperations.append(operation);
+ continue;
+ }
+
+ // There is a replacement, but the replacement is not scheduled for update, keep it as well.
+ if (m_componentsToReplaceAllMode.contains(name)
+ && !m_componentsToReplaceAllMode.value(name).first->installationRequested()) {
+ nonRevertedOperations.append(operation);
+ continue;
+ }
+ } else {
+ Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid package manager mode!");
+ }
+
+ // Filter out the create target dir undo operation, it's only needed for full uninstall.
+ // Note: We filter for unnamed operations as well, since old installations had the remove target
+ // dir operation without the "uninstall-only", which will result in an complete uninstallation
+ // during an update for the maintenance tool.
+ if (operation->value(QLatin1String("uninstall-only")).toBool()
+ || operation->value(QLatin1String("component")).toString().isEmpty()) {
+ nonRevertedOperations.append(operation);
+ continue;
+ }
+
+ undoOperations.prepend(operation);
+ updateAdminRights |= operation->value(QLatin1String("admin")).toBool();
+ }
+
+ // we did not request admin rights till we found out that a component/ undo needs admin rights
+ if (updateAdminRights && !adminRightsGained) {
+ m_core->gainAdminRights();
+ m_core->dropAdminRights();
+ }
+
+ double undoOperationProgressSize = 0;
+ const double downloadPartProgressSize = double(2) / double(5);
+ double componentsInstallPartProgressSize = double(3) / double(5);
+ if (undoOperations.count() > 0) {
+ undoOperationProgressSize = double(1) / double(5);
+ componentsInstallPartProgressSize = downloadPartProgressSize;
+ undoOperationProgressSize /= countProgressOperations(undoOperations);
+ }
+
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("Preparing the installation..."));
+
+ // following, we download the needed archives
+ m_core->downloadNeededArchives(downloadPartProgressSize);
+
+ if (undoOperations.count() > 0) {
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("Removing deselected components..."));
+ runUndoOperations(undoOperations, undoOperationProgressSize, adminRightsGained, true);
+ }
+ m_performedOperationsOld = nonRevertedOperations; // these are all operations left: those not reverted
+
+ callBeginInstallation(componentsToInstall);
+ stopProcessesForUpdates(componentsToInstall);
+
+ const double progressOperationCount = countProgressOperations(componentsToInstall);
+ const double progressOperationSize = componentsInstallPartProgressSize / progressOperationCount;
+
+ foreach (Component *component, componentsToInstall)
+ installComponent(component, progressOperationSize, adminRightsGained);
+
+ emit m_core->titleMessageChanged(tr("Creating Uninstaller"));
+
+ commitSessionOperations(); //end session, move ops to "old"
+ m_needToWriteUninstaller = true;
+
+ // fake a possible wrong value to show a full progress bar
+ const int progress = ProgressCoordinator::instance()->progressInPercentage();
+ if (progress < 100)
+ ProgressCoordinator::instance()->addManualPercentagePoints(100 - progress);
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nUpdate finished!"));
+
+ if (adminRightsGained)
+ m_core->dropAdminRights();
+ setStatus(PackageManagerCore::Success);
+ emit installationFinished();
+ } catch (const Error &err) {
+ if (m_core->status() != PackageManagerCore::Canceled) {
+ setStatus(PackageManagerCore::Failure);
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("installationError"), tr("Error"), err.message());
+ qDebug() << "ROLLING BACK operations=" << m_performedOperationsCurrentSession.count();
+ }
+
+ m_core->rollBackInstallation();
+
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nUpdate aborted!"));
+ if (adminRightsGained)
+ m_core->dropAdminRights();
+ emit installationFinished();
+
+ throw;
+ }
+}
+
+void PackageManagerCorePrivate::runUninstaller()
+{
+ bool adminRightsGained = false;
+ try {
+ setStatus(PackageManagerCore::Running);
+ emit uninstallationStarted();
+
+ // check if we need administration rights and ask before the action happens
+ if (!QFileInfo(installerBinaryPath()).isWritable())
+ adminRightsGained = m_core->gainAdminRights();
+
+ OperationList undoOperations;
+ bool updateAdminRights = false;
+ foreach (Operation *op, m_performedOperationsOld) {
+ undoOperations.prepend(op);
+ updateAdminRights |= op->value(QLatin1String("admin")).toBool();
+ }
+
+ // we did not request administration rights till we found out that a undo needs administration rights
+ if (updateAdminRights && !adminRightsGained) {
+ m_core->gainAdminRights();
+ m_core->dropAdminRights();
+ }
+
+ const int uninstallOperationCount = countProgressOperations(undoOperations);
+ const double undoOperationProgressSize = double(1) / double(uninstallOperationCount);
+
+ //yes uninstallation is like an update on the component so please inform the user to stop processes
+ callBeginInstallation(m_core->availableComponents());
+ stopProcessesForUpdates(m_core->availableComponents());
+
+ runUndoOperations(undoOperations, undoOperationProgressSize, adminRightsGained, false);
+ // No operation delete here, as all old undo operations are deleted in the destructor.
+
+ const QString startMenuDir = m_vars.value(scStartMenuDir);
+ if (!startMenuDir.isEmpty()) {
+ try {
+ QInstaller::removeDirectory(startMenuDir);
+ } catch (const Error &error) {
+ qDebug() << QString::fromLatin1("Could not remove %1: %2").arg(startMenuDir, error.message());
+ }
+ } else {
+ qDebug() << "Start menu dir not set.";
+ }
+
+ // this will also delete the TargetDir on Windows
+ deleteUninstaller();
+
+ if (QVariant(m_core->value(scRemoveTargetDir)).toBool()) {
+ // on !Windows, we need to remove TargetDir manually
+ qDebug() << "Complete uninstallation is chosen";
+ const QString target = targetDir();
+ if (!target.isEmpty()) {
+ if (updateAdminRights && !adminRightsGained) {
+ // we were root at least once, so we remove the target dir as root
+ m_core->gainAdminRights();
+ removeDirectoryThreaded(target, true);
+ m_core->dropAdminRights();
+ } else {
+ removeDirectoryThreaded(target, true);
+ }
+ }
+ }
+
+ unregisterUninstaller();
+ m_needToWriteUninstaller = false;
+
+ setStatus(PackageManagerCore::Success);
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nDeinstallation finished!"));
+ if (adminRightsGained)
+ m_core->dropAdminRights();
+ emit uninstallationFinished();
+ } catch (const Error &err) {
+ if (m_core->status() != PackageManagerCore::Canceled) {
+ setStatus(PackageManagerCore::Failure);
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("installationError"), tr("Error"), err.message());
+ }
+
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nDeinstallation aborted!"));
+ if (adminRightsGained)
+ m_core->dropAdminRights();
+ emit uninstallationFinished();
+
+ throw;
+ }
+}
+
+void PackageManagerCorePrivate::installComponent(Component *component, double progressOperationSize,
+ bool adminRightsGained)
+{
+ const OperationList operations = component->operations();
+ if (!component->operationsCreatedSuccessfully())
+ m_core->setCanceled();
+
+ const int opCount = operations.count();
+ // show only components which do something, MinimumProgress is only for progress calculation safeness
+ if (opCount > 1 || (opCount == 1 && operations.at(0)->name() != QLatin1String("MinimumProgress"))) {
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nInstalling component %1")
+ .arg(component->displayName()));
+ }
+
+ foreach (Operation *operation, operations) {
+ if (statusCanceledOrFailed())
+ throw Error(tr("Installation canceled by user"));
+
+ // maybe this operations wants us to be admin...
+ bool becameAdmin = false;
+ if (!adminRightsGained && operation->value(QLatin1String("admin")).toBool()) {
+ becameAdmin = m_core->gainAdminRights();
+ qDebug() << operation->name() << "as admin:" << becameAdmin;
+ }
+
+ connectOperationToInstaller(operation, progressOperationSize);
+ connectOperationCallMethodRequest(operation);
+
+ // allow the operation to backup stuff before performing the operation
+ PackageManagerCorePrivate::performOperationThreaded(operation, PackageManagerCorePrivate::Backup);
+
+ bool ignoreError = false;
+ bool ok = PackageManagerCorePrivate::performOperationThreaded(operation);
+ while (!ok && !ignoreError && m_core->status() != PackageManagerCore::Canceled) {
+ qDebug() << QString::fromLatin1("Operation '%1' with arguments: '%2' failed: %3")
+ .arg(operation->name(), operation->arguments().join(QLatin1String("; ")),
+ operation->errorString());
+ const QMessageBox::StandardButton button =
+ MessageBoxHandler::warning(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("installationErrorWithRetry"), tr("Installer Error"),
+ tr("Error during installation process (%1):\n%2").arg(component->name(),
+ operation->errorString()),
+ QMessageBox::Retry | QMessageBox::Ignore | QMessageBox::Cancel, QMessageBox::Retry);
+
+ if (button == QMessageBox::Retry)
+ ok = PackageManagerCorePrivate::performOperationThreaded(operation);
+ else if (button == QMessageBox::Ignore)
+ ignoreError = true;
+ else if (button == QMessageBox::Cancel)
+ m_core->interrupt();
+ }
+
+ if (ok || operation->error() > Operation::InvalidArguments) {
+ // Remember that the operation was performed, what allows us to undo it if a following operation
+ // fails or if this operation failed but still needs an undo call to cleanup.
+ addPerformed(operation);
+ operation->setValue(QLatin1String("component"), component->name());
+ }
+
+ if (becameAdmin)
+ m_core->dropAdminRights();
+
+ if (!ok && !ignoreError)
+ throw Error(operation->errorString());
+
+ if (component->value(scEssential, scFalse) == scTrue)
+ m_forceRestart = true;
+ }
+
+ registerPathesForUninstallation(component->pathesForUninstallation(), component->name());
+
+ if (!component->stopProcessForUpdateRequests().isEmpty()) {
+ Operation *stopProcessForUpdatesOp = KDUpdater::UpdateOperationFactory::instance()
+ .create(QLatin1String("FakeStopProcessForUpdate"));
+ const QStringList arguments(component->stopProcessForUpdateRequests().join(QLatin1String(",")));
+ stopProcessForUpdatesOp->setArguments(arguments);
+ addPerformed(stopProcessForUpdatesOp);
+ stopProcessForUpdatesOp->setValue(QLatin1String("component"), component->name());
+ }
+
+ // now mark the component as installed
+ KDUpdater::PackagesInfo &packages = *m_updaterApplication.packagesInfo();
+ packages.installPackage(component->name(), component->value(scVersion), component->value(scDisplayName),
+ component->value(scDescription), component->dependencies(), component->forcedInstallation(),
+ component->isVirtual(), component->value(scUncompressedSize).toULongLong(),
+ component->value(scInheritVersion));
+ packages.writeToDisk();
+
+ component->setInstalled();
+ component->markAsPerformedInstallation();
+}
+
+// -- private
+
+void PackageManagerCorePrivate::deleteUninstaller()
+{
+#ifdef Q_OS_WIN
+ // Since Windows does not support that the uninstaller deletes itself we have to go with a rather dirty
+ // hack. What we do is to create a batchfile that will try to remove the uninstaller once per second. Then
+ // we start that batchfile detached, finished our job and close ourselves. Once that's done the batchfile
+ // will succeed in deleting our uninstall.exe and, if the installation directory was created but us and if
+ // it's empty after the uninstall, deletes the installation-directory.
+ const QString batchfile = QDir::toNativeSeparators(QFileInfo(QDir::tempPath(),
+ QLatin1String("uninstall.vbs")).absoluteFilePath());
+ QFile f(batchfile);
+ if (!f.open(QIODevice::WriteOnly | QIODevice::Text))
+ throw Error(tr("Cannot prepare uninstall"));
+
+ QTextStream batch(&f);
+ batch << "Set fso = WScript.CreateObject(\"Scripting.FileSystemObject\")\n";
+ batch << "file = WScript.Arguments.Item(0)\n";
+ batch << "folderpath = WScript.Arguments.Item(1)\n";
+ batch << "Set folder = fso.GetFolder(folderpath)\n";
+ batch << "on error resume next\n";
+
+ batch << "while fso.FileExists(file)\n";
+ batch << " fso.DeleteFile(file)\n";
+ batch << " WScript.Sleep(1000)\n";
+ batch << "wend\n";
+// batch << "if folder.SubFolders.Count = 0 and folder.Files.Count = 0 then\n";
+ batch << " Set folder = Nothing\n";
+ batch << " fso.DeleteFolder folderpath, true\n";
+// batch << "end if\n";
+ batch << "fso.DeleteFile(WScript.ScriptFullName)\n";
+
+ f.close();
+
+ QStringList arguments;
+ arguments << QLatin1String("//Nologo") << batchfile; // execute the batchfile
+ arguments << QDir::toNativeSeparators(QFileInfo(installerBinaryPath()).absoluteFilePath());
+ if (!m_performedOperationsOld.isEmpty()) {
+ const Operation *const op = m_performedOperationsOld.first();
+ if (op->name() == QLatin1String("Mkdir")) // the target directory name
+ arguments << QDir::toNativeSeparators(QFileInfo(op->arguments().first()).absoluteFilePath());
+ }
+
+ if (!QProcessWrapper::startDetached(QLatin1String("cscript"), arguments, QDir::rootPath()))
+ throw Error(tr("Cannot start uninstall"));
+#else
+ // every other platform has no problem if we just delete ourselves now
+ QFile uninstaller(QFileInfo(installerBinaryPath()).absoluteFilePath());
+ uninstaller.remove();
+# ifdef Q_WS_MAC
+ const QLatin1String cdUp("/../../..");
+ if (QFileInfo(QFileInfo(installerBinaryPath() + cdUp).absoluteFilePath()).isBundle()) {
+ removeDirectoryThreaded(QFileInfo(installerBinaryPath() + cdUp).absoluteFilePath());
+ QFile::remove(QFileInfo(installerBinaryPath() + cdUp).absolutePath()
+ + QLatin1String("/") + configurationFileName());
+ } else
+# endif
+#endif
+ {
+ // finally remove the components.xml, since it still exists now
+ QFile::remove(QFileInfo(installerBinaryPath()).absolutePath() + QLatin1String("/")
+ + configurationFileName());
+ }
+}
+
+void PackageManagerCorePrivate::registerUninstaller()
+{
+#ifdef Q_OS_WIN
+ QSettingsWrapper settings(registerPath(), QSettingsWrapper::NativeFormat);
+ settings.setValue(scDisplayName, m_vars.value(QLatin1String("ProductName")));
+ settings.setValue(QLatin1String("DisplayVersion"), m_vars.value(QLatin1String("ProductVersion")));
+ const QString uninstaller = QDir::toNativeSeparators(uninstallerName());
+ settings.setValue(QLatin1String("DisplayIcon"), uninstaller);
+ settings.setValue(scPublisher, m_vars.value(scPublisher));
+ settings.setValue(QLatin1String("UrlInfoAbout"), m_vars.value(QLatin1String("Url")));
+ settings.setValue(QLatin1String("Comments"), m_vars.value(scTitle));
+ settings.setValue(QLatin1String("InstallDate"), QDateTime::currentDateTime().toString());
+ settings.setValue(QLatin1String("InstallLocation"), QDir::toNativeSeparators(targetDir()));
+ settings.setValue(QLatin1String("UninstallString"), uninstaller);
+ settings.setValue(QLatin1String("ModifyPath"), uninstaller + QLatin1String(" --manage-packages"));
+ settings.setValue(QLatin1String("EstimatedSize"), QFileInfo(installerBinaryPath()).size());
+ settings.setValue(QLatin1String("NoModify"), 0);
+ settings.setValue(QLatin1String("NoRepair"), 1);
+#endif
+}
+
+void PackageManagerCorePrivate::unregisterUninstaller()
+{
+#ifdef Q_OS_WIN
+ QSettingsWrapper settings(registerPath(), QSettingsWrapper::NativeFormat);
+ settings.remove(QString());
+#endif
+}
+
+void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOperations, double progressSize,
+ bool adminRightsGained, bool deleteOperation)
+{
+ KDUpdater::PackagesInfo &packages = *m_updaterApplication.packagesInfo();
+ try {
+ foreach (Operation *undoOperation, undoOperations) {
+ if (statusCanceledOrFailed())
+ throw Error(tr("Installation canceled by user"));
+
+ bool becameAdmin = false;
+ if (!adminRightsGained && undoOperation->value(QLatin1String("admin")).toBool())
+ becameAdmin = m_core->gainAdminRights();
+
+ connectOperationToInstaller(undoOperation, progressSize);
+ qDebug() << "undo operation=" << undoOperation->name();
+ performOperationThreaded(undoOperation, PackageManagerCorePrivate::Undo);
+
+ const QString componentName = undoOperation->value(QLatin1String("component")).toString();
+ if (undoOperation->error() != Operation::NoError) {
+ if (!componentName.isEmpty()) {
+ bool run = true;
+ while (run && m_core->status() != PackageManagerCore::Canceled) {
+ const QMessageBox::StandardButton button =
+ MessageBoxHandler::warning(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("installationErrorWithRetry"), tr("Installer Error"),
+ tr("Error during uninstallation process:\n%1").arg(undoOperation->errorString()),
+ QMessageBox::Retry | QMessageBox::Ignore, QMessageBox::Retry);
+
+ if (button == QMessageBox::Retry) {
+ performOperationThreaded(undoOperation, Undo);
+ if (undoOperation->error() == Operation::NoError)
+ run = false;
+ } else if (button == QMessageBox::Ignore) {
+ run = false;
+ }
+ }
+ }
+ }
+
+ if (!componentName.isEmpty()) {
+ Component *component = m_core->componentByName(componentName);
+ if (!component)
+ component = componentsToReplace(m_core->runMode()).value(componentName).second;
+ if (component) {
+ component->setUninstalled();
+ packages.removePackage(component->name());
+ }
+ }
+
+ if (becameAdmin)
+ m_core->dropAdminRights();
+
+ if (deleteOperation)
+ delete undoOperation;
+ }
+ } catch (const Error &error) {
+ packages.writeToDisk();
+ throw Error(error.message());
+ } catch (...) {
+ packages.writeToDisk();
+ throw Error(tr("Unknown error"));
+ }
+ packages.writeToDisk();
+}
+
+PackagesList PackageManagerCorePrivate::remotePackages()
+{
+ if (m_updates && m_updateFinder)
+ return m_updateFinder->updates();
+
+ m_updates = false;
+ delete m_updateFinder;
+
+ m_updateFinder = new KDUpdater::UpdateFinder(&m_updaterApplication);
+ m_updateFinder->setAutoDelete(false);
+ m_updateFinder->setUpdateType(KDUpdater::PackageUpdate | KDUpdater::NewPackage);
+ m_updateFinder->run();
+
+ if (m_updateFinder->updates().isEmpty()) {
+ setStatus(PackageManagerCore::Failure, tr("Could not retrieve remote tree: %1.")
+ .arg(m_updateFinder->errorString()));
+ return PackagesList();
+ }
+
+ m_updates = true;
+ return m_updateFinder->updates();
+}
+
+/*!
+ Returns a hash containing the installed package name and it's associated package information. If
+ the application is running in installer mode or the local components file could not be parsed, the
+ hash is empty.
+*/
+LocalPackagesHash PackageManagerCorePrivate::localInstalledPackages()
+{
+ LocalPackagesHash installedPackages;
+
+ if (!isInstaller()) {
+ KDUpdater::PackagesInfo &packagesInfo = *m_updaterApplication.packagesInfo();
+ if (!packagesInfo.isValid()) {
+ packagesInfo.setFileName(componentsXmlPath());
+ if (packagesInfo.applicationName().isEmpty())
+ packagesInfo.setApplicationName(m_settings.applicationName());
+ if (packagesInfo.applicationVersion().isEmpty())
+ packagesInfo.setApplicationVersion(m_settings.applicationVersion());
+ }
+
+ if (packagesInfo.error() != KDUpdater::PackagesInfo::NoError)
+ setStatus(PackageManagerCore::Failure, tr("Failure to read packages from: %1.").arg(componentsXmlPath()));
+
+ foreach (const LocalPackage &package, packagesInfo.packageInfos()) {
+ if (statusCanceledOrFailed())
+ break;
+ installedPackages.insert(package.name, package);
+ }
+ }
+
+ return installedPackages;
+}
+
+bool PackageManagerCorePrivate::fetchMetaInformationFromRepositories()
+{
+ if (m_repoFetched)
+ return m_repoFetched;
+
+ m_updates = false;
+ m_repoFetched = false;
+ m_updateSourcesAdded = false;
+
+ m_repoMetaInfoJob->reset();
+ try {
+ m_repoMetaInfoJob->start();
+ m_repoMetaInfoJob->waitForFinished();
+ } catch (Error &error) {
+ setStatus(PackageManagerCore::Failure, tr("Could not retrieve meta information: %1").arg(error.message()));
+ return m_repoFetched;
+ }
+
+ if (m_repoMetaInfoJob->isCanceled() || m_repoMetaInfoJob->error() != KDJob::NoError) {
+ switch (m_repoMetaInfoJob->error()) {
+ case QInstaller::UserIgnoreError:
+ break; // we can simply ignore this error, the user knows about it
+ default:
+ setStatus(PackageManagerCore::Failure, m_repoMetaInfoJob->errorString());
+ return m_repoFetched;
+ }
+ }
+
+ m_repoFetched = true;
+ return m_repoFetched;
+}
+
+bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool parseChecksum)
+{
+ if (m_updateSourcesAdded)
+ return m_updateSourcesAdded;
+
+ if (m_repoMetaInfoJob->temporaryDirectories().isEmpty()) {
+ m_updateSourcesAdded = true;
+ return m_updateSourcesAdded;
+ }
+
+ // forces an refresh/ clear on all update sources
+ m_updaterApplication.updateSourcesInfo()->refresh();
+ if (isInstaller()) {
+ m_updaterApplication.addUpdateSource(m_settings.applicationName(), m_settings.applicationName(),
+ QString(), QUrl(QLatin1String("resource://metadata/")), 0);
+ m_updaterApplication.updateSourcesInfo()->setModified(false);
+ }
+
+ m_updates = false;
+ m_updateSourcesAdded = false;
+
+ const QString &appName = m_settings.applicationName();
+ const QStringList tempDirs = m_repoMetaInfoJob->temporaryDirectories();
+ foreach (const QString &tmpDir, tempDirs) {
+ if (statusCanceledOrFailed())
+ return false;
+
+ if (tmpDir.isEmpty())
+ continue;
+
+ if (parseChecksum) {
+ const QString updatesXmlPath = tmpDir + QLatin1String("/Updates.xml");
+ QFile updatesFile(updatesXmlPath);
+ try {
+ openForRead(&updatesFile, updatesFile.fileName());
+ } catch(const Error &e) {
+ qDebug() << "Error opening Updates.xml:" << e.message();
+ setStatus(PackageManagerCore::Failure, tr("Could not add temporary update source information."));
+ return false;
+ }
+
+ int line = 0;
+ int column = 0;
+ QString error;
+ QDomDocument doc;
+ if (!doc.setContent(&updatesFile, &error, &line, &column)) {
+ qDebug() << QString::fromLatin1("Parse error in File %4 : %1 at line %2 col %3").arg(error,
+ QString::number(line), QString::number(column), updatesFile.fileName());
+ setStatus(PackageManagerCore::Failure, tr("Could not add temporary update source information."));
+ return false;
+ }
+
+ const QDomNode checksum = doc.documentElement().firstChildElement(QLatin1String("Checksum"));
+ if (!checksum.isNull())
+ m_core->setTestChecksum(checksum.toElement().text().toLower() == scTrue);
+ }
+ m_updaterApplication.addUpdateSource(appName, appName, QString(), QUrl::fromLocalFile(tmpDir), 1);
+ }
+ m_updaterApplication.updateSourcesInfo()->setModified(false);
+
+ if (m_updaterApplication.updateSourcesInfo()->updateSourceInfoCount() == 0) {
+ setStatus(PackageManagerCore::Failure, tr("Could not find any update source information."));
+ return false;
+ }
+
+ m_updateSourcesAdded = true;
+ return m_updateSourcesAdded;
+}
+
+void PackageManagerCorePrivate::realAppendToInstallComponents(Component *component)
+{
+ if (!component->isInstalled() || component->updateRequested()) {
+ //remove the checkState method if we don't use selected in scripts
+ setCheckedState(component, Qt::Checked);
+
+ m_orderedComponentsToInstall.append(component);
+ m_toInstallComponentIds.insert(component->name());
+ }
+}
+
+void PackageManagerCorePrivate::insertInstallReason(Component *component, const QString &reason)
+{
+ //keep the first reason
+ if (m_toInstallComponentIdReasonHash.value(component->name()).isEmpty())
+ m_toInstallComponentIdReasonHash.insert(component->name(), reason);
+}
+
+bool PackageManagerCorePrivate::appendComponentToUninstall(Component *component)
+{
+ // remove all already resolved dependees
+ QSet<Component *> dependees = m_core->dependees(component).toSet().subtract(m_componentsToUninstall);
+ if (dependees.isEmpty()) {
+ setCheckedState(component, Qt::Unchecked);
+ m_componentsToUninstall.insert(component);
+ return true;
+ }
+
+ QSet<Component *> dependeesToResolve;
+ foreach (Component *dependee, dependees) {
+ if (dependee->isInstalled()) {
+ // keep them as already resolved
+ setCheckedState(dependee, Qt::Unchecked);
+ m_componentsToUninstall.insert(dependee);
+ // gather possible dependees, keep them to resolve it later
+ dependeesToResolve.unite(m_core->dependees(dependee).toSet());
+ }
+ }
+
+ bool allResolved = true;
+ foreach (Component *dependee, dependeesToResolve)
+ allResolved &= appendComponentToUninstall(dependee);
+
+ return allResolved;
+}
+
+bool PackageManagerCorePrivate::appendComponentsToUninstall(const QList<Component*> &components)
+{
+ if (components.isEmpty()) {
+ qDebug() << "components list is empty in" << Q_FUNC_INFO;
+ return true;
+ }
+
+ bool allResolved = true;
+ foreach (Component *component, components) {
+ if (component->isInstalled()) {
+ setCheckedState(component, Qt::Unchecked);
+ m_componentsToUninstall.insert(component);
+ allResolved &= appendComponentToUninstall(component);
+ }
+ }
+
+ QSet<Component*> installedComponents;
+ foreach (const QString &name, localInstalledPackages().keys()) {
+ if (Component *component = m_core->componentByName(name)) {
+ if (!component->uninstallationRequested())
+ installedComponents.insert(component);
+ }
+ }
+
+ QList<Component*> autoDependOnList;
+ if (allResolved) {
+ // All regular dependees are resolved. Now we are looking for auto depend on components.
+ foreach (Component *component, installedComponents) {
+ // If a components is installed and not yet scheduled for un-installation, check for auto depend.
+ if (component->isInstalled() && !m_componentsToUninstall.contains(component)) {
+ QStringList autoDependencies = component->autoDependencies();
+ if (autoDependencies.isEmpty())
+ continue;
+
+ // This code needs to be enabled once the scripts use isInstalled, installationRequested and
+ // uninstallationRequested...
+ if (autoDependencies.first().compare(QLatin1String("script"), Qt::CaseInsensitive) == 0) {
+ //QScriptValue valueFromScript;
+ //try {
+ // valueFromScript = callScriptMethod(QLatin1String("isAutoDependOn"));
+ //} catch (const Error &error) {
+ // // keep the component, should do no harm
+ // continue;
+ //}
+
+ //if (valueFromScript.isValid() && !valueFromScript.toBool())
+ // autoDependOnList.append(component);
+ continue;
+ }
+
+ foreach (Component *c, installedComponents) {
+ const QString replaces = c->value(scReplaces);
+ QStringList possibleNames = replaces.split(QRegExp(QLatin1String("\\b(,|, )\\b")),
+ QString::SkipEmptyParts);
+ possibleNames.append(c->name());
+ foreach (const QString &possibleName, possibleNames)
+ autoDependencies.removeAll(possibleName);
+ }
+
+ // A component requested auto installation, keep it to resolve their dependencies as well.
+ if (!autoDependencies.isEmpty())
+ autoDependOnList.append(component);
+ }
+ }
+ }
+
+ if (!autoDependOnList.isEmpty())
+ return appendComponentsToUninstall(autoDependOnList);
+ return allResolved;
+}
+
+void PackageManagerCorePrivate::resetComponentsToUserCheckedState()
+{
+ if (m_coreCheckedHash.isEmpty())
+ return;
+
+ foreach (Component *component, m_coreCheckedHash.keys())
+ component->setCheckState(m_coreCheckedHash.value(component));
+
+ m_coreCheckedHash.clear();
+ m_componentsToInstallCalculated = false;
+}
+
+void PackageManagerCorePrivate::setCheckedState(Component *component, Qt::CheckState state)
+{
+ m_coreCheckedHash.insert(component, component->checkState());
+ component->setCheckState(state);
+}
+
+void PackageManagerCorePrivate::connectOperationCallMethodRequest(Operation *const operation)
+{
+ QObject *const operationObject = dynamic_cast<QObject *> (operation);
+ if (operationObject != 0) {
+ const QMetaObject *const mo = operationObject->metaObject();
+ if (mo->indexOfSignal(QMetaObject::normalizedSignature("requestBlockingExecution(QString)")) > -1) {
+ connect(operationObject, SIGNAL(requestBlockingExecution(QString)),
+ this, SLOT(handleMethodInvocationRequest(QString)), Qt::BlockingQueuedConnection);
+ }
+ }
+}
+
+void PackageManagerCorePrivate::handleMethodInvocationRequest(const QString &invokableMethodName)
+{
+ QObject *obj = QObject::sender();
+ if (obj != 0)
+ QMetaObject::invokeMethod(obj, qPrintable(invokableMethodName));
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/packagemanagercore_p.h b/src/libs/installer/packagemanagercore_p.h
new file mode 100644
index 000000000..1a585cf96
--- /dev/null
+++ b/src/libs/installer/packagemanagercore_p.h
@@ -0,0 +1,260 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef PACKAGEMANAGERCORE_P_H
+#define PACKAGEMANAGERCORE_P_H
+
+#include "getrepositoriesmetainfojob.h"
+#include "settings.h"
+#include "packagemanagercore.h"
+
+#include <kdsysinfo.h>
+#include <kdupdaterapplication.h>
+#include <kdupdaterupdatefinder.h>
+
+#include <QtCore/QHash>
+#include <QtCore/QObject>
+#include <QtCore/QPair>
+#include <QtCore/QPointer>
+
+class FSEngineClientHandler;
+class KDJob;
+
+QT_FORWARD_DECLARE_CLASS(QFile)
+QT_FORWARD_DECLARE_CLASS(QFileInfo)
+
+using namespace KDUpdater;
+
+namespace QInstaller {
+
+struct BinaryLayout;
+class Component;
+class TempDirDeleter;
+
+class PackageManagerCorePrivate : public QObject
+{
+ Q_OBJECT
+ friend class PackageManagerCore;
+
+public:
+ enum OperationType {
+ Backup,
+ Perform,
+ Undo
+ };
+
+ explicit PackageManagerCorePrivate(PackageManagerCore *core);
+ explicit PackageManagerCorePrivate(PackageManagerCore *core, qint64 magicInstallerMaker,
+ const OperationList &performedOperations);
+ ~PackageManagerCorePrivate();
+
+ static bool isProcessRunning(const QString &name, const QList<ProcessInfo> &processes);
+
+ static bool performOperationThreaded(Operation *op, PackageManagerCorePrivate::OperationType type
+ = PackageManagerCorePrivate::Perform);
+
+ void initialize();
+ bool isOfflineOnly() const;
+
+ bool statusCanceledOrFailed() const;
+ void setStatus(int status, const QString &error = QString());
+
+ QString targetDir() const;
+ QString registerPath() const;
+
+ QString uninstallerName() const;
+ QString installerBinaryPath() const;
+
+ void writeMaintenanceConfigFiles();
+ void readMaintenanceConfigFiles(const QString &targetDir);
+
+ void writeUninstaller(OperationList performedOperations);
+
+ QString componentsXmlPath() const;
+ QString configurationFileName() const;
+
+ bool buildComponentTree(QHash<QString, Component*> &components, bool loadScript);
+
+ void clearAllComponentLists();
+ void clearUpdaterComponentLists();
+ QList<Component*> &replacementDependencyComponents(RunMode mode);
+ QHash<QString, QPair<Component*, Component*> > &componentsToReplace(RunMode mode);
+
+ void clearComponentsToInstall();
+ bool appendComponentsToInstall(const QList<Component*> &components);
+ bool appendComponentToInstall(Component *components);
+ QString installReason(Component *component);
+
+ void runInstaller();
+ bool isInstaller() const;
+
+ void runUninstaller();
+ bool isUninstaller() const;
+
+ void runUpdater();
+ bool isUpdater() const;
+
+ void runPackageUpdater();
+ bool isPackageManager() const;
+
+ QString replaceVariables(const QString &str) const;
+ QByteArray replaceVariables(const QByteArray &str) const;
+
+ void callBeginInstallation(const QList<Component*> &componentList);
+ void stopProcessesForUpdates(const QList<Component*> &components);
+ int countProgressOperations(const QList<Component*> &components);
+ int countProgressOperations(const OperationList &operations);
+ void connectOperationToInstaller(Operation *const operation, double progressOperationPartSize);
+ void connectOperationCallMethodRequest(Operation *const operation);
+
+ Operation *createOwnedOperation(const QString &type);
+ Operation *takeOwnedOperation(Operation *operation);
+
+ Operation *createPathOperation(const QFileInfo &fileInfo, const QString &componentName);
+ void registerPathesForUninstallation(const QList<QPair<QString, bool> > &pathesForUninstallation,
+ const QString &componentName);
+
+ void addPerformed(Operation *op) {
+ m_performedOperationsCurrentSession.append(op);
+ }
+
+ void commitSessionOperations() {
+ m_performedOperationsOld += m_performedOperationsCurrentSession;
+ m_performedOperationsCurrentSession.clear();
+ }
+
+ void installComponent(Component *component, double progressOperationSize,
+ bool adminRightsGained = false);
+
+ bool appendComponentToUninstall(Component *component);
+ bool appendComponentsToUninstall(const QList<Component*> &components);
+
+signals:
+ void installationStarted();
+ void installationFinished();
+ void uninstallationStarted();
+ void uninstallationFinished();
+
+public:
+ UpdateFinder *m_updateFinder;
+ Application m_updaterApplication;
+ FSEngineClientHandler *m_FSEngineClientHandler;
+
+ int m_status;
+ QString m_error;
+
+ Settings m_settings;
+ bool m_forceRestart;
+ bool m_testChecksum;
+ bool m_launchedAsRoot;
+ bool m_completeUninstall;
+ bool m_needToWriteUninstaller;
+ QHash<QString, QString> m_vars;
+ QHash<QString, bool> m_sharedFlags;
+ QString m_installerBaseBinaryUnreplaced;
+
+ QList<Component*> m_rootComponents;
+ QList<Component*> m_rootDependencyReplacements;
+
+ QList<Component*> m_updaterComponents;
+ QList<Component*> m_updaterComponentsDeps;
+ QList<Component*> m_updaterDependencyReplacements;
+
+ OperationList m_ownedOperations;
+ OperationList m_performedOperationsOld;
+ OperationList m_performedOperationsCurrentSession;
+
+private slots:
+ void infoMessage(KDJob *, const QString &message) {
+ emit m_core->metaJobInfoMessage(message);
+ }
+
+ void handleMethodInvocationRequest(const QString &invokableMethodName);
+
+private:
+ void deleteUninstaller();
+ void registerUninstaller();
+ void unregisterUninstaller();
+
+ void writeUninstallerBinary(QFile *const input, qint64 size, bool writeBinaryLayout);
+ void writeUninstallerBinaryData(QIODevice *output, QFile *const input, const OperationList &performed,
+ const BinaryLayout &layout);
+
+ void runUndoOperations(const OperationList &undoOperations, double undoOperationProgressSize,
+ bool adminRightsGained, bool deleteOperation);
+
+ PackagesList remotePackages();
+ LocalPackagesHash localInstalledPackages();
+ bool fetchMetaInformationFromRepositories();
+ bool addUpdateResourcesFromRepositories(bool parseChecksum);
+ void realAppendToInstallComponents(Component *component);
+ void insertInstallReason(Component *component, const QString &reason);
+
+private:
+ PackageManagerCore *m_core;
+ GetRepositoriesMetaInfoJob *m_repoMetaInfoJob;
+
+ bool m_updates;
+ bool m_repoFetched;
+ bool m_updateSourcesAdded;
+ qint64 m_magicBinaryMarker;
+ bool m_componentsToInstallCalculated;
+
+ // < name (component to replace), < replacement component, component to replace > >
+ QHash<QString, QPair<Component*, Component*> > m_componentsToReplaceAllMode;
+ QHash<QString, QPair<Component*, Component*> > m_componentsToReplaceUpdaterMode;
+
+ //calculate installation order variables
+ QList<Component*> m_orderedComponentsToInstall;
+ QHash<Component*, QSet<Component*> > m_visitedComponents;
+
+ QSet<QString> m_toInstallComponentIds; //for faster lookups
+
+ //we can't use this reason hash as component id hash, because some reasons are ready before
+ //the component is added
+ QHash<QString, QString> m_toInstallComponentIdReasonHash;
+
+ QSet<Component*> m_componentsToUninstall;
+ QString m_componentsToInstallError;
+ FileDownloaderProxyFactory *m_proxyFactory;
+ bool m_createLocalRepositoryFromBinary;
+
+private:
+ // remove once we deprecate isSelected, setSelected etc...
+ void resetComponentsToUserCheckedState();
+ QHash<Component*, Qt::CheckState> m_coreCheckedHash;
+ void setCheckedState(Component *component, Qt::CheckState state);
+};
+
+} // namespace QInstaller
+
+#endif // PACKAGEMANAGERCORE_P_H
diff --git a/src/libs/installer/packagemanagergui.cpp b/src/libs/installer/packagemanagergui.cpp
new file mode 100644
index 000000000..0b96e2d94
--- /dev/null
+++ b/src/libs/installer/packagemanagergui.cpp
@@ -0,0 +1,1983 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#include "packagemanagergui.h"
+
+#include "component.h"
+#include "componentmodel.h"
+#include "errors.h"
+#include "fileutils.h"
+#include "messageboxhandler.h"
+#include "packagemanagercore.h"
+#include "qinstallerglobal.h"
+#include "progresscoordinator.h"
+#include "performinstallationform.h"
+#include "settings.h"
+#include "utils.h"
+
+#include "kdsysinfo.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QDynamicPropertyChangeEvent>
+#include <QtCore/QPair>
+#include <QtCore/QProcess>
+#include <QtCore/QRegExp>
+#include <QtCore/QSettings>
+#include <QtCore/QTimer>
+
+#include <QtGui/QApplication>
+#include <QtGui/QCheckBox>
+#include <QtGui/QDesktopServices>
+#include <QtGui/QFileDialog>
+#include <QtGui/QGridLayout>
+#include <QtGui/QFormLayout>
+#include <QtGui/QHBoxLayout>
+#include <QtGui/QHeaderView>
+#include <QtGui/QLabel>
+#include <QtGui/QLineEdit>
+#include <QtGui/QListWidget>
+#include <QtGui/QListWidgetItem>
+#include <QtGui/QMessageBox>
+#include <QtGui/QProgressBar>
+#include <QtGui/QPushButton>
+#include <QtGui/QRadioButton>
+#include <QtGui/QTextBrowser>
+#include <QtGui/QTreeWidget>
+#include <QtGui/QTreeView>
+#include <QtGui/QVBoxLayout>
+#include <QtGui/QScrollBar>
+#include <QtGui/QShowEvent>
+
+#include <QtScript/QScriptEngine>
+
+using namespace KDUpdater;
+using namespace QInstaller;
+
+/*
+TRANSLATOR QInstaller::PackageManagerCore;
+*/
+/*
+TRANSLATOR QInstaller::PackageManagerGui
+*/
+/*
+TRANSLATOR QInstaller::PackageManagerPage
+*/
+/*
+TRANSLATOR QInstaller::IntroductionPage
+*/
+/*
+TRANSLATOR QInstaller::LicenseAgreementPage
+*/
+/*
+TRANSLATOR QInstaller::ComponentSelectionPage
+*/
+/*
+TRANSLATOR QInstaller::TargetDirectoryPage
+*/
+/*
+TRANSLATOR QInstaller::StartMenuDirectoryPage
+*/
+/*
+TRANSLATOR QInstaller::ReadyForInstallationPage
+*/
+/*
+TRANSLATOR QInstaller::PerformInstallationPage
+*/
+/*
+TRANSLATOR QInstaller::FinishedPage
+*/
+
+
+static QString humanReadableSize(quint64 intSize)
+{
+ QString unit;
+ double size;
+
+ if (intSize < 1024 * 1024) {
+ size = 1. + intSize / 1024.;
+ unit = QObject::tr("kB");
+ } else if (intSize < 1024 * 1024 * 1024) {
+ size = 1. + intSize / 1024. / 1024.;
+ unit = QObject::tr("MB");
+ } else {
+ size = 1. + intSize / 1024. / 1024. / 1024.;
+ unit = QObject::tr("GB");
+ }
+
+ size = qRound(size * 10) / 10.0;
+ return QString::fromLatin1("%L1 %2").arg(size, 0, 'g', 4).arg(unit);
+}
+
+
+class DynamicInstallerPage : public PackageManagerPage
+{
+public:
+ explicit DynamicInstallerPage(QWidget *widget, PackageManagerCore *core = 0)
+ : PackageManagerPage(core)
+ , m_widget(widget)
+ {
+ setObjectName(QLatin1String("Dynamic") + widget->objectName());
+ setPixmap(QWizard::LogoPixmap, logoPixmap());
+ setPixmap(QWizard::WatermarkPixmap, QPixmap());
+
+ setLayout(new QVBoxLayout);
+ setSubTitle(QString());
+ setTitle(widget->windowTitle());
+ m_widget->setProperty("complete", true);
+ m_widget->setProperty("final", false);
+ widget->installEventFilter(this);
+ layout()->addWidget(widget);
+ }
+
+ QWidget *widget() const
+ {
+ return m_widget;
+ }
+
+ bool isComplete() const
+ {
+ return m_widget->property("complete").toBool();
+ }
+
+protected:
+ bool eventFilter(QObject *obj, QEvent *event)
+ {
+ if (obj == m_widget) {
+ switch(event->type()) {
+ case QEvent::WindowTitleChange:
+ setTitle(m_widget->windowTitle());
+ break;
+
+ case QEvent::DynamicPropertyChange:
+ emit completeChanged();
+ if (m_widget->property("final").toBool() != isFinalPage())
+ setFinalPage(m_widget->property("final").toBool());
+ break;
+
+ default:
+ break;
+ }
+ }
+ return PackageManagerPage::eventFilter(obj, event);
+ }
+
+private:
+ QWidget *const m_widget;
+};
+
+
+// -- PackageManagerGui::Private
+
+class PackageManagerGui::Private
+{
+public:
+ Private()
+ : m_modified(false)
+ , m_autoSwitchPage(true)
+ , m_showSettingsButton(false)
+ { }
+
+ bool m_modified;
+ bool m_autoSwitchPage;
+ bool m_showSettingsButton;
+ QMap<int, QWizardPage*> m_defaultPages;
+ QMap<int, QString> m_defaultButtonText;
+
+ QScriptValue m_controlScript;
+ QScriptEngine m_controlScriptEngine;
+};
+
+
+// -- PackageManagerGui
+
+QScriptEngine *PackageManagerGui::controlScriptEngine() const
+{
+ return &d->m_controlScriptEngine;
+}
+
+/*!
+ \class QInstaller::PackageManagerGui
+ Is the "gui" object in a none interactive installation
+*/
+PackageManagerGui::PackageManagerGui(PackageManagerCore *core, QWidget *parent)
+ : QWizard(parent)
+ , d(new Private)
+ , m_core(core)
+{
+ if (m_core->isInstaller())
+ setWindowTitle(tr("%1 Setup").arg(m_core->value(scTitle)));
+ else
+ setWindowTitle(tr("Maintain %1").arg(m_core->value(scTitle)));
+
+#ifndef Q_WS_MAC
+ setWindowIcon(QIcon(m_core->settings().icon()));
+#else
+ setPixmap(QWizard::BackgroundPixmap, m_core->settings().background());
+#endif
+#ifdef Q_OS_LINUX
+ setWizardStyle(QWizard::ModernStyle);
+ setSizeGripEnabled(true);
+#endif
+ setOption(QWizard::NoBackButtonOnStartPage);
+ setOption(QWizard::NoBackButtonOnLastPage);
+ setLayout(new QVBoxLayout(this));
+
+ connect(this, SIGNAL(rejected()), m_core, SLOT(setCanceled()));
+ connect(this, SIGNAL(interrupted()), m_core, SLOT(interrupt()));
+
+ // both queued to show the finished page once everything is done
+ connect(m_core, SIGNAL(installationFinished()), this, SLOT(showFinishedPage()), Qt::QueuedConnection);
+ connect(m_core, SIGNAL(uninstallationFinished()), this, SLOT(showFinishedPage()), Qt::QueuedConnection);
+
+ connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(slotCurrentPageChanged(int)));
+ connect(this, SIGNAL(currentIdChanged(int)), m_core, SIGNAL(currentPageChanged(int)));
+ connect(button(QWizard::FinishButton), SIGNAL(clicked()), this, SIGNAL(finishButtonClicked()));
+ connect(button(QWizard::FinishButton), SIGNAL(clicked()), m_core, SIGNAL(finishButtonClicked()));
+
+ // make sure the QUiLoader's retranslateUi is executed first, then the script
+ connect(this, SIGNAL(languageChanged()), m_core, SLOT(languageChanged()), Qt::QueuedConnection);
+ connect(this, SIGNAL(languageChanged()), this, SLOT(onLanguageChanged()), Qt::QueuedConnection);
+
+ connect(m_core, SIGNAL(wizardPageInsertionRequested(QWidget*, QInstaller::PackageManagerCore::WizardPage)),
+ this, SLOT(wizardPageInsertionRequested(QWidget*, QInstaller::PackageManagerCore::WizardPage)));
+ connect(m_core, SIGNAL(wizardPageRemovalRequested(QWidget*)),this,
+ SLOT(wizardPageRemovalRequested(QWidget*)));
+ connect(m_core, SIGNAL(wizardWidgetInsertionRequested(QWidget*, QInstaller::PackageManagerCore::WizardPage)),
+ this, SLOT(wizardWidgetInsertionRequested(QWidget*, QInstaller::PackageManagerCore::WizardPage)));
+ connect(m_core, SIGNAL(wizardWidgetRemovalRequested(QWidget*)), this,
+ SLOT(wizardWidgetRemovalRequested(QWidget*)));
+ connect(m_core, SIGNAL(wizardPageVisibilityChangeRequested(bool, int)), this,
+ SLOT(wizardPageVisibilityChangeRequested(bool, int)), Qt::QueuedConnection);
+
+ connect(m_core, SIGNAL(setAutomatedPageSwitchEnabled(bool)), this,
+ SLOT(setAutomatedPageSwitchEnabled(bool)));
+
+ connect(this, SIGNAL(customButtonClicked(int)), this, SLOT(customButtonClicked(int)));
+
+ for (int i = QWizard::BackButton; i < QWizard::CustomButton1; ++i)
+ d->m_defaultButtonText.insert(i, buttonText(QWizard::WizardButton(i)));
+
+#ifdef Q_WS_MAC
+ resize(sizeHint() * 1.25);
+#else
+ resize(sizeHint());
+#endif
+}
+
+PackageManagerGui::~PackageManagerGui()
+{
+ delete d;
+}
+
+void PackageManagerGui::setAutomatedPageSwitchEnabled(bool request)
+{
+ d->m_autoSwitchPage = request;
+}
+
+QString PackageManagerGui::defaultButtonText(int wizardButton) const
+{
+ return d->m_defaultButtonText.value(wizardButton);
+}
+
+void PackageManagerGui::clickButton(int wb, int delay)
+{
+ if (QAbstractButton *b = button(static_cast<QWizard::WizardButton>(wb) )) {
+ QTimer::singleShot(delay, b, SLOT(click()));
+ } else {
+ // TODO: we should probably abort immediately here (faulty test script)
+ qDebug() << "Button" << wb << "not found!";
+ }
+}
+
+/*!
+ Loads a script to perform the installation non-interactively.
+ @throws QInstaller::Error if the script is not readable/cannot be parsed
+*/
+void PackageManagerGui::loadControlScript(const QString &scriptPath)
+{
+ QFile file(scriptPath);
+ if (!file.open(QIODevice::ReadOnly)) {
+ throw Error(QObject::tr("Could not open the requested script file at %1: %2")
+ .arg(scriptPath, file.errorString()));
+ }
+
+ QScriptValue installerObject = d->m_controlScriptEngine.newQObject(m_core);
+ installerObject.setProperty(QLatin1String("componentByName"), d->m_controlScriptEngine
+ .newFunction(qInstallerComponentByName, 1));
+
+ d->m_controlScriptEngine.globalObject().setProperty(QLatin1String("installer"),
+ installerObject);
+ d->m_controlScriptEngine.globalObject().setProperty(QLatin1String("gui"),
+ d->m_controlScriptEngine.newQObject(this));
+ d->m_controlScriptEngine.globalObject().setProperty(QLatin1String("packagemanagergui"),
+ d->m_controlScriptEngine.newQObject(this));
+ registerMessageBox(&d->m_controlScriptEngine);
+
+#undef REGISTER_BUTTON
+#define REGISTER_BUTTON(x) buttons.setProperty(QLatin1String(#x), \
+ d->m_controlScriptEngine.newVariant(static_cast<int>(QWizard::x)));
+
+ QScriptValue buttons = d->m_controlScriptEngine.newArray();
+ REGISTER_BUTTON(BackButton)
+ REGISTER_BUTTON(NextButton)
+ REGISTER_BUTTON(CommitButton)
+ REGISTER_BUTTON(FinishButton)
+ REGISTER_BUTTON(CancelButton)
+ REGISTER_BUTTON(HelpButton)
+ REGISTER_BUTTON(CustomButton1)
+ REGISTER_BUTTON(CustomButton2)
+ REGISTER_BUTTON(CustomButton3)
+
+#undef REGISTER_BUTTON
+
+ d->m_controlScriptEngine.globalObject().setProperty(QLatin1String("buttons"), buttons);
+
+ d->m_controlScriptEngine.evaluate(QLatin1String(file.readAll()), scriptPath);
+ if (d->m_controlScriptEngine.hasUncaughtException()) {
+ throw Error(QObject::tr("Exception while loading the control script %1")
+ .arg(uncaughtExceptionString(&(d->m_controlScriptEngine)/*, scriptPath*/)));
+ }
+
+ QScriptValue comp = d->m_controlScriptEngine.evaluate(QLatin1String("Controller"));
+ if (d->m_controlScriptEngine.hasUncaughtException()) {
+ throw Error(QObject::tr("Exception while loading the control script %1")
+ .arg(uncaughtExceptionString(&(d->m_controlScriptEngine)/*, scriptPath*/)));
+ }
+
+ d->m_controlScript = comp;
+ d->m_controlScript.construct();
+
+ qDebug() << "Loaded control script" << scriptPath;
+}
+
+void PackageManagerGui::slotCurrentPageChanged(int id)
+{
+ QMetaObject::invokeMethod(this, "delayedControlScriptExecution", Qt::QueuedConnection,
+ Q_ARG(int, id));
+}
+
+void PackageManagerGui::callControlScriptMethod(const QString &methodName)
+{
+ if (!d->m_controlScript.isValid())
+ return;
+
+ QScriptValue method = d->m_controlScript.property(QLatin1String("prototype")).property(methodName);
+
+ if (!method.isValid()) {
+ qDebug() << "Control script callback" << methodName << "does not exist.";
+ return;
+ }
+
+ qDebug() << "Calling control script callback" << methodName;
+
+ method.call(d->m_controlScript);
+
+ if (d->m_controlScriptEngine.hasUncaughtException()) {
+ qCritical()
+ << uncaughtExceptionString(&(d->m_controlScriptEngine) /*, QLatin1String("control script")*/);
+ // TODO: handle error
+ }
+}
+
+void PackageManagerGui::delayedControlScriptExecution(int id)
+{
+ if (PackageManagerPage *const p = qobject_cast<PackageManagerPage*> (page(id)))
+ callControlScriptMethod(p->objectName() + QLatin1String("Callback"));
+}
+
+void PackageManagerGui::onLanguageChanged()
+{
+ d->m_defaultButtonText.clear();
+ for (int i = QWizard::BackButton; i < QWizard::CustomButton1; ++i)
+ d->m_defaultButtonText.insert(i, buttonText(QWizard::WizardButton(i)));
+}
+
+bool PackageManagerGui::event(QEvent *event)
+{
+ switch(event->type()) {
+ case QEvent::LanguageChange:
+ emit languageChanged();
+ break;
+ default:
+ break;
+ }
+ return QWizard::event(event);
+}
+
+void PackageManagerGui::showEvent(QShowEvent *event)
+{
+#ifndef Q_OS_LINUX
+ if (!event->spontaneous()) {
+ foreach (int id, pageIds()) {
+ const QString subTitle = page(id)->subTitle();
+ if (subTitle.isEmpty()) {
+ const QWizard::WizardStyle style = wizardStyle();
+ if ((style == QWizard::ClassicStyle || style == QWizard::ModernStyle))
+ page(id)->setSubTitle(QLatin1String(" ")); // otherwise the colors might screw up
+ }
+ }
+ }
+#endif
+ QWizard::showEvent(event);
+}
+
+void PackageManagerGui::wizardPageInsertionRequested(QWidget *widget,
+ QInstaller::PackageManagerCore::WizardPage page)
+{
+ // just in case it was already in there...
+ wizardPageRemovalRequested(widget);
+
+ int pageId = static_cast<int>(page) - 1;
+ while (QWizard::page(pageId) != 0)
+ --pageId;
+
+ // add it
+ setPage(pageId, new DynamicInstallerPage(widget, m_core));
+}
+
+void PackageManagerGui::wizardPageRemovalRequested(QWidget *widget)
+{
+ foreach (int pageId, pageIds()) {
+ DynamicInstallerPage *const dynamicPage = dynamic_cast<DynamicInstallerPage*>(page(pageId));
+ if (dynamicPage == 0)
+ continue;
+ if (dynamicPage->widget() != widget)
+ continue;
+ removePage(pageId);
+ d->m_defaultPages.remove(pageId);
+ }
+}
+
+void PackageManagerGui::wizardWidgetInsertionRequested(QWidget *widget,
+ QInstaller::PackageManagerCore::WizardPage page)
+{
+ Q_ASSERT(widget);
+ if (QWizardPage *const p = QWizard::page(page))
+ p->layout()->addWidget(widget);
+}
+
+void PackageManagerGui::wizardWidgetRemovalRequested(QWidget *widget)
+{
+ Q_ASSERT(widget);
+ widget->setParent(0);
+}
+
+void PackageManagerGui::wizardPageVisibilityChangeRequested(bool visible, int p)
+{
+ if (visible && page(p) == 0) {
+ setPage(p, d->m_defaultPages[p]);
+ } else if (!visible && page(p) != 0) {
+ d->m_defaultPages[p] = page(p);
+ removePage(p);
+ }
+}
+
+PackageManagerPage *PackageManagerGui::page(int pageId) const
+{
+ return qobject_cast<PackageManagerPage*> (QWizard::page(pageId));
+}
+
+QWidget *PackageManagerGui::pageWidgetByObjectName(const QString &name) const
+{
+ const QList<int> ids = pageIds();
+ foreach (const int i, ids) {
+ PackageManagerPage *const p = qobject_cast<PackageManagerPage*> (page(i));
+ if (p && p->objectName() == name) {
+ // For dynamic pages, return the contained widget (as read from the UI file), not the
+ // wrapper page
+ if (DynamicInstallerPage *dp = dynamic_cast<DynamicInstallerPage*>(p))
+ return dp->widget();
+ return p;
+ }
+ }
+ qDebug() << "No page found for object name" << name;
+ return 0;
+}
+
+QWidget *PackageManagerGui::currentPageWidget() const
+{
+ return currentPage();
+}
+
+void PackageManagerGui::cancelButtonClicked()
+{
+ if (currentId() != PackageManagerCore::Introduction
+ && currentId() != PackageManagerCore::InstallationFinished) {
+ PackageManagerPage *const page = qobject_cast<PackageManagerPage*> (currentPage());
+ if (page && page->isInterruptible() && m_core->status() != PackageManagerCore::Canceled
+ && m_core->status() != PackageManagerCore::Failure) {
+ const QMessageBox::StandardButton bt =
+ MessageBoxHandler::question(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("cancelInstallation"), tr("Question"),
+ tr("Do you want to abort the %1 process?").arg(m_core->isUninstaller() ? tr("uninstallation")
+ : tr("installation")), QMessageBox::Yes | QMessageBox::No);
+ if (bt == QMessageBox::Yes)
+ emit interrupted();
+ } else {
+ QString app = tr("installer");
+ if (m_core->isUninstaller())
+ app = tr("uninstaller");
+ if (m_core->isUpdater() || m_core->isPackageManager())
+ app = tr("maintenance");
+
+ const QMessageBox::StandardButton bt =
+ MessageBoxHandler::question(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("cancelInstallation"), tr("Question"),
+ tr("Do you want to quit the %1 application?").arg(app), QMessageBox::Yes | QMessageBox::No);
+ if (bt == QMessageBox::Yes)
+ QDialog::reject();
+ }
+ } else {
+ QDialog::reject();
+ }
+}
+
+void PackageManagerGui::rejectWithoutPrompt()
+{
+ m_core->setCanceled();
+ QDialog::reject();
+}
+
+void PackageManagerGui::reject()
+{
+ cancelButtonClicked();
+}
+
+void PackageManagerGui::setModified(bool value)
+{
+ d->m_modified = value;
+}
+
+void PackageManagerGui::showFinishedPage()
+{
+ qDebug() << "SHOW FINISHED PAGE";
+ if (d->m_autoSwitchPage)
+ next();
+ else
+ qobject_cast<QPushButton*>(button(QWizard::CancelButton))->setEnabled(false);
+}
+
+void PackageManagerGui::showSettingsButton(bool show)
+{
+ if (d->m_showSettingsButton == show)
+ return;
+
+ d->m_showSettingsButton = show;
+ setOption(QWizard::HaveCustomButton1, show);
+ setButtonText(QWizard::CustomButton1, tr("Settings"));
+
+ updateButtonLayout();
+}
+
+/*!
+ Force an update of our own button layout, needs to be called whenever a button option has been set.
+*/
+void PackageManagerGui::updateButtonLayout()
+{
+ QVector<QWizard::WizardButton> buttons(12, QWizard::NoButton);
+ if (options() & QWizard::HaveHelpButton)
+ buttons[(options() & QWizard::HelpButtonOnRight) ? 11 : 0] = QWizard::HelpButton;
+
+ buttons[1] = QWizard::Stretch;
+ if (options() & QWizard::HaveCustomButton1) {
+ buttons[1] = QWizard::CustomButton1;
+ buttons[2] = QWizard::Stretch;
+ }
+
+ if (options() & QWizard::HaveCustomButton2)
+ buttons[3] = QWizard::CustomButton2;
+
+ if (options() & QWizard::HaveCustomButton3)
+ buttons[4] = QWizard::CustomButton3;
+
+ if (!(options() & QWizard::NoCancelButton))
+ buttons[(options() & QWizard::CancelButtonOnLeft) ? 5 : 10] = QWizard::CancelButton;
+
+ buttons[6] = QWizard::BackButton;
+ buttons[7] = QWizard::NextButton;
+ buttons[8] = QWizard::CommitButton;
+ buttons[9] = QWizard::FinishButton;
+
+ setOption(QWizard::NoBackButtonOnLastPage, true);
+ setOption(QWizard::NoBackButtonOnStartPage, true);
+
+ setButtonLayout(buttons.toList());
+}
+
+void PackageManagerGui::setSettingsButtonEnabled(bool enabled)
+{
+ if (QAbstractButton *btn = button(QWizard::CustomButton1))
+ btn->setEnabled(enabled);
+}
+
+void PackageManagerGui::customButtonClicked(int which)
+{
+ if (QWizard::WizardButton(which) == QWizard::CustomButton1 && d->m_showSettingsButton)
+ emit settingsButtonClicked();
+}
+
+
+// -- PackageManagerPage
+
+PackageManagerPage::PackageManagerPage(PackageManagerCore *core)
+ : m_fresh(true)
+ , m_complete(true)
+ , m_core(core)
+{
+}
+
+PackageManagerCore *PackageManagerPage::packageManagerCore() const
+{
+ return m_core;
+}
+
+QVariantHash PackageManagerPage::elementsForPage(const QString &pageName) const
+{
+ const QVariant variant = m_core->settings().value(pageName);
+
+ QVariantHash hash;
+ if (variant.canConvert<QVariantHash>())
+ hash = variant.value<QVariantHash>();
+ return hash;
+}
+
+QString PackageManagerPage::titleForPage(const QString &pageName, const QString &value) const
+{
+ return titleFromHash(m_core->settings().titlesForPage(pageName), value);
+}
+
+QString PackageManagerPage::subTitleForPage(const QString &pageName, const QString &value) const
+{
+ return titleFromHash(m_core->settings().subTitlesForPage(pageName), value);
+}
+
+QString PackageManagerPage::titleFromHash(const QVariantHash &hash, const QString &value) const
+{
+ QString defaultValue = hash.value(QLatin1String("Default")).toString();
+ if (defaultValue.isEmpty())
+ defaultValue = value;
+
+ if (m_core->isUpdater())
+ return hash.value(QLatin1String("Updater"), defaultValue).toString();
+ if (m_core->isInstaller())
+ return hash.value(QLatin1String("Installer"), defaultValue).toString();
+ if (m_core->isPackageManager())
+ return hash.value(QLatin1String("PackageManager"), defaultValue).toString();
+ return hash.value(QLatin1String("Uninstaller"), defaultValue).toString();
+}
+
+QPixmap PackageManagerPage::watermarkPixmap() const
+{
+ return QPixmap(m_core->value(QLatin1String("WatermarkPixmap")));
+}
+
+QPixmap PackageManagerPage::logoPixmap() const
+{
+ return QPixmap(m_core->value(QLatin1String("LogoPixmap")));
+}
+
+QString PackageManagerPage::productName() const
+{
+ return m_core->value(QLatin1String("ProductName"));
+}
+
+bool PackageManagerPage::isComplete() const
+{
+ return m_complete;
+}
+
+void PackageManagerPage::setComplete(bool complete)
+{
+ m_complete = complete;
+ if (QWizard *w = wizard()) {
+ if (QAbstractButton *cancel = w->button(QWizard::CancelButton)) {
+ if (cancel->hasFocus()) {
+ if (QAbstractButton *next = w->button(QWizard::NextButton))
+ next->setFocus();
+ }
+ }
+ }
+ emit completeChanged();
+}
+
+void PackageManagerPage::insertWidget(QWidget *widget, const QString &siblingName, int offset)
+{
+ QWidget *sibling = findChild<QWidget *>(siblingName);
+ QWidget *parent = sibling ? sibling->parentWidget() : 0;
+ QLayout *layout = parent ? parent->layout() : 0;
+ QBoxLayout *blayout = qobject_cast<QBoxLayout *>(layout);
+
+ if (blayout) {
+ const int index = blayout->indexOf(sibling) + offset;
+ blayout->insertWidget(index, widget);
+ }
+}
+
+QWidget *PackageManagerPage::findWidget(const QString &objectName) const
+{
+ return findChild<QWidget*> (objectName);
+}
+
+/*!
+ \reimp
+ \Overwritten to support some kind of initializePage() in the case the wizard has been set
+ to QWizard::IndependentPages. If that option has been set, initializePage() would be only called
+ once. So we provide entering() and leaving() based on this overwritten function.
+*/
+void PackageManagerPage::setVisible(bool visible)
+{
+ QWizardPage::setVisible(visible);
+ qApp->processEvents();
+
+ if (m_fresh && !visible) {
+ // this is only hit once when the page gets added to the wizard
+ m_fresh = false;
+ return;
+ }
+
+ if (visible)
+ entering();
+ else
+ leaving();
+}
+
+int PackageManagerPage::nextId() const
+{
+ return QWizardPage::nextId();
+}
+
+
+// -- IntroductionPage
+
+IntroductionPage::IntroductionPage(PackageManagerCore *core)
+ : PackageManagerPage(core)
+ , m_widget(0)
+{
+ setObjectName(QLatin1String("IntroductionPage"));
+ setPixmap(QWizard::WatermarkPixmap, watermarkPixmap());
+ setSubTitle(subTitleForPage(QLatin1String("IntroductionPage")));
+ setTitle(titleForPage(QLatin1String("IntroductionPage"), tr("Setup - %1")).arg(productName()));
+
+ m_msgLabel = new QLabel(this);
+ m_msgLabel->setWordWrap(true);
+ m_msgLabel->setObjectName(QLatin1String("MessageLabel"));
+ const QVariantHash hash = elementsForPage(QLatin1String("IntroductionPage"));
+ m_msgLabel->setText(hash.value(QLatin1String("MessageLabel"), tr("Welcome to the %1 "
+ "Setup Wizard.")).toString().arg(productName()));
+
+ QVBoxLayout *layout = new QVBoxLayout(this);
+ setLayout(layout);
+ layout->addWidget(m_msgLabel);
+ layout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
+}
+
+void IntroductionPage::setWidget(QWidget *widget)
+{
+ if (m_widget) {
+ layout()->removeWidget(m_widget);
+ delete m_widget;
+ }
+ m_widget = widget;
+ if (m_widget)
+ static_cast<QVBoxLayout*>(layout())->addWidget(m_widget, 1);
+}
+
+void IntroductionPage::setText(const QString &text)
+{
+ m_msgLabel->setText(text);
+}
+
+
+// -- LicenseAgreementPage::ClickForwarder
+
+class LicenseAgreementPage::ClickForwarder : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit ClickForwarder(QAbstractButton *button)
+ : QObject(button)
+ , m_abstractButton(button) {}
+
+protected:
+ bool eventFilter(QObject *object, QEvent *event)
+ {
+ if (event->type() == QEvent::MouseButtonRelease) {
+ m_abstractButton->click();
+ return true;
+ }
+ // standard event processing
+ return QObject::eventFilter(object, event);
+ }
+private:
+ QAbstractButton *m_abstractButton;
+};
+
+
+// -- LicenseAgreementPage
+
+LicenseAgreementPage::LicenseAgreementPage(PackageManagerCore *core)
+ : PackageManagerPage(core)
+{
+ setPixmap(QWizard::LogoPixmap, logoPixmap());
+ setPixmap(QWizard::WatermarkPixmap, QPixmap());
+ setObjectName(QLatin1String("LicenseAgreementPage"));
+ setTitle(titleForPage(QLatin1String("LicenseAgreementPage"), tr("License Agreement")));
+ setSubTitle(subTitleForPage(QLatin1String("LicenseAgreementPage"), tr("Please read the following license "
+ "agreement(s). You must accept the terms contained in these agreement(s) before continuing with the "
+ "installation.")));
+
+ m_licenseListWidget = new QListWidget(this);
+ m_licenseListWidget->setObjectName(QLatin1String("LicenseListWidget"));
+ m_licenseListWidget->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
+ connect(m_licenseListWidget, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
+ this, SLOT(currentItemChanged(QListWidgetItem *)));
+
+ m_textBrowser = new QTextBrowser(this);
+ m_textBrowser->setReadOnly(true);
+ m_textBrowser->setOpenLinks(false);
+ m_textBrowser->setOpenExternalLinks(true);
+ m_textBrowser->setObjectName(QLatin1String("LicenseTextBrowser"));
+ m_textBrowser->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
+ connect(m_textBrowser, SIGNAL(anchorClicked(QUrl)), this, SLOT(openLicenseUrl(QUrl)));
+
+ QHBoxLayout *licenseBoxLayout = new QHBoxLayout();
+ licenseBoxLayout->addWidget(m_licenseListWidget);
+ licenseBoxLayout->addWidget(m_textBrowser);
+
+ QVBoxLayout *layout = new QVBoxLayout(this);
+ layout->addLayout(licenseBoxLayout);
+
+ m_acceptRadioButton = new QRadioButton(this);
+ m_acceptRadioButton->setShortcut(QKeySequence(tr("Alt+A", "agree license")));
+ m_acceptRadioButton->setObjectName(QLatin1String("AcceptLicenseRadioButton"));
+ ClickForwarder *acceptClickForwarder = new ClickForwarder(m_acceptRadioButton);
+
+ QLabel *acceptLabel = new QLabel;
+ acceptLabel->setWordWrap(true);
+ acceptLabel->installEventFilter(acceptClickForwarder);
+ acceptLabel->setObjectName(QLatin1String("AcceptLicenseLabel"));
+ acceptLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
+ const QVariantHash hash = elementsForPage(QLatin1String("LicenseAgreementPage"));
+ acceptLabel->setText(hash.value(QLatin1String("AcceptLicenseLabel"), tr("I accept the licenses.")).toString());
+
+ m_rejectRadioButton = new QRadioButton(this);
+ ClickForwarder *rejectClickForwarder = new ClickForwarder(m_rejectRadioButton);
+ m_rejectRadioButton->setObjectName(QString::fromUtf8("RejectLicenseRadioButton"));
+ m_rejectRadioButton->setShortcut(QKeySequence(tr("Alt+D", "do not agree license")));
+
+ QLabel *rejectLabel = new QLabel;
+ rejectLabel->setWordWrap(true);
+ rejectLabel->installEventFilter(rejectClickForwarder);
+ rejectLabel->setObjectName(QLatin1String("RejectLicenseLabel"));
+ rejectLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
+ rejectLabel->setText(hash.value(QLatin1String("RejectLicenseLabel"), tr("I do not accept the licenses.")).toString());
+
+#if defined(Q_WS_X11) || defined(Q_WS_MAC)
+ QFont labelFont(font());
+ labelFont.setPixelSize(9);
+ acceptLabel->setFont(labelFont);
+ rejectLabel->setFont(labelFont);
+#endif
+
+ QGridLayout *gridLayout = new QGridLayout;
+ gridLayout->setColumnStretch(1, 1);
+ gridLayout->addWidget(m_acceptRadioButton, 0, 0);
+ gridLayout->addWidget(acceptLabel, 0, 1);
+ gridLayout->addWidget(m_rejectRadioButton, 1, 0);
+ gridLayout->addWidget(rejectLabel, 1, 1);
+ layout->addLayout(gridLayout);
+
+ connect(m_acceptRadioButton, SIGNAL(toggled(bool)), this, SIGNAL(completeChanged()));
+ connect(m_rejectRadioButton, SIGNAL(toggled(bool)), this, SIGNAL(completeChanged()));
+
+ m_rejectRadioButton->setChecked(true);
+}
+
+void LicenseAgreementPage::entering()
+{
+ m_licenseListWidget->clear();
+ m_textBrowser->setText(QString());
+ m_licenseListWidget->setVisible(false);
+
+ packageManagerCore()->calculateComponentsToInstall();
+ foreach (QInstaller::Component *component, packageManagerCore()->orderedComponentsToInstall())
+ addLicenseItem(component->licenses());
+
+ const int licenseCount = m_licenseListWidget->count();
+ if (licenseCount > 0) {
+ m_licenseListWidget->setVisible(licenseCount > 1);
+ m_licenseListWidget->setCurrentItem(m_licenseListWidget->item(0));
+ }
+}
+
+bool LicenseAgreementPage::isComplete() const
+{
+ return m_acceptRadioButton->isChecked();
+}
+
+void LicenseAgreementPage::openLicenseUrl(const QUrl &url)
+{
+ QDesktopServices::openUrl(url);
+}
+
+void LicenseAgreementPage::currentItemChanged(QListWidgetItem *current)
+{
+ if (current)
+ m_textBrowser->setText(current->data(Qt::UserRole).toString());
+}
+
+void LicenseAgreementPage::addLicenseItem(const QHash<QString, QPair<QString, QString> > &hash)
+{
+ for (QHash<QString, QPair<QString, QString> >::const_iterator it = hash.begin();
+ it != hash.end(); ++it) {
+ QListWidgetItem *item = new QListWidgetItem(it.key(), m_licenseListWidget);
+ item->setData(Qt::UserRole, it.value().second);
+ }
+}
+
+
+// -- ComponentSelectionPage::Private
+
+class ComponentSelectionPage::Private : public QObject
+{
+ Q_OBJECT
+
+public:
+ Private(ComponentSelectionPage *qq, PackageManagerCore *core)
+ : q(qq)
+ , m_core(core)
+ , m_treeView(new QTreeView(q))
+ , m_allModel(new ComponentModel(4, m_core))
+ , m_updaterModel(new ComponentModel(4, m_core))
+ {
+ m_treeView->setObjectName(QLatin1String("ComponentsTreeView"));
+ m_allModel->setObjectName(QLatin1String("AllComponentsModel"));
+ m_updaterModel->setObjectName(QLatin1String("UpdaterComponentsModel"));
+
+ int i = 0;
+ m_currentModel = m_allModel;
+ ComponentModel *list[] = { m_allModel, m_updaterModel, 0 };
+ while (ComponentModel *model = list[i++]) {
+ connect(model, SIGNAL(defaultCheckStateChanged(bool)), q, SLOT(setModified(bool)));
+ connect(model, SIGNAL(defaultCheckStateChanged(bool)), m_core,
+ SLOT(componentsToInstallNeedsRecalculation()));
+
+ model->setHeaderData(ComponentModelHelper::NameColumn, Qt::Horizontal, ComponentSelectionPage::tr("Component Name"));
+ model->setHeaderData(ComponentModelHelper::InstalledVersionColumn, Qt::Horizontal,
+ ComponentSelectionPage::tr("Installed Version"));
+ model->setHeaderData(ComponentModelHelper::NewVersionColumn, Qt::Horizontal, ComponentSelectionPage::tr("New Version"));
+ model->setHeaderData(ComponentModelHelper::UncompressedSizeColumn, Qt::Horizontal, ComponentSelectionPage::tr("Size"));
+ }
+
+ QHBoxLayout *hlayout = new QHBoxLayout;
+ hlayout->addWidget(m_treeView, 3);
+
+ m_descriptionLabel = new QLabel(q);
+ m_descriptionLabel->setWordWrap(true);
+ m_descriptionLabel->setObjectName(QLatin1String("ComponentDescriptionLabel"));
+
+ QVBoxLayout *vlayout = new QVBoxLayout;
+ vlayout->addWidget(m_descriptionLabel);
+
+ m_sizeLabel = new QLabel(q);
+ m_sizeLabel->setWordWrap(true);
+ vlayout->addWidget(m_sizeLabel);
+ m_sizeLabel->setObjectName(QLatin1String("ComponentSizeLabel"));
+
+ vlayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::MinimumExpanding,
+ QSizePolicy::MinimumExpanding));
+ hlayout->addLayout(vlayout, 2);
+
+ QVBoxLayout *layout = new QVBoxLayout(q);
+ layout->addLayout(hlayout, 1);
+
+ m_checkDefault = new QPushButton;
+ connect(m_checkDefault, SIGNAL(clicked()), this, SLOT(selectDefault()));
+ connect(m_allModel, SIGNAL(defaultCheckStateChanged(bool)), m_checkDefault, SLOT(setEnabled(bool)));
+ const QVariantHash hash = q->elementsForPage(QLatin1String("ComponentSelectionPage"));
+ if (m_core->isInstaller()) {
+ m_checkDefault->setObjectName(QLatin1String("SelectDefaultComponentsButton"));
+ m_checkDefault->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+A", "select default components")));
+ m_checkDefault->setText(hash.value(QLatin1String("SelectDefaultComponentsButton"), ComponentSelectionPage::tr("Def&ault"))
+ .toString());
+ } else {
+ m_checkDefault->setEnabled(false);
+ m_checkDefault->setObjectName(QLatin1String("ResetComponentsButton"));
+ m_checkDefault->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+R", "reset to already installed components")));
+ m_checkDefault->setText(hash.value(QLatin1String("ResetComponentsButton"), ComponentSelectionPage::tr("&Reset")).toString());
+ }
+ hlayout = new QHBoxLayout;
+ hlayout->addWidget(m_checkDefault);
+
+ m_checkAll = new QPushButton;
+ hlayout->addWidget(m_checkAll);
+ connect(m_checkAll, SIGNAL(clicked()), this, SLOT(selectAll()));
+ m_checkAll->setObjectName(QLatin1String("SelectAllComponentsButton"));
+ m_checkAll->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+S", "select all components")));
+ m_checkAll->setText(hash.value(QLatin1String("SelectAllComponentsButton"), ComponentSelectionPage::tr("&Select All")).toString());
+
+ m_uncheckAll = new QPushButton;
+ hlayout->addWidget(m_uncheckAll);
+ connect(m_uncheckAll, SIGNAL(clicked()), this, SLOT(deselectAll()));
+ m_uncheckAll->setObjectName(QLatin1String("DeselectAllComponentsButton"));
+ m_uncheckAll->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+D", "deselect all components")));
+ m_uncheckAll->setText(hash.value(QLatin1String("DeselectAllComponentsButton"), ComponentSelectionPage::tr("&Deselect All"))
+ .toString());
+
+ hlayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::MinimumExpanding,
+ QSizePolicy::MinimumExpanding));
+ layout->addLayout(hlayout);
+
+ connect(m_core, SIGNAL(finishAllComponentsReset()), this, SLOT(allComponentsChanged()),
+ Qt::QueuedConnection);
+ connect(m_core, SIGNAL(finishUpdaterComponentsReset()), this, SLOT(updaterComponentsChanged()),
+ Qt::QueuedConnection);
+ }
+
+ void updateTreeView()
+ {
+ m_checkDefault->setVisible(m_core->isInstaller() || m_core->isPackageManager());
+ if (m_treeView->selectionModel()) {
+ disconnect(m_treeView->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)),
+ this, SLOT(currentChanged(QModelIndex)));
+ disconnect(m_currentModel, SIGNAL(checkStateChanged(QModelIndex)), this,
+ SLOT(currentChanged(QModelIndex)));
+ }
+
+ m_currentModel = m_core->isUpdater() ? m_updaterModel : m_allModel;
+ m_treeView->setModel(m_currentModel);
+ m_treeView->setExpanded(m_currentModel->index(0, 0), true);
+
+ if (m_core->isInstaller()) {
+ m_treeView->setHeaderHidden(true);
+ for (int i = 1; i < m_currentModel->columnCount(); ++i)
+ m_treeView->hideColumn(i);
+ } else {
+ m_treeView->header()->setStretchLastSection(true);
+ for (int i = 0; i < m_currentModel->columnCount(); ++i)
+ m_treeView->resizeColumnToContents(i);
+ }
+
+ bool hasChildren = false;
+ const int rowCount = m_currentModel->rowCount();
+ for (int row = 0; row < rowCount && !hasChildren; ++row)
+ hasChildren = m_currentModel->hasChildren(m_currentModel->index(row, 0));
+ m_treeView->setRootIsDecorated(hasChildren);
+
+ connect(m_treeView->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)),
+ this, SLOT(currentChanged(QModelIndex)));
+ connect(m_currentModel, SIGNAL(checkStateChanged(QModelIndex)), this,
+ SLOT(currentChanged(QModelIndex)));
+
+ m_treeView->setCurrentIndex(m_currentModel->index(0, 0));
+ }
+
+public slots:
+ void currentChanged(const QModelIndex &current)
+ {
+ // if there is not selection or the current selected node didn't change, return
+ if (!current.isValid() || current != m_treeView->selectionModel()->currentIndex())
+ return;
+
+ m_descriptionLabel->setText(m_currentModel->data(m_currentModel->index(current.row(),
+ ComponentModelHelper::NameColumn, current.parent()), Qt::ToolTipRole).toString());
+
+ m_sizeLabel->clear();
+ if (!m_core->isUninstaller()) {
+ Component *component = m_currentModel->componentFromIndex(current);
+ if (component && component->updateUncompressedSize() > 0) {
+ const QVariantHash hash = q->elementsForPage(QLatin1String("ComponentSelectionPage"));
+ m_sizeLabel->setText(hash.value(QLatin1String("ComponentSizeLabel"),
+ ComponentSelectionPage::tr("This component will occupy approximately %1 on your hard disk drive.")).toString()
+ .arg(m_currentModel->data(m_currentModel->index(current.row(),
+ ComponentModelHelper::UncompressedSizeColumn, current.parent())).toString()));
+ }
+ }
+ }
+
+ void selectAll()
+ {
+ m_currentModel->selectAll();
+
+ m_checkAll->setEnabled(!m_currentModel->hasCheckedComponents());
+ m_uncheckAll->setEnabled(m_currentModel->hasCheckedComponents());
+ }
+
+ void deselectAll()
+ {
+ m_currentModel->deselectAll();
+
+ m_checkAll->setEnabled(m_currentModel->hasCheckedComponents());
+ m_uncheckAll->setEnabled(!m_currentModel->hasCheckedComponents());
+ }
+
+ void selectDefault()
+ {
+ m_currentModel->selectDefault();
+
+ // Do not apply special magic here to keep the enabled/ disabled state in sync with the checked
+ // components. We would need to implement the counter in the model, which has an unnecessary impact
+ // on the complexity and amount of code compared to what we gain in functionality.
+ m_checkAll->setEnabled(true);
+ m_uncheckAll->setEnabled(true);
+ }
+
+private slots:
+ void allComponentsChanged()
+ {
+ m_allModel->setRootComponents(m_core->rootComponents());
+ }
+
+ void updaterComponentsChanged()
+ {
+ m_updaterModel->setRootComponents(m_core->updaterComponents());
+ }
+
+public:
+ ComponentSelectionPage *q;
+ PackageManagerCore *m_core;
+ QTreeView *m_treeView;
+ ComponentModel *m_allModel;
+ ComponentModel *m_updaterModel;
+ ComponentModel *m_currentModel;
+ QLabel *m_sizeLabel;
+ QLabel *m_descriptionLabel;
+ QPushButton *m_checkAll;
+ QPushButton *m_uncheckAll;
+ QPushButton *m_checkDefault;
+};
+
+
+// -- ComponentSelectionPage
+
+/*!
+ \class QInstaller::ComponentSelectionPage
+ On this page the user can select and deselect what he wants to be installed.
+*/
+ComponentSelectionPage::ComponentSelectionPage(PackageManagerCore *core)
+ : PackageManagerPage(core)
+ , d(new Private(this, core))
+{
+ setPixmap(QWizard::LogoPixmap, logoPixmap());
+ setPixmap(QWizard::WatermarkPixmap, QPixmap());
+ setObjectName(QLatin1String("ComponentSelectionPage"));
+ setTitle(titleForPage(QLatin1String("ComponentSelectionPage"), tr("Select Components")));
+}
+
+ComponentSelectionPage::~ComponentSelectionPage()
+{
+ delete d;
+}
+
+void ComponentSelectionPage::entering()
+{
+ static const char *strings[] = {
+ QT_TR_NOOP("Please select the components you want to update."),
+ QT_TR_NOOP("Please select the components you want to install."),
+ QT_TR_NOOP("Please select the components you want to uninstall."),
+ QT_TR_NOOP("Select the components to install. Deselect installed components to uninstall them.")
+ };
+
+ int index = 0;
+ PackageManagerCore *core = packageManagerCore();
+ if (core->isInstaller()) index = 1;
+ if (core->isUninstaller()) index = 2;
+ if (core->isPackageManager()) index = 3;
+ setSubTitle(subTitleForPage(QLatin1String("ComponentSelectionPage"), tr(strings[index])));
+
+ d->updateTreeView();
+ setModified(isComplete());
+}
+
+void ComponentSelectionPage::showEvent(QShowEvent *event)
+{
+ // remove once we deprecate isSelected, setSelected etc...
+ if (!event->spontaneous())
+ packageManagerCore()->resetComponentsToUserCheckedState();
+ QWizardPage::showEvent(event);
+}
+
+void ComponentSelectionPage::selectAll()
+{
+ d->selectAll();
+}
+
+void ComponentSelectionPage::deselectAll()
+{
+ d->deselectAll();
+}
+
+void ComponentSelectionPage::selectDefault()
+{
+ if (packageManagerCore()->isInstaller())
+ d->selectDefault();
+}
+
+/*!
+ Selects the component with /a id in the component tree.
+*/
+void ComponentSelectionPage::selectComponent(const QString &id)
+{
+ const QModelIndex &idx = d->m_currentModel->indexFromComponentName(id);
+ if (idx.isValid())
+ d->m_currentModel->setData(idx, Qt::Checked, Qt::CheckStateRole);
+}
+
+/*!
+ Deselects the component with /a id in the component tree.
+*/
+void ComponentSelectionPage::deselectComponent(const QString &id)
+{
+ const QModelIndex &idx = d->m_currentModel->indexFromComponentName(id);
+ if (idx.isValid())
+ d->m_currentModel->setData(idx, Qt::Unchecked, Qt::CheckStateRole);
+}
+
+void ComponentSelectionPage::setModified(bool modified)
+{
+ setComplete(modified);
+}
+
+bool ComponentSelectionPage::isComplete() const
+{
+ if (packageManagerCore()->isInstaller() || packageManagerCore()->isUpdater())
+ return d->m_currentModel->hasCheckedComponents();
+ return !d->m_currentModel->defaultCheckState();
+}
+
+
+// -- TargetDirectoryPage
+
+TargetDirectoryPage::TargetDirectoryPage(PackageManagerCore *core)
+ : PackageManagerPage(core)
+{
+ setPixmap(QWizard::LogoPixmap, logoPixmap());
+ setPixmap(QWizard::WatermarkPixmap, QPixmap());
+ setObjectName(QLatin1String("TargetDirectoryPage"));
+ setSubTitle(subTitleForPage(QLatin1String("TargetDirectoryPage")));
+ setTitle(titleForPage(QLatin1String("TargetDirectoryPage"), tr("Installation Folder")));
+
+ QVBoxLayout *layout = new QVBoxLayout(this);
+
+ QLabel *msgLabel = new QLabel(this);
+ msgLabel->setWordWrap(true);
+ msgLabel->setObjectName(QLatin1String("MessageLabel"));
+ const QVariantHash hash = elementsForPage(QLatin1String("TargetDirectoryPage"));
+ msgLabel->setText(hash.value(QLatin1String("MessageLabel"), tr("Please specify the folder "
+ "where %1 will be installed.")).toString().arg(productName()));
+ layout->addWidget(msgLabel);
+
+ QHBoxLayout *hlayout = new QHBoxLayout;
+
+ m_lineEdit = new QLineEdit(this);
+ m_lineEdit->setObjectName(QLatin1String("TargetDirectoryLineEdit"));
+ connect(m_lineEdit, SIGNAL(textChanged(QString)), this, SIGNAL(completeChanged()));
+ hlayout->addWidget(m_lineEdit);
+
+ QPushButton *browseButton = new QPushButton(this);
+ browseButton->setObjectName(QLatin1String("BrowseDirectoryButton"));
+ connect(browseButton, SIGNAL(clicked()), this, SLOT(dirRequested()));
+ browseButton->setShortcut(QKeySequence(tr("Alt+R", "browse file system to choose a file")));
+ browseButton->setText(hash.value(QLatin1String("BrowseDirectoryButton"), tr("B&rowse..."))
+ .toString());
+ hlayout->addWidget(browseButton);
+
+ layout->addLayout(hlayout);
+ setLayout(layout);
+}
+
+QString TargetDirectoryPage::targetDir() const
+{
+ return m_lineEdit->text();
+}
+
+void TargetDirectoryPage::setTargetDir(const QString &dirName)
+{
+ m_lineEdit->setText(dirName);
+}
+
+void TargetDirectoryPage::initializePage()
+{
+ QString targetDir = packageManagerCore()->value(scTargetDir);
+ if (targetDir.isEmpty()) {
+ targetDir = QDir::homePath() + QDir::separator();
+ // prevent spaces in the default target directory
+ if (targetDir.contains(QLatin1Char(' ')))
+ targetDir = QDir::rootPath();
+ targetDir += productName().remove(QLatin1Char(' '));
+ }
+ m_lineEdit->setText(QDir::toNativeSeparators(QDir(targetDir).absolutePath()));
+
+ PackageManagerPage::initializePage();
+}
+
+bool TargetDirectoryPage::validatePage()
+{
+ const QVariantHash hash = elementsForPage(QLatin1String("TargetDirectoryPage"));
+ if (targetDir().isEmpty()) {
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("EmptyTargetDirectoryMessage"), tr("Error"), hash
+ .value(QLatin1String("EmptyTargetDirectoryMessage"), tr("The install directory cannot be "
+ "empty, please specify a valid folder.")).toString(), QMessageBox::Ok);
+ return false;
+ }
+
+ const QDir dir(targetDir());
+ // it exists, but is empty (might be created by the Browse button (getExistingDirectory)
+ if (dir.exists() && dir.entryList(QDir::NoDotAndDotDot).isEmpty())
+ return true;
+
+ if (dir.exists() && dir.isReadable()) {
+ // it exists, but is not empty
+ if (dir == QDir::root()) {
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("ForbiddenTargetDirectoryMessage"), tr("Error"), hash
+ .value(QLatin1String("ForbiddenTargetDirectoryMessage"), tr("As the install directory is "
+ "completely deleted, installing in %1 is forbidden.")).toString().arg(QDir::rootPath()),
+ QMessageBox::Ok);
+ return false;
+ }
+
+ if (!QVariant(packageManagerCore()->value(scRemoveTargetDir)).toBool())
+ return true;
+
+ return MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("OverwriteTargetDirectoryMessage"), tr("Warning"), hash
+ .value(QLatin1String("OverwriteTargetDirectoryMessage"), tr("You have selected an existing, "
+ "non-empty folder for installation. Note that it will be completely wiped on uninstallation of "
+ "this application. It is not advisable to install into this folder as installation might fail. "
+ "Do you want to continue?")).toString(), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes;
+ }
+ return true;
+}
+
+void TargetDirectoryPage::entering()
+{
+}
+
+void TargetDirectoryPage::leaving()
+{
+ packageManagerCore()->setValue(scTargetDir, targetDir());
+}
+
+void TargetDirectoryPage::targetDirSelected()
+{
+}
+
+void TargetDirectoryPage::dirRequested()
+{
+ const QVariantHash hash = elementsForPage(QLatin1String("TargetDirectoryPage"));
+ const QString newDirName = QFileDialog::getExistingDirectory(this, hash
+ .value(QLatin1String("SelectInstallationFolderCaption"), tr("Select Installation Folder")).toString(),
+ targetDir());
+ if (newDirName.isEmpty() || newDirName == targetDir())
+ return;
+ m_lineEdit->setText(QDir::toNativeSeparators(newDirName));
+}
+
+
+// -- StartMenuDirectoryPage
+
+StartMenuDirectoryPage::StartMenuDirectoryPage(PackageManagerCore *core)
+ : PackageManagerPage(core)
+{
+ setPixmap(QWizard::LogoPixmap, logoPixmap());
+ setPixmap(QWizard::WatermarkPixmap, QPixmap());
+ setObjectName(QLatin1String("StartMenuDirectoryPage"));
+ setTitle(titleForPage(QLatin1String("StartMenuDirectoryPage"), tr("Start Menu shortcuts")));
+ setSubTitle(subTitleForPage(QLatin1String("StartMenuDirectoryPage"), tr("Select the Start Menu in which "
+ "you would like to create the program's shortcuts. You can also enter a name to create a new folder.")));
+
+ m_lineEdit = new QLineEdit(this);
+ m_lineEdit->setObjectName(QLatin1String("LineEdit"));
+
+ QString startMenuDir = core->value(scStartMenuDir);
+ if (startMenuDir.isEmpty())
+ startMenuDir = productName();
+ m_lineEdit->setText(startMenuDir);
+
+ // grab existing start menu folders
+ QSettings user(QLatin1String("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\"
+ "Explorer\\User Shell Folders"), QSettings::NativeFormat);
+ // User Shell Folders uses %USERPROFILE%
+ startMenuPath = replaceWindowsEnvironmentVariables(user.value(QLatin1String("Programs"), QString())
+ .toString());
+ core->setValue(QLatin1String("DesktopDir"), replaceWindowsEnvironmentVariables(user
+ .value(QLatin1String("Desktop")).toString()));
+
+ QDir dir(startMenuPath); // user only dirs
+ QStringList dirs = dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
+
+ if (core->value(QLatin1String("AllUsers")) == QLatin1String("true")) {
+ qDebug() << "AllUsers set. Using HKEY_LOCAL_MACHINE";
+ QSettings system(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\"
+ "Explorer\\Shell Folders"), QSettings::NativeFormat);
+ startMenuPath = system.value(QLatin1String("Common Programs"), QString()).toString();
+ core->setValue(QLatin1String("DesktopDir"),system.value(QLatin1String("Desktop")).toString());
+
+ dir.setPath(startMenuPath); // system only dirs
+ dirs += dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
+ }
+
+ qDebug() << "StartMenuPath: \t" << startMenuPath;
+ qDebug() << "DesktopDir: \t" << core->value(QLatin1String("DesktopDir"));
+
+ m_listWidget = new QListWidget(this);
+ if (!dirs.isEmpty()) {
+ dirs.removeDuplicates();
+ foreach (const QString &dir, dirs)
+ new QListWidgetItem(dir, m_listWidget);
+ }
+
+ QVBoxLayout *layout = new QVBoxLayout(this);
+ layout->addWidget(m_lineEdit);
+ layout->addWidget(m_listWidget);
+
+ setLayout(layout);
+
+ connect(m_listWidget, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), this,
+ SLOT(currentItemChanged(QListWidgetItem*)));
+}
+
+QString StartMenuDirectoryPage::startMenuDir() const
+{
+ return m_lineEdit->text();
+}
+
+void StartMenuDirectoryPage::setStartMenuDir(const QString &startMenuDir)
+{
+ m_lineEdit->setText(startMenuDir);
+}
+
+void StartMenuDirectoryPage::leaving()
+{
+ packageManagerCore()->setValue(scStartMenuDir, startMenuPath + QDir::separator() + startMenuDir());
+}
+
+void StartMenuDirectoryPage::currentItemChanged(QListWidgetItem *current)
+{
+ if (current) {
+ QString dir = current->data(Qt::DisplayRole).toString();
+ if (!dir.isEmpty())
+ dir += QDir::separator();
+ setStartMenuDir(dir + packageManagerCore()->value(scStartMenuDir));
+ }
+}
+
+
+// -- ReadyForInstallationPage
+
+ReadyForInstallationPage::ReadyForInstallationPage(PackageManagerCore *core)
+ : PackageManagerPage(core)
+ , m_msgLabel(new QLabel)
+{
+ setPixmap(QWizard::LogoPixmap, logoPixmap());
+ setPixmap(QWizard::WatermarkPixmap, QPixmap());
+ setObjectName(QLatin1String("ReadyForInstallationPage"));
+ setSubTitle(subTitleForPage(QLatin1String("ReadyForInstallationPage")));
+
+ QVBoxLayout *baseLayout = new QVBoxLayout();
+ baseLayout->setObjectName(QLatin1String("BaseLayout"));
+
+ QVBoxLayout *topLayout = new QVBoxLayout();
+ topLayout->setObjectName(QLatin1String("TopLayout"));
+
+ m_msgLabel->setWordWrap(true);
+ m_msgLabel->setObjectName(QLatin1String("MessageLabel"));
+ m_msgLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+ topLayout->addWidget(m_msgLabel);
+ baseLayout->addLayout(topLayout);
+
+ m_taskDetailsButton = new QPushButton(tr("&Show Details"), this);
+ m_taskDetailsButton->setObjectName(QLatin1String("TaskDetailsButton"));
+ m_taskDetailsButton->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
+ connect(m_taskDetailsButton, SIGNAL(clicked()), this, SLOT(toggleDetails()));
+ topLayout->addWidget(m_taskDetailsButton);
+
+ QVBoxLayout *bottomLayout = new QVBoxLayout();
+ bottomLayout->setObjectName(QLatin1String("BottomLayout"));
+ bottomLayout->addStretch();
+
+ m_taskDetailsBrowser = new QTextBrowser(this);
+ m_taskDetailsBrowser->setReadOnly(true);
+ m_taskDetailsBrowser->setObjectName(QLatin1String("TaskDetailsBrowser"));
+ m_taskDetailsBrowser->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ m_taskDetailsBrowser->setVisible(false);
+ bottomLayout->addWidget(m_taskDetailsBrowser);
+ bottomLayout->setStretch(1, 10);
+ baseLayout->addLayout(bottomLayout);
+
+ setLayout(baseLayout);
+}
+
+
+/*!
+ \reimp
+*/
+void ReadyForInstallationPage::entering()
+{
+ setCommitPage(false);
+
+ if (packageManagerCore()->isUninstaller()) {
+ m_taskDetailsButton->setVisible(false);
+ m_taskDetailsBrowser->setVisible(false);
+ setButtonText(QWizard::CommitButton, tr("U&ninstall"));
+ setTitle(titleForPage(objectName(), tr("Ready to Uninstall")));
+ m_msgLabel->setText(tr("Setup is now ready to begin removing %1 from your computer.<br>"
+ "<font color=\"red\">The program dir %2 will be deleted completely</font>, "
+ "including all content in that directory!")
+ .arg(productName(), QDir::toNativeSeparators(QDir(packageManagerCore()->value(scTargetDir))
+ .absolutePath())));
+ setCommitPage(true);
+ return;
+ } else if (packageManagerCore()->isPackageManager() || packageManagerCore()->isUpdater()) {
+ setButtonText(QWizard::CommitButton, tr("U&pdate"));
+ setTitle(titleForPage(objectName(), tr("Ready to Update Packages")));
+ m_msgLabel->setText(tr("Setup is now ready to begin updating your installation."));
+ } else {
+ Q_ASSERT(packageManagerCore()->isInstaller());
+ setButtonText(QWizard::CommitButton, tr("&Install"));
+ setTitle(titleForPage(objectName(), tr("Ready to Install")));
+ m_msgLabel->setText(tr("Setup is now ready to begin installing %1 on your computer.")
+ .arg(productName()));
+ }
+
+ refreshTaskDetailsBrowser();
+
+ const VolumeInfo tempVolume = VolumeInfo::fromPath(QDir::tempPath());
+ const VolumeInfo targetVolume = VolumeInfo::fromPath(packageManagerCore()->value(scTargetDir));
+
+ const quint64 tempVolumeAvailableSize = tempVolume.availableSize();
+ const quint64 installVolumeAvailableSize = targetVolume.availableSize();
+
+ // at the moment there is no better way to check this
+ if (targetVolume.size() == 0 && installVolumeAvailableSize == 0) {
+ qDebug() << QString::fromLatin1("Could not determine available space on device. Volume descriptor: %1,"
+ "Mount path: %2. Continue silently.").arg(targetVolume.volumeDescriptor(), targetVolume.mountPath());
+ setCommitPage(true);
+ return; // TODO: Shouldn't this also disable the "Next" button?
+ }
+
+ const bool tempOnSameVolume = (targetVolume == tempVolume);
+ if (tempOnSameVolume) {
+ qDebug() << "Tmp and install folder are on the same volume. Volume mount point:" << targetVolume
+ .mountPath() << "Free space available:" << humanReadableSize(installVolumeAvailableSize);
+ } else {
+ qDebug() << "Tmp is on a different volume than the install folder. Tmp volume mount point:"
+ << tempVolume.mountPath() << "Free space available:" << humanReadableSize(tempVolumeAvailableSize)
+ << "Install volume mount point:" << targetVolume.mountPath() << "Free space "
+ "available:" << humanReadableSize(installVolumeAvailableSize);
+ }
+
+ const quint64 extraSpace = 256 * 1024 * 1024LL;
+ quint64 required(packageManagerCore()->requiredDiskSpace());
+ quint64 tempRequired(packageManagerCore()->requiredTemporaryDiskSpace());
+ if (required < extraSpace) {
+ required += 0.1 * required;
+ tempRequired += 0.1 * tempRequired;
+ } else {
+ required += extraSpace;
+ tempRequired += extraSpace;
+ }
+
+ quint64 repositorySize = 0;
+ const bool createLocalRepository = packageManagerCore()->createLocalRepositoryFromBinary();
+ if (createLocalRepository) {
+ repositorySize = QFile(QCoreApplication::applicationFilePath()).size();
+ required += repositorySize; // if we create a local repository, take that space into account as well
+ }
+
+ qDebug() << "Installation space required:" << humanReadableSize(required) << "Temporary space required:"
+ << humanReadableSize(tempRequired) << "Local repository size:" << humanReadableSize(repositorySize);
+
+ if (tempOnSameVolume && (installVolumeAvailableSize <= (required + tempRequired))) {
+ m_msgLabel->setText(tr("Not enough disk space to store temporary files and the installation! "
+ "Available space: %1, at least required %2.").arg(humanReadableSize(installVolumeAvailableSize),
+ humanReadableSize(required + tempRequired)));
+ return;
+ }
+
+ if (installVolumeAvailableSize < required) {
+ m_msgLabel->setText(tr("Not enough disk space to store all selected components! Available space: %1, "
+ "at least required: %2.").arg(humanReadableSize(installVolumeAvailableSize),
+ humanReadableSize(required)));
+ return;
+ }
+
+ if (tempVolumeAvailableSize < tempRequired) {
+ m_msgLabel->setText(tr("Not enough disk space to store temporary files! Available space: %1, at "
+ "least required: %2.").arg(humanReadableSize(tempVolumeAvailableSize),
+ humanReadableSize(tempRequired)));
+ return;
+ }
+
+ if (installVolumeAvailableSize - required < 0.01 * targetVolume.size()) {
+ // warn for less than 1% of the volume's space being free
+ m_msgLabel->setText(tr("The volume you selected for installation seems to have sufficient space for "
+ "installation, but there will be less than 1% of the volume's space available afterwards. %1")
+ .arg(m_msgLabel->text()));
+ } else if (installVolumeAvailableSize - required < 100 * 1024 * 1024LL) {
+ // warn for less than 100MB being free
+ m_msgLabel->setText(tr("The volume you selected for installation seems to have sufficient space for "
+ "installation, but there will be less than 100 MB available afterwards. %1")
+ .arg(m_msgLabel->text()));
+ }
+ setCommitPage(true);
+}
+
+void ReadyForInstallationPage::refreshTaskDetailsBrowser()
+{
+ QString htmlOutput;
+ QString lastInstallReason;
+ if (!packageManagerCore()->calculateComponentsToUninstall() ||
+ !packageManagerCore()->calculateComponentsToInstall()) {
+ htmlOutput.append(QString::fromLatin1("<h2><font color=\"red\">%1</font></h2><ul>")
+ .arg(tr("Can not resolve all dependencies!")));
+ //if we have a missing dependency or a recursion we can display it
+ if (!packageManagerCore()->componentsToInstallError().isEmpty()) {
+ htmlOutput.append(QString::fromLatin1("<li> %1 </li>").arg(
+ packageManagerCore()->componentsToInstallError()));
+ }
+ htmlOutput.append(QLatin1String("</ul>"));
+ m_taskDetailsBrowser->setHtml(htmlOutput);
+ if (!m_taskDetailsBrowser->isVisible())
+ toggleDetails();
+ setCommitPage(false);
+ return;
+ }
+
+ // In case of updater mode we don't uninstall components.
+ if (!packageManagerCore()->isUpdater()) {
+ QList<Component*> componentsToRemove = packageManagerCore()->componentsToUninstall();
+ if (!componentsToRemove.isEmpty()) {
+ htmlOutput.append(QString::fromLatin1("<h3>%1</h3><ul>").arg(tr("Components about to be removed.")));
+ foreach (Component *component, componentsToRemove)
+ htmlOutput.append(QString::fromLatin1("<li> %1 </li>").arg(component->name()));
+ htmlOutput.append(QLatin1String("</ul>"));
+ }
+ }
+
+ foreach (Component *component, packageManagerCore()->orderedComponentsToInstall()) {
+ const QString installReason = packageManagerCore()->installReason(component);
+ if (lastInstallReason != installReason) {
+ if (!lastInstallReason.isEmpty()) // means we had to close the previous list
+ htmlOutput.append(QLatin1String("</ul>"));
+ htmlOutput.append(QString::fromLatin1("<h3>%1</h3><ul>").arg(installReason));
+ lastInstallReason = installReason;
+ }
+ htmlOutput.append(QString::fromLatin1("<li> %1 </li>").arg(component->name()));
+ }
+ m_taskDetailsBrowser->setHtml(htmlOutput);
+}
+
+void ReadyForInstallationPage::toggleDetails()
+{
+ const bool visible = !m_taskDetailsBrowser->isVisible();
+ m_taskDetailsBrowser->setVisible(visible);
+ m_taskDetailsButton->setText(visible ? tr("&Hide Details") : tr("&Show Details"));
+}
+
+void ReadyForInstallationPage::leaving()
+{
+ setButtonText(QWizard::CommitButton, gui()->defaultButtonText(QWizard::CommitButton));
+}
+
+/*!
+ \reimp
+*/
+bool ReadyForInstallationPage::isComplete() const
+{
+ return isCommitPage();
+}
+
+
+// -- PerformInstallationPage
+
+/*!
+ \class QInstaller::PerformInstallationPage
+ On this page the user can see on a progress bar how far the current installation is.
+*/
+PerformInstallationPage::PerformInstallationPage(PackageManagerCore *core)
+ : PackageManagerPage(core)
+ , m_performInstallationForm(new PerformInstallationForm(this))
+{
+ setPixmap(QWizard::LogoPixmap, logoPixmap());
+ setPixmap(QWizard::WatermarkPixmap, QPixmap());
+ setObjectName(QLatin1String("PerformInstallationPage"));
+ setSubTitle(subTitleForPage(QLatin1String("PerformInstallationPage")));
+
+ m_performInstallationForm->setupUi(this);
+
+ connect(ProgressCoordinator::instance(), SIGNAL(detailTextChanged(QString)), m_performInstallationForm,
+ SLOT(appendProgressDetails(QString)));
+ connect(ProgressCoordinator::instance(), SIGNAL(detailTextResetNeeded()), m_performInstallationForm,
+ SLOT(clearDetailsBrowser()));
+ connect(m_performInstallationForm, SIGNAL(showDetailsChanged()), this, SLOT(toggleDetailsWereChanged()));
+
+ connect(core, SIGNAL(installationStarted()), this, SLOT(installationStarted()));
+ connect(core, SIGNAL(uninstallationStarted()), this, SLOT(installationStarted()));
+ connect(core, SIGNAL(installationFinished()), this, SLOT(installationFinished()));
+ connect(core, SIGNAL(uninstallationFinished()), this, SLOT(installationFinished()));
+ connect(core, SIGNAL(titleMessageChanged(QString)), this, SLOT(setTitleMessage(QString)));
+ connect(this, SIGNAL(setAutomatedPageSwitchEnabled(bool)), core,
+ SIGNAL(setAutomatedPageSwitchEnabled(bool)));
+
+ m_performInstallationForm->setDetailsWidgetVisible(true);
+}
+
+PerformInstallationPage::~PerformInstallationPage()
+{
+ delete m_performInstallationForm;
+}
+
+bool PerformInstallationPage::isAutoSwitching() const
+{
+ return !m_performInstallationForm->isShowingDetails();
+}
+
+// -- protected
+
+void PerformInstallationPage::entering()
+{
+ setComplete(false);
+ setCommitPage(true);
+
+ const QString productName = packageManagerCore()->value(QLatin1String("ProductName"));
+ if (packageManagerCore()->isUninstaller()) {
+ setButtonText(QWizard::CommitButton, tr("&Uninstall"));
+ setTitle(titleForPage(objectName(), tr("Uninstalling %1")).arg(productName));
+
+ QTimer::singleShot(30, packageManagerCore(), SLOT(runUninstaller()));
+ } else if (packageManagerCore()->isPackageManager() || packageManagerCore()->isUpdater()) {
+ setButtonText(QWizard::CommitButton, tr("&Update"));
+ setTitle(titleForPage(objectName(), tr("Updating components of %1")).arg(productName));
+
+ QTimer::singleShot(30, packageManagerCore(), SLOT(runPackageUpdater()));
+ } else {
+ setButtonText(QWizard::CommitButton, tr("&Install"));
+ setTitle(titleForPage(objectName(), tr("Installing %1")).arg(productName));
+
+ QTimer::singleShot(30, packageManagerCore(), SLOT(runInstaller()));
+ }
+
+ m_performInstallationForm->enableDetails();
+ emit setAutomatedPageSwitchEnabled(true);
+}
+
+void PerformInstallationPage::leaving()
+{
+ setButtonText(QWizard::CommitButton, gui()->defaultButtonText(QWizard::CommitButton));
+}
+
+// -- public slots
+
+void PerformInstallationPage::setTitleMessage(const QString &title)
+{
+ setTitle(title);
+}
+
+// -- private slots
+
+void PerformInstallationPage::installationStarted()
+{
+ m_performInstallationForm->startUpdateProgress();
+}
+
+void PerformInstallationPage::installationFinished()
+{
+ m_performInstallationForm->stopUpdateProgress();
+ if (!isAutoSwitching()) {
+ m_performInstallationForm->scrollDetailsToTheEnd();
+ m_performInstallationForm->setDetailsButtonEnabled(false);
+
+ setComplete(true);
+ setButtonText(QWizard::CommitButton, gui()->defaultButtonText(QWizard::NextButton));
+ }
+}
+
+void PerformInstallationPage::toggleDetailsWereChanged()
+{
+ emit setAutomatedPageSwitchEnabled(isAutoSwitching());
+}
+
+
+// -- FinishedPage
+
+FinishedPage::FinishedPage(PackageManagerCore *core)
+ : PackageManagerPage(core)
+ , m_commitButton(0)
+{
+ setObjectName(QLatin1String("FinishedPage"));
+ setPixmap(QWizard::WatermarkPixmap, watermarkPixmap());
+ setSubTitle(subTitleForPage(QLatin1String("FinishedPage")));
+ setTitle(titleForPage(QLatin1String("FinishedPage"), tr("Completing the %1 Wizard")).arg(productName()));
+
+ m_msgLabel = new QLabel(this);
+ m_msgLabel->setWordWrap(true);
+ m_msgLabel->setObjectName(QLatin1String("MessageLabel"));
+
+ const QVariantHash hash = elementsForPage(QLatin1String("FinishedPage"));
+#ifdef Q_WS_MAC
+ m_msgLabel->setText(hash.value(QLatin1String("MessageLabel"), tr("Click Done to exit the %1 "
+ "Wizard.")).toString().arg(productName()));
+#else
+ m_msgLabel->setText(hash.value(QLatin1String("MessageLabel"), tr("Click Finish to exit the "
+ "%1 Wizard.")).toString().arg(productName()));
+#endif
+
+ m_runItCheckBox = new QCheckBox(this);
+ m_runItCheckBox->setObjectName(QLatin1String("RunItCheckBox"));
+ m_runItCheckBox->setChecked(true);
+
+ QVBoxLayout *layout = new QVBoxLayout(this);
+ layout->addWidget(m_msgLabel);
+ layout->addWidget(m_runItCheckBox);
+ setLayout(layout);
+}
+
+void FinishedPage::entering()
+{
+ if (m_commitButton) {
+ disconnect(m_commitButton, SIGNAL(clicked()), this, SLOT(handleFinishClicked()));
+ m_commitButton = 0;
+ }
+
+ setCommitPage(true);
+ if (packageManagerCore()->isUpdater() || packageManagerCore()->isPackageManager()) {
+#ifdef Q_WS_MAC
+ gui()->setOption(QWizard::NoCancelButton, false);
+#endif
+ if (QAbstractButton *cancel = gui()->button(QWizard::CancelButton)) {
+ m_commitButton = cancel;
+ cancel->setEnabled(true);
+ cancel->setVisible(true);
+ }
+ setButtonText(QWizard::CommitButton, tr("Restart"));
+ setButtonText(QWizard::CancelButton, gui()->defaultButtonText(QWizard::FinishButton));
+ } else {
+ if (packageManagerCore()->isInstaller())
+ m_commitButton = wizard()->button(QWizard::FinishButton);
+
+ gui()->setOption(QWizard::NoCancelButton, true);
+ if (QAbstractButton *cancel = gui()->button(QWizard::CancelButton))
+ cancel->setVisible(false);
+ }
+
+ gui()->updateButtonLayout();
+
+ if (m_commitButton) {
+ disconnect(m_commitButton, SIGNAL(clicked()), this, SLOT(handleFinishClicked()));
+ connect(m_commitButton, SIGNAL(clicked()), this, SLOT(handleFinishClicked()));
+ }
+
+ if (packageManagerCore()->status() == PackageManagerCore::Success) {
+ const QString finishedText = packageManagerCore()->value(QLatin1String("FinishedText"));
+ if (!finishedText.isEmpty())
+ m_msgLabel->setText(finishedText);
+
+ if (!packageManagerCore()->value(scRunProgram).isEmpty()) {
+ m_runItCheckBox->show();
+ m_runItCheckBox->setText(packageManagerCore()->value(scRunProgramDescription, tr("Run %1 now."))
+ .arg(productName()));
+ return; // job done
+ }
+ } else {
+ // TODO: how to handle this using the config.xml
+ setTitle(tr("The %1 Wizard failed.").arg(productName()));
+ }
+
+ m_runItCheckBox->hide();
+ m_runItCheckBox->setChecked(false);
+}
+
+void FinishedPage::leaving()
+{
+#ifdef Q_WS_MAC
+ gui()->setOption(QWizard::NoCancelButton, true);
+#endif
+
+ if (QAbstractButton *cancel = gui()->button(QWizard::CancelButton))
+ cancel->setVisible(false);
+ gui()->updateButtonLayout();
+
+ setButtonText(QWizard::CommitButton, gui()->defaultButtonText(QWizard::CommitButton));
+ setButtonText(QWizard::CancelButton, gui()->defaultButtonText(QWizard::CancelButton));
+}
+
+void FinishedPage::handleFinishClicked()
+{
+ const QString program = packageManagerCore()->replaceVariables(packageManagerCore()->value(scRunProgram));
+ if (!m_runItCheckBox->isChecked() || program.isEmpty())
+ return;
+
+ qDebug() << "starting" << program;
+ QProcess::startDetached(program);
+}
+
+
+// -- RestartPage
+
+RestartPage::RestartPage(PackageManagerCore *core)
+ : PackageManagerPage(core)
+{
+ setObjectName(QLatin1String("RestartPage"));
+ setPixmap(QWizard::WatermarkPixmap, watermarkPixmap());
+ setSubTitle(subTitleForPage(QLatin1String("RestartPage")));
+ setTitle(titleForPage(QLatin1String("RestartPage"), tr("Completing the %1 Setup Wizard"))
+ .arg(productName()));
+
+ setFinalPage(false);
+ setCommitPage(false);
+}
+
+int RestartPage::nextId() const
+{
+ return PackageManagerCore::Introduction;
+}
+
+void RestartPage::entering()
+{
+ if (!packageManagerCore()->needsRestart()) {
+ if (QAbstractButton *finish = wizard()->button(QWizard::FinishButton))
+ finish->setVisible(false);
+ QMetaObject::invokeMethod(this, "restart", Qt::QueuedConnection);
+ } else {
+ gui()->accept();
+ }
+}
+
+void RestartPage::leaving()
+{
+}
+
+#include "packagemanagergui.moc"
+#include "moc_packagemanagergui.cpp"
diff --git a/src/libs/installer/packagemanagergui.h b/src/libs/installer/packagemanagergui.h
new file mode 100644
index 000000000..b3f1bdf88
--- /dev/null
+++ b/src/libs/installer/packagemanagergui.h
@@ -0,0 +1,418 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef PACKAGEMANAGERGUI_H
+#define PACKAGEMANAGERGUI_H
+
+#include "packagemanagercore.h"
+
+#include <QtCore/QEvent>
+#include <QtCore/QMetaType>
+
+#include <QtGui/QWizard>
+#include <QtGui/QWizardPage>
+
+// FIXME: move to private classes
+QT_BEGIN_NAMESPACE
+class QAbstractButton;
+class QCheckBox;
+class QLabel;
+class QLineEdit;
+class QListWidget;
+class QListWidgetItem;
+class QRadioButton;
+class QTextBrowser;
+class QScriptEngine;
+QT_END_NAMESPACE
+
+namespace QInstaller {
+
+class PackageManagerCore;
+class PackageManagerPage;
+class PerformInstallationForm;
+
+
+// -- PackageManagerGui
+
+class INSTALLER_EXPORT PackageManagerGui : public QWizard
+{
+ Q_OBJECT
+
+public:
+ explicit PackageManagerGui(PackageManagerCore *core, QWidget *parent = 0);
+ virtual ~PackageManagerGui();
+ virtual void init() = 0;
+
+ void loadControlScript(const QString& scriptPath);
+ void callControlScriptMethod(const QString& methodName);
+
+ QScriptEngine *controlScriptEngine() const;
+
+ Q_INVOKABLE PackageManagerPage* page(int pageId) const;
+ Q_INVOKABLE QWidget* pageWidgetByObjectName(const QString& name) const;
+ Q_INVOKABLE QWidget* currentPageWidget() const;
+ Q_INVOKABLE QString defaultButtonText(int wizardButton) const;
+ Q_INVOKABLE void clickButton(int wizardButton, int delayInMs = 0);
+
+ Q_INVOKABLE void showSettingsButton(bool show);
+ Q_INVOKABLE void setSettingsButtonEnabled(bool enable);
+
+ void updateButtonLayout();
+
+Q_SIGNALS:
+ void interrupted();
+ void languageChanged();
+ void finishButtonClicked();
+ void gotRestarted();
+ void settingsButtonClicked();
+
+public Q_SLOTS:
+ void cancelButtonClicked();
+ void reject();
+ void rejectWithoutPrompt();
+ void showFinishedPage();
+ void setModified(bool value);
+
+protected Q_SLOTS:
+ void wizardPageInsertionRequested(QWidget *widget, QInstaller::PackageManagerCore::WizardPage page);
+ void wizardPageRemovalRequested(QWidget *widget);
+ void wizardWidgetInsertionRequested(QWidget *widget, QInstaller::PackageManagerCore::WizardPage page);
+ void wizardWidgetRemovalRequested(QWidget *widget);
+ void wizardPageVisibilityChangeRequested(bool visible, int page);
+ void slotCurrentPageChanged(int id);
+ void delayedControlScriptExecution(int id);
+
+ void setAutomatedPageSwitchEnabled(bool request);
+
+private Q_SLOTS:
+ void onLanguageChanged();
+ void customButtonClicked(int which);
+
+protected:
+ bool event(QEvent *event);
+ void showEvent(QShowEvent *event);
+ PackageManagerCore *packageManagerCore() const { return m_core; }
+
+private:
+ class Private;
+ Private *const d;
+ PackageManagerCore *m_core;
+};
+
+
+// -- PackageManagerPage
+
+class INSTALLER_EXPORT PackageManagerPage : public QWizardPage
+{
+ Q_OBJECT
+
+public:
+ explicit PackageManagerPage(PackageManagerCore *core);
+ virtual ~PackageManagerPage() {}
+
+ virtual QPixmap logoPixmap() const;
+ virtual QString productName() const;
+ virtual QPixmap watermarkPixmap() const;
+
+ virtual bool isComplete() const;
+ void setComplete(bool complete);
+
+ virtual bool isInterruptible() const { return false; }
+ PackageManagerGui* gui() const { return qobject_cast<PackageManagerGui*>(wizard()); }
+
+protected:
+ PackageManagerCore *packageManagerCore() const;
+ QVariantHash elementsForPage(const QString &pageName) const;
+
+ QString titleForPage(const QString &pageName, const QString &value = QString()) const;
+ QString subTitleForPage(const QString &pageName, const QString &value = QString()) const;
+
+ // Inserts widget into the same layout like a sibling identified
+ // by its name. Default position is just behind the sibling.
+ virtual void insertWidget(QWidget *widget, const QString &siblingName, int offset = 1);
+ virtual QWidget *findWidget(const QString &objectName) const;
+
+ virtual void setVisible(bool visible); // reimp
+ virtual int nextId() const; // reimp
+
+ virtual void entering() {} // called on entering
+ virtual void leaving() {} // called on leaving
+
+ bool isConstructing() const { return m_fresh; }
+
+private:
+ QString titleFromHash(const QVariantHash &hash, const QString &value = QString()) const;
+
+private:
+ bool m_fresh;
+ bool m_complete;
+
+ PackageManagerCore *m_core;
+};
+
+
+// -- IntroductionPage
+
+class INSTALLER_EXPORT IntroductionPage : public PackageManagerPage
+{
+ Q_OBJECT
+
+public:
+ explicit IntroductionPage(PackageManagerCore *core);
+
+ void setWidget(QWidget *widget);
+ void setText(const QString &text);
+
+private:
+ QLabel *m_msgLabel;
+ QWidget *m_widget;
+};
+
+
+// -- LicenseAgreementPage
+
+class INSTALLER_EXPORT LicenseAgreementPage : public PackageManagerPage
+{
+ Q_OBJECT
+ class ClickForwarder;
+
+public:
+ explicit LicenseAgreementPage(PackageManagerCore *core);
+
+ void entering();
+ bool isComplete() const;
+
+private Q_SLOTS:
+ void openLicenseUrl(const QUrl &url);
+ void currentItemChanged(QListWidgetItem *current);
+
+private:
+ void addLicenseItem(const QHash<QString, QPair<QString, QString> > &hash);
+
+private:
+ QTextBrowser *m_textBrowser;
+ QListWidget *m_licenseListWidget;
+
+ QRadioButton *m_acceptRadioButton;
+ QRadioButton *m_rejectRadioButton;
+};
+
+
+// -- ComponentSelectionPage
+
+class INSTALLER_EXPORT ComponentSelectionPage : public PackageManagerPage
+{
+ Q_OBJECT
+
+public:
+ explicit ComponentSelectionPage(PackageManagerCore *core);
+ ~ComponentSelectionPage();
+
+ bool isComplete() const;
+
+ Q_INVOKABLE void selectAll();
+ Q_INVOKABLE void deselectAll();
+ Q_INVOKABLE void selectDefault();
+ Q_INVOKABLE void selectComponent(const QString &id);
+ Q_INVOKABLE void deselectComponent(const QString &id);
+
+protected:
+ void entering();
+ void showEvent(QShowEvent *event);
+
+private Q_SLOTS:
+ void setModified(bool modified);
+
+private:
+ class Private;
+ Private *d;
+};
+
+
+// -- TargetDirectoryPage
+
+class INSTALLER_EXPORT TargetDirectoryPage : public PackageManagerPage
+{
+ Q_OBJECT
+
+public:
+ explicit TargetDirectoryPage(PackageManagerCore *core);
+ QString targetDir() const;
+ void setTargetDir(const QString &dirName);
+
+ void initializePage();
+ bool validatePage();
+
+protected:
+ void entering();
+ void leaving();
+
+private Q_SLOTS:
+ void targetDirSelected();
+ void dirRequested();
+
+private:
+ QLineEdit *m_lineEdit;
+};
+
+
+// -- StartMenuDirectoryPage
+
+class INSTALLER_EXPORT StartMenuDirectoryPage : public PackageManagerPage
+{
+ Q_OBJECT
+
+public:
+ explicit StartMenuDirectoryPage(PackageManagerCore *core);
+
+ QString startMenuDir() const;
+ void setStartMenuDir(const QString &startMenuDir);
+
+protected:
+ void leaving();
+
+private Q_SLOTS:
+ void currentItemChanged(QListWidgetItem* current);
+
+private:
+ QString startMenuPath;
+ QLineEdit *m_lineEdit;
+ QListWidget *m_listWidget;
+};
+
+
+// -- ReadyForInstallationPage
+
+class INSTALLER_EXPORT ReadyForInstallationPage : public PackageManagerPage
+{
+ Q_OBJECT
+
+public:
+ explicit ReadyForInstallationPage(PackageManagerCore *core);
+
+ bool isComplete() const;
+
+private slots:
+ void toggleDetails();
+
+protected:
+ void entering();
+ void leaving();
+
+private:
+ void refreshTaskDetailsBrowser();
+
+private:
+ QLabel *m_msgLabel;
+ QPushButton *m_taskDetailsButton;
+ QTextBrowser* m_taskDetailsBrowser;
+};
+
+
+// -- PerformInstallationPage
+
+class INSTALLER_EXPORT PerformInstallationPage : public PackageManagerPage
+{
+ Q_OBJECT
+
+public:
+ explicit PerformInstallationPage(PackageManagerCore *core);
+ ~PerformInstallationPage();
+ bool isAutoSwitching() const;
+
+protected:
+ void entering();
+ void leaving();
+ bool isInterruptible() const { return true; }
+
+public Q_SLOTS:
+ void setTitleMessage(const QString& title);
+
+Q_SIGNALS:
+ void installationRequested();
+ void setAutomatedPageSwitchEnabled(bool request);
+
+private Q_SLOTS:
+ void installationStarted();
+ void installationFinished();
+ void toggleDetailsWereChanged();
+
+private:
+ PerformInstallationForm *m_performInstallationForm;
+};
+
+
+// -- FinishedPage
+
+class INSTALLER_EXPORT FinishedPage : public PackageManagerPage
+{
+ Q_OBJECT
+
+public:
+ explicit FinishedPage(PackageManagerCore *core);
+
+public Q_SLOTS:
+ void handleFinishClicked();
+
+protected:
+ void entering();
+ void leaving();
+
+private:
+ QLabel *m_msgLabel;
+ QCheckBox *m_runItCheckBox;
+ QAbstractButton *m_commitButton;
+};
+
+
+// -- RestartPage
+
+class INSTALLER_EXPORT RestartPage : public PackageManagerPage
+{
+ Q_OBJECT
+
+public:
+ explicit RestartPage(PackageManagerCore *core);
+
+ virtual int nextId() const;
+
+protected:
+ void entering();
+ void leaving();
+
+Q_SIGNALS:
+ void restart();
+};
+
+} //namespace QInstaller
+
+#endif // PACKAGEMANAGERGUI_H
diff --git a/src/libs/installer/packagemanagerproxyfactory.cpp b/src/libs/installer/packagemanagerproxyfactory.cpp
new file mode 100644
index 000000000..bd1d2c268
--- /dev/null
+++ b/src/libs/installer/packagemanagerproxyfactory.cpp
@@ -0,0 +1,79 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "packagemanagerproxyfactory.h"
+
+#include "packagemanagercore.h"
+#include "settings.h"
+
+namespace QInstaller {
+
+PackageManagerProxyFactory::PackageManagerProxyFactory(const PackageManagerCore *const core)
+ : m_core(core)
+{
+}
+
+PackageManagerProxyFactory *PackageManagerProxyFactory::clone() const
+{
+ return new PackageManagerProxyFactory(m_core);
+}
+
+QList<QNetworkProxy> PackageManagerProxyFactory::queryProxy(const QNetworkProxyQuery &query)
+{
+ const Settings &settings = m_core->settings();
+ QList<QNetworkProxy> list;
+
+ if (settings.proxyType() == Settings::SystemProxy) {
+#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
+ QUrl proxyUrl = QUrl::fromUserInput(QString::fromUtf8(qgetenv("http_proxy")));
+ if (proxyUrl.isValid()) {
+ return list << QNetworkProxy(QNetworkProxy::HttpProxy, proxyUrl.host(), proxyUrl.port(),
+ proxyUrl.userName(), proxyUrl.password());
+ }
+#endif
+ return QNetworkProxyFactory::systemProxyForQuery(query);
+ }
+
+ if ((settings.proxyType() == Settings::NoProxy))
+ return list << QNetworkProxy(QNetworkProxy::NoProxy);
+
+ if (query.queryType() == QNetworkProxyQuery::UrlRequest) {
+ if (query.url().scheme() == QLatin1String("ftp"))
+ return list << settings.ftpProxy();
+
+ if (query.url().scheme() == QLatin1String("http"))
+ return list << settings.httpProxy();
+ }
+ return list << QNetworkProxy(QNetworkProxy::DefaultProxy);
+}
+
+} // QInstaller
diff --git a/src/libs/installer/packagemanagerproxyfactory.h b/src/libs/installer/packagemanagerproxyfactory.h
new file mode 100644
index 000000000..18a72e2c8
--- /dev/null
+++ b/src/libs/installer/packagemanagerproxyfactory.h
@@ -0,0 +1,56 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef PACKAGEMANAGERPROXYFACTORY_H
+#define PACKAGEMANAGERPROXYFACTORY_H
+
+#include "kdupdaterfiledownloaderfactory.h"
+
+namespace QInstaller {
+
+class PackageManagerCore;
+
+class PackageManagerProxyFactory : public KDUpdater::FileDownloaderProxyFactory
+{
+public:
+ PackageManagerProxyFactory(const PackageManagerCore *const core);
+
+ PackageManagerProxyFactory *clone() const;
+ QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query = QNetworkProxyQuery());
+
+private:
+ const PackageManagerCore *const m_core;
+};
+
+} // QInstaller
+
+#endif // PACKAGEMANAGERPROXYFACTORY_H
diff --git a/src/libs/installer/performinstallationform.cpp b/src/libs/installer/performinstallationform.cpp
new file mode 100644
index 000000000..37cf993fe
--- /dev/null
+++ b/src/libs/installer/performinstallationform.cpp
@@ -0,0 +1,189 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "performinstallationform.h"
+
+#include "lazyplaintextedit.h"
+#include "progresscoordinator.h"
+
+
+#include <QtGui/QLabel>
+#include <QtGui/QProgressBar>
+#include <QtGui/QPushButton>
+#include <QtGui/QScrollBar>
+#include <QtGui/QVBoxLayout>
+
+#include <QtCore/QTimer>
+
+using namespace QInstaller;
+
+// -- PerformInstallationForm
+
+PerformInstallationForm::PerformInstallationForm(QObject *parent)
+ : QObject(parent),
+ m_progressBar(0),
+ m_progressLabel(0),
+ m_detailsButton(0),
+ m_detailsBrowser(0),
+ m_updateTimer(0)
+{
+}
+
+void PerformInstallationForm::setupUi(QWidget *widget)
+{
+ QVBoxLayout *baseLayout = new QVBoxLayout(widget);
+ baseLayout->setObjectName(QLatin1String("BaseLayout"));
+
+ QVBoxLayout *topLayout = new QVBoxLayout();
+ topLayout->setObjectName(QLatin1String("TopLayout"));
+
+ m_progressBar = new QProgressBar(widget);
+ m_progressBar->setRange(1, 100);
+ m_progressBar->setObjectName(QLatin1String("ProgressBar"));
+ topLayout->addWidget(m_progressBar);
+
+ m_progressLabel = new QLabel(widget);
+ m_progressLabel->setObjectName(QLatin1String("ProgressLabel"));
+ m_progressLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
+ topLayout->addWidget(m_progressLabel);
+
+ m_downloadStatus = new QLabel(widget);
+ m_downloadStatus->setObjectName(QLatin1String("DownloadStatus"));
+ m_downloadStatus->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
+ topLayout->addWidget(m_downloadStatus);
+ connect(ProgressCoordinator::instance(), SIGNAL(downloadStatusChanged(QString)), this,
+ SLOT(onDownloadStatusChanged(QString)));
+
+ m_detailsButton = new QPushButton(tr("&Show Details"), widget);
+ m_detailsButton->setObjectName(QLatin1String("DetailsButton"));
+ m_detailsButton->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
+ connect(m_detailsButton, SIGNAL(clicked()), this, SLOT(toggleDetails()));
+ topLayout->addWidget(m_detailsButton);
+
+ QVBoxLayout *bottomLayout = new QVBoxLayout();
+ bottomLayout->setObjectName(QLatin1String("BottomLayout"));
+ bottomLayout->addStretch();
+
+ m_detailsBrowser = new LazyPlainTextEdit(widget);
+ m_detailsBrowser->setReadOnly(true);
+ m_detailsBrowser->setWordWrapMode(QTextOption::NoWrap);
+ m_detailsBrowser->setObjectName(QLatin1String("DetailsBrowser"));
+ m_detailsBrowser->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ bottomLayout->addWidget(m_detailsBrowser);
+
+ bottomLayout->setStretch(1, 10);
+ baseLayout->addLayout(topLayout);
+ baseLayout->addLayout(bottomLayout);
+
+ m_updateTimer = new QTimer(widget);
+ connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(updateProgress())); //updateProgress includes label
+ m_updateTimer->setInterval(30);
+}
+
+void PerformInstallationForm::setDetailsWidgetVisible(bool visible)
+{
+ m_detailsButton->setVisible(visible);
+}
+
+void PerformInstallationForm::appendProgressDetails(const QString &details)
+{
+ m_detailsBrowser->append(details);
+}
+
+void PerformInstallationForm::updateProgress()
+{
+ QInstaller::ProgressCoordinator *progressCoordninator = QInstaller::ProgressCoordinator::instance();
+ const int progressPercentage = progressCoordninator->progressInPercentage();
+
+ m_progressBar->setRange(0, 100);
+ m_progressBar->setValue(progressPercentage);
+ m_progressLabel->setText(m_progressLabel->fontMetrics().elidedText(progressCoordninator->labelText(),
+ Qt::ElideRight, m_progressLabel->width()));
+}
+
+void PerformInstallationForm::toggleDetails()
+{
+ const bool willShow = !isShowingDetails();
+ m_detailsButton->setText(willShow ? tr("&Hide Details") : tr("&Show Details"));
+
+ if (willShow)
+ scrollDetailsToTheEnd();
+
+ m_detailsBrowser->setVisible(willShow);
+ emit showDetailsChanged();
+}
+
+void PerformInstallationForm::clearDetailsBrowser()
+{
+ m_detailsBrowser->clear();
+}
+
+void PerformInstallationForm::enableDetails()
+{
+ m_detailsButton->setEnabled(true);
+ m_detailsButton->setText(tr("&Show Details"));
+ m_detailsBrowser->setVisible(false);
+}
+
+void PerformInstallationForm::startUpdateProgress()
+{
+ m_updateTimer->start();
+ updateProgress();
+}
+
+void PerformInstallationForm::stopUpdateProgress()
+{
+ m_updateTimer->stop();
+ updateProgress();
+}
+
+void PerformInstallationForm::setDetailsButtonEnabled(bool enable)
+{
+ m_detailsButton->setEnabled(enable);
+}
+
+void PerformInstallationForm::scrollDetailsToTheEnd()
+{
+ m_detailsBrowser->horizontalScrollBar()->setValue(0);
+ m_detailsBrowser->verticalScrollBar()->setValue(m_detailsBrowser->verticalScrollBar()->maximum());
+}
+
+bool PerformInstallationForm::isShowingDetails() const
+{
+ return m_detailsBrowser->isVisible();
+}
+
+void PerformInstallationForm::onDownloadStatusChanged(const QString &status)
+{
+ m_downloadStatus->setText(m_downloadStatus->fontMetrics().elidedText(status, Qt::ElideRight,
+ m_downloadStatus->width()));
+}
diff --git a/src/libs/installer/performinstallationform.h b/src/libs/installer/performinstallationform.h
new file mode 100644
index 000000000..f13869b13
--- /dev/null
+++ b/src/libs/installer/performinstallationform.h
@@ -0,0 +1,87 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef PERFORMINSTALLATIONFORM_H
+#define PERFORMINSTALLATIONFORM_H
+
+#include <QObject>
+
+QT_BEGIN_NAMESPACE
+class QLabel;
+class QProgressBar;
+class QPushButton;
+class QTimer;
+class QWidget;
+QT_END_NAMESPACE
+
+class LazyPlainTextEdit;
+
+namespace QInstaller {
+
+class PerformInstallationForm : public QObject
+{
+ Q_OBJECT
+
+public:
+ PerformInstallationForm(QObject *parent);
+
+ void setupUi(QWidget *widget);
+ void setDetailsWidgetVisible(bool visible);
+ void enableDetails();
+ void startUpdateProgress();
+ void stopUpdateProgress();
+ void setDetailsButtonEnabled(bool enable);
+ void scrollDetailsToTheEnd();
+ bool isShowingDetails() const;
+
+signals:
+ void showDetailsChanged();
+
+public slots:
+ void appendProgressDetails(const QString &details);
+ void updateProgress();
+ void toggleDetails();
+ void clearDetailsBrowser();
+ void onDownloadStatusChanged(const QString &status);
+
+private:
+ QProgressBar *m_progressBar;
+ QLabel *m_progressLabel;
+ QLabel *m_downloadStatus;
+ QPushButton *m_detailsButton;
+ LazyPlainTextEdit *m_detailsBrowser;
+ QTimer *m_updateTimer;
+};
+
+} // namespace QInstaller
+
+#endif // PERFORMINSTALLATIONFORM_H
diff --git a/src/libs/installer/persistentsettings.cpp b/src/libs/installer/persistentsettings.cpp
new file mode 100644
index 000000000..8bbfdb62b
--- /dev/null
+++ b/src/libs/installer/persistentsettings.cpp
@@ -0,0 +1,212 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "persistentsettings.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QFile>
+#include <QtCore/QVariant>
+#include <QtXml/QDomDocument>
+#include <QtXml/QDomCDATASection>
+#include <QtXml/QDomElement>
+
+
+using namespace ProjectExplorer;
+
+PersistentSettingsReader::PersistentSettingsReader()
+{
+}
+
+QVariant PersistentSettingsReader::restoreValue(const QString &variable) const
+{
+ if (m_valueMap.contains(variable))
+ return m_valueMap.value(variable);
+ return QVariant();
+}
+
+QVariantMap PersistentSettingsReader::restoreValues() const
+{
+ return m_valueMap;
+}
+
+bool PersistentSettingsReader::load(const QString &fileName)
+{
+ m_valueMap.clear();
+
+ QFile file(fileName);
+ if (!file.open(QIODevice::ReadOnly))
+ return false;
+
+ QDomDocument doc;
+ if (!doc.setContent(&file))
+ return false;
+
+ QDomElement root = doc.documentElement();
+ if (root.nodeName() != QLatin1String("qtcreator"))
+ return false;
+
+ QDomElement child = root.firstChildElement();
+ for (; !child.isNull(); child = child.nextSiblingElement()) {
+ if (child.nodeName() == QLatin1String("data"))
+ readValues(child);
+ }
+
+ file.close();
+ return true;
+}
+
+QVariant PersistentSettingsReader::readValue(const QDomElement &valElement) const
+{
+ QString name = valElement.nodeName();
+ QString type = valElement.attribute(QLatin1String("type"));
+ QVariant v;
+
+ if (name == QLatin1String("value")) {
+ if(type == QLatin1String("QChar")) {
+ //Workaround: QTBUG-12345
+ v.setValue(QChar(valElement.text().at(0)));
+ } else {
+ v.setValue(valElement.text());
+ v.convert(QVariant::nameToType(type.toLatin1().data()));
+ }
+ } else if (name == QLatin1String("valuelist")) {
+ QDomElement child = valElement.firstChildElement();
+ QList<QVariant> valList;
+ for (; !child.isNull(); child = child.nextSiblingElement()) {
+ valList << readValue(child);
+ }
+ v.setValue(valList);
+ } else if (name == QLatin1String("valuemap")) {
+ QDomElement child = valElement.firstChildElement();
+ QMap<QString, QVariant> valMap;
+ for (; !child.isNull(); child = child.nextSiblingElement()) {
+ QString key = child.attribute(QLatin1String("key"));
+ valMap.insert(key, readValue(child));
+ }
+ v.setValue(valMap);
+ }
+
+ return v;
+}
+
+void PersistentSettingsReader::readValues(const QDomElement &data)
+{
+ QString variable;
+ QVariant v;
+
+ QDomElement child = data.firstChildElement();
+ for (; !child.isNull(); child = child.nextSiblingElement()) {
+ if (child.nodeName() == QLatin1String("variable"))
+ variable = child.text();
+ else
+ v = readValue(child);
+ }
+
+ m_valueMap.insert(variable, v);
+}
+
+///
+/// PersistentSettingsWriter
+///
+
+PersistentSettingsWriter::PersistentSettingsWriter()
+{
+}
+
+void PersistentSettingsWriter::writeValue(QDomElement &ps, const QVariant &variant)
+{
+ if (variant.type() == QVariant::StringList || variant.type() == QVariant::List) {
+ QDomElement values = ps.ownerDocument().createElement(QLatin1String("valuelist"));
+ values.setAttribute(QLatin1String("type"), QLatin1String(QVariant::typeToName(QVariant::List)));
+ QList<QVariant> varList = variant.toList();
+ foreach (const QVariant &var, varList) {
+ writeValue(values, var);
+ }
+ ps.appendChild(values);
+ } else if (variant.type() == QVariant::Map) {
+ QDomElement values = ps.ownerDocument().createElement(QLatin1String("valuemap"));
+ values.setAttribute(QLatin1String("type"), QLatin1String(QVariant::typeToName(QVariant::Map)));
+
+ QMap<QString, QVariant> varMap = variant.toMap();
+ QMap<QString, QVariant>::const_iterator i = varMap.constBegin();
+ while (i != varMap.constEnd()) {
+ writeValue(values, i.value());
+ values.lastChild().toElement().
+ setAttribute(QLatin1String("key"), i.key());
+ ++i;
+ }
+
+ ps.appendChild(values);
+ } else {
+ QDomElement value = ps.ownerDocument().createElement(QLatin1String("value"));
+ ps.appendChild(value);
+ QDomText valueText = ps.ownerDocument().createTextNode(variant.toString());
+ value.appendChild(valueText);
+ value.setAttribute(QLatin1String("type"), QLatin1String(variant.typeName()));
+ ps.appendChild(value);
+ }
+}
+
+void PersistentSettingsWriter::saveValue(const QString &variable, const QVariant &value)
+{
+ m_valueMap[variable] = value;
+}
+
+bool PersistentSettingsWriter::save(const QString &fileName, const QString &docType)
+{
+ QFile file(fileName);
+ if (!file.open(QIODevice::WriteOnly))
+ return false;
+
+ QDomDocument doc(docType);
+
+ QDomElement root = doc.createElement(QLatin1String("qtcreator"));
+ doc.appendChild(root);
+
+ QMap<QString, QVariant>::const_iterator i = m_valueMap.constBegin();
+ while (i != m_valueMap.constEnd()) {
+ QDomElement ps = doc.createElement(QLatin1String("data"));
+ root.appendChild(ps);
+
+ QDomElement variable = doc.createElement(QLatin1String("variable"));
+ ps.appendChild(variable);
+ QDomText variableText = doc.createTextNode(i.key());
+ variable.appendChild(variableText);
+
+ writeValue(ps, i.value());
+ ++i;
+ }
+
+ file.write(doc.toByteArray());
+ file.close();
+ return true;
+}
diff --git a/src/libs/installer/persistentsettings.h b/src/libs/installer/persistentsettings.h
new file mode 100644
index 000000000..4e3171346
--- /dev/null
+++ b/src/libs/installer/persistentsettings.h
@@ -0,0 +1,75 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef PERSISTENTSETTINGS_H
+#define PERSISTENTSETTINGS_H
+
+#include "projectexplorer_export.h"
+
+#include <QtCore/QMap>
+#include <QtCore/QVariant>
+
+QT_BEGIN_NAMESPACE
+class QDomElement;
+QT_END_NAMESPACE
+
+namespace ProjectExplorer {
+
+class PROJECTEXPLORER_EXPORT PersistentSettingsReader
+{
+public:
+ PersistentSettingsReader();
+ QVariant restoreValue(const QString &variable) const;
+ QVariantMap restoreValues() const;
+ bool load(const QString & fileName);
+
+private:
+ QVariant readValue(const QDomElement &valElement) const;
+ void readValues(const QDomElement &data);
+ QMap<QString, QVariant> m_valueMap;
+};
+
+class PROJECTEXPLORER_EXPORT PersistentSettingsWriter
+{
+public:
+ PersistentSettingsWriter();
+ void saveValue(const QString &variable, const QVariant &value);
+ bool save(const QString &fileName, const QString &docType);
+
+private:
+ void writeValue(QDomElement &ps, const QVariant &value);
+ QMap<QString, QVariant> m_valueMap;
+};
+
+} // namespace ProjectExplorer
+
+#endif // PERSISTENTSETTINGS_H
diff --git a/src/libs/installer/progresscoordinator.cpp b/src/libs/installer/progresscoordinator.cpp
new file mode 100644
index 000000000..b9bf7738e
--- /dev/null
+++ b/src/libs/installer/progresscoordinator.cpp
@@ -0,0 +1,275 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "progresscoordinator.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+
+using namespace QInstaller;
+
+QT_BEGIN_NAMESPACE
+uint qHash(QPointer<QObject> key)
+{
+ return qHash(key.data());
+}
+QT_END_NAMESPACE
+
+ProgressCoordinator::ProgressCoordinator(QObject *parent)
+ : QObject(parent),
+ m_currentCompletePercentage(0),
+ m_currentBasePercentage(0),
+ m_manualAddedPercentage(0),
+ m_reservedPercentage(0),
+ m_undoMode(false),
+ m_reachedPercentageBeforeUndo(0)
+{
+ // it has to be in the main thread to be able refresh the ui with processEvents
+ Q_ASSERT(thread() == qApp->thread());
+}
+
+ProgressCoordinator::~ProgressCoordinator()
+{
+}
+
+ProgressCoordinator *ProgressCoordinator::instance()
+{
+ static ProgressCoordinator *instance = 0;
+ if (instance == 0)
+ instance = new ProgressCoordinator(qApp);
+ return instance;
+}
+
+void ProgressCoordinator::reset()
+{
+ disconnectAllSenders();
+ m_installationLabelText.clear();
+ m_currentCompletePercentage = 0;
+ m_currentBasePercentage = 0;
+ m_manualAddedPercentage = 0;
+ m_reservedPercentage = 0;
+ m_undoMode = false;
+ m_reachedPercentageBeforeUndo = 0;
+ emit detailTextResetNeeded();
+}
+
+void ProgressCoordinator::registerPartProgress(QObject *sender, const char *signal, double partProgressSize)
+{
+ Q_ASSERT(sender);
+ Q_ASSERT(QString::fromLatin1(signal).contains(QLatin1String("(double)")));
+ Q_ASSERT(partProgressSize <= 1);
+
+ m_senderPartProgressSizeHash.insert(sender, partProgressSize);
+ bool isConnected = connect(sender, signal, this, SLOT(partProgressChanged(double)));
+ Q_UNUSED(isConnected);
+ Q_ASSERT(isConnected);
+}
+
+void ProgressCoordinator::partProgressChanged(double fraction)
+{
+ if (fraction < 0 || fraction > 1) {
+ qWarning() << "The fraction is outside from possible value:" << QString::number(fraction);
+ return;
+ }
+
+ double partProgressSize = m_senderPartProgressSizeHash.value(sender(), 0);
+ if (partProgressSize == 0) {
+ qWarning() << "It seems that this sender was not registered in the right way:" << sender();
+ return;
+ }
+
+ if (m_undoMode) {
+ //qDebug() << "fraction:" << fraction;
+ double maxSize = m_reachedPercentageBeforeUndo * partProgressSize;
+ double pendingCalculatedPartPercentage = maxSize * fraction;
+
+ // allPendingCalculatedPartPercentages has negative values
+ double newCurrentCompletePercentage = m_currentBasePercentage - pendingCalculatedPartPercentage
+ + allPendingCalculatedPartPercentages(sender());
+
+ //we can't check this here, because some round issues can make it little bit under 0 or over 100
+ //Q_ASSERT(newCurrentCompletePercentage >= 0);
+ //Q_ASSERT(newCurrentCompletePercentage <= 100);
+ if (newCurrentCompletePercentage < 0) {
+ qDebug() << newCurrentCompletePercentage << "is smaller then 0 - this should happen max once";
+ newCurrentCompletePercentage = 0;
+ }
+ if (newCurrentCompletePercentage > 100) {
+ qDebug() << newCurrentCompletePercentage << "is bigger then 100 - this should happen max once";
+ newCurrentCompletePercentage = 100;
+ }
+
+ if (qRound(m_currentCompletePercentage) < qRound(newCurrentCompletePercentage)) {
+ qFatal("This should not happen!");
+ }
+
+ m_currentCompletePercentage = newCurrentCompletePercentage;
+ if (fraction == 1) {
+ m_currentBasePercentage = m_currentBasePercentage - pendingCalculatedPartPercentage;
+ m_senderPendingCalculatedPercentageHash.insert(sender(), 0);
+ } else {
+ m_senderPendingCalculatedPercentageHash.insert(sender(), pendingCalculatedPartPercentage);
+ }
+
+ } else { //if (m_undoMode)
+ int availablePercentagePoints = 100 - m_manualAddedPercentage - m_reservedPercentage;
+ double pendingCalculatedPartPercentage = availablePercentagePoints * partProgressSize * fraction;
+ //double checkValue = allPendingCalculatedPartPercentages(sender());
+
+ double newCurrentCompletePercentage = m_manualAddedPercentage + m_currentBasePercentage
+ + pendingCalculatedPartPercentage + allPendingCalculatedPartPercentages(sender());
+
+ //we can't check this here, because some round issues can make it little bit under 0 or over 100
+ //Q_ASSERT(newCurrentCompletePercentage >= 0);
+ //Q_ASSERT(newCurrentCompletePercentage <= 100);
+ if (newCurrentCompletePercentage < 0) {
+ qDebug() << newCurrentCompletePercentage << "is smaller then 0 - this should happen max once";
+ newCurrentCompletePercentage = 0;
+ }
+
+ if (newCurrentCompletePercentage > 100) {
+ qDebug() << newCurrentCompletePercentage << "is bigger then 100 - this should happen max once";
+ newCurrentCompletePercentage = 100;
+ }
+
+ if (qRound(m_currentCompletePercentage) > qRound(newCurrentCompletePercentage)) {
+ qFatal("This should not happen!");
+ }
+ m_currentCompletePercentage = newCurrentCompletePercentage;
+
+ if (fraction == 1 || fraction == 0) {
+ m_currentBasePercentage = m_currentBasePercentage + pendingCalculatedPartPercentage;
+ m_senderPendingCalculatedPercentageHash.insert(sender(), 0);
+ } else {
+ m_senderPendingCalculatedPercentageHash.insert(sender(), pendingCalculatedPartPercentage);
+ }
+ } //if (m_undoMode)
+}
+
+
+/*!
+ Contains the installation progress percentage.
+*/
+int ProgressCoordinator::progressInPercentage() const
+{
+ int currentValue = qRound(m_currentCompletePercentage);
+ Q_ASSERT( currentValue <= 100);
+ Q_ASSERT( currentValue >= 0);
+ return currentValue;
+}
+
+void ProgressCoordinator::disconnectAllSenders()
+{
+ foreach (QPointer<QObject> sender, m_senderPartProgressSizeHash.keys()) {
+ if (!sender.isNull()) {
+ bool isDisconnected = sender->disconnect(this);
+ Q_UNUSED(isDisconnected);
+ Q_ASSERT(isDisconnected);
+ }
+ }
+ m_senderPartProgressSizeHash.clear();
+ m_senderPendingCalculatedPercentageHash.clear();
+}
+
+void ProgressCoordinator::setUndoMode()
+{
+ Q_ASSERT(!m_undoMode);
+ m_undoMode = true;
+
+ disconnectAllSenders();
+ m_reachedPercentageBeforeUndo = progressInPercentage();
+ m_currentBasePercentage = m_reachedPercentageBeforeUndo;
+}
+
+void ProgressCoordinator::addManualPercentagePoints(int value)
+{
+ m_manualAddedPercentage = m_manualAddedPercentage + value;
+ if (m_undoMode) {
+ //we don't do other things in the undomode, maybe later if the last percentage point comes to early
+ return;
+ }
+
+ m_currentCompletePercentage = m_currentCompletePercentage + value;
+ if (m_currentCompletePercentage > 100.0)
+ m_currentCompletePercentage = 100.0;
+
+ qApp->processEvents(); //makes the result available in the ui
+}
+
+void ProgressCoordinator::addReservePercentagePoints(int value)
+{
+ m_reservedPercentage = m_reservedPercentage + value;
+}
+
+void ProgressCoordinator::setLabelText(const QString &text)
+{
+ if (m_installationLabelText == text)
+ return;
+ m_installationLabelText = text;
+}
+
+/*!
+ Contains the installation progress label text.
+*/
+QString ProgressCoordinator::labelText() const
+{
+ return m_installationLabelText;
+}
+
+void ProgressCoordinator::emitDetailTextChanged(const QString &text)
+{
+ emit detailTextChanged(text);
+}
+
+void ProgressCoordinator::emitLabelAndDetailTextChanged(const QString &text)
+{
+ emit detailTextChanged(text);
+ m_installationLabelText = QString(text).remove(QLatin1String("\n"));
+ qApp->processEvents(); //makes the result available in the ui
+}
+
+double ProgressCoordinator::allPendingCalculatedPartPercentages(QObject *excludeKeyObject)
+{
+ double result = 0;
+ QHash<QPointer<QObject>, double>::iterator it = m_senderPendingCalculatedPercentageHash.begin();
+ while (it != m_senderPendingCalculatedPercentageHash.end()) {
+ if (it.key() != excludeKeyObject)
+ result += it.value();
+ it++;
+ }
+ return result;
+}
+
+void ProgressCoordinator::emitDownloadStatus(const QString &status)
+{
+ emit downloadStatusChanged(status);
+}
diff --git a/src/libs/installer/progresscoordinator.h b/src/libs/installer/progresscoordinator.h
new file mode 100644
index 000000000..12f554666
--- /dev/null
+++ b/src/libs/installer/progresscoordinator.h
@@ -0,0 +1,96 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef PROGRESSCOORDNINATOR_H
+#define PROGRESSCOORDNINATOR_H
+
+#include <QtCore/QHash>
+#include <QtCore/QObject>
+#include <QtCore/QPointer>
+
+namespace QInstaller {
+
+class ProgressCoordinator : public QObject
+{
+ Q_OBJECT
+
+public:
+ static ProgressCoordinator *instance();
+ ~ProgressCoordinator();
+
+ void registerPartProgress(QObject *sender, const char *signal, double partProgressSize);
+
+public slots:
+ void reset();
+ void setUndoMode();
+
+ QString labelText() const;
+ void setLabelText(const QString &text);
+
+ int progressInPercentage() const;
+ void partProgressChanged(double fraction);
+
+ void addManualPercentagePoints(int value);
+ void addReservePercentagePoints(int value);
+
+ void emitDetailTextChanged(const QString &text);
+ void emitLabelAndDetailTextChanged(const QString &text);
+
+ void emitDownloadStatus(const QString &status);
+
+signals:
+ void detailTextChanged(const QString &text);
+ void detailTextResetNeeded();
+ void downloadStatusChanged(const QString &status);
+
+protected:
+ explicit ProgressCoordinator(QObject *parent);
+
+private:
+ double allPendingCalculatedPartPercentages(QObject *excludeKeyObject = 0);
+ void disconnectAllSenders();
+
+private:
+ QHash<QPointer<QObject>, double> m_senderPendingCalculatedPercentageHash;
+ QHash<QPointer<QObject>, double> m_senderPartProgressSizeHash;
+ QString m_installationLabelText;
+ double m_currentCompletePercentage;
+ double m_currentBasePercentage;
+ int m_manualAddedPercentage;
+ int m_reservedPercentage;
+ bool m_undoMode;
+ double m_reachedPercentageBeforeUndo;
+};
+
+} //namespace QInstaller
+
+#endif //PROGRESSCOORDNINATOR_H
diff --git a/src/libs/installer/projectexplorer_export.h b/src/libs/installer/projectexplorer_export.h
new file mode 100644
index 000000000..0e8cb470b
--- /dev/null
+++ b/src/libs/installer/projectexplorer_export.h
@@ -0,0 +1 @@
+#define PROJECTEXPLORER_EXPORT
diff --git a/src/libs/installer/qinstallerglobal.h b/src/libs/installer/qinstallerglobal.h
new file mode 100644
index 000000000..8df909e76
--- /dev/null
+++ b/src/libs/installer/qinstallerglobal.h
@@ -0,0 +1,92 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef QINSTALLER_GLOBAL_H
+#define QINSTALLER_GLOBAL_H
+
+#include <installer_global.h>
+
+#include <kdupdaterupdate.h>
+#include <kdupdaterupdateoperation.h>
+#include <kdupdaterpackagesinfo.h>
+
+QT_BEGIN_NAMESPACE
+class QScriptContext;
+class QScriptEngine;
+class QScriptValue;
+QT_END_NAMESPACE
+
+
+namespace QInstaller {
+
+#define IFW_VERSION 0x010000
+#define IFW_VERSION_STRING "1.0.0"
+#define IFW_VERSION_CHECK(major, minor, patch) \
+ ((major << 16)|(minor << 8)|(patch))
+
+enum INSTALLER_EXPORT RunMode
+{
+ AllMode,
+ UpdaterMode
+};
+
+enum INSTALLER_EXPORT JobError
+{
+ InvalidUrl = 0x24B04,
+ Timeout,
+ DownloadError,
+ InvalidUpdatesXml,
+ InvalidMetaInfo,
+ ExtractionError,
+ UserIgnoreError,
+ RepositoryUpdatesReceived
+};
+
+typedef KDUpdater::UpdateOperation Operation;
+typedef QList<QInstaller::Operation*> OperationList;
+
+typedef KDUpdater::Update Package;
+typedef QList<QInstaller::Package*> PackagesList;
+
+typedef KDUpdater::PackageInfo LocalPackage;
+typedef QHash<QString, LocalPackage> LocalPackagesHash;
+
+QString uncaughtExceptionString(QScriptEngine *scriptEngine, const QString &context = QString());
+QScriptValue qInstallerComponentByName(QScriptContext *context, QScriptEngine *engine);
+
+QScriptValue qDesktopServicesOpenUrl(QScriptContext *context, QScriptEngine *engine);
+QScriptValue qDesktopServicesDisplayName(QScriptContext *context, QScriptEngine *engine);
+QScriptValue qDesktopServicesStorageLocation(QScriptContext *context, QScriptEngine *engine);
+
+} // namespace QInstaller
+
+#endif // QINSTALLER_GLOBAL_H
diff --git a/src/libs/installer/qprocesswrapper.cpp b/src/libs/installer/qprocesswrapper.cpp
new file mode 100644
index 000000000..e15168fac
--- /dev/null
+++ b/src/libs/installer/qprocesswrapper.cpp
@@ -0,0 +1,413 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "qprocesswrapper.h"
+
+#include "fsengineclient.h"
+#include "templates.cpp"
+
+#include <QtCore/QThread>
+
+#include <QtNetwork/QTcpSocket>
+
+// -- QProcessWrapper::Private
+
+class QProcessWrapper::Private
+{
+public:
+ Private(QProcessWrapper *qq)
+ : q(qq),
+ ignoreTimer(false),
+ socket(0)
+ {}
+
+ bool createSocket()
+ {
+ if (!FSEngineClientHandler::instance().isActive())
+ return false;
+ if (socket != 0 && socket->state() == static_cast< int >(QAbstractSocket::ConnectedState))
+ return true;
+ if (socket != 0)
+ delete socket;
+ socket = new QTcpSocket;
+
+ if (!FSEngineClientHandler::instance().connect(socket))
+ return false;
+ stream.setDevice(socket);
+ stream.setVersion(QDataStream::Qt_4_2);
+
+ stream << QString::fromLatin1("createQProcess");
+ socket->flush();
+ stream.device()->waitForReadyRead(-1);
+ quint32 test;
+ stream >> test;
+ stream.device()->readAll();
+
+ q->startTimer(250);
+
+ return true;
+ }
+
+ class TimerBlocker
+ {
+ public:
+ explicit TimerBlocker(const QProcessWrapper *wrapper)
+ : w(const_cast<QProcessWrapper *>(wrapper))
+ {
+ w->d->ignoreTimer = true;
+ }
+
+ ~TimerBlocker()
+ {
+ w->d->ignoreTimer = false;
+ }
+
+ private:
+ QProcessWrapper *const w;
+ };
+
+private:
+ QProcessWrapper *const q;
+
+public:
+ bool ignoreTimer;
+
+ QProcess process;
+ mutable QTcpSocket *socket;
+ mutable QDataStream stream;
+};
+
+
+// -- QProcessWrapper
+
+QProcessWrapper::QProcessWrapper(QObject *parent)
+ : QObject(parent),
+ d(new Private(this))
+{
+ connect(&d->process, SIGNAL(bytesWritten(qint64)), SIGNAL(bytesWritten(qint64)));
+ connect(&d->process, SIGNAL(aboutToClose()), SIGNAL(aboutToClose()));
+ connect(&d->process, SIGNAL(readChannelFinished()), SIGNAL(readChannelFinished()));
+ connect(&d->process, SIGNAL(error(QProcess::ProcessError)), SIGNAL(error(QProcess::ProcessError)));
+ connect(&d->process, SIGNAL(readyReadStandardOutput()), SIGNAL(readyReadStandardOutput()));
+ connect(&d->process, SIGNAL(readyReadStandardError()), SIGNAL(readyReadStandardError()));
+ connect(&d->process, SIGNAL(finished(int)), SIGNAL(finished(int)));
+ connect(&d->process, SIGNAL(finished(int,QProcess::ExitStatus)), SIGNAL(finished(int,QProcess::ExitStatus)));
+ connect(&d->process, SIGNAL(readyRead()), SIGNAL(readyRead()));
+ connect(&d->process, SIGNAL(started()), SIGNAL(started()));
+ connect(&d->process, SIGNAL(stateChanged(QProcess::ProcessState)), SIGNAL(stateChanged(QProcess::ProcessState)));
+}
+
+QProcessWrapper::~QProcessWrapper()
+{
+ if (d->socket != 0) {
+ d->stream << QString::fromLatin1("destroyQProcess");
+ d->socket->flush();
+ quint32 result;
+ d->stream >> result;
+
+ if (QThread::currentThread() == d->socket->thread()) {
+ d->socket->close();
+ delete d->socket;
+ } else {
+ d->socket->deleteLater();
+ }
+ }
+ delete d;
+}
+
+void QProcessWrapper::timerEvent(QTimerEvent *event)
+{
+ Q_UNUSED(event)
+
+ if (d->ignoreTimer)
+ return;
+
+ QList<QVariant> receivedSignals;
+ {
+ const Private::TimerBlocker blocker(this);
+
+ d->stream << QString::fromLatin1("getQProcessSignals");
+ d->socket->flush();
+ d->stream.device()->waitForReadyRead(-1);
+ quint32 test;
+ d->stream >> test;
+ d->stream >> receivedSignals;
+ d->stream.device()->readAll();
+ }
+
+ while (!receivedSignals.isEmpty()) {
+ const QString name = receivedSignals.takeFirst().toString();
+ if (name == QLatin1String("started")) {
+ emit started();
+ } else if (name == QLatin1String("readyRead")) {
+ emit readyRead();
+ } else if (name == QLatin1String("stateChanged")) {
+ const QProcess::ProcessState newState =
+ static_cast<QProcess::ProcessState> (receivedSignals.takeFirst().toInt());
+ emit stateChanged(newState);
+ } else if (name == QLatin1String("finished")) {
+ const int exitCode = receivedSignals.takeFirst().toInt();
+ const QProcess::ExitStatus exitStatus =
+ static_cast<QProcess::ExitStatus> (receivedSignals.takeFirst().toInt());
+ emit finished(exitCode);
+ emit finished(exitCode, exitStatus);
+ }
+ }
+}
+
+bool startDetached(const QString &program, const QStringList &args, const QString &workingDirectory,
+ qint64 *pid);
+
+bool QProcessWrapper::startDetached(const QString &program, const QStringList &arguments,
+ const QString &workingDirectory, qint64 *pid)
+{
+ QProcessWrapper w;
+ if (w.d->createSocket()) {
+ const QPair<bool, qint64> result = callRemoteMethod<QPair<bool, qint64> >(w.d->stream,
+ QLatin1String("QProcess::startDetached"), program, arguments, workingDirectory);
+ if (pid != 0)
+ *pid = result.second;
+ return result.first;
+ }
+ return ::startDetached(program, arguments, workingDirectory, pid);
+}
+
+bool QProcessWrapper::startDetached(const QString &program, const QStringList &arguments)
+{
+ return startDetached(program, arguments, QDir::currentPath());
+}
+
+bool QProcessWrapper::startDetached(const QString &program)
+{
+ return startDetached(program, QStringList());
+}
+
+void QProcessWrapper::setProcessChannelMode(QProcessWrapper::ProcessChannelMode mode)
+{
+ const Private::TimerBlocker blocker(this);
+ if (d->createSocket()) {
+ callRemoteVoidMethod(d->stream, QLatin1String("QProcess::setProcessChannelMode"),
+ static_cast<QProcess::ProcessChannelMode>(mode));
+ } else {
+ d->process.setProcessChannelMode(static_cast<QProcess::ProcessChannelMode>(mode));
+ }
+}
+
+/*!
+ Cancels the process. This methods tries to terminate the process
+ gracefully by calling QProcess::terminate. After 10 seconds, the process gets killed.
+ */
+void QProcessWrapper::cancel()
+{
+ if (state() == QProcessWrapper::Running)
+ terminate();
+
+ if (!waitForFinished(10000))
+ kill();
+}
+
+void QProcessWrapper::setReadChannel(QProcessWrapper::ProcessChannel chan)
+{
+ const Private::TimerBlocker blocker(this);
+ if (d->createSocket()) {
+ callRemoteVoidMethod(d->stream, QLatin1String("QProcess::setReadChannel"),
+ static_cast<QProcess::ProcessChannel>(chan));
+ } else {
+ d->process.setReadChannel(static_cast<QProcess::ProcessChannel>(chan));
+ }
+}
+
+bool QProcessWrapper::waitForFinished(int msecs)
+{
+ const Private::TimerBlocker blocker(this);
+ if (d->createSocket())
+ return callRemoteMethod<bool>(d->stream, QLatin1String("QProcess::waitForFinished"), msecs);
+ return d->process.waitForFinished(msecs);
+}
+
+bool QProcessWrapper::waitForStarted(int msecs)
+{
+ const Private::TimerBlocker blocker(this);
+ if (d->createSocket())
+ return callRemoteMethod<bool>(d->stream, QLatin1String("QProcess::waitForStarted"), msecs);
+ return d->process.waitForStarted(msecs);
+}
+
+qint64 QProcessWrapper::write(const QByteArray &data)
+{
+ const Private::TimerBlocker blocker(this);
+ if (d->createSocket())
+ return callRemoteMethod<qint64>(d->stream, QLatin1String("QProcess::write"), data);
+ return d->process.write(data);
+}
+
+void QProcessWrapper::closeWriteChannel()
+{
+ const Private::TimerBlocker blocker(this);
+ if (d->createSocket())
+ callRemoteVoidMethod<void>(d->stream, QLatin1String("QProcess::closeWriteChannel"));
+ else
+ d->process.closeWriteChannel();
+}
+
+int QProcessWrapper::exitCode() const
+{
+ const Private::TimerBlocker blocker(this);
+ if (d->createSocket())
+ return callRemoteMethod<int>(d->stream, QLatin1String("QProcess::exitCode"));
+ return static_cast< int>(d->process.exitCode());
+}
+
+QProcessWrapper::ExitStatus QProcessWrapper::exitStatus() const
+{
+ const Private::TimerBlocker blocker(this);
+ if (d->createSocket())
+ return callRemoteMethod<QProcessWrapper::ExitStatus>(d->stream, QLatin1String("QProcess::exitStatus"));
+ return static_cast< QProcessWrapper::ExitStatus>(d->process.exitStatus());
+}
+
+void QProcessWrapper::kill()
+{
+ const Private::TimerBlocker blocker(this);
+ if (d->createSocket())
+ callRemoteVoidMethod<void>(d->stream, QLatin1String("QProcess::kill"));
+ else
+ d->process.kill();
+}
+
+QByteArray QProcessWrapper::readAll()
+{
+ const Private::TimerBlocker blocker(this);
+ if (d->createSocket())
+ return callRemoteMethod<QByteArray>(d->stream, QLatin1String("QProcess::readAll"));
+ return d->process.readAll();
+}
+
+QByteArray QProcessWrapper::readAllStandardOutput()
+{
+ const Private::TimerBlocker blocker(this);
+ if (d->createSocket())
+ return callRemoteMethod<QByteArray>(d->stream, QLatin1String("QProcess::readAllStandardOutput"));
+ return d->process.readAllStandardOutput();
+}
+
+void QProcessWrapper::start(const QString &param1, const QStringList &param2, QIODevice::OpenMode param3)
+{
+ const Private::TimerBlocker blocker(this);
+ if (d->createSocket())
+ callRemoteVoidMethod(d->stream, QLatin1String("QProcess::start"), param1, param2, param3);
+ else
+ d->process.start(param1, param2, param3);
+}
+
+void QProcessWrapper::start(const QString &param1)
+{
+ const Private::TimerBlocker blocker(this);
+ if (d->createSocket())
+ callRemoteVoidMethod(d->stream, QLatin1String("QProcess::start"), param1);
+ else
+ d->process.start(param1);
+}
+
+QProcessWrapper::ProcessState QProcessWrapper::state() const
+{
+ const Private::TimerBlocker blocker(this);
+ if (d->createSocket())
+ return callRemoteMethod<QProcessWrapper::ProcessState>(d->stream, QLatin1String("QProcess::state"));
+ return static_cast< QProcessWrapper::ProcessState>(d->process.state());
+}
+
+void QProcessWrapper::terminate()
+{
+ const Private::TimerBlocker blocker(this);
+ if (d->createSocket())
+ callRemoteVoidMethod<void>(d->stream, QLatin1String("QProcess::terminate"));
+ else
+ d->process.terminate();
+}
+
+QProcessWrapper::ProcessChannel QProcessWrapper::readChannel() const
+{
+ const Private::TimerBlocker blocker(this);
+ if (d->createSocket()) {
+ return callRemoteMethod<QProcessWrapper::ProcessChannel>(d->stream,
+ QLatin1String("QProcess::readChannel"));
+ }
+ return static_cast< QProcessWrapper::ProcessChannel>(d->process.readChannel());
+}
+
+QProcessWrapper::ProcessChannelMode QProcessWrapper::processChannelMode() const
+{
+ const Private::TimerBlocker blocker(this);
+ if (d->createSocket()) {
+ return callRemoteMethod<QProcessWrapper::ProcessChannelMode>(d->stream,
+ QLatin1String("QProcess::processChannelMode"));
+ }
+ return static_cast< QProcessWrapper::ProcessChannelMode>(d->process.processChannelMode());
+}
+
+QString QProcessWrapper::workingDirectory() const
+{
+ const Private::TimerBlocker blocker(this);
+ if (d->createSocket())
+ return callRemoteMethod<QString>(d->stream, QLatin1String("QProcess::workingDirectory"));
+ return static_cast< QString>(d->process.workingDirectory());
+}
+
+void QProcessWrapper::setEnvironment(const QStringList &param1)
+{
+ const Private::TimerBlocker blocker(this);
+ if (d->createSocket())
+ callRemoteVoidMethod(d->stream, QLatin1String("QProcess::setEnvironment"), param1);
+ else
+ d->process.setEnvironment(param1);
+}
+
+#ifdef Q_OS_WIN
+void QProcessWrapper::setNativeArguments(const QString &param1)
+{
+ const Private::TimerBlocker blocker(this);
+ if (d->createSocket())
+ callRemoteVoidMethod(d->stream, QLatin1String("QProcess::setNativeArguments"), param1);
+ else
+ d->process.setNativeArguments(param1);
+}
+#endif
+
+void QProcessWrapper::setWorkingDirectory(const QString &param1)
+{
+ const Private::TimerBlocker blocker(this);
+ if (d->createSocket())
+ callRemoteVoidMethod(d->stream, QLatin1String("QProcess::setWorkingDirectory"), param1);
+ else
+ d->process.setWorkingDirectory(param1);
+}
diff --git a/src/libs/installer/qprocesswrapper.h b/src/libs/installer/qprocesswrapper.h
new file mode 100644
index 000000000..e9cf1a9d2
--- /dev/null
+++ b/src/libs/installer/qprocesswrapper.h
@@ -0,0 +1,127 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef QPROCESSWRAPPER_H
+#define QPROCESSWRAPPER_H
+
+#include <installer_global.h>
+
+#include<QtCore/QIODevice>
+#include<QtCore/QObject>
+#include<QtCore/QProcess>
+
+class INSTALLER_EXPORT QProcessWrapper : public QObject
+{
+ Q_OBJECT
+public:
+ enum ProcessState {
+ NotRunning,
+ Starting,
+ Running
+ };
+
+ enum ExitStatus {
+ NormalExit,
+ CrashExit
+ };
+
+ enum ProcessChannel {
+ StandardOutput = 0,
+ StandardError = 1
+ };
+
+ enum ProcessChannelMode {
+ SeparateChannels = 0,
+ MergedChannels = 1,
+ ForwardedChannels = 2
+ };
+
+ explicit QProcessWrapper(QObject *parent = 0);
+ ~QProcessWrapper();
+
+ void closeWriteChannel();
+ int exitCode() const;
+ ExitStatus exitStatus() const;
+ void kill();
+ void terminate();
+ QByteArray readAll();
+ QByteArray readAllStandardOutput();
+ void setWorkingDirectory(const QString &dir);
+
+ void start(const QString &program);
+ void start(const QString &program, const QStringList &arguments,
+ QIODevice::OpenMode mode = QIODevice::ReadWrite);
+
+ static bool startDetached(const QString &program);
+ static bool startDetached(const QString &program, const QStringList &arguments);
+ static bool startDetached(const QString &program, const QStringList &arguments,
+ const QString &workingDirectory, qint64 *pid = 0);
+
+ ProcessState state() const;
+ bool waitForStarted(int msecs = 30000);
+ bool waitForFinished(int msecs = 30000);
+ void setEnvironment(const QStringList &environment);
+ QString workingDirectory() const;
+ qint64 write(const QByteArray &byteArray);
+ QProcessWrapper::ProcessChannel readChannel() const;
+ void setReadChannel(QProcessWrapper::ProcessChannel channel);
+ QProcessWrapper::ProcessChannelMode processChannelMode() const;
+ void setProcessChannelMode(QProcessWrapper::ProcessChannelMode channel);
+#ifdef Q_OS_WIN
+ void setNativeArguments(const QString &arguments);
+#endif
+
+Q_SIGNALS:
+ void bytesWritten(qint64);
+ void aboutToClose();
+ void readChannelFinished();
+ void error(QProcess::ProcessError);
+ void readyReadStandardOutput();
+ void readyReadStandardError();
+ void finished(int exitCode);
+ void finished(int exitCode, QProcess::ExitStatus exitStatus);
+ void readyRead();
+ void started();
+ void stateChanged(QProcess::ProcessState newState);
+
+public Q_SLOTS:
+ void cancel();
+
+protected:
+ void timerEvent(QTimerEvent *event);
+
+private:
+ class Private;
+ Private *d;
+};
+
+#endif // QPROCESSWRAPPER_H
diff --git a/src/libs/installer/qsettingswrapper.cpp b/src/libs/installer/qsettingswrapper.cpp
new file mode 100644
index 000000000..09d5b227a
--- /dev/null
+++ b/src/libs/installer/qsettingswrapper.cpp
@@ -0,0 +1,366 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "qsettingswrapper.h"
+
+#include "fsengineclient.h"
+#include "templates.cpp"
+
+#include <QtCore/QSettings>
+#include <QtCore/QThread>
+
+#include <QtNetwork/QTcpSocket>
+
+
+// -- QSettingsWrapper::Private
+
+class QSettingsWrapper::Private
+{
+public:
+ Private(const QString &organization, const QString &application)
+ : native(true),
+ settings(organization, application),
+ socket(0)
+ {
+ }
+
+ Private(QSettings::Scope scope, const QString &organization, const QString &application)
+ : native(true),
+ settings(scope, organization, application),
+ socket(0)
+ {
+ }
+
+ Private(QSettings::Format format, QSettings::Scope scope, const QString &organization,
+ const QString &application)
+ : native(format == QSettings::NativeFormat),
+ settings(format, scope, organization, application),
+ socket(0)
+ {
+ }
+
+ Private(const QString &fileName, QSettings::Format format)
+ : native(format == QSettings::NativeFormat),
+ fileName(fileName),
+ settings(fileName, format),
+ socket(0)
+ {
+ }
+
+ Private()
+ : native(true),
+ socket(0)
+ {
+ }
+
+ bool createSocket()
+ {
+ if (!native || !FSEngineClientHandler::instance().isActive())
+ return false;
+
+ if (socket != 0 && socket->state() == static_cast<int>(QAbstractSocket::ConnectedState))
+ return true;
+
+ if (socket != 0)
+ delete socket;
+
+ socket = new QTcpSocket;
+ if (!FSEngineClientHandler::instance().connect(socket))
+ return false;
+
+ stream.setDevice(socket);
+ stream.setVersion(QDataStream::Qt_4_2);
+
+ stream << QString::fromLatin1("createQSettings");
+ stream << this->fileName;
+ socket->flush();
+ stream.device()->waitForReadyRead(-1);
+ quint32 test;
+ stream >> test;
+ stream.device()->readAll();
+ return true;
+ }
+
+ const bool native;
+ const QString fileName;
+ QSettings settings;
+ mutable QTcpSocket *socket;
+ mutable QDataStream stream;
+};
+
+
+// -- QSettingsWrapper
+
+QSettingsWrapper::QSettingsWrapper(const QString &organization, const QString &application, QObject *parent)
+ : QObject(parent)
+ , d(new Private(organization, application))
+{
+}
+
+QSettingsWrapper::QSettingsWrapper(QSettingsWrapper::Scope scope, const QString &organization,
+ const QString &application, QObject *parent)
+ : QObject(parent)
+ , d(new Private(static_cast<QSettings::Scope>(scope), organization, application))
+{
+}
+
+QSettingsWrapper::QSettingsWrapper(QSettingsWrapper::Format format, QSettingsWrapper::Scope scope,
+ const QString &organization, const QString &application, QObject *parent)
+ : QObject(parent)
+ , d(new Private(static_cast<QSettings::Format>(format), static_cast<QSettings::Scope> (scope),
+ organization, application))
+{
+}
+
+QSettingsWrapper::QSettingsWrapper(const QString &fileName, QSettingsWrapper::Format format, QObject *parent)
+ : QObject(parent)
+ , d(new Private(fileName, static_cast<QSettings::Format>(format)))
+{
+}
+
+QSettingsWrapper::QSettingsWrapper(QObject *parent)
+ : QObject(parent)
+ , d(new Private)
+{
+}
+
+QSettingsWrapper::~QSettingsWrapper()
+{
+ if (d->socket != 0) {
+ d->stream << QString::fromLatin1("destroyQSettings");
+ d->socket->flush();
+ quint32 result;
+ d->stream >> result;
+
+ if (QThread::currentThread() == d->socket->thread()) {
+ d->socket->close();
+ delete d->socket;
+ } else {
+ d->socket->deleteLater();
+ }
+ }
+ delete d;
+}
+
+QStringList QSettingsWrapper::allKeys() const
+{
+ if (d->createSocket())
+ return callRemoteMethod<QStringList>(d->stream, QLatin1String("QSettings::allKeys"));
+ return static_cast<QStringList>(d->settings.allKeys());
+}
+
+QString QSettingsWrapper::applicationName() const
+{
+ if (d->createSocket())
+ return callRemoteMethod<QString>(d->stream, QLatin1String("QSettings::applicationName"));
+ return static_cast<QString>(d->settings.applicationName());
+}
+
+void QSettingsWrapper::beginGroup(const QString &param1)
+{
+ if (d->createSocket())
+ callRemoteVoidMethod(d->stream, QLatin1String("QSettings::beginGroup"), param1);
+ else
+ d->settings.beginGroup(param1);
+}
+
+int QSettingsWrapper::beginReadArray(const QString &param1)
+{
+ if (d->createSocket())
+ return callRemoteMethod<int>(d->stream, QLatin1String("QSettings::beginReadArray"), param1);
+ return d->settings.beginReadArray(param1);
+}
+
+void QSettingsWrapper::beginWriteArray(const QString &param1, int param2)
+{
+ if (d->createSocket())
+ callRemoteVoidMethod(d->stream, QLatin1String("QSettings::beginWriteArray"), param1, param2);
+ else
+ d->settings.beginWriteArray(param1, param2);
+}
+
+QStringList QSettingsWrapper::childGroups() const
+{
+ if (d->createSocket())
+ return callRemoteMethod<QStringList>(d->stream, QLatin1String("QSettings::childGroups"));
+ return static_cast<QStringList>(d->settings.childGroups());
+}
+
+QStringList QSettingsWrapper::childKeys() const
+{
+ if (d->createSocket())
+ return callRemoteMethod<QStringList>(d->stream, QLatin1String("QSettings::childKeys"));
+ return static_cast<QStringList>(d->settings.childKeys());
+}
+
+void QSettingsWrapper::clear()
+{
+ if (d->createSocket())
+ callRemoteVoidMethod<void>(d->stream, QLatin1String("QSettings::clear"));
+ else d->settings.clear();
+}
+
+bool QSettingsWrapper::contains(const QString &param1) const
+{
+ if (d->createSocket())
+ return callRemoteMethod<bool>(d->stream, QLatin1String("QSettings::contains"), param1);
+ return d->settings.contains(param1);
+}
+
+void QSettingsWrapper::endArray()
+{
+ if (d->createSocket())
+ callRemoteVoidMethod<void>(d->stream, QLatin1String("QSettings::endArray"));
+ else
+ d->settings.endArray();
+}
+
+void QSettingsWrapper::endGroup()
+{
+ if (d->createSocket())
+ callRemoteVoidMethod<void>(d->stream, QLatin1String("QSettings::endGroup"));
+ else
+ d->settings.endGroup();
+}
+
+bool QSettingsWrapper::fallbacksEnabled() const
+{
+ if (d->createSocket())
+ return callRemoteMethod<bool>(d->stream, QLatin1String("QSettings::fallbacksEnabled"));
+ return static_cast<bool>(d->settings.fallbacksEnabled());
+}
+
+QString QSettingsWrapper::fileName() const
+{
+ if (d->createSocket())
+ return callRemoteMethod<QString>(d->stream, QLatin1String("QSettings::fileName"));
+ return static_cast<QString>(d->settings.fileName());
+}
+
+QSettingsWrapper::Format QSettingsWrapper::format() const
+{
+ return static_cast<QSettingsWrapper::Format>(d->settings.format());
+}
+
+QString QSettingsWrapper::group() const
+{
+ if (d->createSocket())
+ return callRemoteMethod<QString>(d->stream, QLatin1String("QSettings::group"));
+ return static_cast<QString>(d->settings.group());
+}
+
+QTextCodec* QSettingsWrapper::iniCodec() const
+{
+ return d->settings.iniCodec();
+}
+
+bool QSettingsWrapper::isWritable() const
+{
+ if (d->createSocket())
+ return callRemoteMethod<bool>(d->stream, QLatin1String("QSettings::isWritable"));
+ return static_cast<bool>(d->settings.isWritable());
+}
+
+QString QSettingsWrapper::organizationName() const
+{
+ if (d->createSocket())
+ return callRemoteMethod<QString>(d->stream, QLatin1String("QSettings::organizationName"));
+ return static_cast<QString>(d->settings.organizationName());
+}
+
+void QSettingsWrapper::remove(const QString &param1)
+{
+ if (d->createSocket())
+ callRemoteVoidMethod(d->stream, QLatin1String("QSettings::remove"), param1);
+ else d->settings.remove(param1);
+}
+
+QSettingsWrapper::Scope QSettingsWrapper::scope() const
+{
+ return static_cast<QSettingsWrapper::Scope>(d->settings.scope());
+}
+
+void QSettingsWrapper::setArrayIndex(int param1)
+{
+ if (d->createSocket())
+ callRemoteVoidMethod(d->stream, QLatin1String("QSettings::setArrayIndex"), param1);
+ else
+ d->settings.setArrayIndex(param1);
+}
+
+void QSettingsWrapper::setFallbacksEnabled(bool param1)
+{
+ if (d->createSocket())
+ callRemoteVoidMethod(d->stream, QLatin1String("QSettings::setFallbacksEnabled"), param1);
+ else
+ d->settings.setFallbacksEnabled(param1);
+}
+
+void QSettingsWrapper::setIniCodec(QTextCodec *codec)
+{
+ d->settings.setIniCodec(codec);
+}
+
+void QSettingsWrapper::setIniCodec(const char *codecName)
+{
+ d->settings.setIniCodec(codecName);
+}
+
+void QSettingsWrapper::setValue(const QString &param1, const QVariant &param2)
+{
+ if (d->createSocket())
+ callRemoteVoidMethod(d->stream, QLatin1String("QSettings::setValue"), param1, param2);
+ else
+ d->settings.setValue(param1, param2);
+}
+
+QSettingsWrapper::Status QSettingsWrapper::status() const
+{
+ if (d->createSocket())
+ return callRemoteMethod<QSettingsWrapper::Status>(d->stream, QLatin1String("QSettings::status"));
+ return static_cast<QSettingsWrapper::Status>(d->settings.status());
+}
+
+void QSettingsWrapper::sync()
+{
+ if (d->createSocket())
+ callRemoteVoidMethod<void>(d->stream, QLatin1String("QSettings::sync"));
+ else
+ d->settings.sync();
+}
+
+QVariant QSettingsWrapper::value(const QString &param1, const QVariant &param2) const
+{
+ if (d->createSocket())
+ return callRemoteMethod<QVariant>(d->stream, QLatin1String("QSettings::value"), param1, param2);
+ return d->settings.value(param1, param2);
+}
diff --git a/src/libs/installer/qsettingswrapper.h b/src/libs/installer/qsettingswrapper.h
new file mode 100644
index 000000000..970b57414
--- /dev/null
+++ b/src/libs/installer/qsettingswrapper.h
@@ -0,0 +1,107 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef QSETTINGSWRAPPER_H
+#define QSETTINGSWRAPPER_H
+
+#include <installer_global.h>
+
+#include <QtCore/QObject>
+#include <QtCore/QVariant>
+
+class INSTALLER_EXPORT QSettingsWrapper : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum Format {
+ NativeFormat,
+ IniFormat,
+ InvalidFormat
+ };
+
+ enum Status {
+ NoError,
+ AccessError,
+ FormatError
+ };
+
+ enum Scope {
+ UserScope,
+ SystemScope
+ };
+
+ explicit QSettingsWrapper(QObject *parent = 0);
+ explicit QSettingsWrapper(const QString &organization, const QString &application = QString(),
+ QObject *parent = 0);
+ QSettingsWrapper(const QString &fileName, QSettingsWrapper::Format format, QObject *parent = 0);
+ QSettingsWrapper(QSettingsWrapper::Scope scope, const QString &organization,
+ const QString &application = QString(), QObject *parent = 0);
+ QSettingsWrapper(QSettingsWrapper::Format format, QSettingsWrapper::Scope scope,
+ const QString &organization, const QString &application = QString(), QObject *parent = 0);
+ ~QSettingsWrapper();
+
+ QStringList allKeys() const;
+ QString applicationName() const;
+ void beginGroup(const QString &prefix);
+ int beginReadArray(const QString &prefix);
+ void beginWriteArray(const QString &prefix, int size = -1);
+ QStringList childGroups() const;
+ QStringList childKeys() const;
+ void clear();
+ bool contains(const QString &key) const;
+ void endArray();
+ void endGroup();
+ bool fallbacksEnabled() const;
+ QString fileName() const;
+ QSettingsWrapper::Format format() const;
+ QString group() const;
+ QTextCodec* iniCodec() const;
+ bool isWritable() const;
+ QString organizationName() const;
+ void remove(const QString &key);
+ QSettingsWrapper::Scope scope() const;
+ void setArrayIndex(int i);
+ void setFallbacksEnabled(bool b);
+ void setIniCodec(QTextCodec *codec);
+ void setIniCodec(const char *codecName);
+ void setValue(const QString &key, const QVariant &value);
+ QSettingsWrapper::Status status() const;
+ void sync();
+ QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
+
+private:
+ class Private;
+ Private *d;
+};
+
+#endif // QSETTINGSWRAPPER_H
diff --git a/src/libs/installer/qtcreator_constants.h b/src/libs/installer/qtcreator_constants.h
new file mode 100644
index 000000000..d8c29a302
--- /dev/null
+++ b/src/libs/installer/qtcreator_constants.h
@@ -0,0 +1,77 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef QTCREATOR_CONSTANTS_H
+#define QTCREATOR_CONSTANTS_H
+
+#if defined(Q_OS_MAC)
+ static const char QtCreatorSettingsSuffixPath[] =
+ "/Qt Creator.app/Contents/Resources/Nokia/QtCreator.ini";
+#else
+ static const char QtCreatorSettingsSuffixPath[] =
+ "/QtCreator/share/qtcreator/Nokia/QtCreator.ini";
+#endif
+
+#if defined(Q_OS_MAC)
+ static const char ToolChainSettingsSuffixPath[] =
+ "/Qt Creator.app/Contents/Resources/Nokia/toolChains.xml";
+#else
+ static const char ToolChainSettingsSuffixPath[] =
+ "/QtCreator/share/qtcreator/Nokia/toolChains.xml";
+#endif
+
+#if defined(Q_OS_MAC)
+ static const char QtVersionSettingsSuffixPath[] =
+ "/Qt Creator.app/Contents/Resources/Nokia/qtversion.xml";
+#else
+ static const char QtVersionSettingsSuffixPath[] =
+ "/QtCreator/share/qtcreator/Nokia/qtversion.xml";
+#endif
+
+// Begin - copied from Creator src\plugins\projectexplorer\toolchainmanager.cpp
+static const char TOOLCHAIN_DATA_KEY[] = "ToolChain.";
+static const char TOOLCHAIN_COUNT_KEY[] = "ToolChain.Count";
+static const char TOOLCHAIN_FILE_VERSION_KEY[] = "Version";
+static const char DEFAULT_DEBUGGER_COUNT_KEY[] = "DefaultDebugger.Count";
+static const char DEFAULT_DEBUGGER_ABI_KEY[] = "DefaultDebugger.Abi.";
+static const char DEFAULT_DEBUGGER_PATH_KEY[] = "DefaultDebugger.Path.";
+
+static const char ID_KEY[] = "ProjectExplorer.ToolChain.Id";
+static const char DISPLAY_NAME_KEY[] = "ProjectExplorer.ToolChain.DisplayName";
+// End - copied from Creator
+
+// Begin - copied from Creator src\plugins\qt4projectmanager\qtversionmanager.cpp
+static const char QtVersionsSectionName[] = "QtVersions";
+static const char newQtVersionsKey[] = "NewQtVersions";
+// End - copied from Creator
+
+#endif // QTCREATOR_CONSTANTS_H
diff --git a/src/libs/installer/qtcreatorpersistentsettings.cpp b/src/libs/installer/qtcreatorpersistentsettings.cpp
new file mode 100644
index 000000000..6f257ab5a
--- /dev/null
+++ b/src/libs/installer/qtcreatorpersistentsettings.cpp
@@ -0,0 +1,227 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "qtcreatorpersistentsettings.h"
+#include "qtcreator_constants.h"
+
+#include <QDebug>
+#include <QStringList>
+#include <QFile>
+#include <QDir>
+
+QtCreatorPersistentSettings::QtCreatorPersistentSettings()
+{
+}
+
+bool QtCreatorPersistentSettings::init(const QString &fileName)
+{
+ Q_ASSERT(!fileName.isEmpty());
+ if (fileName.isEmpty())
+ return false;
+ m_fileName = fileName;
+ if (m_reader.load(m_fileName) && !versionCheck())
+ return false;
+
+ m_toolChains = readValidToolChains();
+ m_abiToDebuggerHash = readAbiToDebuggerHash();
+
+ return true;
+}
+
+bool QtCreatorPersistentSettings::versionCheck()
+{
+ QVariantMap data = m_reader.restoreValues();
+
+ // Check version:
+ int version = data.value(QLatin1String(TOOLCHAIN_FILE_VERSION_KEY), 0).toInt();
+ if (version < 1)
+ return false;
+ return true;
+}
+
+
+QHash<QString, QVariantMap> QtCreatorPersistentSettings::readValidToolChains()
+{
+ if (m_fileName.isEmpty())
+ return QHash<QString, QVariantMap>();
+
+ QHash<QString, QVariantMap> toolChainHash;
+
+ QVariantMap data = m_reader.restoreValues();
+
+ int count = data.value(QLatin1String(TOOLCHAIN_COUNT_KEY), 0).toInt();
+ for (int i = 0; i < count; ++i) {
+ const QString key = QLatin1String(TOOLCHAIN_DATA_KEY) + QString::number(i);
+
+ const QVariantMap toolChainMap = data.value(key).toMap();
+
+ //gets the path variable, hope ".Path" will stay in QtCreator
+ QStringList pathContainingKeyList = QStringList(toolChainMap.keys()).filter(QLatin1String(
+ ".Path"));
+ foreach (const QString& pathKey, pathContainingKeyList) {
+ QString path = toolChainMap.value(pathKey).toString();
+ if (!path.isEmpty() && QFile::exists(path)) {
+ toolChainHash.insert(path, toolChainMap);
+ }
+ }
+ }
+ return toolChainHash;
+}
+
+QHash<QString, QString> QtCreatorPersistentSettings::abiToDebuggerHash()
+{
+ return m_abiToDebuggerHash;
+}
+
+QHash<QString, QString> QtCreatorPersistentSettings::readAbiToDebuggerHash()
+{
+ if (m_fileName.isEmpty())
+ return QHash<QString, QString>();
+
+ QHash<QString, QString> abiToDebuggerHash;
+
+ QVariantMap data = m_reader.restoreValues();
+
+ // Read default debugger settings (if any)
+ int count = data.value(QLatin1String(DEFAULT_DEBUGGER_COUNT_KEY)).toInt();
+ for (int i = 0; i < count; ++i) {
+ const QString abiKey = QString::fromLatin1(DEFAULT_DEBUGGER_ABI_KEY) + QString::number(i);
+ if (!data.contains(abiKey))
+ continue;
+ const QString pathKey = QString::fromLatin1(DEFAULT_DEBUGGER_PATH_KEY) + QString::number(i);
+ if (!data.contains(pathKey))
+ continue;
+ abiToDebuggerHash.insert(data.value(abiKey).toString(), data.value(pathKey).toString());
+ }
+ return abiToDebuggerHash;
+}
+
+bool QtCreatorPersistentSettings::addToolChain(const QtCreatorToolChain &toolChain)
+{
+ if (toolChain.key.isEmpty())
+ return false;
+ if (toolChain.type.isEmpty())
+ return false;
+ if (toolChain.displayName.isEmpty())
+ return false;
+ if (toolChain.abiString.isEmpty())
+ return false;
+ if (toolChain.compilerPath.isEmpty())
+ return false;
+
+ QVariantMap newToolChainVariantMap;
+
+ newToolChainVariantMap.insert(QLatin1String(ID_KEY),
+ QString::fromLatin1("%1:%2.%3").arg(toolChain.type, QFileInfo(toolChain.compilerPath
+ ).absoluteFilePath(), toolChain.abiString));
+ newToolChainVariantMap.insert(QLatin1String(DISPLAY_NAME_KEY), toolChain.displayName);
+ newToolChainVariantMap.insert(QString::fromLatin1("ProjectExplorer.%1.Path").arg(toolChain.key),
+ QFileInfo(toolChain.compilerPath).absoluteFilePath());
+ newToolChainVariantMap.insert(QString::fromLatin1("ProjectExplorer.%1.TargetAbi").arg(toolChain.key),
+ toolChain.abiString);
+ newToolChainVariantMap.insert(QString::fromLatin1("ProjectExplorer.%1.Debugger").arg(toolChain.key),
+ QFileInfo(toolChain.debuggerPath).absoluteFilePath());
+
+ m_toolChains.insert(QFileInfo(toolChain.compilerPath).absoluteFilePath(), newToolChainVariantMap);
+ return true;
+}
+
+bool QtCreatorPersistentSettings::removeToolChain(const QtCreatorToolChain &toolChain)
+{
+ m_toolChains.remove(QFileInfo(toolChain.compilerPath).absoluteFilePath());
+ return true;
+}
+
+void QtCreatorPersistentSettings::addDefaultDebugger(const QString &abiString, const QString &debuggerPath)
+{
+ m_abiToDebuggerHash.insert(abiString, debuggerPath);
+}
+
+void QtCreatorPersistentSettings::removeDefaultDebugger(const QString &abiString)
+{
+ m_abiToDebuggerHash.remove(abiString);
+}
+
+bool QtCreatorPersistentSettings::save()
+{
+ if (m_fileName.isEmpty())
+ return false;
+
+ m_writer.saveValue(QLatin1String(DEFAULT_DEBUGGER_COUNT_KEY), m_abiToDebuggerHash.count());
+
+ QHashIterator<QString, QString> it(m_abiToDebuggerHash);
+ int debuggerCounter = 0;
+ while (it.hasNext()) {
+ it.next();
+ const QString abiKey = QString::fromLatin1(DEFAULT_DEBUGGER_ABI_KEY) + QString::number(
+ debuggerCounter);
+ const QString pathKey = QString::fromLatin1(DEFAULT_DEBUGGER_PATH_KEY) + QString::number(
+ debuggerCounter);
+ m_writer.saveValue(abiKey, it.key());
+ m_writer.saveValue(pathKey, it.value());
+ debuggerCounter++;
+ }
+
+ m_writer.saveValue(QLatin1String(TOOLCHAIN_COUNT_KEY), m_toolChains.count());
+
+ int toolChainCounter = 0;
+
+ foreach (QVariantMap toolChainMap, m_toolChains.values()) {
+ if (toolChainMap.isEmpty())
+ continue;
+
+ //if we added a new debugger we need to adjust the tool chains
+ QString abiString;
+ //find the abiString
+ foreach (const QString &key, toolChainMap.keys()) {
+ if (key.contains(QLatin1String(".TargetAbi"))) {
+ abiString = toolChainMap.value(key).toString();
+ break;
+ }
+ }
+ //adjust debugger path
+ foreach (const QString &key, toolChainMap.keys()) {
+ if (key.contains(QLatin1String(".Debugger"))) {
+ toolChainMap.insert(key, m_abiToDebuggerHash.value(abiString));
+ break;
+ }
+ }
+
+ m_writer.saveValue(QLatin1String(TOOLCHAIN_DATA_KEY) + QString::number(toolChainCounter++),
+ toolChainMap);
+ }
+
+ m_writer.saveValue(QLatin1String(TOOLCHAIN_FILE_VERSION_KEY), 1);
+
+ QDir().mkpath(QFileInfo(m_fileName).absolutePath());
+ return m_writer.save(m_fileName, QLatin1String("QtCreatorToolChains"));
+}
diff --git a/src/libs/installer/qtcreatorpersistentsettings.h b/src/libs/installer/qtcreatorpersistentsettings.h
new file mode 100644
index 000000000..6e7a251a6
--- /dev/null
+++ b/src/libs/installer/qtcreatorpersistentsettings.h
@@ -0,0 +1,79 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef QTCREATORPERSISTENTSETTINGS_H
+#define QTCREATORPERSISTENTSETTINGS_H
+
+#include "persistentsettings.h"
+
+#include <QHash>
+#include <QString>
+#include <QVariantMap>
+
+struct QtCreatorToolChain
+{
+ QString key;
+ QString type;
+ QString displayName;
+ QString abiString;
+ QString compilerPath;
+ QString debuggerPath;
+ QString armVersion;
+ QString force32Bit;
+};
+
+class QtCreatorPersistentSettings
+{
+public:
+ QtCreatorPersistentSettings();
+ bool init(const QString &fileName);
+ bool addToolChain(const QtCreatorToolChain &toolChain);
+ bool removeToolChain(const QtCreatorToolChain &toolChain);
+ void addDefaultDebugger(const QString &abiString, const QString &debuggerPath);
+ void removeDefaultDebugger(const QString &abiString);
+ bool save();
+ QHash<QString, QString> abiToDebuggerHash();
+
+private:
+ QHash<QString, QVariantMap> readValidToolChains();
+ QHash<QString, QString> readAbiToDebuggerHash();
+ bool versionCheck();
+
+ QString m_fileName;
+ QHash<QString, QVariantMap> m_toolChains;
+ QHash<QString, QString> m_abiToDebuggerHash;
+ ProjectExplorer::PersistentSettingsReader m_reader;
+ ProjectExplorer::PersistentSettingsWriter m_writer;
+
+};
+
+#endif // QTCREATORPERSISTENTSETTINGS_H
diff --git a/src/libs/installer/qtpatch.cpp b/src/libs/installer/qtpatch.cpp
new file mode 100644
index 000000000..bff86506a
--- /dev/null
+++ b/src/libs/installer/qtpatch.cpp
@@ -0,0 +1,233 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "qtpatch.h"
+
+#include <QString>
+#include <QStringList>
+#include <QFileInfo>
+#include <QProcess>
+#include <QTextStream>
+#include <QVector>
+#include <QTime>
+#include <QtCore/QDebug>
+#include <QCoreApplication>
+#include <QByteArrayMatcher>
+
+#ifdef Q_OS_WIN
+#include <windows.h> // for Sleep
+#endif
+#ifdef Q_OS_UNIX
+#include <errno.h>
+#include <signal.h>
+#include <time.h>
+#endif
+
+static void sleepCopiedFromQTest(int ms)
+{
+ if (ms < 0)
+ return;
+#ifdef Q_OS_WIN
+ Sleep(uint(ms));
+#else
+ struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 };
+ nanosleep(&ts, NULL);
+#endif
+}
+
+static void uiDetachedWait(int ms)
+{
+ QTime timer;
+ timer.start();
+ do {
+ QCoreApplication::processEvents(QEventLoop::AllEvents, ms);
+ sleepCopiedFromQTest(10);
+ } while (timer.elapsed() < ms);
+}
+
+QHash<QString, QByteArray> QtPatch::qmakeValues(const QString &qmakePath, QByteArray *qmakeOutput)
+{
+ QHash<QString, QByteArray> qmakeValueHash;
+
+ // in some cases qmake is not runable, because another process is blocking it(filewatcher ...)
+ int waitCount = 0;
+ while (qmakeValueHash.isEmpty() && waitCount < 60) {
+ QFileInfo qmake(qmakePath);
+
+ if (!qmake.exists()) {
+ qmakeOutput->append(QString::fromLatin1("%1 is not existing").arg(qmakePath));
+ return qmakeValueHash;
+ }
+ if (!qmake.isExecutable()) {
+ qmakeOutput->append(QString::fromLatin1("%1 is not executable").arg(qmakePath));
+ return qmakeValueHash;
+ }
+
+ QStringList args;
+ args << QLatin1String("-query");
+
+ QProcess process;
+ process.start(qmake.absoluteFilePath(), args, QIODevice::ReadOnly);
+ if (process.waitForFinished(2000)) {
+ if (process.exitStatus() == QProcess::CrashExit) {
+ qDebug() << qmakePath << "was crashed";
+ return qmakeValueHash;
+ }
+ QByteArray output = process.readAllStandardOutput();
+ qmakeOutput->append(output);
+ QTextStream stream(&output);
+ while (!stream.atEnd()) {
+ const QString line = stream.readLine();
+ const int index = line.indexOf(QLatin1Char(':'));
+ if (index != -1) {
+ QString value = line.mid(index+1);
+ if (value != QLatin1String("**Unknown**") )
+ qmakeValueHash.insert(line.left(index), value.toUtf8());
+ }
+ }
+ }
+ if (qmakeValueHash.isEmpty()) {
+ ++waitCount;
+ static const int waitTimeInMilliSeconds = 500;
+ uiDetachedWait(waitTimeInMilliSeconds);
+ }
+ if (process.state() > QProcess::NotRunning ) {
+ qDebug() << "qmake process is still running, need to kill it.";
+ process.kill();
+ }
+
+ }
+ if (qmakeValueHash.isEmpty())
+ qDebug() << "Can't get any query output from qmake.";
+ return qmakeValueHash;
+}
+
+bool QtPatch::patchBinaryFile(const QString &fileName,
+ const QByteArray &oldQtPath,
+ const QByteArray &newQtPath)
+{
+ QFile file(fileName);
+ if (!file.exists()) {
+ qDebug() << "qpatch: warning: file" << fileName << "not found";
+ return false;
+ }
+
+ openFileForPatching(&file);
+ if (!file.isOpen()) {
+ qDebug() << "qpatch: warning: file" << qPrintable(fileName) << "can not open.";
+ qDebug() << qPrintable(file.errorString());
+ return false;
+ }
+
+ bool isPatched = patchBinaryFile(&file, oldQtPath, newQtPath);
+
+ file.close();
+ return isPatched;
+}
+
+// device must be open
+bool QtPatch::patchBinaryFile(QIODevice *device,
+ const QByteArray &oldQtPath,
+ const QByteArray &newQtPath)
+{
+ if (!(device->openMode() == QIODevice::ReadWrite)) {
+ qDebug() << "qpatch: warning: This function needs an open device for writing.";
+ return false;
+ }
+ const QByteArray source = device->readAll();
+ device->seek(0);
+
+ int offset = 0;
+ QByteArray overwritePath(newQtPath);
+ if (overwritePath.size() < oldQtPath.size()) {
+ QByteArray fillByteArray(oldQtPath.size() - overwritePath.size(), '\0');
+ overwritePath.append(fillByteArray);
+ }
+
+ QByteArrayMatcher byteArrayMatcher(oldQtPath);
+ forever {
+ offset = byteArrayMatcher.indexIn(source, offset);
+ if (offset == -1)
+ break;
+ device->seek(offset);
+ device->write(overwritePath);
+ offset += overwritePath.size();
+ }
+ device->seek(0); //for next reading we should be at the beginning
+ return true;
+}
+
+bool QtPatch::patchTextFile(const QString &fileName,
+ const QHash<QByteArray, QByteArray> &searchReplacePairs)
+{
+ QFile file(fileName);
+
+ if (!file.open(QFile::ReadOnly)) {
+ qDebug() << QString::fromLatin1("qpatch: warning: Open the file '%1' stopped: %2").arg(
+ fileName, file.errorString());
+ return false;
+ }
+
+ QByteArray source = file.readAll();
+ file.close();
+
+ QHashIterator<QByteArray, QByteArray> it(searchReplacePairs);
+ while (it.hasNext()) {
+ it.next();
+ source.replace(it.key(), it.value());
+ }
+
+ if (!file.open(QFile::WriteOnly | QFile::Truncate)) {
+ qDebug() << QString::fromLatin1("qpatch: error: file '%1' not writable").arg(fileName);
+ return false;
+ }
+
+ file.write(source);
+ return true;
+}
+
+bool QtPatch::openFileForPatching(QFile *file)
+{
+ if (file->openMode() == QIODevice::NotOpen) {
+ // in some cases the file can not open, because another process is blocking it(filewatcher ...)
+ int waitCount = 0;
+ while (!file->open(QFile::ReadWrite) && waitCount < 60) {
+ ++waitCount;
+ static const int waitTimeInMilliSeconds = 500;
+ uiDetachedWait(waitTimeInMilliSeconds);
+ }
+ return file->openMode() == QFile::ReadWrite;
+ }
+ qDebug() << QString::fromLatin1("qpatch: error: File '%1 is open, so it can not open it again.").arg(
+ file->fileName());
+ return false;
+}
diff --git a/src/libs/installer/qtpatch.h b/src/libs/installer/qtpatch.h
new file mode 100644
index 000000000..566ce9903
--- /dev/null
+++ b/src/libs/installer/qtpatch.h
@@ -0,0 +1,60 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef QTPATCH_H
+#define QTPATCH_H
+
+#include "installer_global.h"
+#include <QString>
+#include <QByteArray>
+#include <QHash>
+#include <QFile>
+
+namespace QtPatch {
+
+QHash<QString, QByteArray> INSTALLER_EXPORT qmakeValues(const QString &qmakePath, QByteArray *qmakeOutput);
+
+bool INSTALLER_EXPORT patchBinaryFile(const QString &fileName,
+ const QByteArray &oldQtPath,
+ const QByteArray &newQtPath );
+
+bool INSTALLER_EXPORT patchBinaryFile(QIODevice *device,
+ const QByteArray &oldQtPath,
+ const QByteArray &newQtPath );
+
+bool INSTALLER_EXPORT patchTextFile(const QString &fileName,
+ const QHash<QByteArray, QByteArray> &searchReplacePairs);
+bool INSTALLER_EXPORT openFileForPatching(QFile *file);
+
+}
+
+#endif // QTPATCH_H
diff --git a/src/libs/installer/qtpatchoperation.cpp b/src/libs/installer/qtpatchoperation.cpp
new file mode 100644
index 000000000..348d52e48
--- /dev/null
+++ b/src/libs/installer/qtpatchoperation.cpp
@@ -0,0 +1,343 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "qtpatchoperation.h"
+#include "qtpatch.h"
+#ifdef Q_OS_MAC
+#include "macrelocateqt.h"
+#endif
+
+#include "constants.h"
+#include "packagemanagercore.h"
+
+#include <QMap>
+#include <QSet>
+#include <QFile>
+#include <QTextStream>
+#include <QDir>
+#include <QtCore/QDebug>
+
+using namespace QInstaller;
+
+static QMap<QByteArray, QByteArray> generatePatchValueMap(const QByteArray &newQtPath,
+ const QHash<QString, QByteArray> &qmakeValueHash)
+{
+ QMap<QByteArray, QByteArray> replaceMap; //first == searchstring: second == replace string
+ char nativeSeperator = QDir::separator().toAscii();
+ QByteArray oldValue;
+
+ oldValue = qmakeValueHash.value(QLatin1String("QT_INSTALL_PREFIX"));
+ replaceMap.insert(QByteArray("qt_prfxpath=%1").replace("%1", oldValue),
+ QByteArray("qt_prfxpath=%1/").replace("%1/", newQtPath));
+
+ oldValue = qmakeValueHash.value(QLatin1String("QT_INSTALL_DOCS"));
+ replaceMap.insert(QByteArray("qt_docspath=%1").replace("%1", oldValue),
+ QByteArray("qt_docspath=%1/doc").replace("%1/", newQtPath + nativeSeperator));
+
+ oldValue = qmakeValueHash.value(QLatin1String("QT_INSTALL_HEADERS"));
+ replaceMap.insert(QByteArray("qt_hdrspath=%1").replace("%1", oldValue),
+ QByteArray("qt_hdrspath=%1/include").replace("%1/", newQtPath + nativeSeperator));
+
+ oldValue = qmakeValueHash.value(QLatin1String("QT_INSTALL_LIBS"));
+ replaceMap.insert(QByteArray("qt_libspath=%1").replace("%1", oldValue),
+ QByteArray("qt_libspath=%1/lib").replace("%1/", newQtPath + nativeSeperator));
+
+ oldValue = qmakeValueHash.value(QLatin1String("QT_INSTALL_BINS"));
+ replaceMap.insert(QByteArray("qt_binspath=%1").replace("%1", oldValue),
+ QByteArray("qt_binspath=%1/bin").replace("%1/", newQtPath + nativeSeperator));
+
+ oldValue = qmakeValueHash.value(QLatin1String("QT_INSTALL_PLUGINS"));
+ replaceMap.insert(QByteArray("qt_plugpath=%1").replace("%1", oldValue),
+ QByteArray("qt_plugpath=%1/plugins").replace("%1/", newQtPath + nativeSeperator));
+
+ oldValue = qmakeValueHash.value(QLatin1String("QT_INSTALL_IMPORTS"));
+ replaceMap.insert(QByteArray("qt_impspath=%1").replace("%1", oldValue),
+ QByteArray("qt_impspath=%1/imports").replace("%1/", newQtPath + nativeSeperator));
+
+ oldValue = qmakeValueHash.value(QLatin1String("QT_INSTALL_DATA"));
+ replaceMap.insert( QByteArray("qt_datapath=%1").replace("%1", oldValue),
+ QByteArray("qt_datapath=%1/").replace("%1/", newQtPath + nativeSeperator));
+
+ oldValue = qmakeValueHash.value(QLatin1String("QT_INSTALL_TRANSLATIONS"));
+ replaceMap.insert( QByteArray("qt_trnspath=%1").replace("%1", oldValue),
+ QByteArray("qt_trnspath=%1/translations").replace("%1/", newQtPath + nativeSeperator));
+
+ // This must not be patched. Commenting out to fix QTSDK-429
+ // oldValue = qmakeValueHash.value(QLatin1String("QT_INSTALL_CONFIGURATION"));
+ // replaceMap.insert( QByteArray("qt_stngpath=%1").replace("%1", oldValue),
+ // QByteArray("qt_stngpath=%1").replace("%1", newQtPath));
+
+ //examples and demoes can patched outside separately,
+ //but for cosmetic reasons - if the qt version gets no examples later.
+ oldValue = qmakeValueHash.value(QLatin1String("QT_INSTALL_EXAMPLES"));
+ replaceMap.insert( QByteArray("qt_xmplpath=%1").replace("%1", oldValue),
+ QByteArray("qt_xmplpath=%1/examples").replace("%1/", newQtPath + nativeSeperator));
+
+ oldValue = qmakeValueHash.value(QLatin1String("QT_INSTALL_DEMOS"));
+ replaceMap.insert( QByteArray("qt_demopath=%1").replace("%1", oldValue),
+ QByteArray("qt_demopath=%1/demos").replace("%1/", newQtPath + nativeSeperator));
+
+ return replaceMap;
+}
+
+QtPatchOperation::QtPatchOperation()
+{
+ setName(QLatin1String("QtPatch"));
+}
+
+void QtPatchOperation::backup()
+{
+}
+
+bool QtPatchOperation::performOperation()
+{
+ // Arguments:
+ // 1. type
+ // 2. new/target qtpath
+
+ if (arguments().count() != 2) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, 2 expected.").arg(name())
+ .arg(arguments().count()));
+ return false;
+ }
+
+ QString type = arguments().at(0);
+ bool isPlatformSupported = type.contains(QLatin1String("linux"), Qt::CaseInsensitive)
+ || type.contains(QLatin1String("windows"), Qt::CaseInsensitive)
+ || type.contains(QLatin1String("mac"), Qt::CaseInsensitive);
+ if (!isPlatformSupported) {
+ setError(InvalidArguments);
+ setErrorString(tr("First argument should be 'linux', 'mac' or 'windows'. No other type is supported "
+ "at this time."));
+ return false;
+ }
+
+ const QString newQtPathStr = QDir::toNativeSeparators(arguments().at(1));
+ const QByteArray newQtPath = newQtPathStr.toUtf8();
+
+ QString qmakePath = QString::fromUtf8(newQtPath) + QLatin1String("/bin/qmake");
+#ifdef Q_OS_WIN
+ qmakePath = qmakePath + QLatin1String(".exe");
+#endif
+
+ if (!QFile::exists(qmakePath)) {
+ setError(UserDefinedError);
+ setErrorString(tr("QMake from the current Qt version \n(%1)is not existing. Please file a bugreport "
+ "with this dialog at https://bugreports.qt-project.org.").arg(QDir::toNativeSeparators(qmakePath)));
+ return false;
+ }
+
+ QByteArray qmakeOutput;
+ QHash<QString, QByteArray> qmakeValueHash = QtPatch::qmakeValues(qmakePath, &qmakeOutput);
+
+ if (qmakeValueHash.isEmpty()) {
+ setError(UserDefinedError);
+ setErrorString(tr("The output of \n%1 -query\nis not parseable. Please file a bugreport with this "
+ "dialog https://bugreports.qt-project.org.\noutput: \"%2\"").arg(QDir::toNativeSeparators(qmakePath),
+ QString::fromUtf8(qmakeOutput)));
+ return false;
+ }
+
+ const QByteArray oldQtPath = qmakeValueHash.value(QLatin1String("QT_INSTALL_PREFIX"));
+ bool oldQtPathFromQMakeIsEmpty = oldQtPath.isEmpty();
+
+ //maybe we don't need this, but I 255 should be a rational limit
+ if (255 < newQtPath.size()) {
+ setError(UserDefinedError);
+ setErrorString(tr("Qt patch error: new Qt dir(%1)\nneeds to be less than 255 characters.")
+ .arg(newQtPathStr));
+ return false;
+ }
+
+ QFile patchFileListFile;
+ if (type == QLatin1String("windows"))
+ patchFileListFile.setFileName(QLatin1String(":/files-to-patch-windows"));
+ else if (type == QLatin1String("linux"))
+ patchFileListFile.setFileName(QLatin1String(":/files-to-patch-linux"));
+ else if (type == QLatin1String("linux-emb-arm"))
+ patchFileListFile.setFileName(QLatin1String(":/files-to-patch-linux-emb-arm"));
+ else if (type == QLatin1String("mac"))
+ patchFileListFile.setFileName(QLatin1String(":/files-to-patch-macx"));
+
+ if (!patchFileListFile.open(QFile::ReadOnly)) {
+ setError(UserDefinedError);
+ setErrorString(tr("Qt patch error: Can not open %1.(%2)").arg(patchFileListFile.fileName(),
+ patchFileListFile.errorString()));
+ return false;
+ }
+
+ QStringList filesToPatch, textFilesToPatch;
+ bool readingTextFilesToPatch = false;
+
+ // read the input file
+ QTextStream in(&patchFileListFile);
+
+ forever {
+ const QString line = in.readLine();
+
+ if (line.isNull())
+ break;
+
+ else if (line.isEmpty())
+ continue;
+
+ else if (line.startsWith(QLatin1String("%%")))
+ readingTextFilesToPatch = true;
+
+ //with empty old path we don't know what we want to replace
+ else if (readingTextFilesToPatch && !oldQtPathFromQMakeIsEmpty)
+ textFilesToPatch.append(line);
+
+ else
+ filesToPatch.append(line);
+ }
+
+ QString prefix = QFile::decodeName(newQtPath);
+
+ if (! prefix.endsWith(QLatin1Char('/')))
+ prefix += QLatin1Char('/');
+
+//BEGIN - patch binary files
+ QMap<QByteArray, QByteArray> patchValueMap = generatePatchValueMap(newQtPath, qmakeValueHash);
+
+ foreach (QString fileName, filesToPatch) {
+ fileName.prepend(prefix);
+ QFile file(fileName);
+
+ //without a file we can't do anything
+ if (!file.exists()) {
+ continue;
+ }
+
+ if (!QtPatch::openFileForPatching(&file)) {
+ setError(UserDefinedError);
+ setErrorString(tr("Qt patch error: Can not open %1.(%2)").arg(file.fileName())
+ .arg(file.errorString()));
+ return false;
+ }
+
+ QMapIterator<QByteArray, QByteArray> it(patchValueMap);
+ while (it.hasNext()) {
+ it.next();
+ bool isPatched = QtPatch::patchBinaryFile(&file, it.key(), it.value());
+ if (!isPatched) {
+ qDebug() << QString::fromLatin1("qpatch: warning: file '%1' could not patched").arg(fileName);
+ }
+ }
+ } //foreach (QString fileName, filesToPatch)
+//END - patch binary files
+
+//BEGIN - patch text files
+ QByteArray newQtPathWithNormalSlashes = QDir::fromNativeSeparators(newQtPathStr).toUtf8();
+
+ QHash<QByteArray, QByteArray> searchReplacePairs;
+ searchReplacePairs.insert(oldQtPath, newQtPathWithNormalSlashes);
+ searchReplacePairs.insert(QByteArray(oldQtPath).replace("/", "\\"), newQtPathWithNormalSlashes);
+ searchReplacePairs.insert(QByteArray(oldQtPath).replace("\\", "/"), newQtPathWithNormalSlashes);
+
+#ifdef Q_OS_WIN
+ QByteArray newQtPathWithDoubleBackSlashes = QByteArray(newQtPathWithNormalSlashes).replace("/", "\\\\");
+ searchReplacePairs.insert(QByteArray(oldQtPath).replace("/", "\\\\"), newQtPathWithDoubleBackSlashes);
+ searchReplacePairs.insert(QByteArray(oldQtPath).replace("\\", "\\\\"), newQtPathWithDoubleBackSlashes);
+
+ //this is checking for a possible drive letter, which could be upper or lower
+ if (oldQtPath.mid(1,1) == ":") {
+ QHash<QByteArray, QByteArray> tempSearchReplacePairs;
+ QHashIterator<QByteArray, QByteArray> it(searchReplacePairs);
+ QByteArray driveLetter = oldQtPath.left(1);
+ while (it.hasNext()) {
+ it.next();
+ QByteArray currentPossibleSearchByteArrayWithoutDriveLetter = QByteArray(it.key()).remove(0, 1);
+ tempSearchReplacePairs.insert(driveLetter.toLower()
+ + currentPossibleSearchByteArrayWithoutDriveLetter, it.value());
+ tempSearchReplacePairs.insert(driveLetter.toUpper()
+ + currentPossibleSearchByteArrayWithoutDriveLetter, it.value());
+ }
+ searchReplacePairs = tempSearchReplacePairs;
+ }
+#endif
+
+ foreach (QString fileName, textFilesToPatch) {
+ fileName.prepend(prefix);
+
+ if (QFile::exists(fileName)) {
+ //TODO: use the return value for an error message at the end of the operation
+ QtPatch::patchTextFile(fileName, searchReplacePairs);
+ }
+ }
+//END - patch text files
+
+#ifdef Q_OS_MAC
+ Relocator relocator;
+ bool successMacRelocating = false;
+ PackageManagerCore *const core = qVariantValue<PackageManagerCore*>(value(QLatin1String("installer")));
+ if (!core) {
+ setError(UserDefinedError);
+ setErrorString(tr("Needed installer object in \"%1\" operation is empty.").arg(name()));
+ return false;
+ }
+ Q_CHECK_PTR(core);
+ successMacRelocating = relocator.apply(newQtPathStr, core->value(scTargetDir));
+ if (!successMacRelocating) {
+ setError(UserDefinedError);
+ setErrorString(tr("Error while relocating Qt: %1").arg(relocator.errorMessage()));
+ return false;
+ }
+#endif
+ if (oldQtPathFromQMakeIsEmpty) {
+ setError(UserDefinedError);
+ setErrorString(tr("The installer was not able to get the unpatched path from \n%1.(maybe it is "
+ "broken or removed)\nIt tried to patch the Qt binaries, but all other files in Qt are unpatched."
+ "\nThis could result in a broken Qt version.\nSometimes it helps to restart the installer with a "
+ "switched off antivirus software.").arg(QDir::toNativeSeparators(qmakePath)));
+ return false;
+ }
+
+ return true;
+}
+
+bool QtPatchOperation::undoOperation()
+{
+ return true;
+}
+
+bool QtPatchOperation::testOperation()
+{
+ return true;
+}
+
+Operation *QtPatchOperation::clone() const
+{
+ return new QtPatchOperation();
+}
+
diff --git a/src/libs/installer/qtpatchoperation.h b/src/libs/installer/qtpatchoperation.h
new file mode 100644
index 000000000..31f3abb4e
--- /dev/null
+++ b/src/libs/installer/qtpatchoperation.h
@@ -0,0 +1,54 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef QTPATCHOPERATION_H
+#define QTPATCHOPERATION_H
+
+#include "qinstallerglobal.h"
+
+namespace QInstaller {
+
+class QtPatchOperation : public Operation
+{
+public:
+ QtPatchOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+};
+
+} // namespace QInstaller
+
+#endif // QTPATCHOPERATION_H
diff --git a/src/libs/installer/range.h b/src/libs/installer/range.h
new file mode 100644
index 000000000..f97aa0317
--- /dev/null
+++ b/src/libs/installer/range.h
@@ -0,0 +1,88 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework**
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).*
+**
+** Contact: Nokia Corporation qt-info@nokia.com**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception version
+** 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you are unsure which license is appropriate for your use, please contact
+** (qt-info@nokia.com).
+**
+**************************************************************************/
+#ifndef RANGE_H
+#define RANGE_H
+
+#include <algorithm>
+
+template <typename T>
+class Range {
+public:
+ static Range<T> fromStartAndEnd( const T& start, const T& end ) {
+ Range<T> r;
+ r.m_start = start;
+ r.m_end = end;
+ return r;
+ }
+
+ static Range<T> fromStartAndLength( const T& start, const T& length ) {
+ Range<T> r;
+ r.m_start = start;
+ r.m_end = start + length;
+ return r;
+ }
+
+ Range() : m_start( 0 ), m_end( 0 ) {}
+
+ T start() const { return m_start; }
+
+ T end() const { return m_end; }
+
+ void move( const T& by ) {
+ m_start += by;
+ m_end += by;
+ }
+
+ Range<T> moved( const T& by ) const {
+ Range<T> b = *this;
+ b.move( by );
+ return b;
+ }
+
+ T length() const { return m_end - m_start; }
+
+ Range<T> normalized() const {
+ Range<T> r2( *this );
+ if ( r2.m_start > r2.m_end )
+ std::swap( r2.m_start, r2.m_end );
+ return r2;
+ }
+
+ bool operator==( const Range<T>& other ) const {
+ return m_start == other.m_start && m_end && other.m_end;
+ }
+ bool operator<( const Range<T>& other ) const {
+ if ( m_start != other.m_start )
+ return m_start < other.m_start;
+ return m_end < other.m_end;
+ }
+
+private:
+ T m_start;
+ T m_end;
+};
+
+#endif /* RANGE_H_ */
diff --git a/src/libs/installer/registerdefaultdebuggeroperation.cpp b/src/libs/installer/registerdefaultdebuggeroperation.cpp
new file mode 100644
index 000000000..2cdb4e6bc
--- /dev/null
+++ b/src/libs/installer/registerdefaultdebuggeroperation.cpp
@@ -0,0 +1,160 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "registerdefaultdebuggeroperation.h"
+
+#include "constants.h"
+#include "persistentsettings.h"
+#include "packagemanagercore.h"
+#include "qtcreator_constants.h"
+#include "qtcreatorpersistentsettings.h"
+
+#include <QString>
+#include <QFileInfo>
+#include <QDir>
+#include <QSettings>
+#include <QDebug>
+
+using namespace QInstaller;
+
+using namespace ProjectExplorer;
+
+//TODO move this to a general location it is used on some classes
+static QString fromNativeSeparatorsAllOS(const QString &pathName)
+{
+ QString n = pathName;
+ for (int i = 0; i < n.size(); ++i) {
+ if (n.at(i) == QLatin1Char('\\'))
+ n[i] = QLatin1Char('/');
+ }
+ return n;
+}
+
+RegisterDefaultDebuggerOperation::RegisterDefaultDebuggerOperation()
+{
+ setName(QLatin1String("RegisterDefaultDebugger"));
+}
+
+void RegisterDefaultDebuggerOperation::backup()
+{
+}
+
+/** application binary interface - this is an internal creator typ as a String CPU-OS-OS_FLAVOR-BINARY_FORMAT-WORD_WIDTH
+ * CPU: arm x86 mips ppc itanium
+ * OS: linux macos symbian unix windows
+ * OS_FLAVOR: generic maemo meego generic device emulator generic msvc2005 msvc2008 msvc2010 msys ce
+ * BINARY_FORMAT: elf pe mach_o qml_rt
+ * WORD_WIDTH: 8 16 32 64
+ */
+
+bool RegisterDefaultDebuggerOperation::performOperation()
+{
+ const QStringList args = arguments();
+
+ if (args.count() != 2) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, 2 expected.")
+ .arg(name()).arg(args.count()));
+ return false;
+ }
+
+ QString toolChainsXmlFilePath;
+
+ PackageManagerCore *const core = qVariantValue<PackageManagerCore *>(value(QLatin1String("installer")));
+ if (!core) {
+ setError(UserDefinedError);
+ setErrorString(tr("Needed installer object in \"%1\" operation is empty.").arg(name()));
+ return false;
+ }
+ const QString &rootInstallPath = core->value(scTargetDir);
+ toolChainsXmlFilePath = rootInstallPath + QLatin1String(ToolChainSettingsSuffixPath);
+
+ int argCounter = 0;
+ const QString &abiString = args.at(argCounter++); //for example x86-windows-msys-pe-32bit
+ const QString &debuggerPath = fromNativeSeparatorsAllOS(args.at(argCounter++));
+
+ QtCreatorPersistentSettings creatorToolChainSettings;
+
+ if (!creatorToolChainSettings.init(toolChainsXmlFilePath)) {
+ setError(UserDefinedError);
+ setErrorString(tr("Can't read from tool chains xml file(%1) correctly.")
+ .arg(toolChainsXmlFilePath));
+ return false;
+ }
+
+ creatorToolChainSettings.addDefaultDebugger(abiString, debuggerPath);
+ return creatorToolChainSettings.save();
+}
+
+bool RegisterDefaultDebuggerOperation::undoOperation()
+{
+ const QStringList args = arguments();
+
+ if (args.count() != 2) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, 2 expected.")
+ .arg(name()).arg(args.count()));
+ return false;
+ }
+
+ QString toolChainsXmlFilePath;
+
+ PackageManagerCore *const core = qVariantValue<PackageManagerCore *>(value(QLatin1String("installer")));
+ if (!core) {
+ setError(UserDefinedError);
+ setErrorString(tr("Needed installer object in \"%1\" operation is empty.").arg(name()));
+ return false;
+ }
+ const QString &rootInstallPath = core->value(scTargetDir);
+ toolChainsXmlFilePath = rootInstallPath + QLatin1String(ToolChainSettingsSuffixPath);
+
+ int argCounter = 0;
+ const QString &abiString = args.at(argCounter++); //for example x86-windows-msys-pe-32bit
+ const QString &debuggerPath = fromNativeSeparatorsAllOS(args.at(argCounter++));
+ Q_UNUSED(debuggerPath)
+
+ QtCreatorPersistentSettings creatorToolChainSettings;
+
+ creatorToolChainSettings.init(toolChainsXmlFilePath);
+ creatorToolChainSettings.removeDefaultDebugger(abiString);
+ return creatorToolChainSettings.save();
+}
+
+bool RegisterDefaultDebuggerOperation::testOperation()
+{
+ return true;
+}
+
+Operation *RegisterDefaultDebuggerOperation::clone() const
+{
+ return new RegisterDefaultDebuggerOperation();
+}
diff --git a/src/libs/installer/registerdefaultdebuggeroperation.h b/src/libs/installer/registerdefaultdebuggeroperation.h
new file mode 100644
index 000000000..6baabb1a6
--- /dev/null
+++ b/src/libs/installer/registerdefaultdebuggeroperation.h
@@ -0,0 +1,54 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef REGISTERDEFAULTDEBUGGEROPERATION_H
+#define REGISTERDEFAULTDEBUGGEROPERATION_H
+
+#include "qinstallerglobal.h"
+
+namespace QInstaller {
+
+class RegisterDefaultDebuggerOperation : public Operation
+{
+public:
+ RegisterDefaultDebuggerOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+};
+
+} // namespace QInstaller
+
+#endif // REGISTERDEFAULTDEBUGGEROPERATION_H
diff --git a/src/libs/installer/registerdocumentationoperation.cpp b/src/libs/installer/registerdocumentationoperation.cpp
new file mode 100644
index 000000000..837128c07
--- /dev/null
+++ b/src/libs/installer/registerdocumentationoperation.cpp
@@ -0,0 +1,153 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "registerdocumentationoperation.h"
+
+#include <QSettings>
+#include <QHelpEngine>
+#include <QString>
+#include <QFileInfo>
+#include <QDir>
+#include <QtCore/QDebug>
+
+using namespace QInstaller;
+
+RegisterDocumentationOperation::RegisterDocumentationOperation()
+{
+ setName(QLatin1String("RegisterDocumentation"));
+}
+
+void RegisterDocumentationOperation::backup()
+{
+}
+
+// get the right filename of the qsettingsfile (root/user)
+static QString settingsFileName()
+{
+ #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
+ // If the system settings are writable, don't touch the user settings.
+ // The reason is that a doc registered while running with sudo could otherwise create
+ // a root-owned configuration file a user directory.
+ QScopedPointer<QSettings> settings(new QSettings(QSettings::IniFormat,
+ QSettings::SystemScope, QLatin1String("Nokia"), QLatin1String("QtCreator")));
+
+ // QSettings::isWritable isn't reliable enough in 4.7, determine writability experimentally
+ settings->setValue(QLatin1String("iswritable"), QLatin1String("accomplished"));
+ settings->sync();
+ if (settings->status() == QSettings::NoError) {
+ // we can use the system settings
+ if (settings->contains(QLatin1String("iswritable")))
+ settings->remove(QLatin1String("iswritable"));
+ } else {
+ // we have to use user settings
+ settings.reset(new QSettings(QSettings::IniFormat, QSettings::UserScope,
+ QLatin1String("Nokia"), QLatin1String("QtCreator")));
+ }
+
+ #else
+ QScopedPointer<QSettings> settings(new QSettings(QSettings::IniFormat,
+ QSettings::UserScope, QLatin1String("Nokia"), QLatin1String("QtCreator")));
+ #endif
+ return settings->fileName();
+}
+
+bool RegisterDocumentationOperation::performOperation()
+{
+ const QStringList args = arguments();
+
+ if (args.count() != 1) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, 1 expected.")
+ .arg(name()).arg(args.count()));
+ return false;
+ }
+ const QString helpFile = args.at(0);
+
+ QFileInfo fileInfo(settingsFileName());
+ QDir settingsDir(fileInfo.absolutePath() + QLatin1String("/qtcreator"));
+
+ if (!settingsDir.exists())
+ settingsDir.mkpath(settingsDir.absolutePath());
+ const QString collectionFile = settingsDir.absolutePath() + QLatin1String("/helpcollection.qhc");
+
+ if (!QFileInfo(helpFile).exists()) {
+ setError(UserDefinedError);
+ setErrorString(tr("Could not register help file %1: File not found.").arg(helpFile));
+ return false;
+ }
+
+ QHelpEngineCore help(collectionFile);
+ QString oldData = help.customValue(QLatin1String("AddedDocs")).toString();
+ if (!oldData.isEmpty())
+ oldData.append(QLatin1Char(';'));
+ const QString newData = oldData + QFileInfo(helpFile).absoluteFilePath();
+ if (!help.setCustomValue(QLatin1String("AddedDocs"), newData)) {
+ qWarning() << "Can't register doc file:" << helpFile << help.error();
+// setError(UserDefinedError);
+// setErrorString(help.error());
+// return false;
+ }
+ return true;
+}
+
+bool RegisterDocumentationOperation::undoOperation()
+{
+ const QString helpFile = arguments().first();
+
+ QFileInfo fileInfo(settingsFileName());
+ QDir settingsDir(fileInfo.absolutePath() + QLatin1String("/qtcreator"));
+
+ if (!settingsDir.exists())
+ settingsDir.mkpath(settingsDir.absolutePath());
+ const QString collectionFile = settingsDir.absolutePath() + QLatin1String("/helpcollection.qhc");
+
+ if (!QFileInfo( helpFile ).exists()) {
+ setError ( UserDefinedError );
+ setErrorString( tr("Could not unregister help file %1: File not found.").arg( helpFile ) );
+ return false;
+ }
+
+ QHelpEngineCore help(collectionFile);
+ const QString nsName = help.namespaceName(helpFile);
+ return help.unregisterDocumentation(nsName);
+}
+
+bool RegisterDocumentationOperation::testOperation()
+{
+ return true;
+}
+
+Operation *RegisterDocumentationOperation::clone() const
+{
+ return new RegisterDocumentationOperation();
+}
+
diff --git a/src/libs/installer/registerdocumentationoperation.h b/src/libs/installer/registerdocumentationoperation.h
new file mode 100644
index 000000000..5fd3a9688
--- /dev/null
+++ b/src/libs/installer/registerdocumentationoperation.h
@@ -0,0 +1,54 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef REGISTERDOCUMENTATIONOPERATION_H
+#define REGISTERDOCUMENTATIONOPERATION_H
+
+#include "qinstallerglobal.h"
+
+namespace QInstaller {
+
+class RegisterDocumentationOperation : public Operation
+{
+public:
+ RegisterDocumentationOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+};
+
+} // namespace QInstaller
+
+#endif // REGISTERDOCUMENTATIONOPERATION_H
diff --git a/src/libs/installer/registerfiletypeoperation.cpp b/src/libs/installer/registerfiletypeoperation.cpp
new file mode 100644
index 000000000..5de90733f
--- /dev/null
+++ b/src/libs/installer/registerfiletypeoperation.cpp
@@ -0,0 +1,207 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "registerfiletypeoperation.h"
+
+#include "qsettingswrapper.h"
+
+using namespace QInstaller;
+
+RegisterFileTypeOperation::RegisterFileTypeOperation()
+{
+ setName(QLatin1String("RegisterFileType"));
+}
+
+void RegisterFileTypeOperation::backup()
+{
+}
+
+bool RegisterFileTypeOperation::performOperation()
+{
+ // extension
+ // command
+ // (description)
+ // (content type)
+ // (icon)
+#ifdef Q_WS_WIN
+ const QStringList args = arguments();
+ if (args.count() < 2 || args.count() > 5) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0").arg(name()));
+ return false;
+ }
+
+ const QString extension = args.at(0);
+ const QString command = args.at(1);
+ const QString description = args.value(2); // optional
+ const QString contentType = args.value(3); // optional
+ const QString icon = args.value(4); // optional
+
+ const QString className = QString::fromLatin1("%1_auto_file").arg(extension);
+ const QString settingsPrefix = QString::fromLatin1("Software/Classes/");
+
+ //QSettingsWrapper settings(QLatin1String("HKEY_CLASSES_ROOT"), QSettingsWrapper::NativeFormat);
+ QSettingsWrapper settings(QLatin1String("HKEY_CURRENT_USER"), QSettingsWrapper::NativeFormat);
+ // first backup the old values
+ setValue(QLatin1String("oldClass"), settings.value(QString::fromLatin1("%1.%2/Default").arg(settingsPrefix,
+ extension)));
+ setValue(QLatin1String("oldContentType"), settings.value(QString::fromLatin1("%1.%2/Content Type")
+ .arg(settingsPrefix, extension)));
+
+ // the file extension was not yet registered. Let's do so now
+ settings.setValue(QString::fromLatin1("%1.%2/Default").arg(settingsPrefix, extension), className);
+
+ // register the content type, if given
+ if (!contentType.isEmpty())
+ settings.setValue(QString::fromLatin1("%1.%2/Content Type").arg(settingsPrefix, extension), contentType);
+
+ setValue(QLatin1String("className"), className);
+ // register the description, if given
+ if (!description.isEmpty())
+ settings.setValue(QString::fromLatin1("%1%2/Default").arg(settingsPrefix, className), description);
+
+ // register the icon, if given
+ if (!icon.isEmpty()) {
+ settings.setValue(QString::fromLatin1("%1%2/DefaultIcon/Default").arg(settingsPrefix,
+ className), icon);
+ }
+
+ // register the command to open the file
+ settings.setValue(QString::fromLatin1("%1%2/shell/Open/Command/Default").arg(settingsPrefix,
+ className), command);
+
+ return true;
+#else
+ setError(UserDefinedError);
+ setErrorString(QObject::tr("Registering file types in only supported on Windows."));
+ return false;
+#endif
+}
+
+bool RegisterFileTypeOperation::undoOperation()
+{
+ // extension
+ // command
+ // (description)
+ // (content type)
+ // (icon)
+#ifdef Q_WS_WIN
+ const QStringList args = arguments();
+ if (args.count() < 2 || args.count() > 5) {
+ setErrorString(tr("Register File Type: Invalid arguments"));
+ return false;
+ }
+ const QString extension = args.at(0);
+ const QString command = args.at(1);
+ const QString description = args.value(2); // optional
+ const QString contentType = args.value(3); // optional
+ const QString icon = args.value(4); // optional
+
+ const QString className = QString::fromLatin1("%1_auto_file").arg(extension);
+ const QString classes = QString::fromLatin1("Software/Classes/%1").arg(className);
+ const QString extensions = QString::fromLatin1("Software/Classes/.%1").arg(extension);
+
+ QSettingsWrapper settings(QLatin1String("HKEY_CURRENT_USER"), QSettingsWrapper::NativeFormat);
+
+ const QString restoredClassName = value(QLatin1String("oldClassName")).toString();
+ // register the command to open the file
+ if (settings.value(QString::fromLatin1("%1/shell/Open/Command/Default").arg(classes)).toString() != command)
+ return false;
+ if (settings.value(QString::fromLatin1("%1/DefaultIcon/Default").arg(classes)).toString() != icon)
+ return false;
+ if (settings.value(QString::fromLatin1("%1/Default").arg(classes)).toString() != description)
+ return false;
+ if (settings.value(QString::fromLatin1("%1/Content Type").arg(extensions)).toString() != contentType)
+ return false;
+ if (settings.value(QString::fromLatin1("%1/Default").arg(extensions)).toString() != className)
+ return false;
+
+ const QLatin1String settingsPrefix("Software/Classes/");
+ const QVariant oldCommand = value(QLatin1String("oldCommand"));
+ if (!oldCommand.isNull()) {
+ settings.setValue(QString::fromLatin1("%1%2/shell/Open/Command/Default").arg(settingsPrefix,
+ restoredClassName), oldCommand);
+ } else {
+ settings.remove(QString::fromLatin1("%1/shell/Open/Command/Default").arg(classes));
+ }
+
+ // register the icon, if given
+ const QVariant oldIcon = value(QLatin1String("oldIcon"));
+ if (!oldIcon.isNull()) {
+ settings.setValue(QString::fromLatin1("%1%2/DefaultIcon/Default").arg(settingsPrefix,
+ restoredClassName), oldIcon);
+ } else {
+ settings.remove(QString::fromLatin1("%1%2/DefaultIcon/Default").arg(classes));
+ }
+
+ // register the description, if given
+ const QVariant oldDescription = value(QLatin1String("oldDescription"));
+ if (!oldDescription.isNull()) {
+ settings.setValue(QString::fromLatin1("%1%2/Default").arg(settingsPrefix, restoredClassName),
+ oldDescription);
+ } else {
+ settings.remove(QString::fromLatin1("%1%2/Default").arg(classes));
+ }
+
+ // content type
+ settings.remove(classes);
+
+ const QVariant oldContentType = value(QLatin1String("oldContentType"));
+ if(!oldContentType.isNull())
+ settings.setValue(QString::fromLatin1("%1/Content Type").arg(extensions), oldContentType);
+ else
+ settings.remove(QString::fromLatin1("%1/Content Type").arg(extensions));
+
+ // class
+
+ const QVariant oldClass = value(QLatin1String("oldClass"));
+ if(!oldClass.isNull())
+ settings.setValue(QString::fromLatin1("%1/Default").arg(extensions), oldClass);
+ else
+ settings.remove(extensions);
+
+ return true;
+#else
+ setErrorString(QObject::tr("Registering file types in only supported on Windows."));
+ return false;
+#endif
+}
+
+bool RegisterFileTypeOperation::testOperation()
+{
+ return true;
+}
+
+Operation *RegisterFileTypeOperation::clone() const
+{
+ return new RegisterFileTypeOperation();
+}
diff --git a/src/libs/installer/registerfiletypeoperation.h b/src/libs/installer/registerfiletypeoperation.h
new file mode 100644
index 000000000..824024892
--- /dev/null
+++ b/src/libs/installer/registerfiletypeoperation.h
@@ -0,0 +1,54 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef REGISTERFILETYPEOPERATION_H
+#define REGISTERFILETYPEOPERATION_H
+
+#include "qinstallerglobal.h"
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT RegisterFileTypeOperation : public Operation
+{
+public:
+ RegisterFileTypeOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+};
+
+}
+
+#endif
diff --git a/src/libs/installer/registerqtoperation.cpp b/src/libs/installer/registerqtoperation.cpp
new file mode 100644
index 000000000..4f721ca4c
--- /dev/null
+++ b/src/libs/installer/registerqtoperation.cpp
@@ -0,0 +1,224 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "registerqtoperation.h"
+
+#include "component.h"
+#include "packagemanagercore.h"
+#include "qtcreator_constants.h"
+#include "qtcreatorpersistentsettings.h"
+#include "registertoolchainoperation.h"
+#include "registerqtv2operation.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+#include <QtCore/QSettings>
+#include <QtCore/QString>
+
+using namespace QInstaller;
+
+
+RegisterQtInCreatorOperation::RegisterQtInCreatorOperation()
+{
+ setName(QLatin1String("RegisterQtInCreator"));
+}
+
+void RegisterQtInCreatorOperation::backup()
+{
+}
+
+bool RegisterQtInCreatorOperation::performOperation()
+{
+ const QStringList args = arguments();
+
+ if (args.count() < 3) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, minimum 3 expected.").arg(name())
+ .arg(args.count()));
+ return false;
+ }
+
+ const QString &rootInstallPath = args.value(0); //for example "C:\\Nokia_SDK\\"
+ const QString &versionName = args.value(1);
+ const QString &path = args.value(2);
+ QString mingwPath = args.value(3);
+ QString s60SdkPath = args.value(4);
+ QString gccePath = args.value(5);
+ QString carbidePath = args.value(6);
+ QString msvcPath = args.value(7);
+ QString sbsPath = args.value(8);
+
+//this is for creator 2.2
+ PackageManagerCore *const core = qVariantValue<PackageManagerCore *>(value(QLatin1String("installer")));
+ if (!core) {
+ setError(UserDefinedError);
+ setErrorString(tr("Needed installer object in \"%1\" operation is empty.").arg(name()));
+ return false;
+ }
+ QString toolChainsXmlFilePath = rootInstallPath + QLatin1String(ToolChainSettingsSuffixPath);
+ bool isCreator22 = false;
+ //in case of the fake installer this component doesn't exist
+ Component *creatorComponent =
+ core->componentByName(QLatin1String("com.nokia.ndk.tools.qtcreator.application"));
+ if (creatorComponent) {
+ const QString creatorVersion = creatorComponent->value(scInstalledVersion);
+ isCreator22 = PackageManagerCore::versionMatches(creatorVersion, QLatin1String("2.2"));
+ }
+
+ if (QFileInfo(toolChainsXmlFilePath).exists() || isCreator22) {
+ QtCreatorPersistentSettings creatorToolChainSettings;
+
+ if (!creatorToolChainSettings.init(toolChainsXmlFilePath)) {
+ setError(UserDefinedError);
+ setErrorString(tr("Can't read from tool chains xml file(%1) correctly.")
+ .arg(toolChainsXmlFilePath));
+ return false;
+ }
+ if (!mingwPath.isEmpty()) {
+ RegisterToolChainOperation operation;
+ operation.setValue(QLatin1String("installer"), QVariant::fromValue(core));
+ operation.setArguments(QStringList()
+ << QLatin1String("GccToolChain")
+ << QLatin1String("ProjectExplorer.ToolChain.Mingw")
+ << QLatin1String("Mingw as a GCC for Windows targets")
+ << QLatin1String("x86-windows-msys-pe-32bit")
+ << mingwPath + QLatin1String("\\bin\\g++.exe")
+ << creatorToolChainSettings.abiToDebuggerHash().value(QLatin1String
+ ("x86-windows-msys-pe-32bit"))
+ );
+ if (!operation.performOperation()) {
+ setError(operation.error());
+ setErrorString(operation.errorString());
+ return false;
+ }
+ }
+ if (!gccePath.isEmpty()) {
+ RegisterToolChainOperation operation;
+ operation.setValue(QLatin1String("installer"), QVariant::fromValue(core));
+ operation.setArguments(QStringList()
+ << QLatin1String("GccToolChain")
+ << QLatin1String("Qt4ProjectManager.ToolChain.GCCE")
+ << QLatin1String("GCCE 4 for Symbian targets")
+ << QLatin1String("arm-symbian-device-elf-32bit")
+ << gccePath + QLatin1String("\\bin\\arm-none-symbianelf-g++.exe")
+ << creatorToolChainSettings.abiToDebuggerHash().value(QLatin1String(
+ "arm-symbian-device-elf-32bit"))
+ );
+ if (!operation.performOperation()) {
+ setError(operation.error());
+ setErrorString(operation.errorString());
+ return false;
+ }
+ }
+ RegisterQtInCreatorV2Operation registerQtInCreatorV2Operation;
+ registerQtInCreatorV2Operation.setValue(QLatin1String("installer"), QVariant::fromValue(core));
+ registerQtInCreatorV2Operation.setArguments(QStringList() << versionName << path << s60SdkPath
+ << sbsPath);
+ if (!registerQtInCreatorV2Operation.performOperation()) {
+ setError(registerQtInCreatorV2Operation.error());
+ setErrorString(registerQtInCreatorV2Operation.errorString());
+ return false;
+ }
+ return true;
+ }
+//END - this is for creator 2.2
+
+ QSettings settings(rootInstallPath + QLatin1String(QtCreatorSettingsSuffixPath), QSettings::IniFormat);
+ const QStringList oldNewQtVersions = settings.value(QLatin1String("NewQtVersions")).toString()
+ .split(QLatin1String(";"));
+
+ QString newVersions;
+ //remove not existing Qt versions
+ if (!oldNewQtVersions.isEmpty()) {
+ foreach (const QString &qtVersion, oldNewQtVersions) {
+ QStringList splitedQtConfiguration = qtVersion.split(QLatin1String("="));
+ if (splitedQtConfiguration.count() > 1
+ && splitedQtConfiguration.at(1).contains(QLatin1String("qmake"), Qt::CaseInsensitive)) {
+ QString qmakePath = splitedQtConfiguration.at(1);
+ if (QFile::exists(qmakePath))
+ newVersions.append(qtVersion + QLatin1String(";"));
+ }
+ }
+ }
+#if defined (Q_OS_WIN )
+ QString addedVersion = versionName + QLatin1Char('=') + QDir(path)
+ .absoluteFilePath(QLatin1String("bin/qmake.exe")).replace(QLatin1String("/"), QLatin1String("\\"));
+#elif defined(Q_OS_UNIX )
+ QString addedVersion = versionName + QLatin1Char('=') + QDir(path)
+ .absoluteFilePath(QLatin1String("bin/qmake"));
+#endif
+ addedVersion += QLatin1Char('=') + mingwPath.replace(QLatin1String("/"), QLatin1String("\\"));
+ addedVersion += QLatin1Char('=') + s60SdkPath.replace(QLatin1String("/"), QLatin1String("\\"));
+ addedVersion += QLatin1Char('=') + gccePath.replace(QLatin1String("/"), QLatin1String("\\"));
+ addedVersion += QLatin1Char('=') + carbidePath.replace(QLatin1String("/"), QLatin1String("\\"));
+ addedVersion += QLatin1Char('=') + msvcPath.replace(QLatin1String("/"), QLatin1String("\\"));
+ addedVersion += QLatin1Char('=') + sbsPath.replace(QLatin1String("/"), QLatin1String("\\"));
+ newVersions += addedVersion;
+ settings.setValue(QLatin1String("NewQtVersions"), newVersions);
+
+ return true;
+}
+
+//works with creator 2.1 and 2.2
+bool RegisterQtInCreatorOperation::undoOperation()
+{
+ const QString &rootInstallPath = arguments().value(0); //for example "C:\\Nokia_SDK\\"
+ QSettings settings(rootInstallPath + QLatin1String(QtCreatorSettingsSuffixPath), QSettings::IniFormat);
+ const QStringList oldNewQtVersions = settings.value(QLatin1String("NewQtVersions")).toString()
+ .split(QLatin1String(";"));
+
+ QString newVersions;
+ //remove not existing Qt versions, the current to remove Qt version has an already removed qmake
+ if (!oldNewQtVersions.isEmpty()) {
+ foreach (const QString &qtVersion, oldNewQtVersions) {
+ QStringList splitedQtConfiguration = qtVersion.split(QLatin1String("="));
+ if (splitedQtConfiguration.count() > 1
+ && splitedQtConfiguration.at(1).contains(QLatin1String("qmake"), Qt::CaseInsensitive)) {
+ QString qmakePath = splitedQtConfiguration.at(1);
+ if (QFile::exists(qmakePath))
+ newVersions.append(qtVersion + QLatin1String(";"));
+ }
+ }
+ }
+ settings.setValue(QLatin1String("NewQtVersions"), newVersions);
+ return true;
+}
+
+bool RegisterQtInCreatorOperation::testOperation()
+{
+ return true;
+}
+
+Operation *RegisterQtInCreatorOperation::clone() const
+{
+ return new RegisterQtInCreatorOperation();
+}
diff --git a/src/libs/installer/registerqtoperation.h b/src/libs/installer/registerqtoperation.h
new file mode 100644
index 000000000..62f9b0a94
--- /dev/null
+++ b/src/libs/installer/registerqtoperation.h
@@ -0,0 +1,54 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef REGISTERQTINCREATOROPERATION_H
+#define REGISTERQTINCREATOROPERATION_H
+
+#include "qinstallerglobal.h"
+
+namespace QInstaller {
+
+class RegisterQtInCreatorOperation : public Operation
+{
+public:
+ RegisterQtInCreatorOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+};
+
+} // namespace QInstaller
+
+#endif // REGISTERQTINCREATOROPERATION_H
diff --git a/src/libs/installer/registerqtv23operation.cpp b/src/libs/installer/registerqtv23operation.cpp
new file mode 100644
index 000000000..19b0342e7
--- /dev/null
+++ b/src/libs/installer/registerqtv23operation.cpp
@@ -0,0 +1,231 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "registerqtv23operation.h"
+
+#include "constants.h"
+#include "packagemanagercore.h"
+#include "qtcreator_constants.h"
+#include "persistentsettings.h"
+
+#include <QString>
+#include <QFileInfo>
+#include <QDir>
+#include <QSettings>
+#include <QDebug>
+
+
+using namespace QInstaller;
+
+// TODO: move this to a general location it is used on some classes
+static QString fromNativeSeparatorsAllOS(const QString &pathName)
+{
+ QString n = pathName;
+ for (int i = 0; i < n.size(); ++i) {
+ if (n.at(i) == QLatin1Char('\\'))
+ n[i] = QLatin1Char('/');
+ }
+ return n;
+}
+
+static QString absoluteQmakePath(const QString &path)
+{
+ QString versionQmakePath = QDir(path).absolutePath();
+ if (!versionQmakePath.endsWith(QLatin1String("qmake"))
+ && !versionQmakePath.endsWith(QLatin1String("qmake.exe"))) {
+#if defined (Q_OS_WIN)
+ versionQmakePath.append(QLatin1String("/bin/qmake.exe"));
+#elif defined(Q_OS_UNIX)
+ versionQmakePath.append(QLatin1String("/bin/qmake"));
+#endif
+ }
+ return fromNativeSeparatorsAllOS(versionQmakePath);
+}
+
+RegisterQtInCreatorV23Operation::RegisterQtInCreatorV23Operation()
+{
+ setName(QLatin1String("RegisterQtInCreatorV23"));
+}
+
+void RegisterQtInCreatorV23Operation::backup()
+{
+}
+
+// Parameter List:
+// Name - String displayed as name in Qt Creator
+// qmake path - location of the qmake binary
+// Type identifier - Desktop, Simulator, Symbian, ...
+// SDK identifier - unique string to identify Qt version inside of the SDK (eg. desk473, simu11, ...)
+// System Root Path
+// sbs path
+bool RegisterQtInCreatorV23Operation::performOperation()
+{
+ const QStringList args = arguments();
+
+ if (args.count() < 4) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, minimum 4 expected.")
+ .arg(name()).arg(args.count()));
+ return false;
+ }
+
+ PackageManagerCore *const core = qVariantValue<PackageManagerCore*>(value(QLatin1String("installer")));
+ if (!core) {
+ setError(UserDefinedError);
+ setErrorString(tr("Needed installer object in \"%1\" operation is empty.").arg(name()));
+ return false;
+ }
+ const QString &rootInstallPath = core->value(scTargetDir);
+ if (rootInstallPath.isEmpty() || !QDir(rootInstallPath).exists()) {
+ setError(UserDefinedError);
+ setErrorString(tr("The given TargetDir %1 is not a valid/existing dir.").arg(rootInstallPath));
+ return false;
+ }
+
+ const QString qtVersionsFileName = rootInstallPath
+ + QLatin1String(QtVersionSettingsSuffixPath);
+ int argCounter = 0;
+ const QString &versionName = args.at(argCounter++);
+ const QString &path = QDir::toNativeSeparators(args.value(argCounter++));
+ const QString versionQmakePath = absoluteQmakePath(path);
+
+ const QString &versionTypeIdentifier = args.at(argCounter++);
+ const QString &versionSDKIdentifier = args.at(argCounter++);
+ const QString &versionSystemRoot = fromNativeSeparatorsAllOS(args.value(argCounter++));
+ const QString &versionSbsPath = fromNativeSeparatorsAllOS(args.value(argCounter++));
+
+ ProjectExplorer::PersistentSettingsReader reader;
+ int qtVersionCount = 0;
+ QVariantMap map;
+ if (reader.load(qtVersionsFileName)) {
+ map = reader.restoreValues();
+ qtVersionCount = map.value(QLatin1String("QtVersion.Count")).toInt();
+ map.remove(QLatin1String("QtVersion.Count"));
+ map.remove(QLatin1String("Version"));
+ }
+
+ ProjectExplorer::PersistentSettingsWriter writer;
+ // Store old qt versions
+ if (!map.isEmpty()) {
+ for (int i = 0; i < qtVersionCount; ++i) {
+ writer.saveValue(QString::fromLatin1("QtVersion.%1").arg(i)
+ , map[QLatin1String("QtVersion.") + QString::number(i)].toMap());
+ }
+ map.clear();
+ }
+ // Enter new version
+ map.insert(QLatin1String("Id"), -1);
+ map.insert(QLatin1String("Name"), versionName);
+ map.insert(QLatin1String("QMakePath"), versionQmakePath);
+ map.insert(QLatin1String("QtVersion.Type"),
+ QLatin1String("Qt4ProjectManager.QtVersion.") + versionTypeIdentifier);
+ map.insert(QLatin1String("isAutodetected"), true);
+ map.insert(QLatin1String("autodetectionSource"),
+ QLatin1String("SDK.") + versionSDKIdentifier);
+ if (!versionSystemRoot.isEmpty())
+ map.insert(QLatin1String("SystemRoot"), versionSystemRoot);
+ if (!versionSbsPath.isEmpty())
+ map.insert(QLatin1String("SBSv2Directory"), versionSbsPath);
+
+ writer.saveValue(QLatin1String("QtVersion.") + QString::number(qtVersionCount), map);
+
+ writer.saveValue(QLatin1String("Version"), 1);
+ writer.saveValue(QLatin1String("QtVersion.Count"), qtVersionCount + 1);
+ QDir().mkpath(QFileInfo(qtVersionsFileName).absolutePath());
+ writer.save(qtVersionsFileName, QLatin1String("QtCreatorQtVersions"));
+
+ return true;
+}
+
+bool RegisterQtInCreatorV23Operation::undoOperation()
+{
+ const QStringList args = arguments();
+
+ if (args.count() < 4) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, minimum 4 expected.")
+ .arg(name()).arg(args.count()));
+ return false;
+ }
+
+ PackageManagerCore *const core = qVariantValue<PackageManagerCore*>(value(QLatin1String("installer")));
+ if (!core) {
+ setError(UserDefinedError);
+ setErrorString(tr("Needed installer object in \"%1\" operation is empty.").arg(name()));
+ return false;
+ }
+ const QString &rootInstallPath = core->value(scTargetDir);
+
+ const QString qtVersionsFileName = rootInstallPath
+ + QLatin1String(QtVersionSettingsSuffixPath);
+
+ ProjectExplorer::PersistentSettingsReader reader;
+ // If no file, then it has been removed already
+ if (!reader.load(qtVersionsFileName))
+ return true;
+
+ const QVariantMap map = reader.restoreValues();
+
+ ProjectExplorer::PersistentSettingsWriter writer;
+ const int qtVersionCount = map.value(QLatin1String("QtVersion.Count")).toInt();
+
+ int currentVersionIndex = 0;
+ for (int i = 0; i < qtVersionCount; ++i) {
+ QVariantMap versionMap = map[QLatin1String("QtVersion.") + QString::number(i)].toMap();
+
+ const QString path = QDir::toNativeSeparators(args.value(1));
+ const QString versionQmakePath = absoluteQmakePath(path);
+
+ //use absoluteQmakePath function to normalize the path string, for example //
+ const QString existingQtQMakePath = absoluteQmakePath(
+ versionMap[QLatin1String("QMakePath")].toString());
+ if (existingQtQMakePath == versionQmakePath)
+ continue;
+ writer.saveValue(QString::fromLatin1("QtVersion.%1").arg(currentVersionIndex++), versionMap);
+ }
+
+ writer.saveValue(QLatin1String("QtVersion.Count"), currentVersionIndex);
+ writer.saveValue(QLatin1String("Version"), map[QLatin1String("Version")].toInt());
+
+ writer.save(qtVersionsFileName, QLatin1String("QtCreatorQtVersions"));
+ return true;
+}
+
+bool RegisterQtInCreatorV23Operation::testOperation()
+{
+ return true;
+}
+
+Operation *RegisterQtInCreatorV23Operation::clone() const
+{
+ return new RegisterQtInCreatorV23Operation();
+}
diff --git a/src/libs/installer/registerqtv23operation.h b/src/libs/installer/registerqtv23operation.h
new file mode 100644
index 000000000..5e4f383b8
--- /dev/null
+++ b/src/libs/installer/registerqtv23operation.h
@@ -0,0 +1,54 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef REGISTERQTINCREATORV23OPERATION_H
+#define REGISTERQTINCREATORV23OPERATION_H
+
+#include "qinstallerglobal.h"
+
+namespace QInstaller {
+
+class RegisterQtInCreatorV23Operation : public Operation
+{
+public:
+ RegisterQtInCreatorV23Operation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+};
+
+} // namespace QInstaller
+
+#endif // REGISTERQTINCREATORV2OPERATION_H
diff --git a/src/libs/installer/registerqtv2operation.cpp b/src/libs/installer/registerqtv2operation.cpp
new file mode 100644
index 000000000..1209e52b9
--- /dev/null
+++ b/src/libs/installer/registerqtv2operation.cpp
@@ -0,0 +1,201 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "registerqtv2operation.h"
+
+#include "constants.h"
+#include "packagemanagercore.h"
+#include "qtcreator_constants.h"
+
+#include <QString>
+#include <QFileInfo>
+#include <QDir>
+#include <QSettings>
+#include <QDebug>
+
+using namespace QInstaller;
+
+RegisterQtInCreatorV2Operation::RegisterQtInCreatorV2Operation()
+{
+ setName(QLatin1String("RegisterQtInCreatorV2"));
+}
+
+void RegisterQtInCreatorV2Operation::backup()
+{
+}
+
+//version name; qmake path; system root path; sbs path
+bool RegisterQtInCreatorV2Operation::performOperation()
+{
+ const QStringList args = arguments();
+
+ if (args.count() < 2) {
+ setError(InvalidArguments);
+ setErrorString( tr("Invalid arguments in %0: %1 arguments given, minimum 2 expected.")
+ .arg(name()).arg( args.count() ) );
+ return false;
+ }
+
+ PackageManagerCore *const core = qVariantValue<PackageManagerCore*>(value(QLatin1String("installer")));
+ if (!core) {
+ setError(UserDefinedError);
+ setErrorString(tr("Needed installer object in \"%1\" operation is empty.").arg(name()));
+ return false;
+ }
+ const QString &rootInstallPath = core->value(scTargetDir);
+ if (rootInstallPath.isEmpty() || !QDir(rootInstallPath).exists()) {
+ setError(UserDefinedError);
+ setErrorString(tr("The given TargetDir %1 is not a valid/existing dir.").arg(rootInstallPath));
+ return false;
+ }
+
+ int argCounter = 0;
+ const QString &versionName = args.value(argCounter++);
+ const QString &path = QDir::toNativeSeparators(args.value(argCounter++));
+ QString qmakePath = QDir(path).absolutePath();
+ if ( !qmakePath.endsWith(QLatin1String("qmake"))
+ && !qmakePath.endsWith(QLatin1String("qmake.exe")))
+ {
+#if defined ( Q_OS_WIN )
+ qmakePath.append(QLatin1String("/bin/qmake.exe"));
+#elif defined( Q_OS_UNIX )
+ qmakePath.append(QLatin1String("/bin/qmake"));
+#endif
+ }
+ qmakePath = QDir::toNativeSeparators(qmakePath);
+
+ const QString &systemRoot = QDir::toNativeSeparators(args.value(argCounter++)); //Symbian SDK root for example
+ const QString &sbsPath = QDir::toNativeSeparators(args.value(argCounter++));
+
+ QSettings settings(rootInstallPath + QLatin1String(QtCreatorSettingsSuffixPath),
+ QSettings::IniFormat);
+
+ QString newVersions;
+ QStringList oldNewQtVersions = settings.value(QLatin1String("NewQtVersions")
+ ).toString().split(QLatin1String(";"));
+
+ //remove not existing Qt versions and the current new one(because its added after this)
+ if (!oldNewQtVersions.isEmpty()) {
+ foreach (const QString &qtVersion, oldNewQtVersions) {
+ QStringList splitedQtConfiguration = qtVersion.split(QLatin1String("="));
+ if (splitedQtConfiguration.value(1).contains(QLatin1String("qmake"),
+ Qt::CaseInsensitive)) {
+ QString foundVersionName = splitedQtConfiguration.at(0);
+ QString foundQmakePath = splitedQtConfiguration.at(1);
+ if (QFileInfo(qmakePath) != QFileInfo(foundQmakePath) && versionName != foundVersionName
+ && QFile::exists(foundQmakePath)) {
+ newVersions.append(qtVersion + QLatin1String(";"));
+ }
+ }
+ }
+ }
+
+ QString addedVersion = versionName;
+
+ addedVersion += QLatin1Char('=') + qmakePath;
+ addedVersion += QLatin1Char('=') + systemRoot;
+ addedVersion += QLatin1Char('=') + sbsPath;
+ newVersions += addedVersion;
+ settings.setValue(QLatin1String("NewQtVersions"), newVersions);
+
+ return true;
+}
+
+bool RegisterQtInCreatorV2Operation::undoOperation()
+{
+ const QStringList args = arguments();
+
+ if (args.count() < 2) {
+ setError(InvalidArguments);
+ setErrorString( tr("Invalid arguments in %0: %1 arguments given, minimum 2 expected.")
+ .arg(name()).arg( args.count() ) );
+ return false;
+ }
+
+ PackageManagerCore *const core = qVariantValue<PackageManagerCore*>(value(QLatin1String("installer")));
+ if (!core) {
+ setError(UserDefinedError);
+ setErrorString(tr("Needed installer object in \"%1\" operation is empty.").arg(name()));
+ return false;
+ }
+ const QString &rootInstallPath = core->value(scTargetDir);
+
+ int argCounter = 0;
+ const QString &versionName = args.value(argCounter++);
+ const QString &path = QDir::toNativeSeparators(args.value(argCounter++));
+ QString qmakePath = QDir(path).absolutePath();
+ if (!qmakePath.endsWith(QLatin1String("qmake"))
+ || !qmakePath.endsWith(QLatin1String("qmake.exe")))
+ {
+#if defined ( Q_OS_WIN )
+ qmakePath.append(QLatin1String("/bin/qmake.exe"));
+#elif defined( Q_OS_UNIX )
+ qmakePath.append(QLatin1String("/bin/qmake"));
+#endif
+ }
+ qmakePath = QDir::toNativeSeparators(qmakePath);
+
+ QSettings settings(rootInstallPath + QLatin1String(QtCreatorSettingsSuffixPath),
+ QSettings::IniFormat);
+
+ QString newVersions;
+ QStringList oldNewQtVersions = settings.value(QLatin1String("NewQtVersions")
+ ).toString().split(QLatin1String(";"));
+
+ //remove the removed Qt version from "NewQtVersions" setting
+ if (!oldNewQtVersions.isEmpty()) {
+ foreach (const QString &qtVersion, oldNewQtVersions) {
+ QStringList splitedQtConfiguration = qtVersion.split(QLatin1String("="));
+ if (splitedQtConfiguration.value(1).contains(QLatin1String("qmake"),
+ Qt::CaseInsensitive)) {
+ QString foundVersionName = splitedQtConfiguration.at(0);
+ QString foundQmakePath = splitedQtConfiguration.at(1);
+ if (QFileInfo(qmakePath) != QFileInfo(foundQmakePath) &&versionName != foundVersionName
+ && QFile::exists(foundQmakePath)) {
+ newVersions.append(qtVersion + QLatin1String(";"));
+ }
+ }
+ }
+ }
+ settings.setValue(QLatin1String("NewQtVersions"), newVersions);
+ return true;
+}
+
+bool RegisterQtInCreatorV2Operation::testOperation()
+{
+ return true;
+}
+
+Operation *RegisterQtInCreatorV2Operation::clone() const
+{
+ return new RegisterQtInCreatorV2Operation();
+}
diff --git a/src/libs/installer/registerqtv2operation.h b/src/libs/installer/registerqtv2operation.h
new file mode 100644
index 000000000..9dc726f6c
--- /dev/null
+++ b/src/libs/installer/registerqtv2operation.h
@@ -0,0 +1,54 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef REGISTERQTINCREATORV2OPERATION_H
+#define REGISTERQTINCREATORV2OPERATION_H
+
+#include "qinstallerglobal.h"
+
+namespace QInstaller {
+
+class RegisterQtInCreatorV2Operation : public Operation
+{
+public:
+ RegisterQtInCreatorV2Operation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+};
+
+} // namespace QInstaller
+
+#endif // REGISTERQTINCREATORV2OPERATION_H
diff --git a/src/libs/installer/registertoolchainoperation.cpp b/src/libs/installer/registertoolchainoperation.cpp
new file mode 100644
index 000000000..66190e289
--- /dev/null
+++ b/src/libs/installer/registertoolchainoperation.cpp
@@ -0,0 +1,178 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "registertoolchainoperation.h"
+
+#include "constants.h"
+#include "persistentsettings.h"
+#include "packagemanagercore.h"
+#include "qtcreator_constants.h"
+#include "qtcreatorpersistentsettings.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+#include <QtCore/QSettings>
+#include <QtCore/QString>
+
+using namespace QInstaller;
+
+using namespace ProjectExplorer;
+
+RegisterToolChainOperation::RegisterToolChainOperation()
+{
+ setName(QLatin1String("RegisterToolChain"));
+}
+
+void RegisterToolChainOperation::backup()
+{
+}
+
+bool RegisterToolChainOperation::performOperation()
+{
+ const QStringList args = arguments();
+
+ if (args.count() < 4) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, minimum 4 expected.")
+ .arg(name()).arg(args.count()));
+ return false;
+ }
+
+ QString toolChainsXmlFilePath;
+
+ PackageManagerCore *const core = qVariantValue<PackageManagerCore*>(value(QLatin1String("installer")));
+ if (!core) {
+ setError(UserDefinedError);
+ setErrorString(tr("Needed installer object in \"%1\" operation is empty.").arg(name()));
+ return false;
+ }
+ const QString &rootInstallPath = core->value(scTargetDir);
+ toolChainsXmlFilePath = rootInstallPath + QLatin1String(ToolChainSettingsSuffixPath);
+
+ QtCreatorToolChain toolChain;
+
+ int argCounter = 0;
+ toolChain.key = args.at(argCounter++); //Qt SDK:gccPath
+ toolChain.type = args.at(argCounter++); //where this toolchain is defined in QtCreator
+ toolChain.displayName = args.at(argCounter++); //nice special Toolchain (Qt SDK)
+ toolChain.abiString = args.at(argCounter++); //x86-windows-msys-pe-32bit
+ toolChain.compilerPath = QDir::toNativeSeparators(args.at(argCounter++)); //gccPath
+ if (args.count() > argCounter)
+ toolChain.debuggerPath = QDir::toNativeSeparators(args.at(argCounter++));
+ if (args.count() > argCounter)
+ toolChain.armVersion = args.at(argCounter++);
+ if (args.count() > argCounter)
+ toolChain.force32Bit = args.at(argCounter++);
+
+ QtCreatorPersistentSettings creatorToolChainSettings;
+
+ if (!creatorToolChainSettings.init(toolChainsXmlFilePath)) {
+ setError(UserDefinedError);
+ setErrorString(tr("Can't read from tool chains xml file(%1) correctly.")
+ .arg(toolChainsXmlFilePath));
+ return false;
+ }
+
+ if (!creatorToolChainSettings.addToolChain(toolChain)) {
+ setError(InvalidArguments);
+ setErrorString(tr("Some arguments are not right in %1 operation.")
+ .arg(name()).arg(args.count()));
+ return false;
+ }
+ return creatorToolChainSettings.save();
+}
+
+bool RegisterToolChainOperation::undoOperation()
+{
+ const QStringList args = arguments();
+
+ if (args.count() < 4) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, minimum 4 expected.")
+ .arg(name()).arg(args.count()));
+ return false;
+ }
+
+ QString toolChainsXmlFilePath;
+
+ PackageManagerCore *const core = qVariantValue<PackageManagerCore*>(value(QLatin1String("installer")));
+ if (!core) {
+ setError(UserDefinedError);
+ setErrorString(tr("Needed installer object in \"%1\" operation is empty.").arg(name()));
+ return false;
+ }
+ const QString &rootInstallPath = core->value(scTargetDir);
+ toolChainsXmlFilePath = rootInstallPath + QLatin1String(ToolChainSettingsSuffixPath);
+
+ QtCreatorToolChain toolChain;
+
+ int argCounter = 0;
+ toolChain.key = args.at(argCounter++); //Qt SDK:gccPath
+ toolChain.type = args.at(argCounter++); //where this toolchain is defined in QtCreator
+ toolChain.displayName = args.at(argCounter++); //nice special Toolchain (Qt SDK)
+ toolChain.abiString = args.at(argCounter++); //x86-windows-msys-pe-32bit
+ toolChain.compilerPath = QDir::toNativeSeparators(args.at(argCounter++)); //gccPath
+ if (args.count() > argCounter)
+ toolChain.debuggerPath = QDir::toNativeSeparators(args.at(argCounter++));
+ if (args.count() > argCounter)
+ toolChain.armVersion = args.at(argCounter++);
+ if (args.count() > argCounter)
+ toolChain.force32Bit = args.at(argCounter++);
+
+ QtCreatorPersistentSettings creatorToolChainSettings;
+
+ if (!creatorToolChainSettings.init(toolChainsXmlFilePath)) {
+ setError(UserDefinedError);
+ setErrorString(tr("Can't read from tool chains xml file(%1) correctly.")
+ .arg(toolChainsXmlFilePath));
+ return false;
+ }
+
+ if (!creatorToolChainSettings.removeToolChain(toolChain)) {
+ setError(InvalidArguments);
+ setErrorString(tr("Some arguments are not right in %1 operation.")
+ .arg(name()).arg(args.count()));
+ return false;
+ }
+ return creatorToolChainSettings.save();
+}
+
+bool RegisterToolChainOperation::testOperation()
+{
+ return true;
+}
+
+Operation *RegisterToolChainOperation::clone() const
+{
+ return new RegisterToolChainOperation();
+}
diff --git a/src/libs/installer/registertoolchainoperation.h b/src/libs/installer/registertoolchainoperation.h
new file mode 100644
index 000000000..90fd20671
--- /dev/null
+++ b/src/libs/installer/registertoolchainoperation.h
@@ -0,0 +1,72 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef REGISTERTOOLCHAINOPERATION_H
+#define REGISTERTOOLCHAINOPERATION_H
+
+#include "qinstallerglobal.h"
+
+namespace QInstaller {
+
+/*!
+ Arguments:
+ * SDK Path - to find the QtCreator installation
+ * ToolChainKey - is the internal QtCreator settings key usually: GccToolChain
+ * toolchain type - where this toolchain is defined in QtCreator
+ * ProjectExplorer.ToolChain.Gcc ProjectExplorer.ToolChain.Mingw
+ * ProjectExplorer.ToolChain.LinuxIcc ProjectExplorer.ToolChain.Msvc
+ * Qt4ProjectManager.ToolChain.GCCE Qt4ProjectManager.ToolChain.Maemo
+ * display name - the name how it will be displayed in QtCreator
+ * application binary interface - this is an internal creator typ as a String CPU-OS-OS_FLAVOR-BINARY_FORMAT-WORD_WIDTH
+ * CPU: arm x86 mips ppc itanium
+ * OS: linux macos symbian unix windows
+ * OS_FLAVOR: generic maemo meego generic device emulator generic msvc2005 msvc2008 msvc2010 msys ce
+ * BINARY_FORMAT: elf pe mach_o qml_rt
+ * WORD_WIDTH: 8 16 32 64
+ * compiler path - the binary which is used as the compiler
+ * debugger path - the binary which is used as the debugger
+*/
+class RegisterToolChainOperation : public Operation
+{
+public:
+ RegisterToolChainOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+};
+
+} // namespace QInstaller
+
+#endif // REGISTERTOOLCHAINOPERATION_H
diff --git a/src/libs/installer/replaceoperation.cpp b/src/libs/installer/replaceoperation.cpp
new file mode 100644
index 000000000..d24383ab9
--- /dev/null
+++ b/src/libs/installer/replaceoperation.cpp
@@ -0,0 +1,106 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "replaceoperation.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QTextStream>
+
+using namespace QInstaller;
+
+ReplaceOperation::ReplaceOperation()
+{
+ setName(QLatin1String("Replace"));
+}
+
+void ReplaceOperation::backup()
+{
+}
+
+bool ReplaceOperation::performOperation()
+{
+ const QStringList args = arguments();
+
+ // Arguments:
+ // 1. filename
+ // 2. Source-String
+ // 3. Replace-String
+ if (args.count() != 3) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, 3 expected.").arg(name()).arg(args
+ .count()));
+ return false;
+ }
+ const QString fileName = args.at(0);
+ const QString before = args.at(1);
+ const QString after = args.at(2);
+
+ QFile file(fileName);
+ if (!file.open(QIODevice::ReadOnly)) {
+ setError(UserDefinedError);
+ setErrorString(QObject::tr("Failed to open %1 for reading").arg(fileName));
+ return false;
+ }
+
+ QTextStream stream(&file);
+ QString replacedFileContent = stream.readAll();
+ file.close();
+
+ if (!file.open(QIODevice::WriteOnly)) {
+ setError(UserDefinedError);
+ setErrorString(QObject::tr("Failed to open %1 for writing").arg(fileName));
+ return false;
+ }
+
+ stream.setDevice(&file);
+ stream << replacedFileContent.replace(before, after);
+ file.close();
+
+ return true;
+}
+
+bool ReplaceOperation::undoOperation()
+{
+ // Need to remove settings again
+ return true;
+}
+
+bool ReplaceOperation::testOperation()
+{
+ return true;
+}
+
+Operation *ReplaceOperation::clone() const
+{
+ return new ReplaceOperation();
+}
diff --git a/src/libs/installer/replaceoperation.h b/src/libs/installer/replaceoperation.h
new file mode 100644
index 000000000..e651e7115
--- /dev/null
+++ b/src/libs/installer/replaceoperation.h
@@ -0,0 +1,54 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef REPLACEOPERATION_H
+#define REPLACEOPERATION_H
+
+#include "qinstallerglobal.h"
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT ReplaceOperation : public Operation
+{
+public:
+ ReplaceOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+};
+
+} // namespace QInstaller
+
+#endif // REPLACEOPERATION_H
diff --git a/src/libs/installer/repository.cpp b/src/libs/installer/repository.cpp
new file mode 100644
index 000000000..cc80a20d9
--- /dev/null
+++ b/src/libs/installer/repository.cpp
@@ -0,0 +1,213 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "repository.h"
+
+namespace QInstaller {
+
+/*
+ Constructs an invalid Repository object.
+*/
+Repository::Repository()
+ : m_default(false)
+ , m_enabled(false)
+{
+ registerMetaType();
+}
+
+/*!
+ Constructs a new repository by using all fields of the given repository \a other.
+*/
+Repository::Repository(const Repository &other)
+ : m_url(other.m_url)
+ , m_default(other.m_default)
+ , m_enabled(other.m_enabled)
+ , m_username(other.m_username)
+ , m_password(other.m_password)
+{
+ registerMetaType();
+}
+
+/*!
+ Constructs a new repository by setting it's address to \a url and it's default state.
+*/
+Repository::Repository(const QUrl &url, bool isDefault)
+ : m_url(url)
+ , m_default(isDefault)
+ , m_enabled(true)
+{
+ registerMetaType();
+}
+
+/*!
+ Returns true if the repository URL is valid; otherwise returns false.
+
+ Note: The URL is simply run through a conformance test. It is not checked that the repository
+ actually exists.
+*/
+bool Repository::isValid() const
+{
+ return m_url.isValid();
+}
+
+/*!
+ Returns true if the repository was set using the package manager configuration file; otherwise returns
+ false.
+*/
+bool Repository::isDefault() const
+{
+ return m_default;
+}
+
+/*!
+ Returns the URL of the repository. By default an invalid \sa QUrl is returned.
+*/
+QUrl Repository::url() const
+{
+ return m_url;
+}
+
+/*!
+ Sets the repository URL to the one specified at \a url.
+*/
+void Repository::setUrl(const QUrl &url)
+{
+ m_url = url;
+}
+
+/*!
+ Returns whether the repository is enabled and used during information retrieval.
+*/
+bool Repository::isEnabled() const
+{
+ return m_enabled;
+}
+
+/*!
+ Sets this repository to \n enabled state and thus to use this repository for information retrieval or not.
+*/
+void Repository::setEnabled(bool enabled)
+{
+ m_enabled = enabled;
+}
+
+/*!
+ Returns the user name used for authentication.
+*/
+QString Repository::username() const
+{
+ return m_username;
+}
+
+/*!
+ Sets the user name for authentication to be \a username.
+*/
+void Repository::setUsername(const QString &username)
+{
+ m_username = username;
+}
+
+/*!
+ Returns the password used for authentication.
+*/
+QString Repository::password() const
+{
+ return m_password;
+}
+
+/*!
+ Sets the password for authentication to be \a password.
+*/
+void Repository::setPassword(const QString &password)
+{
+ m_password = password;
+}
+
+/*!
+ Compares the values of this repository to \a other and returns true if they are equal (same server,
+ default state, enabled state as well as username and password). \sa operator!=()
+*/
+bool Repository::operator==(const Repository &other) const
+{
+ return m_url == other.m_url && m_default == other.m_default && m_enabled == other.m_enabled
+ && m_username == other.m_username && m_password == other.m_password;
+}
+
+/*!
+ Returns true if the \a other repository is not equal to this repository; otherwise returns false. Two
+ repositories are considered equal if they contain the same elements. \sa operator==()
+*/
+bool Repository::operator!=(const Repository &other) const
+{
+ return !(*this == other);
+}
+
+/*!
+ Assigns the values of repository \a other to this repository.
+*/
+const Repository &Repository::operator=(const Repository &other)
+{
+ if (this == &other)
+ return *this;
+
+ m_url = other.m_url;
+ m_default = other.m_default;
+ m_enabled = other.m_enabled;
+ m_username = other.m_username;
+ m_password = other.m_password;
+
+ return *this;
+}
+
+void Repository::registerMetaType()
+{
+ qRegisterMetaType<Repository>("Repository");
+ qRegisterMetaTypeStreamOperators<Repository>("Repository");
+}
+
+QDataStream &operator>>(QDataStream &istream, Repository &repository)
+{
+ QByteArray url, username, password;
+ istream >> url >> repository.m_default >> repository.m_enabled >> username >> password;
+ repository.setUrl(QUrl::fromEncoded(QByteArray::fromBase64(url)));
+ repository.setUsername(QString::fromUtf8(QByteArray::fromBase64(username)));
+ repository.setPassword(QString::fromUtf8(QByteArray::fromBase64(password)));
+ return istream;
+}
+
+QDataStream &operator<<(QDataStream &ostream, const Repository &repository)
+{
+ return ostream << repository.m_url.toEncoded().toBase64() << repository.m_default << repository.m_enabled
+ << repository.m_username.toUtf8().toBase64() << repository.m_password.toUtf8().toBase64();
+}
+
+}
diff --git a/src/libs/installer/repository.h b/src/libs/installer/repository.h
new file mode 100644
index 000000000..432a851c4
--- /dev/null
+++ b/src/libs/installer/repository.h
@@ -0,0 +1,97 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef REPOSITORY_H
+#define REPOSITORY_H
+
+#include "installer_global.h"
+
+#include <QtCore/QMetaType>
+#include <QtCore/QUrl>
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT Repository
+{
+public:
+ explicit Repository();
+ Repository(const Repository &other);
+ explicit Repository(const QUrl &url, bool isDefault);
+
+ bool isValid() const;
+ bool isDefault() const;
+
+ QUrl url() const;
+ void setUrl(const QUrl &url);
+
+ bool isEnabled() const;
+ void setEnabled(bool enabled);
+
+ QString username() const;
+ void setUsername(const QString &username);
+
+ QString password() const;
+ void setPassword(const QString &password);
+
+ bool operator==(const Repository &other) const;
+ bool operator!=(const Repository &other) const;
+
+ uint qHash(const Repository &repository);
+ const Repository &operator=(const Repository &other);
+
+ friend QDataStream &operator>>(QDataStream &istream, Repository &repository);
+ friend QDataStream &operator<<(QDataStream &ostream, const Repository &repository);
+
+private:
+ void registerMetaType();
+
+private:
+ QUrl m_url;
+ bool m_default;
+ bool m_enabled;
+ QString m_username;
+ QString m_password;
+};
+
+inline uint qHash(const Repository &repository)
+{
+ return qHash(repository.url().toString());
+}
+
+QDataStream &operator>>(QDataStream &istream, Repository &repository);
+QDataStream &operator<<(QDataStream &ostream, const Repository &repository);
+
+} // namespace QInstaller
+
+Q_DECLARE_METATYPE(QInstaller::Repository)
+
+#endif // REPOSITORY_H
diff --git a/src/libs/installer/resources/files-to-patch-linux b/src/libs/installer/resources/files-to-patch-linux
new file mode 100644
index 000000000..9c7b39f4a
--- /dev/null
+++ b/src/libs/installer/resources/files-to-patch-linux
@@ -0,0 +1,72 @@
+bin/qmake
+bin/lrelease
+lib/libQtCore.so
+%%
+lib/libQtCore.la
+lib/libQt3Support.la
+lib/libQtCLucene.la
+lib/libQtDBus.la
+lib/libQtDeclarative.la
+lib/libQtGui.la
+lib/libQtHelp.la
+lib/libQtMultimedia.la
+lib/libQtNetwork.la
+lib/libQtOpenGL.la
+lib/libphonon.la
+lib/libQtScript.la
+lib/libQtScriptTools.la
+lib/libQtSql.la
+lib/libQtSvg.la
+lib/libQtTest.la
+lib/libQtWebKit.la
+lib/libQtXml.la
+lib/libQtXmlPatterns.la
+demos/shared/libdemo_shared.prl
+lib/libQt3Support.prl
+lib/libQtAssistantClient.prl
+lib/libQtCLucene.prl
+lib/libQtCore.prl
+lib/libQtDBus.prl
+lib/libQtDesignerComponents.prl
+lib/libQtDesigner.prl
+lib/libQtDeclarative.prl
+lib/libQtGui.prl
+lib/libQtHelp.prl
+lib/libQtMultimedia.prl
+lib/libQtNetwork.prl
+lib/libQtOpenGL.prl
+lib/libQtScript.prl
+lib/libQtScriptTools.prl
+lib/libQtSql.prl
+lib/libQtSvg.prl
+lib/libQtTest.prl
+lib/libQtUiTools.prl
+lib/libQtWebKit.prl
+lib/libQtXmlPatterns.prl
+lib/libQtXml.prl
+lib/libphonon.prl
+lib/libqtmain.prl
+lib/pkgconfig/phonon.pc
+lib/pkgconfig/Qt3Support.pc
+lib/pkgconfig/QtAssistantClient.pc
+lib/pkgconfig/QtCLucene.pc
+lib/pkgconfig/QtCore.pc
+lib/pkgconfig/QtDBus.pc
+lib/pkgconfig/QtDeclarative.pc
+lib/pkgconfig/QtDesignerComponents.pc
+lib/pkgconfig/QtDesigner.pc
+lib/pkgconfig/QtGui.pc
+lib/pkgconfig/QtHelp.pc
+lib/pkgconfig/QtMultimedia.pc
+lib/pkgconfig/QtNetwork.pc
+lib/pkgconfig/QtOpenGL.pc
+lib/pkgconfig/QtScript.pc
+lib/pkgconfig/QtScriptTools.pc
+lib/pkgconfig/QtSql.pc
+lib/pkgconfig/QtSvg.pc
+lib/pkgconfig/QtTest.pc
+lib/pkgconfig/QtUiTools.pc
+lib/pkgconfig/QtWebKit.pc
+lib/pkgconfig/QtXmlPatterns.pc
+lib/pkgconfig/QtXml.pc
+mkspecs/qconfig.pri
diff --git a/src/libs/installer/resources/files-to-patch-linux-emb-arm b/src/libs/installer/resources/files-to-patch-linux-emb-arm
new file mode 100644
index 000000000..0ae1605de
--- /dev/null
+++ b/src/libs/installer/resources/files-to-patch-linux-emb-arm
@@ -0,0 +1,70 @@
+bin/qmake
+bin/lrelease
+%%
+lib/libQtCore.la
+lib/libQt3Support.la
+lib/libQtCLucene.la
+lib/libQtDBus.la
+lib/libQtDeclarative.la
+lib/libQtGui.la
+lib/libQtHelp.la
+lib/libQtMultimedia.la
+lib/libQtNetwork.la
+lib/libQtOpenGL.la
+lib/libphonon.la
+lib/libQtScript.la
+lib/libQtScriptTools.la
+lib/libQtSql.la
+lib/libQtSvg.la
+lib/libQtTest.la
+lib/libQtWebKit.la
+lib/libQtXml.la
+lib/libQtXmlPatterns.la
+demos/shared/libdemo_shared.prl
+lib/libQt3Support.prl
+lib/libQtAssistantClient.prl
+lib/libQtCLucene.prl
+lib/libQtCore.prl
+lib/libQtDBus.prl
+lib/libQtDesignerComponents.prl
+lib/libQtDesigner.prl
+lib/libQtDeclarative.prl
+lib/libQtGui.prl
+lib/libQtHelp.prl
+lib/libQtMultimedia.prl
+lib/libQtNetwork.prl
+lib/libQtOpenGL.prl
+lib/libQtScript.prl
+lib/libQtScriptTools.prl
+lib/libQtSql.prl
+lib/libQtSvg.prl
+lib/libQtTest.prl
+lib/libQtUiTools.prl
+lib/libQtWebKit.prl
+lib/libQtXmlPatterns.prl
+lib/libQtXml.prl
+lib/libphonon.prl
+lib/libqtmain.prl
+lib/pkgconfig/phonon.pc
+lib/pkgconfig/Qt3Support.pc
+lib/pkgconfig/QtAssistantClient.pc
+lib/pkgconfig/QtCLucene.pc
+lib/pkgconfig/QtCore.pc
+lib/pkgconfig/QtDBus.pc
+lib/pkgconfig/QtDeclarative.pc
+lib/pkgconfig/QtDesignerComponents.pc
+lib/pkgconfig/QtDesigner.pc
+lib/pkgconfig/QtGui.pc
+lib/pkgconfig/QtHelp.pc
+lib/pkgconfig/QtMultimedia.pc
+lib/pkgconfig/QtNetwork.pc
+lib/pkgconfig/QtOpenGL.pc
+lib/pkgconfig/QtScript.pc
+lib/pkgconfig/QtScriptTools.pc
+lib/pkgconfig/QtSql.pc
+lib/pkgconfig/QtSvg.pc
+lib/pkgconfig/QtTest.pc
+lib/pkgconfig/QtUiTools.pc
+lib/pkgconfig/QtWebKit.pc
+lib/pkgconfig/QtXmlPatterns.pc
+lib/pkgconfig/QtXml.pc
diff --git a/src/libs/installer/resources/files-to-patch-macx b/src/libs/installer/resources/files-to-patch-macx
new file mode 100644
index 000000000..7f8deda14
--- /dev/null
+++ b/src/libs/installer/resources/files-to-patch-macx
@@ -0,0 +1,61 @@
+bin/qmake
+bin/lrelease
+bin/lconvert
+bin/lupdate
+bin/macdeployqt
+bin/qcollectiongenerator
+bin/qdoc3
+bin/qhelpgenerator
+bin/qt3to4
+bin/xmlpatterns
+bin/xmlpatternsvalidator
+lib/QtCore.framework/QtCore
+bin/Designer.app/Contents/MacOS/Designer
+bin/Linguist.app/Contents/MacOS/Linguist
+bin/qhelpconverter.app/Contents/MacOS/qhelpconverter
+bin/QMLViewer.app/Contents/MacOS/QMLViewer
+bin/qttracereplay.app/Contents/MacOS/qttracereplay
+%%
+lib/QtCore.la
+lib/libQtCLucene.la
+lib/QtDeclarative.la
+lib/QtGui.la
+lib/QtHelp.la
+lib/QtMultimedia.la
+lib/QtNetwork.la
+lib/QtOpenGL.la
+lib/phonon.la
+lib/QtScript.la
+lib/QtScriptTools.la
+lib/QtSql.la
+lib/QtSvg.la
+lib/QtTest.la
+lib/QtWebKit.la
+lib/QtXml.la
+lib/QtXmlPatterns.la
+lib/libQtCLucene.prl
+lib/libQtUiTools.prl
+lib/pkgconfig/phonon.pc
+lib/pkgconfig/Qt3Support.pc
+lib/pkgconfig/QtAssistantClient.pc
+lib/pkgconfig/QtCLucene.pc
+lib/pkgconfig/QtCore.pc
+lib/pkgconfig/QtDBus.pc
+lib/pkgconfig/QtDeclarative.pc
+lib/pkgconfig/QtDesignerComponents.pc
+lib/pkgconfig/QtDesigner.pc
+lib/pkgconfig/QtGui.pc
+lib/pkgconfig/QtHelp.pc
+lib/pkgconfig/QtMultimedia.pc
+lib/pkgconfig/QtNetwork.pc
+lib/pkgconfig/QtOpenGL.pc
+lib/pkgconfig/QtScript.pc
+lib/pkgconfig/QtScriptTools.pc
+lib/pkgconfig/QtSql.pc
+lib/pkgconfig/QtSvg.pc
+lib/pkgconfig/QtTest.pc
+lib/pkgconfig/QtUiTools.pc
+lib/pkgconfig/QtWebKit.pc
+lib/pkgconfig/QtXmlPatterns.pc
+lib/pkgconfig/QtXml.pc
+mkspecs/qconfig.pri
diff --git a/src/libs/installer/resources/files-to-patch-windows b/src/libs/installer/resources/files-to-patch-windows
new file mode 100644
index 000000000..845a299ce
--- /dev/null
+++ b/src/libs/installer/resources/files-to-patch-windows
@@ -0,0 +1,60 @@
+bin/qmake.exe
+bin/lrelease.exe
+bin/QtCore4.dll
+bin/QtCored4.dll
+lib/QtCore4.dll
+lib/QtCored4.dll
+%%
+mkspecs/default/qmake.conf
+demos/shared/libdemo_shared.prl
+lib/Qt3Support.prl
+lib/QtAssistantClient.prl
+lib/QtCLucene.prl
+lib/QtCore.prl
+lib/QtDesignerComponents.prl
+lib/QtDesigner.prl
+lib/QtGui.prl
+lib/QtHelp.prl
+lib/QtMultimedia.prl
+lib/QtNetwork.prl
+lib/QtOpenGL.prl
+lib/QtScript.prl
+lib/QtScriptTools.prl
+lib/QtSql.prl
+lib/QtSvg.prl
+lib/QtTest.prl
+lib/QtUiTools.prl
+lib/QtWebKit.prl
+lib/QtXmlPatterns.prl
+lib/QtXml.prl
+lib/Qt3Supportd.prl
+lib/QtAssistantClientd.prl
+lib/QtCLucened.prl
+lib/QtCored.prl
+lib/QtDesignerComponentsd.prl
+lib/QtDesignerd.prl
+lib/QtGuid.prl
+lib/QtHelpd.prl
+lib/QtMultimediad.prl
+lib/QtNetworkd.prl
+lib/QtOpenGLd.prl
+lib/QtScriptd.prl
+lib/QtScriptToolsd.prl
+lib/QtSqld.prl
+lib/QtSvgd.prl
+lib/QtTestd.prl
+lib/QtUiToolsd.prl
+lib/QtWebKitd.prl
+lib/QtXmlPatternsd.prl
+lib/QtXmld.prl
+lib/phonon.prl
+lib/phonond.prl
+lib/QtDeclarative.prl
+lib/QtDeclaratived.prl
+lib/qtmain.prl
+lib/qtmaind.prl
+lib/QAxContainer.prl
+lib/QAxContainerd.prl
+lib/QAxServer.prl
+lib/QAxServerd.prl
+.qmake.cache
diff --git a/src/libs/installer/resources/patch_file_lists.qrc b/src/libs/installer/resources/patch_file_lists.qrc
new file mode 100644
index 000000000..a51500369
--- /dev/null
+++ b/src/libs/installer/resources/patch_file_lists.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/">
+ <file>files-to-patch-linux</file>
+ <file>files-to-patch-windows</file>
+ <file>files-to-patch-macx</file>
+ </qresource>
+</RCC>
diff --git a/src/libs/installer/selfrestartoperation.cpp b/src/libs/installer/selfrestartoperation.cpp
new file mode 100644
index 000000000..a9df116b8
--- /dev/null
+++ b/src/libs/installer/selfrestartoperation.cpp
@@ -0,0 +1,88 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "selfrestartoperation.h"
+#include "packagemanagercore.h"
+
+#include <kdselfrestarter.h>
+
+using namespace QInstaller;
+
+SelfRestartOperation::SelfRestartOperation()
+{
+ setName(QLatin1String("SelfRestart"));
+}
+
+void SelfRestartOperation::backup()
+{
+ setValue(QLatin1String("PreviousSelfRestart"), KDSelfRestarter::restartOnQuit());
+}
+
+bool SelfRestartOperation::performOperation()
+{
+ PackageManagerCore *const core = qVariantValue<PackageManagerCore *>(value(QLatin1String("installer")));
+ if (!core) {
+ setError(UserDefinedError);
+ setErrorString(tr("Needed installer object in \"%1\" operation is empty.").arg(name()));
+ return false;
+ }
+
+ if (!core->isUpdater() && !core->isPackageManager()) {
+ setError(UserDefinedError);
+ setErrorString(tr("Self Restart: Only valid within updater or packagemanager mode."));
+ return false;
+ }
+
+ if (!arguments().isEmpty()) {
+ setError(InvalidArguments);
+ setErrorString(tr("Self Restart: Invalid arguments"));
+ return false;
+ }
+ KDSelfRestarter::setRestartOnQuit(true);
+ return KDSelfRestarter::restartOnQuit();
+}
+
+bool SelfRestartOperation::undoOperation()
+{
+ KDSelfRestarter::setRestartOnQuit(value(QLatin1String("PreviousSelfRestart")).toBool());
+ return true;
+}
+
+bool SelfRestartOperation::testOperation()
+{
+ return true;
+}
+
+Operation *SelfRestartOperation::clone() const
+{
+ return new SelfRestartOperation();
+}
diff --git a/src/libs/installer/selfrestartoperation.h b/src/libs/installer/selfrestartoperation.h
new file mode 100644
index 000000000..24b9b5733
--- /dev/null
+++ b/src/libs/installer/selfrestartoperation.h
@@ -0,0 +1,54 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef SELFRESTARTOPERATION_H
+#define SELFRESTARTOPERATION_H
+
+#include "qinstallerglobal.h"
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT SelfRestartOperation : public Operation
+{
+public:
+ SelfRestartOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+};
+
+}
+
+#endif
diff --git a/src/libs/installer/setdemospathonqtoperation.cpp b/src/libs/installer/setdemospathonqtoperation.cpp
new file mode 100644
index 000000000..8f3137d55
--- /dev/null
+++ b/src/libs/installer/setdemospathonqtoperation.cpp
@@ -0,0 +1,126 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "setdemospathonqtoperation.h"
+
+#include "qtpatch.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QSettings>
+#include <QtCore/QDebug>
+
+using namespace QInstaller;
+
+SetDemosPathOnQtOperation::SetDemosPathOnQtOperation()
+{
+ setName(QLatin1String("SetDemosPathOnQt"));
+}
+
+void SetDemosPathOnQtOperation::backup()
+{
+}
+
+bool SetDemosPathOnQtOperation::performOperation()
+{
+ const QStringList args = arguments();
+
+ if (args.count() != 2) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, exactly 2 expected.").arg(name())
+ .arg(arguments().count()));
+ return false;
+ }
+
+ const QString qtDir = args.at(0);
+ QByteArray newValue = QDir::toNativeSeparators(args.at(1)).toUtf8();
+
+ QString qmakePath = qtDir + QLatin1String("/bin/qmake");
+#ifdef Q_OS_WIN
+ qmakePath = qmakePath + QLatin1String(".exe");
+#endif
+
+ QByteArray qmakeOutput;
+ QHash<QString, QByteArray> qmakeValueHash = QtPatch::qmakeValues(qmakePath, &qmakeOutput);
+
+ if (qmakeValueHash.isEmpty()) {
+ setError(UserDefinedError);
+ setErrorString(tr("The output of \n%1 -query\nis not parseable. Please file a bugreport with this "
+ "dialog https://bugreports.qt-project.org.\noutput: %2").arg(QDir::toNativeSeparators(qmakePath),
+ QString::fromUtf8(qmakeOutput)));
+ return false;
+ }
+
+ QByteArray oldValue = qmakeValueHash.value(QLatin1String("QT_INSTALL_DEMOS"));
+ bool oldQtPathFromQMakeIsEmpty = oldValue.isEmpty();
+ if (oldQtPathFromQMakeIsEmpty) {
+ qDebug() << "qpatch: warning: It was not able to get the old values from" << qmakePath;
+ }
+
+ if (255 < newValue.size()) {
+ setError(UserDefinedError);
+ setErrorString(tr("Qt patch error: new Qt demo path (%1)\nneeds to be less than 255 characters.")
+ .arg(QString::fromLocal8Bit(newValue)) );
+ return false;
+ }
+
+ QString qtConfPath = qtDir + QLatin1String("/bin/qt.conf");
+ if (QFile::exists(qtConfPath)) {
+ QSettings settings(qtConfPath, QSettings::IniFormat);
+ settings.setValue(QLatin1String("Paths/Demos"), QString::fromUtf8(newValue));
+ }
+
+ oldValue = QByteArray("qt_demopath=%1").replace("%1", oldValue);
+ newValue = QByteArray("qt_demopath=%1").replace("%1", newValue);
+
+ bool isPatched = QtPatch::patchBinaryFile(qmakePath, oldValue, newValue);
+ if (!isPatched) {
+ qDebug() << "qpatch: warning: could not patch the demo path in" << qmakePath;
+ }
+
+ return true;
+}
+
+bool SetDemosPathOnQtOperation::undoOperation()
+{
+ return true;
+}
+
+bool SetDemosPathOnQtOperation::testOperation()
+{
+ return true;
+}
+
+Operation *SetDemosPathOnQtOperation::clone() const
+{
+ return new SetDemosPathOnQtOperation();
+}
+
diff --git a/src/libs/installer/setdemospathonqtoperation.h b/src/libs/installer/setdemospathonqtoperation.h
new file mode 100644
index 000000000..c8400b9ff
--- /dev/null
+++ b/src/libs/installer/setdemospathonqtoperation.h
@@ -0,0 +1,54 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef SETDEMOSPATHONQTOPERATION_H
+#define SETDEMOSPATHONQTOPERATION_H
+
+#include "qinstallerglobal.h"
+
+namespace QInstaller {
+
+class SetDemosPathOnQtOperation : public Operation
+{
+public:
+ SetDemosPathOnQtOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+};
+
+} // namespace
+
+#endif // SETDEMOSPATHONQTOPERATION_H
diff --git a/src/libs/installer/setexamplespathonqtoperation.cpp b/src/libs/installer/setexamplespathonqtoperation.cpp
new file mode 100644
index 000000000..7b1e8e55b
--- /dev/null
+++ b/src/libs/installer/setexamplespathonqtoperation.cpp
@@ -0,0 +1,127 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "setexamplespathonqtoperation.h"
+
+#include "qtpatch.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QSettings>
+#include <QtCore/QDebug>
+
+using namespace QInstaller;
+
+SetExamplesPathOnQtOperation::SetExamplesPathOnQtOperation()
+{
+ setName(QLatin1String("SetExamplesPathOnQt"));
+}
+
+void SetExamplesPathOnQtOperation::backup()
+{
+}
+
+bool SetExamplesPathOnQtOperation::performOperation()
+{
+ const QStringList args = arguments();
+
+ if (args.count() != 2) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, exactly 2 expected.").arg(name())
+ .arg(arguments().count()));
+ return false;
+ }
+
+ const QString qtDir = args.at(0);
+ QByteArray newValue = QDir::toNativeSeparators(args.at(1)).toUtf8();
+
+ QString qmakePath = qtDir + QLatin1String("/bin/qmake");
+#ifdef Q_OS_WIN
+ qmakePath = qmakePath + QLatin1String(".exe");
+#endif
+
+ QByteArray qmakeOutput;
+ QHash<QString, QByteArray> qmakeValueHash = QtPatch::qmakeValues(qmakePath, &qmakeOutput);
+
+ if (qmakeValueHash.isEmpty()) {
+ setError(UserDefinedError);
+ setErrorString(tr("The output of \n%1 -query\nis not parseable. Please file a bugreport with this "
+ "dialog https://bugreports.qt-project.org.\noutput: %2").arg(QDir::toNativeSeparators(qmakePath),
+ QString::fromUtf8(qmakeOutput)));
+ return false;
+ }
+
+ QByteArray oldValue = qmakeValueHash.value(QLatin1String("QT_INSTALL_EXAMPLES"));
+ bool oldQtPathFromQMakeIsEmpty = oldValue.isEmpty();
+ if (oldQtPathFromQMakeIsEmpty) {
+ qDebug() << "qpatch: warning: It was not able to get the old values from" << qmakePath;
+ }
+
+ if (255 < newValue.size()) {
+ setError(UserDefinedError);
+ setErrorString(tr("Qt patch error: new Qt example path (%1)\nneeds to be less than 255 characters.")
+ .arg(QString::fromLocal8Bit(newValue)));
+ return false;
+ }
+
+ QString qtConfPath = qtDir + QLatin1String("/bin/qt.conf");
+
+ if (QFile::exists(qtConfPath)) {
+ QSettings settings(qtConfPath, QSettings::IniFormat);
+ settings.setValue( QLatin1String("Paths/Examples"), QString::fromUtf8(newValue));
+ }
+
+ oldValue = QByteArray("qt_xmplpath=%1").replace("%1", oldValue);
+ newValue = QByteArray("qt_xmplpath=%1").replace("%1", newValue);
+
+ bool isPatched = QtPatch::patchBinaryFile(qmakePath, oldValue, newValue);
+ if (!isPatched) {
+ qDebug() << "qpatch: warning: could not patch the example path in" << qmakePath;
+ }
+
+ return true;
+}
+
+bool SetExamplesPathOnQtOperation::undoOperation()
+{
+ return true;
+}
+
+bool SetExamplesPathOnQtOperation::testOperation()
+{
+ return true;
+}
+
+Operation *SetExamplesPathOnQtOperation::clone() const
+{
+ return new SetExamplesPathOnQtOperation();
+}
+
diff --git a/src/libs/installer/setexamplespathonqtoperation.h b/src/libs/installer/setexamplespathonqtoperation.h
new file mode 100644
index 000000000..96e15b206
--- /dev/null
+++ b/src/libs/installer/setexamplespathonqtoperation.h
@@ -0,0 +1,54 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef SETEXAMPLESPATHONQTOPERATION_H
+#define SETEXAMPLESPATHONQTOPERATION_H
+
+#include "qinstallerglobal.h"
+
+namespace QInstaller {
+
+class SetExamplesPathOnQtOperation : public Operation
+{
+public:
+ SetExamplesPathOnQtOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+};
+
+} // namespace
+
+#endif // SETEXAMPLESPATHONQTOPERATION_H
diff --git a/src/libs/installer/setimportspathonqtcoreoperation.cpp b/src/libs/installer/setimportspathonqtcoreoperation.cpp
new file mode 100644
index 000000000..2bfacebb3
--- /dev/null
+++ b/src/libs/installer/setimportspathonqtcoreoperation.cpp
@@ -0,0 +1,151 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "setimportspathonqtcoreoperation.h"
+
+#include "qtpatch.h"
+
+#include <QtCore/QByteArrayMatcher>
+#include <QtCore/QDir>
+#include <QtCore/QDebug>
+
+using namespace QInstaller;
+
+namespace {
+ QByteArray getOldValue(const QString &binaryPath)
+ {
+ QFileInfo fileInfo(binaryPath);
+
+ if (!fileInfo.exists()) {
+ qDebug() << QString::fromLatin1("qpatch: warning: file %1 not found").arg(binaryPath);
+ return QByteArray();
+ }
+
+
+ QFile file(binaryPath);
+ int readOpenCount = 0;
+ while (!file.open(QFile::ReadOnly) && readOpenCount < 20000) {
+ ++readOpenCount;
+ qApp->processEvents();
+ }
+ Q_ASSERT(file.isOpen());
+ if (!file.isOpen()) {
+ qDebug() << QString::fromLatin1("qpatch: warning: file %1 can not be opened as ReadOnly.").arg(
+ binaryPath);
+ qDebug() << file.errorString();
+ return QByteArray();
+ }
+
+ const QByteArray source = file.readAll();
+ file.close();
+
+ int offset = 0;
+ QByteArray searchValue("qt_impspath=");
+ QByteArrayMatcher byteArrayMatcher(searchValue);
+ offset = byteArrayMatcher.indexIn(source, offset);
+ Q_ASSERT(offset > 0);
+ if (offset == -1)
+ return QByteArray();
+
+ int stringEndPosition = offset;
+ while(source.at(stringEndPosition++) != '\0') {}
+ //after search string till the first \0 it should be our looking for QByteArray
+ return source.mid(offset + searchValue.size(), stringEndPosition - offset);
+ }
+}
+
+SetImportsPathOnQtCoreOperation::SetImportsPathOnQtCoreOperation()
+{
+ setName(QLatin1String("SetImportsPathOnQtCore"));
+}
+
+void SetImportsPathOnQtCoreOperation::backup()
+{
+}
+
+bool SetImportsPathOnQtCoreOperation::performOperation()
+{
+ const QStringList args = arguments();
+
+ if (args.count() != 2) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, exactly 2 expected.").arg(name())
+ .arg(arguments().count()));
+ return false;
+ }
+
+ const QString qtCoreLibraryDir = args.at(0);
+ const QByteArray newValue = QDir::toNativeSeparators(args.at(1)).toUtf8();
+
+ if (255 < newValue.size()) {
+ qDebug() << "qpatch: error: newQtDir needs to be less than 255 characters.";
+ return false;
+ }
+ QStringList libraryFiles;
+#ifdef Q_OS_WIN
+ libraryFiles << QString::fromLatin1("%1/QtCore4.dll").arg(qtCoreLibraryDir);
+ libraryFiles << QString::fromLatin1("%1/QtCore4d.dll").arg(qtCoreLibraryDir);
+#else
+ libraryFiles << qtCoreLibraryDir + QLatin1String("/libQtCore.so");
+#endif
+ foreach (const QString coreLibrary, libraryFiles) {
+ if (QFile::exists(coreLibrary)) {
+ QByteArray oldValue(getOldValue(coreLibrary));
+ Q_ASSERT(!oldValue.isEmpty());
+ oldValue = QByteArray("qt_impspath=%1").replace("%1", oldValue);
+ QByteArray adjutedNewValue = QByteArray("qt_impspath=%1").replace("%1", newValue);
+
+ bool isPatched = QtPatch::patchBinaryFile(coreLibrary, oldValue, adjutedNewValue);
+ if (!isPatched) {
+ qDebug() << "qpatch: warning: could not patch the imports path in" << coreLibrary;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool SetImportsPathOnQtCoreOperation::undoOperation()
+{
+ return true;
+}
+
+bool SetImportsPathOnQtCoreOperation::testOperation()
+{
+ return true;
+}
+
+Operation *SetImportsPathOnQtCoreOperation::clone() const
+{
+ return new SetImportsPathOnQtCoreOperation();
+}
+
diff --git a/src/libs/installer/setimportspathonqtcoreoperation.h b/src/libs/installer/setimportspathonqtcoreoperation.h
new file mode 100644
index 000000000..93a1290d7
--- /dev/null
+++ b/src/libs/installer/setimportspathonqtcoreoperation.h
@@ -0,0 +1,54 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef SETIMPORTSPATHONQTCOREOPERATION_H
+#define SETIMPORTSPATHONQTCOREOPERATION_H
+
+#include "qinstallerglobal.h"
+
+namespace QInstaller {
+
+class SetImportsPathOnQtCoreOperation : public Operation
+{
+public:
+ SetImportsPathOnQtCoreOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+};
+
+} // namespace
+
+#endif // SETIMPORTSPATHONQTCOREOPERATION_H
diff --git a/src/libs/installer/setpathonqtcoreoperation.cpp b/src/libs/installer/setpathonqtcoreoperation.cpp
new file mode 100644
index 000000000..80001b773
--- /dev/null
+++ b/src/libs/installer/setpathonqtcoreoperation.cpp
@@ -0,0 +1,175 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "setpathonqtcoreoperation.h"
+
+#include "qtpatch.h"
+
+#include <QtCore/QByteArrayMatcher>
+#include <QtCore/QDir>
+#include <QtCore/QDebug>
+
+using namespace QInstaller;
+
+namespace {
+ QByteArray getOldValue(const QString &binaryPath, const QByteArray &typeValue)
+ {
+ QFileInfo fileInfo(binaryPath);
+
+ if (!fileInfo.exists()) {
+ qDebug() << "qpatch: warning: file '" << binaryPath << "' not found";
+ return QByteArray();
+ }
+
+ QFile file(binaryPath);
+ int readOpenCount = 0;
+ while (!file.open(QFile::ReadOnly) && readOpenCount < 20000) {
+ ++readOpenCount;
+ qApp->processEvents();
+ }
+ Q_ASSERT(file.isOpen());
+ if (!file.isOpen()) {
+ qDebug() << "qpatch: warning: file '" << binaryPath << "' can not open as ReadOnly.";
+ qDebug() << file.errorString();
+ return QByteArray();
+ }
+
+ const QByteArray source = file.readAll();
+ file.close();
+
+ int offset = 0;
+ QByteArray searchValue = typeValue;
+ searchValue.append("=");
+ QByteArrayMatcher byteArrayMatcher(searchValue);
+ offset = byteArrayMatcher.indexIn(source, offset);
+ Q_ASSERT(offset > 0);
+ if (offset == -1)
+ return QByteArray();
+
+ int stringEndPosition = offset;
+
+ //go to the position where other data starts
+ while (source.at(stringEndPosition++) != '\0') {}
+
+ //after search string till the first \0 it should be our looking for QByteArray
+ return source.mid(offset + searchValue.size(), stringEndPosition - offset);
+ }
+}
+
+SetPathOnQtCoreOperation::SetPathOnQtCoreOperation()
+{
+ setName(QLatin1String("SetPathOnQtCore"));
+}
+
+void SetPathOnQtCoreOperation::backup()
+{
+}
+
+bool SetPathOnQtCoreOperation::performOperation()
+{
+ const QStringList args = arguments();
+
+ if (args.count() != 3) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, exactly 3 expected.").arg(name())
+ .arg(arguments().count()));
+ return false;
+ }
+
+ const QString qtCoreLibraryDir = args.at(0);
+ const QByteArray typeValue(args.at(1).toUtf8());
+ const QByteArray newValue = QDir::toNativeSeparators(args.at(2)).toUtf8();
+
+ QStringList possibleTypes;
+ possibleTypes << QLatin1String("qt_prfxpath")
+ << QLatin1String("qt_docspath")
+ << QLatin1String("qt_hdrspath")
+ << QLatin1String("qt_libspath")
+ << QLatin1String("qt_binspath")
+ << QLatin1String("qt_plugpath")
+ << QLatin1String("qt_impspath")
+ << QLatin1String("qt_datapath")
+ << QLatin1String("qt_trnspath")
+ << QLatin1String("qt_xmplpath")
+ << QLatin1String("qt_demopath");
+
+ if (!possibleTypes.contains(QString::fromUtf8(typeValue))) {
+ setError(InvalidArguments);
+ setErrorString(tr("The second type/value needs to be one of: %1").arg(possibleTypes.join(
+ QLatin1String(", "))));
+ return false;
+ }
+
+ if (255 < newValue.size()) {
+ qDebug() << "qpatch: error: newQtDir needs to be less than 255 characters.";
+ return false;
+ }
+ QStringList libraryFiles;
+#ifdef Q_OS_WIN
+ libraryFiles << QString::fromLatin1("%1/QtCore4.dll").arg(qtCoreLibraryDir);
+ libraryFiles << QString::fromLatin1("%1/QtCore4d.dll").arg(qtCoreLibraryDir);
+#else
+ libraryFiles << qtCoreLibraryDir + QLatin1String("/libQtCore.so");
+#endif
+ foreach (const QString coreLibrary, libraryFiles) {
+ if (QFile::exists(coreLibrary)) {
+ QByteArray oldValue(getOldValue(coreLibrary, typeValue));
+ Q_ASSERT(!oldValue.isEmpty());
+ oldValue = QByteArray("%0=%1").replace("%0", typeValue).replace("%1", oldValue);
+ QByteArray adjutedNewValue =
+ QByteArray("%0=%1").replace("%0", typeValue).replace("%1", newValue);
+
+ bool isPatched = QtPatch::patchBinaryFile(coreLibrary, oldValue, adjutedNewValue);
+ if (!isPatched) {
+ qDebug() << "qpatch: warning: could not patch the plugin path in" << coreLibrary;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool SetPathOnQtCoreOperation::undoOperation()
+{
+ return true;
+}
+
+bool SetPathOnQtCoreOperation::testOperation()
+{
+ return true;
+}
+
+Operation *SetPathOnQtCoreOperation::clone() const
+{
+ return new SetPathOnQtCoreOperation();
+}
+
diff --git a/src/libs/installer/setpathonqtcoreoperation.h b/src/libs/installer/setpathonqtcoreoperation.h
new file mode 100644
index 000000000..912cbf5a5
--- /dev/null
+++ b/src/libs/installer/setpathonqtcoreoperation.h
@@ -0,0 +1,54 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef SETPATHONQTCOREOPERATION_H
+#define SETPATHONQTCOREOPERATION_H
+
+#include "qinstallerglobal.h"
+
+namespace QInstaller {
+
+class SetPathOnQtCoreOperation : public Operation
+{
+public:
+ SetPathOnQtCoreOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+};
+
+} // namespace
+
+#endif // SETPATHONQTCOREOPERATION_H
diff --git a/src/libs/installer/setpluginpathonqtcoreoperation.cpp b/src/libs/installer/setpluginpathonqtcoreoperation.cpp
new file mode 100644
index 000000000..20573e9b1
--- /dev/null
+++ b/src/libs/installer/setpluginpathonqtcoreoperation.cpp
@@ -0,0 +1,149 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "setpluginpathonqtcoreoperation.h"
+
+#include "qtpatch.h"
+
+#include <QtCore/QByteArrayMatcher>
+#include <QtCore/QDir>
+#include <QtCore/QDebug>
+
+using namespace QInstaller;
+
+namespace {
+ QByteArray getOldValue(const QString &binaryPath)
+ {
+ QFileInfo fileInfo(binaryPath);
+
+ if (!fileInfo.exists()) {
+ qDebug() << QString::fromLatin1("qpatch: warning: file '%1' not found").arg(binaryPath);
+ return QByteArray();
+ }
+
+ QFile file(binaryPath);
+ int readOpenCount = 0;
+ while (!file.open(QFile::ReadOnly) && readOpenCount < 20000) {
+ ++readOpenCount;
+ qApp->processEvents();
+ }
+ Q_ASSERT(file.isOpen());
+ if (!file.isOpen()) {
+ qDebug() << QString::fromLatin1("qpatch: warning: file '%1' can not open as ReadOnly.").arg(
+ binaryPath);
+ qDebug() << file.errorString();
+ return QByteArray();
+ }
+
+ const QByteArray source = file.readAll();
+ file.close();
+
+ int offset = 0;
+ QByteArray searchValue("qt_plugpath=");
+ QByteArrayMatcher byteArrayMatcher(searchValue);
+ offset = byteArrayMatcher.indexIn(source, offset);
+ Q_ASSERT(offset > 0);
+ if (offset == -1)
+ return QByteArray();
+
+ int stringEndPosition = offset;
+ while(source.at(stringEndPosition++) != '\0') {}
+ //after search string till the first \0 it should be our looking for QByteArray
+ return source.mid(offset + searchValue.size(), stringEndPosition - offset);
+ }
+}
+
+SetPluginPathOnQtCoreOperation::SetPluginPathOnQtCoreOperation()
+{
+ setName(QLatin1String("SetPluginPathOnQtCore"));
+}
+
+void SetPluginPathOnQtCoreOperation::backup()
+{
+}
+
+bool SetPluginPathOnQtCoreOperation::performOperation()
+{
+ const QStringList args = arguments();
+
+ if (args.count() != 2) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, exactly 2 expected.").arg(name())
+ .arg(arguments().count()));
+ return false;
+ }
+
+ const QString qtCoreLibraryDir = args.at(0);
+ const QByteArray newValue = QDir::toNativeSeparators(args.at(1)).toUtf8();
+
+ if (255 < newValue.size()) {
+ qDebug() << "qpatch: error: newQtDir needs to be less than 255 characters.";
+ return false;
+ }
+ QStringList libraryFiles;
+#ifdef Q_OS_WIN
+ libraryFiles << QString::fromLatin1("%1/QtCore4.dll").arg(qtCoreLibraryDir);
+ libraryFiles << QString::fromLatin1("%1/QtCore4d.dll").arg(qtCoreLibraryDir);
+#else
+ libraryFiles << qtCoreLibraryDir + QLatin1String("/libQtCore.so");
+#endif
+ foreach (const QString &coreLibrary, libraryFiles) {
+ if (QFile::exists(coreLibrary)) {
+ QByteArray oldValue(getOldValue(coreLibrary));
+ Q_ASSERT(!oldValue.isEmpty());
+ oldValue = QByteArray("qt_plugpath=%1").replace("%1", oldValue);
+ QByteArray adjutedNewValue = QByteArray("qt_plugpath=%1").replace("%1", newValue);
+
+ bool isPatched = QtPatch::patchBinaryFile(coreLibrary, oldValue, adjutedNewValue);
+ if (!isPatched)
+ qDebug() << "qpatch: warning: could not patch the plugin path in" << coreLibrary;
+ }
+ }
+
+ return true;
+}
+
+bool SetPluginPathOnQtCoreOperation::undoOperation()
+{
+ return true;
+}
+
+bool SetPluginPathOnQtCoreOperation::testOperation()
+{
+ return true;
+}
+
+Operation *SetPluginPathOnQtCoreOperation::clone() const
+{
+ return new SetPluginPathOnQtCoreOperation();
+}
+
diff --git a/src/libs/installer/setpluginpathonqtcoreoperation.h b/src/libs/installer/setpluginpathonqtcoreoperation.h
new file mode 100644
index 000000000..78121e01d
--- /dev/null
+++ b/src/libs/installer/setpluginpathonqtcoreoperation.h
@@ -0,0 +1,54 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef SETPLUGINPATHONQTCOREOPERATION_H
+#define SETPLUGINPATHONQTCOREOPERATION_H
+
+#include "qinstallerglobal.h"
+
+namespace QInstaller {
+
+class SetPluginPathOnQtCoreOperation : public Operation
+{
+public:
+ SetPluginPathOnQtCoreOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+};
+
+} // namespace
+
+#endif // SETPLUGINPATHONQTCOREOPERATION_H
diff --git a/src/libs/installer/setqtcreatorvalueoperation.cpp b/src/libs/installer/setqtcreatorvalueoperation.cpp
new file mode 100644
index 000000000..a63c46e58
--- /dev/null
+++ b/src/libs/installer/setqtcreatorvalueoperation.cpp
@@ -0,0 +1,137 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "setqtcreatorvalueoperation.h"
+
+#include "qtcreator_constants.h"
+#include "updatecreatorsettingsfrom21to22operation.h"
+#include "packagemanagercore.h"
+
+#include <QtCore/QSettings>
+
+using namespace QInstaller;
+
+static QString groupName(const QString &groupName)
+{
+ return groupName == QLatin1String("General") ? QString() : groupName;
+}
+
+SetQtCreatorValueOperation::SetQtCreatorValueOperation()
+{
+ setName(QLatin1String("SetQtCreatorValue"));
+}
+
+void SetQtCreatorValueOperation::backup()
+{
+}
+
+bool SetQtCreatorValueOperation::performOperation()
+{
+ const QStringList args = arguments();
+
+ if (args.count() != 4) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, exactly 4 expected (rootInstallPath, "
+ "group, key, value).").arg(name()).arg( arguments().count()));
+ return false;
+ }
+
+ const QString &rootInstallPath = args.at(0); //for example "C:\\Nokia_SDK\\"
+
+ const QString &group = groupName(args.at(1));
+ const QString &key = args.at(2);
+ const QString &settingsValue = args.at(3);
+{
+ QSettings settings(rootInstallPath + QLatin1String(QtCreatorSettingsSuffixPath), QSettings::IniFormat);
+ if (!group.isEmpty())
+ settings.beginGroup(group);
+
+ if (settingsValue.contains(QLatin1String(","))) // comma separated list of strings
+ settings.setValue(key, settingsValue.split(QRegExp(QLatin1String("\\s*,\\s*")), QString::SkipEmptyParts));
+ else
+ settings.setValue(key, settingsValue);
+
+ if (!group.isEmpty())
+ settings.endGroup();
+
+ settings.sync(); //be save ;)
+} //destruct QSettings
+
+ if (group == QLatin1String("GdbBinaries21")) {
+ PackageManagerCore *const core = qVariantValue<PackageManagerCore*>(value(QLatin1String("installer")));
+ if (!core) {
+ setError(UserDefinedError);
+ setErrorString(tr("Needed installer object in %1 operation is empty.").arg(name()));
+ return false;
+ }
+ UpdateCreatorSettingsFrom21To22Operation updateCreatorSettingsOperation;
+ updateCreatorSettingsOperation.setValue(QLatin1String("installer"), QVariant::fromValue(core));
+ if (!updateCreatorSettingsOperation.performOperation()) {
+ setError(updateCreatorSettingsOperation.error());
+ setErrorString(updateCreatorSettingsOperation.errorString());
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool SetQtCreatorValueOperation::undoOperation()
+{
+ const QStringList args = arguments();
+
+ const QString &rootInstallPath = args.at(0); //for example "C:\\Nokia_SDK\\"
+
+ const QString &group = groupName(args.at(1));
+ const QString &key = args.at(2);
+
+ QSettings settings(rootInstallPath + QLatin1String(QtCreatorSettingsSuffixPath), QSettings::IniFormat);
+ if (!group.isEmpty())
+ settings.beginGroup(group);
+
+ settings.remove(key);
+
+ if (!group.isEmpty())
+ settings.endGroup();
+
+ return true;
+}
+
+bool SetQtCreatorValueOperation::testOperation()
+{
+ return true;
+}
+
+Operation *SetQtCreatorValueOperation::clone() const
+{
+ return new SetQtCreatorValueOperation();
+}
diff --git a/src/libs/installer/setqtcreatorvalueoperation.h b/src/libs/installer/setqtcreatorvalueoperation.h
new file mode 100644
index 000000000..88e104ee0
--- /dev/null
+++ b/src/libs/installer/setqtcreatorvalueoperation.h
@@ -0,0 +1,54 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef SETQTCREATORVALUEOPERATION_H
+#define SETQTCREATORVALUEOPERATION_H
+
+#include "qinstallerglobal.h"
+
+namespace QInstaller {
+
+class SetQtCreatorValueOperation : public Operation
+{
+public:
+ SetQtCreatorValueOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+};
+
+} // namespace
+
+#endif // SETQTCREATORVALUEOPERATION_H
diff --git a/src/libs/installer/settings.cpp b/src/libs/installer/settings.cpp
new file mode 100644
index 000000000..ace61761e
--- /dev/null
+++ b/src/libs/installer/settings.cpp
@@ -0,0 +1,523 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#include "settings.h"
+
+#include "errors.h"
+#include "qinstallerglobal.h"
+#include "repository.h"
+
+#include <QtCore/QFileInfo>
+#include <QtCore/QStringList>
+
+#include <QtXml/QXmlStreamReader>
+
+using namespace QInstaller;
+
+static const QLatin1String scIcon("Icon");
+static const QLatin1String scLogo("Logo");
+static const QLatin1String scPages("Pages");
+static const QLatin1String scPrefix("Prefix");
+static const QLatin1String scLogoSmall("LogoSmall");
+static const QLatin1String scWatermark("Watermark");
+static const QLatin1String scProductUrl("ProductUrl");
+static const QLatin1String scBackground("Background");
+static const QLatin1String scAdminTargetDir("AdminTargetDir");
+static const QLatin1String scUninstallerName("UninstallerName");
+static const QLatin1String scUserRepositories("UserRepositories");
+static const QLatin1String scTmpRepositories("TemporaryRepositories");
+static const QLatin1String scUninstallerIniFile("UninstallerIniFile");
+static const QLatin1String scRemoteRepositories("RemoteRepositories");
+static const QLatin1String scSigningCertificate("SigningCertificate");
+
+static const QLatin1String scFtpProxy("FtpProxy");
+static const QLatin1String scHttpProxy("HttpProxy");
+static const QLatin1String scProxyType("ProxyType");
+
+template <typename T>
+static QSet<T> variantListToSet(const QVariantList &list)
+{
+ QSet<T> set;
+ foreach (const QVariant &variant, list)
+ set.insert(variant.value<T>());
+ return set;
+}
+
+static QSet<Repository> readRepositories(QXmlStreamReader &reader, bool isDefault)
+{
+ QSet<Repository> set;
+ while (reader.readNextStartElement()) {
+ if (reader.name() == QLatin1String("Repository")) {
+ Repository repo(QString(), isDefault);
+ while (reader.readNextStartElement()) {
+ if (reader.name() == QLatin1String("Url"))
+ repo.setUrl(reader.readElementText());
+ else if (reader.name() == QLatin1String("Username"))
+ repo.setUsername(reader.readElementText());
+ else if (reader.name() == QLatin1String("Password"))
+ repo.setPassword(reader.readElementText());
+ else if (reader.name() == QLatin1String("Enabled"))
+ repo.setEnabled(bool(reader.readElementText().toInt()));
+ else
+ reader.skipCurrentElement();
+ }
+ set.insert(repo);
+ } else {
+ reader.skipCurrentElement();
+ }
+ }
+ return set;
+}
+
+static QVariantHash readTitles(QXmlStreamReader &reader)
+{
+ QVariantHash hash;
+ while (reader.readNextStartElement())
+ hash.insert(reader.name().toString(), reader.readElementText(QXmlStreamReader::SkipChildElements));
+ return hash;
+}
+
+static QHash<QString, QVariantHash> readPages(QXmlStreamReader &reader)
+{
+ QHash<QString, QVariantHash> hash;
+ while (reader.readNextStartElement()) {
+ if (reader.name() == QLatin1String("Page")) {
+ QVariantHash pageElements;
+ QString pageName = reader.attributes().value(QLatin1String("name")).toString();
+ while (reader.readNextStartElement()) {
+ const QString name = reader.name().toString();
+ if (name == QLatin1String("Title") || name == QLatin1String("SubTitle")) {
+ pageElements.insert(name, readTitles(reader));
+ } else {
+ pageElements.insert(name, reader.readElementText(QXmlStreamReader::SkipChildElements));
+ }
+ }
+ hash.insert(pageName, pageElements);
+ }
+ }
+ return hash;
+}
+
+
+// -- Settings::Private
+
+class Settings::Private : public QSharedData
+{
+public:
+ Private()
+ : m_replacementRepos(false)
+ {}
+
+ QVariantHash m_data;
+ bool m_replacementRepos;
+
+ QString makeAbsolutePath(const QString &path) const
+ {
+ if (QFileInfo(path).isAbsolute())
+ return path;
+ return m_data.value(scPrefix).toString() + QLatin1String("/") + path;
+ }
+};
+
+
+// -- Settings
+
+Settings::Settings()
+ : d(new Private)
+{
+}
+
+Settings::~Settings()
+{
+}
+
+Settings::Settings(const Settings &other)
+ : d(other.d)
+{
+}
+
+Settings& Settings::operator=(const Settings &other)
+{
+ Settings copy(other);
+ std::swap(d, copy.d);
+ return *this;
+}
+
+/* static */
+Settings Settings::fromFileAndPrefix(const QString &path, const QString &prefix)
+{
+ QFile file(path);
+ QFile overrideConfig(QLatin1String(":/overrideconfig.xml"));
+
+ if (overrideConfig.exists())
+ file.setFileName(overrideConfig.fileName());
+
+ if (!file.open(QIODevice::ReadOnly))
+ throw Error(tr("Could not open settings file %1 for reading: %2").arg(path, file.errorString()));
+
+ QXmlStreamReader reader(&file);
+ if (reader.readNextStartElement()) {
+ if (reader.name() != QLatin1String("Installer"))
+ throw Error(tr("%1 is not valid: Installer root node expected.").arg(path));
+ }
+
+ QStringList blackList;
+ blackList << scRemoteRepositories << scSigningCertificate << scPages;
+
+ Settings s;
+ s.d->m_data.insert(scPrefix, prefix);
+ while (reader.readNextStartElement()) {
+ const QString name = reader.name().toString();
+ if (blackList.contains(name)) {
+ if (name == scSigningCertificate)
+ s.d->m_data.insertMulti(name, s.d->makeAbsolutePath(reader.readElementText()));
+
+ if (name == scRemoteRepositories)
+ s.addDefaultRepositories(readRepositories(reader, true));
+
+ if (name == scPages) {
+ QHash<QString, QVariantHash> pages = readPages(reader);
+ const QStringList &keys = pages.keys();
+ foreach (const QString &key, keys)
+ s.d->m_data.insert(key, pages.value(key));
+ }
+ } else {
+ if (s.d->m_data.contains(name))
+ throw Error(tr("Multiple %1 elements found, but only one allowed.").arg(name));
+ s.d->m_data.insert(name, reader.readElementText(QXmlStreamReader::SkipChildElements));
+ }
+ }
+
+ if (reader.error() != QXmlStreamReader::NoError) {
+ throw Error(QString::fromLatin1("Xml parse error in %1: %2 Line: %3, Column: %4").arg(path)
+ .arg(reader.errorString()).arg(reader.lineNumber()).arg(reader.columnNumber()));
+ }
+
+ // Add some possible missing values
+ if (!s.d->m_data.contains(scRemoveTargetDir))
+ s.d->m_data.insert(scRemoveTargetDir, scTrue);
+ if (!s.d->m_data.contains(scUninstallerName))
+ s.d->m_data.insert(scUninstallerName, QLatin1String("uninstall"));
+ if (!s.d->m_data.contains(scTargetConfigurationFile))
+ s.d->m_data.insert(scTargetConfigurationFile, QLatin1String("components.xml"));
+ if (!s.d->m_data.contains(scUninstallerIniFile))
+ s.d->m_data.insert(scUninstallerIniFile, s.uninstallerName() + QLatin1String(".ini"));
+
+ return s;
+}
+
+QString Settings::logo() const
+{
+ return d->makeAbsolutePath(d->m_data.value(scLogo).toString());
+}
+
+QString Settings::logoSmall() const
+{
+ return d->makeAbsolutePath(d->m_data.value(scLogoSmall).toString());
+}
+
+QString Settings::title() const
+{
+ return d->m_data.value(scTitle).toString();
+}
+
+QString Settings::applicationName() const
+{
+ return d->m_data.value(scName).toString();
+}
+
+QString Settings::applicationVersion() const
+{
+ return d->m_data.value(scVersion).toString();
+}
+
+QString Settings::publisher() const
+{
+ return d->m_data.value(scPublisher).toString();
+}
+
+QString Settings::url() const
+{
+ return d->m_data.value(scProductUrl).toString();
+}
+
+QString Settings::watermark() const
+{
+ return d->makeAbsolutePath(d->m_data.value(scWatermark).toString());
+}
+
+QString Settings::background() const
+{
+ return d->makeAbsolutePath(d->m_data.value(scBackground).toString());
+}
+
+QString Settings::icon() const
+{
+ const QString icon = d->makeAbsolutePath(d->m_data.value(scIcon).toString());
+#if defined(Q_WS_MAC)
+ return icon + QLatin1String(".icns");
+#elif defined(Q_WS_WIN)
+ return icon + QLatin1String(".ico");
+#endif
+ return icon + QLatin1String(".png");
+}
+
+QString Settings::removeTargetDir() const
+{
+ return d->m_data.value(scRemoveTargetDir).toString();
+}
+
+QString Settings::uninstallerName() const
+{
+ return d->m_data.value(scUninstallerName).toString();
+}
+
+QString Settings::uninstallerIniFile() const
+{
+ return d->m_data.value(scUninstallerIniFile).toString();
+}
+
+QString Settings::runProgram() const
+{
+ return d->m_data.value(scRunProgram).toString();
+}
+
+QString Settings::runProgramDescription() const
+{
+ return d->m_data.value(scRunProgramDescription).toString();
+}
+
+QString Settings::startMenuDir() const
+{
+ return d->m_data.value(scStartMenuDir).toString();
+}
+
+QString Settings::targetDir() const
+{
+ return d->m_data.value(scTargetDir).toString();
+}
+
+QString Settings::adminTargetDir() const
+{
+ return d->m_data.value(scAdminTargetDir).toString();
+}
+
+QString Settings::configurationFileName() const
+{
+ return d->m_data.value(scTargetConfigurationFile).toString();
+}
+
+QStringList Settings::certificateFiles() const
+{
+ return d->m_data.value(scSigningCertificate).toStringList();
+}
+
+bool Settings::allowNoneAsciiCharacters() const
+{
+ return d->m_data.value(scAllowNonAsciiCharacters).toBool();
+}
+
+bool Settings::hasReplacementRepos() const
+{
+ return d->m_replacementRepos;
+}
+
+QSet<Repository> Settings::repositories() const
+{
+ if (d->m_replacementRepos)
+ return variantListToSet<Repository>(d->m_data.values(scTmpRepositories));
+
+ return variantListToSet<Repository>(d->m_data.values(scRepositories)
+ + d->m_data.values(scUserRepositories) + d->m_data.values(scTmpRepositories));
+}
+
+QSet<Repository> Settings::defaultRepositories() const
+{
+ return variantListToSet<Repository>(d->m_data.values(scRepositories));
+}
+
+void Settings::setDefaultRepositories(const QSet<Repository> &repositories)
+{
+ d->m_data.remove(scRepositories);
+ addDefaultRepositories(repositories);
+}
+
+void Settings::addDefaultRepositories(const QSet<Repository> &repositories)
+{
+ foreach (const Repository &repository, repositories)
+ d->m_data.insertMulti(scRepositories, QVariant().fromValue(repository));
+}
+
+Settings::Update
+Settings::updateDefaultRepositories(const QHash<QString, QPair<Repository, Repository> > &updates)
+{
+ if (updates.isEmpty())
+ return Settings::NoUpdatesApplied;
+
+ QHash <QUrl, Repository> defaultRepos;
+ foreach (const QVariant &variant, d->m_data.values(scRepositories)) {
+ const Repository repository = variant.value<Repository>();
+ defaultRepos.insert(repository.url(), repository);
+ }
+
+ bool update = false;
+ QList<QPair<Repository, Repository> > values = updates.values(QLatin1String("replace"));
+ for (int a = 0; a < values.count(); ++a) {
+ const QPair<Repository, Repository> data = values.at(a);
+ if (defaultRepos.contains(data.second.url())) {
+ update = true;
+ defaultRepos.remove(data.second.url());
+ defaultRepos.insert(data.first.url(), data.first);
+ }
+ }
+
+ values = updates.values(QLatin1String("remove"));
+ for (int a = 0; a < values.count(); ++a) {
+ const QPair<Repository, Repository> data = values.at(a);
+ if (defaultRepos.contains(data.first.url())) {
+ update = true;
+ defaultRepos.remove(data.first.url());
+ }
+ }
+
+ values = updates.values(QLatin1String("add"));
+ for (int a = 0; a < values.count(); ++a) {
+ const QPair<Repository, Repository> data = values.at(a);
+ if (!defaultRepos.contains(data.first.url())) {
+ update = true;
+ defaultRepos.insert(data.first.url(), data.first);
+ }
+ }
+
+ if (update)
+ setDefaultRepositories(defaultRepos.values().toSet());
+ return update ? Settings::UpdatesApplied : Settings::NoUpdatesApplied;
+}
+
+QSet<Repository> Settings::temporaryRepositories() const
+{
+ return variantListToSet<Repository>(d->m_data.values(scTmpRepositories));
+}
+
+void Settings::setTemporaryRepositories(const QSet<Repository> &repositories, bool replace)
+{
+ d->m_data.remove(scTmpRepositories);
+ addTemporaryRepositories(repositories, replace);
+}
+
+void Settings::addTemporaryRepositories(const QSet<Repository> &repositories, bool replace)
+{
+ d->m_replacementRepos = replace;
+ foreach (const Repository &repository, repositories)
+ d->m_data.insertMulti(scTmpRepositories, QVariant().fromValue(repository));
+}
+
+QSet<Repository> Settings::userRepositories() const
+{
+ return variantListToSet<Repository>(d->m_data.values(scUserRepositories));
+}
+
+void Settings::setUserRepositories(const QSet<Repository> &repositories)
+{
+ d->m_data.remove(scUserRepositories);
+ addUserRepositories(repositories);
+}
+
+void Settings::addUserRepositories(const QSet<Repository> &repositories)
+{
+ foreach (const Repository &repository, repositories)
+ d->m_data.insertMulti(scUserRepositories, QVariant().fromValue(repository));
+}
+
+QVariant Settings::value(const QString &key, const QVariant &defaultValue) const
+{
+ return d->m_data.value(key, defaultValue);
+}
+
+QVariantList Settings::values(const QString &key, const QVariantList &defaultValue) const
+{
+ QVariantList list = d->m_data.values(key);
+ return list.isEmpty() ? defaultValue : list;
+}
+
+QVariantHash Settings::titlesForPage(const QString &pageName) const
+{
+ const QVariantHash hash = d->m_data.value(pageName).toHash();
+ const QVariant variant = hash.value(QLatin1String("Title"), QVariant());
+ if (!variant.canConvert<QVariantHash>())
+ return QVariantHash();
+ return variant.value<QVariantHash>();
+}
+
+QVariantHash Settings::subTitlesForPage(const QString &pageName) const
+{
+ const QVariantHash hash = d->m_data.value(pageName).toHash();
+ const QVariant variant = hash.value(QLatin1String("SubTitle"), QVariant());
+ if (!variant.canConvert<QVariantHash>())
+ return QVariantHash();
+ return variant.value<QVariantHash>();
+}
+
+Settings::ProxyType Settings::proxyType() const
+{
+ return Settings::ProxyType(d->m_data.value(scProxyType, Settings::NoProxy).toInt());
+}
+
+void Settings::setProxyType(Settings::ProxyType type)
+{
+ d->m_data.insert(scProxyType, type);
+}
+
+QNetworkProxy Settings::ftpProxy() const
+{
+ const QVariant variant = d->m_data.value(scFtpProxy);
+ if (variant.canConvert<QNetworkProxy>())
+ return variant.value<QNetworkProxy>();
+ return QNetworkProxy();
+}
+
+void Settings::setFtpProxy(const QNetworkProxy &proxy)
+{
+ d->m_data.insert(scFtpProxy, QVariant::fromValue(proxy));
+}
+
+QNetworkProxy Settings::httpProxy() const
+{
+ const QVariant variant = d->m_data.value(scHttpProxy);
+ if (variant.canConvert<QNetworkProxy>())
+ return variant.value<QNetworkProxy>();
+ return QNetworkProxy();
+}
+
+void Settings::setHttpProxy(const QNetworkProxy &proxy)
+{
+ d->m_data.insert(scHttpProxy, QVariant::fromValue(proxy));
+}
diff --git a/src/libs/installer/settings.h b/src/libs/installer/settings.h
new file mode 100644
index 000000000..5fb60a7c9
--- /dev/null
+++ b/src/libs/installer/settings.h
@@ -0,0 +1,141 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef SETTINGS_H
+#define SETTINGS_H
+
+#include "constants.h"
+#include "installer_global.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QSharedDataPointer>
+#include <QtCore/QVariant>
+
+#include <QtNetwork/QNetworkProxy>
+
+Q_DECLARE_METATYPE(QNetworkProxy)
+
+namespace QInstaller {
+class Repository;
+
+class INSTALLER_EXPORT Settings
+{
+ Q_DECLARE_TR_FUNCTIONS(Settings)
+
+public:
+ enum Update {
+ UpdatesApplied,
+ NoUpdatesApplied
+ };
+
+ enum ProxyType {
+ NoProxy,
+ SystemProxy,
+ UserDefinedProxy
+ };
+
+ explicit Settings();
+ ~Settings();
+
+ Settings(const Settings &other);
+ Settings &operator=(const Settings &other);
+
+ static Settings fromFileAndPrefix(const QString &path, const QString &prefix);
+
+ QString logo() const;
+ QString logoSmall() const;
+ QString title() const;
+ QString publisher() const;
+ QString url() const;
+ QString watermark() const;
+ QString background() const;
+ QString icon() const;
+
+ QString applicationName() const;
+ QString applicationVersion() const;
+
+ QString runProgram() const;
+ QString runProgramDescription() const;
+ QString startMenuDir() const;
+ QString targetDir() const;
+ QString adminTargetDir() const;
+
+ QString removeTargetDir() const;
+ QString uninstallerName() const;
+ QString uninstallerIniFile() const;
+
+ QString configurationFileName() const;
+
+ bool hasReplacementRepos() const;
+ QSet<Repository> repositories() const;
+
+ QSet<Repository> defaultRepositories() const;
+ void setDefaultRepositories(const QSet<Repository> &repositories);
+ void addDefaultRepositories(const QSet<Repository> &repositories);
+ Settings::Update updateDefaultRepositories(const QHash<QString, QPair<Repository, Repository> > &updates);
+
+ QSet<Repository> temporaryRepositories() const;
+ void setTemporaryRepositories(const QSet<Repository> &repositories, bool replace);
+ void addTemporaryRepositories(const QSet<Repository> &repositories, bool replace);
+
+ QSet<Repository> userRepositories() const;
+ void setUserRepositories(const QSet<Repository> &repositories);
+ void addUserRepositories(const QSet<Repository> &repositories);
+
+ QStringList certificateFiles() const;
+ bool allowNoneAsciiCharacters() const;
+
+ QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
+ QVariantList values(const QString &key, const QVariantList &defaultValue = QVariantList()) const;
+
+ QVariantHash titlesForPage(const QString &pageName) const;
+ QVariantHash subTitlesForPage(const QString &pageName) const;
+
+ Settings::ProxyType proxyType() const;
+ void setProxyType(Settings::ProxyType type);
+
+ QNetworkProxy ftpProxy() const;
+ void setFtpProxy(const QNetworkProxy &proxy);
+
+ QNetworkProxy httpProxy() const;
+ void setHttpProxy(const QNetworkProxy &proxy);
+
+private:
+ class Private;
+ QSharedDataPointer<Private> d;
+};
+
+}
+
+Q_DECLARE_METATYPE(QInstaller::Settings)
+
+#endif // SETTINGS_H
diff --git a/src/libs/installer/simplemovefileoperation.cpp b/src/libs/installer/simplemovefileoperation.cpp
new file mode 100644
index 000000000..f99915600
--- /dev/null
+++ b/src/libs/installer/simplemovefileoperation.cpp
@@ -0,0 +1,113 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "simplemovefileoperation.h"
+
+#include <QtCore/QFileInfo>
+
+namespace QInstaller {
+
+SimpleMoveFileOperation::SimpleMoveFileOperation()
+{
+ setName(QLatin1String("SimpleMoveFile"));
+}
+
+void SimpleMoveFileOperation::backup()
+{
+}
+
+bool SimpleMoveFileOperation::performOperation()
+{
+ const QStringList args = arguments();
+ if (args.count() != 2) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, exactly 2 expected.").arg(name())
+ .arg(args.count()));
+ return false;
+ }
+
+ const QString source = args.at(0);
+ const QString target = args.at(1);
+
+ if (source.isEmpty() || target.isEmpty()) {
+ setError(UserDefinedError);
+ setErrorString(tr("None of the arguments can be empty: source(%1), target(%2).")
+ .arg(source, target));
+ return false;
+ }
+
+ // If destination file exists, then we cannot use QFile::copy() because it does not overwrite an existing
+ // file. So we remove the destination file.
+ QFile file(target);
+ if (file.exists()) {
+ if (!file.remove()) {
+ setError(UserDefinedError);
+ setErrorString(tr("Can not copy source(%1) to target(%2), because target exists and is "
+ "not removable.").arg(source, target));
+ return false;
+ }
+ }
+
+ file.setFileName(source);
+ if (!file.rename(target)) {
+ setError(UserDefinedError);
+ setErrorString(tr("Can not move source(%1) to target(%2): %3").arg(source, target,
+ file.errorString()));
+ return false;
+ }
+
+ emit outputTextChanged(tr("Move %1 to %2.").arg(source, target));
+ return true;
+}
+
+bool SimpleMoveFileOperation::undoOperation()
+{
+ const QString source = arguments().at(0);
+ const QString target = arguments().at(1);
+
+ QFile(target).rename(source);
+ emit outputTextChanged(tr("Move %1 to %2.").arg(target, source));
+
+ return true;
+}
+
+bool SimpleMoveFileOperation::testOperation()
+{
+ return true;
+}
+
+Operation *SimpleMoveFileOperation::clone() const
+{
+ return new SimpleMoveFileOperation();
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/simplemovefileoperation.h b/src/libs/installer/simplemovefileoperation.h
new file mode 100644
index 000000000..97b3391ed
--- /dev/null
+++ b/src/libs/installer/simplemovefileoperation.h
@@ -0,0 +1,61 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef SIMPLEMOVEFILEOPERATION_H
+#define SIMPLEMOVEFILEOPERATION_H
+
+#include "qinstallerglobal.h"
+
+#include <QtCore/QObject>
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT SimpleMoveFileOperation : public QObject, public Operation
+{
+ Q_OBJECT
+
+public:
+ SimpleMoveFileOperation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+
+Q_SIGNALS:
+ void outputTextChanged(const QString &progress);
+};
+
+}
+
+#endif //SIMPLEMOVEFILEOPERATION_H
diff --git a/src/libs/installer/templates.cpp b/src/libs/installer/templates.cpp
new file mode 100644
index 000000000..90eed17e3
--- /dev/null
+++ b/src/libs/installer/templates.cpp
@@ -0,0 +1,176 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include<QtCore/QIODevice>
+
+template<typename T>
+QDataStream &operator>>(QDataStream &stream, T &state)
+{
+ int s;
+ stream >> s;
+ state = static_cast<T> (s);
+ return stream;
+}
+
+template<typename UNUSED>
+void callRemoteVoidMethod(QDataStream &stream, const QString &name)
+{
+ stream.device()->readAll();
+ stream << name;
+ stream.device()->waitForBytesWritten(-1);
+ if (!stream.device()->bytesAvailable())
+ stream.device()->waitForReadyRead(-1);
+ quint32 test;
+ stream >> test;
+ stream.device()->readAll();
+ return;
+}
+
+template<typename T>
+void callRemoteVoidMethod(QDataStream & stream, const QString &name, const T &param1)
+{
+ stream.device()->readAll();
+ stream << name;
+ stream << param1;
+ stream.device()->waitForBytesWritten(-1);
+ if (!stream.device()->bytesAvailable())
+ stream.device()->waitForReadyRead(-1);
+ quint32 test;
+ stream >> test;
+ stream.device()->readAll();
+ return;
+}
+
+template<typename T1, typename T2>
+void callRemoteVoidMethod(QDataStream &stream, const QString &name, const T1 &param1, const T2 &param2)
+{
+ stream.device()->readAll();
+ stream << name;
+ stream << param1;
+ stream << param2;
+ stream.device()->waitForBytesWritten(-1);
+ if (!stream.device()->bytesAvailable())
+ stream.device()->waitForReadyRead(-1);
+ quint32 test;
+ stream >> test;
+ stream.device()->readAll();
+ return;
+}
+
+template<typename T1, typename T2, typename T3>
+void callRemoteVoidMethod(QDataStream &stream, const QString &name, const T1 &param1, const T2 &param2,
+ const T3 & param3)
+{
+ stream.device()->readAll();
+ stream << name;
+ stream << param1;
+ stream << param2;
+ stream << param3;
+ stream.device()->waitForBytesWritten(-1);
+ if (!stream.device()->bytesAvailable())
+ stream.device()->waitForReadyRead(-1);
+ quint32 test;
+ stream >> test;
+ stream.device()->readAll();
+ return;
+}
+
+template<typename RESULT>
+RESULT callRemoteMethod(QDataStream &stream, const QString &name)
+{
+ stream.device()->readAll();
+ stream << name;
+ stream.device()->waitForBytesWritten(-1);
+ if (!stream.device()->bytesAvailable())
+ stream.device()->waitForReadyRead(-1);
+ quint32 test;
+ stream >> test;
+ RESULT result;
+ stream >> result;
+ stream.device()->readAll();
+ return result;
+}
+
+template<typename RESULT, typename T>
+RESULT callRemoteMethod(QDataStream &stream, const QString &name, const T &param1)
+{
+ stream.device()->readAll();
+ stream << name;
+ stream << param1;
+ stream.device()->waitForBytesWritten(-1);
+ if (!stream.device()->bytesAvailable())
+ stream.device()->waitForReadyRead(-1);
+ quint32 test;
+ stream >> test;
+ RESULT result;
+ stream >> result;
+ stream.device()->readAll();
+ return result;
+}
+
+template<typename RESULT, typename T1, typename T2>
+RESULT callRemoteMethod(QDataStream &stream, const QString &name, const T1 & param1, const T2 &param2)
+{
+ stream.device()->readAll();
+ stream << name;
+ stream << param1;
+ stream << param2;
+ stream.device()->waitForBytesWritten(-1);
+ if (!stream.device()->bytesAvailable())
+ stream.device()->waitForReadyRead(-1);
+ quint32 test;
+ stream >> test;
+ RESULT result;
+ stream >> result;
+ stream.device()->readAll();
+ return result;
+}
+
+template<typename RESULT, typename T1, typename T2, typename T3>
+RESULT callRemoteMethod(QDataStream &stream, const QString &name, const T1 &param1, const T2 &param2,
+ const T3 &param3)
+{
+ stream.device()->readAll();
+ stream << name;
+ stream << param1;
+ stream << param2;
+ stream << param3;
+ stream.device()->waitForBytesWritten(-1);
+ if (!stream.device()->bytesAvailable())
+ stream.device()->waitForReadyRead(-1);
+ quint32 test;
+ stream >> test;
+ RESULT result;
+ stream >> result;
+ stream.device()->readAll();
+ return result;
+}
diff --git a/src/libs/installer/updatecreatorsettingsfrom21to22operation.cpp b/src/libs/installer/updatecreatorsettingsfrom21to22operation.cpp
new file mode 100644
index 000000000..480060496
--- /dev/null
+++ b/src/libs/installer/updatecreatorsettingsfrom21to22operation.cpp
@@ -0,0 +1,325 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "updatecreatorsettingsfrom21to22operation.h"
+
+#include "constants.h"
+#include "registerdefaultdebuggeroperation.h"
+#include "registertoolchainoperation.h"
+#include "qtcreatorpersistentsettings.h"
+#include "packagemanagercore.h"
+#include "qtcreator_constants.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+#include <QtCore/QSettings>
+#include <QtCore/QString>
+
+using namespace QInstaller;
+
+using namespace ProjectExplorer;
+
+QStringList getQmakePathesOfAllInstallerRegisteredQtVersions(const QSettings &settings)
+{
+ QStringList qmakePathes;
+
+ QStringList oldNewQtVersions = settings.value(QLatin1String("NewQtVersions")).toString().split(
+ QLatin1String(";"));
+
+ foreach (const QString &qtVersion, oldNewQtVersions) {
+ QStringList splitedQtConfiguration = qtVersion.split(QLatin1String("="));
+ if (splitedQtConfiguration.count() > 1
+ && splitedQtConfiguration.at(1).contains(QLatin1String("qmake"), Qt::CaseInsensitive)) {
+ QString qmakePath = splitedQtConfiguration.at(1);
+ qmakePathes.append(qmakePath);
+ }
+ }
+ return qmakePathes;
+}
+
+bool removeInstallerRegisteredQtVersions(QSettings &settings, const QStringList &qmakePathes)
+{
+ return true;
+ qDebug() << Q_FUNC_INFO << settings.fileName();
+ settings.beginGroup(QLatin1String(QtVersionsSectionName));
+ int qtVersionSizeValue = settings.value(QLatin1String("size")).toInt();
+ qDebug() << "qtVersionSizeValue:" << qtVersionSizeValue;
+
+ //read all settings for Qt Versions
+ QHash<QString, QVariant> oldSettingsAsHash;
+ foreach (const QString &key, settings.allKeys())
+ oldSettingsAsHash.insert(key, settings.value(key));
+ qDebug() << "settings.allKeys():" << settings.allKeys();
+
+ //get the installer added Qt Version settings ids
+ QList<int> toRemoveIds;
+ QHashIterator<QString, QVariant> it(oldSettingsAsHash);
+ while (it.hasNext()) {
+ it.next();
+ if (it.key().endsWith(QLatin1String("QMakePath")) && !it.value().toString().isEmpty()) {
+ foreach (const QString &toRemoveQmakePath, qmakePathes) {
+ if (QFileInfo(it.value().toString()) == QFileInfo(toRemoveQmakePath)) {
+ int firstNoDigitCharIndex = it.key().indexOf(QRegExp(QLatin1String("[^0-9]")));
+ QString numberAtTheBeginning = it.key().left(firstNoDigitCharIndex);
+ toRemoveIds << numberAtTheBeginning.toInt();
+ }
+ }
+ }
+ }
+ qDebug() << "toRemoveIds:" << toRemoveIds;
+
+ //now write only the other Qt Versions to QtCreator settings
+ it.toFront();
+ QHash<int, int> qtVersionIdMapper; //old, new
+ int newVersionId = 1;
+ while (it.hasNext()) {
+ it.next();
+ settings.remove(it.key());
+ int firstNoDigitCharIndex = it.key().indexOf(QRegExp(QLatin1String("[^0-9]")));
+ QString numberAtTheBeginningAsString = it.key().left(firstNoDigitCharIndex);
+ QString restOfTheKey = it.key().mid(firstNoDigitCharIndex);
+ bool isNumber = false;
+ //check that it is a nummer - for example "size" value of the settings array is not
+ int numberAtTheBeginning = numberAtTheBeginningAsString.toInt(&isNumber);
+ if (isNumber && !toRemoveIds.contains(numberAtTheBeginning)) {
+ if (!qtVersionIdMapper.contains(numberAtTheBeginning)) {
+ qtVersionIdMapper.insert(numberAtTheBeginning, newVersionId);
+ newVersionId++;
+ }
+ QString newKey = QString::number(qtVersionIdMapper.value(numberAtTheBeginning)) + restOfTheKey;
+ if (newKey.endsWith(QLatin1String("Id"))) {
+ settings.setValue(newKey, qtVersionIdMapper.value(numberAtTheBeginning));
+ } else {
+ settings.setValue(newKey, it.value());
+ }
+ }
+ }
+
+ settings.setValue(QLatin1String("size"), qtVersionIdMapper.count());
+ settings.endGroup(); //QtVersionsSectionName
+
+ if (qtVersionIdMapper.count() != qtVersionSizeValue - toRemoveIds.count()) {
+ return false;
+ }
+ return true;
+}
+
+bool convertQtInstallerSettings(QSettings &settings, const QString &toolChainsXmlFilePath,
+ QInstaller::PackageManagerCore *const core)
+{
+ QStringList oldNewQtVersions = settings.value(QLatin1String("NewQtVersions")).toString().split(
+ QLatin1String(";"));
+
+ QSet<QString> mingwToolChains;
+ QSet<QString> gcceToolChains;
+ QString newQtVersions;
+ foreach (const QString &qtVersion, oldNewQtVersions) {
+ QStringList splitedQtConfiguration = qtVersion.split(QLatin1String("="));
+ if (splitedQtConfiguration.count() == 8) {
+ int positionCounter = 0;
+ const QString &versionName = splitedQtConfiguration.at(positionCounter++);
+ const QString &qmakePath = splitedQtConfiguration.at(positionCounter++);
+ mingwToolChains.insert(splitedQtConfiguration.at(positionCounter++));
+ QString systemRoot = splitedQtConfiguration.at(positionCounter++);
+ QString gccePath = splitedQtConfiguration.at(positionCounter++);
+ gcceToolChains.insert(gccePath);
+ QString carbidePath = splitedQtConfiguration.at(positionCounter++);
+ Q_UNUSED(carbidePath)
+ QString msvcPath = splitedQtConfiguration.at(positionCounter++);
+ Q_UNUSED(msvcPath)
+ QString sbsPath = splitedQtConfiguration.at(positionCounter++);
+
+ QString addedQtVersion = versionName;
+
+ addedQtVersion += QLatin1Char('=') + qmakePath;
+ addedQtVersion += QLatin1Char('=') + systemRoot;
+ addedQtVersion += QLatin1Char('=') + sbsPath;
+ newQtVersions.append(addedQtVersion + QLatin1Char(';'));
+ } else {
+ newQtVersions.append(qtVersion + QLatin1Char(';'));
+ }
+ }
+ settings.setValue(QLatin1String("NewQtVersions"), newQtVersions);
+
+ QtCreatorPersistentSettings creatorToolChainSettings;
+
+ if (!creatorToolChainSettings.init(toolChainsXmlFilePath))
+ return false;
+
+ foreach (const QString &mingwPath, mingwToolChains) {
+ if (mingwPath.isEmpty())
+ continue;
+ QInstaller::RegisterToolChainOperation operation;
+ operation.setValue(QLatin1String("installer"), QVariant::fromValue(core));
+ operation.setArguments(QStringList()
+ << QLatin1String("GccToolChain")
+ << QLatin1String("ProjectExplorer.ToolChain.Mingw")
+ << QLatin1String("Mingw as a GCC for Windows targets")
+ << QLatin1String("x86-windows-msys-pe-32bit")
+ << mingwPath + QLatin1String("\\bin\\g++.exe")
+ << creatorToolChainSettings.abiToDebuggerHash().value(QLatin1String
+ ("x86-windows-msys-pe-32bit"))
+ );
+ bool result = operation.performOperation();
+ Q_UNUSED(result);
+ Q_ASSERT(result);
+ }
+ foreach (const QString gccePath, gcceToolChains) {
+ if (gccePath.isEmpty())
+ continue;
+ QInstaller::RegisterToolChainOperation operation;
+ operation.setValue(QLatin1String("installer"), QVariant::fromValue(core));
+ operation.setArguments(QStringList()
+ << QLatin1String("GccToolChain")
+ << QLatin1String("Qt4ProjectManager.ToolChain.GCCE")
+ << QLatin1String("GCCE 4 for Symbian targets")
+ << QLatin1String("arm-symbian-device-elf-32bit")
+ << gccePath + QLatin1String("\\bin\\arm-none-symbianelf-g++.exe")
+ << creatorToolChainSettings.abiToDebuggerHash().value(QLatin1String(
+ "arm-symbian-device-elf-32bit"))
+ );
+ bool result = operation.performOperation();
+ Q_UNUSED(result);
+ Q_ASSERT(result);
+ }
+ return true;
+}
+
+void convertDefaultGDBInstallerSettings(QSettings &settings, QInstaller::PackageManagerCore *const core)
+{
+ settings.beginGroup(QLatin1String("GdbBinaries21"));
+
+ //read all settings for GDBs
+ QHash<QString, QString> abiToDefaultDebuggerHash;
+ foreach (const QString &key, settings.allKeys()) {
+ QString oldValue = settings.value(key).toString();
+ QString gdbBinaryPath = oldValue.left(oldValue.indexOf(QLatin1String(",")));
+
+ QString gdbTypesAsCommaSeperatedString = oldValue.mid(oldValue.indexOf(QLatin1String(",")));
+ QStringList gdbTypeList = gdbTypesAsCommaSeperatedString.split(QLatin1String(","));
+ foreach (const QString &gdbType, gdbTypeList) {
+ if (gdbType == QLatin1String("0")) {
+ abiToDefaultDebuggerHash.insert(QLatin1String("x86-linux-generic-elf-64bit"), gdbBinaryPath);
+ abiToDefaultDebuggerHash.insert(QLatin1String("x86-linux-generic-elf-32bit"), gdbBinaryPath);
+ }
+ if (gdbType == QLatin1String("2")) {
+ abiToDefaultDebuggerHash.insert(QLatin1String("x86-windows-msys-pe-32bit"), gdbBinaryPath);
+ }
+ if (gdbType == QLatin1String("6")) {
+ abiToDefaultDebuggerHash.insert(QLatin1String("arm-symbian-device-elf-32bit"), gdbBinaryPath);
+ }
+ if (gdbType == QLatin1String("9")) {
+ abiToDefaultDebuggerHash.insert(QLatin1String("arm-linux-harmattan-elf-32bit"), gdbBinaryPath);
+ abiToDefaultDebuggerHash.insert(QLatin1String("arm-linux-maemo-elf-32bit"), gdbBinaryPath);
+ abiToDefaultDebuggerHash.insert(QLatin1String("arm-linux-meego-elf-32bit"), gdbBinaryPath);
+ }
+ }
+ }
+ QInstaller::RegisterDefaultDebuggerOperation operation;
+ operation.setValue(QLatin1String("installer"), QVariant::fromValue(core));
+
+ QHashIterator<QString, QString> it(abiToDefaultDebuggerHash);
+ while (it.hasNext()) {
+ it.next();
+ operation.setArguments(QStringList() << it.key() << it.value());
+ bool result = operation.performOperation();
+ Q_UNUSED(result);
+ Q_ASSERT(result);
+ }
+
+ settings.endGroup(); //"GdbBinaries21"
+}
+
+UpdateCreatorSettingsFrom21To22Operation::UpdateCreatorSettingsFrom21To22Operation()
+{
+ setName(QLatin1String("UpdateCreatorSettingsFrom21To22"));
+}
+
+void UpdateCreatorSettingsFrom21To22Operation::backup()
+{
+}
+
+bool UpdateCreatorSettingsFrom21To22Operation::performOperation()
+{
+ const QStringList args = arguments();
+
+ if (args.count() != 0) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid arguments in %0: %1 arguments given, exactly 0 expected.")
+ .arg(name()).arg(args.count()));
+ return false;
+ }
+
+ PackageManagerCore *const core = qVariantValue<PackageManagerCore*>(value(QLatin1String("installer")));
+ if (!core) {
+ setError(UserDefinedError);
+ setErrorString(tr("Needed installer object in %1 operation is empty.").arg(name()));
+ return false;
+ }
+ const QString &rootInstallPath = core->value(scTargetDir);
+
+ QString toolChainsXmlFilePath = rootInstallPath + QLatin1String(ToolChainSettingsSuffixPath);
+
+ QSettings sdkSettings(rootInstallPath + QLatin1String(QtCreatorSettingsSuffixPath),
+ QSettings::IniFormat);
+
+ convertDefaultGDBInstallerSettings(sdkSettings, core);
+
+ QString userSettingsFileName = core->value(QLatin1String("QtCreatorSettingsFile"));
+ if (QFile::exists(userSettingsFileName)) {
+ QSettings userSettings(userSettingsFileName, QSettings::IniFormat);
+ QStringList qmakePathes = getQmakePathesOfAllInstallerRegisteredQtVersions(sdkSettings);
+ if (!removeInstallerRegisteredQtVersions(userSettings, qmakePathes)) {
+ setError(UserDefinedError);
+ setErrorString(tr("Can not remove previous registered Qt Versions in %1 operation.").arg(name()));
+ return false;
+ }
+ }
+
+ return convertQtInstallerSettings(sdkSettings, toolChainsXmlFilePath, core);
+}
+
+bool UpdateCreatorSettingsFrom21To22Operation::undoOperation()
+{
+ return true;
+}
+
+bool UpdateCreatorSettingsFrom21To22Operation::testOperation()
+{
+ return true;
+}
+
+Operation *UpdateCreatorSettingsFrom21To22Operation::clone() const
+{
+ return new UpdateCreatorSettingsFrom21To22Operation();
+}
diff --git a/src/libs/installer/updatecreatorsettingsfrom21to22operation.h b/src/libs/installer/updatecreatorsettingsfrom21to22operation.h
new file mode 100644
index 000000000..f0e2b343c
--- /dev/null
+++ b/src/libs/installer/updatecreatorsettingsfrom21to22operation.h
@@ -0,0 +1,54 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef UPDATECREATORSETTINGSFROM21TO22OPERATION_H
+#define UPDATECREATORSETTINGSFROM21TO22OPERATION_H
+
+#include "qinstallerglobal.h"
+
+namespace QInstaller {
+
+class UpdateCreatorSettingsFrom21To22Operation : public Operation
+{
+public:
+ UpdateCreatorSettingsFrom21To22Operation();
+
+ void backup();
+ bool performOperation();
+ bool undoOperation();
+ bool testOperation();
+ Operation *clone() const;
+};
+
+} // namespace QInstaller
+
+#endif // UPDATECREATORSETTINGSFROM21TO22OPERATION_H
diff --git a/src/libs/installer/updater.cpp b/src/libs/installer/updater.cpp
new file mode 100644
index 000000000..ec0564a8f
--- /dev/null
+++ b/src/libs/installer/updater.cpp
@@ -0,0 +1,97 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#include "updater.h"
+
+#include "binaryformat.h"
+#include "component.h"
+#include "init.h"
+#include "packagemanagercore.h"
+#include "utils.h"
+
+#include <QtCore/QDebug>
+
+#include <QtXml/QDomDocument>
+
+#include <iostream>
+
+using namespace QInstaller;
+using namespace QInstallerCreator;
+
+
+Updater::Updater()
+{
+ QInstaller::init();
+}
+
+void Updater::setVerbose(bool verbose)
+{
+ QInstaller::setVerbose(verbose);
+}
+
+bool Updater::checkForUpdates()
+{
+ BinaryContent content = BinaryContent::readAndRegisterFromApplicationFile();
+ if (content.magicMarker() == MagicInstallerMarker) {
+ qDebug() << "Impossible to use an installer to check for updates!";
+ return false;
+ }
+
+ PackageManagerCore core(content.magicMarker(), content.performedOperations());
+ core.setUpdater();
+ PackageManagerCore::setVirtualComponentsVisible(true);
+
+ if (!core.fetchRemotePackagesTree())
+ return false;
+
+ const QList<QInstaller::Component *> components = core.updaterComponents();
+
+ if (components.isEmpty()) {
+ qDebug() << "There are currently no updates available.";
+ return false;
+ }
+
+ QDomDocument doc;
+ QDomElement root = doc.createElement(QLatin1String("updates"));
+ doc.appendChild(root);
+
+ QList<QInstaller::Component *>::const_iterator it;
+ for (it = components.begin(); it != components.end(); ++it) {
+ QDomElement update = doc.createElement(QLatin1String("update"));
+ update.setAttribute(QLatin1String("name"), (*it)->value(scDisplayName));
+ update.setAttribute(QLatin1String("version"), (*it)->value(scRemoteVersion));
+ update.setAttribute(QLatin1String("size"), (*it)->value(scUncompressedSize));
+ root.appendChild(update);
+ }
+
+ std::cout << doc.toString(4) << std::endl;
+ return true;
+}
diff --git a/src/libs/installer/updater.h b/src/libs/installer/updater.h
new file mode 100644
index 000000000..8d6423250
--- /dev/null
+++ b/src/libs/installer/updater.h
@@ -0,0 +1,55 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef UPDATER_H
+#define UPDATER_H
+
+#include "installer_global.h"
+
+#include <QtCore/QObject>
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT Updater : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit Updater();
+
+ bool checkForUpdates();
+ void setVerbose(bool verbose);
+};
+
+} // namespace QInstaller
+
+#endif
diff --git a/src/libs/installer/updatesettings.cpp b/src/libs/installer/updatesettings.cpp
new file mode 100644
index 000000000..f06a1fa87
--- /dev/null
+++ b/src/libs/installer/updatesettings.cpp
@@ -0,0 +1,164 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#include "updatesettings.h"
+
+#include "errors.h"
+#include "repository.h"
+#include "settings.h"
+
+#include <QtCore/QDateTime>
+#include <QtCore/QSettings>
+#include <QtCore/QStringList>
+
+using namespace QInstaller;
+
+class UpdateSettings::Private
+{
+public:
+ Private(UpdateSettings* qq)
+ : q(qq) { }
+
+private:
+ UpdateSettings *const q;
+
+public:
+ QSettings &settings()
+ {
+ return externalSettings ? *externalSettings : internalSettings;
+ }
+
+ static void setExternalSettings(QSettings *settings)
+ {
+ externalSettings = settings;
+ }
+
+private:
+ QSettings internalSettings;
+ static QSettings *externalSettings;
+};
+
+QSettings *UpdateSettings::Private::externalSettings = 0;
+
+
+// -- UpdateSettings
+
+UpdateSettings::UpdateSettings()
+ : d(new Private(this))
+{
+ d->settings().sync();
+}
+
+UpdateSettings::~UpdateSettings()
+{
+ d->settings().sync();
+ delete d;
+}
+
+/* static */
+void UpdateSettings::setSettingsSource(QSettings *settings)
+{
+ Private::setExternalSettings(settings);
+}
+
+int UpdateSettings::updateInterval() const
+{
+ return d->settings().value(QLatin1String("updatesettings/interval"), static_cast<int>(Weekly)).toInt();
+}
+
+void UpdateSettings::setUpdateInterval(int seconds)
+{
+ d->settings().setValue(QLatin1String("updatesettings/interval"), seconds);
+}
+
+QString UpdateSettings::lastResult() const
+{
+ return d->settings().value(QLatin1String("updatesettings/lastresult")).toString();
+}
+
+void UpdateSettings::setLastResult(const QString &lastResult)
+{
+ d->settings().setValue(QLatin1String("updatesettings/lastresult"), lastResult);
+}
+
+QDateTime UpdateSettings::lastCheck() const
+{
+ return d->settings().value(QLatin1String("updatesettings/lastcheck")).toDateTime();
+}
+
+void UpdateSettings::setLastCheck(const QDateTime &lastCheck)
+{
+ d->settings().setValue(QLatin1String("updatesettings/lastcheck"), lastCheck);
+}
+
+bool UpdateSettings::checkOnlyImportantUpdates() const
+{
+ return d->settings().value(QLatin1String("updatesettings/onlyimportant"), false).toBool();
+}
+
+void UpdateSettings::setCheckOnlyImportantUpdates(bool checkOnlyImportantUpdates)
+{
+ d->settings().setValue(QLatin1String("updatesettings/onlyimportant"), checkOnlyImportantUpdates);
+}
+
+QSet<Repository> UpdateSettings::repositories() const
+{
+ QSettings &settings = d->settings();
+ const int count = settings.beginReadArray(QLatin1String("updatesettings/repositories"));
+
+ QSet<Repository> result;
+ for (int i = 0; i < count; ++i) {
+ settings.setArrayIndex(i);
+ result.insert(Repository(d->settings().value(QLatin1String("url")).toUrl(), false));
+ }
+ settings.endArray();
+
+ try {
+ if(result.isEmpty()) {
+ result = Settings::fromFileAndPrefix(QLatin1String(":/metadata/installer-config/config.xml"),
+ QLatin1String(":/metadata/installer-config/")).userRepositories();
+ }
+ } catch (const Error &error) {
+ qDebug("Could not parse config: %s", qPrintable(error.message()));
+ }
+ return result;
+}
+
+void UpdateSettings::setRepositories(const QSet<Repository> &repositories)
+{
+ QSet<Repository>::ConstIterator it = repositories.constBegin();
+ d->settings().beginWriteArray(QLatin1String("updatesettings/repositories"));
+ for (int i = 0; i < repositories.count(); ++i, ++it) {
+ d->settings().setArrayIndex(i);
+ d->settings().setValue(QLatin1String("url"), (*it).url());
+ }
+ d->settings().endArray();
+}
diff --git a/src/libs/installer/updatesettings.h b/src/libs/installer/updatesettings.h
new file mode 100644
index 000000000..9994e20d6
--- /dev/null
+++ b/src/libs/installer/updatesettings.h
@@ -0,0 +1,85 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef UPDATESETTINGS_H
+#define UPDATESETTINGS_H
+
+#include "installer_global.h"
+
+QT_BEGIN_NAMESPACE
+class QDateTime;
+template<typename T>
+class QSet;
+class QSettings;
+QT_END_NAMESPACE
+
+namespace QInstaller {
+
+class Repository;
+
+class INSTALLER_EXPORT UpdateSettings
+{
+public:
+ UpdateSettings();
+ ~UpdateSettings();
+
+ enum Interval {
+ Daily = 86400,
+ Weekly = Daily * 7,
+ Monthly = Daily * 30
+ };
+
+ static void setSettingsSource(QSettings *settings);
+
+ int updateInterval() const;
+ void setUpdateInterval(int seconds);
+
+ QString lastResult() const;
+ void setLastResult(const QString &lastResult);
+
+ QDateTime lastCheck() const;
+ void setLastCheck(const QDateTime &lastCheck);
+
+ bool checkOnlyImportantUpdates() const;
+ void setCheckOnlyImportantUpdates(bool checkOnlyImportantUpdates);
+
+ QSet<Repository> repositories() const;
+ void setRepositories(const QSet<Repository> &repositories);
+
+private:
+ class Private;
+ Private *const d;
+};
+
+}
+
+#endif
diff --git a/src/libs/installer/utils.cpp b/src/libs/installer/utils.cpp
new file mode 100644
index 000000000..4bd93430f
--- /dev/null
+++ b/src/libs/installer/utils.cpp
@@ -0,0 +1,341 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "utils.h"
+
+#include <QtCore/QDateTime>
+#include <QtCore/QDir>
+#include <QtCore/QProcessEnvironment>
+#include <QtCore/QVector>
+
+#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
+# include "qt_windows.h"
+#endif
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+
+static bool verb = false;
+
+void QInstaller::setVerbose(bool v)
+{
+ verb = v;
+}
+
+bool QInstaller::isVerbose()
+{
+ return verb;
+}
+
+#ifdef Q_WS_WIN
+void qWinMsgHandler(QtMsgType t, const char *str);
+
+class debugstream : public std::ostream
+{
+ class buf : public std::stringbuf
+ {
+ public:
+ buf() {}
+
+ int sync()
+ {
+ std::string s = str();
+ if (s[s.length() - 1] == '\n' )
+ s[s.length() - 1] = '\0'; // remove \n
+ qWinMsgHandler(QtDebugMsg, s.c_str());
+ std::cout << s << std::endl;
+ str(std::string());
+ return 0;
+ }
+ };
+public:
+ debugstream() : std::ostream(&b) {}
+private:
+ buf b;
+};
+#endif
+
+std::ostream &QInstaller::stdverbose()
+{
+ static std::fstream null;
+#ifdef Q_WS_WIN
+ static debugstream stream;
+#else
+ static std::ostream& stream = std::cout;
+#endif
+ if (verb)
+ return stream;
+ return null;
+}
+
+std::ostream &QInstaller::operator<<(std::ostream &os, const QString &string)
+{
+ return os << qPrintable(string);
+}
+
+//TODO from kdupdaterfiledownloader.cpp, use that one once merged
+QByteArray QInstaller::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
+}
+
+
+QString QInstaller::replaceVariables(const QHash<QString, QString> &vars, const QString &str)
+{
+ QString res;
+ int pos = 0;
+ while (true) {
+ int pos1 = str.indexOf(QLatin1Char('@'), pos);
+ if (pos1 == -1)
+ break;
+ int pos2 = str.indexOf(QLatin1Char('@'), pos1 + 1);
+ if (pos2 == -1)
+ break;
+ res += str.mid(pos, pos1 - pos);
+ QString name = str.mid(pos1 + 1, pos2 - pos1 - 1);
+ res += vars.value(name);
+ pos = pos2 + 1;
+ }
+ res += str.mid(pos);
+ return res;
+}
+
+QString QInstaller::replaceWindowsEnvironmentVariables(const QString &str)
+{
+ const QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+ QString res;
+ int pos = 0;
+ while (true) {
+ int pos1 = str.indexOf(QLatin1Char( '%'), pos);
+ if (pos1 == -1)
+ break;
+ int pos2 = str.indexOf(QLatin1Char( '%'), pos1 + 1);
+ if (pos2 == -1)
+ break;
+ res += str.mid(pos, pos1 - pos);
+ QString name = str.mid(pos1 + 1, pos2 - pos1 - 1);
+ res += env.value(name);
+ pos = pos2 + 1;
+ }
+ res += str.mid(pos);
+ return res;
+}
+
+QInstaller::VerboseWriter::VerboseWriter(QObject *parent) : QObject(parent)
+{
+ preFileBuffer.open(QIODevice::ReadWrite);
+ stream.setDevice(&preFileBuffer);
+}
+
+QInstaller::VerboseWriter::~VerboseWriter()
+{
+ stream.flush();
+ if (logFileName.isEmpty()) // binarycreator
+ return;
+ //if the installer installed nothing - there is no target directory - where the logfile can be saved
+ if (!QFileInfo(logFileName).absoluteDir().exists())
+ return;
+
+ QFile output(logFileName);
+ if (output.open(QIODevice::ReadWrite | QIODevice::Append)) {
+ QString logInfo;
+ logInfo += QLatin1String("*************************************");
+ logInfo += QLatin1String("Invoked:") + QDateTime::currentDateTime().toString();
+ output.write(logInfo.toLocal8Bit());
+ output.write(preFileBuffer.data());
+ output.close();
+ }
+ stream.setDevice(0);
+}
+
+void QInstaller::VerboseWriter::setOutputStream(const QString &fileName)
+{
+ logFileName = fileName;
+}
+
+
+Q_GLOBAL_STATIC(QInstaller::VerboseWriter, verboseWriter)
+
+QInstaller::VerboseWriter *QInstaller::VerboseWriter::instance()
+{
+ return verboseWriter();
+}
+
+QInstaller::VerboseWriter &QInstaller::verbose()
+{
+ return *verboseWriter();
+}
+
+#ifdef Q_OS_WIN
+// taken from qcoreapplication_p.h
+template<typename Char>
+static QVector<Char*> qWinCmdLine(Char *cmdParam, int length, int &argc)
+{
+ QVector<Char*> argv(8);
+ Char *p = cmdParam;
+ Char *p_end = p + length;
+
+ argc = 0;
+
+ while (*p && p < p_end) { // parse cmd line arguments
+ while (QChar((short)(*p)).isSpace()) // skip white space
+ p++;
+ if (*p && p < p_end) { // arg starts
+ int quote;
+ Char *start, *r;
+ if (*p == Char('\"') || *p == Char('\'')) { // " or ' quote
+ quote = *p;
+ start = ++p;
+ } else {
+ quote = 0;
+ start = p;
+ }
+ r = start;
+ while (*p && p < p_end) {
+ if (quote) {
+ if (*p == quote) {
+ p++;
+ if (QChar((short)(*p)).isSpace())
+ break;
+ quote = 0;
+ }
+ }
+ if (*p == '\\') { // escape char?
+ p++;
+ if (*p == Char('\"') || *p == Char('\''))
+ ; // yes
+ else
+ p--; // treat \ literally
+ } else {
+ if (!quote && (*p == Char('\"') || *p == Char('\''))) { // " or ' quote
+ quote = *p++;
+ continue;
+ } else if (QChar((short)(*p)).isSpace() && !quote)
+ break;
+ }
+ if (*p)
+ *r++ = *p++;
+ }
+ if (*p && p < p_end)
+ p++;
+ *r = Char('\0');
+
+ if (argc >= (int)argv.size()-1) // expand array
+ argv.resize(argv.size()*2);
+ argv[argc++] = start;
+ }
+ }
+ argv[argc] = 0;
+
+ return argv;
+}
+
+QStringList QInstaller::parseCommandLineArgs(int argc, char **argv)
+{
+ Q_UNUSED(argc)
+ Q_UNUSED(argv)
+
+ QStringList arguments;
+ QString cmdLine = QString::fromWCharArray(GetCommandLine());
+
+ QVector<wchar_t*> args = qWinCmdLine<wchar_t>((wchar_t *)cmdLine.utf16(), cmdLine.length(), argc);
+ for (int a = 0; a < argc; ++a)
+ arguments << QString::fromWCharArray(args[a]);
+ return arguments;
+}
+#else
+QStringList QInstaller::parseCommandLineArgs(int argc, char **argv)
+{
+ QStringList arguments;
+ for (int a = 0; a < argc; ++a)
+ arguments << QString::fromLocal8Bit(argv[a]);
+ return arguments;
+}
+#endif
+
+#ifdef Q_OS_WIN
+// taken 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;
+}
+
+QString QInstaller::createCommandline(const QString &program, const QStringList &arguments)
+{
+ return qt_create_commandline(program, arguments);
+}
+#endif
diff --git a/src/libs/installer/utils.h b/src/libs/installer/utils.h
new file mode 100644
index 000000000..cbf2e95e7
--- /dev/null
+++ b/src/libs/installer/utils.h
@@ -0,0 +1,92 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef QINSTALLER_UTILS_H
+#define QINSTALLER_UTILS_H
+
+#include "installer_global.h"
+
+#include <QtCore/QBuffer>
+#include <QtCore/QCryptographicHash>
+#include <QtCore/QHash>
+#include <QtCore/QUrl>
+#include <QtCore/QTextStream>
+
+#include <ostream>
+
+QT_BEGIN_NAMESPACE
+class QIODevice;
+QT_END_NAMESPACE
+
+namespace QInstaller {
+
+ QByteArray INSTALLER_EXPORT calculateHash(QIODevice *device, QCryptographicHash::Algorithm algo);
+
+ QString INSTALLER_EXPORT replaceVariables(const QHash<QString,QString> &vars, const QString &str);
+ QString INSTALLER_EXPORT replaceWindowsEnvironmentVariables(const QString &str);
+ QStringList INSTALLER_EXPORT parseCommandLineArgs(int argc, char **argv);
+#ifdef Q_OS_WIN
+ QString createCommandline(const QString &program, const QStringList &arguments);
+#endif
+
+ void INSTALLER_EXPORT setVerbose(bool v);
+ bool INSTALLER_EXPORT isVerbose();
+
+ INSTALLER_EXPORT std::ostream& stdverbose();
+ INSTALLER_EXPORT std::ostream& operator<<(std::ostream &os, const QString &string);
+
+ class VerboseWriter;
+ INSTALLER_EXPORT VerboseWriter &verbose();
+
+ class INSTALLER_EXPORT VerboseWriter : public QObject
+ {
+ Q_OBJECT
+ public:
+ VerboseWriter(QObject *parent = 0);
+ ~VerboseWriter();
+
+ static VerboseWriter *instance();
+
+ inline VerboseWriter &operator<<(const char *t) { stdverbose() << t; stream << t; return *this; }
+ inline VerboseWriter &operator<<(std::ostream& (*f)(std::ostream &s)) { stdverbose() << *f; stream << "\n"; return *this; }
+ public slots:
+ void setOutputStream(const QString &fileName);
+
+ private:
+ QTextStream stream;
+ QBuffer preFileBuffer;
+ QString logFileName;
+ };
+
+}
+
+#endif // QINSTALLER_UTILS_H
diff --git a/src/libs/installer/zipjob.cpp b/src/libs/installer/zipjob.cpp
new file mode 100644
index 000000000..bcc617d31
--- /dev/null
+++ b/src/libs/installer/zipjob.cpp
@@ -0,0 +1,206 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+#include <zipjob.h>
+
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QMetaType>
+#include <QtCore/QStringList>
+
+#include <cassert>
+#include <climits>
+
+class ZipJob::Private
+{
+public:
+ Private() : outputDevice(0), process(0) {}
+
+ QIODevice *outputDevice;
+ QDir workingDir;
+ QProcess *process;
+ QStringList filesToArchive;
+};
+
+Q_DECLARE_METATYPE(QProcess::ExitStatus)
+
+ZipJob::ZipJob()
+ : d(new Private())
+{
+ qRegisterMetaType<QProcess::ExitStatus>();
+}
+
+ZipJob::~ZipJob()
+{
+ delete d;
+}
+
+void ZipJob::run()
+{
+ assert(!d->process);
+ d->process = new QProcess;
+ d->process->setWorkingDirectory(d->workingDir.absolutePath());
+ QStringList args;
+ args << QLatin1String( "-" ) << QLatin1String( "-r" ) << d->filesToArchive;
+ connect(d->process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError)));
+ connect(d->process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished(int,QProcess::ExitStatus)));
+ connect(d->process, SIGNAL(readyReadStandardOutput()), this, SLOT(processReadyReadStandardOutput()));
+
+ d->process->start(QLatin1String("zip"), args);
+ if (!d->process->waitForStarted()) {
+ //TODO handle
+ }
+
+ if (!d->process->waitForFinished(INT_MAX)) {
+ //TODO handle
+ }
+
+ delete d->process;
+ d->process = 0;
+ // emit result
+}
+
+void ZipJob::processError(QProcess::ProcessError)
+{
+ emit error();
+}
+
+void ZipJob::processFinished(int, QProcess::ExitStatus)
+{
+ emit finished();
+}
+
+void ZipJob::processReadyReadStandardOutput()
+{
+ const QByteArray buf = d->process->readAll();
+ const qint64 toWrite = buf.size();
+ qint64 written = 0;
+ while (written < toWrite) {
+ const qint64 num = d->outputDevice->write(buf.constData() + written, toWrite - written);
+ if (num < 0) {
+ //TODO: handle error
+ return;
+ }
+ written += num;
+ }
+}
+
+void ZipJob::setOutputDevice(QIODevice *device)
+{
+ d->outputDevice = device;
+}
+
+void ZipJob::setWorkingDirectory(const QDir &dir)
+{
+ d->workingDir = dir;
+}
+
+void ZipJob::setFilesToArchive(const QStringList &files)
+{
+ d->filesToArchive = files;
+}
+
+class UnzipJob::Private
+{
+public:
+ Private() : inputDevice(0) {}
+
+ QIODevice *inputDevice;
+ QString outputPath;
+ QStringList filesToExtract;
+};
+
+UnzipJob::UnzipJob()
+ : d(new Private())
+{
+ qRegisterMetaType<QProcess::ExitStatus>();
+}
+
+UnzipJob::~UnzipJob()
+{
+ delete d;
+}
+
+void UnzipJob::setInputDevice(QIODevice *device)
+{
+ d->inputDevice = device;
+}
+
+void UnzipJob::setOutputPath(const QString &path)
+{
+ d->outputPath = path;
+}
+
+void UnzipJob::processError(QProcess::ProcessError)
+{
+ emit error();
+}
+
+void UnzipJob::run()
+{
+ QProcess process;
+ // TODO: this won't work on Windows... grmpfl, but on Mac and Linux, at least...
+ QStringList args;
+ args << QLatin1String( "/dev/stdin" );
+ if (!d->filesToExtract.isEmpty())
+ args << QLatin1String("-x") << d->filesToExtract;
+ process.setWorkingDirectory(d->outputPath);
+ process.start(QLatin1String("unzip"), args);
+ connect(&process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError)));
+ connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processFinished(int, QProcess::ExitStatus )));
+ if (!process.waitForStarted()) {
+ // TODO handle
+ return;
+ }
+
+ const int bufferSize = 4096;
+ QByteArray buffer;
+ while (d->inputDevice->bytesAvailable() > 0 || d->inputDevice->waitForReadyRead(INT_MAX)) {
+ buffer = d->inputDevice->read(bufferSize);
+ process.write(buffer);
+ process.waitForBytesWritten(INT_MAX);
+ }
+ process.closeWriteChannel();
+
+ if (!process.waitForFinished(INT_MAX)) {
+ // TODO handle
+ }
+}
+
+void UnzipJob::processFinished(int, QProcess::ExitStatus)
+{
+ emit finished();
+}
+
+void UnzipJob::setFilesToExtract(const QStringList &files)
+{
+ d->filesToExtract = files;
+}
diff --git a/src/libs/installer/zipjob.h b/src/libs/installer/zipjob.h
new file mode 100644
index 000000000..72ab40796
--- /dev/null
+++ b/src/libs/installer/zipjob.h
@@ -0,0 +1,100 @@
+/**************************************************************************
+**
+** This file is part of Installer Framework
+**
+** Copyright (c) 2010-2012 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef ZIPJOB_H
+#define ZIPJOB_H
+
+#include <QProcess>
+#include <QRunnable>
+
+QT_BEGIN_NAMESPACE
+class QDir;
+class QIODevice;
+class QStringList;
+QT_END_NAMESPACE
+
+class ZipJob : public QObject, public QRunnable
+{
+ Q_OBJECT
+
+public:
+ ZipJob();
+ ~ZipJob();
+
+ void setOutputDevice(QIODevice *device);
+ void setWorkingDirectory(const QDir &dir);
+ void setFilesToArchive(const QStringList &files);
+
+ void run();
+
+Q_SIGNALS:
+ void finished();
+ void error();
+
+private Q_SLOTS:
+ void processError(QProcess::ProcessError);
+ void processFinished(int, QProcess::ExitStatus);
+ void processReadyReadStandardOutput();
+
+private:
+ class Private;
+ Private *const d;
+};
+
+class UnzipJob : public QObject, public QRunnable
+{
+ Q_OBJECT
+
+public:
+ UnzipJob();
+ ~UnzipJob();
+
+ void setInputDevice(QIODevice *device);
+ void setOutputPath(const QString &path);
+ void setFilesToExtract(const QStringList &files);
+
+ void run();
+
+Q_SIGNALS:
+ void finished();
+ void error();
+
+private Q_SLOTS:
+ void processError(QProcess::ProcessError);
+ void processFinished(int, QProcess::ExitStatus);
+
+private:
+ class Private;
+ Private *const d;
+};
+
+#endif // ZIPJOB_H