diff --git a/autotests/plugins/clizipplugin/cliziptest.cpp b/autotests/plugins/clizipplugin/cliziptest.cpp index 162b829e..da51860e 100644 --- a/autotests/plugins/clizipplugin/cliziptest.cpp +++ b/autotests/plugins/clizipplugin/cliziptest.cpp @@ -1,233 +1,246 @@ /* * Copyright (c) 2016 Elvis Angelaccio * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "cliziptest.h" #include "cliplugin.h" #include QTEST_GUILESS_MAIN(CliZipTest) using namespace Kerfuffle; void CliZipTest::initTestCase() { m_plugin = new Plugin(this); foreach (Plugin *plugin, m_pluginManger.availablePlugins()) { if (plugin->metaData().pluginId() == QStringLiteral("kerfuffle_clizip")) { m_plugin = plugin; return; } } } void CliZipTest::testListArgs_data() { QTest::addColumn("archiveName"); + QTest::addColumn("password"); QTest::addColumn("expectedArgs"); QTest::newRow("fake zip") << QStringLiteral("/tmp/foo.zip") + << QString() + << QStringList { + QStringLiteral("-l"), + QStringLiteral("-T"), + QStringLiteral("-z"), + QStringLiteral("/tmp/foo.zip") + }; + + QTest::newRow("fake encrypted zip") + << QStringLiteral("/tmp/foo.zip") + << QStringLiteral("1234") << QStringList { QStringLiteral("-l"), QStringLiteral("-T"), QStringLiteral("-z"), QStringLiteral("/tmp/foo.zip") }; } void CliZipTest::testListArgs() { if (!m_plugin->isValid()) { QSKIP("clizip plugin not available. Skipping test.", SkipSingle); } QFETCH(QString, archiveName); CliPlugin *plugin = new CliPlugin(this, {QVariant(archiveName), QVariant::fromValue(m_plugin->metaData())}); QVERIFY(plugin); - const auto replacedArgs = plugin->cliProperties()->listArgs(archiveName, QString()); + QFETCH(QString, password); + const auto replacedArgs = plugin->cliProperties()->listArgs(archiveName, password); QFETCH(QStringList, expectedArgs); QCOMPARE(replacedArgs, expectedArgs); plugin->deleteLater(); } void CliZipTest::testAddArgs_data() { QTest::addColumn("archiveName"); QTest::addColumn("password"); QTest::addColumn("compressionLevel"); QTest::addColumn("compressionMethod"); QTest::addColumn("expectedArgs"); QTest::newRow("unencrypted") << QStringLiteral("/tmp/foo.zip") << QString() << 3 << QStringLiteral("Deflate") << QStringList { QStringLiteral("-r"), QStringLiteral("-3"), QStringLiteral("-Zdeflate"), QStringLiteral("/tmp/foo.zip") }; QTest::newRow("encrypted") << QStringLiteral("/tmp/foo.zip") << QStringLiteral("1234") << 3 << QString() << QStringList { QStringLiteral("-r"), QStringLiteral("-P1234"), QStringLiteral("-3"), QStringLiteral("/tmp/foo.zip") }; QTest::newRow("comp-method-bzip2") << QStringLiteral("/tmp/foo.zip") << QString() << 3 << QStringLiteral("BZip2") << QStringList { QStringLiteral("-r"), QStringLiteral("-3"), QStringLiteral("-Zbzip2"), QStringLiteral("/tmp/foo.zip") }; } void CliZipTest::testAddArgs() { if (!m_plugin->isValid()) { QSKIP("clizip plugin not available. Skipping test.", SkipSingle); } QFETCH(QString, archiveName); CliPlugin *plugin = new CliPlugin(this, {QVariant(archiveName), QVariant::fromValue(m_plugin->metaData())}); QVERIFY(plugin); QFETCH(QString, password); QFETCH(int, compressionLevel); QFETCH(QString, compressionMethod); const auto replacedArgs = plugin->cliProperties()->addArgs(archiveName, {}, password, false, compressionLevel, compressionMethod, QString(), 0); QFETCH(QStringList, expectedArgs); QCOMPARE(replacedArgs, expectedArgs); plugin->deleteLater(); } void CliZipTest::testExtractArgs_data() { QTest::addColumn("archiveName"); QTest::addColumn>("files"); QTest::addColumn("preservePaths"); QTest::addColumn("password"); QTest::addColumn("expectedArgs"); QTest::newRow("preserve paths, encrypted") << QStringLiteral("/tmp/foo.zip") << QVector { new Archive::Entry(this, QStringLiteral("aDir/textfile2.txt"), QStringLiteral("aDir")), new Archive::Entry(this, QStringLiteral("c.txt"), QString()) } << true << QStringLiteral("1234") << QStringList { QStringLiteral("-P1234"), QStringLiteral("/tmp/foo.zip"), QStringLiteral("aDir/textfile2.txt"), QStringLiteral("c.txt"), }; QTest::newRow("preserve paths, unencrypted") << QStringLiteral("/tmp/foo.zip") << QVector { new Archive::Entry(this, QStringLiteral("aDir/textfile2.txt"), QStringLiteral("aDir")), new Archive::Entry(this, QStringLiteral("c.txt"), QString()) } << true << QString() << QStringList { QStringLiteral("/tmp/foo.zip"), QStringLiteral("aDir/textfile2.txt"), QStringLiteral("c.txt"), }; QTest::newRow("without paths, encrypted") << QStringLiteral("/tmp/foo.zip") << QVector { new Archive::Entry(this, QStringLiteral("aDir/textfile2.txt"), QStringLiteral("aDir")), new Archive::Entry(this, QStringLiteral("c.txt"), QString()) } << false << QStringLiteral("1234") << QStringList { QStringLiteral("-j"), QStringLiteral("-P1234"), QStringLiteral("/tmp/foo.zip"), QStringLiteral("aDir/textfile2.txt"), QStringLiteral("c.txt"), }; QTest::newRow("without paths, unencrypted") << QStringLiteral("/tmp/foo.zip") << QVector { new Archive::Entry(this, QStringLiteral("aDir/textfile2.txt"), QStringLiteral("aDir")), new Archive::Entry(this, QStringLiteral("c.txt"), QString()) } << false << QString() << QStringList { QStringLiteral("-j"), QStringLiteral("/tmp/foo.zip"), QStringLiteral("aDir/textfile2.txt"), QStringLiteral("c.txt"), }; } void CliZipTest::testExtractArgs() { if (!m_plugin->isValid()) { QSKIP("clizip plugin not available. Skipping test.", SkipSingle); } QFETCH(QString, archiveName); CliPlugin *plugin = new CliPlugin(this, {QVariant(archiveName), QVariant::fromValue(m_plugin->metaData())}); QVERIFY(plugin); QFETCH(QVector, files); QStringList filesList; foreach (const Archive::Entry *e, files) { filesList << e->fullPath(NoTrailingSlash); } QFETCH(bool, preservePaths); QFETCH(QString, password); const auto replacedArgs = plugin->cliProperties()->extractArgs(archiveName, filesList, preservePaths, password); QFETCH(QStringList, expectedArgs); QCOMPARE(replacedArgs, expectedArgs); plugin->deleteLater(); } diff --git a/kerfuffle/cliproperties.cpp b/kerfuffle/cliproperties.cpp index b2965193..8eb05fe3 100644 --- a/kerfuffle/cliproperties.cpp +++ b/kerfuffle/cliproperties.cpp @@ -1,361 +1,363 @@ /* * ark -- archiver for the KDE project * * Copyright (C) 2016 Ragnar Thomsen * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "cliproperties.h" #include "ark_debug.h" #include "archiveformat.h" #include "pluginmanager.h" namespace Kerfuffle { CliProperties::CliProperties(QObject *parent, const KPluginMetaData &metaData, const QMimeType &archiveType) : QObject(parent) , m_mimeType(archiveType) , m_metaData(metaData) { } QStringList CliProperties::addArgs(const QString &archive, const QStringList &files, const QString &password, bool headerEncryption, int compressionLevel, const QString &compressionMethod, const QString &encryptionMethod, ulong volumeSize) { if (!encryptionMethod.isEmpty()) { Q_ASSERT(!password.isEmpty()); } QStringList args; foreach (const QString &s, m_addSwitch) { args << s; } if (!password.isEmpty()) { args << substitutePasswordSwitch(password, headerEncryption); } if (compressionLevel > -1) { args << substituteCompressionLevelSwitch(compressionLevel); } if (!compressionMethod.isEmpty()) { args << substituteCompressionMethodSwitch(compressionMethod); } if (!encryptionMethod.isEmpty()) { args << substituteEncryptionMethodSwitch(encryptionMethod); } if (volumeSize > 0) { args << substituteMultiVolumeSwitch(volumeSize); } args << archive; args << files; args.removeAll(QString()); return args; } QStringList CliProperties::commentArgs(const QString &archive, const QString &commentfile) { QStringList args; foreach (const QString &s, substituteCommentSwitch(commentfile)) { args << s; } args << archive; args.removeAll(QString()); return args; } QStringList CliProperties::deleteArgs(const QString &archive, const QVector &files, const QString &password) { QStringList args; args << m_deleteSwitch; if (!password.isEmpty()) { args << substitutePasswordSwitch(password); } args << archive; foreach (const Archive::Entry *e, files) { args << e->fullPath(NoTrailingSlash); } args.removeAll(QString()); return args; } QStringList CliProperties::extractArgs(const QString &archive, const QStringList &files, bool preservePaths, const QString &password) { QStringList args; if (preservePaths && !m_extractSwitch.isEmpty()) { args << m_extractSwitch; } else if (!preservePaths && !m_extractSwitchNoPreserve.isEmpty()) { args << m_extractSwitchNoPreserve; } if (!password.isEmpty()) { args << substitutePasswordSwitch(password); } args << archive; args << files; args.removeAll(QString()); return args; } QStringList CliProperties::listArgs(const QString &archive, const QString &password) { QStringList args; foreach (const QString &s, m_listSwitch) { args << s; } - if (!password.isEmpty()) { + + const auto encryptionType = ArchiveFormat::fromMetadata(m_mimeType, m_metaData).encryptionType(); + if (!password.isEmpty() && encryptionType == Archive::EncryptionType::HeaderEncrypted) { args << substitutePasswordSwitch(password); } args << archive; args.removeAll(QString()); return args; } QStringList CliProperties::moveArgs(const QString &archive, const QVector &entries, Archive::Entry *destination, const QString &password) { QStringList args; args << m_moveSwitch; if (!password.isEmpty()) { args << substitutePasswordSwitch(password); } args << archive; if (entries.count() > 1) { foreach (const Archive::Entry *file, entries) { args << file->fullPath(NoTrailingSlash) << destination->fullPath() + file->name(); } } else { args << entries.at(0)->fullPath(NoTrailingSlash) << destination->fullPath(NoTrailingSlash); } args.removeAll(QString()); return args; } QStringList CliProperties::testArgs(const QString &archive, const QString &password) { QStringList args; foreach (const QString &s, m_testSwitch) { args << s; } if (!password.isEmpty()) { args << substitutePasswordSwitch(password); } args << archive; args.removeAll(QString()); return args; } QStringList CliProperties::substituteCommentSwitch(const QString &commentfile) const { Q_ASSERT(!commentfile.isEmpty()); Q_ASSERT(ArchiveFormat::fromMetadata(m_mimeType, m_metaData).supportsWriteComment()); QStringList commentSwitches = m_commentSwitch; Q_ASSERT(!commentSwitches.isEmpty()); QMutableListIterator i(commentSwitches); while (i.hasNext()) { i.next(); i.value().replace(QLatin1String("$CommentFile"), commentfile); } return commentSwitches; } QStringList CliProperties::substitutePasswordSwitch(const QString &password, bool headerEnc) const { if (password.isEmpty()) { return QStringList(); } Archive::EncryptionType encryptionType = ArchiveFormat::fromMetadata(m_mimeType, m_metaData).encryptionType(); Q_ASSERT(encryptionType != Archive::EncryptionType::Unencrypted); QStringList passwordSwitch; if (headerEnc) { passwordSwitch = m_passwordSwitchHeaderEnc; } else { passwordSwitch = m_passwordSwitch; } Q_ASSERT(!passwordSwitch.isEmpty()); QMutableListIterator i(passwordSwitch); while (i.hasNext()) { i.next(); i.value().replace(QLatin1String("$Password"), password); } return passwordSwitch; } QString CliProperties::substituteCompressionLevelSwitch(int level) const { if (level < 0 || level > 9) { return QString(); } Q_ASSERT(ArchiveFormat::fromMetadata(m_mimeType, m_metaData).maxCompressionLevel() != -1); QString compLevelSwitch = m_compressionLevelSwitch; Q_ASSERT(!compLevelSwitch.isEmpty()); compLevelSwitch.replace(QLatin1String("$CompressionLevel"), QString::number(level)); return compLevelSwitch; } QString CliProperties::substituteCompressionMethodSwitch(const QString &method) const { if (method.isEmpty()) { return QString(); } Q_ASSERT(!ArchiveFormat::fromMetadata(m_mimeType, m_metaData).compressionMethods().isEmpty()); QString compMethodSwitch = m_compressionMethodSwitch[m_mimeType.name()].toString(); Q_ASSERT(!compMethodSwitch.isEmpty()); QString cliMethod = ArchiveFormat::fromMetadata(m_mimeType, m_metaData).compressionMethods().value(method).toString(); compMethodSwitch.replace(QLatin1String("$CompressionMethod"), cliMethod); return compMethodSwitch; } QString CliProperties::substituteEncryptionMethodSwitch(const QString &method) const { if (method.isEmpty()) { return QString(); } const ArchiveFormat format = ArchiveFormat::fromMetadata(m_mimeType, m_metaData); Q_ASSERT(!format.encryptionMethods().isEmpty()); QString encMethodSwitch = m_encryptionMethodSwitch[m_mimeType.name()].toString(); if (encMethodSwitch.isEmpty()) { return QString(); } Q_ASSERT(format.encryptionMethods().contains(method)); encMethodSwitch.replace(QLatin1String("$EncryptionMethod"), method); return encMethodSwitch; } QString CliProperties::substituteMultiVolumeSwitch(ulong volumeSize) const { // The maximum value we allow in the QDoubleSpinBox is 1,000,000MB. Converted to // KB this is 1,024,000,000. if (volumeSize <= 0 || volumeSize > 1024000000) { return QString(); } Q_ASSERT(ArchiveFormat::fromMetadata(m_mimeType, m_metaData).supportsMultiVolume()); QString multiVolumeSwitch = m_multiVolumeSwitch; Q_ASSERT(!multiVolumeSwitch.isEmpty()); multiVolumeSwitch.replace(QLatin1String("$VolumeSize"), QString::number(volumeSize)); return multiVolumeSwitch; } bool CliProperties::isPasswordPrompt(const QString &line) { foreach(const QString &rx, m_passwordPromptPatterns) { if (QRegularExpression(rx).match(line).hasMatch()) { return true; } } return false; } bool CliProperties::isWrongPasswordMsg(const QString &line) { foreach(const QString &rx, m_wrongPasswordPatterns) { if (QRegularExpression(rx).match(line).hasMatch()) { return true; } } return false; } bool CliProperties::isTestPassedMsg(const QString &line) { foreach(const QString &rx, m_testPassedPatterns) { if (QRegularExpression(rx).match(line).hasMatch()) { return true; } } return false; } bool CliProperties::isfileExistsMsg(const QString &line) { foreach(const QString &rx, m_fileExistsPatterns) { if (QRegularExpression(rx).match(line).hasMatch()) { return true; } } return false; } bool CliProperties::isFileExistsFileName(const QString &line) { foreach(const QString &rx, m_fileExistsFileName) { if (QRegularExpression(rx).match(line).hasMatch()) { return true; } } return false; } bool CliProperties::isCorruptArchiveMsg(const QString &line) { foreach(const QString &rx, m_corruptArchivePatterns) { if (QRegularExpression(rx).match(line).hasMatch()) { return true; } } return false; } bool CliProperties::isDiskFullMsg(const QString &line) { foreach(const QString &rx, m_diskFullPatterns) { if (QRegularExpression(rx).match(line).hasMatch()) { return true; } } return false; } }