Adapt migration for files
[pidgin-git.git] / libpurple / plugins.c
blob152a9356305ca8cb3bce2557f58c1bcc7741ea22
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 "dbus-maybe.h"
27 #include "enums.h"
28 #include "plugins.h"
30 #define PURPLE_PLUGIN_INFO_GET_PRIVATE(obj) \
31 (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_PLUGIN_INFO, PurplePluginInfoPrivate))
33 typedef struct _PurplePluginInfoPrivate PurplePluginInfoPrivate;
35 /**************************************************************************
36 * Plugin info private data
37 **************************************************************************/
38 struct _PurplePluginInfoPrivate {
39 char *ui_requirement; /* ID of UI that is required to load the plugin */
40 char *error; /* Why a plugin is not loadable */
42 PurplePluginInfoFlags flags; /* Flags for the plugin */
44 /* Callback that returns a list of actions the plugin can perform */
45 PurplePluginActionsCb actions_cb;
47 /* Callback that returns extra information about a plugin */
48 PurplePluginExtraCb extra_cb;
50 /* Callback that returns a preferences frame for a plugin */
51 PurplePluginPrefFrameCb pref_frame_cb;
53 /* Callback that returns a preferences request handle for a plugin */
54 PurplePluginPrefRequestCb pref_request_cb;
56 /* TRUE if a plugin has been unloaded at least once. Auto-load
57 * plugins that have been unloaded once will not be auto-loaded again. */
58 gboolean unloaded;
61 enum
63 PROP_0,
64 PROP_UI_REQUIREMENT,
65 PROP_ACTIONS_CB,
66 PROP_EXTRA_CB,
67 PROP_PREF_FRAME_CB,
68 PROP_PREF_REQUEST_CB,
69 PROP_FLAGS,
70 PROP_LAST
73 static GObjectClass *parent_class;
75 /**************************************************************************
76 * Globals
77 **************************************************************************/
78 #ifdef PURPLE_PLUGINS
79 static GList *loaded_plugins = NULL;
80 static GList *plugins_to_disable = NULL;
81 #endif
83 /**************************************************************************
84 * Plugin API
85 **************************************************************************/
86 #ifdef PURPLE_PLUGINS
87 static gboolean
88 plugin_loading_cb(GObject *manager, PurplePlugin *plugin, GError **error,
89 gpointer data)
91 PurplePluginInfo *info;
92 PurplePluginInfoPrivate *priv;
94 g_return_val_if_fail(PURPLE_IS_PLUGIN(plugin), FALSE);
96 info = purple_plugin_get_info(plugin);
97 if (!info)
98 return TRUE; /* a GPlugin internal plugin */
100 priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info);
102 if (priv->error) {
103 purple_debug_error("plugins", "Failed to load plugin %s: %s",
104 purple_plugin_get_filename(plugin), priv->error);
106 g_set_error(error, PURPLE_PLUGINS_DOMAIN, 0,
107 "Plugin is not loadable: %s", priv->error);
109 return FALSE;
112 return TRUE;
115 static void
116 plugin_loaded_cb(GObject *manager, PurplePlugin *plugin)
118 PurplePluginInfo *info;
120 g_return_if_fail(PURPLE_IS_PLUGIN(plugin));
122 info = purple_plugin_get_info(plugin);
123 if (!info)
124 return; /* a GPlugin internal plugin */
126 loaded_plugins = g_list_prepend(loaded_plugins, plugin);
128 purple_debug_info("plugins", "Loaded plugin %s\n",
129 purple_plugin_get_filename(plugin));
131 purple_signal_emit(purple_plugins_get_handle(), "plugin-load", plugin);
134 static gboolean
135 plugin_unloading_cb(GObject *manager, PurplePlugin *plugin, GError **error,
136 gpointer data)
138 PurplePluginInfo *info;
140 g_return_val_if_fail(PURPLE_IS_PLUGIN(plugin), FALSE);
142 info = purple_plugin_get_info(plugin);
143 if (info) {
144 purple_debug_info("plugins", "Unloading plugin %s\n",
145 purple_plugin_get_filename(plugin));
148 return TRUE;
151 static void
152 plugin_unloaded_cb(GObject *manager, PurplePlugin *plugin)
154 PurplePluginInfo *info;
155 PurplePluginInfoPrivate *priv;
157 g_return_if_fail(PURPLE_IS_PLUGIN(plugin));
159 info = purple_plugin_get_info(plugin);
160 if (!info)
161 return; /* a GPlugin internal plugin */
163 priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info);
165 /* cancel any pending dialogs the plugin has */
166 purple_request_close_with_handle(plugin);
167 purple_notify_close_with_handle(plugin);
169 purple_signals_disconnect_by_handle(plugin);
170 purple_signals_unregister_by_instance(plugin);
172 priv->unloaded = TRUE;
174 loaded_plugins = g_list_remove(loaded_plugins, plugin);
175 plugins_to_disable = g_list_remove(plugins_to_disable, plugin);
177 purple_signal_emit(purple_plugins_get_handle(), "plugin-unload", plugin);
179 purple_prefs_disconnect_by_handle(plugin);
181 #endif /* PURPLE_PLUGINS */
183 gboolean
184 purple_plugin_load(PurplePlugin *plugin, GError **error)
186 #ifdef PURPLE_PLUGINS
187 GError *err = NULL;
189 g_return_val_if_fail(plugin != NULL, FALSE);
191 if (purple_plugin_is_loaded(plugin))
192 return TRUE;
194 if (!gplugin_manager_load_plugin(plugin, &err)) {
195 purple_debug_error("plugins", "Failed to load plugin %s: %s",
196 purple_plugin_get_filename(plugin),
197 (err ? err->message : "Unknown reason"));
199 if (error)
200 *error = g_error_copy(err);
201 g_error_free(err);
203 return FALSE;
206 return TRUE;
208 #else
209 g_set_error(error, PURPLE_PLUGINS_DOMAIN, 0, "Plugin support is disabled.");
210 return FALSE;
211 #endif /* PURPLE_PLUGINS */
214 gboolean
215 purple_plugin_unload(PurplePlugin *plugin, GError **error)
217 #ifdef PURPLE_PLUGINS
218 GError *err = NULL;
220 g_return_val_if_fail(plugin != NULL, FALSE);
222 if (!purple_plugin_is_loaded(plugin))
223 return TRUE;
225 if (!gplugin_manager_unload_plugin(plugin, &err)) {
226 purple_debug_error("plugins", "Failed to unload plugin %s: %s",
227 purple_plugin_get_filename(plugin),
228 (err ? err->message : "Unknown reason"));
230 if (error)
231 *error = g_error_copy(err);
232 g_error_free(err);
234 return FALSE;
237 return TRUE;
239 #else
240 g_set_error(error, PURPLE_PLUGINS_DOMAIN, 0, "Plugin support is disabled.");
241 return FALSE;
242 #endif /* PURPLE_PLUGINS */
245 gboolean
246 purple_plugin_is_loaded(const PurplePlugin *plugin)
248 #ifdef PURPLE_PLUGINS
249 g_return_val_if_fail(plugin != NULL, FALSE);
251 return (gplugin_plugin_get_state(plugin) == GPLUGIN_PLUGIN_STATE_LOADED);
253 #else
254 return FALSE;
255 #endif
258 const gchar *
259 purple_plugin_get_filename(const PurplePlugin *plugin)
261 #ifdef PURPLE_PLUGINS
262 g_return_val_if_fail(plugin != NULL, NULL);
264 return gplugin_plugin_get_filename(plugin);
266 #else
267 return NULL;
268 #endif
271 PurplePluginInfo *
272 purple_plugin_get_info(const PurplePlugin *plugin)
274 #ifdef PURPLE_PLUGINS
275 GPluginPluginInfo *info;
277 g_return_val_if_fail(plugin != NULL, NULL);
279 info = gplugin_plugin_get_info(plugin);
281 /* GPlugin refs the plugin info object before returning it. This workaround
282 * is to avoid managing the reference counts everywhere in our codebase
283 * where we use the plugin info. The plugin info instance is guaranteed to
284 * exist as long as the plugin exists. */
285 g_object_unref(info);
287 if (PURPLE_IS_PLUGIN_INFO(info))
288 return PURPLE_PLUGIN_INFO(info);
289 else
290 return NULL;
291 #else
292 return NULL;
293 #endif
296 void
297 purple_plugin_disable(PurplePlugin *plugin)
299 #ifdef PURPLE_PLUGINS
300 g_return_if_fail(plugin != NULL);
302 if (!g_list_find(plugins_to_disable, plugin))
303 plugins_to_disable = g_list_prepend(plugins_to_disable, plugin);
304 #endif
307 GType
308 purple_plugin_register_type(PurplePlugin *plugin, GType parent,
309 const gchar *name, const GTypeInfo *info,
310 GTypeFlags flags)
312 #ifdef PURPLE_PLUGINS
313 g_return_val_if_fail(GPLUGIN_IS_NATIVE_PLUGIN(plugin), G_TYPE_INVALID);
315 return gplugin_native_plugin_register_type(GPLUGIN_NATIVE_PLUGIN(plugin),
316 parent, name, info, flags);
318 #else
319 return G_TYPE_INVALID;
320 #endif
323 void
324 purple_plugin_add_interface(PurplePlugin *plugin, GType instance_type,
325 GType interface_type,
326 const GInterfaceInfo *interface_info)
328 #ifdef PURPLE_PLUGINS
329 g_return_if_fail(GPLUGIN_IS_NATIVE_PLUGIN(plugin));
331 gplugin_native_plugin_add_interface(GPLUGIN_NATIVE_PLUGIN(plugin),
332 instance_type, interface_type,
333 interface_info);
334 #endif
337 gboolean
338 purple_plugin_is_internal(const PurplePlugin *plugin)
340 PurplePluginInfo *info;
342 g_return_val_if_fail(plugin != NULL, FALSE);
344 info = purple_plugin_get_info(plugin);
345 if (!info)
346 return TRUE;
348 return (purple_plugin_info_get_flags(info) &
349 PURPLE_PLUGIN_INFO_FLAGS_INTERNAL);
352 GSList *
353 purple_plugin_get_dependent_plugins(const PurplePlugin *plugin)
355 #warning TODO: Implement this when GPlugin can return dependent plugins.
357 #ifdef PURPLE_PLUGINS
358 return NULL;
359 #else
360 return NULL;
361 #endif
364 /**************************************************************************
365 * GObject code for PurplePluginInfo
366 **************************************************************************/
367 /* GObject initialization function */
368 static void
369 purple_plugin_info_init(GTypeInstance *instance, gpointer klass)
371 PURPLE_DBUS_REGISTER_POINTER(PURPLE_PLUGIN_INFO(instance), PurplePluginInfo);
374 /* Set method for GObject properties */
375 static void
376 purple_plugin_info_set_property(GObject *obj, guint param_id, const GValue *value,
377 GParamSpec *pspec)
379 PurplePluginInfo *info = PURPLE_PLUGIN_INFO(obj);
380 PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info);
382 switch (param_id) {
383 case PROP_UI_REQUIREMENT:
384 priv->ui_requirement = g_strdup(g_value_get_string(value));
385 break;
386 case PROP_ACTIONS_CB:
387 priv->actions_cb = g_value_get_pointer(value);
388 break;
389 case PROP_EXTRA_CB:
390 priv->extra_cb = g_value_get_pointer(value);
391 break;
392 case PROP_PREF_FRAME_CB:
393 priv->pref_frame_cb = g_value_get_pointer(value);
394 break;
395 case PROP_PREF_REQUEST_CB:
396 priv->pref_request_cb = g_value_get_pointer(value);
397 break;
398 case PROP_FLAGS:
399 priv->flags = g_value_get_flags(value);
400 break;
401 default:
402 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
403 break;
407 /* Get method for GObject properties */
408 static void
409 purple_plugin_info_get_property(GObject *obj, guint param_id, GValue *value,
410 GParamSpec *pspec)
412 PurplePluginInfo *info = PURPLE_PLUGIN_INFO(obj);
414 switch (param_id) {
415 case PROP_ACTIONS_CB:
416 g_value_set_pointer(value,
417 purple_plugin_info_get_actions_cb(info));
418 break;
419 case PROP_EXTRA_CB:
420 g_value_set_pointer(value,
421 purple_plugin_info_get_extra_cb(info));
422 break;
423 case PROP_PREF_FRAME_CB:
424 g_value_set_pointer(value,
425 purple_plugin_info_get_pref_frame_cb(info));
426 break;
427 case PROP_PREF_REQUEST_CB:
428 g_value_set_pointer(value,
429 purple_plugin_info_get_pref_request_cb(info));
430 break;
431 case PROP_FLAGS:
432 g_value_set_flags(value, purple_plugin_info_get_flags(info));
433 break;
434 default:
435 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
436 break;
440 /* Called when done constructing */
441 static void
442 purple_plugin_info_constructed(GObject *object)
444 PurplePluginInfo *info = PURPLE_PLUGIN_INFO(object);
445 PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info);
446 const char *id = purple_plugin_info_get_id(info);
447 guint32 version;
449 parent_class->constructed(object);
451 if (id == NULL || *id == '\0')
452 priv->error = g_strdup(_("This plugin has not defined an ID."));
454 if (priv->ui_requirement && !purple_strequal(priv->ui_requirement, purple_core_get_ui()))
456 priv->error = g_strdup_printf(_("You are using %s, but this plugin requires %s."),
457 purple_core_get_ui(), priv->ui_requirement);
458 purple_debug_error("plugins", "%s is not loadable: The UI requirement is not met. (%s)\n",
459 id, priv->error);
462 version = purple_plugin_info_get_abi_version(info);
463 if (PURPLE_PLUGIN_ABI_MAJOR_VERSION(version) != PURPLE_MAJOR_VERSION ||
464 PURPLE_PLUGIN_ABI_MINOR_VERSION(version) > PURPLE_MINOR_VERSION)
466 priv->error = g_strdup_printf(_("Your libpurple version is %d.%d.x (need %d.%d.x)"),
467 PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION,
468 PURPLE_PLUGIN_ABI_MAJOR_VERSION(version),
469 PURPLE_PLUGIN_ABI_MINOR_VERSION(version));
470 purple_debug_error("plugins", "%s is not loadable: libpurple version is %d.%d.x (need %d.%d.x)\n",
471 id, PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION,
472 PURPLE_PLUGIN_ABI_MAJOR_VERSION(version),
473 PURPLE_PLUGIN_ABI_MINOR_VERSION(version));
477 /* GObject finalize function */
478 static void
479 purple_plugin_info_finalize(GObject *object)
481 PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(object);
483 g_free(priv->ui_requirement);
484 g_free(priv->error);
486 PURPLE_DBUS_UNREGISTER_POINTER(object);
488 parent_class->finalize(object);
491 /* Class initializer function */
492 static void purple_plugin_info_class_init(PurplePluginInfoClass *klass)
494 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
496 parent_class = g_type_class_peek_parent(klass);
498 g_type_class_add_private(klass, sizeof(PurplePluginInfoPrivate));
500 obj_class->constructed = purple_plugin_info_constructed;
501 obj_class->finalize = purple_plugin_info_finalize;
503 /* Setup properties */
504 obj_class->get_property = purple_plugin_info_get_property;
505 obj_class->set_property = purple_plugin_info_set_property;
507 g_object_class_install_property(obj_class, PROP_UI_REQUIREMENT,
508 g_param_spec_string("ui-requirement",
509 "UI Requirement",
510 "ID of UI that is required by this plugin", NULL,
511 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
513 g_object_class_install_property(obj_class, PROP_ACTIONS_CB,
514 g_param_spec_pointer("actions-cb",
515 "Plugin actions",
516 "Callback that returns list of plugin's actions",
517 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
518 G_PARAM_STATIC_STRINGS));
520 g_object_class_install_property(obj_class, PROP_EXTRA_CB,
521 g_param_spec_pointer("extra-cb",
522 "Extra info callback",
523 "Callback that returns extra info about the plugin",
524 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
525 G_PARAM_STATIC_STRINGS));
527 g_object_class_install_property(obj_class, PROP_PREF_FRAME_CB,
528 g_param_spec_pointer("pref-frame-cb",
529 "Preferences frame callback",
530 "The callback that returns the preferences frame",
531 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
532 G_PARAM_STATIC_STRINGS));
534 g_object_class_install_property(obj_class, PROP_PREF_REQUEST_CB,
535 g_param_spec_pointer("pref-request-cb",
536 "Preferences request callback",
537 "Callback that returns preferences request handle",
538 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
539 G_PARAM_STATIC_STRINGS));
541 g_object_class_install_property(obj_class, PROP_FLAGS,
542 g_param_spec_flags("flags",
543 "Plugin flags",
544 "The flags for the plugin",
545 PURPLE_TYPE_PLUGIN_INFO_FLAGS, 0,
546 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
547 G_PARAM_STATIC_STRINGS));
550 /**************************************************************************
551 * PluginInfo API
552 **************************************************************************/
553 GType
554 purple_plugin_info_get_type(void)
556 static GType type = 0;
558 if (G_UNLIKELY(type == 0)) {
559 static const GTypeInfo info = {
560 .class_size = sizeof(PurplePluginInfoClass),
561 .class_init = (GClassInitFunc)purple_plugin_info_class_init,
562 .instance_size = sizeof(PurplePluginInfo),
563 .instance_init = (GInstanceInitFunc)purple_plugin_info_init,
566 type = g_type_register_static(
567 #ifdef PURPLE_PLUGINS
568 GPLUGIN_TYPE_PLUGIN_INFO,
569 #else
570 G_TYPE_OBJECT,
571 #endif
572 "PurplePluginInfo", &info, 0);
575 return type;
578 PurplePluginInfo *
579 purple_plugin_info_new(const char *first_property, ...)
581 GObject *info;
582 va_list var_args;
584 /* at least ID is required */
585 if (!first_property)
586 return NULL;
588 va_start(var_args, first_property);
589 info = g_object_new_valist(PURPLE_TYPE_PLUGIN_INFO, first_property,
590 var_args);
591 va_end(var_args);
593 return PURPLE_PLUGIN_INFO(info);
596 const gchar *
597 purple_plugin_info_get_id(const PurplePluginInfo *info)
599 #ifdef PURPLE_PLUGINS
600 g_return_val_if_fail(info != NULL, NULL);
602 return gplugin_plugin_info_get_id(GPLUGIN_PLUGIN_INFO(info));
604 #else
605 return NULL;
606 #endif
609 const gchar *
610 purple_plugin_info_get_name(const PurplePluginInfo *info)
612 #ifdef PURPLE_PLUGINS
613 g_return_val_if_fail(info != NULL, NULL);
615 return gplugin_plugin_info_get_name(GPLUGIN_PLUGIN_INFO(info));
617 #else
618 return NULL;
619 #endif
622 const gchar *
623 purple_plugin_info_get_version(const PurplePluginInfo *info)
625 #ifdef PURPLE_PLUGINS
626 g_return_val_if_fail(info != NULL, NULL);
628 return gplugin_plugin_info_get_version(GPLUGIN_PLUGIN_INFO(info));
630 #else
631 return NULL;
632 #endif
635 const gchar *
636 purple_plugin_info_get_category(const PurplePluginInfo *info)
638 #ifdef PURPLE_PLUGINS
639 g_return_val_if_fail(info != NULL, NULL);
641 return gplugin_plugin_info_get_category(GPLUGIN_PLUGIN_INFO(info));
643 #else
644 return NULL;
645 #endif
648 const gchar *
649 purple_plugin_info_get_summary(const PurplePluginInfo *info)
651 #ifdef PURPLE_PLUGINS
652 g_return_val_if_fail(info != NULL, NULL);
654 return gplugin_plugin_info_get_summary(GPLUGIN_PLUGIN_INFO(info));
656 #else
657 return NULL;
658 #endif
661 const gchar *
662 purple_plugin_info_get_description(const PurplePluginInfo *info)
664 #ifdef PURPLE_PLUGINS
665 g_return_val_if_fail(info != NULL, NULL);
667 return gplugin_plugin_info_get_description(GPLUGIN_PLUGIN_INFO(info));
669 #else
670 return NULL;
671 #endif
674 const gchar * const *
675 purple_plugin_info_get_authors(const PurplePluginInfo *info)
677 #ifdef PURPLE_PLUGINS
678 g_return_val_if_fail(info != NULL, NULL);
680 return gplugin_plugin_info_get_authors(GPLUGIN_PLUGIN_INFO(info));
682 #else
683 return NULL;
684 #endif
687 const gchar *
688 purple_plugin_info_get_website(const PurplePluginInfo *info)
690 #ifdef PURPLE_PLUGINS
691 g_return_val_if_fail(info != NULL, NULL);
693 return gplugin_plugin_info_get_website(GPLUGIN_PLUGIN_INFO(info));
695 #else
696 return NULL;
697 #endif
700 const gchar *
701 purple_plugin_info_get_icon(const PurplePluginInfo *info)
703 #ifdef PURPLE_PLUGINS
704 g_return_val_if_fail(info != NULL, NULL);
706 return gplugin_plugin_info_get_icon(GPLUGIN_PLUGIN_INFO(info));
708 #else
709 return NULL;
710 #endif
713 const gchar *
714 purple_plugin_info_get_license_id(const PurplePluginInfo *info)
716 #ifdef PURPLE_PLUGINS
717 g_return_val_if_fail(info != NULL, NULL);
719 return gplugin_plugin_info_get_license_id(GPLUGIN_PLUGIN_INFO(info));
721 #else
722 return NULL;
723 #endif
726 const gchar *
727 purple_plugin_info_get_license_text(const PurplePluginInfo *info)
729 #ifdef PURPLE_PLUGINS
730 g_return_val_if_fail(info != NULL, NULL);
732 return gplugin_plugin_info_get_license_text(GPLUGIN_PLUGIN_INFO(info));
734 #else
735 return NULL;
736 #endif
739 const gchar *
740 purple_plugin_info_get_license_url(const PurplePluginInfo *info)
742 #ifdef PURPLE_PLUGINS
743 g_return_val_if_fail(info != NULL, NULL);
745 return gplugin_plugin_info_get_license_url(GPLUGIN_PLUGIN_INFO(info));
747 #else
748 return NULL;
749 #endif
752 const gchar * const *
753 purple_plugin_info_get_dependencies(const PurplePluginInfo *info)
755 #ifdef PURPLE_PLUGINS
756 g_return_val_if_fail(info != NULL, NULL);
758 return gplugin_plugin_info_get_dependencies(GPLUGIN_PLUGIN_INFO(info));
760 #else
761 return NULL;
762 #endif
765 guint32
766 purple_plugin_info_get_abi_version(const PurplePluginInfo *info)
768 #ifdef PURPLE_PLUGINS
769 g_return_val_if_fail(info != NULL, 0);
771 return gplugin_plugin_info_get_abi_version(GPLUGIN_PLUGIN_INFO(info));
773 #else
774 return 0;
775 #endif
778 PurplePluginActionsCb
779 purple_plugin_info_get_actions_cb(const PurplePluginInfo *info)
781 PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info);
783 g_return_val_if_fail(priv != NULL, NULL);
785 return priv->actions_cb;
788 PurplePluginExtraCb
789 purple_plugin_info_get_extra_cb(const PurplePluginInfo *info)
791 PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info);
793 g_return_val_if_fail(priv != NULL, NULL);
795 return priv->extra_cb;
798 PurplePluginPrefFrameCb
799 purple_plugin_info_get_pref_frame_cb(const PurplePluginInfo *info)
801 PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info);
803 g_return_val_if_fail(priv != NULL, NULL);
805 return priv->pref_frame_cb;
808 PurplePluginPrefRequestCb
809 purple_plugin_info_get_pref_request_cb(const PurplePluginInfo *info)
811 PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info);
813 g_return_val_if_fail(priv != NULL, NULL);
815 return priv->pref_request_cb;
818 PurplePluginInfoFlags
819 purple_plugin_info_get_flags(const PurplePluginInfo *info)
821 PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info);
823 g_return_val_if_fail(priv != NULL, 0);
825 return priv->flags;
828 const gchar *
829 purple_plugin_info_get_error(const PurplePluginInfo *info)
831 PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info);
833 g_return_val_if_fail(priv != NULL, NULL);
835 return priv->error;
838 void
839 purple_plugin_info_set_ui_data(PurplePluginInfo *info, gpointer ui_data)
841 g_return_if_fail(PURPLE_IS_PLUGIN_INFO(info));
843 info->ui_data = ui_data;
846 gpointer
847 purple_plugin_info_get_ui_data(const PurplePluginInfo *info)
849 g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
851 return info->ui_data;
854 /**************************************************************************
855 * PluginAction API
856 **************************************************************************/
857 PurplePluginAction *
858 purple_plugin_action_new(const char* label, PurplePluginActionCb callback)
860 PurplePluginAction *action;
862 g_return_val_if_fail(label != NULL && callback != NULL, NULL);
864 action = g_new0(PurplePluginAction, 1);
866 action->label = g_strdup(label);
867 action->callback = callback;
869 return action;
872 void
873 purple_plugin_action_free(PurplePluginAction *action)
875 g_return_if_fail(action != NULL);
877 g_free(action->label);
878 g_free(action);
881 static PurplePluginAction *
882 purple_plugin_action_copy(PurplePluginAction *action)
884 g_return_val_if_fail(action != NULL, NULL);
886 return purple_plugin_action_new(action->label, action->callback);
889 GType
890 purple_plugin_action_get_type(void)
892 static GType type = 0;
894 if (G_UNLIKELY(type == 0)) {
895 type = g_boxed_type_register_static("PurplePluginAction",
896 (GBoxedCopyFunc)purple_plugin_action_copy,
897 (GBoxedFreeFunc)purple_plugin_action_free);
900 return type;
903 /**************************************************************************
904 * Plugins API
905 **************************************************************************/
906 GList *
907 purple_plugins_find_all(void)
909 #ifdef PURPLE_PLUGINS
910 GList *ret = NULL, *ids, *l;
911 GSList *plugins, *ll;
913 ids = gplugin_manager_list_plugins();
915 for (l = ids; l; l = l->next) {
916 plugins = gplugin_manager_find_plugins(l->data);
918 for (ll = plugins; ll; ll = ll->next) {
919 PurplePlugin *plugin = PURPLE_PLUGIN(ll->data);
920 if (purple_plugin_get_info(plugin))
921 ret = g_list_append(ret, plugin);
924 gplugin_manager_free_plugin_list(plugins);
926 g_list_free(ids);
928 return ret;
930 #else
931 return NULL;
932 #endif
935 GList *
936 purple_plugins_get_loaded(void)
938 #ifdef PURPLE_PLUGINS
939 return loaded_plugins;
940 #else
941 return NULL;
942 #endif
945 void
946 purple_plugins_add_search_path(const gchar *path)
948 #ifdef PURPLE_PLUGINS
949 gplugin_manager_append_path(path);
950 #endif
953 void
954 purple_plugins_refresh(void)
956 #ifdef PURPLE_PLUGINS
957 GList *plugins, *l;
959 gplugin_manager_refresh();
961 plugins = purple_plugins_find_all();
962 for (l = plugins; l != NULL; l = l->next) {
963 PurplePlugin *plugin = PURPLE_PLUGIN(l->data);
964 PurplePluginInfo *info;
965 PurplePluginInfoPrivate *priv;
967 if (purple_plugin_is_loaded(plugin))
968 continue;
970 info = purple_plugin_get_info(plugin);
971 priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info);
973 if (!priv->unloaded && purple_plugin_info_get_flags(info) &
974 PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD) {
975 purple_debug_info("plugins", "Auto-loading plugin %s\n",
976 purple_plugin_get_filename(plugin));
977 purple_plugin_load(plugin, NULL);
981 g_list_free(plugins);
982 #endif
985 PurplePlugin *
986 purple_plugins_find_plugin(const gchar *id)
988 #ifdef PURPLE_PLUGINS
989 PurplePlugin *plugin;
991 g_return_val_if_fail(id != NULL && *id != '\0', NULL);
993 plugin = gplugin_manager_find_plugin(id);
995 if (!plugin)
996 return NULL;
998 /* GPlugin refs the plugin object before returning it. This workaround is
999 * to avoid managing the reference counts everywhere in our codebase where
1000 * we use plugin instances. A plugin object will exist till the plugins
1001 * subsystem is uninitialized. */
1002 g_object_unref(plugin);
1004 return plugin;
1006 #else
1007 return NULL;
1008 #endif
1011 PurplePlugin *
1012 purple_plugins_find_by_filename(const char *filename)
1014 GList *plugins, *l;
1016 g_return_val_if_fail(filename != NULL && *filename != '\0', NULL);
1018 plugins = purple_plugins_find_all();
1020 for (l = plugins; l != NULL; l = l->next) {
1021 PurplePlugin *plugin = PURPLE_PLUGIN(l->data);
1023 if (purple_strequal(purple_plugin_get_filename(plugin), filename)) {
1024 g_list_free(plugins);
1025 return plugin;
1028 g_list_free(plugins);
1030 return NULL;
1033 void
1034 purple_plugins_save_loaded(const char *key)
1036 #ifdef PURPLE_PLUGINS
1037 GList *pl;
1038 GList *files = NULL;
1040 g_return_if_fail(key != NULL && *key != '\0');
1042 for (pl = purple_plugins_get_loaded(); pl != NULL; pl = pl->next) {
1043 PurplePlugin *plugin = PURPLE_PLUGIN(pl->data);
1044 PurplePluginInfo *info = purple_plugin_get_info(plugin);
1045 if (!info)
1046 continue;
1048 if (purple_plugin_info_get_flags(info) &
1049 PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD)
1050 continue;
1052 if (!g_list_find(plugins_to_disable, plugin))
1053 files = g_list_append(files, (gchar *)purple_plugin_get_filename(plugin));
1056 purple_prefs_set_path_list(key, files);
1057 g_list_free(files);
1058 #endif
1061 void
1062 purple_plugins_load_saved(const char *key)
1064 #ifdef PURPLE_PLUGINS
1065 GList *l, *files;
1067 g_return_if_fail(key != NULL && *key != '\0');
1069 files = purple_prefs_get_path_list(key);
1071 for (l = files; l; l = l->next)
1073 char *file;
1074 PurplePlugin *plugin;
1076 if (l->data == NULL)
1077 continue;
1079 file = l->data;
1080 plugin = purple_plugins_find_by_filename(file);
1082 if (plugin) {
1083 purple_debug_info("plugins", "Loading saved plugin %s\n", file);
1084 purple_plugin_load(plugin, NULL);
1085 } else {
1086 purple_debug_error("plugins", "Unable to find saved plugin %s\n", file);
1089 g_free(l->data);
1092 g_list_free(files);
1093 #endif /* PURPLE_PLUGINS */
1096 /**************************************************************************
1097 * Plugins Subsystem API
1098 **************************************************************************/
1099 void *
1100 purple_plugins_get_handle(void)
1102 static int handle;
1104 return &handle;
1107 void
1108 purple_plugins_init(void)
1110 void *handle = purple_plugins_get_handle();
1111 #ifdef PURPLE_PLUGINS
1112 const gchar *search_path;
1113 #endif
1115 purple_signal_register(handle, "plugin-load",
1116 purple_marshal_VOID__POINTER,
1117 G_TYPE_NONE, 1, PURPLE_TYPE_PLUGIN);
1118 purple_signal_register(handle, "plugin-unload",
1119 purple_marshal_VOID__POINTER,
1120 G_TYPE_NONE, 1, PURPLE_TYPE_PLUGIN);
1122 #ifdef PURPLE_PLUGINS
1123 gplugin_init();
1125 search_path = g_getenv("PURPLE_PLUGIN_PATH");
1126 if (search_path) {
1127 gchar **paths;
1128 int i;
1130 paths = g_strsplit(search_path, G_SEARCHPATH_SEPARATOR_S, 0);
1131 for (i = 0; paths[i]; ++i) {
1132 purple_plugins_add_search_path(paths[i]);
1135 g_strfreev(paths);
1138 gplugin_manager_add_default_paths();
1140 purple_plugins_add_search_path(PURPLE_LIBDIR);
1142 g_signal_connect(gplugin_manager_get_instance(), "loading-plugin",
1143 G_CALLBACK(plugin_loading_cb), NULL);
1144 g_signal_connect(gplugin_manager_get_instance(), "loaded-plugin",
1145 G_CALLBACK(plugin_loaded_cb), NULL);
1146 g_signal_connect(gplugin_manager_get_instance(), "unloading-plugin",
1147 G_CALLBACK(plugin_unloading_cb), NULL);
1148 g_signal_connect(gplugin_manager_get_instance(), "unloaded-plugin",
1149 G_CALLBACK(plugin_unloaded_cb), NULL);
1151 purple_plugins_refresh();
1152 #endif
1155 void
1156 purple_plugins_uninit(void)
1158 void *handle = purple_plugins_get_handle();
1160 #ifdef PURPLE_PLUGINS
1161 purple_debug_info("plugins", "Unloading all plugins\n");
1162 while (loaded_plugins != NULL)
1163 purple_plugin_unload(loaded_plugins->data, NULL);
1164 #endif
1166 purple_signals_disconnect_by_handle(handle);
1167 purple_signals_unregister_by_instance(handle);
1169 #ifdef PURPLE_PLUGINS
1170 gplugin_uninit();
1171 #endif