1 /* This file is part of the KDE project
2    Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
3    Copyright (C) 2007 David Faure <faure@kde.org>
4    Copyright (C) 2015 Jarosław Staniek <staniek@kde.org>
5 
6    This library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public
8    License as published by the Free Software Foundation; either
9    version 2 of the License, or (at your option) any later version.
10 
11    This library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15 
16    You should have received a copy of the GNU Library General Public License
17    along with this library; see the file COPYING.LIB.  If not, write to
18    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20 */
21 
22 #include "KexiJsonTrader.h"
23 #include "utils.h"
24 
25 #include <QDebug>
26 #include <QList>
27 #include <QPluginLoader>
28 #include <QJsonObject>
29 #include <QJsonArray>
30 #include <QDirIterator>
31 #include <QDir>
32 #include <QCoreApplication>
33 
34 class Q_DECL_HIDDEN KexiJsonTrader::Private
35 {
36 public:
Private()37     Private() : pluginPathFound(false)
38     {
39     }
40     QString subDir;
41     bool pluginPathFound;
42     QStringList pluginPaths;
43 };
44 
45 // ---
46 
KexiJsonTrader(const QString & subDir)47 KexiJsonTrader::KexiJsonTrader(const QString& subDir)
48     : d(new Private)
49 {
50     Q_ASSERT(!subDir.isEmpty());
51     Q_ASSERT(!subDir.contains(' '));
52     d->subDir = subDir;
53 }
54 
~KexiJsonTrader()55 KexiJsonTrader::~KexiJsonTrader()
56 {
57     delete d;
58 }
59 
60 //! @return true if at least one service type from @a serviceTypeNames exists in @a foundServiceTypes
supportsAtLeastServiceType(const QStringList & foundServiceTypes,const QStringList & serviceTypeNames)61 static bool supportsAtLeastServiceType(const QStringList &foundServiceTypes,
62                                        const QStringList &serviceTypeNames)
63 {
64     foreach(const QString &serviceTypeName, serviceTypeNames) {
65         if (foundServiceTypes.contains(serviceTypeName)) {
66             return true;
67         }
68     }
69     return false;
70 }
71 
72 //static
metaDataObjectForPluginLoader(const QPluginLoader & pluginLoader)73 QJsonObject KexiJsonTrader::metaDataObjectForPluginLoader(const QPluginLoader &pluginLoader)
74 {
75     return pluginLoader.metaData().value(QLatin1String("MetaData")).toObject();
76 }
77 
78 //static
rootObjectForPluginLoader(const QPluginLoader & pluginLoader)79 QJsonObject KexiJsonTrader::rootObjectForPluginLoader(const QPluginLoader &pluginLoader)
80 {
81     QJsonObject json = metaDataObjectForPluginLoader(pluginLoader);
82     if (json.isEmpty()) {
83         return QJsonObject();
84     }
85     return json.value(QLatin1String("KPlugin")).toObject();
86 }
87 
88 //! Checks loader @a loader
checkLoader(QPluginLoader * loader,const QStringList & servicetypes,const QString & mimetype)89 static bool checkLoader(QPluginLoader *loader, const QStringList &servicetypes,
90                         const QString &mimetype)
91 {
92     const QJsonObject pluginData = KexiJsonTrader::rootObjectForPluginLoader(*loader);
93     if (pluginData.isEmpty()) {
94         //qDebug() << dirIter.filePath() << "has no json!";
95         return false;
96     }
97     const QJsonArray foundServiceTypesAray = pluginData.value(QLatin1String("ServiceTypes")).toArray();
98     if (foundServiceTypesAray.isEmpty()) {
99         qWarning() << "No ServiceTypes defined for plugin" << loader->fileName() << "-- skipping!";
100         return false;
101     }
102     QStringList foundServiceTypes = KexiUtils::convertTypesUsingMethod<QVariant, QString, &QVariant::toString>(foundServiceTypesAray.toVariantList());
103     if (!supportsAtLeastServiceType(foundServiceTypes, servicetypes)) {
104         return false;
105     }
106 
107     if (!mimetype.isEmpty()) {
108         QJsonObject json = KexiJsonTrader::metaDataObjectForPluginLoader(*loader);
109         QStringList mimeTypes = json.value(QLatin1String("X-KDE-ExtraNativeMimeTypes"))
110                 .toString().split(QLatin1Char(','));
111         mimeTypes += json.value(QLatin1String("MimeType")).toString().split(QLatin1Char(';'));
112         mimeTypes += json.value(QLatin1String("X-KDE-NativeMimeType")).toString();
113         if (! mimeTypes.contains(mimetype)) {
114             return false;
115         }
116     }
117     return true;
118 }
119 
findPlugins(const QString & path,const QStringList & servicetypes,const QString & mimetype)120 static QList<QPluginLoader *> findPlugins(const QString &path, const QStringList &servicetypes,
121                                           const QString &mimetype)
122 {
123     QList<QPluginLoader*> list;
124     QDirIterator dirIter(path,
125                          /* QDirIterator::Subdirectories -- Since 3.0.1: Don't look into subdirs
126                                                             because there may be 3.x dirs from
127                                                             future Kexi versions. We will look
128                                                             into subdirs since 3.1 again. */
129                          QDirIterator::FollowSymlinks);
130     while (dirIter.hasNext()) {
131         dirIter.next();
132         if (dirIter.fileInfo().isFile()) {
133             QPluginLoader *loader = new QPluginLoader(dirIter.filePath());
134             if (checkLoader(loader, servicetypes, mimetype)) {
135                 list.append(loader);
136             } else {
137                 delete loader;
138             }
139         }
140     }
141     return list;
142 }
143 
query(const QStringList & servicetypes,const QString & mimetype)144 QList<QPluginLoader *> KexiJsonTrader::query(const QStringList &servicetypes,
145                                              const QString &mimetype)
146 {
147     if (!d->pluginPathFound) {
148         QStringList searchDirs;
149         searchDirs += QCoreApplication::libraryPaths();
150         foreach(const QString &dir, searchDirs) {
151             //qDebug() << dir;
152             QString possiblePath = dir + QLatin1Char('/') + d->subDir;
153             if (QDir(possiblePath).exists()) {
154                 d->pluginPaths += possiblePath;
155             }
156         }
157         d->pluginPathFound = true;
158     }
159 
160     QList<QPluginLoader *> list;
161     foreach(const QString &path, d->pluginPaths) {
162         list += findPlugins(path, servicetypes, mimetype);
163     }
164     return list;
165 }
166 
query(const QString & servicetype,const QString & mimetype)167 QList<QPluginLoader *> KexiJsonTrader::query(const QString &servicetype, const QString &mimetype)
168 {
169     QStringList servicetypes;
170     servicetypes << servicetype;
171     return query(servicetypes, mimetype);
172 }
173