Merged in qulogic/pidgin (pull request #607)
[pidgin-git.git] / libpurple / plugins.c
blobefb9aa01aa529dd5dc39e112e8dd2f9cfdcd3534
1 /*
2 * purple
4 * Purple is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
6 * source distribution.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
22 #include "internal.h"
24 #include "core.h"
25 #include "debug.h"
26 #include "enums.h"
27 #include "plugins.h"
29 typedef struct _PurplePluginInfoPrivate PurplePluginInfoPrivate;
31 /**************************************************************************
32 * Plugin info private data
33 **************************************************************************/
34 struct _PurplePluginInfoPrivate {
35 char *ui_requirement; /* ID of UI that is required to load the plugin */
36 char *error; /* Why a plugin is not loadable */
38 PurplePluginInfoFlags flags; /* Flags for the plugin */
40 /* Callback that returns a list of actions the plugin can perform */
41 PurplePluginActionsCb actions_cb;
43 /* Callback that returns extra information about a plugin */
44 PurplePluginExtraCb extra_cb;
46 /* Callback that returns a preferences frame for a plugin */
47 PurplePluginPrefFrameCb pref_frame_cb;
49 /* Callback that returns a preferences request handle for a plugin */
50 PurplePluginPrefRequestCb pref_request_cb;
52 /* TRUE if a plugin has been unloaded at least once. Auto-load
53 * plugins that have been unloaded once will not be auto-loaded again. */
54 gboolean unloaded;
57 enum
59 PROP_0,
60 PROP_UI_REQUIREMENT,
61 PROP_ACTIONS_CB,
62 PROP_EXTRA_CB,
63 PROP_PREF_FRAME_CB,
64 PROP_PREF_REQUEST_CB,
65 PROP_FLAGS,
66 PROP_LAST
69 G_DEFINE_TYPE_WITH_PRIVATE(PurplePluginInfo, purple_plugin_info,
70 GPLUGIN_TYPE_PLUGIN_INFO);
72 /**************************************************************************
73 * Globals
74 **************************************************************************/
75 static GList *loaded_plugins = NULL;
76 static GList *plugins_to_disable = NULL;
78 /**************************************************************************
79 * Plugin API
80 **************************************************************************/
81 static gboolean
82 plugin_loading_cb(GObject *manager, PurplePlugin *plugin, GError **error,
83 gpointer data)
85 PurplePluginInfo *info;
86 PurplePluginInfoPrivate *priv;
88 g_return_val_if_fail(PURPLE_IS_PLUGIN(plugin), FALSE);
90 info = purple_plugin_get_info(plugin);
91 if (!info)
92 return TRUE; /* a GPlugin internal plugin */
94 priv = purple_plugin_info_get_instance_private(info);
96 if (priv->error) {
97 purple_debug_error("plugins", "Failed to load plugin %s: %s",
98 gplugin_plugin_get_filename(plugin),
99 priv->error);
101 g_set_error(error, PURPLE_PLUGINS_DOMAIN, 0,
102 "Plugin is not loadable: %s", priv->error);
104 return FALSE;
107 return TRUE;
110 static void
111 plugin_loaded_cb(GObject *manager, PurplePlugin *plugin)
113 PurplePluginInfo *info;
115 g_return_if_fail(PURPLE_IS_PLUGIN(plugin));
117 info = purple_plugin_get_info(plugin);
118 if (!info)
119 return; /* a GPlugin internal plugin */
121 loaded_plugins = g_list_prepend(loaded_plugins, plugin);
123 purple_debug_info("plugins", "Loaded plugin %s\n",
124 gplugin_plugin_get_filename(plugin));
126 purple_signal_emit(purple_plugins_get_handle(), "plugin-load", plugin);
129 static gboolean
130 plugin_unloading_cb(GObject *manager, PurplePlugin *plugin, GError **error,
131 gpointer data)
133 PurplePluginInfo *info;
135 g_return_val_if_fail(PURPLE_IS_PLUGIN(plugin), FALSE);
137 info = purple_plugin_get_info(plugin);
138 if (info) {
139 purple_debug_info("plugins", "Unloading plugin %s\n",
140 gplugin_plugin_get_filename(plugin));
143 return TRUE;
146 static void
147 plugin_unloaded_cb(GObject *manager, PurplePlugin *plugin)
149 PurplePluginInfo *info;
150 PurplePluginInfoPrivate *priv;
152 g_return_if_fail(PURPLE_IS_PLUGIN(plugin));
154 info = purple_plugin_get_info(plugin);
155 if (!info)
156 return; /* a GPlugin internal plugin */
158 priv = purple_plugin_info_get_instance_private(info);
160 /* cancel any pending dialogs the plugin has */
161 purple_request_close_with_handle(plugin);
162 purple_notify_close_with_handle(plugin);
164 purple_signals_disconnect_by_handle(plugin);
165 purple_signals_unregister_by_instance(plugin);
167 priv->unloaded = TRUE;
169 loaded_plugins = g_list_remove(loaded_plugins, plugin);
170 plugins_to_disable = g_list_remove(plugins_to_disable, plugin);
172 purple_signal_emit(purple_plugins_get_handle(), "plugin-unload", plugin);
174 purple_prefs_disconnect_by_handle(plugin);
177 gboolean
178 purple_plugin_load(PurplePlugin *plugin, GError **error)
180 GError *err = NULL;
182 g_return_val_if_fail(plugin != NULL, FALSE);
184 if (purple_plugin_is_loaded(plugin))
185 return TRUE;
187 if (!gplugin_manager_load_plugin(plugin, &err)) {
188 purple_debug_error("plugins", "Failed to load plugin %s: %s",
189 gplugin_plugin_get_filename(plugin),
190 err ? err->message : "Unknown reason");
192 if (error)
193 *error = g_error_copy(err);
194 g_error_free(err);
196 return FALSE;
199 return TRUE;
202 gboolean
203 purple_plugin_unload(PurplePlugin *plugin, GError **error)
205 GError *err = NULL;
207 g_return_val_if_fail(plugin != NULL, FALSE);
209 if (!purple_plugin_is_loaded(plugin))
210 return TRUE;
212 if (!gplugin_manager_unload_plugin(plugin, &err)) {
213 purple_debug_error("plugins", "Failed to unload plugin %s: %s",
214 gplugin_plugin_get_filename(plugin),
215 err ? err->message : "Unknown reason");
217 if (error)
218 *error = g_error_copy(err);
219 g_error_free(err);
221 return FALSE;
224 return TRUE;
227 gboolean
228 purple_plugin_is_loaded(PurplePlugin *plugin)
230 g_return_val_if_fail(plugin != NULL, FALSE);
232 return (gplugin_plugin_get_state(plugin) == GPLUGIN_PLUGIN_STATE_LOADED);
235 PurplePluginInfo *
236 purple_plugin_get_info(PurplePlugin *plugin)
238 GPluginPluginInfo *info;
240 g_return_val_if_fail(plugin != NULL, NULL);
242 info = gplugin_plugin_get_info(plugin);
244 /* GPlugin refs the plugin info object before returning it. This workaround
245 * is to avoid managing the reference counts everywhere in our codebase
246 * where we use the plugin info. The plugin info instance is guaranteed to
247 * exist as long as the plugin exists. */
248 g_object_unref(info);
250 if (PURPLE_IS_PLUGIN_INFO(info))
251 return PURPLE_PLUGIN_INFO(info);
252 else
253 return NULL;
256 void
257 purple_plugin_disable(PurplePlugin *plugin)
259 g_return_if_fail(plugin != NULL);
261 if (!g_list_find(plugins_to_disable, plugin))
262 plugins_to_disable = g_list_prepend(plugins_to_disable, plugin);
265 gboolean
266 purple_plugin_is_internal(PurplePlugin *plugin)
268 PurplePluginInfo *info;
270 g_return_val_if_fail(plugin != NULL, FALSE);
272 info = purple_plugin_get_info(plugin);
273 if (!info)
274 return TRUE;
276 return (purple_plugin_info_get_flags(info) &
277 PURPLE_PLUGIN_INFO_FLAGS_INTERNAL);
280 GSList *
281 purple_plugin_get_dependent_plugins(PurplePlugin *plugin)
283 #warning TODO: Implement this when GPlugin can return dependent plugins.
284 return NULL;
287 /**************************************************************************
288 * GObject code for PurplePluginInfo
289 **************************************************************************/
290 /* GObject initialization function */
291 static void
292 purple_plugin_info_init(PurplePluginInfo *info)
296 /* Set method for GObject properties */
297 static void
298 purple_plugin_info_set_property(GObject *obj, guint param_id, const GValue *value,
299 GParamSpec *pspec)
301 PurplePluginInfo *info = PURPLE_PLUGIN_INFO(obj);
302 PurplePluginInfoPrivate *priv =
303 purple_plugin_info_get_instance_private(info);
305 switch (param_id) {
306 case PROP_UI_REQUIREMENT:
307 priv->ui_requirement = g_value_dup_string(value);
308 break;
309 case PROP_ACTIONS_CB:
310 priv->actions_cb = g_value_get_pointer(value);
311 break;
312 case PROP_EXTRA_CB:
313 priv->extra_cb = g_value_get_pointer(value);
314 break;
315 case PROP_PREF_FRAME_CB:
316 priv->pref_frame_cb = g_value_get_pointer(value);
317 break;
318 case PROP_PREF_REQUEST_CB:
319 priv->pref_request_cb = g_value_get_pointer(value);
320 break;
321 case PROP_FLAGS:
322 priv->flags = g_value_get_flags(value);
323 break;
324 default:
325 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
326 break;
330 /* Get method for GObject properties */
331 static void
332 purple_plugin_info_get_property(GObject *obj, guint param_id, GValue *value,
333 GParamSpec *pspec)
335 PurplePluginInfo *info = PURPLE_PLUGIN_INFO(obj);
337 switch (param_id) {
338 case PROP_ACTIONS_CB:
339 g_value_set_pointer(value,
340 purple_plugin_info_get_actions_cb(info));
341 break;
342 case PROP_EXTRA_CB:
343 g_value_set_pointer(value,
344 purple_plugin_info_get_extra_cb(info));
345 break;
346 case PROP_PREF_FRAME_CB:
347 g_value_set_pointer(value,
348 purple_plugin_info_get_pref_frame_cb(info));
349 break;
350 case PROP_PREF_REQUEST_CB:
351 g_value_set_pointer(value,
352 purple_plugin_info_get_pref_request_cb(info));
353 break;
354 case PROP_FLAGS:
355 g_value_set_flags(value, purple_plugin_info_get_flags(info));
356 break;
357 default:
358 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
359 break;
363 /* Called when done constructing */
364 static void
365 purple_plugin_info_constructed(GObject *object)
367 PurplePluginInfo *info = PURPLE_PLUGIN_INFO(object);
368 GPluginPluginInfo *ginfo = GPLUGIN_PLUGIN_INFO(info);
369 PurplePluginInfoPrivate *priv =
370 purple_plugin_info_get_instance_private(info);
371 const char *id = gplugin_plugin_info_get_id(ginfo);
372 guint32 version;
374 G_OBJECT_CLASS(purple_plugin_info_parent_class)->constructed(object);
376 if (id == NULL || *id == '\0')
377 priv->error = g_strdup(_("This plugin has not defined an ID."));
379 if (priv->ui_requirement && !purple_strequal(priv->ui_requirement, purple_core_get_ui()))
381 priv->error = g_strdup_printf(_("You are using %s, but this plugin requires %s."),
382 purple_core_get_ui(), priv->ui_requirement);
383 purple_debug_error("plugins", "%s is not loadable: The UI requirement is not met. (%s)\n",
384 id, priv->error);
387 version = gplugin_plugin_info_get_abi_version(ginfo);
388 if (PURPLE_PLUGIN_ABI_MAJOR_VERSION(version) != PURPLE_MAJOR_VERSION ||
389 PURPLE_PLUGIN_ABI_MINOR_VERSION(version) > PURPLE_MINOR_VERSION)
391 priv->error = g_strdup_printf(_("Your libpurple version is %d.%d.x (need %d.%d.x)"),
392 PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION,
393 PURPLE_PLUGIN_ABI_MAJOR_VERSION(version),
394 PURPLE_PLUGIN_ABI_MINOR_VERSION(version));
395 purple_debug_error("plugins", "%s is not loadable: libpurple version is %d.%d.x (need %d.%d.x)\n",
396 id, PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION,
397 PURPLE_PLUGIN_ABI_MAJOR_VERSION(version),
398 PURPLE_PLUGIN_ABI_MINOR_VERSION(version));
402 /* GObject finalize function */
403 static void
404 purple_plugin_info_finalize(GObject *object)
406 PurplePluginInfoPrivate *priv =
407 purple_plugin_info_get_instance_private(
408 PURPLE_PLUGIN_INFO(object));
410 g_free(priv->ui_requirement);
411 g_free(priv->error);
413 G_OBJECT_CLASS(purple_plugin_info_parent_class)->finalize(object);
416 /* Class initializer function */
417 static void purple_plugin_info_class_init(PurplePluginInfoClass *klass)
419 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
421 obj_class->constructed = purple_plugin_info_constructed;
422 obj_class->finalize = purple_plugin_info_finalize;
424 /* Setup properties */
425 obj_class->get_property = purple_plugin_info_get_property;
426 obj_class->set_property = purple_plugin_info_set_property;
428 g_object_class_install_property(obj_class, PROP_UI_REQUIREMENT,
429 g_param_spec_string("ui-requirement",
430 "UI Requirement",
431 "ID of UI that is required by this plugin", NULL,
432 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
434 g_object_class_install_property(obj_class, PROP_ACTIONS_CB,
435 g_param_spec_pointer("actions-cb",
436 "Plugin actions",
437 "Callback that returns list of plugin's actions",
438 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
439 G_PARAM_STATIC_STRINGS));
441 g_object_class_install_property(obj_class, PROP_EXTRA_CB,
442 g_param_spec_pointer("extra-cb",
443 "Extra info callback",
444 "Callback that returns extra info about the plugin",
445 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
446 G_PARAM_STATIC_STRINGS));
448 g_object_class_install_property(obj_class, PROP_PREF_FRAME_CB,
449 g_param_spec_pointer("pref-frame-cb",
450 "Preferences frame callback",
451 "The callback that returns the preferences frame",
452 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
453 G_PARAM_STATIC_STRINGS));
455 g_object_class_install_property(obj_class, PROP_PREF_REQUEST_CB,
456 g_param_spec_pointer("pref-request-cb",
457 "Preferences request callback",
458 "Callback that returns preferences request handle",
459 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
460 G_PARAM_STATIC_STRINGS));
462 g_object_class_install_property(obj_class, PROP_FLAGS,
463 g_param_spec_flags("flags",
464 "Plugin flags",
465 "The flags for the plugin",
466 PURPLE_TYPE_PLUGIN_INFO_FLAGS, 0,
467 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
468 G_PARAM_STATIC_STRINGS));
471 /**************************************************************************
472 * PluginInfo API
473 **************************************************************************/
474 PurplePluginInfo *
475 purple_plugin_info_new(const char *first_property, ...)
477 GObject *info;
478 va_list var_args;
480 /* at least ID is required */
481 if (!first_property)
482 return NULL;
484 va_start(var_args, first_property);
485 info = g_object_new_valist(PURPLE_TYPE_PLUGIN_INFO, first_property,
486 var_args);
487 va_end(var_args);
489 return PURPLE_PLUGIN_INFO(info);
492 PurplePluginActionsCb
493 purple_plugin_info_get_actions_cb(PurplePluginInfo *info)
495 PurplePluginInfoPrivate *priv = NULL;
497 g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
499 priv = purple_plugin_info_get_instance_private(info);
500 return priv->actions_cb;
503 PurplePluginExtraCb
504 purple_plugin_info_get_extra_cb(PurplePluginInfo *info)
506 PurplePluginInfoPrivate *priv = NULL;
508 g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
510 priv = purple_plugin_info_get_instance_private(info);
511 return priv->extra_cb;
514 PurplePluginPrefFrameCb
515 purple_plugin_info_get_pref_frame_cb(PurplePluginInfo *info)
517 PurplePluginInfoPrivate *priv = NULL;
519 g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
521 priv = purple_plugin_info_get_instance_private(info);
522 return priv->pref_frame_cb;
525 PurplePluginPrefRequestCb
526 purple_plugin_info_get_pref_request_cb(PurplePluginInfo *info)
528 PurplePluginInfoPrivate *priv = NULL;
530 g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
532 priv = purple_plugin_info_get_instance_private(info);
533 return priv->pref_request_cb;
536 PurplePluginInfoFlags
537 purple_plugin_info_get_flags(PurplePluginInfo *info)
539 PurplePluginInfoPrivate *priv = NULL;
541 g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), 0);
543 priv = purple_plugin_info_get_instance_private(info);
544 return priv->flags;
547 const gchar *
548 purple_plugin_info_get_error(PurplePluginInfo *info)
550 PurplePluginInfoPrivate *priv = NULL;
552 g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
554 priv = purple_plugin_info_get_instance_private(info);
555 return priv->error;
558 void
559 purple_plugin_info_set_ui_data(PurplePluginInfo *info, gpointer ui_data)
561 g_return_if_fail(PURPLE_IS_PLUGIN_INFO(info));
563 info->ui_data = ui_data;
566 gpointer
567 purple_plugin_info_get_ui_data(PurplePluginInfo *info)
569 g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
571 return info->ui_data;
574 /**************************************************************************
575 * PluginAction API
576 **************************************************************************/
577 PurplePluginAction *
578 purple_plugin_action_new(const char* label, PurplePluginActionCb callback)
580 PurplePluginAction *action;
582 g_return_val_if_fail(label != NULL && callback != NULL, NULL);
584 action = g_new0(PurplePluginAction, 1);
586 action->label = g_strdup(label);
587 action->callback = callback;
589 return action;
592 void
593 purple_plugin_action_free(PurplePluginAction *action)
595 g_return_if_fail(action != NULL);
597 g_free(action->label);
598 g_free(action);
601 static PurplePluginAction *
602 purple_plugin_action_copy(PurplePluginAction *action)
604 g_return_val_if_fail(action != NULL, NULL);
606 return purple_plugin_action_new(action->label, action->callback);
609 G_DEFINE_BOXED_TYPE(PurplePluginAction, purple_plugin_action,
610 purple_plugin_action_copy, purple_plugin_action_free)
612 /**************************************************************************
613 * Plugins API
614 **************************************************************************/
615 GList *
616 purple_plugins_find_all(void)
618 GList *ret = NULL, *ids, *l;
619 GSList *plugins, *ll;
621 ids = gplugin_manager_list_plugins();
623 for (l = ids; l; l = l->next) {
624 plugins = gplugin_manager_find_plugins(l->data);
626 for (ll = plugins; ll; ll = ll->next) {
627 PurplePlugin *plugin = PURPLE_PLUGIN(ll->data);
628 if (purple_plugin_get_info(plugin))
629 ret = g_list_append(ret, plugin);
632 gplugin_manager_free_plugin_list(plugins);
634 g_list_free(ids);
636 return ret;
639 GList *
640 purple_plugins_get_loaded(void)
642 return loaded_plugins;
645 void
646 purple_plugins_add_search_path(const gchar *path)
648 gplugin_manager_append_path(path);
651 void
652 purple_plugins_refresh(void)
654 GList *plugins, *l;
656 gplugin_manager_refresh();
658 plugins = purple_plugins_find_all();
659 for (l = plugins; l != NULL; l = l->next) {
660 PurplePlugin *plugin = PURPLE_PLUGIN(l->data);
661 PurplePluginInfo *info;
662 PurplePluginInfoPrivate *priv;
664 if (purple_plugin_is_loaded(plugin))
665 continue;
667 info = purple_plugin_get_info(plugin);
668 priv = purple_plugin_info_get_instance_private(info);
670 if (!priv->unloaded && purple_plugin_info_get_flags(info) &
671 PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD) {
672 purple_debug_info("plugins", "Auto-loading plugin %s\n",
673 gplugin_plugin_get_filename(plugin));
674 purple_plugin_load(plugin, NULL);
678 g_list_free(plugins);
681 PurplePlugin *
682 purple_plugins_find_plugin(const gchar *id)
684 PurplePlugin *plugin;
686 g_return_val_if_fail(id != NULL && *id != '\0', NULL);
688 plugin = gplugin_manager_find_plugin(id);
690 if (!plugin)
691 return NULL;
693 /* GPlugin refs the plugin object before returning it. This workaround is
694 * to avoid managing the reference counts everywhere in our codebase where
695 * we use plugin instances. A plugin object will exist till the plugins
696 * subsystem is uninitialized. */
697 g_object_unref(plugin);
699 return plugin;
702 PurplePlugin *
703 purple_plugins_find_by_filename(const char *filename)
705 GList *plugins, *l;
707 g_return_val_if_fail(filename != NULL && *filename != '\0', NULL);
709 plugins = purple_plugins_find_all();
711 for (l = plugins; l != NULL; l = l->next) {
712 PurplePlugin *plugin = PURPLE_PLUGIN(l->data);
714 if (purple_strequal(gplugin_plugin_get_filename(plugin),
715 filename)) {
716 g_list_free(plugins);
717 return plugin;
720 g_list_free(plugins);
722 return NULL;
725 void
726 purple_plugins_save_loaded(const char *key)
728 GList *pl;
729 GList *files = NULL;
731 g_return_if_fail(key != NULL && *key != '\0');
733 for (pl = purple_plugins_get_loaded(); pl != NULL; pl = pl->next) {
734 PurplePlugin *plugin = PURPLE_PLUGIN(pl->data);
735 PurplePluginInfo *info = purple_plugin_get_info(plugin);
736 if (!info)
737 continue;
739 if (purple_plugin_info_get_flags(info) &
740 PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD)
741 continue;
743 if (!g_list_find(plugins_to_disable, plugin))
744 files = g_list_append(
745 files,
746 (gchar *)gplugin_plugin_get_filename(plugin));
749 purple_prefs_set_path_list(key, files);
750 g_list_free(files);
753 void
754 purple_plugins_load_saved(const char *key)
756 GList *l, *files;
758 g_return_if_fail(key != NULL && *key != '\0');
760 files = purple_prefs_get_path_list(key);
762 for (l = files; l; l = l->next)
764 char *file;
765 PurplePlugin *plugin;
767 if (l->data == NULL)
768 continue;
770 file = l->data;
771 plugin = purple_plugins_find_by_filename(file);
773 if (plugin) {
774 purple_debug_info("plugins", "Loading saved plugin %s\n", file);
775 purple_plugin_load(plugin, NULL);
776 } else {
777 purple_debug_error("plugins", "Unable to find saved plugin %s\n", file);
780 g_free(l->data);
783 g_list_free(files);
786 /**************************************************************************
787 * Plugins Subsystem API
788 **************************************************************************/
789 void *
790 purple_plugins_get_handle(void)
792 static int handle;
794 return &handle;
797 void
798 purple_plugins_init(void)
800 void *handle = purple_plugins_get_handle();
801 const gchar *search_path;
803 purple_signal_register(handle, "plugin-load",
804 purple_marshal_VOID__POINTER,
805 G_TYPE_NONE, 1, PURPLE_TYPE_PLUGIN);
806 purple_signal_register(handle, "plugin-unload",
807 purple_marshal_VOID__POINTER,
808 G_TYPE_NONE, 1, PURPLE_TYPE_PLUGIN);
810 gplugin_init();
812 search_path = g_getenv("PURPLE_PLUGIN_PATH");
813 if (search_path) {
814 gchar **paths;
815 int i;
817 paths = g_strsplit(search_path, G_SEARCHPATH_SEPARATOR_S, 0);
818 for (i = 0; paths[i]; ++i) {
819 purple_plugins_add_search_path(paths[i]);
822 g_strfreev(paths);
825 gplugin_manager_add_default_paths();
827 if(!g_getenv("PURPLE_PLUGINS_SKIP")) {
828 purple_plugins_add_search_path(PURPLE_LIBDIR);
829 } else {
830 purple_debug_info("plugins", "PURPLE_PLUGINS_SKIP environment variable set, skipping normal plugin paths");
833 g_signal_connect(gplugin_manager_get_instance(), "loading-plugin",
834 G_CALLBACK(plugin_loading_cb), NULL);
835 g_signal_connect(gplugin_manager_get_instance(), "loaded-plugin",
836 G_CALLBACK(plugin_loaded_cb), NULL);
837 g_signal_connect(gplugin_manager_get_instance(), "unloading-plugin",
838 G_CALLBACK(plugin_unloading_cb), NULL);
839 g_signal_connect(gplugin_manager_get_instance(), "unloaded-plugin",
840 G_CALLBACK(plugin_unloaded_cb), NULL);
842 purple_plugins_refresh();
845 void
846 purple_plugins_uninit(void)
848 void *handle = purple_plugins_get_handle();
850 purple_debug_info("plugins", "Unloading all plugins\n");
851 while (loaded_plugins != NULL)
852 purple_plugin_unload(loaded_plugins->data, NULL);
854 purple_signals_disconnect_by_handle(handle);
855 purple_signals_unregister_by_instance(handle);
857 gplugin_uninit();