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