From de8b7ede25558e0cd97cf3cfea039158b71ad8e2 Mon Sep 17 00:00:00 2001 From: Karsten Heimrich Date: Tue, 14 Jul 2015 13:52:28 +0200 Subject: Rename files. Remove {kd} prefix. Remove {kdupdater} prefix. Change-Id: I88c93cab718f4659296d5f7f562beefd747a366f Reviewed-by: Jarek Kobus --- src/libs/kdtools/updateoperations.cpp | 786 ++++++++++++++++++++++++++++++++++ 1 file changed, 786 insertions(+) create mode 100644 src/libs/kdtools/updateoperations.cpp (limited to 'src/libs/kdtools/updateoperations.cpp') diff --git a/src/libs/kdtools/updateoperations.cpp b/src/libs/kdtools/updateoperations.cpp new file mode 100644 index 000000000..f72bd100c --- /dev/null +++ b/src/libs/kdtools/updateoperations.cpp @@ -0,0 +1,786 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB) +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "updateoperations.h" +#include "errors.h" +#include "fileutils.h" + +#include +#include +#include +#include +#include + +#include + +using namespace KDUpdater; + +static QString errnoToQString(int error) +{ +#if defined(Q_OS_WIN) && !defined(Q_CC_MINGW) + char msg[128]; + if (strerror_s(msg, sizeof msg, error) != 0) + return QString::fromLocal8Bit(msg); + return QString(); +#else + return QString::fromLocal8Bit(strerror(error)); +#endif +} + +static bool removeDirectory(const QString &path, QString *errorString, bool force) +{ + Q_ASSERT(errorString); + + QDir dir = path; + const QFileInfoList entries = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden); + foreach (const QFileInfo &entry, entries) { + if (entry.isDir() && (!entry.isSymLink())) + removeDirectory(entry.filePath(), errorString, force); + else if (force && (!QFile(entry.filePath()).remove())) + return false; + } + + // even remove some hidden, OS-created files in there + QInstaller::removeSystemGeneratedFiles(path); + + errno = 0; + const bool success = dir.rmdir(path); + if (errno) + *errorString = errnoToQString(errno); + return success; +} + +/* + * \internal + * Returns a filename for a temporary file based on \a templateName + */ +static QString backupFileName(const QString &templateName) +{ + QTemporaryFile file(templateName); + file.open(); + const QString name = file.fileName(); + file.close(); + file.remove(); + return name; +} + +//////////////////////////////////////////////////////////////////////////// +// KDUpdater::CopyOperation +//////////////////////////////////////////////////////////////////////////// + +CopyOperation::CopyOperation(QInstaller::PackageManagerCore *core) + : UpdateOperation(core) +{ + setName(QLatin1String("Copy")); +} + +CopyOperation::~CopyOperation() +{ + deleteFileNowOrLater(value(QLatin1String("backupOfExistingDestination")).toString()); +} + +QString CopyOperation::sourcePath() +{ + return arguments().first(); +} + +QString CopyOperation::destinationPath() +{ + QString destination = arguments().last(); + + // if the target is a directory use the source filename to complete the destination path + if (QFileInfo(destination).isDir()) + destination = QDir(destination).filePath(QFileInfo(sourcePath()).fileName()); + return destination; +} + + +void CopyOperation::backup() +{ + QString destination = destinationPath(); + + if (!QFile::exists(destination)) { + clearValue(QLatin1String("backupOfExistingDestination")); + return; + } + + setValue(QLatin1String("backupOfExistingDestination"), backupFileName(destination)); + + // race condition: The backup file could get created by another process right now. But this is the same + // in QFile::copy... + if (!QFile::rename(destination, value(QLatin1String("backupOfExistingDestination")).toString())) + setError(UserDefinedError, tr("Cannot backup file \"%1\".").arg(QDir::toNativeSeparators(destination))); +} + +bool CopyOperation::performOperation() +{ + // We need two args to complete the copy operation. First arg provides the complete file name of source + // Second arg provides the complete file name of dest + if (!checkArgumentCount(2)) + return false; + + QString source = sourcePath(); + QString destination = destinationPath(); + + QFile sourceFile(source); + if (!sourceFile.exists()) { + setError(UserDefinedError); + setErrorString(tr("Cannot copy a non-existent file: %1").arg(QDir::toNativeSeparators(source))); + return false; + } + // If destination file exists, we cannot use QFile::copy() because it does not overwrite an existing + // file. So we remove the destination file. + QFile destinationFile(destination); + if (destinationFile.exists()) { + if (!destinationFile.remove()) { + setError(UserDefinedError); + setErrorString(tr("Cannot remove file \"%1\": %2").arg( + QDir::toNativeSeparators(destination), destinationFile.errorString())); + return false; + } + } + + const bool copied = sourceFile.copy(destination); + if (!copied) { + setError(UserDefinedError); + setErrorString(tr("Cannot copy file \"%1\" to \"%2\": %3").arg( + QDir::toNativeSeparators(source), QDir::toNativeSeparators(destination), + sourceFile.errorString())); + } + return copied; +} + +bool CopyOperation::undoOperation() +{ + QString source = sourcePath(); + QString destination = destinationPath(); + + // if the target is a directory use the source filename to complete the destination path + if (QFileInfo(destination).isDir()) + destination = destination + QDir::separator() + QFileInfo(source).fileName(); + + QFile destFile(destination); + // first remove the dest + if (destFile.exists() && !destFile.remove()) { + setError(UserDefinedError, tr("Cannot delete file \"%1\": %2").arg( + QDir::toNativeSeparators(destination), destFile.errorString())); + return false; + } + + // no backup was done: + // the copy destination file wasn't existing yet - that's no error + if (!hasValue(QLatin1String("backupOfExistingDestination"))) + return true; + + QFile backupFile(value(QLatin1String("backupOfExistingDestination")).toString()); + // otherwise we have to copy the backup back: + const bool success = backupFile.rename(destination); + if (!success) + setError(UserDefinedError, tr("Cannot restore backup file into \"%1\": %2").arg( + QDir::toNativeSeparators(destination), backupFile.errorString())); + return success; +} + +/*! + \reimp + */ +QDomDocument CopyOperation::toXml() const +{ + // we don't want to save the backupOfExistingDestination + if (!hasValue(QLatin1String("backupOfExistingDestination"))) + return UpdateOperation::toXml(); + + CopyOperation *const me = const_cast(this); + + const QVariant v = value(QLatin1String("backupOfExistingDestination")); + me->clearValue(QLatin1String("backupOfExistingDestination")); + const QDomDocument xml = UpdateOperation::toXml(); + me->setValue(QLatin1String("backupOfExistingDestination"), v); + return xml; +} + +bool CopyOperation::testOperation() +{ + // TODO + return true; +} + + +//////////////////////////////////////////////////////////////////////////// +// KDUpdater::MoveOperation +//////////////////////////////////////////////////////////////////////////// + +MoveOperation::MoveOperation(QInstaller::PackageManagerCore *core) + : UpdateOperation(core) +{ + setName(QLatin1String("Move")); +} + +MoveOperation::~MoveOperation() +{ + deleteFileNowOrLater(value(QLatin1String("backupOfExistingDestination")).toString()); +} + +void MoveOperation::backup() +{ + const QString dest = arguments().last(); + if (!QFile::exists(dest)) { + clearValue(QLatin1String("backupOfExistingDestination")); + return; + } + + setValue(QLatin1String("backupOfExistingDestination"), backupFileName(dest)); + + // race condition: The backup file could get created by another process right now. But this is the same + // in QFile::copy... + if (!QFile::rename(dest, value(QLatin1String("backupOfExistingDestination")).toString())) + setError(UserDefinedError, tr("Cannot backup file \"%1\".").arg(QDir::toNativeSeparators(dest))); +} + +bool MoveOperation::performOperation() +{ + // We need two args to complete the copy operation. // First arg provides the complete file name of + // source, second arg provides the complete file name of dest + if (!checkArgumentCount(2)) + return false; + + const QStringList args = arguments(); + const QString dest = args.at(1); + // If destination file exists, then we cannot use QFile::copy() because it does not overwrite an existing + // file. So we remove the destination file. + if (QFile::exists(dest)) { + QFile file(dest); + if (!file.remove(dest)) { + setError(UserDefinedError); + setErrorString(tr("Cannot remove file \"%1\": %2").arg( + QDir::toNativeSeparators(dest), file.errorString())); + return false; + } + } + + // Copy source to destination. + QFile file(args.at(0)); + if (!file.copy(dest)) { + setError(UserDefinedError); + setErrorString(tr("Cannot copy file \"%1\" to \"%2\": %3").arg(QDir::toNativeSeparators(file.fileName()), + QDir::toNativeSeparators(dest), file.errorString())); + return false; + } + return deleteFileNowOrLater(file.fileName()); +} + +bool MoveOperation::undoOperation() +{ + const QStringList args = arguments(); + const QString dest = args.last(); + // first: copy back the destination to source + QFile destF(dest); + if (!destF.copy(args.first())) { + setError(UserDefinedError, tr("Cannot copy file \"%1\" to \"%2\": %3").arg( + QDir::toNativeSeparators(dest), QDir::toNativeSeparators(args.first()), destF.errorString())); + return false; + } + + // second: delete the move destination + if (!deleteFileNowOrLater(dest)) { + setError(UserDefinedError, tr("Cannot remove file \"%1\".").arg(QDir::toNativeSeparators(dest))); + return false; + } + + // no backup was done: + // the move destination file wasn't existing yet - that's no error + if (!hasValue(QLatin1String("backupOfExistingDestination"))) + return true; + + // otherwise we have to copy the backup back: + QFile backupF(value(QLatin1String("backupOfExistingDestination")).toString()); + const bool success = backupF.rename(dest); + if (!success) + setError(UserDefinedError, tr("Cannot restore backup file for \"%1\": %2").arg( + QDir::toNativeSeparators(dest), backupF.errorString())); + + return success; +} + +bool MoveOperation::testOperation() +{ + // TODO + return true; +} + + +//////////////////////////////////////////////////////////////////////////// +// KDUpdater::DeleteOperation +//////////////////////////////////////////////////////////////////////////// + +DeleteOperation::DeleteOperation(QInstaller::PackageManagerCore *core) + : UpdateOperation(core) +{ + setName(QLatin1String("Delete")); +} + +DeleteOperation::~DeleteOperation() +{ + deleteFileNowOrLater(value(QLatin1String("backupOfExistingFile")).toString()); +} + +void DeleteOperation::backup() +{ + const QString fileName = arguments().first(); + setValue(QLatin1String("backupOfExistingFile"), backupFileName(fileName)); + + QFile file(fileName); + if (!file.copy(value(QLatin1String("backupOfExistingFile")).toString())) + setError(UserDefinedError, tr("Cannot create backup of file \"%1\": %2").arg( + QDir::toNativeSeparators(fileName), file.errorString())); +} + +bool DeleteOperation::performOperation() +{ + // Requires only one parameter. That is the name of the file to remove. + if (!checkArgumentCount(1)) + return false; + + return deleteFileNowOrLater(arguments().at(0)); +} + +bool DeleteOperation::undoOperation() +{ + if (!hasValue(QLatin1String("backupOfExistingFile"))) + return true; + + const QString fileName = arguments().first(); + QFile backupF(value(QLatin1String("backupOfExistingFile")).toString()); + const bool success = backupF.copy(fileName) && deleteFileNowOrLater(backupF.fileName()); + if (!success) + setError(UserDefinedError, tr("Cannot restore backup file for \"%1\": %2").arg( + QDir::toNativeSeparators(fileName), backupF.errorString())); + return success; +} + +bool DeleteOperation::testOperation() +{ + // TODO + return true; +} + +/*! + \reimp + */ +QDomDocument DeleteOperation::toXml() const +{ + // we don't want to save the backupOfExistingFile + if (!hasValue(QLatin1String("backupOfExistingFile"))) + return UpdateOperation::toXml(); + + DeleteOperation *const me = const_cast(this); + + const QVariant v = value(QLatin1String("backupOfExistingFile")); + me->clearValue(QLatin1String("backupOfExistingFile")); + const QDomDocument xml = UpdateOperation::toXml(); + me->setValue(QLatin1String("backupOfExistingFile"), v); + return xml; +} + +//////////////////////////////////////////////////////////////////////////// +// KDUpdater::MkdirOperation +//////////////////////////////////////////////////////////////////////////// + +MkdirOperation::MkdirOperation(QInstaller::PackageManagerCore *core) + : UpdateOperation(core) +{ + setName(QLatin1String("Mkdir")); +} + +void MkdirOperation::backup() +{ + QString path = arguments().first(); + path.replace(QLatin1Char('\\'), QLatin1Char('/')); + + QDir createdDir = QDir::root(); + + // find out, which part of the path is the first one we actually need to create + int end = 0; + while (true) { + QString p = path.section(QLatin1Char('/'), 0, ++end); + createdDir = QDir(p); + if (!createdDir.exists()) + break; + if (p == path) { + // everything did already exist -> nothing to do for us (nothing to revert then, either) + createdDir = QDir::root(); + break; + } + } + + setValue(QLatin1String("createddir"), createdDir.absolutePath()); +} + +bool MkdirOperation::performOperation() +{ + // Requires only one parameter. That is the path which should be created + if (!checkArgumentCount(1)) + return false; + + const QString dirName = arguments().at(0); + const bool created = QDir::root().mkpath(dirName); + if (!created) { + setError(UserDefinedError); + setErrorString(tr("Cannot create directory \"%1\": %2").arg( + QDir::toNativeSeparators(dirName), tr("Unknown error."))); + } + return created; +} + +bool MkdirOperation::undoOperation() +{ + Q_ASSERT(arguments().count() == 1); + + QString createdDirValue = value(QLatin1String("createddir")).toString(); + if (createdDirValue.isEmpty()) + createdDirValue = arguments().first(); + QDir createdDir = QDir(createdDirValue); + const bool forceremoval = QVariant(value(QLatin1String("forceremoval"))).toBool(); + + // Since refactoring we know the mkdir operation which is creating the target path. If we do a full + // uninstall prevent removing the full path including target, instead remove the target only. (QTIFW-46) + if (hasValue(QLatin1String("uninstall-only")) && value(QLatin1String("uninstall-only")).toBool()) + createdDir = QDir(arguments().first()); + + if (createdDir == QDir::root()) + return true; + + if (!createdDir.exists()) + return true; + + QString errorString; + + const bool result = removeDirectory(createdDir.path(), &errorString, forceremoval); + + if (!result) { + if (errorString.isEmpty()) + setError(UserDefinedError, tr("Cannot remove directory \"%1\": %2").arg( + QDir::toNativeSeparators(createdDir.path()), errorString)); + else + setError(UserDefinedError, tr("Cannot remove directory \"%1\": %2").arg( + QDir::toNativeSeparators(createdDir.path()), errnoToQString(errno))); + } + return result; +} + +bool KDUpdater::MkdirOperation::testOperation() +{ + // TODO + return true; +} + + +//////////////////////////////////////////////////////////////////////////// +// KDUpdater::RmdirOperation +//////////////////////////////////////////////////////////////////////////// + +RmdirOperation::RmdirOperation(QInstaller::PackageManagerCore *core) + : UpdateOperation(core) +{ + setName(QLatin1String("Rmdir")); + setValue(QLatin1String("removed"), false); +} + +void RmdirOperation::backup() +{ + // nothing to backup - rollback will just create the directory +} + +bool RmdirOperation::performOperation() +{ + // Requires only one parameter. That is the name of the file to remove. + if (!checkArgumentCount(1)) + return false; + + const QString firstArg = arguments().at(0); + QDir dir(firstArg); + if (!dir.exists()) { + setError(UserDefinedError); + setErrorString(tr("Cannot remove directory \"%1\": %2").arg( + QDir::toNativeSeparators(firstArg), tr("The directory does not exist."))); + return false; + } + + errno = 0; + const bool removed = dir.rmdir(firstArg); + setValue(QLatin1String("removed"), removed); + if (!removed) { + setError(UserDefinedError); + setErrorString(tr("Cannot remove directory \"%1\": %2").arg( + QDir::toNativeSeparators(firstArg), errnoToQString(errno))); + } + return removed; +} + +bool RmdirOperation::undoOperation() +{ + if (!value(QLatin1String("removed")).toBool()) + return true; + + errno = 0; + const QFileInfo fi(arguments().first()); + const bool success = fi.dir().mkdir(fi.fileName()); + if( !success) + setError(UserDefinedError, tr("Cannot recreate directory \"%1\": %2").arg( + QDir::toNativeSeparators(fi.fileName()), errnoToQString(errno))); + + return success; +} + +bool RmdirOperation::testOperation() +{ + // TODO + return true; +} + + +//////////////////////////////////////////////////////////////////////////// +// KDUpdater::AppendFileOperation +//////////////////////////////////////////////////////////////////////////// + +AppendFileOperation::AppendFileOperation(QInstaller::PackageManagerCore *core) + : UpdateOperation(core) +{ + setName(QLatin1String("AppendFile")); +} + +void AppendFileOperation::backup() +{ + const QString filename = arguments().first(); + + QFile file(filename); + if (!file.exists()) + return; // nothing to backup + + setValue(QLatin1String("backupOfFile"), backupFileName(filename)); + if (!file.copy(value(QLatin1String("backupOfFile")).toString())) { + setError(UserDefinedError, tr("Cannot backup file \"%1\": %2").arg( + QDir::toNativeSeparators(filename), file.errorString())); + clearValue(QLatin1String("backupOfFile")); + } +} + +bool AppendFileOperation::performOperation() +{ + // This operation takes two arguments. First argument is the name of the file into which a text has to be + // appended. Second argument is the text to append. + if (!checkArgumentCount(2)) + return false; + + const QStringList args = this->arguments(); + const QString fName = args.at(0); + QFile file(fName); + if (!file.open(QFile::Append)) { + // first we rename the file, then we copy it to the real target and open the copy - the renamed original is then marked for deletion + bool error = false; + const QString newName = backupFileName(fName); + + if (!QFile::rename(fName, newName)) + error = true; + + if (!error && !QFile::copy(newName, fName)) { + error = true; + QFile::rename(newName, fName); + } + + if (!error && !file.open(QFile::Append)) { + error = true; + deleteFileNowOrLater(newName); + } + + if (error) { + setError(UserDefinedError); + setErrorString(tr("Cannot open file \"%1\" for writing: %2").arg( + QDir::toNativeSeparators(file.fileName()), file.errorString())); + return false; + } + deleteFileNowOrLater(newName); + } + + QTextStream ts(&file); + ts << args.at(1); + file.close(); + + return true; +} + +bool AppendFileOperation::undoOperation() +{ + // backupOfFile being empty -> file didn't exist before -> no error + const QString filename = arguments().first(); + const QString backupOfFile = value(QLatin1String("backupOfFile")).toString(); + if (!backupOfFile.isEmpty() && !QFile::exists(backupOfFile)) { + setError(UserDefinedError, tr("Cannot find backup file for \"%1\".").arg( + QDir::toNativeSeparators(filename))); + return false; + } + + const bool removed = deleteFileNowOrLater(filename); + if (!removed) { + setError(UserDefinedError, tr("Cannot restore backup file for \"%1\".").arg( + QDir::toNativeSeparators(filename))); + return false; + } + + // got deleted? We might be done, if it didn't exist before + if (backupOfFile.isEmpty()) + return true; + + QFile backupFile(backupOfFile); + const bool success = backupFile.rename(filename); + if (!success) + setError(UserDefinedError, tr("Cannot restore backup file for \"%1\": %2").arg( + QDir::toNativeSeparators(filename), backupFile.errorString())); + return success; +} + +bool AppendFileOperation::testOperation() +{ + // TODO + return true; +} + + +//////////////////////////////////////////////////////////////////////////// +// KDUpdater::PrependFileOperation +//////////////////////////////////////////////////////////////////////////// + +PrependFileOperation::PrependFileOperation(QInstaller::PackageManagerCore *core) + : UpdateOperation(core) +{ + setName(QLatin1String("PrependFile")); +} + +void PrependFileOperation::backup() +{ + const QString filename = arguments().first(); + + QFile file(filename); + if (!file.exists()) + return; // nothing to backup + + setValue(QLatin1String("backupOfFile"), backupFileName(filename)); + if (!file.copy(value(QLatin1String("backupOfFile")).toString())) { + setError(UserDefinedError, tr("Cannot backup file \"%1\": %2").arg( + QDir::toNativeSeparators(filename), file.errorString())); + clearValue(QLatin1String("backupOfFile")); + } +} + +bool PrependFileOperation::performOperation() +{ + // This operation takes two arguments. First argument is the name + // of the file into which a text has to be appended. Second argument + // is the text to append. + if (!checkArgumentCount(2)) + return false; + + const QStringList args = this->arguments(); + const QString fName = args.at(0); + // Load the file first. + QFile file(fName); + if (!file.open(QFile::ReadOnly)) { + setError(UserDefinedError); + setErrorString(tr("Cannot open file \"%1\" for reading: %2").arg( + QDir::toNativeSeparators(file.fileName()), file.errorString())); + return false; + } + + // TODO: fix this, use a text stream + QString fContents(QLatin1String(file.readAll())); + file.close(); + + // Prepend text to the file text + fContents = args.at(1) + fContents; + + // Now re-open the file in write only mode. + if (!file.open(QFile::WriteOnly)) { + // first we rename the file, then we copy it to the real target and open the copy - the renamed original is then marked for deletion + const QString newName = backupFileName(fName); + if (!QFile::rename(fName, newName) && QFile::copy(newName, fName) && file.open(QFile::WriteOnly)) { + QFile::rename(newName, fName); + setError(UserDefinedError); + setErrorString(tr("Cannot open file \"%1\" for writing: %2").arg( + QDir::toNativeSeparators(file.fileName()), file.errorString())); + return false; + } + deleteFileNowOrLater(newName); + } + QTextStream ts(&file); + ts << fContents; + file.close(); + + return true; +} + +bool PrependFileOperation::undoOperation() +{ + // bockupOfFile being empty -> file didn't exist before -> no error + const QString filename = arguments().first(); + const QString backupOfFile = value(QLatin1String("backupOfFile")).toString(); + if (!backupOfFile.isEmpty() && !QFile::exists(backupOfFile)) { + setError(UserDefinedError, + tr("Cannot find backup file for \"%1\".").arg(QDir::toNativeSeparators(filename))); + return false; + } + + if (!deleteFileNowOrLater(filename)) { + setError(UserDefinedError, + tr("Cannot restore backup file for \"%1\".").arg(QDir::toNativeSeparators(filename))); + return false; + } + + // got deleted? We might be done, if it didn't exist before + if (backupOfFile.isEmpty()) + return true; + + QFile backupF(backupOfFile); + const bool success = backupF.rename(filename); + if (!success) + setError(UserDefinedError, tr("Cannot restore backup file for \"%1\": %2").arg( + QDir::toNativeSeparators(filename), backupF.errorString())); + + return success; +} + +bool PrependFileOperation::testOperation() +{ + // TODO + return true; +} -- cgit v1.2.3