summaryrefslogtreecommitdiffstats
path: root/src/libs/installer
diff options
context:
space:
mode:
authorkh1 <karsten.heimrich@nokia.com>2012-03-15 14:53:47 +0100
committerKarsten Heimrich <karsten.heimrich@nokia.com>2012-03-19 16:14:04 +0100
commitbe3b47d0d504a3409ce66bd77bb8c0acff87c4f5 (patch)
tree09dfb02d484a4f395991972b828da71400fb761a /src/libs/installer
parent9fd62353cf7f973d78cd2093328ac15b5c4980b6 (diff)
Reorganize the tree, have better ifw.pri. Shadow build support.
Change-Id: I01fb12537f863ed0744979973c7e4153889cc5cb Reviewed-by: Tim Jenssen <tim.jenssen@nokia.com>
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 insta