File indexing completed on 2025-01-05 03:59:31
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2008 Torsten Rahn <tackat@kde.org> 0004 // SPDX-FileCopyrightText: 2009 Jens-Michael Hoffmann <jensmh@gmx.de> 0005 // 0006 0007 // Own 0008 #include "PluginManager.h" 0009 0010 // Qt 0011 #include <QPluginLoader> 0012 #include <QElapsedTimer> 0013 #include <QMessageBox> 0014 #include <QLibraryInfo> 0015 0016 // Local dir 0017 #include "MarbleDirs.h" 0018 #include "RenderPlugin.h" 0019 #include "ParseRunnerPlugin.h" 0020 #include "ReverseGeocodingRunnerPlugin.h" 0021 #include "SearchRunnerPlugin.h" 0022 0023 #include "digikam_debug.h" 0024 0025 namespace Marble 0026 { 0027 0028 class PluginManagerPrivate 0029 { 0030 public: 0031 PluginManagerPrivate(PluginManager* parent) 0032 : m_pluginsLoaded(false), 0033 m_parent(parent) 0034 { 0035 } 0036 0037 ~PluginManagerPrivate(); 0038 0039 void loadPlugins(); 0040 bool addPlugin(QObject *obj, const QPluginLoader *loader); 0041 0042 bool m_pluginsLoaded; 0043 QList<const RenderPlugin *> m_renderPluginTemplates; 0044 QList<const SearchRunnerPlugin *> m_searchRunnerPlugins; 0045 QList<const ReverseGeocodingRunnerPlugin *> m_reverseGeocodingRunnerPlugins; 0046 QList<const ParseRunnerPlugin *> m_parsingRunnerPlugins; 0047 PluginManager* m_parent; 0048 static QStringList m_blacklist; 0049 static QStringList m_whitelist; 0050 0051 #ifdef Q_OS_ANDROID 0052 QStringList m_pluginPaths; 0053 #endif 0054 }; 0055 0056 QStringList PluginManagerPrivate::m_blacklist; 0057 QStringList PluginManagerPrivate::m_whitelist; 0058 0059 PluginManagerPrivate::~PluginManagerPrivate() 0060 { 0061 // nothing to do 0062 } 0063 0064 PluginManager::PluginManager( QObject *parent ) : QObject( parent ), 0065 d( new PluginManagerPrivate(this) ) 0066 { 0067 //Checking assets:/plugins for uninstalled plugins 0068 #ifdef Q_OS_ANDROID 0069 installPluginsFromAssets(); 0070 #endif 0071 } 0072 0073 PluginManager::~PluginManager() 0074 { 0075 delete d; 0076 } 0077 0078 QList<const RenderPlugin *> PluginManager::renderPlugins() const 0079 { 0080 d->loadPlugins(); 0081 return d->m_renderPluginTemplates; 0082 } 0083 0084 void PluginManager::addRenderPlugin( const RenderPlugin *plugin ) 0085 { 0086 d->loadPlugins(); 0087 d->m_renderPluginTemplates << plugin; 0088 Q_EMIT renderPluginsChanged(); 0089 } 0090 0091 QList<const SearchRunnerPlugin *> PluginManager::searchRunnerPlugins() const 0092 { 0093 d->loadPlugins(); 0094 return d->m_searchRunnerPlugins; 0095 } 0096 0097 void PluginManager::addSearchRunnerPlugin( const SearchRunnerPlugin *plugin ) 0098 { 0099 d->loadPlugins(); 0100 d->m_searchRunnerPlugins << plugin; 0101 Q_EMIT searchRunnerPluginsChanged(); 0102 } 0103 0104 QList<const ReverseGeocodingRunnerPlugin *> PluginManager::reverseGeocodingRunnerPlugins() const 0105 { 0106 d->loadPlugins(); 0107 return d->m_reverseGeocodingRunnerPlugins; 0108 } 0109 0110 void PluginManager::addReverseGeocodingRunnerPlugin( const ReverseGeocodingRunnerPlugin *plugin ) 0111 { 0112 d->loadPlugins(); 0113 d->m_reverseGeocodingRunnerPlugins << plugin; 0114 Q_EMIT reverseGeocodingRunnerPluginsChanged(); 0115 } 0116 0117 QList<const ParseRunnerPlugin *> PluginManager::parsingRunnerPlugins() const 0118 { 0119 d->loadPlugins(); 0120 return d->m_parsingRunnerPlugins; 0121 } 0122 0123 void PluginManager::addParseRunnerPlugin( const ParseRunnerPlugin *plugin ) 0124 { 0125 d->loadPlugins(); 0126 d->m_parsingRunnerPlugins << plugin; 0127 Q_EMIT parseRunnerPluginsChanged(); 0128 } 0129 0130 void PluginManager::blacklistPlugin(const QString &filename) 0131 { 0132 0133 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0134 0135 PluginManagerPrivate::m_blacklist << QLibraryInfo::path(QLibraryInfo::PluginsPath) + QString::fromUtf8("/marble/") + filename; 0136 0137 #else 0138 0139 PluginManagerPrivate::m_blacklist << QLibraryInfo::location(QLibraryInfo::PluginsPath) + QString::fromUtf8("/marble/") + filename; 0140 0141 #endif 0142 0143 } 0144 0145 void PluginManager::whitelistPlugin(const QString &filename) 0146 { 0147 0148 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0149 0150 PluginManagerPrivate::m_whitelist << QLibraryInfo::path(QLibraryInfo::PluginsPath) + QString::fromUtf8("/marble/") + filename; 0151 0152 #else 0153 0154 PluginManagerPrivate::m_whitelist << QLibraryInfo::location(QLibraryInfo::PluginsPath) + QString::fromUtf8("/marble/") + filename; 0155 0156 #endif 0157 0158 } 0159 0160 /** Append obj to the given plugins list if it inherits both T and U */ 0161 template<class Iface, class Plugin> 0162 bool appendPlugin( QObject * obj, const QPluginLoader *loader, QList<Plugin> &plugins ) 0163 { 0164 if ( qobject_cast<Iface*>( obj ) && qobject_cast<Plugin>( obj ) ) { 0165 Q_ASSERT( obj->metaObject()->superClass() ); // all our plugins have a super class 0166 qCDebug(DIGIKAM_MARBLE_LOG) << obj->metaObject()->superClass()->className() 0167 << "plugin loaded from" << (loader ? loader->fileName() : QString::fromUtf8( "<static>")); 0168 auto plugin = qobject_cast<Plugin>( obj ); 0169 Q_ASSERT( plugin ); // checked above 0170 plugins << plugin; 0171 return true; 0172 } 0173 0174 return false; 0175 } 0176 0177 bool PluginManagerPrivate::addPlugin(QObject *obj, const QPluginLoader *loader) 0178 { 0179 bool isPlugin = appendPlugin<RenderPluginInterface> 0180 ( obj, loader, m_renderPluginTemplates ); 0181 isPlugin = isPlugin || appendPlugin<SearchRunnerPlugin> 0182 ( obj, loader, m_searchRunnerPlugins ); 0183 isPlugin = isPlugin || appendPlugin<ReverseGeocodingRunnerPlugin> 0184 ( obj, loader, m_reverseGeocodingRunnerPlugins ); 0185 isPlugin = isPlugin || appendPlugin<ParseRunnerPlugin> 0186 ( obj, loader, m_parsingRunnerPlugins ); 0187 if ( !isPlugin ) { 0188 qCWarning(DIGIKAM_MARBLE_LOG) << "Ignoring the following plugin since it couldn't be loaded:" << (loader ? loader->fileName() : QString::fromUtf8( "<static>")); 0189 qCDebug(DIGIKAM_MARBLE_LOG) << "Plugin failure:" << (loader ? loader->fileName() : QString::fromUtf8( "<static>")) << "is a plugin, but it does not implement the " 0190 << "right interfaces or it was compiled against an old version of Marble. Ignoring it."; 0191 } 0192 return isPlugin; 0193 } 0194 0195 void PluginManagerPrivate::loadPlugins() 0196 { 0197 if (m_pluginsLoaded) 0198 { 0199 return; 0200 } 0201 0202 QElapsedTimer t; 0203 t.start(); 0204 qCDebug(DIGIKAM_MARBLE_LOG) << "Starting to load Plugins."; 0205 0206 QStringList pluginFileNameList = MarbleDirs::pluginEntryList( QString::fromUtf8( "" ), QDir::Files ); 0207 0208 MarbleDirs::debug(); 0209 0210 Q_ASSERT( m_renderPluginTemplates.isEmpty() ); 0211 Q_ASSERT( m_searchRunnerPlugins.isEmpty() ); 0212 Q_ASSERT( m_reverseGeocodingRunnerPlugins.isEmpty() ); 0213 Q_ASSERT( m_parsingRunnerPlugins.isEmpty() ); 0214 0215 bool foundPlugin = false; 0216 for( const QString &fileName: pluginFileNameList ) { 0217 QString const baseName = QFileInfo(fileName).baseName(); 0218 0219 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0220 0221 QString const libBaseName = QLibraryInfo::path(QLibraryInfo::PluginsPath) + QString::fromUtf8("/marble/") + QFileInfo(fileName).baseName(); 0222 0223 #else 0224 0225 QString const libBaseName = QLibraryInfo::location(QLibraryInfo::PluginsPath) + QString::fromUtf8("/marble/") + QFileInfo(fileName).baseName(); 0226 0227 #endif 0228 0229 if (!m_whitelist.isEmpty() && !m_whitelist.contains(baseName) && !m_whitelist.contains(libBaseName)) { 0230 qCDebug(DIGIKAM_MARBLE_LOG) << "Ignoring non-whitelisted plugin " << fileName; 0231 continue; 0232 } 0233 if (m_blacklist.contains(baseName) || m_blacklist.contains(libBaseName)) { 0234 qCDebug(DIGIKAM_MARBLE_LOG) << "Ignoring blacklisted plugin " << fileName; 0235 continue; 0236 } 0237 0238 // qCDebug(DIGIKAM_MARBLE_LOG) << fileName << " - " << MarbleDirs::pluginPath( fileName ); 0239 QString const path = MarbleDirs::pluginPath( fileName ); 0240 #ifdef Q_OS_ANDROID 0241 QFileInfo targetFile( path ); 0242 if ( !m_pluginPaths.contains( targetFile.canonicalFilePath() ) ) { 0243 // @todo Delete the file here? 0244 qCDebug(DIGIKAM_MARBLE_LOG) << "Ignoring file " << path << " which is not among the currently installed plugins"; 0245 continue; 0246 } 0247 #endif 0248 QPluginLoader* loader = new QPluginLoader( path, m_parent ); 0249 0250 QObject * obj = loader->instance(); 0251 0252 if ( obj ) { 0253 bool isPlugin = addPlugin(obj, loader); 0254 if (!isPlugin) { 0255 delete loader; 0256 } else { 0257 foundPlugin = true; 0258 } 0259 } else { 0260 qCWarning(DIGIKAM_MARBLE_LOG) << "Ignoring to load the following file since it doesn't look like a valid Marble plugin:" << path << Qt::endl 0261 << "Reason:" << loader->errorString(); 0262 delete loader; 0263 } 0264 } 0265 0266 const auto staticPlugins = QPluginLoader::staticInstances(); 0267 for (auto obj : staticPlugins) { 0268 if (addPlugin(obj, nullptr)) { 0269 foundPlugin = true; 0270 } 0271 } 0272 0273 if ( !foundPlugin ) { 0274 qCWarning(DIGIKAM_MARBLE_LOG) << "No plugins loaded. Please check if the plugins were installed in the correct path," 0275 << "or if any errors occurred while loading plugins."; 0276 } 0277 0278 m_pluginsLoaded = true; 0279 0280 qCDebug(DIGIKAM_MARBLE_LOG) << Q_FUNC_INFO << "Time elapsed:" << t.elapsed() << "ms"; 0281 } 0282 0283 #ifdef Q_OS_ANDROID 0284 void PluginManager::installPluginsFromAssets() const 0285 { 0286 d->m_pluginPaths.clear(); 0287 QStringList copyList = MarbleDirs::pluginEntryList(QString()); 0288 QDir pluginHome(MarbleDirs::localPath()); 0289 pluginHome.mkpath(MarbleDirs::pluginLocalPath()); 0290 pluginHome.setCurrent(MarbleDirs::pluginLocalPath()); 0291 0292 QStringList pluginNameFilter = QStringList() << "lib*.so"; 0293 QStringList const existingPlugins = QDir(MarbleDirs::pluginLocalPath()).entryList(pluginNameFilter, QDir::Files); 0294 for(const QString &existingPlugin: existingPlugins) { 0295 QFile::remove(existingPlugin); 0296 } 0297 0298 for (const QString & file: copyList) { 0299 QString const target = MarbleDirs::pluginLocalPath() + QLatin1Char('/') + file; 0300 if (QFileInfo(MarbleDirs::pluginSystemPath() + QLatin1Char('/') + file).isDir()) { 0301 pluginHome.mkpath(target); 0302 } 0303 else { 0304 QFile temporaryFile(MarbleDirs::pluginSystemPath() + QLatin1Char('/') + file); 0305 temporaryFile.copy(target); 0306 QFileInfo targetFile(target); 0307 d->m_pluginPaths << targetFile.canonicalFilePath(); 0308 } 0309 } 0310 } 0311 #endif 0312 0313 } 0314 0315 #include "moc_PluginManager.cpp"