diff options
9 files changed, 523 insertions, 4 deletions
diff --git a/installerbuilder/libinstaller/createlocalrepositoryoperation.cpp b/installerbuilder/libinstaller/createlocalrepositoryoperation.cpp new file mode 100644 index 000000000..5e4af2fb1 --- /dev/null +++ b/installerbuilder/libinstaller/createlocalrepositoryoperation.cpp @@ -0,0 +1,383 @@ +/************************************************************************** +** +** 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 "common/binaryformat.h" +#include "common/errors.h" +#include "common/fileutils.h" +#include "copydirectoryoperation.h" +#include "kdupdaterupdateoperations.h" +#include "lib7z_facade.h" +#include "packagemanagercore.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(©DirOp, 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/installerbuilder/libinstaller/createlocalrepositoryoperation.h b/installerbuilder/libinstaller/createlocalrepositoryoperation.h new file mode 100644 index 000000000..928e2959e --- /dev/null +++ b/installerbuilder/libinstaller/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/installerbuilder/libinstaller/init.cpp b/installerbuilder/libinstaller/init.cpp index 87b6de6d6..f71facf5b 100644 --- a/installerbuilder/libinstaller/init.cpp +++ b/installerbuilder/libinstaller/init.cpp @@ -34,6 +34,7 @@ #include "createshortcutoperation.h" #include "createdesktopentryoperation.h" +#include "createlocalrepositoryoperation.h" #include "extractarchiveoperation.h" #include "globalsettingsoperation.h" #include "environmentvariablesoperation.h" @@ -196,6 +197,7 @@ void QInstaller::init() 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")); diff --git a/installerbuilder/libinstaller/libinstaller.pro b/installerbuilder/libinstaller/libinstaller.pro index 7ff7c2130..c7a0599e7 100644 --- a/installerbuilder/libinstaller/libinstaller.pro +++ b/installerbuilder/libinstaller/libinstaller.pro @@ -101,7 +101,8 @@ HEADERS += $$PWD/packagemanagercore.h \ qprocesswrapper.h \ qsettingswrapper.h \ constants.h \ - packagemanagerproxyfactory.h + packagemanagerproxyfactory.h \ + createlocalrepositoryoperation.h SOURCES += $$PWD/packagemanagercore.cpp \ $$PWD/packagemanagercore_p.cpp \ @@ -168,7 +169,8 @@ SOURCES += $$PWD/packagemanagercore.cpp \ templates.cpp \ qsettingswrapper.cpp \ settings.cpp \ - packagemanagerproxyfactory.cpp + packagemanagerproxyfactory.cpp \ + createlocalrepositoryoperation.cpp macx { HEADERS += macrelocateqt.h \ diff --git a/installerbuilder/libinstaller/packagemanagercore.cpp b/installerbuilder/libinstaller/packagemanagercore.cpp index 0b24152c0..b47708c7a 100644 --- a/installerbuilder/libinstaller/packagemanagercore.cpp +++ b/installerbuilder/libinstaller/packagemanagercore.cpp @@ -1832,3 +1832,15 @@ QString PackageManagerCore::findDisplayVersion(const QString &componentName, return findDisplayVersion(replaceWith, components, versionKey, visited); } + +bool PackageManagerCore::createLocalRepositoryFromBinary() const +{ + return d->m_createLocalRepositoryFromBinary; +} + +void PackageManagerCore::setCreateLocalRepositoryFromBinary(bool create) +{ + if (!isOfflineOnly()) + return; + d->m_createLocalRepositoryFromBinary = create; +} diff --git a/installerbuilder/libinstaller/packagemanagercore.h b/installerbuilder/libinstaller/packagemanagercore.h index eadbc841d..13509b6b1 100644 --- a/installerbuilder/libinstaller/packagemanagercore.h +++ b/installerbuilder/libinstaller/packagemanagercore.h @@ -223,6 +223,9 @@ public: bool needsRestart() const; bool finishedWithSuccess() const; + Q_INVOKABLE bool createLocalRepositoryFromBinary() const; + Q_INVOKABLE void setCreateLocalRepositoryFromBinary(bool create); + public Q_SLOTS: bool runInstaller(); bool runUninstaller(); diff --git a/installerbuilder/libinstaller/packagemanagercore_p.cpp b/installerbuilder/libinstaller/packagemanagercore_p.cpp index c3d2e3a01..abb1523a9 100644 --- a/installerbuilder/libinstaller/packagemanagercore_p.cpp +++ b/installerbuilder/libinstaller/packagemanagercore_p.cpp @@ -164,6 +164,7 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core) , m_updateSourcesAdded(false) , m_componentsToInstallCalculated(false) , m_proxyFactory(0) + , m_createLocalRepositoryFromBinary(false) { } @@ -186,6 +187,7 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, q , m_magicBinaryMarker(magicInstallerMaker) , m_componentsToInstallCalculated(false) , m_proxyFactory(0) + , m_createLocalRepositoryFromBinary(false) { connect(this, SIGNAL(installationStarted()), m_core, SIGNAL(installationStarted())); connect(this, SIGNAL(installationFinished()), m_core, SIGNAL(installationFinished())); @@ -505,6 +507,7 @@ void PackageManagerCorePrivate::initialize() { m_coreCheckedHash.clear(); m_componentsToInstallCalculated = false; + m_createLocalRepositoryFromBinary = false; // first set some common variables that may used e.g. as placeholder // in some of the settings variables or in a script or... @@ -1430,12 +1433,50 @@ void PackageManagerCorePrivate::runInstaller() callBeginInstallation(componentsToInstall); stopProcessesForUpdates(componentsToInstall); - const int progressOperationCount = countProgressOperations(componentsToInstall); + const int progressOperationCount = countProgressOperations(componentsToInstall) + + (m_createLocalRepositoryFromBinary ? 1 : 0); // add one more operation as we support progress double progressOperationSize = componentsInstallPartProgressSize / progressOperationCount; foreach (Component *component, componentsToInstall) installComponent(component, progressOperationSize, adminRightsGained); + if (m_createLocalRepositoryFromBinary) { + emit m_core->titleMessageChanged(tr("Creating local repository")); + ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QString()); + ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("Creating local repository")); + + Operation *createRepo = createOwnedOperation(QLatin1String("CreateLocalRepository")); + if (createRepo) { + createRepo->setValue(QLatin1String("uninstall-only"), true); + createRepo->setValue(QLatin1String("installer"), QVariant::fromValue(m_core)); + createRepo->setArguments(QStringList() << QCoreApplication::applicationFilePath() << target); + + connectOperationToInstaller(createRepo, progressOperationSize); + + bool success = performOperationThreaded(createRepo); + if (!success) { + adminRightsGained = m_core->gainAdminRights(); + success = performOperationThreaded(createRepo); + } + + if (success) { + QSet<Repository> repos; + foreach (Repository repo, m_settings.defaultRepositories()) { + repo.setEnabled(false); + repos.insert(repo); + } + repos.insert(Repository(QUrl::fromUserInput(createRepo + ->value(QLatin1String("local-repo")).toString()), true)); + m_settings.setDefaultRepositories(repos); + addPerformed(takeOwnedOperation(createRepo)); + } else { + MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), + QLatin1String("installationError"), tr("Error"), createRepo->errorString()); + createRepo->undoOperation(); + } + } + } + emit m_core->titleMessageChanged(tr("Creating Uninstaller")); writeUninstaller(m_performedOperationsOld + m_performedOperationsCurrentSession); diff --git a/installerbuilder/libinstaller/packagemanagercore_p.h b/installerbuilder/libinstaller/packagemanagercore_p.h index 7f0b03918..1a585cf96 100644 --- a/installerbuilder/libinstaller/packagemanagercore_p.h +++ b/installerbuilder/libinstaller/packagemanagercore_p.h @@ -246,6 +246,7 @@ private: QSet<Component*> m_componentsToUninstall; QString m_componentsToInstallError; FileDownloaderProxyFactory *m_proxyFactory; + bool m_createLocalRepositoryFromBinary; private: // remove once we deprecate isSelected, setSelected etc... diff --git a/installerbuilder/libinstaller/packagemanagergui.cpp b/installerbuilder/libinstaller/packagemanagergui.cpp index 62448dfc2..3fb88bee8 100644 --- a/installerbuilder/libinstaller/packagemanagergui.cpp +++ b/installerbuilder/libinstaller/packagemanagergui.cpp @@ -1610,8 +1610,15 @@ void ReadyForInstallationPage::entering() tempRequired += extraSpace; } + quint64 repositorySize = 0; + const bool createLocalRepository = packageManagerCore()->createLocalRepositoryFromBinary(); + if (createLocalRepository) { + repositorySize = QFile(QCoreApplication::applicationFilePath()).size(); + required += repositorySize; // if we create a local repository, take that space into account as well + } + qDebug() << "Installation space required:" << humanReadableSize(required) << "Temporary space required:" - << humanReadableSize(tempRequired); + << humanReadableSize(tempRequired) << "Local repository size:" << humanReadableSize(repositorySize); if (tempOnSameVolume && (installVolumeAvailableSize <= (required + tempRequired))) { m_msgLabel->setText(tr("Not enough disk space to store temporary files and the installation! " |