Convert XMPP console dialogs to popovers.
[pidgin-git.git] / libpurple / plugins.c
blob1536f130a8a7f0e0834067cb178592822d7b9edd
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 purple_plugin_get_filename(plugin), priv->error);
100 g_set_error(error, PURPLE_PLUGINS_DOMAIN, 0,
101 "Plugin is not loadable: %s", priv->error);
103 return FALSE;
106 return TRUE;
109 static void
110 plugin_loaded_cb(GObject *manager, PurplePlugin *plugin)
112 PurplePluginInfo *info;
114 g_return_if_fail(PURPLE_IS_PLUGIN(plugin));
116 info = purple_plugin_get_info(plugin);
117 if (!info)
118 return; /* a GPlugin internal plugin */
120 loaded_plugins = g_list_prepend(loaded_plugins, plugin);
122 purple_debug_info("plugins", "Loaded plugin %s\n",
123 purple_plugin_get_filename(plugin));
125 purple_signal_emit(purple_plugins_get_handle(), "plugin-load", plugin);
128 static gboolean
129 plugin_unloading_cb(GObject *manager, PurplePlugin *plugin, GError **error,
130 gpointer data)
132 PurplePluginInfo *info;
134 g_return_val_if_fail(PURPLE_IS_PLUGIN(plugin), FALSE);
136 info = purple_plugin_get_info(plugin);
137 if (info) {
138 purple_debug_info("plugins", "Unloading plugin %s\n",
139 purple_plugin_get_filename(plugin));
142 return TRUE;
145 static void
146 plugin_unloaded_cb(GObject *manager, PurplePlugin *plugin)
148 PurplePluginInfo *info;
149 PurplePluginInfoPrivate *priv;
151 g_return_if_fail(PURPLE_IS_PLUGIN(plugin));
153 info = purple_plugin_get_info(plugin);
154 if (!info)
155 return; /* a GPlugin internal plugin */
157 priv = purple_plugin_info_get_instance_private(info);
159 /* cancel any pending dialogs the plugin has */
160 purple_request_close_with_handle(plugin);
161 purple_notify_close_with_handle(plugin);
163 purple_signals_disconnect_by_handle(plugin);
164 purple_signals_unregister_by_instance(plugin);
166 priv->unloaded = TRUE;
168 loaded_plugins = g_list_remove(loaded_plugins, plugin);
169 plugins_to_disable = g_list_remove(plugins_to_disable, plugin);
171 purple_signal_emit(purple_plugins_get_handle(), "plugin-unload", plugin);
173 purple_prefs_disconnect_by_handle(plugin);
176 gboolean
177 purple_plugin_load(PurplePlugin *plugin, GError **error)
179 GError *err = NULL;
181 g_return_val_if_fail(plugin != NULL, FALSE);
183 if (purple_plugin_is_loaded(plugin))
184 return TRUE;
186 if (!gplugin_manager_load_plugin(plugin, &err)) {
187 purple_debug_error("plugins", "Failed to load plugin %s: %s",
188 purple_plugin_get_filename(plugin),
189 (err ? err->message : "Unknown reason"));
191 if (error)
192 *error = g_error_copy(err);
193 g_error_free(err);
195 return FALSE;
198 return TRUE;
201 gboolean
202 purple_plugin_unload(PurplePlugin *plugin, GError **error)
204 GError *err = NULL;
206 g_return_val_if_fail(plugin != NULL, FALSE);
208 if (!purple_plugin_is_loaded(plugin))
209 return TRUE;
211 if (!gplugin_manager_unload_plugin(plugin, &err)) {
212 purple_debug_error("plugins", "Failed to unload plugin %s: %s",
213 purple_plugin_get_filename(plugin),
214 (err ? err->message : "Unknown reason"));
216 if (error)
217 *error = g_error_copy(err);
218 g_error_free(err);
220 return FALSE;
223 return TRUE;
226 gboolean
227 purple_plugin_is_loaded(PurplePlugin *plugin)
229 g_return_val_if_fail(plugin != NULL, FALSE);
231 return (gplugin_plugin_get_state(plugin) == GPLUGIN_PLUGIN_STATE_LOADED);
234 const gchar *
235 purple_plugin_get_filename(PurplePlugin *plugin)
237 g_return_val_if_fail(plugin != NULL, NULL);
239 return gplugin_plugin_get_filename(plugin);
242 PurplePluginInfo *
243 purple_plugin_get_info(PurplePlugin *plugin)
245 GPluginPluginInfo *info;
247 g_return_val_if_fail(plugin != NULL, NULL);
249 info = gplugin_plugin_get_info(plugin);
251 /* GPlugin refs the plugin info object before returning it. This workaround
252 * is to avoid managing the reference counts everywhere in our codebase
253 * where we use the plugin info. The plugin info instance is guaranteed to
254 * exist as long as the plugin exists. */
255 g_object_unref(info);
257 if (PURPLE_IS_PLUGIN_INFO(info))
258 return PURPLE_PLUGIN_INFO(info);
259 else
260 return NULL;
263 void
264 purple_plugin_disable(PurplePlugin *plugin)
266 g_return_if_fail(plugin != NULL);
268 if (!g_list_find(plugins_to_disable, plugin))
269 plugins_to_disable = g_list_prepend(plugins_to_disable, plugin);
272 GType
273 purple_plugin_register_type(PurplePlugin *plugin, GType parent,
274 const gchar *name, const GTypeInfo *info,
275 GTypeFlags flags)
277 g_return_val_if_fail(G_IS_TYPE_MODULE(plugin), G_TYPE_INVALID);
279 return g_type_module_register_type(G_TYPE_MODULE(plugin),
280 parent, name, info, flags);
283 void
284 purple_plugin_add_interface(PurplePlugin *plugin, GType instance_type,
285 GType interface_type,
286 const GInterfaceInfo *interface_info)
288 g_return_if_fail(G_IS_TYPE_MODULE(plugin));
290 g_type_module_add_interface(G_TYPE_MODULE(plugin),
291 instance_type, interface_type,
292 interface_info);
295 gboolean
296 purple_plugin_is_internal(PurplePlugin *plugin)
298 PurplePluginInfo *info;
300 g_return_val_if_fail(plugin != NULL, FALSE);
302 info = purple_plugin_get_info(plugin);
303 if (!info)
304 return TRUE;
306 return (purple_plugin_info_get_flags(info) &
307 PURPLE_PLUGIN_INFO_FLAGS_INTERNAL);
310 GSList *
311 purple_plugin_get_dependent_plugins(PurplePlugin *plugin)
313 #warning TODO: Implement this when GPlugin can return dependent plugins.
314 return NULL;
317 /**************************************************************************
318 * GObject code for PurplePluginInfo
319 **************************************************************************/
320 /* GObject initialization function */
321 static void
322 purple_plugin_info_init(PurplePluginInfo *info)
326 /* Set method for GObject properties */
327 static void
328 purple_plugin_info_set_property(GObject *obj, guint param_id, const GValue *value,
329 GParamSpec *pspec)
331 PurplePluginInfo *info = PURPLE_PLUGIN_INFO(obj);
332 PurplePluginInfoPrivate *priv =
333 purple_plugin_info_get_instance_private(info);
335 switch (param_id) {
336 case PROP_UI_REQUIREMENT:
337 priv->ui_requirement = g_value_dup_string(value);
338 break;
339 case PROP_ACTIONS_CB:
340 priv->actions_cb = g_value_get_pointer(value);
341 break;
342 case PROP_EXTRA_CB:
343 priv->extra_cb = g_value_get_pointer(value);
344 break;
345 case PROP_PREF_FRAME_CB:
346 priv->pref_frame_cb = g_value_get_pointer(value);
347 break;
348 case PROP_PREF_REQUEST_CB:
349 priv->pref_request_cb = g_value_get_pointer(value);
350 break;
351 case PROP_FLAGS:
352 priv->flags = g_value_get_flags(value);
353 break;
354 default:
355 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
356 break;
360 /* Get method for GObject properties */
361 static void
362 purple_plugin_info_get_property(GObject *obj, guint param_id, GValue *value,
363 GParamSpec *pspec)
365 PurplePluginInfo *info = PURPLE_PLUGIN_INFO(obj);
367 switch (param_id) {
368 case PROP_ACTIONS_CB:
369 g_value_set_pointer(value,
370 purple_plugin_info_get_actions_cb(info));
371 break;
372 case PROP_EXTRA_CB:
373 g_value_set_pointer(value,
374 purple_plugin_info_get_extra_cb(info));
375 break;
376 case PROP_PREF_FRAME_CB:
377 g_value_set_pointer(value,
378 purple_plugin_info_get_pref_frame_cb(info));
379 break;
380 case PROP_PREF_REQUEST_CB:
381 g_value_set_pointer(value,
382 purple_plugin_info_get_pref_request_cb(info));
383 break;
384 case PROP_FLAGS:
385 g_value_set_flags(value, purple_plugin_info_get_flags(info));
386 break;
387 default:
388 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
389 break;
393 /* Called when done constructing */
394 static void
395 purple_plugin_info_constructed(GObject *object)
397 PurplePluginInfo *info = PURPLE_PLUGIN_INFO(object);
398 PurplePluginInfoPrivate *priv =
399 purple_plugin_info_get_instance_private(info);
400 const char *id = purple_plugin_info_get_id(info);
401 guint32 version;
403 G_OBJECT_CLASS(purple_plugin_info_parent_class)->constructed(object);
405 if (id == NULL || *id == '\0')
406 priv->error = g_strdup(_("This plugin has not defined an ID."));
408 if (priv->ui_requirement && !purple_strequal(priv->ui_requirement, purple_core_get_ui()))
410 priv->error = g_strdup_printf(_("You are using %s, but this plugin requires %s."),
411 purple_core_get_ui(), priv->ui_requirement);
412 purple_debug_error("plugins", "%s is not loadable: The UI requirement is not met. (%s)\n",
413 id, priv->error);
416 version = purple_plugin_info_get_abi_version(info);
417 if (PURPLE_PLUGIN_ABI_MAJOR_VERSION(version) != PURPLE_MAJOR_VERSION ||
418 PURPLE_PLUGIN_ABI_MINOR_VERSION(version) > PURPLE_MINOR_VERSION)
420 priv->error = g_strdup_printf(_("Your libpurple version is %d.%d.x (need %d.%d.x)"),
421 PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION,
422 PURPLE_PLUGIN_ABI_MAJOR_VERSION(version),
423 PURPLE_PLUGIN_ABI_MINOR_VERSION(version));
424 purple_debug_error("plugins", "%s is not loadable: libpurple version is %d.%d.x (need %d.%d.x)\n",
425 id, PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION,
426 PURPLE_PLUGIN_ABI_MAJOR_VERSION(version),
427 PURPLE_PLUGIN_ABI_MINOR_VERSION(version));
431 /* GObject finalize function */
432 static void
433 purple_plugin_info_finalize(GObject *object)
435 PurplePluginInfoPrivate *priv =
436 purple_plugin_info_get_instance_private(
437 PURPLE_PLUGIN_INFO(object));
439 g_free(priv->ui_requirement);
440 g_free(priv->error);
442 G_OBJECT_CLASS(purple_plugin_info_parent_class)->finalize(object);
445 /* Class initializer function */
446 static void purple_plugin_info_class_init(PurplePluginInfoClass *klass)
448 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
450 obj_class->constructed = purple_plugin_info_constructed;
451 obj_class->finalize = purple_plugin_info_finalize;
453 /* Setup properties */
454 obj_class->get_property = purple_plugin_info_get_property;
455 obj_class->set_property = purple_plugin_info_set_property;
457 g_object_class_install_property(obj_class, PROP_UI_REQUIREMENT,
458 g_param_spec_string("ui-requirement",
459 "UI Requirement",
460 "ID of UI that is required by this plugin", NULL,
461 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
463 g_object_class_install_property(obj_class, PROP_ACTIONS_CB,
464 g_param_spec_pointer("actions-cb",
465 "Plugin actions",
466 "Callback that returns list of plugin's actions",
467 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
468 G_PARAM_STATIC_STRINGS));
470 g_object_class_install_property(obj_class, PROP_EXTRA_CB,
471 g_param_spec_pointer("extra-cb",
472 "Extra info callback",
473 "Callback that returns extra info about the plugin",
474 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
475 G_PARAM_STATIC_STRINGS));
477 g_object_class_install_property(obj_class, PROP_PREF_FRAME_CB,
478 g_param_spec_pointer("pref-frame-cb",
479 "Preferences frame callback",
480 "The callback that returns the preferences frame",
481 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
482 G_PARAM_STATIC_STRINGS));
484 g_object_class_install_property(obj_class, PROP_PREF_REQUEST_CB,
485 g_param_spec_pointer("pref-request-cb",
486 "Preferences request callback",
487 "Callback that returns preferences request handle",
488 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
489 G_PARAM_STATIC_STRINGS));
491 g_object_class_install_property(obj_class, PROP_FLAGS,
492 g_param_spec_flags("flags",
493 "Plugin flags",
494 "The flags for the plugin",
495 PURPLE_TYPE_PLUGIN_INFO_FLAGS, 0,
496 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
497 G_PARAM_STATIC_STRINGS));
500 /**************************************************************************
501 * PluginInfo API
502 **************************************************************************/
503 PurplePluginInfo *
504 purple_plugin_info_new(const char *first_property, ...)
506 GObject *info;
507 va_list var_args;
509 /* at least ID is required */
510 if (!first_property)
511 return NULL;
513 va_start(var_args, first_property);
514 info = g_object_new_valist(PURPLE_TYPE_PLUGIN_INFO, first_property,
515 var_args);
516 va_end(var_args);
518 return PURPLE_PLUGIN_INFO(info);
521 const gchar *
522 purple_plugin_info_get_id(const PurplePluginInfo *info)
524 g_return_val_if_fail(info != NULL, NULL);
526 return gplugin_plugin_info_get_id(GPLUGIN_PLUGIN_INFO(info));
529 const gchar *
530 purple_plugin_info_get_name(const PurplePluginInfo *info)
532 g_return_val_if_fail(info != NULL, NULL);
534 return gplugin_plugin_info_get_name(GPLUGIN_PLUGIN_INFO(info));
537 const gchar *
538 purple_plugin_info_get_version(const PurplePluginInfo *info)
540 g_return_val_if_fail(info != NULL, NULL);
542 return gplugin_plugin_info_get_version(GPLUGIN_PLUGIN_INFO(info));
545 const gchar *
546 purple_plugin_info_get_category(const PurplePluginInfo *info)
548 g_return_val_if_fail(info != NULL, NULL);
550 return gplugin_plugin_info_get_category(GPLUGIN_PLUGIN_INFO(info));
553 const gchar *
554 purple_plugin_info_get_summary(const PurplePluginInfo *info)
556 g_return_val_if_fail(info != NULL, NULL);
558 return gplugin_plugin_info_get_summary(GPLUGIN_PLUGIN_INFO(info));
561 const gchar *
562 purple_plugin_info_get_description(const PurplePluginInfo *info)
564 g_return_val_if_fail(info != NULL, NULL);
566 return gplugin_plugin_info_get_description(GPLUGIN_PLUGIN_INFO(info));
569 const gchar * const *
570 purple_plugin_info_get_authors(const PurplePluginInfo *info)
572 g_return_val_if_fail(info != NULL, NULL);
574 return gplugin_plugin_info_get_authors(GPLUGIN_PLUGIN_INFO(info));
577 const gchar *
578 purple_plugin_info_get_website(const PurplePluginInfo *info)
580 g_return_val_if_fail(info != NULL, NULL);
582 return gplugin_plugin_info_get_website(GPLUGIN_PLUGIN_INFO(info));
585 const gchar *
586 purple_plugin_info_get_icon(const PurplePluginInfo *info)
588 g_return_val_if_fail(info != NULL, NULL);
590 return gplugin_plugin_info_get_icon(GPLUGIN_PLUGIN_INFO(info));
593 const gchar *
594 purple_plugin_info_get_license_id(const PurplePluginInfo *info)
596 g_return_val_if_fail(info != NULL, NULL);
598 return gplugin_plugin_info_get_license_id(GPLUGIN_PLUGIN_INFO(info));
601 const gchar *
602 purple_plugin_info_get_license_text(const PurplePluginInfo *info)
604 g_return_val_if_fail(info != NULL, NULL);
606 return gplugin_plugin_info_get_license_text(GPLUGIN_PLUGIN_INFO(info));
609 const gchar *
610 purple_plugin_info_get_license_url(const PurplePluginInfo *info)
612 g_return_val_if_fail(info != NULL, NULL);
614 return gplugin_plugin_info_get_license_url(GPLUGIN_PLUGIN_INFO(info));
617 const gchar * const *
618 purple_plugin_info_get_dependencies(const PurplePluginInfo *info)
620 g_return_val_if_fail(info != NULL, NULL);
622 return gplugin_plugin_info_get_dependencies(GPLUGIN_PLUGIN_INFO(info));
625 guint32
626 purple_plugin_info_get_abi_version(const PurplePluginInfo *info)
628 g_return_val_if_fail(info != NULL, 0);
630 return gplugin_plugin_info_get_abi_version(GPLUGIN_PLUGIN_INFO(info));
633 PurplePluginActionsCb
634 purple_plugin_info_get_actions_cb(PurplePluginInfo *info)
636 PurplePluginInfoPrivate *priv = NULL;
638 g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
640 priv = purple_plugin_info_get_instance_private(info);
641 return priv->actions_cb;
644 PurplePluginExtraCb
645 purple_plugin_info_get_extra_cb(PurplePluginInfo *info)
647 PurplePluginInfoPrivate *priv = NULL;
649 g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
651 priv = purple_plugin_info_get_instance_private(info);
652 return priv->extra_cb;
655 PurplePluginPrefFrameCb
656 purple_plugin_info_get_pref_frame_cb(PurplePluginInfo *info)
658 PurplePluginInfoPrivate *priv = NULL;
660 g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
662 priv = purple_plugin_info_get_instance_private(info);
663 return priv->pref_frame_cb;
666 PurplePluginPrefRequestCb
667 purple_plugin_info_get_pref_request_cb(PurplePluginInfo *info)
669 PurplePluginInfoPrivate *priv = NULL;
671 g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
673 priv = purple_plugin_info_get_instance_private(info);
674 return priv->pref_request_cb;
677 PurplePluginInfoFlags
678 purple_plugin_info_get_flags(PurplePluginInfo *info)
680 PurplePluginInfoPrivate *priv = NULL;
682 g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), 0);
684 priv = purple_plugin_info_get_instance_private(info);
685 return priv->flags;
688 const gchar *
689 purple_plugin_info_get_error(PurplePluginInfo *info)
691 PurplePluginInfoPrivate *priv = NULL;
693 g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
695 priv = purple_plugin_info_get_instance_private(info);
696 return priv->error;
699 void
700 purple_plugin_info_set_ui_data(PurplePluginInfo *info, gpointer ui_data)
702 g_return_if_fail(PURPLE_IS_PLUGIN_INFO(info));
704 info->ui_data = ui_data;
707 gpointer
708 purple_plugin_info_get_ui_data(const PurplePluginInfo *info)
710 g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
712 return info->ui_data;
715 /**************************************************************************
716 * PluginAction API
717 **************************************************************************/
718 PurplePluginAction *
719 purple_plugin_action_new(const char* label, PurplePluginActionCb callback)
721 PurplePluginAction *action;
723 g_return_val_if_fail(label != NULL && callback != NULL, NULL);
725 action = g_new0(PurplePluginAction, 1);
727 action->label = g_strdup(label);
728 action->callback = callback;
730 return action;
733 void
734 purple_plugin_action_free(PurplePluginAction *action)
736 g_return_if_fail(action != NULL);
738 g_free(action->label);
739 g_free(action);
742 static PurplePluginAction *
743 purple_plugin_action_copy(PurplePluginAction *action)
745 g_return_val_if_fail(action != NULL, NULL);
747 return purple_plugin_action_new(action->label, action->callback);
750 GType
751 purple_plugin_action_get_type(void)
753 static GType type = 0;
755 if (G_UNLIKELY(type == 0)) {
756 type = g_boxed_type_register_static("PurplePluginAction",
757 (GBoxedCopyFunc)purple_plugin_action_copy,
758 (GBoxedFreeFunc)purple_plugin_action_free);
761 return type;
764 /**************************************************************************
765 * Plugins API
766 **************************************************************************/
767 GList *
768 purple_plugins_find_all(void)
770 GList *ret = NULL, *ids, *l;
771 GSList *plugins, *ll;
773 ids = gplugin_manager_list_plugins();
775 for (l = ids; l; l = l->next) {
776 plugins = gplugin_manager_find_plugins(l->data);
778 for (ll = plugins; ll; ll = ll->next) {
779 PurplePlugin *plugin = PURPLE_PLUGIN(ll->data);
780 if (purple_plugin_get_info(plugin))
781 ret = g_list_append(ret, plugin);
784 gplugin_manager_free_plugin_list(plugins);
786 g_list_free(ids);
788 return ret;
791 GList *
792 purple_plugins_get_loaded(void)
794 return loaded_plugins;
797 void
798 purple_plugins_add_search_path(const gchar *path)
800 gplugin_manager_append_path(path);
803 void
804 purple_plugins_refresh(void)
806 GList *plugins, *l;
808 gplugin_manager_refresh();
810 plugins = purple_plugins_find_all();
811 for (l = plugins; l != NULL; l = l->next) {
812 PurplePlugin *plugin = PURPLE_PLUGIN(l->data);
813 PurplePluginInfo *info;
814 PurplePluginInfoPrivate *priv;
816 if (purple_plugin_is_loaded(plugin))
817 continue;
819 info = purple_plugin_get_info(plugin);
820 priv = purple_plugin_info_get_instance_private(info);
822 if (!priv->unloaded && purple_plugin_info_get_flags(info) &
823 PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD) {
824 purple_debug_info("plugins", "Auto-loading plugin %s\n",
825 purple_plugin_get_filename(plugin));
826 purple_plugin_load(plugin, NULL);
830 g_list_free(plugins);
833 PurplePlugin *
834 purple_plugins_find_plugin(const gchar *id)
836 PurplePlugin *plugin;
838 g_return_val_if_fail(id != NULL && *id != '\0', NULL);
840 plugin = gplugin_manager_find_plugin(id);
842 if (!plugin)
843 return NULL;
845 /* GPlugin refs the plugin object before returning it. This workaround is
846 * to avoid managing the reference counts everywhere in our codebase where
847 * we use plugin instances. A plugin object will exist till the plugins
848 * subsystem is uninitialized. */
849 g_object_unref(plugin);
851 return plugin;
854 PurplePlugin *
855 purple_plugins_find_by_filename(const char *filename)
857 GList *plugins, *l;
859 g_return_val_if_fail(filename != NULL && *filename != '\0', NULL);
861 plugins = purple_plugins_find_all();
863 for (l = plugins; l != NULL; l = l->next) {
864 PurplePlugin *plugin = PURPLE_PLUGIN(l->data);
866 if (purple_strequal(purple_plugin_get_filename(plugin), filename)) {
867 g_list_free(plugins);
868 return plugin;
871 g_list_free(plugins);
873 return NULL;
876 void
877 purple_plugins_save_loaded(const char *key)
879 GList *pl;
880 GList *files = NULL;
882 g_return_if_fail(key != NULL && *key != '\0');
884 for (pl = purple_plugins_get_loaded(); pl != NULL; pl = pl->next) {
885 PurplePlugin *plugin = PURPLE_PLUGIN(pl->data);
886 PurplePluginInfo *info = purple_plugin_get_info(plugin);
887 if (!info)
888 continue;
890 if (purple_plugin_info_get_flags(info) &
891 PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD)
892 continue;
894 if (!g_list_find(plugins_to_disable, plugin))
895 files = g_list_append(files, (gchar *)purple_plugin_get_filename(plugin));
898 purple_prefs_set_path_list(key, files);
899 g_list_free(files);
902 void
903 purple_plugins_load_saved(const char *key)
905 GList *l, *files;
907 g_return_if_fail(key != NULL && *key != '\0');
909 files = purple_prefs_get_path_list(key);
911 for (l = files; l; l = l->next)
913 char *file;
914 PurplePlugin *plugin;
916 if (l->data == NULL)
917 continue;
919 file = l->data;
920 plugin = purple_plugins_find_by_filename(file);
922 if (plugin) {
923 purple_debug_info("plugins", "Loading saved plugin %s\n", file);
924 purple_plugin_load(plugin, NULL);
925 } else {
926 purple_debug_error("plugins", "Unable to find saved plugin %s\n", file);
929 g_free(l->data);
932 g_list_free(files);
935 /**************************************************************************
936 * Plugins Subsystem API
937 **************************************************************************/
938 void *
939 purple_plugins_get_handle(void)
941 static int handle;
943 return &handle;
946 void
947 purple_plugins_init(void)
949 void *handle = purple_plugins_get_handle();
950 const gchar *search_path;
952 purple_signal_register(handle, "plugin-load",
953 purple_marshal_VOID__POINTER,
954 G_TYPE_NONE, 1, PURPLE_TYPE_PLUGIN);
955 purple_signal_register(handle, "plugin-unload",
956 purple_marshal_VOID__POINTER,
957 G_TYPE_NONE, 1, PURPLE_TYPE_PLUGIN);
959 gplugin_init();
961 search_path = g_getenv("PURPLE_PLUGIN_PATH");
962 if (search_path) {
963 gchar **paths;
964 int i;
966 paths = g_strsplit(search_path, G_SEARCHPATH_SEPARATOR_S, 0);
967 for (i = 0; paths[i]; ++i) {
968 purple_plugins_add_search_path(paths[i]);
971 g_strfreev(paths);
974 gplugin_manager_add_default_paths();
976 if(!g_getenv("PURPLE_PLUGINS_SKIP")) {
977 purple_plugins_add_search_path(PURPLE_LIBDIR);
978 } else {
979 purple_debug_info("plugins", "PURPLE_PLUGINS_SKIP environment variable set, skipping normal plugin paths");
982 g_signal_connect(gplugin_manager_get_instance(), "loading-plugin",
983 G_CALLBACK(plugin_loading_cb), NULL);
984 g_signal_connect(gplugin_manager_get_instance(), "loaded-plugin",
985 G_CALLBACK(plugin_loaded_cb), NULL);
986 g_signal_connect(gplugin_manager_get_instance(), "unloading-plugin",
987 G_CALLBACK(plugin_unloading_cb), NULL);
988 g_signal_connect(gplugin_manager_get_instance(), "unloaded-plugin",
989 G_CALLBACK(plugin_unloaded_cb), NULL);
991 purple_plugins_refresh();
994 void
995 purple_plugins_uninit(void)
997 void *handle = purple_plugins_get_handle();
999 purple_debug_info("plugins", "Unloading all plugins\n");
1000 while (loaded_plugins != NULL)
1001 purple_plugin_unload(loaded_plugins->data, NULL);
1003 purple_signals_disconnect_by_handle(handle);
1004 purple_signals_unregister_by_instance(handle);
1006 gplugin_uninit();