diff --git a/src/utils/path-helper.cpp b/src/utils/path-helper.cpp index f27715648..7509cf0e5 100644 --- a/src/utils/path-helper.cpp +++ b/src/utils/path-helper.cpp @@ -1,104 +1,178 @@ /* -*- mode: c++; c-basic-offset:4 -*- utils/path-helper.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "path-helper.h" #include #include #include "kleopatra_debug.h" #include #include #include #include #include #include #include using namespace Kleo; static QString commonPrefix(const QString &s1, const QString &s2) { return QString(s1.data(), std::mismatch(s1.data(), s1.data() + std::min(s1.size(), s2.size()), s2.data()).first - s1.data()); } static QString longestCommonPrefix(const QStringList &sl) { if (sl.empty()) { return QString(); } QString result = sl.front(); for (const QString &s : sl) { result = commonPrefix(s, result); } return result; } QString Kleo::heuristicBaseDirectory(const QStringList &fileNames) { QStringList dirs; for (const QString &fileName : fileNames) { dirs.push_back(QFileInfo(fileName).path() + QLatin1Char('/')); } qCDebug(KLEOPATRA_LOG) << "dirs" << dirs; const QString candidate = longestCommonPrefix(dirs); /* Special case handling for Outlook and KMail attachment temporary path. * This is otherwise something like: * c:\users\username\AppData\Local\Microsoft\Windows\INetCache\ * Content.Outlook\ADSDFG9\foo.txt * * For KMail it is usually /tmp/messageviewer/foo * * Both are paths that are unlikely to be the target path to save the * decrypted attachment. * * This is very common when encrypted attachments are opened * within Outlook or KMail. */ if (candidate.contains(QStringLiteral("Content.Outlook")) || candidate.contains(QStringLiteral("messageviewer"))) { return QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); } const int idx = candidate.lastIndexOf(QLatin1Char('/')); return candidate.left(idx); } QStringList Kleo::makeRelativeTo(const QString &base, const QStringList &fileNames) { if (base.isEmpty()) { return fileNames; } else { return makeRelativeTo(QDir(base), fileNames); } } QStringList Kleo::makeRelativeTo(const QDir &baseDir, const QStringList &fileNames) { QStringList rv; rv.reserve(fileNames.size()); std::transform(fileNames.cbegin(), fileNames.cend(), std::back_inserter(rv), [&baseDir](const QString &file) { return baseDir.relativeFilePath(file); }); return rv; } QString Kleo::stripSuffix(const QString &fileName) { const QFileInfo fi(fileName); return fi.dir().filePath(fi.completeBaseName()); } + +void Kleo::recursivelyRemovePath(const QString &path) +{ + const QFileInfo fi(path); + if (fi.isDir()) { + QDir dir(path); + const auto dirs{dir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot)}; + for (const QString &fname : dirs) { + recursivelyRemovePath(dir.filePath(fname)); + } + const QString dirName = fi.fileName(); + dir.cdUp(); + if (!dir.rmdir(dirName)) { + throw Exception(GPG_ERR_EPERM, i18n("Cannot remove directory %1", path)); + } + } else { + QFile file(path); + if (!file.remove()) { + throw Exception(GPG_ERR_EPERM, i18n("Cannot remove file %1: %2", path, file.errorString())); + } + } +} + +bool Kleo::recursivelyCopy(const QString &src,const QString &dest) +{ + QDir srcDir(src); + + if(!srcDir.exists()) { + return false; + } + + QDir destDir(dest); + if(!destDir.exists() && !destDir.mkdir(dest)) { + return false; + } + + for(const auto &file: srcDir.entryList(QDir::Files)) { + const QString srcName = src + QLatin1Char('/') + file; + const QString destName = dest + QLatin1Char('/') + file; + if(!QFile::copy(srcName, destName)) { + return false; + } + } + + for (const auto &dir: srcDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) { + const QString srcName = src + QLatin1Char('/') + dir; + const QString destName = dest + QLatin1Char('/') + dir; + if (!recursivelyCopy(srcName, destName)) { + return false; + } + } + + return true; +} + +bool Kleo::moveDir(const QString &src, const QString &dest) +{ + // Need an existing path to query the device + const QString parentDest = QFileInfo(dest).dir().absolutePath(); + const auto srcDevice = QStorageInfo(src).device(); + if (!srcDevice.isEmpty() && srcDevice == QStorageInfo(parentDest).device() && + QFile::rename(src, dest)) { + qCDebug(KLEOPATRA_LOG) << "Renamed" << src << "to" << dest; + return true; + } + // first copy + if (!recursivelyCopy(src, dest)) { + return false; + } + // Then delete original + recursivelyRemovePath(src); + + return true; +} diff --git a/src/utils/path-helper.h b/src/utils/path-helper.h index 9c9cf077a..aba3f72b2 100644 --- a/src/utils/path-helper.h +++ b/src/utils/path-helper.h @@ -1,25 +1,28 @@ /* -*- mode: c++; c-basic-offset:4 -*- utils/path-helper.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once class QString; #include class QDir; namespace Kleo { QString heuristicBaseDirectory(const QStringList &files); QStringList makeRelativeTo(const QDir &dir, const QStringList &files); QStringList makeRelativeTo(const QString &dir, const QStringList &files); QString stripSuffix(const QString &fileName); +void recursivelyRemovePath(const QString &path); +bool recursivelyCopy(const QString &src, const QString &dest); +bool moveDir(const QString &src, const QString &dest); }