diff --git a/kerfuffle/CMakeLists.txt b/kerfuffle/CMakeLists.txt --- a/kerfuffle/CMakeLists.txt +++ b/kerfuffle/CMakeLists.txt @@ -1,6 +1,7 @@ ########### next target ############### set(kerfuffle_SRCS + archiveformat.cpp archive_kerfuffle.cpp archiveinterface.cpp extractionsettingspage.cpp diff --git a/kerfuffle/mimetypes.h b/kerfuffle/archiveformat.h copy from kerfuffle/mimetypes.h copy to kerfuffle/archiveformat.h --- a/kerfuffle/mimetypes.h +++ b/kerfuffle/archiveformat.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Ragnar Thomsen + * Copyright (c) 2016 Elvis Angelaccio * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,33 +23,42 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef MIMETYPES_H -#define MIMETYPES_H +#ifndef ARCHIVEFORMAT_H +#define ARCHIVEFORMAT_H -#include "kerfuffle_export.h" +#include "archive_kerfuffle.h" -#include -#include -#include +#include namespace Kerfuffle { - KERFUFFLE_EXPORT QMimeType determineMimeType(const QString& filename); - KERFUFFLE_EXPORT QStringList supportedMimeTypes(); - KERFUFFLE_EXPORT QStringList supportedWriteMimeTypes(); - KERFUFFLE_EXPORT QSet supportedEncryptEntriesMimeTypes(); - KERFUFFLE_EXPORT QSet supportedEncryptHeaderMimeTypes(); + +class KERFUFFLE_EXPORT ArchiveFormat +{ +public: + explicit ArchiveFormat(); + explicit ArchiveFormat(const QMimeType& mimeType, Kerfuffle::Archive::EncryptionType encryptionType); + + /** + * @return The archive format of the given @p mimeType, according to the given @p metadata. + */ + static ArchiveFormat fromMetadata(const QMimeType& mimeType, const KPluginMetaData& metadata); /** - * @return A list with the supported read-only mimetypes, alphabetically sorted according to their comment. + * @return Whether the format is associated to a valid mimetype. */ - QStringList sortByComment(const QSet &mimeTypeSet); + bool isValid() const; /** - * @return Whether all the @p executables are available in the system PATH. + * @return The encryption type supported by the archive format. */ - bool findExecutables(const QJsonArray& executables); + Kerfuffle::Archive::EncryptionType encryptionType() const; + +private: + QMimeType m_mimeType; + Kerfuffle::Archive::EncryptionType m_encryptionType; +}; -} // namespace Kerfuffle +} -#endif // MIMETYPES_H +#endif // ARCHIVEFORMAT_H \ No newline at end of file diff --git a/kerfuffle/archiveformat.cpp b/kerfuffle/archiveformat.cpp new file mode 100644 --- /dev/null +++ b/kerfuffle/archiveformat.cpp @@ -0,0 +1,77 @@ +/* + * 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 "archiveformat.h" + +namespace Kerfuffle +{ + +ArchiveFormat::ArchiveFormat() : + m_encryptionType(Archive::Unencrypted) +{ +} + +ArchiveFormat::ArchiveFormat(const QMimeType& mimeType, Archive::EncryptionType encryptionType) : + m_mimeType(mimeType), + m_encryptionType(encryptionType) +{ +} + +ArchiveFormat ArchiveFormat::fromMetadata(const QMimeType& mimeType, const KPluginMetaData& metadata) +{ + const QJsonObject json = metadata.rawData(); + foreach (const QString& mime, metadata.mimeTypes()) { + if (mimeType.name() != mime) { + continue; + } + + const QJsonObject formatProps = json[mime].toObject(); + if (formatProps.isEmpty()) { + return ArchiveFormat(mimeType, Archive::Unencrypted); + } + + if (formatProps[QStringLiteral("HeaderEncryption")].toBool()) { + return ArchiveFormat(mimeType, Archive::HeaderEncrypted); + } + + if (formatProps[QStringLiteral("Encryption")].toBool()) { + return ArchiveFormat(mimeType, Archive::Encrypted); + } + } + + return ArchiveFormat(); +} + +bool ArchiveFormat::isValid() const +{ + return m_mimeType.isValid(); +} + +Archive::EncryptionType ArchiveFormat::encryptionType() const +{ + return m_encryptionType; +} + +} diff --git a/kerfuffle/createdialog.h b/kerfuffle/createdialog.h --- a/kerfuffle/createdialog.h +++ b/kerfuffle/createdialog.h @@ -34,6 +34,7 @@ #include "kerfuffle_export.h" #include +#include #include #include @@ -87,6 +88,7 @@ QVBoxLayout *m_vlayout; KConfigGroup m_config; QStringList m_supportedMimeTypes; + QVector m_writePlugins; private slots: void slotFileNameEdited(const QString &text); diff --git a/kerfuffle/createdialog.cpp b/kerfuffle/createdialog.cpp --- a/kerfuffle/createdialog.cpp +++ b/kerfuffle/createdialog.cpp @@ -28,6 +28,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "archiveformat.h" #include "createdialog.h" #include "ark_debug.h" #include "ui_createdialog.h" @@ -65,6 +66,7 @@ setModal(true); m_supportedMimeTypes = Kerfuffle::supportedWriteMimeTypes(); + m_writePlugins = Kerfuffle::supportedWritePlugins(); m_vlayout = new QVBoxLayout(); setLayout(m_vlayout); @@ -119,8 +121,11 @@ void CreateDialog::slotUpdateWidgets(int index) { const QMimeType mimeType = QMimeDatabase().mimeTypeForName(m_supportedMimeTypes.at(index)); + const KPluginMetaData metadata = preferredPluginFor(mimeType, m_writePlugins); + const ArchiveFormat archiveFormat = ArchiveFormat::fromMetadata(mimeType, metadata); + Q_ASSERT(archiveFormat.isValid()); - if (Kerfuffle::supportedEncryptEntriesMimeTypes().contains(mimeType.name())) { + if (archiveFormat.encryptionType() != Archive::Unencrypted) { m_ui->collapsibleEncryption->setEnabled(true); m_ui->collapsibleEncryption->setToolTip(QString()); } else { @@ -206,8 +211,12 @@ void CreateDialog::slotEncryptionToggled() { + const KPluginMetaData metadata = preferredPluginFor(currentMimeType(), m_writePlugins); + const ArchiveFormat archiveFormat = ArchiveFormat::fromMetadata(currentMimeType(), metadata); + Q_ASSERT(archiveFormat.isValid()); + const bool isExpanded = m_ui->collapsibleEncryption->isExpanded(); - if (isExpanded && Kerfuffle::supportedEncryptHeaderMimeTypes().contains(currentMimeType().name())) { + if (isExpanded && (archiveFormat.encryptionType() == Archive::HeaderEncrypted)) { m_ui->encryptHeaderCheckBox->setEnabled(true); m_ui->encryptHeaderCheckBox->setToolTip(QString()); } else { diff --git a/kerfuffle/kerfufflePlugin.desktop b/kerfuffle/kerfufflePlugin.desktop --- a/kerfuffle/kerfufflePlugin.desktop +++ b/kerfuffle/kerfufflePlugin.desktop @@ -71,14 +71,6 @@ [PropertyDef::X-KDE-Kerfuffle-ReadWrite] Type=bool -# Whether the plugin can encrypt its archive entries -[PropertyDef::X-KDE-Kerfuffle-EncryptEntries] -Type=QStringList - -# Whether the plugin can encrypt its archive header -[PropertyDef::X-KDE-Kerfuffle-EncryptHeader] -Type=QStringList - # The list of executables required by the plugin to operate in read-only mode. [PropertyDef::X-KDE-Kerfuffle-ReadOnlyExecutables] Type=QStringList diff --git a/kerfuffle/mimetypes.h b/kerfuffle/mimetypes.h --- a/kerfuffle/mimetypes.h +++ b/kerfuffle/mimetypes.h @@ -28,6 +28,8 @@ #include "kerfuffle_export.h" +#include + #include #include #include @@ -37,8 +39,16 @@ KERFUFFLE_EXPORT QMimeType determineMimeType(const QString& filename); KERFUFFLE_EXPORT QStringList supportedMimeTypes(); KERFUFFLE_EXPORT QStringList supportedWriteMimeTypes(); - KERFUFFLE_EXPORT QSet supportedEncryptEntriesMimeTypes(); - KERFUFFLE_EXPORT QSet supportedEncryptHeaderMimeTypes(); + + /** + * @return The list of available read-write plugins. + */ + KERFUFFLE_EXPORT QVector supportedWritePlugins(); + + /** + * @return The preferred plugin to handle @p mimeType, out of the given list of @p plugins. + */ + KERFUFFLE_EXPORT KPluginMetaData preferredPluginFor(const QMimeType& mimeType, const QVector& plugins); /** * @return A list with the supported read-only mimetypes, alphabetically sorted according to their comment. @@ -50,6 +60,11 @@ */ bool findExecutables(const QJsonArray& executables); + /** + * @return Whether @p p1 has higher priority than @p p2. + */ + bool comparePlugins(const KPluginMetaData& p1, const KPluginMetaData& p2); + } // namespace Kerfuffle #endif // MIMETYPES_H diff --git a/kerfuffle/mimetypes.cpp b/kerfuffle/mimetypes.cpp --- a/kerfuffle/mimetypes.cpp +++ b/kerfuffle/mimetypes.cpp @@ -32,7 +32,6 @@ #include #include -#include namespace Kerfuffle { @@ -159,50 +158,44 @@ return sortByComment(supported); } -QSet supportedEncryptEntriesMimeTypes() +QVector supportedWritePlugins() { - const QVector offers = KPluginLoader::findPlugins(QStringLiteral("kerfuffle"), [](const KPluginMetaData& metaData) { - return metaData.serviceTypes().contains(QStringLiteral("Kerfuffle/Plugin")); + const auto readWritePlugins = KPluginLoader::findPlugins(QStringLiteral("kerfuffle"), [](const KPluginMetaData& metaData) { + return metaData.serviceTypes().contains(QStringLiteral("Kerfuffle/Plugin")) && + metaData.rawData()[QStringLiteral("X-KDE-Kerfuffle-ReadWrite")].toBool(); }); - QSet supported; + // Drop plugins whose executables are not available. + QVector supportedPlugins; + foreach (const auto& plugin, readWritePlugins) { + const QJsonArray rwExecutables = plugin.rawData()[QStringLiteral("X-KDE-Kerfuffle-ReadWriteExecutables")].toArray(); + // Libarchive does not depend on executables. + if (rwExecutables.isEmpty()) { + supportedPlugins << plugin; + continue; + } - foreach (const KPluginMetaData& pluginMetadata, offers) { - const auto mimeTypes = pluginMetadata.rawData()[QStringLiteral("X-KDE-Kerfuffle-EncryptEntries")].toArray(); - foreach (const auto& mimeType, mimeTypes) { - if (mimeType.toString().isEmpty()) { - continue; - } - supported.insert(mimeType.toString()); + if (findExecutables(rwExecutables)) { + supportedPlugins << plugin; } } - qCDebug(ARK) << "Entry encryption supported for mimetypes" << supported; - - return supported; + return supportedPlugins; } -QSet supportedEncryptHeaderMimeTypes() +KPluginMetaData preferredPluginFor(const QMimeType &mimeType, const QVector &plugins) { - const QVector offers = KPluginLoader::findPlugins(QStringLiteral("kerfuffle"), [](const KPluginMetaData& metaData) { - return metaData.serviceTypes().contains(QStringLiteral("Kerfuffle/Plugin")); - }); - - QSet supported; - - foreach (const KPluginMetaData& pluginMetadata, offers) { - const auto mimeTypes = pluginMetadata.rawData()[QStringLiteral("X-KDE-Kerfuffle-EncryptHeader")].toArray(); - foreach (const auto& mimeType, mimeTypes) { - if (mimeType.toString().isEmpty()) { - continue; - } - supported.insert(mimeType.toString()); + QVector filteredPlugins; + foreach (const KPluginMetaData& plugin, plugins) { + if (plugin.mimeTypes().contains(mimeType.name())) { + filteredPlugins << plugin; } } - qCDebug(ARK) << "Header encryption supported for mimetypes" << supported; + qSort(filteredPlugins.begin(), filteredPlugins.end(), comparePlugins); + qCDebug(ARK) << "Preferred plugin for" << mimeType.name() << "is" << filteredPlugins.first().pluginId(); - return supported; + return filteredPlugins.first(); } QStringList sortByComment(const QSet &mimeTypeSet) @@ -241,4 +234,9 @@ return true; } +bool comparePlugins(const KPluginMetaData& p1, const KPluginMetaData& p2) +{ + return (p1.rawData()[QStringLiteral("X-KDE-Priority")].toInt()) > (p2.rawData()[QStringLiteral("X-KDE-Priority")].toInt()); +} + } // namespace Kerfuffle diff --git a/plugins/cli7zplugin/kerfuffle_cli7z.json b/plugins/cli7zplugin/kerfuffle_cli7z.json --- a/plugins/cli7zplugin/kerfuffle_cli7z.json +++ b/plugins/cli7zplugin/kerfuffle_cli7z.json @@ -47,19 +47,18 @@ "Website": "http://www.kde.org" }, "X-KDE-Kerfuffle-APIRevision": 1, - "X-KDE-Kerfuffle-EncryptEntries": [ - "application/x-7z-compressed", - "application/zip" - ], - "X-KDE-Kerfuffle-EncryptHeader": [ - "application/x-7z-compressed" - ], "X-KDE-Kerfuffle-ReadOnlyExecutables": [ "7z" ], "X-KDE-Kerfuffle-ReadWrite": true, "X-KDE-Kerfuffle-ReadWriteExecutables": [ "7z" ], - "X-KDE-Priority": 180 -} \ No newline at end of file + "X-KDE-Priority": 180, + "application/x-7z-compressed": { + "HeaderEncryption": true + }, + "application/zip": { + "Encryption": true + } +} diff --git a/plugins/clirarplugin/kerfuffle_clirar.json b/plugins/clirarplugin/kerfuffle_clirar.json --- a/plugins/clirarplugin/kerfuffle_clirar.json +++ b/plugins/clirarplugin/kerfuffle_clirar.json @@ -49,18 +49,15 @@ "Website": "http://www.kde.org" }, "X-KDE-Kerfuffle-APIRevision": 1, - "X-KDE-Kerfuffle-EncryptEntries": [ - "application/x-rar" - ], - "X-KDE-Kerfuffle-EncryptHeader": [ - "application/x-rar" - ], "X-KDE-Kerfuffle-ReadOnlyExecutables": [ "unrar" ], "X-KDE-Kerfuffle-ReadWrite": true, "X-KDE-Kerfuffle-ReadWriteExecutables": [ "rar" ], - "X-KDE-Priority": 120 -} \ No newline at end of file + "X-KDE-Priority": 120, + "application/x-rar": { + "HeaderEncryption": true + } +} diff --git a/plugins/clizipplugin/kerfuffle_clizip.json b/plugins/clizipplugin/kerfuffle_clizip.json --- a/plugins/clizipplugin/kerfuffle_clizip.json +++ b/plugins/clizipplugin/kerfuffle_clizip.json @@ -45,17 +45,19 @@ "Website": "http://www.kde.org" }, "X-KDE-Kerfuffle-APIRevision": 1, - "X-KDE-Kerfuffle-EncryptEntries": [ - "application/x-java-archive", - "application/zip" - ], "X-KDE-Kerfuffle-ReadOnlyExecutables": [ "zipinfo", "unzip" ], "X-KDE-Kerfuffle-ReadWrite": true, "X-KDE-Kerfuffle-ReadWriteExecutables": [ "zip" ], - "X-KDE-Priority": 160 -} \ No newline at end of file + "X-KDE-Priority": 160, + "application/zip": { + "Encryption": true + }, + "application/x-java-archive": { + "Encryption": true + } +}