1 /*
2 * Copyright (C) 2006 Justin Karneges
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * either version 2
8 of the License, or (at your option) any later version.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 * 02110-1301 USA
19 *
20 */
21
22 #include "irisnetglobal_p.h"
23
24 #include "irisnetplugin.h"
25
26 namespace XMPP {
27
28 // built-in providers
29 #ifdef Q_OS_WIN
30 extern IrisNetProvider *irisnet_createWinNetProvider();
31 #endif
32 #ifdef Q_OS_UNIX
33 extern IrisNetProvider *irisnet_createUnixNetProvider();
34 #endif
35 extern IrisNetProvider *irisnet_createJDnsProvider();
36 #ifdef APPLEDNS_STATIC
37 extern IrisNetProvider *irisnet_createAppleProvider();
38 #endif
39
40 //----------------------------------------------------------------------------
41 // internal
42 //----------------------------------------------------------------------------
43 class PluginInstance
44 {
45 private:
46 QPluginLoader *_loader;
47 QObject *_instance;
48 bool _ownInstance;
49
PluginInstance()50 PluginInstance()
51 {
52 }
53
54 public:
fromFile(const QString & fname)55 static PluginInstance *fromFile(const QString &fname)
56 {
57 QPluginLoader *loader = new QPluginLoader(fname);
58 if(!loader->load())
59 {
60 delete loader;
61 return 0;
62 }
63 QObject *obj = loader->instance();
64 if(!obj)
65 {
66 loader->unload();
67 delete loader;
68 return 0;
69 }
70 PluginInstance *i = new PluginInstance;
71 i->_loader = loader;
72 i->_instance = obj;
73 i->_ownInstance = true;
74 return i;
75 }
76
fromStatic(QObject * obj)77 static PluginInstance *fromStatic(QObject *obj)
78 {
79 PluginInstance *i = new PluginInstance;
80 i->_loader = 0;
81 i->_instance = obj;
82 i->_ownInstance = false;
83 return i;
84 }
85
fromInstance(QObject * obj)86 static PluginInstance *fromInstance(QObject *obj)
87 {
88 PluginInstance *i = new PluginInstance;
89 i->_loader = 0;
90 i->_instance = obj;
91 i->_ownInstance = true;
92 return i;
93 }
94
~PluginInstance()95 ~PluginInstance()
96 {
97 if(_ownInstance)
98 delete _instance;
99
100 if(_loader)
101 {
102 _loader->unload();
103 delete _loader;
104 }
105 }
106
claim()107 void claim()
108 {
109 if(_loader)
110 _loader->moveToThread(0);
111 if(_ownInstance)
112 _instance->moveToThread(0);
113 }
114
instance()115 QObject *instance()
116 {
117 return _instance;
118 }
119
sameType(const PluginInstance * other)120 bool sameType(const PluginInstance *other)
121 {
122 if(!_instance || !other->_instance)
123 return false;
124
125 if(qstrcmp(_instance->metaObject()->className(), other->_instance->metaObject()->className()) != 0)
126 return false;
127
128 return true;
129 }
130 };
131
132 class PluginManager
133 {
134 public:
135 bool builtin_done;
136 QStringList paths;
137 QList<PluginInstance*> plugins;
138 QList<IrisNetProvider*> providers;
139
PluginManager()140 PluginManager()
141 {
142 builtin_done = false;
143 }
144
~PluginManager()145 ~PluginManager()
146 {
147 unload();
148 }
149
tryAdd(PluginInstance * i,bool lowPriority=false)150 bool tryAdd(PluginInstance *i, bool lowPriority = false)
151 {
152 // is it the right kind of plugin?
153 IrisNetProvider *p = qobject_cast<IrisNetProvider*>(i->instance());
154 if(!p)
155 return false;
156
157 // make sure we don't have it already
158 for(int n = 0; n < plugins.count(); ++n)
159 {
160 if(i->sameType(plugins[n]))
161 return false;
162 }
163
164 i->claim();
165 plugins += i;
166 if(lowPriority)
167 providers.append(p);
168 else
169 providers.prepend(p);
170 return true;
171 }
172
addBuiltIn(IrisNetProvider * p)173 void addBuiltIn(IrisNetProvider *p)
174 {
175 PluginInstance *i = PluginInstance::fromInstance(p);
176 if(!tryAdd(i, true))
177 delete i;
178 }
179
scan()180 void scan()
181 {
182 if(!builtin_done)
183 {
184 #ifdef Q_OS_WIN
185 addBuiltIn(irisnet_createWinNetProvider());
186 #endif
187 #ifdef Q_OS_UNIX
188 addBuiltIn(irisnet_createUnixNetProvider());
189 #endif
190 addBuiltIn(irisnet_createJDnsProvider());
191 builtin_done = true;
192 }
193
194 QObjectList list = QPluginLoader::staticInstances();
195 for(int n = 0; n < list.count(); ++n)
196 {
197 PluginInstance *i = PluginInstance::fromStatic(list[n]);
198 if(!tryAdd(i))
199 delete i;
200 }
201 for(int n = 0; n < paths.count(); ++n)
202 {
203 QDir dir(paths[n]);
204 if(!dir.exists())
205 continue;
206
207 QStringList entries = dir.entryList();
208 for(int k = 0; k < entries.count(); ++k)
209 {
210 QFileInfo fi(dir.filePath(entries[k]));
211 if(!fi.exists())
212 continue;
213 QString fname = fi.filePath();
214 PluginInstance *i = PluginInstance::fromFile(fname);
215 if(!i)
216 continue;
217
218 if(!tryAdd(i))
219 delete i;
220 }
221 }
222 }
223
unload()224 void unload()
225 {
226 // unload in reverse order
227 QList<PluginInstance*> revlist;
228 for(int n = 0; n < plugins.count(); ++n)
229 revlist.prepend(plugins[n]);
230 qDeleteAll(revlist);
231
232 plugins.clear();
233 providers.clear();
234 }
235 };
236
237 class IrisNetGlobal
238 {
239 public:
240 QMutex m;
241 PluginManager pluginManager;
242 QList<IrisNetCleanUpFunction> cleanupList;
243 };
244
245 Q_GLOBAL_STATIC(QMutex, global_mutex)
246 static IrisNetGlobal *global = 0;
247
248 static void deinit();
249
init()250 static void init()
251 {
252 QMutexLocker locker(global_mutex());
253 if(global)
254 return;
255
256 global = new IrisNetGlobal;
257 qAddPostRoutine(deinit);
258 }
259
deinit()260 void deinit()
261 {
262 if(!global)
263 return;
264
265 while(!global->cleanupList.isEmpty())
266 (global->cleanupList.takeFirst())();
267
268 delete global;
269 global = 0;
270 }
271
272 //----------------------------------------------------------------------------
273 // Global
274 //----------------------------------------------------------------------------
irisNetSetPluginPaths(const QStringList & paths)275 void irisNetSetPluginPaths(const QStringList &paths)
276 {
277 init();
278
279 QMutexLocker locker(&global->m);
280 global->pluginManager.paths = paths;
281 }
282
irisNetCleanup()283 void irisNetCleanup()
284 {
285 deinit();
286 }
287
irisNetAddPostRoutine(IrisNetCleanUpFunction func)288 void irisNetAddPostRoutine(IrisNetCleanUpFunction func)
289 {
290 init();
291
292 QMutexLocker locker(&global->m);
293 global->cleanupList.prepend(func);
294 }
295
irisNetProviders()296 QList<IrisNetProvider*> irisNetProviders()
297 {
298 init();
299
300 QMutexLocker locker(&global->m);
301 global->pluginManager.scan();
302 return global->pluginManager.providers;
303 }
304
305 }
306