diff --git a/app/mainwindow.cpp b/app/mainwindow.cpp --- a/app/mainwindow.cpp +++ b/app/mainwindow.cpp @@ -307,6 +307,10 @@ if (!dialog.data()->compressionMethod().isEmpty()) { m_openArgs.metaData()[QStringLiteral("compressionMethod")] = dialog.data()->compressionMethod(); } + if (!dialog.data()->encryptionMethod().isEmpty()) { + m_openArgs.metaData()[QStringLiteral("encryptionMethod")] = dialog.data()->encryptionMethod(); + } + m_openArgs.metaData()[QStringLiteral("encryptionPassword")] = password; if (dialog.data()->isHeaderEncryptionEnabled()) { diff --git a/autotests/plugins/cli7zplugin/cli7ztest.cpp b/autotests/plugins/cli7zplugin/cli7ztest.cpp --- a/autotests/plugins/cli7zplugin/cli7ztest.cpp +++ b/autotests/plugins/cli7zplugin/cli7ztest.cpp @@ -107,6 +107,7 @@ // Is zero for non-multi-volume archives: QTest::addColumn("numberOfVolumes"); QTest::addColumn("compressionMethods"); + QTest::addColumn("encryptionMethod"); // Index of some entry to be tested. QTest::addColumn("someEntryIndex"); // Entry metadata. @@ -119,45 +120,51 @@ // p7zip version 16.02 tests QTest::newRow("normal-file-1602") - << QFINDTESTDATA("data/archive-with-symlink-1602.txt") << 10 << false << 0 << QStringList{QStringLiteral("LZMA2")} + << QFINDTESTDATA("data/archive-with-symlink-1602.txt") << 10 << false << 0 << QStringList{QStringLiteral("LZMA2")} << QString() << 4 << QStringLiteral("testarchive/dir2/file2.txt") << false << false << (qulonglong) 32 << QStringLiteral("2015-05-17T20:41:48"); QTest::newRow("encrypted-1602") - << QFINDTESTDATA("data/archive-encrypted-1602.txt") << 4 << false << 0 << QStringList{QStringLiteral("LZMA2"), QStringLiteral("7zAES")} + << QFINDTESTDATA("data/archive-encrypted-1602.txt") << 4 << false << 0 << QStringList{QStringLiteral("LZMA2")} << QStringLiteral("7zAES") << 1 << QStringLiteral("file2.txt") << false << true << (qulonglong) 14 << QStringLiteral("2016-03-02T22:37:55"); QTest::newRow("multi-volume-1602") - << QFINDTESTDATA("data/archive-multivol-1602.txt") << 2 << true << 5 << QStringList{QStringLiteral("LZMA2")} + << QFINDTESTDATA("data/archive-multivol-1602.txt") << 2 << true << 5 << QStringList{QStringLiteral("LZMA2")} << QString() << 1 << QStringLiteral("largefile2") << false << false << (qulonglong) 2097152 << QStringLiteral("2016-07-17T11:26:19"); + QTest::newRow("zip-with-AES256-encryption") + << QFINDTESTDATA("data/archive-zip-AES256-1602.txt") << 4 << false << 0 + << QStringList{QStringLiteral("Store"), QStringLiteral("Store"), QStringLiteral("Deflate"), QStringLiteral("Deflate")} + << QStringLiteral("AES256") + << 3 << QStringLiteral("testarchive/file2.txt") << false << true << (qulonglong) 33 << QStringLiteral("2016-11-06T21:17:02"); + // p7zip version 15.14 tests QTest::newRow("normal-file-1514") - << QFINDTESTDATA("data/archive-with-symlink-1514.txt") << 10 << false << 0 << QStringList{QStringLiteral("LZMA2")} + << QFINDTESTDATA("data/archive-with-symlink-1514.txt") << 10 << false << 0 << QStringList{QStringLiteral("LZMA2")} << QString() << 4 << QStringLiteral("testarchive/dir2/file2.txt") << false << false << (qulonglong) 32 << QStringLiteral("2015-05-17T19:41:48"); QTest::newRow("encrypted-1514") - << QFINDTESTDATA("data/archive-encrypted-1514.txt") << 9 << false << 0 << QStringList{QStringLiteral("LZMA2"), QStringLiteral("7zAES")} + << QFINDTESTDATA("data/archive-encrypted-1514.txt") << 9 << false << 0 << QStringList{QStringLiteral("LZMA2")} << QStringLiteral("7zAES") << 3 << QStringLiteral("testarchive/dir1/file1.txt") << false << true << (qulonglong) 32 << QStringLiteral("2015-05-17T19:41:48"); // p7zip version 15.09 tests QTest::newRow("normal-file-1509") - << QFINDTESTDATA("data/archive-with-symlink-1509.txt") << 10 << false << 0 << QStringList{QStringLiteral("LZMA2")} + << QFINDTESTDATA("data/archive-with-symlink-1509.txt") << 10 << false << 0 << QStringList{QStringLiteral("LZMA2")} << QString() << 4 << QStringLiteral("testarchive/dir2/file2.txt") << false << false << (qulonglong) 32 << QStringLiteral("2015-05-17T19:41:48"); QTest::newRow("encrypted-1509") - << QFINDTESTDATA("data/archive-encrypted-1509.txt") << 9 << false << 0 << QStringList{QStringLiteral("LZMA2"), QStringLiteral("7zAES")} + << QFINDTESTDATA("data/archive-encrypted-1509.txt") << 9 << false << 0 << QStringList{QStringLiteral("LZMA2")} << QStringLiteral("7zAES") << 3 << QStringLiteral("testarchive/dir1/file1.txt") << false << true << (qulonglong) 32 << QStringLiteral("2015-05-17T19:41:48"); // p7zip version 9.38.1 tests QTest::newRow("normal-file-9381") - << QFINDTESTDATA("data/archive-with-symlink-9381.txt") << 10 << false << 0 << QStringList{QStringLiteral("LZMA2")} + << QFINDTESTDATA("data/archive-with-symlink-9381.txt") << 10 << false << 0 << QStringList{QStringLiteral("LZMA2")} << QString() << 4 << QStringLiteral("testarchive/dir2/file2.txt") << false << false << (qulonglong) 32 << QStringLiteral("2015-05-17T19:41:48"); QTest::newRow("encrypted-9381") - << QFINDTESTDATA("data/archive-encrypted-9381.txt") << 9 << false << 0 << QStringList{QStringLiteral("LZMA2"), QStringLiteral("7zAES")} + << QFINDTESTDATA("data/archive-encrypted-9381.txt") << 9 << false << 0 << QStringList{QStringLiteral("LZMA2")} << QStringLiteral("7zAES") << 3 << QStringLiteral("testarchive/dir1/file1.txt") << false << true << (qulonglong) 32 << QStringLiteral("2015-05-17T19:41:48"); } @@ -168,6 +175,7 @@ QVariant::fromValue(m_plugin->metaData())}); QSignalSpy signalSpyEntry(plugin, &CliPlugin::entry); QSignalSpy signalSpyCompMethod(plugin, &CliPlugin::compressionMethodFound); + QSignalSpy signalSpyEncMethod(plugin, &CliPlugin::encryptionMethodFound); QFETCH(QString, outputTextFile); QFETCH(int, expectedEntriesCount); @@ -189,9 +197,21 @@ QFETCH(int, numberOfVolumes); QCOMPARE(plugin->numberOfVolumes(), numberOfVolumes); - QCOMPARE(signalSpyCompMethod.count(), 1); QFETCH(QStringList, compressionMethods); - QCOMPARE(signalSpyCompMethod.at(0).at(0).toStringList(), compressionMethods); + QCOMPARE(signalSpyCompMethod.count(), compressionMethods.count()); + QStringList combinedCompMethods; + for (int i = 0; i < signalSpyCompMethod.count(); i++) { + combinedCompMethods.append(signalSpyCompMethod.at(i).at(0).toString()); + } + QCOMPARE(combinedCompMethods, compressionMethods); + + QFETCH(QString, encryptionMethod); + if (encryptionMethod.isEmpty()) { + QCOMPARE(signalSpyEncMethod.count(), 0); + } else { + QVERIFY(signalSpyEncMethod.count() > 0); + QCOMPARE(signalSpyEncMethod.at(0).at(0).toString(), encryptionMethod); + } QFETCH(int, someEntryIndex); QVERIFY(someEntryIndex < signalSpyEntry.count()); @@ -349,7 +369,7 @@ QFETCH(ulong, volumeSize); QFETCH(QString, compressionMethod); - const auto replacedArgs = plugin->cliProperties()->addArgs(archiveName, {}, password, encryptHeader, compressionLevel, compressionMethod, volumeSize); + const auto replacedArgs = plugin->cliProperties()->addArgs(archiveName, {}, password, encryptHeader, compressionLevel, compressionMethod, QString(), volumeSize); QFETCH(QStringList, expectedArgs); QCOMPARE(replacedArgs, expectedArgs); diff --git a/autotests/plugins/cli7zplugin/data/archive-zip-AES256-1602.txt b/autotests/plugins/cli7zplugin/data/archive-zip-AES256-1602.txt new file mode 100644 --- /dev/null +++ b/autotests/plugins/cli7zplugin/data/archive-zip-AES256-1602.txt @@ -0,0 +1,79 @@ + +7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21 +p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,8 CPUs x64) + +Scanning the drive for archives: +1 file, 781 bytes (1 KiB) + +Listing archive: testarchive.zip + +-- +Path = testarchive.zip +Type = zip +Physical Size = 781 + +---------- +Path = testarchive +Folder = + +Size = 0 +Packed Size = 0 +Modified = 2016-11-06 21:17:02 +Created = 2016-11-06 21:17:02 +Accessed = 2016-11-06 21:17:03 +Attributes = D_ drwxrwxr-x +Encrypted = - +Comment = +CRC = +Method = Store +Host OS = Unix +Version = 20 +Volume Index = 0 + +Path = testarchive/dir1 +Folder = + +Size = 0 +Packed Size = 0 +Modified = 2015-05-14 00:45:24 +Created = 2016-11-06 21:16:31 +Accessed = 2016-11-06 21:16:35 +Attributes = D_ drwxrwxr-x +Encrypted = - +Comment = +CRC = +Method = Store +Host OS = Unix +Version = 20 +Volume Index = 0 + +Path = testarchive/dir1/file1.txt +Folder = - +Size = 32 +Packed Size = 57 +Modified = 2015-05-17 19:41:48 +Created = 2016-11-06 21:16:31 +Accessed = 2016-11-06 21:17:06 +Attributes = _ -rw-rw-r-- +Encrypted = + +Comment = +CRC = +Method = AES-256 Deflate +Host OS = Unix +Version = 51 +Volume Index = 0 + +Path = testarchive/file2.txt +Folder = - +Size = 33 +Packed Size = 58 +Modified = 2016-11-06 21:17:02 +Created = 2016-11-06 21:17:02 +Accessed = 2016-11-06 21:17:02 +Attributes = _ -rw-rw-r-- +Encrypted = + +Comment = +CRC = +Method = AES-256 Deflate +Host OS = Unix +Version = 51 +Volume Index = 0 + diff --git a/autotests/plugins/clirarplugin/clirartest.cpp b/autotests/plugins/clirarplugin/clirartest.cpp --- a/autotests/plugins/clirarplugin/clirartest.cpp +++ b/autotests/plugins/clirarplugin/clirartest.cpp @@ -396,7 +396,7 @@ QFETCH(QString, compressionMethod); QFETCH(ulong, volumeSize); - const auto replacedArgs = plugin->cliProperties()->addArgs(archiveName, {}, password, encryptHeader, compressionLevel, compressionMethod, volumeSize); + const auto replacedArgs = plugin->cliProperties()->addArgs(archiveName, {}, password, encryptHeader, compressionLevel, compressionMethod, QString(), volumeSize); QFETCH(QStringList, expectedArgs); QCOMPARE(replacedArgs, expectedArgs); diff --git a/autotests/plugins/clizipplugin/cliziptest.cpp b/autotests/plugins/clizipplugin/cliziptest.cpp --- a/autotests/plugins/clizipplugin/cliziptest.cpp +++ b/autotests/plugins/clizipplugin/cliziptest.cpp @@ -129,7 +129,7 @@ QFETCH(int, compressionLevel); QFETCH(QString, compressionMethod); - const auto replacedArgs = plugin->cliProperties()->addArgs(archiveName, {}, password, false, compressionLevel, compressionMethod, 0); + const auto replacedArgs = plugin->cliProperties()->addArgs(archiveName, {}, password, false, compressionLevel, compressionMethod, QString(), 0); QFETCH(QStringList, expectedArgs); QCOMPARE(replacedArgs, expectedArgs); diff --git a/kerfuffle/archive_kerfuffle.h b/kerfuffle/archive_kerfuffle.h --- a/kerfuffle/archive_kerfuffle.h +++ b/kerfuffle/archive_kerfuffle.h @@ -86,6 +86,7 @@ Q_PROPERTY(QString subfolderName MEMBER m_subfolderName READ subfolderName) Q_PROPERTY(QString password READ password) Q_PROPERTY(QStringList compressionMethods MEMBER m_compressionMethods) + Q_PROPERTY(QStringList encryptionMethods MEMBER m_encryptionMethods) public: @@ -210,7 +211,8 @@ private slots: void onAddFinished(KJob*); void onUserQuery(Kerfuffle::Query*); - void onCompressionMethodFound(const QStringList &methods); + void onCompressionMethodFound(const QString &method); + void onEncryptionMethodFound(const QString &method); private: Archive(ReadOnlyArchiveInterface *archiveInterface, bool isReadOnly, QObject *parent = 0); @@ -238,6 +240,7 @@ qulonglong m_numberOfFolders; QMimeType m_mimeType; QStringList m_compressionMethods; + QStringList m_encryptionMethods; }; } // namespace Kerfuffle diff --git a/kerfuffle/archive_kerfuffle.cpp b/kerfuffle/archive_kerfuffle.cpp --- a/kerfuffle/archive_kerfuffle.cpp +++ b/kerfuffle/archive_kerfuffle.cpp @@ -172,17 +172,33 @@ m_iface->setParent(this); connect(m_iface, &ReadOnlyArchiveInterface::compressionMethodFound, this, &Archive::onCompressionMethodFound); + connect(m_iface, &ReadOnlyArchiveInterface::encryptionMethodFound, this, &Archive::onEncryptionMethodFound); } -void Archive::onCompressionMethodFound(const QStringList &methods) +void Archive::onCompressionMethodFound(const QString &method) { - // If other methods are found, we don't report "Store" method. - QStringList processedMethods = methods; - if (processedMethods.size() > 1 && - processedMethods.contains(QStringLiteral("Store"))) { - processedMethods.removeOne(QStringLiteral("Store")); + QStringList methods = property("compressionMethods").toStringList(); + + if (!property("compressionMethods").toStringList().contains(method) && + method != QLatin1String("Store")) { + methods.append(method); } - setProperty("compressionMethods", processedMethods); + + methods.sort(); + + setProperty("compressionMethods", methods); +} + +void Archive::onEncryptionMethodFound(const QString &method) +{ + QStringList methods = property("encryptionMethods").toStringList(); + + if (!property("encryptionMethods").toStringList().contains(method)) { + methods.append(method); + } + methods.sort(); + + setProperty("encryptionMethods", methods); } Archive::~Archive() diff --git a/kerfuffle/archiveformat.h b/kerfuffle/archiveformat.h --- a/kerfuffle/archiveformat.h +++ b/kerfuffle/archiveformat.h @@ -46,7 +46,9 @@ bool supportsTesting, bool suppportsMultiVolume, const QVariantMap& compressionMethods, - const QString& defaultCompressionMethod); + const QString& defaultCompressionMethod, + QVariantMap encryptionMethods, + QString defaultEncryptionMethod); /** * @return The archive format of the given @p mimeType, according to the given @p metadata. @@ -71,6 +73,8 @@ bool supportsMultiVolume() const; QVariantMap compressionMethods() const; QString defaultCompressionMethod() const; + QVariantMap encryptionMethods() const; + QString defaultEncryptionMethod() const; private: QMimeType m_mimeType; @@ -83,6 +87,8 @@ bool m_supportsMultiVolume = false; QVariantMap m_compressionMethods; QString m_defaultCompressionMethod; + QVariantMap m_encryptionMethods; + QString m_defaultEncryptionMethod; }; } diff --git a/kerfuffle/archiveformat.cpp b/kerfuffle/archiveformat.cpp --- a/kerfuffle/archiveformat.cpp +++ b/kerfuffle/archiveformat.cpp @@ -44,7 +44,9 @@ bool supportsTesting, bool supportsMultiVolume, const QVariantMap& compressionMethods, - const QString& defaultCompressionMethod) : + const QString& defaultCompressionMethod, + QVariantMap encryptionMethods, + QString defaultEncryptionMethod) : m_mimeType(mimeType), m_encryptionType(encryptionType), m_minCompressionLevel(minCompLevel), @@ -54,7 +56,9 @@ m_supportsTesting(supportsTesting), m_supportsMultiVolume(supportsMultiVolume), m_compressionMethods(compressionMethods), - m_defaultCompressionMethod(defaultCompressionMethod) + m_defaultCompressionMethod(defaultCompressionMethod), + m_encryptionMethods(encryptionMethods), + m_defaultEncryptionMethod(defaultEncryptionMethod) { } @@ -79,14 +83,28 @@ QVariantMap compressionMethods = formatProps[QStringLiteral("CompressionMethods")].toObject().toVariantMap(); QString defaultCompMethod = formatProps[QStringLiteral("CompressionMethodDefault")].toString(); + QVariantMap encryptionMethods = formatProps[QStringLiteral("EncryptionMethods")].toObject().toVariantMap(); + QString defaultEncMethod = formatProps[QStringLiteral("EncryptionMethodDefault")].toString(); + Archive::EncryptionType encType = Archive::Unencrypted; if (formatProps[QStringLiteral("HeaderEncryption")].toBool()) { encType = Archive::HeaderEncrypted; } else if (formatProps[QStringLiteral("Encryption")].toBool()) { encType = Archive::Encrypted; } - return ArchiveFormat(mimeType, encType, minCompLevel, maxCompLevel, defaultCompLevel, supportsWriteComment, supportsTesting, supportsMultiVolume, compressionMethods, defaultCompMethod); + return ArchiveFormat(mimeType, + encType, + minCompLevel, + maxCompLevel, + defaultCompLevel, + supportsWriteComment, + supportsTesting, + supportsMultiVolume, + compressionMethods, + defaultCompMethod, + encryptionMethods, + defaultEncMethod); } return ArchiveFormat(); @@ -142,4 +160,14 @@ return m_defaultCompressionMethod; } +QVariantMap ArchiveFormat::encryptionMethods() const +{ + return m_encryptionMethods; +} + +QString ArchiveFormat::defaultEncryptionMethod() const +{ + return m_defaultEncryptionMethod; +} + } diff --git a/kerfuffle/archiveinterface.h b/kerfuffle/archiveinterface.h --- a/kerfuffle/archiveinterface.h +++ b/kerfuffle/archiveinterface.h @@ -167,7 +167,8 @@ void info(const QString &info); void finished(bool result); void testSuccess(); - void compressionMethodFound(const QStringList); + void compressionMethodFound(const QString &method); + void encryptionMethodFound(const QString &method); /** * Emitted when @p query needs to be executed on the GUI thread. diff --git a/kerfuffle/cliinterface.cpp b/kerfuffle/cliinterface.cpp --- a/kerfuffle/cliinterface.cpp +++ b/kerfuffle/cliinterface.cpp @@ -218,6 +218,7 @@ isHeaderEncryptionEnabled(), options.compressionLevel(), options.compressionMethod(), + options.encryptionMethod(), options.volumeSize())); } diff --git a/kerfuffle/cliproperties.h b/kerfuffle/cliproperties.h --- a/kerfuffle/cliproperties.h +++ b/kerfuffle/cliproperties.h @@ -59,6 +59,7 @@ Q_PROPERTY(QStringList passwordSwitchHeaderEnc MEMBER m_passwordSwitchHeaderEnc) Q_PROPERTY(QString compressionLevelSwitch MEMBER m_compressionLevelSwitch) Q_PROPERTY(QHash compressionMethodSwitch MEMBER m_compressionMethodSwitch) + Q_PROPERTY(QHash encryptionMethodSwitch MEMBER m_encryptionMethodSwitch) Q_PROPERTY(QString multiVolumeSwitch MEMBER m_multiVolumeSwitch) Q_PROPERTY(QStringList passwordPromptPatterns MEMBER m_passwordPromptPatterns) @@ -84,6 +85,7 @@ bool headerEncryption, int compressionLevel, const QString &compressionMethod, + const QString &encryptionMethod, uint volumeSize); QStringList commentArgs(const QString &archive, const QString &commentfile); QStringList deleteArgs(const QString &archive, const QVector &files, const QString &password); @@ -106,6 +108,7 @@ QStringList substitutePasswordSwitch(const QString &password, bool headerEnc = false) const; QString substituteCompressionLevelSwitch(int level) const; QString substituteCompressionMethodSwitch(const QString &method) const; + QString substituteEncryptionMethodSwitch(const QString &method) const; QString substituteMultiVolumeSwitch(uint volumeSize) const; QString m_addProgram; @@ -128,6 +131,7 @@ QStringList m_passwordSwitchHeaderEnc; QString m_compressionLevelSwitch; QHash m_compressionMethodSwitch; + QHash m_encryptionMethodSwitch; QString m_multiVolumeSwitch; QStringList m_passwordPromptPatterns; diff --git a/kerfuffle/cliproperties.cpp b/kerfuffle/cliproperties.cpp --- a/kerfuffle/cliproperties.cpp +++ b/kerfuffle/cliproperties.cpp @@ -40,8 +40,12 @@ { } -QStringList CliProperties::addArgs(const QString &archive, const QStringList &files, const QString &password, bool headerEncryption, int compressionLevel, const QString &compressionMethod, uint volumeSize) +QStringList CliProperties::addArgs(const QString &archive, const QStringList &files, const QString &password, bool headerEncryption, int compressionLevel, const QString &compressionMethod, const QString &encryptionMethod, uint volumeSize) { + if (!encryptionMethod.isEmpty()) { + Q_ASSERT(!password.isEmpty()); + } + QStringList args; foreach (const QString &s, m_addSwitch) { args << s; @@ -55,6 +59,9 @@ if (!compressionMethod.isEmpty()) { args << substituteCompressionMethodSwitch(compressionMethod); } + if (!encryptionMethod.isEmpty()) { + args << substituteEncryptionMethodSwitch(encryptionMethod); + } if (volumeSize > 0) { args << substituteMultiVolumeSwitch(volumeSize); } @@ -241,6 +248,26 @@ return compMethodSwitch; } +QString CliProperties::substituteEncryptionMethodSwitch(const QString &method) const +{ + if (method.isEmpty()) { + return QString(); + } + + Q_ASSERT(!ArchiveFormat::fromMetadata(m_mimeType, m_metaData).encryptionMethods().isEmpty()); + + QString encMethodSwitch = m_encryptionMethodSwitch[m_mimeType.name()].toString(); + if (encMethodSwitch.isEmpty()) { + return QString(); + } + + QString cliMethod = ArchiveFormat::fromMetadata(m_mimeType, m_metaData).encryptionMethods().value(method).toString(); + + encMethodSwitch.replace(QLatin1String("$EncryptionMethod"), cliMethod); + + return encMethodSwitch; +} + QString CliProperties::substituteMultiVolumeSwitch(uint volumeSize) const { // The maximum value we allow in the QDoubleSpinBox is 1000MB. Converted to diff --git a/kerfuffle/compressionoptionswidget.h b/kerfuffle/compressionoptionswidget.h --- a/kerfuffle/compressionoptionswidget.h +++ b/kerfuffle/compressionoptionswidget.h @@ -46,6 +46,7 @@ const CompressionOptions &opts = {}); int compressionLevel() const; QString compressionMethod() const; + QString encryptionMethod() const; ulong volumeSize() const; QString password() const; CompressionOptions commpressionOptions() const; @@ -66,6 +67,7 @@ private slots: void slotMultiVolumeChecked(int state); + void slotCompMethodChanged(const QString &value); }; } diff --git a/kerfuffle/compressionoptionswidget.cpp b/kerfuffle/compressionoptionswidget.cpp --- a/kerfuffle/compressionoptionswidget.cpp +++ b/kerfuffle/compressionoptionswidget.cpp @@ -49,6 +49,7 @@ pwdWidget->setPasswordStrengthMeterVisible(false); connect(multiVolumeCheckbox, &QCheckBox::stateChanged, this, &CompressionOptionsWidget::slotMultiVolumeChecked); + connect(compMethodComboBox, &QComboBox::currentTextChanged, this, &CompressionOptionsWidget::slotCompMethodChanged); if (m_opts.isVolumeSizeSet()) { multiVolumeCheckbox->setChecked(true); @@ -112,8 +113,25 @@ Q_ASSERT(archiveFormat.isValid()); if (archiveFormat.encryptionType() != Archive::Unencrypted) { + collapsibleEncryption->setEnabled(true); collapsibleEncryption->setToolTip(QString()); + + encMethodComboBox->clear(); + encMethodComboBox->insertItems(0, archiveFormat.encryptionMethods().keys()); + + if (!m_opts.encryptionMethod().isEmpty() && + encMethodComboBox->findText(m_opts.encryptionMethod()) > -1) { + encMethodComboBox->setCurrentText(m_opts.encryptionMethod()); + } else { + encMethodComboBox->setCurrentText(archiveFormat.defaultEncryptionMethod()); + } + + if (!archiveFormat.encryptionMethods().isEmpty()) { + lblEncMethod->setEnabled(true); + encMethodComboBox->setEnabled(true); + } + pwdWidget->setEnabled(true); if (archiveFormat.encryptionType() == Archive::HeaderEncrypted) { @@ -135,6 +153,9 @@ collapsibleEncryption->setEnabled(false); collapsibleEncryption->setToolTip(i18n("Protection of the archive with password is not possible with the %1 format.", m_mimetype.comment())); + lblEncMethod->setEnabled(false); + encMethodComboBox->setEnabled(false); + encMethodComboBox->clear(); pwdWidget->setEnabled(false); encryptHeaderCheckBox->setToolTip(QString()); } @@ -224,6 +245,14 @@ return pwdWidget->passwordStatus(); } +QString CompressionOptionsWidget::encryptionMethod() const +{ + if (encMethodComboBox->isEnabled() && encMethodComboBox->count() > 1 && !password().isEmpty()) { + return encMethodComboBox->currentText(); + } + return QString(); +} + void CompressionOptionsWidget::slotMultiVolumeChecked(int state) { if (state == Qt::Checked) { @@ -235,4 +264,24 @@ } } +void CompressionOptionsWidget::slotCompMethodChanged(const QString &value) +{ + // This hack is needed for the RAR format because the available encryption + // method is dependent on the selected compression method. Rar uses AES128 + // for RAR4 format and AES256 for RAR5 format. + + if (m_mimetype == QMimeDatabase().mimeTypeForName(QStringLiteral("application/vnd.rar")) || + m_mimetype == QMimeDatabase().mimeTypeForName(QStringLiteral("application/x-rar"))) { + + if (value == QLatin1String("RAR4")) { + encMethodComboBox->clear(); + encMethodComboBox->insertItem(0, QStringLiteral("AES128")); + } else { + encMethodComboBox->clear(); + encMethodComboBox->insertItem(0, QStringLiteral("AES256")); + } + + } +} + } diff --git a/kerfuffle/compressionoptionswidget.ui b/kerfuffle/compressionoptionswidget.ui --- a/kerfuffle/compressionoptionswidget.ui +++ b/kerfuffle/compressionoptionswidget.ui @@ -6,8 +6,8 @@ 0 0 - 402 - 90 + 450 + 433 @@ -23,23 +23,6 @@ false - - - - Min - - - - - - - Max - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - @@ -56,6 +39,13 @@ + + + + Min + + + @@ -87,9 +77,6 @@ - - - @@ -100,6 +87,19 @@ + + + + Max + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + @@ -120,15 +120,32 @@ false - - + + + + + false + + + + + + + false + + + Encryption method: + + + + false - + false @@ -218,15 +235,15 @@ - KNewPasswordWidget + KCollapsibleGroupBox QWidget -
knewpasswordwidget.h
+
kcollapsiblegroupbox.h
1
- KCollapsibleGroupBox + KNewPasswordWidget QWidget -
kcollapsiblegroupbox.h
+
knewpasswordwidget.h
1
diff --git a/kerfuffle/createdialog.h b/kerfuffle/createdialog.h --- a/kerfuffle/createdialog.h +++ b/kerfuffle/createdialog.h @@ -60,6 +60,7 @@ bool setMimeType(const QString &mimeTypeName); int compressionLevel() const; QString compressionMethod() const; + QString encryptionMethod() const; ulong volumeSize() const; /** diff --git a/kerfuffle/createdialog.cpp b/kerfuffle/createdialog.cpp --- a/kerfuffle/createdialog.cpp +++ b/kerfuffle/createdialog.cpp @@ -158,6 +158,11 @@ return m_ui->optionsWidget->compressionMethod(); } +QString CreateDialog::encryptionMethod() const +{ + return m_ui->optionsWidget->encryptionMethod(); +} + ulong CreateDialog::volumeSize() const { return m_ui->optionsWidget->volumeSize(); diff --git a/kerfuffle/options.h b/kerfuffle/options.h --- a/kerfuffle/options.h +++ b/kerfuffle/options.h @@ -71,13 +71,16 @@ void setVolumeSize(int size); QString compressionMethod() const; void setCompressionMethod(const QString &method); + QString encryptionMethod() const; + void setEncryptionMethod(const QString &method); QString globalWorkDir() const; void setGlobalWorkDir(const QString &workDir); private: int m_compressionLevel = -1; int m_volumeSize = 0; QString m_compressionMethod; + QString m_encryptionMethod; QString m_globalWorkDir; }; diff --git a/kerfuffle/options.cpp b/kerfuffle/options.cpp --- a/kerfuffle/options.cpp +++ b/kerfuffle/options.cpp @@ -110,6 +110,16 @@ m_compressionMethod = method; } +QString CompressionOptions::encryptionMethod() const +{ + return m_encryptionMethod; +} + +void CompressionOptions::setEncryptionMethod(const QString &method) +{ + m_encryptionMethod = method; +} + QString CompressionOptions::globalWorkDir() const { return m_globalWorkDir; @@ -126,6 +136,9 @@ if (!options.compressionMethod().isEmpty()) { d.nospace() << ", compression method: " << options.compressionMethod(); } + if (!options.encryptionMethod().isEmpty()) { + d.nospace() << ", encryption method: " << options.encryptionMethod(); + } if (!options.globalWorkDir().isEmpty()) { d.nospace() << ", global work dir: " << options.globalWorkDir(); } diff --git a/kerfuffle/propertiesdialog.cpp b/kerfuffle/propertiesdialog.cpp --- a/kerfuffle/propertiesdialog.cpp +++ b/kerfuffle/propertiesdialog.cpp @@ -84,10 +84,10 @@ m_ui->lblPasswordProtected->setText(i18n("no")); break; case Archive::Encrypted: - m_ui->lblPasswordProtected->setText(i18n("yes (excluding the list of files)")); + m_ui->lblPasswordProtected->setText(i18n("yes (%1 encryption)", archive->property("encryptionMethods").toStringList().join(QStringLiteral(", ")))); break; case Archive::HeaderEncrypted: - m_ui->lblPasswordProtected->setText(i18n("yes (including the list of files)")); + m_ui->lblPasswordProtected->setText(i18n("yes (including header, %1 encryption)", archive->property("encryptionMethods").toStringList().join(QStringLiteral(", ")))); break; } diff --git a/part/part.cpp b/part/part.cpp --- a/part/part.cpp +++ b/part/part.cpp @@ -1319,11 +1319,15 @@ if (m_compressionOptions.compressionMethod().isEmpty() && arguments().metaData().contains(QStringLiteral("compressionMethod"))) { m_compressionOptions.setCompressionMethod(arguments().metaData()[QStringLiteral("compressionMethod")]); } + if (m_compressionOptions.encryptionMethod().isEmpty() && arguments().metaData().contains(QStringLiteral("encryptionMethod"))) { + m_compressionOptions.setEncryptionMethod(arguments().metaData()[QStringLiteral("encryptionMethod")]); + } if (!m_compressionOptions.isVolumeSizeSet() && arguments().metaData().contains(QStringLiteral("volumeSize"))) { m_compressionOptions.setVolumeSize(arguments().metaData()[QStringLiteral("volumeSize")].toInt()); } const auto compressionMethods = m_model->archive()->property("compressionMethods").toStringList(); + qCDebug(ARK) << "compmethods:" << compressionMethods; if (compressionMethods.size() == 1) { m_compressionOptions.setCompressionMethod(compressionMethods.first()); } diff --git a/plugins/cli7zplugin/cliplugin.h b/plugins/cli7zplugin/cliplugin.h --- a/plugins/cli7zplugin/cliplugin.h +++ b/plugins/cli7zplugin/cliplugin.h @@ -59,11 +59,11 @@ } m_parseState; void setupCliProperties(); + void handleMethods(const QStringList &methods); int m_linesComment; Kerfuffle::Archive::Entry *m_currentArchiveEntry; bool m_isFirstInformationEntry; - QStringList m_compressionMethods; }; #endif // CLIPLUGIN_H diff --git a/plugins/cli7zplugin/cliplugin.cpp b/plugins/cli7zplugin/cliplugin.cpp --- a/plugins/cli7zplugin/cliplugin.cpp +++ b/plugins/cli7zplugin/cliplugin.cpp @@ -91,10 +91,11 @@ 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")}}); + {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$")}); @@ -178,19 +179,7 @@ } else if (line.startsWith(QStringLiteral("Method = "))) { QStringList methods = line.section(QLatin1Char('='), 1).trimmed().split(QLatin1Char(' '), QString::SkipEmptyParts); - // LZMA methods are output with some trailing numbers by 7z representing dictionary/block sizes. - // We are not interested in these, so remove them. - QMutableListIterator i(methods); - while (i.hasNext()) { - QString m = i.next(); - if (m.startsWith(QLatin1String("LZMA2"))) { - m = m.left(5); - } else if (m.startsWith(QLatin1String("LZMA"))) { - m = m.left(4); - } - i.setValue(m); - } - emit compressionMethodFound(methods); + handleMethods(methods); } else if (rxComment.match(line).hasMatch()) { m_parseState = ParseStateComment; @@ -256,14 +245,8 @@ // For zip archives we need to check method for each entry. if (m_archiveType == ArchiveTypeZip) { - QString method = line.mid(9).trimmed(); - if (method == QLatin1String("xz")) { - method = QStringLiteral("XZ"); - } - if (!m_compressionMethods.contains(method)) { - m_compressionMethods.append(method); - emit compressionMethodFound(m_compressionMethods); - } + QStringList methods = line.section(QLatin1Char('='), 1).trimmed().split(QLatin1Char(' '), QString::SkipEmptyParts); + handleMethods(methods); } } else if (line.startsWith(QStringLiteral("Encrypted = ")) && @@ -285,4 +268,34 @@ 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/cli7zplugin/kerfuffle_cli7z.json.cmake b/plugins/cli7zplugin/kerfuffle_cli7z.json.cmake --- a/plugins/cli7zplugin/kerfuffle_cli7z.json.cmake +++ b/plugins/cli7zplugin/kerfuffle_cli7z.json.cmake @@ -12,7 +12,7 @@ "Name[es]": "Complemento de archivo 7zip", "Name[et]": "7zip arhiivi plugin", "Name[fi]": "7zip-pakkaustuki", - "Name[fr]": "Module externe d'archive « 7zip »", + "Name[fr]": "Module externe d'archive « 7zip »", "Name[gl]": "Complemento de arquivo de 7zip", "Name[he]": "תוסף ארכיוני 7zip", "Name[it]": "Estensione per archivi 7zip", @@ -59,8 +59,12 @@ "LZMA2": "LZMA2", "PPMd": "PPMd" }, - "HeaderEncryption": true, - "SupportsMultiVolume": true, + "HeaderEncryption": true, + "EncryptionMethodDefault": "AES256", + "EncryptionMethods": { + "AES256": "AES256" + }, + "SupportsMultiVolume": true, "SupportsTesting": true }, "application/zip": { @@ -77,7 +81,14 @@ "PPMd": "PPMd" }, "Encryption": true, - "SupportsMultiVolume": true, + "EncryptionMethodDefault": "AES256", + "EncryptionMethods": { + "AES128": "AES128", + "AES192": "AES192", + "AES256": "AES256", + "ZipCrypto": "ZipCrypto" + }, + "SupportsMultiVolume": true, "SupportsTesting": true } } diff --git a/plugins/clirarplugin/cliplugin.h b/plugins/clirarplugin/cliplugin.h --- a/plugins/clirarplugin/cliplugin.h +++ b/plugins/clirarplugin/cliplugin.h @@ -64,6 +64,7 @@ bool m_isUnrar5; bool m_isPasswordProtected; bool m_isSolid; + bool m_isRAR5; int m_remainingIgnoreLines; int m_linesComment; diff --git a/plugins/clirarplugin/cliplugin.cpp b/plugins/clirarplugin/cliplugin.cpp --- a/plugins/clirarplugin/cliplugin.cpp +++ b/plugins/clirarplugin/cliplugin.cpp @@ -41,6 +41,7 @@ , m_isUnrar5(false) , m_isPasswordProtected(false) , m_isSolid(false) + , m_isRAR5(false) , m_remainingIgnoreLines(1) //The first line of UNRAR output is empty. , m_linesComment(0) { @@ -215,9 +216,10 @@ qCDebug(ARK) << "Solid archive detected"; } if (line.contains(QLatin1String("RAR 4"))) { - emit compressionMethodFound(QStringList{QStringLiteral("RAR4")}); + emit compressionMethodFound(QStringLiteral("RAR4")); } else if (line.contains(QLatin1String("RAR 5"))) { - emit compressionMethodFound(QStringList{QStringLiteral("RAR5")}); + emit compressionMethodFound(QStringLiteral("RAR5")); + m_isRAR5 = true; } } return true; @@ -287,6 +289,9 @@ m_isPasswordProtected = m_unrar5Details.value(QStringLiteral("flags")).contains(QStringLiteral("encrypted")); e->setProperty("isPasswordProtected", m_isPasswordProtected); + if (m_isPasswordProtected) { + m_isRAR5 ? emit encryptionMethodFound(QStringLiteral("AES256")) : emit encryptionMethodFound(QStringLiteral("AES128")); + } e->setProperty("fullPath", m_unrar5Details.value(QStringLiteral("name"))); e->setProperty("size", m_unrar5Details.value(QStringLiteral("size"))); @@ -327,7 +332,7 @@ // If we reach this point, then we can be sure that it's not a RAR5 // archive, so assume RAR4. - emit compressionMethodFound(QStringList{QStringLiteral("RAR4")}); + emit compressionMethodFound(QStringLiteral("RAR4")); if (rxCommentEnd.match(line).hasMatch()) { @@ -405,6 +410,7 @@ } else if (line.startsWith(QLatin1Char('*'))) { m_isPasswordProtected = true; m_unrar4Details.append(QString(line.trimmed()).remove(0, 1)); //Remove the asterisk + emit encryptionMethodFound(QStringLiteral("AES128")); // Entry names always start at the second position, so a line not // starting with a space is not an entry name. diff --git a/plugins/clirarplugin/kerfuffle_clirar.json.cmake b/plugins/clirarplugin/kerfuffle_clirar.json.cmake --- a/plugins/clirarplugin/kerfuffle_clirar.json.cmake +++ b/plugins/clirarplugin/kerfuffle_clirar.json.cmake @@ -12,7 +12,7 @@ "Name[es]": "Complemento de archivo RAR", "Name[et]": "RAR-arhiivi plugin", "Name[fi]": "RAR-pakkaustuki", - "Name[fr]": "Module externe d'archive « RAR »", + "Name[fr]": "Module externe d'archive « RAR »", "Name[gl]": "Complemento de arquivo RAR", "Name[he]": "תוסף ארכיוני RAR", "Name[it]": "Estensione per archivi RAR", @@ -56,7 +56,12 @@ "RAR4": "4", "RAR5": "5" }, - "HeaderEncryption": true, + "HeaderEncryption": true, + "EncryptionMethodDefault": "AES128", + "EncryptionMethods": { + "AES128": "AES128", + "AES256": "AES256" + }, "SupportsMultiVolume": true, "SupportsTesting": true, "SupportsWriteComment": true @@ -71,7 +76,12 @@ "RAR5": "5" }, "HeaderEncryption": true, - "SupportsMultiVolume": true, + "EncryptionMethodDefault": "AES128", + "EncryptionMethods": { + "AES128": "AES128", + "AES256": "AES256" + }, + "SupportsMultiVolume": true, "SupportsTesting": true, "SupportsWriteComment": true } diff --git a/plugins/cliunarchiverplugin/cliplugin.cpp b/plugins/cliunarchiverplugin/cliplugin.cpp --- a/plugins/cliunarchiverplugin/cliplugin.cpp +++ b/plugins/cliunarchiverplugin/cliplugin.cpp @@ -222,9 +222,9 @@ QString formatName = json.value(QStringLiteral("lsarFormatName")).toString(); if (formatName == QLatin1String("RAR")) { - emit compressionMethodFound(QStringList{QStringLiteral("RAR4")}); + emit compressionMethodFound(QStringLiteral("RAR4")); } else if (formatName == QLatin1String("RAR 5")) { - emit compressionMethodFound(QStringList{QStringLiteral("RAR5")}); + emit compressionMethodFound(QStringLiteral("RAR5")); } const QJsonArray entries = json.value(QStringLiteral("lsarContents")).toArray(); diff --git a/plugins/clizipplugin/cliplugin.h b/plugins/clizipplugin/cliplugin.h --- a/plugins/clizipplugin/cliplugin.h +++ b/plugins/clizipplugin/cliplugin.h @@ -60,7 +60,6 @@ int m_linesComment; QString m_tempComment; - QStringList m_compressionMethods; }; #endif // CLIPLUGIN_H diff --git a/plugins/clizipplugin/cliplugin.cpp b/plugins/clizipplugin/cliplugin.cpp --- a/plugins/clizipplugin/cliplugin.cpp +++ b/plugins/clizipplugin/cliplugin.cpp @@ -172,10 +172,7 @@ e->setProperty("method", rxMatch.captured(7)); QString method = convertCompressionMethod(rxMatch.captured(7)); - if (!m_compressionMethods.contains(method)) { - m_compressionMethods.append(method); - emit compressionMethodFound(m_compressionMethods); - } + emit compressionMethodFound(method); const QDateTime ts(QDate::fromString(rxMatch.captured(8), QStringLiteral("yyyyMMdd")), QTime::fromString(rxMatch.captured(9), QStringLiteral("hhmmss"))); diff --git a/plugins/clizipplugin/kerfuffle_clizip.json.cmake b/plugins/clizipplugin/kerfuffle_clizip.json.cmake --- a/plugins/clizipplugin/kerfuffle_clizip.json.cmake +++ b/plugins/clizipplugin/kerfuffle_clizip.json.cmake @@ -12,7 +12,7 @@ "Name[es]": "Complemento de archivo ZIP", "Name[et]": "ZIP-arhiivi plugin", "Name[fi]": "ZIP-pakkaustuki", - "Name[fr]": "Module externe d'archive « zip »", + "Name[fr]": "Module externe d'archive « zip »", "Name[gl]": "Complemento de arquivo ZIP", "Name[he]": "תוסף ארכיוני ZIP", "Name[it]": "Estensione per archivi ZIP", @@ -64,7 +64,11 @@ "Deflate": "deflate", "Store": "store" }, - "Encryption": true, + "Encryption": true, + "EncryptionMethodDefault": "ZipCrypto", + "EncryptionMethods": { + "ZipCrypto": "ZipCrypto" + }, "SupportsTesting": true } } diff --git a/plugins/libarchive/libarchiveplugin.cpp b/plugins/libarchive/libarchiveplugin.cpp --- a/plugins/libarchive/libarchiveplugin.cpp +++ b/plugins/libarchive/libarchiveplugin.cpp @@ -64,7 +64,7 @@ qDebug(ARK) << "Detected compression filter:" << archive_filter_name(m_archiveReader.data(), 0); QString compMethod = convertCompressionName(QString::fromUtf8(archive_filter_name(m_archiveReader.data(), 0))); if (!compMethod.isEmpty()) { - emit compressionMethodFound(QStringList{compMethod}); + emit compressionMethodFound(compMethod); } m_cachedArchiveEntryCount = 0;