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"