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