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
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
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. */
69 G_DEFINE_TYPE_WITH_PRIVATE(PurplePluginInfo
, purple_plugin_info
,
70 GPLUGIN_TYPE_PLUGIN_INFO
);
72 /**************************************************************************
74 **************************************************************************/
75 static GList
*loaded_plugins
= NULL
;
76 static GList
*plugins_to_disable
= NULL
;
78 /**************************************************************************
80 **************************************************************************/
82 plugin_loading_cb(GObject
*manager
, PurplePlugin
*plugin
, GError
**error
,
85 PurplePluginInfo
*info
;
86 PurplePluginInfoPrivate
*priv
;
88 g_return_val_if_fail(PURPLE_IS_PLUGIN(plugin
), FALSE
);
90 info
= purple_plugin_get_info(plugin
);
92 return TRUE
; /* a GPlugin internal plugin */
94 priv
= purple_plugin_info_get_instance_private(info
);
97 purple_debug_error("plugins", "Failed to load plugin %s: %s",
98 gplugin_plugin_get_filename(plugin
),
101 g_set_error(error
, PURPLE_PLUGINS_DOMAIN
, 0,
102 "Plugin is not loadable: %s", priv
->error
);
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
);
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
);
130 plugin_unloading_cb(GObject
*manager
, PurplePlugin
*plugin
, GError
**error
,
133 PurplePluginInfo
*info
;
135 g_return_val_if_fail(PURPLE_IS_PLUGIN(plugin
), FALSE
);
137 info
= purple_plugin_get_info(plugin
);
139 purple_debug_info("plugins", "Unloading plugin %s\n",
140 gplugin_plugin_get_filename(plugin
));
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
);
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
);
178 purple_plugin_load(PurplePlugin
*plugin
, GError
**error
)
182 g_return_val_if_fail(plugin
!= NULL
, FALSE
);
184 if (purple_plugin_is_loaded(plugin
))
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");
193 *error
= g_error_copy(err
);
203 purple_plugin_unload(PurplePlugin
*plugin
, GError
**error
)
207 g_return_val_if_fail(plugin
!= NULL
, FALSE
);
209 if (!purple_plugin_is_loaded(plugin
))
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");
218 *error
= g_error_copy(err
);
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
);
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
);
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
);
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
);
276 return (purple_plugin_info_get_flags(info
) &
277 PURPLE_PLUGIN_INFO_FLAGS_INTERNAL
);
281 purple_plugin_get_dependent_plugins(PurplePlugin
*plugin
)
283 #warning TODO: Implement this when GPlugin can return dependent plugins.
287 /**************************************************************************
288 * GObject code for PurplePluginInfo
289 **************************************************************************/
290 /* GObject initialization function */
292 purple_plugin_info_init(PurplePluginInfo
*info
)
296 /* Set method for GObject properties */
298 purple_plugin_info_set_property(GObject
*obj
, guint param_id
, const GValue
*value
,
301 PurplePluginInfo
*info
= PURPLE_PLUGIN_INFO(obj
);
302 PurplePluginInfoPrivate
*priv
=
303 purple_plugin_info_get_instance_private(info
);
306 case PROP_UI_REQUIREMENT
:
307 priv
->ui_requirement
= g_value_dup_string(value
);
309 case PROP_ACTIONS_CB
:
310 priv
->actions_cb
= g_value_get_pointer(value
);
313 priv
->extra_cb
= g_value_get_pointer(value
);
315 case PROP_PREF_FRAME_CB
:
316 priv
->pref_frame_cb
= g_value_get_pointer(value
);
318 case PROP_PREF_REQUEST_CB
:
319 priv
->pref_request_cb
= g_value_get_pointer(value
);
322 priv
->flags
= g_value_get_flags(value
);
325 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, param_id
, pspec
);
330 /* Get method for GObject properties */
332 purple_plugin_info_get_property(GObject
*obj
, guint param_id
, GValue
*value
,
335 PurplePluginInfo
*info
= PURPLE_PLUGIN_INFO(obj
);
338 case PROP_ACTIONS_CB
:
339 g_value_set_pointer(value
,
340 purple_plugin_info_get_actions_cb(info
));
343 g_value_set_pointer(value
,
344 purple_plugin_info_get_extra_cb(info
));
346 case PROP_PREF_FRAME_CB
:
347 g_value_set_pointer(value
,
348 purple_plugin_info_get_pref_frame_cb(info
));
350 case PROP_PREF_REQUEST_CB
:
351 g_value_set_pointer(value
,
352 purple_plugin_info_get_pref_request_cb(info
));
355 g_value_set_flags(value
, purple_plugin_info_get_flags(info
));
358 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, param_id
, pspec
);
363 /* Called when done constructing */
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
);
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",
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 */
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
);
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",
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",
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",
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 /**************************************************************************
473 **************************************************************************/
475 purple_plugin_info_new(const char *first_property
, ...)
480 /* at least ID is required */
484 va_start(var_args
, first_property
);
485 info
= g_object_new_valist(PURPLE_TYPE_PLUGIN_INFO
, first_property
,
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
;
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
);
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
);
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
;
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 /**************************************************************************
576 **************************************************************************/
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
;
593 purple_plugin_action_free(PurplePluginAction
*action
)
595 g_return_if_fail(action
!= NULL
);
597 g_free(action
->label
);
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 /**************************************************************************
614 **************************************************************************/
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
);
640 purple_plugins_get_loaded(void)
642 return loaded_plugins
;
646 purple_plugins_add_search_path(const gchar
*path
)
648 gplugin_manager_append_path(path
);
652 purple_plugins_refresh(void)
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
))
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
);
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
);
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
);
703 purple_plugins_find_by_filename(const char *filename
)
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
),
716 g_list_free(plugins
);
720 g_list_free(plugins
);
726 purple_plugins_save_loaded(const char *key
)
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
);
739 if (purple_plugin_info_get_flags(info
) &
740 PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD
)
743 if (!g_list_find(plugins_to_disable
, plugin
))
744 files
= g_list_append(
746 (gchar
*)gplugin_plugin_get_filename(plugin
));
749 purple_prefs_set_path_list(key
, files
);
754 purple_plugins_load_saved(const char *key
)
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
)
765 PurplePlugin
*plugin
;
771 plugin
= purple_plugins_find_by_filename(file
);
774 purple_debug_info("plugins", "Loading saved plugin %s\n", file
);
775 purple_plugin_load(plugin
, NULL
);
777 purple_debug_error("plugins", "Unable to find saved plugin %s\n", file
);
786 /**************************************************************************
787 * Plugins Subsystem API
788 **************************************************************************/
790 purple_plugins_get_handle(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
);
812 search_path
= g_getenv("PURPLE_PLUGIN_PATH");
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
]);
825 gplugin_manager_add_default_paths();
827 if(!g_getenv("PURPLE_PLUGINS_SKIP")) {
828 purple_plugins_add_search_path(PURPLE_LIBDIR
);
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();
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
);