File indexing completed on 2025-01-19 03:55:36
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2018-07-30 0007 * Description : manager to load external plugins at run-time: private container 0008 * 0009 * SPDX-FileCopyrightText: 2018-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * 0011 * SPDX-License-Identifier: GPL-2.0-or-later 0012 * 0013 * ============================================================ */ 0014 0015 #include "dpluginloader_p.h" 0016 0017 // Qt includes 0018 0019 #include <QStringList> 0020 #include <QElapsedTimer> 0021 #include <QDirIterator> 0022 #include <QStandardPaths> 0023 #include <QMessageBox> 0024 #include <QLibraryInfo> 0025 #include <QMetaProperty> 0026 #include <QFileInfo> 0027 0028 // KDE includes 0029 0030 #include <ksharedconfig.h> 0031 #include <kconfiggroup.h> 0032 0033 // Local includes 0034 0035 #include "digikam_debug.h" 0036 #include "digikam_config.h" 0037 #include "digikam_version.h" 0038 #include "digikam_globals.h" 0039 0040 namespace Digikam 0041 { 0042 0043 DPluginLoader::Private::Private() 0044 : pluginsLoaded(false) 0045 { 0046 // Do not load these plugins as they are not currently working. 0047 0048 blacklist << QLatin1String("Generic_FaceBook_Plugin"); 0049 blacklist << QLatin1String("Generic_IpFs_Plugin"); 0050 blacklist << QLatin1String("Generic_Rajce_Plugin"); 0051 DKBlacklist << QLatin1String("Generic_DNGConverter_Plugin"); 0052 blacklist << QLatin1String("Generic_YandexFotki_Plugin"); 0053 blacklist << QLatin1String("Generic_VKontakte_Plugin"); 0054 } 0055 0056 DPluginLoader::Private::~Private() 0057 { 0058 } 0059 0060 QFileInfoList DPluginLoader::Private::pluginEntriesList() const 0061 { 0062 QStringList pathList; 0063 0064 // First we try to load in first the local plugin if DK_PLUG_PATH variable is declared. 0065 // Else, we will load plugins from the system using the standard Qt plugin path. 0066 0067 QByteArray dkenv = qgetenv("DK_PLUGIN_PATH"); 0068 0069 if (dkenv.isEmpty()) 0070 { 0071 0072 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0073 0074 pathList << QLibraryInfo::path(QLibraryInfo::PluginsPath) + QLatin1String("/digikam/"); 0075 0076 #else 0077 0078 pathList << QLibraryInfo::location(QLibraryInfo::PluginsPath) + QLatin1String("/digikam/"); 0079 0080 #endif 0081 0082 } 0083 else 0084 { 0085 qCWarning(DIGIKAM_GENERAL_LOG) << "DK_PLUGIN_PATH env.variable detected. " 0086 "We will use it to load plugin..."; 0087 0088 pathList << QString::fromUtf8(dkenv).split(QLatin1Char(';'), 0089 QT_SKIP_EMPTY_PARTS); 0090 } 0091 0092 qCDebug(DIGIKAM_GENERAL_LOG) << "Parsing plugins from" << pathList; 0093 0094 #ifdef Q_OS_MACOS 0095 0096 QString filter(QLatin1String("*.dylib *.so")); 0097 0098 #elif defined Q_OS_WIN 0099 0100 QString filter(QLatin1String("*.dll")); 0101 0102 #else 0103 0104 QString filter(QLatin1String("*.so")); 0105 0106 #endif 0107 0108 QFileInfoList allFiles; 0109 QStringList dupFiles; 0110 0111 Q_FOREACH (const QString& path, pathList) 0112 { 0113 QDir dir(path, filter, QDir::Unsorted, 0114 QDir::Files | QDir::NoDotAndDotDot); 0115 0116 QDirIterator it(dir, QDirIterator::Subdirectories); 0117 0118 while (it.hasNext()) 0119 { 0120 it.next(); 0121 0122 if (!it.filePath().contains(QLatin1String("dSYM")) && // Ignore debug binary extensions under MacOS 0123 !it.filePath().contains(QLatin1String("marble")) && // Ignore Marble plugins. 0124 !dupFiles.contains(it.fileInfo().baseName())) 0125 { 0126 dupFiles << it.fileInfo().baseName(); 0127 0128 if (it.fileInfo().baseName().startsWith(QLatin1String("DImg_"))) 0129 { 0130 allFiles.prepend(it.fileInfo()); 0131 } 0132 else 0133 { 0134 allFiles.append(it.fileInfo()); 0135 } 0136 } 0137 } 0138 } 0139 0140 qCDebug(DIGIKAM_GENERAL_LOG) << "Plugins found:" << allFiles.count(); 0141 0142 return allFiles; 0143 } 0144 0145 bool DPluginLoader::Private::appendPlugin(QObject* const obj, 0146 QPluginLoader* const loader) 0147 { 0148 DPlugin* const plugin = qobject_cast<DPlugin*>(obj); 0149 0150 if (plugin) 0151 { 0152 Q_ASSERT(obj->metaObject()->superClass()); // all our plugins have a super class 0153 0154 if (plugin->version() == QLatin1String(digikam_version_short)) 0155 { 0156 qCDebug(DIGIKAM_GENERAL_LOG) << "Plugin of type" << obj->metaObject()->superClass()->className() 0157 << "loaded from" << loader->fileName(); 0158 0159 KSharedConfigPtr config = KSharedConfig::openConfig(); 0160 KConfigGroup group = config->group(DPluginLoader::instance()->configGroupName()); 0161 0162 plugin->setShouldLoaded(group.readEntry(plugin->iid(), true)); 0163 plugin->setLibraryFileName(loader->fileName()); 0164 0165 allPlugins << plugin; 0166 0167 return true; 0168 } 0169 } 0170 0171 return false; 0172 } 0173 0174 void DPluginLoader::Private::loadPlugins() 0175 { 0176 if (pluginsLoaded) 0177 { 0178 return; 0179 } 0180 0181 QElapsedTimer t; 0182 t.start(); 0183 qCDebug(DIGIKAM_GENERAL_LOG) << "Starting to load external tools."; 0184 0185 Q_ASSERT(allPlugins.isEmpty()); 0186 0187 Q_FOREACH (const QFileInfo& info, pluginEntriesList()) 0188 { 0189 if (!whitelist.isEmpty() && !whitelist.contains(info.baseName())) 0190 { 0191 qCDebug(DIGIKAM_GENERAL_LOG) << "Ignoring non-whitelisted plugin" << info.filePath(); 0192 continue; 0193 } 0194 0195 if (blacklist.contains(info.baseName())) 0196 { 0197 qCDebug(DIGIKAM_GENERAL_LOG) << "Ignoring blacklisted plugin" << info.filePath(); 0198 continue; 0199 } 0200 0201 if ((qApp->applicationName() == QLatin1String("showfoto")) && 0202 (info.baseName().startsWith(QLatin1String("Bqm_")))) 0203 { 0204 qCDebug(DIGIKAM_GENERAL_LOG) << "Ignoring specific digiKam BQM plugin in Showfoto" << info.filePath(); 0205 continue; 0206 } 0207 0208 if ((qApp->applicationName() == QLatin1String("digikam")) && 0209 (DKBlacklist.contains(info.baseName()))) 0210 { 0211 qCDebug(DIGIKAM_GENERAL_LOG) << "Ignoring specific Showfoto plugin in digiKam" << info.filePath(); 0212 0213 continue; 0214 } 0215 /* 0216 qCDebug(DIGIKAM_GENERAL_LOG) << info.baseName() << "-" << info.canonicalPath(); 0217 */ 0218 const QString path = info.canonicalFilePath(); 0219 QPluginLoader* const loader = new QPluginLoader(path, DPluginLoader::instance()); 0220 QObject* const obj = loader->instance(); 0221 0222 if (obj) 0223 { 0224 bool isPlugin = appendPlugin(obj, loader); 0225 0226 if (!isPlugin) 0227 { 0228 qCWarning(DIGIKAM_GENERAL_LOG) << "Ignoring the following plugin since it couldn't be loaded:" 0229 << path; 0230 0231 qCDebug(DIGIKAM_GENERAL_LOG) << "External plugin failure:" << path 0232 << "is a plugin, but it does not implement the" 0233 << "right interface or it was compiled against" 0234 << "an old version of digiKam. Ignoring it."; 0235 delete loader; 0236 } 0237 } 0238 else 0239 { 0240 qCWarning(DIGIKAM_GENERAL_LOG) << "Ignoring to load the following file since it doesn't look like " 0241 "a valid digiKam external plugin:" << path << QT_ENDL 0242 << "Reason:" << loader->errorString(); 0243 delete loader; 0244 } 0245 } 0246 0247 if (allPlugins.isEmpty()) 0248 { 0249 qCWarning(DIGIKAM_GENERAL_LOG) << "No plugins loaded. Please check if the plugins were installed in the correct path," 0250 << "or if any errors occurred while loading plugins."; 0251 } 0252 0253 pluginsLoaded = true; 0254 0255 qCDebug(DIGIKAM_GENERAL_LOG) << Q_FUNC_INFO << "Time elapsed:" << t.elapsed() << "ms"; 0256 } 0257 0258 } // namespace Digikam