File indexing completed on 2024-05-19 09:44:01
0001 /* 0002 SPDX-FileCopyrightText: 2016 Elvis Angelaccio <elvis.angelaccio@kde.org> 0003 0004 SPDX-License-Identifier: BSD-2-Clause 0005 */ 0006 0007 #include "pluginmanager.h" 0008 #include "ark_debug.h" 0009 #include "settings.h" 0010 0011 #include <KSharedConfig> 0012 0013 #include <QFileInfo> 0014 #include <QMimeDatabase> 0015 #include <QPluginLoader> 0016 #include <QProcess> 0017 #include <QRegularExpression> 0018 #include <QSet> 0019 0020 #include <algorithm> 0021 0022 namespace Kerfuffle 0023 { 0024 PluginManager::PluginManager(QObject *parent) 0025 : QObject(parent) 0026 { 0027 loadPlugins(); 0028 } 0029 0030 QVector<Plugin *> PluginManager::installedPlugins() const 0031 { 0032 return m_plugins; 0033 } 0034 0035 QVector<Plugin *> PluginManager::availablePlugins() const 0036 { 0037 QVector<Plugin *> availablePlugins; 0038 for (Plugin *plugin : std::as_const(m_plugins)) { 0039 if (plugin->isValid()) { 0040 availablePlugins << plugin; 0041 } 0042 } 0043 0044 return availablePlugins; 0045 } 0046 0047 QVector<Plugin *> PluginManager::availableWritePlugins() const 0048 { 0049 QVector<Plugin *> availableWritePlugins; 0050 const auto plugins = availablePlugins(); 0051 for (Plugin *plugin : plugins) { 0052 if (plugin->isReadWrite()) { 0053 availableWritePlugins << plugin; 0054 } 0055 } 0056 0057 return availableWritePlugins; 0058 } 0059 0060 QVector<Plugin *> PluginManager::enabledPlugins() const 0061 { 0062 QVector<Plugin *> enabledPlugins; 0063 for (Plugin *plugin : std::as_const(m_plugins)) { 0064 if (plugin->isEnabled()) { 0065 enabledPlugins << plugin; 0066 } 0067 } 0068 0069 return enabledPlugins; 0070 } 0071 0072 QVector<Plugin *> PluginManager::preferredPluginsFor(const QMimeType &mimeType) 0073 { 0074 const auto mimeName = mimeType.name(); 0075 if (m_preferredPluginsCache.contains(mimeName)) { 0076 return m_preferredPluginsCache.value(mimeName); 0077 } 0078 0079 const auto plugins = preferredPluginsFor(mimeType, false); 0080 m_preferredPluginsCache.insert(mimeName, plugins); 0081 return plugins; 0082 } 0083 0084 QVector<Plugin *> PluginManager::preferredWritePluginsFor(const QMimeType &mimeType) const 0085 { 0086 return preferredPluginsFor(mimeType, true); 0087 } 0088 0089 Plugin *PluginManager::preferredPluginFor(const QMimeType &mimeType) 0090 { 0091 const QVector<Plugin *> preferredPlugins = preferredPluginsFor(mimeType); 0092 return preferredPlugins.isEmpty() ? new Plugin() : preferredPlugins.first(); 0093 } 0094 0095 Plugin *PluginManager::preferredWritePluginFor(const QMimeType &mimeType) const 0096 { 0097 const QVector<Plugin *> preferredWritePlugins = preferredWritePluginsFor(mimeType); 0098 return preferredWritePlugins.isEmpty() ? new Plugin() : preferredWritePlugins.first(); 0099 } 0100 0101 QStringList PluginManager::supportedMimeTypes(MimeSortingMode mode) const 0102 { 0103 QSet<QString> supported; 0104 QMimeDatabase db; 0105 const auto plugins = availablePlugins(); 0106 for (Plugin *plugin : plugins) { 0107 const auto mimeTypes = plugin->metaData().mimeTypes(); 0108 for (const auto &mimeType : mimeTypes) { 0109 if (db.mimeTypeForName(mimeType).isValid()) { 0110 supported.insert(mimeType); 0111 } 0112 } 0113 } 0114 0115 // Remove entry for lrzipped tar if lrzip executable not found in path. 0116 if (QStandardPaths::findExecutable(QStringLiteral("lrzip")).isEmpty()) { 0117 supported.remove(QStringLiteral("application/x-lrzip-compressed-tar")); 0118 } 0119 0120 // Remove entry for lz4-compressed tar if lz4 executable not found in path. 0121 if (QStandardPaths::findExecutable(QStringLiteral("lz4")).isEmpty()) { 0122 supported.remove(QStringLiteral("application/x-lz4-compressed-tar")); 0123 } 0124 0125 static bool s_libarchiveHasLzo = libarchiveHasLzo(); 0126 // Remove entry for lzo-compressed tar if libarchive not linked against lzo and lzop executable not found in path. 0127 if (!s_libarchiveHasLzo && QStandardPaths::findExecutable(QStringLiteral("lzop")).isEmpty()) { 0128 supported.remove(QStringLiteral("application/x-tzo")); 0129 } 0130 0131 if (mode == SortByComment) { 0132 return sortByComment(supported); 0133 } 0134 0135 return supported.values(); 0136 } 0137 0138 QStringList PluginManager::supportedWriteMimeTypes(MimeSortingMode mode) const 0139 { 0140 QSet<QString> supported; 0141 QMimeDatabase db; 0142 const auto plugins = availableWritePlugins(); 0143 for (Plugin *plugin : plugins) { 0144 const auto mimeTypes = plugin->metaData().mimeTypes(); 0145 for (const auto &mimeType : mimeTypes) { 0146 if (db.mimeTypeForName(mimeType).isValid()) { 0147 supported.insert(mimeType); 0148 } 0149 } 0150 } 0151 0152 // Remove entry for lrzipped tar if lrzip executable not found in path. 0153 if (QStandardPaths::findExecutable(QStringLiteral("lrzip")).isEmpty()) { 0154 supported.remove(QStringLiteral("application/x-lrzip-compressed-tar")); 0155 } 0156 0157 // Remove entry for lz4-compressed tar if lz4 executable not found in path. 0158 if (QStandardPaths::findExecutable(QStringLiteral("lz4")).isEmpty()) { 0159 supported.remove(QStringLiteral("application/x-lz4-compressed-tar")); 0160 } 0161 0162 // Remove entry for lzo-compressed tar if libarchive not linked against lzo and lzop executable not found in path. 0163 if (!libarchiveHasLzo() && QStandardPaths::findExecutable(QStringLiteral("lzop")).isEmpty()) { 0164 supported.remove(QStringLiteral("application/x-tzo")); 0165 } 0166 0167 // shared-mime-info 2.3 explicitly separated application/x-bzip2-compressed-tar from application/x-bzip-compressed-tar 0168 // since bzip2 is not compatible with the old (and deprecated) bzip format. 0169 // See https://gitlab.freedesktop.org/xdg/shared-mime-info/-/merge_requests/239 0170 // With shared-mime-info 2.3 (or newer) we can't have both mimetypes at the same time, since libarchive does not support 0171 // the old deprecated bzip format. Also we can't know which version of shared-mime-info the system is actually using. 0172 // For these reasons, just take the mimetype from QMimeDatabase to keep the compatibility with any shared-mime-info version. 0173 if (supported.contains(QLatin1String("application/x-bzip-compressed-tar")) && supported.contains(QLatin1String("application/x-bzip2-compressed-tar"))) { 0174 supported.remove(QLatin1String("application/x-bzip-compressed-tar")); 0175 supported.remove(QLatin1String("application/x-bzip2-compressed-tar")); 0176 supported.insert(QMimeDatabase().mimeTypeForFile(QStringLiteral("dummy.tar.bz2"), QMimeDatabase::MatchExtension).name()); 0177 } 0178 0179 if (mode == SortByComment) { 0180 return sortByComment(supported); 0181 } 0182 0183 return supported.values(); 0184 } 0185 0186 QVector<Plugin *> PluginManager::filterBy(const QVector<Plugin *> &plugins, const QMimeType &mimeType) const 0187 { 0188 const bool supportedMime = supportedMimeTypes().contains(mimeType.name()); 0189 QVector<Plugin *> filteredPlugins; 0190 for (Plugin *plugin : plugins) { 0191 if (!supportedMime) { 0192 // Check whether the mimetype inherits from a supported mimetype. 0193 const QStringList mimeTypes = plugin->metaData().mimeTypes(); 0194 for (const QString &mime : mimeTypes) { 0195 if (mimeType.inherits(mime)) { 0196 filteredPlugins << plugin; 0197 } 0198 } 0199 } else if (plugin->metaData().mimeTypes().contains(mimeType.name())) { 0200 filteredPlugins << plugin; 0201 } 0202 } 0203 0204 return filteredPlugins; 0205 } 0206 0207 void PluginManager::loadPlugins() 0208 { 0209 const QVector<KPluginMetaData> plugins = KPluginMetaData::findPlugins(QStringLiteral("kerfuffle")); 0210 for (const KPluginMetaData &metaData : plugins) { 0211 Plugin *plugin = new Plugin(this, metaData); 0212 plugin->setEnabled(!ArkSettings::disabledPlugins().contains(metaData.pluginId())); 0213 m_plugins << plugin; 0214 } 0215 } 0216 0217 QVector<Plugin *> PluginManager::preferredPluginsFor(const QMimeType &mimeType, bool readWrite) const 0218 { 0219 QVector<Plugin *> preferredPlugins = filterBy((readWrite ? availableWritePlugins() : availablePlugins()), mimeType); 0220 0221 std::sort(preferredPlugins.begin(), preferredPlugins.end(), [](Plugin *p1, Plugin *p2) { 0222 return p1->priority() > p2->priority(); 0223 }); 0224 0225 return preferredPlugins; 0226 } 0227 0228 QStringList PluginManager::sortByComment(const QSet<QString> &mimeTypes) 0229 { 0230 QMap<QString, QString> map; 0231 0232 // Initialize the QMap to sort by comment. 0233 for (const QString &mimeType : mimeTypes) { 0234 QMimeType mime(QMimeDatabase().mimeTypeForName(mimeType)); 0235 map[mime.comment().toLower()] = mime.name(); 0236 } 0237 0238 // Convert to sorted QStringList. 0239 QStringList sortedMimeTypes; 0240 for (const QString &value : std::as_const(map)) { 0241 sortedMimeTypes << value; 0242 } 0243 0244 return sortedMimeTypes; 0245 } 0246 0247 bool PluginManager::libarchiveHasLzo() 0248 { 0249 // Step 1: look for the libarchive plugin, which is built against libarchive. 0250 const QString pluginPath = QPluginLoader(QStringLiteral("kerfuffle/kerfuffle_libarchive")).fileName(); 0251 0252 // Step 2: process the libarchive plugin dependencies to figure out the absolute libarchive path. 0253 QProcess dependencyTool; 0254 QStringList args; 0255 #ifdef DEPENDENCY_TOOL_ARGS 0256 args << QStringLiteral(DEPENDENCY_TOOL_ARGS); 0257 #endif 0258 dependencyTool.setProgram(QStringLiteral(DEPENDENCY_TOOL)); 0259 dependencyTool.setArguments(args + QStringList(pluginPath)); 0260 dependencyTool.start(); 0261 dependencyTool.waitForFinished(); 0262 const QString output = QString::fromUtf8(dependencyTool.readAllStandardOutput()); 0263 QRegularExpression regex(QStringLiteral("/.*/libarchive.so|/.*/libarchive.*.dylib")); 0264 if (!regex.match(output).hasMatch()) { 0265 return false; 0266 } 0267 0268 // Step 3: check whether libarchive links against liblzo. 0269 const QStringList libarchivePath(regex.match(output).captured(0)); 0270 dependencyTool.setArguments(args + libarchivePath); 0271 dependencyTool.start(); 0272 dependencyTool.waitForFinished(); 0273 return dependencyTool.readAllStandardOutput().contains(QByteArrayLiteral("lzo")); 0274 } 0275 0276 } 0277 0278 #include "moc_pluginmanager.cpp"