1 /*
2  * plugins-manager: Single-instance managing plugins
3  *
4  * Copyright 2012-2020 Stephan Haller <nomad@froevel.de>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program 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
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  * MA 02110-1301, USA.
20  *
21  *
22  */
23 
24 /**
25  * SECTION:plugins-manager
26  * @short_description: The plugin manager class
27  * @include: xfdashboard/plugins-manager.h
28  *
29  * #XfdashboardPluginsManager is a single instance object. It is managing all
30  * plugins by loading and enabling or disabling them.
31  *
32  * The plugin manager will look up each plugin at the following paths and order:
33  *
34  * - Paths specified in environment variable XFDASHBOARD_PLUGINS_PATH (colon-seperated list)
35  * - $XDG_DATA_HOME/xfdashboard/plugins
36  * - (install prefix)/lib/xfdashboard/plugins
37  */
38 
39 #ifdef HAVE_CONFIG_H
40 #include "config.h"
41 #endif
42 
43 #include <libxfdashboard/plugins-manager.h>
44 
45 #include <glib/gi18n-lib.h>
46 
47 #include <libxfdashboard/plugin.h>
48 #include <libxfdashboard/application.h>
49 #include <libxfdashboard/compat.h>
50 #include <libxfdashboard/debug.h>
51 
52 
53 /* Define this class in GObject system */
54 struct _XfdashboardPluginsManagerPrivate
55 {
56 	/* Instance related */
57 	gboolean				isInited;
58 	GList					*searchPaths;
59 	GList					*plugins;
60 
61 	XfconfChannel			*xfconfChannel;
62 
63 	XfdashboardApplication	*application;
64 	guint					applicationInitializedSignalID;
65 };
66 
67 G_DEFINE_TYPE_WITH_PRIVATE(XfdashboardPluginsManager,
68 							xfdashboard_plugins_manager,
69 							G_TYPE_OBJECT)
70 
71 /* IMPLEMENTATION: Private variables and methods */
72 #define ENABLED_PLUGINS_XFCONF_PROP			"/enabled-plugins"
73 
74 /* Single instance of plugin manager */
75 static XfdashboardPluginsManager*			_xfdashboard_plugins_manager=NULL;
76 
77 /* Add path to search path but avoid duplicates */
_xfdashboard_plugins_manager_add_search_path(XfdashboardPluginsManager * self,const gchar * inPath)78 static gboolean _xfdashboard_plugins_manager_add_search_path(XfdashboardPluginsManager *self,
79 																const gchar *inPath)
80 {
81 	XfdashboardPluginsManagerPrivate	*priv;
82 	gchar								*normalizedPath;
83 	GList								*iter;
84 	gchar								*iterPath;
85 
86 	g_return_val_if_fail(XFDASHBOARD_IS_PLUGINS_MANAGER(self), FALSE);
87 	g_return_val_if_fail(inPath && *inPath, FALSE);
88 
89 	priv=self->priv;
90 
91 	/* Normalize requested path to add to list of search paths that means
92 	 * that it should end with a directory seperator.
93 	 */
94 	if(!g_str_has_suffix(inPath, G_DIR_SEPARATOR_S))
95 	{
96 		normalizedPath=g_strjoin(NULL, inPath, G_DIR_SEPARATOR_S, NULL);
97 	}
98 		else normalizedPath=g_strdup(inPath);
99 
100 	/* Check if path is already in list of search paths */
101 	for(iter=priv->searchPaths; iter; iter=g_list_next(iter))
102 	{
103 		/* Get search path at iterator */
104 		iterPath=(gchar*)iter->data;
105 
106 		/* If the path at iterator matches the requested one that it is
107 		 * already in list of search path, so return here with fail result.
108 		 */
109 		if(g_strcmp0(iterPath, normalizedPath)==0)
110 		{
111 			XFDASHBOARD_DEBUG(self, PLUGINS,
112 								"Path '%s' was already added to search paths of plugin manager",
113 								normalizedPath);
114 
115 			/* Release allocated resources */
116 			if(normalizedPath) g_free(normalizedPath);
117 
118 			/* Return fail result */
119 			return(FALSE);
120 		}
121 	}
122 
123 	/* If we get here the requested path is not in list of search path and
124 	 * we can add it now.
125 	 */
126 	priv->searchPaths=g_list_append(priv->searchPaths, g_strdup(normalizedPath));
127 	XFDASHBOARD_DEBUG(self, PLUGINS,
128 						"Added path '%s' to search paths of plugin manager",
129 						normalizedPath);
130 
131 	/* Release allocated resources */
132 	if(normalizedPath) g_free(normalizedPath);
133 
134 	/* Return success result */
135 	return(TRUE);
136 }
137 
138 /* Find path to plugin */
_xfdashboard_plugins_manager_find_plugin_path(XfdashboardPluginsManager * self,const gchar * inPluginName)139 static gchar* _xfdashboard_plugins_manager_find_plugin_path(XfdashboardPluginsManager *self,
140 															const gchar *inPluginName)
141 {
142 	XfdashboardPluginsManagerPrivate	*priv;
143 	gchar								*path;
144 	GList								*iter;
145 	gchar								*iterPath;
146 
147 	g_return_val_if_fail(XFDASHBOARD_IS_PLUGINS_MANAGER(self), NULL);
148 	g_return_val_if_fail(inPluginName && *inPluginName, NULL);
149 
150 	priv=self->priv;
151 
152 	/* Iterate through list of search paths, lookup file name containing plugin name
153 	 * followed by suffix ".so" and return first path found.
154 	 */
155 	for(iter=priv->searchPaths; iter; iter=g_list_next(iter))
156 	{
157 		/* Get search path at iterator */
158 		iterPath=(gchar*)iter->data;
159 
160 		/* Create full path of search path and plugin name */
161 		path=g_strdup_printf("%s%s%s.%s", iterPath, G_DIR_SEPARATOR_S, inPluginName, G_MODULE_SUFFIX);
162 		if(!path) continue;
163 
164 		XFDASHBOARD_DEBUG(self, PLUGINS,
165 							"Trying path %s for plugin '%s'",
166 							path,
167 							inPluginName);
168 
169 		/* Check if file exists and return it if we does */
170 		if(g_file_test(path, G_FILE_TEST_IS_REGULAR))
171 		{
172 			XFDASHBOARD_DEBUG(self, PLUGINS,
173 								"Found path %s for plugin '%s'",
174 								path,
175 								inPluginName);
176 			return(path);
177 		}
178 
179 		/* Release allocated resources */
180 		if(path) g_free(path);
181 	}
182 
183 	/* If we get here we did not found any suitable file, so return NULL */
184 	XFDASHBOARD_DEBUG(self, PLUGINS,
185 						"Plugin '%s' not found in search paths",
186 						inPluginName);
187 	return(NULL);
188 }
189 
190 /* Find plugin with requested ID */
_xfdashboard_plugins_manager_find_plugin_by_id(XfdashboardPluginsManager * self,const gchar * inPluginID)191 static XfdashboardPlugin* _xfdashboard_plugins_manager_find_plugin_by_id(XfdashboardPluginsManager *self,
192 																			const gchar *inPluginID)
193 {
194 	XfdashboardPluginsManagerPrivate	*priv;
195 	XfdashboardPlugin					*plugin;
196 	const gchar							*pluginID;
197 	GList								*iter;
198 
199 	g_return_val_if_fail(XFDASHBOARD_IS_PLUGINS_MANAGER(self), NULL);
200 	g_return_val_if_fail(inPluginID && *inPluginID, NULL);
201 
202 	priv=self->priv;
203 
204 	/* Iterate through list of loaded plugins and return the plugin
205 	 * with requested ID.
206 	 */
207 	for(iter=priv->plugins; iter; iter=g_list_next(iter))
208 	{
209 		/* Get currently iterated plugin */
210 		plugin=XFDASHBOARD_PLUGIN(iter->data);
211 
212 		/* Get plugin ID */
213 		pluginID=xfdashboard_plugin_get_id(plugin);
214 
215 		/* Check if plugin has requested ID then return it */
216 		if(g_strcmp0(pluginID, inPluginID)==0) return(plugin);
217 	}
218 
219 	/* If we get here the plugin with requested ID was not in the list of
220 	 * loaded plugins, so return NULL.
221 	 */
222 	return(NULL);
223 }
224 
225 /* Checks if a plugin with requested ID exists */
_xfdashboard_plugins_manager_has_plugin_id(XfdashboardPluginsManager * self,const gchar * inPluginID)226 static gboolean _xfdashboard_plugins_manager_has_plugin_id(XfdashboardPluginsManager *self,
227 															const gchar *inPluginID)
228 {
229 	XfdashboardPlugin					*plugin;
230 
231 	g_return_val_if_fail(XFDASHBOARD_IS_PLUGINS_MANAGER(self), FALSE);
232 	g_return_val_if_fail(inPluginID && *inPluginID, FALSE);
233 
234 	/* Get plugin by requested ID. If NULL is returned the plugin does not exist */
235 	plugin=_xfdashboard_plugins_manager_find_plugin_by_id(self, inPluginID);
236 	if(!plugin) return(FALSE);
237 
238 	/* If we get here the plugin was found */
239 	return(TRUE);
240 }
241 
242 /* Try to load plugin */
_xfdashboard_plugins_manager_load_plugin(XfdashboardPluginsManager * self,const gchar * inPluginID,GError ** outError)243 static gboolean _xfdashboard_plugins_manager_load_plugin(XfdashboardPluginsManager *self,
244 															const gchar *inPluginID,
245 															GError **outError)
246 {
247 	XfdashboardPluginsManagerPrivate	*priv;
248 	gchar								*path;
249 	XfdashboardPlugin					*plugin;
250 	GError								*error;
251 
252 	g_return_val_if_fail(XFDASHBOARD_IS_PLUGINS_MANAGER(self), FALSE);
253 	g_return_val_if_fail(inPluginID && *inPluginID, FALSE);
254 	g_return_val_if_fail(outError==NULL || *outError==NULL, FALSE);
255 
256 	priv=self->priv;
257 	error=NULL;
258 
259 	/* Check if plugin with requested ID exists already in list of loaded plugins */
260 	if(_xfdashboard_plugins_manager_has_plugin_id(self, inPluginID))
261 	{
262 		XFDASHBOARD_DEBUG(self, PLUGINS,
263 							"Plugin ID '%s' already loaded.",
264 							inPluginID);
265 
266 		/* The plugin is already loaded so return success result */
267 		return(TRUE);
268 	}
269 
270 	/* Find path to plugin */
271 	path=_xfdashboard_plugins_manager_find_plugin_path(self, inPluginID);
272 	if(!path)
273 	{
274 		/* Set error */
275 		g_set_error(outError,
276 					XFDASHBOARD_PLUGIN_ERROR,
277 					XFDASHBOARD_PLUGIN_ERROR_ERROR,
278 					"Could not find module for plugin ID '%s'",
279 					inPluginID);
280 
281 		/* Return error */
282 		return(FALSE);
283 	}
284 
285 	/* Create and load plugin */
286 	plugin=xfdashboard_plugin_new(path, &error);
287 	if(!plugin)
288 	{
289 		/* Propagate error */
290 		g_propagate_error(outError, error);
291 
292 		/* Return error */
293 		return(FALSE);
294 	}
295 
296 	/* Enable plugin if early initialization is requested by plugin */
297 	if(xfdashboard_plugin_get_flags(plugin) & XFDASHBOARD_PLUGIN_FLAG_EARLY_INITIALIZATION)
298 	{
299 		XFDASHBOARD_DEBUG(self, PLUGINS,
300 							"Enabling plugin '%s' on load because early initialization was requested",
301 							inPluginID);
302 		xfdashboard_plugin_enable(plugin);
303 	}
304 
305 	/* Store enabled plugin in list of enabled plugins */
306 	priv->plugins=g_list_prepend(priv->plugins, plugin);
307 
308 	/* Plugin loaded so return success result */
309 	return(TRUE);
310 }
311 
312 /* Property for list of enabled plugins in xfconf has changed */
_xfdashboard_plugins_manager_on_enabled_plugins_changed(XfdashboardPluginsManager * self,const gchar * inProperty,const GValue * inValue,gpointer inUserData)313 static void _xfdashboard_plugins_manager_on_enabled_plugins_changed(XfdashboardPluginsManager *self,
314 																	const gchar *inProperty,
315 																	const GValue *inValue,
316 																	gpointer inUserData)
317 {
318 	XfdashboardPluginsManagerPrivate	*priv;
319 	gchar								**enabledPlugins;
320 
321 	g_return_if_fail(XFDASHBOARD_IS_PLUGINS_MANAGER(self));
322 	g_return_if_fail(XFCONF_IS_CHANNEL(inUserData));
323 
324 	priv=self->priv;
325 
326 	/* If plugin managed is not inited do not load or "unload" any plugin */
327 	if(!priv->isInited) return;
328 
329 	/* Get new list of enabled plugins */
330 	enabledPlugins=xfconf_channel_get_string_list(xfdashboard_application_get_xfconf_channel(NULL),
331 													ENABLED_PLUGINS_XFCONF_PROP);
332 
333 	/* Iterate through list of loaded plugin and check if it also in new list
334 	 * of enabled plugins. If it is not then disable this plugin.
335 	 */
336 	if(priv->plugins)
337 	{
338 		GList								*iter;
339 		XfdashboardPlugin					*plugin;
340 		const gchar							*pluginID;
341 
342 		iter=priv->plugins;
343 		while(iter)
344 		{
345 			GList							*nextIter;
346 			gchar							**listIter;
347 			gchar							*listIterPluginID;
348 			gboolean						found;
349 
350 			/* Get next item getting iterated before doing anything
351 			 * because the current item may get removed and the iterator
352 			 * would get invalid.
353 			 */
354 			nextIter=g_list_next(iter);
355 
356 			/* Get currently iterated plugin */
357 			plugin=XFDASHBOARD_PLUGIN(iter->data);
358 
359 			/* Get plugin ID */
360 			pluginID=xfdashboard_plugin_get_id(plugin);
361 
362 			/* Check if plugin ID is still in new list of enabled plugins */
363 			found=FALSE;
364 			for(listIter=enabledPlugins; !found && *listIter; listIter++)
365 			{
366 				/* Get plugin ID of new enabled plugins currently iterated */
367 				listIterPluginID=*listIter;
368 
369 				/* Check if plugin ID matches this iterated one. If so,
370 				 * set flag that plugin was found.
371 				 */
372 				if(g_strcmp0(pluginID, listIterPluginID)==0) found=TRUE;
373 			}
374 
375 			/* Check that found flag is set. If it is not then disable plugin */
376 			if(!found)
377 			{
378 				XFDASHBOARD_DEBUG(self, PLUGINS,
379 									"Disable plugin '%s'",
380 									pluginID);
381 
382 				/* Disable plugin */
383 				xfdashboard_plugin_disable(plugin);
384 			}
385 
386 			/* Move iterator to next item */
387 			iter=nextIter;
388 		}
389 	}
390 
391 	/* Iterate through new list of enabled plugin and check if it is already
392 	 * or still in the list of loaded plugins. If it not in this list then
393 	 * try to load this plugin. If it is then enable it again when it is in
394 	 * disable state.
395 	 */
396 	if(enabledPlugins)
397 	{
398 		gchar								**iter;
399 		gchar								*pluginID;
400 		GError								*error;
401 		XfdashboardPlugin					*plugin;
402 
403 		error=NULL;
404 
405 		/* Iterate through new list of enabled plugins and load new plugins */
406 		for(iter=enabledPlugins; *iter; iter++)
407 		{
408 			/* Get plugin ID to check */
409 			pluginID=*iter;
410 
411 			/* Check if a plugin with this ID exists already */
412 			plugin=_xfdashboard_plugins_manager_find_plugin_by_id(self, pluginID);
413 			if(!plugin)
414 			{
415 				/* The plugin does not exist so try to load it */
416 				if(!_xfdashboard_plugins_manager_load_plugin(self, pluginID, &error))
417 				{
418 					/* Show error message */
419 					g_warning("Could not load plugin '%s': %s",
420 								pluginID,
421 								error ? error->message : "Unknown error");
422 
423 					/* Release allocated resources */
424 					if(error)
425 					{
426 						g_error_free(error);
427 						error=NULL;
428 					}
429 				}
430 					else
431 					{
432 						XFDASHBOARD_DEBUG(self, PLUGINS,
433 											"Loaded plugin '%s'",
434 											pluginID);
435 					}
436 			}
437 				else
438 				{
439 					/* The plugin exists already so check if it is disabled and
440 					 * re-enable it.
441 					 */
442 					if(!xfdashboard_plugin_is_enabled(plugin))
443 					{
444 						XFDASHBOARD_DEBUG(self, PLUGINS,
445 											"Re-enable plugin '%s'",
446 											pluginID);
447 						xfdashboard_plugin_enable(plugin);
448 					}
449 				}
450 		}
451 	}
452 
453 	/* Release allocated resources */
454 	if(enabledPlugins) g_strfreev(enabledPlugins);
455 }
456 
457 /* Application was fully initialized so enabled all loaded plugin except the
458  * ones which are already enabled, e.g. plugins which requested early initialization.
459  */
_xfdashboard_plugins_manager_on_application_initialized(XfdashboardPluginsManager * self,gpointer inUserData)460 static void _xfdashboard_plugins_manager_on_application_initialized(XfdashboardPluginsManager *self,
461 																	gpointer inUserData)
462 {
463 	XfdashboardPluginsManagerPrivate	*priv;
464 	GList								*iter;
465 	XfdashboardPlugin					*plugin;
466 
467 	g_return_if_fail(XFDASHBOARD_IS_PLUGINS_MANAGER(self));
468 	g_return_if_fail(XFDASHBOARD_IS_APPLICATION(inUserData));
469 
470 	priv=self->priv;
471 
472 	/* Iterate through all loaded plugins and enable all plugins which are
473 	 * not enabled yet.
474 	 */
475 	XFDASHBOARD_DEBUG(self, PLUGINS, "Plugin manager will now enable all remaining plugins because application is fully initialized now");
476 	for(iter=priv->plugins; iter; iter=g_list_next(iter))
477 	{
478 		/* Get plugin */
479 		plugin=XFDASHBOARD_PLUGIN(iter->data);
480 
481 		/* If plugin is not enabled do it now */
482 		if(!xfdashboard_plugin_is_enabled(plugin))
483 		{
484 			/* Enable plugin */
485 			XFDASHBOARD_DEBUG(self, PLUGINS,
486 								"Enabling plugin '%s'",
487 								xfdashboard_plugin_get_id(plugin));
488 			xfdashboard_plugin_enable(plugin);
489 		}
490 	}
491 
492 	/* Disconnect signal handler as this signal is emitted only once */
493 	if(priv->application)
494 	{
495 		if(priv->applicationInitializedSignalID)
496 		{
497 			g_signal_handler_disconnect(priv->application, priv->applicationInitializedSignalID);
498 			priv->applicationInitializedSignalID=0;
499 		}
500 
501 		priv->application=NULL;
502 	}
503 }
504 
505 /* IMPLEMENTATION: GObject */
506 
507 /* Construct this object */
_xfdashboard_plugins_manager_constructor(GType inType,guint inNumberConstructParams,GObjectConstructParam * inConstructParams)508 static GObject* _xfdashboard_plugins_manager_constructor(GType inType,
509 															guint inNumberConstructParams,
510 															GObjectConstructParam *inConstructParams)
511 {
512 	GObject									*object;
513 
514 	if(!_xfdashboard_plugins_manager)
515 	{
516 		object=G_OBJECT_CLASS(xfdashboard_plugins_manager_parent_class)->constructor(inType, inNumberConstructParams, inConstructParams);
517 		_xfdashboard_plugins_manager=XFDASHBOARD_PLUGINS_MANAGER(object);
518 	}
519 		else
520 		{
521 			object=g_object_ref(G_OBJECT(_xfdashboard_plugins_manager));
522 		}
523 
524 	return(object);
525 }
526 
527 /* Dispose this object */
_xfdashboard_plugins_manager_dispose_remove_plugin(gpointer inData)528 static void _xfdashboard_plugins_manager_dispose_remove_plugin(gpointer inData)
529 {
530 	XfdashboardPlugin					*plugin;
531 
532 	g_return_if_fail(inData && XFDASHBOARD_IS_PLUGIN(inData));
533 
534 	plugin=XFDASHBOARD_PLUGIN(inData);
535 
536 	/* Disable plugin */
537 	xfdashboard_plugin_disable(plugin);
538 
539 	/* Unload plugin */
540 	g_type_module_unuse(G_TYPE_MODULE(plugin));
541 }
542 
_xfdashboard_plugins_manager_dispose(GObject * inObject)543 static void _xfdashboard_plugins_manager_dispose(GObject *inObject)
544 {
545 	XfdashboardPluginsManager			*self=XFDASHBOARD_PLUGINS_MANAGER(inObject);
546 	XfdashboardPluginsManagerPrivate	*priv=self->priv;
547 
548 	/* Release allocated resources */
549 	if(priv->application)
550 	{
551 		if(priv->applicationInitializedSignalID)
552 		{
553 			g_signal_handler_disconnect(priv->application, priv->applicationInitializedSignalID);
554 			priv->applicationInitializedSignalID=0;
555 		}
556 
557 		priv->application=NULL;
558 	}
559 
560 	if(priv->plugins)
561 	{
562 		g_list_free_full(priv->plugins, (GDestroyNotify)_xfdashboard_plugins_manager_dispose_remove_plugin);
563 		priv->plugins=NULL;
564 	}
565 
566 	if(priv->searchPaths)
567 	{
568 		g_list_free_full(priv->searchPaths, g_free);
569 		priv->searchPaths=NULL;
570 	}
571 
572 	/* Call parent's class dispose method */
573 	G_OBJECT_CLASS(xfdashboard_plugins_manager_parent_class)->dispose(inObject);
574 }
575 
576 /* Finalize this object */
_xfdashboard_plugins_manager_finalize(GObject * inObject)577 static void _xfdashboard_plugins_manager_finalize(GObject *inObject)
578 {
579 	/* Release allocated resources finally, e.g. unset singleton */
580 	if(G_LIKELY(G_OBJECT(_xfdashboard_plugins_manager)==inObject))
581 	{
582 		_xfdashboard_plugins_manager=NULL;
583 	}
584 
585 	/* Call parent's class dispose method */
586 	G_OBJECT_CLASS(xfdashboard_plugins_manager_parent_class)->finalize(inObject);
587 }
588 
589 /* Class initialization
590  * Override functions in parent classes and define properties
591  * and signals
592  */
xfdashboard_plugins_manager_class_init(XfdashboardPluginsManagerClass * klass)593 static void xfdashboard_plugins_manager_class_init(XfdashboardPluginsManagerClass *klass)
594 {
595 	GObjectClass		*gobjectClass=G_OBJECT_CLASS(klass);
596 
597 	/* Override functions */
598 	gobjectClass->constructor=_xfdashboard_plugins_manager_constructor;
599 	gobjectClass->dispose=_xfdashboard_plugins_manager_dispose;
600 	gobjectClass->finalize=_xfdashboard_plugins_manager_finalize;
601 }
602 
603 /* Object initialization
604  * Create private structure and set up default values
605  */
xfdashboard_plugins_manager_init(XfdashboardPluginsManager * self)606 static void xfdashboard_plugins_manager_init(XfdashboardPluginsManager *self)
607 {
608 	XfdashboardPluginsManagerPrivate		*priv;
609 
610 	priv=self->priv=xfdashboard_plugins_manager_get_instance_private(self);
611 
612 	/* Set default values */
613 	priv->isInited=FALSE;
614 	priv->searchPaths=NULL;
615 	priv->plugins=NULL;
616 	priv->xfconfChannel=xfdashboard_application_get_xfconf_channel(NULL);
617 	priv->application=xfdashboard_application_get_default();
618 
619 	/* Connect signal to get notified about changed of enabled-plugins
620 	 * property in Xfconf.
621 	 */
622 	g_signal_connect_swapped(priv->xfconfChannel,
623 								"property-changed::"ENABLED_PLUGINS_XFCONF_PROP,
624 								G_CALLBACK(_xfdashboard_plugins_manager_on_enabled_plugins_changed),
625 								self);
626 
627 	/* Connect signal to get notified when application is fully initialized
628 	 * to enable loaded plugins.
629 	 */
630 	priv->applicationInitializedSignalID=
631 		g_signal_connect_swapped(priv->application,
632 									"initialized",
633 									G_CALLBACK(_xfdashboard_plugins_manager_on_application_initialized),
634 									self);
635 }
636 
637 /* IMPLEMENTATION: Public API */
638 
639 /**
640  * xfdashboard_plugins_manager_get_default:
641  *
642  * Retrieves the singleton instance of #XfdashboardPluginsManager.
643  *
644  * Return value: (transfer full): The instance of #XfdashboardPluginsManager.
645  *   Use g_object_unref() when done.
646  */
xfdashboard_plugins_manager_get_default(void)647 XfdashboardPluginsManager* xfdashboard_plugins_manager_get_default(void)
648 {
649 	GObject									*singleton;
650 
651 	singleton=g_object_new(XFDASHBOARD_TYPE_PLUGINS_MANAGER, NULL);
652 	return(XFDASHBOARD_PLUGINS_MANAGER(singleton));
653 }
654 
655 /**
656  * xfdashboard_plugins_manager_setup:
657  * @self: A #XfdashboardPluginsManager
658  *
659  * Initializes the plugin manager at @self by loading all enabled plugins. This
660  * function can only be called once and is initialized by the application at
661  * start-up. So you usually do not have to call this function or it does anything
662  * as the plugin manager is already setup.
663  *
664  * The plugin manager will continue initializing successfully even if a plugin
665  * could not be loaded. In this case just a warning is printed.
666  *
667  * Return value: Returns %TRUE if plugin manager was initialized successfully
668  *   or was already initialized. Otherwise %FALSE will be returned.
669  */
xfdashboard_plugins_manager_setup(XfdashboardPluginsManager * self)670 gboolean xfdashboard_plugins_manager_setup(XfdashboardPluginsManager *self)
671 {
672 	XfdashboardPluginsManagerPrivate	*priv;
673 	gchar								*path;
674 	const gchar							*envPath;
675 	gchar								**enabledPlugins;
676 	gchar								**iter;
677 	GError								*error;
678 
679 	g_return_val_if_fail(XFDASHBOARD_IS_PLUGINS_MANAGER(self), FALSE);
680 
681 	priv=self->priv;
682 	error=NULL;
683 
684 	/* If plugin manager is already initialized then return immediately */
685 	if(priv->isInited) return(TRUE);
686 
687 	/* Add search paths. Some paths may fail because they already exist
688 	 * in list of search paths. So this should not fail this function.
689 	 */
690 	envPath=g_getenv("XFDASHBOARD_PLUGINS_PATH");
691 	if(envPath)
692 	{
693 		gchar						**paths;
694 
695 		iter=paths=g_strsplit(envPath, ":", -1);
696 		while(*iter)
697 		{
698 			_xfdashboard_plugins_manager_add_search_path(self, *iter);
699 			iter++;
700 		}
701 		g_strfreev(paths);
702 	}
703 
704 	path=g_build_filename(g_get_user_data_dir(), "xfdashboard", "plugins", NULL);
705 	_xfdashboard_plugins_manager_add_search_path(self, path);
706 	g_free(path);
707 
708 	path=g_build_filename(PACKAGE_LIBDIR, "xfdashboard", "plugins", NULL);
709 	_xfdashboard_plugins_manager_add_search_path(self, path);
710 	g_free(path);
711 
712 	/* Get list of enabled plugins and try to load them */
713 	enabledPlugins=xfconf_channel_get_string_list(xfdashboard_application_get_xfconf_channel(NULL),
714 													ENABLED_PLUGINS_XFCONF_PROP);
715 
716 	/* Try to load all enabled plugin and collect each error occurred. */
717 	for(iter=enabledPlugins; iter && *iter; iter++)
718 	{
719 		gchar							*pluginID;
720 
721 		/* Get plugin name */
722 		pluginID=*iter;
723 		XFDASHBOARD_DEBUG(self, PLUGINS,
724 							"Try to load plugin '%s'",
725 							pluginID);
726 
727 		/* Try to load plugin */
728 		if(!_xfdashboard_plugins_manager_load_plugin(self, pluginID, &error))
729 		{
730 			/* Show error message */
731 			g_warning("Could not load plugin '%s': %s",
732 						pluginID,
733 						error ? error->message : "Unknown error");
734 
735 			/* Release allocated resources */
736 			if(error)
737 			{
738 				g_error_free(error);
739 				error=NULL;
740 			}
741 		}
742 			else
743 			{
744 				XFDASHBOARD_DEBUG(self, PLUGINS,
745 									"Loaded plugin '%s'",
746 									pluginID);
747 			}
748 	}
749 
750 	/* If we get here then initialization was successful so set flag that
751 	 * this plugin manager is initialized and return with TRUE result.
752 	 */
753 	priv->isInited=TRUE;
754 
755 	/* Release allocated resources */
756 	if(enabledPlugins) g_strfreev(enabledPlugins);
757 
758 	return(TRUE);
759 }
760