diff --git a/autotests/kerfuffle/data/testdirwithemptysubdir_1.rar b/autotests/kerfuffle/data/testdirwithemptysubdir_1.rar new file mode 100644 index 00000000..e3183c7a Binary files /dev/null and b/autotests/kerfuffle/data/testdirwithemptysubdir_1.rar differ diff --git a/plugins/cli7zplugin/cliplugin.cpp b/plugins/cli7zplugin/cliplugin.cpp index 166a66a0..41a2be53 100644 --- a/plugins/cli7zplugin/cliplugin.cpp +++ b/plugins/cli7zplugin/cliplugin.cpp @@ -1,329 +1,335 @@ /* * ark -- archiver for the KDE project * * Copyright (C) 2009 Harald Hvaal * Copyright (C) 2009-2011 Raphael Kubo da Costa * Copyright (c) 2016 Vladyslav Batyrenko * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "cliplugin.h" #include "ark_debug.h" #include "cliinterface.h" #include #include #include #include #include using namespace Kerfuffle; K_PLUGIN_FACTORY_WITH_JSON(CliPluginFactory, "kerfuffle_cli7z.json", registerPlugin();) CliPlugin::CliPlugin(QObject *parent, const QVariantList & args) : CliInterface(parent, args) , m_archiveType(ArchiveType7z) , m_parseState(ParseStateTitle) , m_linesComment(0) , m_isFirstInformationEntry(true) { qCDebug(ARK) << "Loaded cli_7z plugin"; setupCliProperties(); } CliPlugin::~CliPlugin() { } void CliPlugin::resetParsing() { m_parseState = ParseStateTitle; m_comment.clear(); m_numberOfVolumes = 0; } void CliPlugin::setupCliProperties() { qCDebug(ARK) << "Setting up parameters..."; m_cliProps->setProperty("captureProgress", false); m_cliProps->setProperty("addProgram", QStringLiteral("7z")); m_cliProps->setProperty("addSwitch", QStringList{QStringLiteral("a"), QStringLiteral("-l")}); m_cliProps->setProperty("deleteProgram", QStringLiteral("7z")); m_cliProps->setProperty("deleteSwitch", QStringLiteral("d")); m_cliProps->setProperty("extractProgram", QStringLiteral("7z")); m_cliProps->setProperty("extractSwitch", QStringList{QStringLiteral("x")}); m_cliProps->setProperty("extractSwitchNoPreserve", QStringList{QStringLiteral("e")}); m_cliProps->setProperty("listProgram", QStringLiteral("7z")); m_cliProps->setProperty("listSwitch", QStringList{QStringLiteral("l"), QStringLiteral("-slt")}); m_cliProps->setProperty("moveProgram", QStringLiteral("7z")); m_cliProps->setProperty("moveSwitch", QStringLiteral("rn")); m_cliProps->setProperty("testProgram", QStringLiteral("7z")); m_cliProps->setProperty("testSwitch", QStringLiteral("t")); m_cliProps->setProperty("passwordSwitch", QStringList{QStringLiteral("-p$Password")}); m_cliProps->setProperty("passwordSwitchHeaderEnc", QStringList{QStringLiteral("-p$Password"), QStringLiteral("-mhe=on")}); m_cliProps->setProperty("compressionLevelSwitch", QStringLiteral("-mx=$CompressionLevel")); m_cliProps->setProperty("compressionMethodSwitch", QHash{{QStringLiteral("application/x-7z-compressed"), QStringLiteral("-m0=$CompressionMethod")}, {QStringLiteral("application/zip"), QStringLiteral("-mm=$CompressionMethod")}}); m_cliProps->setProperty("encryptionMethodSwitch", QHash{{QStringLiteral("application/x-7z-compressed"), QStringLiteral()}, {QStringLiteral("application/zip"), QStringLiteral("-mem=$EncryptionMethod")}}); m_cliProps->setProperty("multiVolumeSwitch", QStringLiteral("-v$VolumeSizek")); m_cliProps->setProperty("passwordPromptPatterns", QStringList{QStringLiteral("Enter password \\(will not be echoed\\)")}); m_cliProps->setProperty("wrongPasswordPatterns", QStringList{QStringLiteral("Wrong password")}); m_cliProps->setProperty("testPassedPatterns", QStringList{QStringLiteral("^Everything is Ok$")}); m_cliProps->setProperty("fileExistsPatterns", QStringList{QStringLiteral("^\\(Y\\)es / \\(N\\)o / \\(A\\)lways / \\(S\\)kip all / A\\(u\\)to rename all / \\(Q\\)uit\\? $"), QStringLiteral("^\\? \\(Y\\)es / \\(N\\)o / \\(A\\)lways / \\(S\\)kip all / A\\(u\\)to rename all / \\(Q\\)uit\\? $")}); m_cliProps->setProperty("fileExistsFileName", QStringList{QStringLiteral("^file \\./(.*)$"), QStringLiteral("^ Path: \\./(.*)$")}); m_cliProps->setProperty("fileExistsInput", QStringList{QStringLiteral("Y"), //Overwrite QStringLiteral("N"), //Skip QStringLiteral("A"), //Overwrite all QStringLiteral("S"), //Autoskip QStringLiteral("Q")}); //Cancel m_cliProps->setProperty("corruptArchivePatterns", QStringList{QStringLiteral("Unexpected end of archive"), QStringLiteral("Headers Error")}); m_cliProps->setProperty("diskFullPatterns", QStringList{QStringLiteral("No space left on device")}); m_cliProps->setProperty("multiVolumeSuffix", QStringList{QStringLiteral("$Suffix.001")}); } bool CliPlugin::readListLine(const QString& line) { static const QLatin1String archiveInfoDelimiter1("--"); // 7z 9.13+ static const QLatin1String archiveInfoDelimiter2("----"); // 7z 9.04 static const QLatin1String entryInfoDelimiter("----------"); const QRegularExpression rxComment(QStringLiteral("Comment = .+$")); const QRegularExpression rxListFailed(QStringLiteral("Open ERROR: Can not open the file as \\[7z\\] archive")); if (rxListFailed.match(line).hasMatch()) { emit error(i18n("Listing the archive failed.")); return false; } if (m_parseState == ParseStateTitle) { const QRegularExpression rxVersionLine(QStringLiteral("^p7zip Version ([\\d\\.]+) .*$")); QRegularExpressionMatch matchVersion = rxVersionLine.match(line); if (matchVersion.hasMatch()) { m_parseState = ParseStateHeader; const QString p7zipVersion = matchVersion.captured(1); qCDebug(ARK) << "p7zip version" << p7zipVersion << "detected"; } } else if (m_parseState == ParseStateHeader) { if (line.startsWith(QStringLiteral("Listing archive:"))) { qCDebug(ARK) << "Archive name: " << line.right(line.size() - 16).trimmed(); } else if ((line == archiveInfoDelimiter1) || (line == archiveInfoDelimiter2)) { m_parseState = ParseStateArchiveInformation; } else if (line.contains(QStringLiteral("Error: "))) { qCWarning(ARK) << line.mid(7); } } else if (m_parseState == ParseStateArchiveInformation) { if (line == entryInfoDelimiter) { m_parseState = ParseStateEntryInformation; } else if (line.startsWith(QStringLiteral("Type = "))) { const QString type = line.mid(7).trimmed(); qCDebug(ARK) << "Archive type: " << type; if (type == QLatin1String("7z")) { m_archiveType = ArchiveType7z; } else if (type == QLatin1String("bzip2")) { m_archiveType = ArchiveTypeBZip2; } else if (type == QLatin1String("gzip")) { m_archiveType = ArchiveTypeGZip; } else if (type == QLatin1String("xz")) { m_archiveType = ArchiveTypeXz; } else if (type == QLatin1String("tar")) { m_archiveType = ArchiveTypeTar; } else if (type == QLatin1String("zip")) { m_archiveType = ArchiveTypeZip; } else if (type == QLatin1String("Rar")) { m_archiveType = ArchiveTypeRar; } else if (type == QLatin1String("Split")) { setMultiVolume(true); } else { // Should not happen qCWarning(ARK) << "Unsupported archive type"; return false; } } else if (line.startsWith(QStringLiteral("Volumes = "))) { m_numberOfVolumes = line.section(QLatin1Char('='), 1).trimmed().toInt(); } else if (line.startsWith(QStringLiteral("Method = "))) { QStringList methods = line.section(QLatin1Char('='), 1).trimmed().split(QLatin1Char(' '), QString::SkipEmptyParts); handleMethods(methods); } else if (rxComment.match(line).hasMatch()) { m_parseState = ParseStateComment; m_comment.append(line.section(QLatin1Char('='), 1) + QLatin1Char('\n')); } } else if (m_parseState == ParseStateComment) { if (line == entryInfoDelimiter) { m_parseState = ParseStateEntryInformation; if (!m_comment.trimmed().isEmpty()) { m_comment = m_comment.trimmed(); m_linesComment = m_comment.count(QLatin1Char('\n')) + 1; qCDebug(ARK) << "Found a comment with" << m_linesComment << "lines"; } } else { m_comment.append(line + QLatin1Char('\n')); } } else if (m_parseState == ParseStateEntryInformation) { if (m_isFirstInformationEntry) { m_isFirstInformationEntry = false; m_currentArchiveEntry = new Archive::Entry(this); m_currentArchiveEntry->compressedSizeIsSet = false; } if (line.startsWith(QStringLiteral("Path = "))) { const QString entryFilename = QDir::fromNativeSeparators(line.mid(7).trimmed()); m_currentArchiveEntry->setProperty("fullPath", entryFilename); } else if (line.startsWith(QStringLiteral("Size = "))) { m_currentArchiveEntry->setProperty("size", line.mid(7).trimmed()); } else if (line.startsWith(QStringLiteral("Packed Size = "))) { // #236696: 7z files only show a single Packed Size value // corresponding to the whole archive. if (m_archiveType != ArchiveType7z) { m_currentArchiveEntry->compressedSizeIsSet = true; m_currentArchiveEntry->setProperty("compressedSize", line.mid(14).trimmed()); } } else if (line.startsWith(QStringLiteral("Modified = "))) { m_currentArchiveEntry->setProperty("timestamp", QDateTime::fromString(line.mid(11).trimmed(), QStringLiteral("yyyy-MM-dd hh:mm:ss"))); } else if (line.startsWith(QStringLiteral("Attributes = "))) { const QString attributes = line.mid(13).trimmed(); const bool isDirectory = attributes.startsWith(QLatin1Char('D')); m_currentArchiveEntry->setProperty("isDirectory", isDirectory); if (isDirectory) { const QString directoryName = m_currentArchiveEntry->fullPath(); if (!directoryName.endsWith(QLatin1Char('/'))) { const bool isPasswordProtected = (line.at(12) == QLatin1Char('+')); m_currentArchiveEntry->setProperty("fullPath", QString(directoryName + QLatin1Char('/'))); m_currentArchiveEntry->setProperty("isPasswordProtected", isPasswordProtected); } } m_currentArchiveEntry->setProperty("permissions", attributes.mid(1)); } else if (line.startsWith(QStringLiteral("CRC = "))) { m_currentArchiveEntry->setProperty("CRC", line.mid(6).trimmed()); } else if (line.startsWith(QStringLiteral("Method = "))) { m_currentArchiveEntry->setProperty("method", line.mid(9).trimmed()); // For zip archives we need to check method for each entry. if (m_archiveType == ArchiveTypeZip) { QStringList methods = line.section(QLatin1Char('='), 1).trimmed().split(QLatin1Char(' '), QString::SkipEmptyParts); handleMethods(methods); } } else if (line.startsWith(QStringLiteral("Encrypted = ")) && line.size() >= 13) { m_currentArchiveEntry->setProperty("isPasswordProtected", line.at(12) == QLatin1Char('+')); } else if (line.startsWith(QStringLiteral("Block = ")) || line.startsWith(QStringLiteral("Version = "))) { m_isFirstInformationEntry = true; if (!m_currentArchiveEntry->fullPath().isEmpty()) { emit entry(m_currentArchiveEntry); } else { delete m_currentArchiveEntry; } m_currentArchiveEntry = nullptr; } } return true; } bool CliPlugin::readExtractLine(const QString &line) { - QRegularExpression rx(QStringLiteral("ERROR: E_FAIL")); + const QRegularExpression rxUnknownError(QStringLiteral("ERROR: E_FAIL")); + const QRegularExpression rxBadCRC(QStringLiteral("ERROR: CRC Failed")); - if (rx.match(line).hasMatch()) { - emit error(i18n("Extraction failed.")); + if (rxUnknownError.match(line).hasMatch()) { + emit error(i18n("Extraction failed due to an unknown error.")); + return false; + } + + if (rxBadCRC.match(line).hasMatch()) { + emit error(i18n("Extraction failed due to one or more corrupt files. Any extracted files may be damaged.")); return false; } return true; } bool CliPlugin::readDeleteLine(const QString &line) { QRegularExpression rx(QStringLiteral("Error: .+ is not supported archive")); if (rx.match(line).hasMatch()) { emit error(i18n("Delete operation failed. Try upgrading p7zip or disabling the p7zip plugin in the configuration dialog.")); return false; } return true; } void CliPlugin::handleMethods(const QStringList &methods) { foreach (const QString &method, methods) { QRegularExpression rxEncMethod(QStringLiteral("^(7zAES|AES-128|AES-192|AES-256|ZipCrypto)$")); if (rxEncMethod.match(method).hasMatch()) { QRegularExpression rxAESMethods(QStringLiteral("^(AES-128|AES-192|AES-256)$")); if (rxAESMethods.match(method).hasMatch()) { // Remove dash for AES methods. emit encryptionMethodFound(QString(method).remove(QLatin1Char('-'))); } else { emit encryptionMethodFound(method); } continue; } // LZMA methods are output with some trailing numbers by 7z representing dictionary/block sizes. // We are not interested in these, so remove them. if (method.startsWith(QLatin1String("LZMA2"))) { emit compressionMethodFound(method.left(5)); } else if (method.startsWith(QLatin1String("LZMA"))) { emit compressionMethodFound(method.left(4)); } else if (method == QLatin1String("xz")) { emit compressionMethodFound(method.toUpper()); } else { emit compressionMethodFound(method); } } } #include "cliplugin.moc" diff --git a/plugins/clizipplugin/cliplugin.cpp b/plugins/clizipplugin/cliplugin.cpp index 8727ac0c..a3674dbf 100644 --- a/plugins/clizipplugin/cliplugin.cpp +++ b/plugins/clizipplugin/cliplugin.cpp @@ -1,331 +1,337 @@ /* * ark -- archiver for the KDE project * * Copyright (C) 2009 Harald Hvaal * Copyright (C) 2009-2011 Raphael Kubo da Costa * Copyright (c) 2016 Vladyslav Batyrenko * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "cliplugin.h" #include "ark_debug.h" #include "cliinterface.h" #include #include #include #include #include #include using namespace Kerfuffle; K_PLUGIN_FACTORY_WITH_JSON(CliPluginFactory, "kerfuffle_clizip.json", registerPlugin();) CliPlugin::CliPlugin(QObject *parent, const QVariantList & args) : CliInterface(parent, args) , m_parseState(ParseStateHeader) , m_linesComment(0) { qCDebug(ARK) << "Loaded cli_zip plugin"; setupCliProperties(); } CliPlugin::~CliPlugin() { } void CliPlugin::resetParsing() { m_parseState = ParseStateHeader; m_tempComment.clear(); m_comment.clear(); } // #208091: infozip applies special meanings to some characters, so we // need to escape them with backslashes.see match.c in // infozip's source code QString CliPlugin::escapeFileName(const QString &fileName) const { const QString escapedCharacters(QStringLiteral("[]*?^-\\!")); QString quoted; const int len = fileName.length(); const QLatin1Char backslash('\\'); quoted.reserve(len * 2); for (int i = 0; i < len; ++i) { if (escapedCharacters.contains(fileName.at(i))) { quoted.append(backslash); } quoted.append(fileName.at(i)); } return quoted; } void CliPlugin::setupCliProperties() { qCDebug(ARK) << "Setting up parameters..."; m_cliProps->setProperty("captureProgress", false); m_cliProps->setProperty("addProgram", QStringLiteral("zip")); m_cliProps->setProperty("addSwitch", QStringList({QStringLiteral("-r")})); m_cliProps->setProperty("deleteProgram", QStringLiteral("zip")); m_cliProps->setProperty("deleteSwitch", QStringLiteral("-d")); m_cliProps->setProperty("extractProgram", QStringLiteral("unzip")); m_cliProps->setProperty("extractSwitchNoPreserve", QStringList{QStringLiteral("-j")}); m_cliProps->setProperty("listProgram", QStringLiteral("zipinfo")); m_cliProps->setProperty("listSwitch", QStringList{QStringLiteral("-l"), QStringLiteral("-T"), QStringLiteral("-z")}); m_cliProps->setProperty("testProgram", QStringLiteral("unzip")); m_cliProps->setProperty("testSwitch", QStringLiteral("-t")); m_cliProps->setProperty("passwordSwitch", QStringList{QStringLiteral("-P$Password")}); m_cliProps->setProperty("compressionLevelSwitch", QStringLiteral("-$CompressionLevel")); m_cliProps->setProperty("compressionMethodSwitch", QHash{{QStringLiteral("application/zip"), QStringLiteral("-Z$CompressionMethod")}, {QStringLiteral("application/x-java-archive"), QStringLiteral("-Z$CompressionMethod")}}); m_cliProps->setProperty("multiVolumeSwitch", QStringLiteral("-v$VolumeSizek")); m_cliProps->setProperty("passwordPromptPatterns", QStringList{QStringLiteral(" password: ")}); m_cliProps->setProperty("wrongPasswordPatterns", QStringList{QStringLiteral("incorrect password")}); m_cliProps->setProperty("testPassedPatterns", QStringList{QStringLiteral("^No errors detected in compressed data of ")}); m_cliProps->setProperty("fileExistsPatterns", QStringList{QStringLiteral("^replace (.+)\\? \\[y\\]es, \\[n\\]o, \\[A\\]ll, \\[N\\]one, \\[r\\]ename: $")}); m_cliProps->setProperty("fileExistsFileName", QStringList{QStringLiteral("^replace (.+)\\? \\[y\\]es, \\[n\\]o, \\[A\\]ll, \\[N\\]one, \\[r\\]ename: $")}); m_cliProps->setProperty("fileExistsInput", QStringList{QStringLiteral("y"), //Overwrite QStringLiteral("n"), //Skip QStringLiteral("A"), //Overwrite all QStringLiteral("N")}); //Autoskip m_cliProps->setProperty("extractionFailedPatterns", QStringList{QStringLiteral("unsupported compression method")}); m_cliProps->setProperty("corruptArchivePatterns", QStringList{QStringLiteral("End-of-central-directory signature not found"), QStringLiteral("didn't find end-of-central-dir signature at end of central dir")}); m_cliProps->setProperty("diskFullPatterns", QStringList{QStringLiteral("write error \\(disk full\\?\\)"), QStringLiteral("No space left on device")}); } bool CliPlugin::readListLine(const QString &line) { static const QRegularExpression entryPattern(QStringLiteral( "^(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\d{8}).(\\d{6})\\s+(.+)$") ); // RegExp to identify the line preceding comments. const QRegularExpression commentPattern(QStringLiteral("^Archive: .*$")); // RegExp to identify the line following comments. const QRegularExpression commentEndPattern(QStringLiteral("^Zip file size: .*$")); switch (m_parseState) { case ParseStateHeader: if (commentPattern.match(line).hasMatch()) { m_parseState = ParseStateComment; } else if (commentEndPattern.match(line).hasMatch()){ m_parseState = ParseStateEntry; } break; case ParseStateComment: if (commentEndPattern.match(line).hasMatch()) { m_parseState = ParseStateEntry; if (!m_tempComment.trimmed().isEmpty()) { m_comment = m_tempComment.trimmed(); m_linesComment = m_comment.count(QLatin1Char('\n')) + 1; qCDebug(ARK) << "Found a comment with" << m_linesComment << "lines"; } } else { m_tempComment.append(line + QLatin1Char('\n')); } case ParseStateEntry: QRegularExpressionMatch rxMatch = entryPattern.match(line); if (rxMatch.hasMatch()) { Archive::Entry *e = new Archive::Entry(this); e->setProperty("permissions", rxMatch.captured(1)); // #280354: infozip may not show the right attributes for a given directory, so an entry // ending with '/' is actually more reliable than 'd' bein in the attributes. e->setProperty("isDirectory", rxMatch.captured(10).endsWith(QLatin1Char('/'))); e->setProperty("size", rxMatch.captured(4)); QString status = rxMatch.captured(5); if (status[0].isUpper()) { e->setProperty("isPasswordProtected", true); } e->setProperty("compressedSize", rxMatch.captured(6).toInt()); e->setProperty("method", rxMatch.captured(7)); QString method = convertCompressionMethod(rxMatch.captured(7)); emit compressionMethodFound(method); const QDateTime ts(QDate::fromString(rxMatch.captured(8), QStringLiteral("yyyyMMdd")), QTime::fromString(rxMatch.captured(9), QStringLiteral("hhmmss"))); e->setProperty("timestamp", ts); e->setProperty("fullPath", rxMatch.captured(10)); emit entry(e); } break; } return true; } bool CliPlugin::readExtractLine(const QString &line) { const QRegularExpression rxUnsupCompMethod(QStringLiteral("unsupported compression method (\\d+)")); const QRegularExpression rxUnsupEncMethod(QStringLiteral("need PK compat. v\\d\\.\\d \\(can do v\\d\\.\\d\\)")); + const QRegularExpression rxBadCRC(QStringLiteral("bad CRC")); QRegularExpressionMatch unsupCompMethodMatch = rxUnsupCompMethod.match(line); if (unsupCompMethodMatch.hasMatch()) { emit error(i18n("Extraction failed due to unsupported compression method (%1).", unsupCompMethodMatch.captured(1))); return false; } - if (QRegularExpression(rxUnsupEncMethod).match(line).hasMatch()) { + if (rxUnsupEncMethod.match(line).hasMatch()) { emit error(i18n("Extraction failed due to unsupported encryption method.")); return false; } + if (rxBadCRC.match(line).hasMatch()) { + emit error(i18n("Extraction failed due to one or more corrupt files. Any extracted files may be damaged.")); + return false; + } + return true; } bool CliPlugin::moveFiles(const QVector &files, Archive::Entry *destination, const CompressionOptions &options) { qCDebug(ARK) << "Moving" << files.count() << "file(s) to destination:" << destination; m_oldWorkingDir = QDir::currentPath(); m_tempWorkingDir.reset(new QTemporaryDir()); m_tempAddDir.reset(new QTemporaryDir()); QDir::setCurrent(m_tempWorkingDir->path()); m_passedFiles = files; m_passedDestination = destination; m_passedOptions = options; m_subOperation = Extract; connect(this, &CliPlugin::finished, this, &CliPlugin::continueMoving); return extractFiles(files, QDir::currentPath(), ExtractionOptions()); } int CliPlugin::moveRequiredSignals() const { return 4; } void CliPlugin::continueMoving(bool result) { if (!result) { finishMoving(false); return; } switch (m_subOperation) { case Extract: m_subOperation = Delete; if (!deleteFiles(m_passedFiles)) { finishMoving(false); } break; case Delete: m_subOperation = Add; if (!setMovingAddedFiles() || !addFiles(m_tempAddedFiles, m_passedDestination, m_passedOptions)) { finishMoving(false); } break; case Add: finishMoving(true); break; default: Q_ASSERT(false); } } bool CliPlugin::setMovingAddedFiles() { m_passedFiles = entriesWithoutChildren(m_passedFiles); // If there are more files being moved than 1, we have destination as a destination folder, // otherwise it's new entry full path. if (m_passedFiles.count() > 1) { return setAddedFiles(); } QDir::setCurrent(m_tempAddDir->path()); const Archive::Entry *file = m_passedFiles.at(0); const QString oldPath = m_tempWorkingDir->path() + QLatin1Char('/') + file->fullPath(NoTrailingSlash); const QString newPath = m_tempAddDir->path() + QLatin1Char('/') + m_passedDestination->name(); if (!QFile::rename(oldPath, newPath)) { return false; } m_tempAddedFiles << new Archive::Entry(nullptr, m_passedDestination->name()); // We have to exclude file name from destination path in order to pass it to addFiles method. const QString destinationPath = m_passedDestination->fullPath(); const int slashCount = destinationPath.count(QLatin1Char('/')); if (slashCount > 1 || (slashCount == 1 && !destinationPath.endsWith(QLatin1Char('/')))) { int destinationLength = destinationPath.count(); bool iteratedChar = false; do { destinationLength--; if (destinationPath.at(destinationLength) != QLatin1Char('/')) { iteratedChar = true; } } while (destinationLength > 0 && !(iteratedChar && destinationPath.at(destinationLength) == QLatin1Char('/'))); m_passedDestination->setProperty("fullPath", destinationPath.left(destinationLength + 1)); } else { // ...unless the destination path is already a single folder, e.g. "dir/", or a file, e.g. "foo.txt". // In this case we're going to add to the root, so we just need to set a null destination. m_passedDestination = nullptr; } return true; } void CliPlugin::finishMoving(bool result) { disconnect(this, &CliPlugin::finished, this, &CliPlugin::continueMoving); emit progress(1.0); emit finished(result); cleanUp(); } QString CliPlugin::convertCompressionMethod(const QString &method) { if (method == QLatin1String("stor")) { return QStringLiteral("Store"); } else if (method.startsWith(QLatin1String("def"))) { return QStringLiteral("Deflate"); } else if (method == QLatin1String("d64N")) { return QStringLiteral("Deflate64"); } else if (method == QLatin1String("bzp2")) { return QStringLiteral("BZip2"); } else if (method == QLatin1String("lzma")) { return QStringLiteral("LZMA"); } else if (method == QLatin1String("ppmd")) { return QStringLiteral("PPMd"); } else if (method == QLatin1String("u095")) { return QStringLiteral("XZ"); } else if (method == QLatin1String("u099")) { emit encryptionMethodFound(QStringLiteral("AES")); return i18nc("referred to compression method", "unknown"); } return method; } #include "cliplugin.moc"