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
22 #define _PURPLE_PLUGIN_C_
26 #include "accountopt.h"
28 #include "dbus-maybe.h"
44 } PurplePluginIpcInfo
;
49 PurpleSignalMarshalFunc marshal
;
53 PurpleValue
*ret_value
;
55 } PurplePluginIpcCommand
;
57 static GList
*search_paths
= NULL
;
58 static GList
*plugins
= NULL
;
59 static GList
*loaded_plugins
= NULL
;
60 static GList
*protocol_plugins
= NULL
;
62 static GList
*load_queue
= NULL
;
63 static GList
*plugin_loaders
= NULL
;
64 static GList
*plugins_to_disable
= NULL
;
67 static void (*probe_cb
)(void *) = NULL
;
68 static void *probe_cb_data
= NULL
;
69 static void (*load_cb
)(PurplePlugin
*, void *) = NULL
;
70 static void *load_cb_data
= NULL
;
71 static void (*unload_cb
)(PurplePlugin
*, void *) = NULL
;
72 static void *unload_cb_data
= NULL
;
77 has_file_extension(const char *filename
, const char *ext
)
81 if (filename
== NULL
|| *filename
== '\0' || ext
== NULL
)
85 len
= strlen(filename
) - extlen
;
90 return (strncmp(filename
+ len
, ext
, extlen
) == 0);
94 is_native(const char *filename
)
96 const char *last_period
;
98 last_period
= strrchr(filename
, '.');
99 if (last_period
== NULL
)
102 return !(strcmp(last_period
, ".dll") &
103 strcmp(last_period
, ".sl") &
104 strcmp(last_period
, ".so"));
108 purple_plugin_get_basename(const char *filename
)
110 const char *basename
;
111 const char *last_period
;
113 basename
= strrchr(filename
, G_DIR_SEPARATOR
);
114 if (basename
!= NULL
)
119 if (is_native(basename
) &&
120 ((last_period
= strrchr(basename
, '.')) != NULL
))
121 return g_strndup(basename
, (last_period
- basename
));
123 return g_strdup(basename
);
127 loader_supports_file(PurplePlugin
*loader
, const char *filename
)
131 for (exts
= PURPLE_PLUGIN_LOADER_INFO(loader
)->exts
; exts
!= NULL
; exts
= exts
->next
) {
132 if (has_file_extension(filename
, (char *)exts
->data
)) {
140 static PurplePlugin
*
141 find_loader_for_plugin(const PurplePlugin
*plugin
)
143 PurplePlugin
*loader
;
146 if (plugin
->path
== NULL
)
149 for (l
= purple_plugins_get_loaded(); l
!= NULL
; l
= l
->next
) {
152 if (loader
->info
->type
== PURPLE_PLUGIN_LOADER
&&
153 loader_supports_file(loader
, plugin
->path
)) {
164 #endif /* PURPLE_PLUGINS */
167 * Negative if a before b, 0 if equal, positive if a after b.
170 compare_prpl(PurplePlugin
*a
, PurplePlugin
*b
)
172 if(PURPLE_IS_PROTOCOL_PLUGIN(a
)) {
173 if(PURPLE_IS_PROTOCOL_PLUGIN(b
))
174 return strcmp(a
->info
->name
, b
->info
->name
);
178 if(PURPLE_IS_PROTOCOL_PLUGIN(b
))
186 purple_plugin_new(gboolean native
, const char *path
)
188 PurplePlugin
*plugin
;
190 plugin
= g_new0(PurplePlugin
, 1);
192 plugin
->native_plugin
= native
;
193 plugin
->path
= g_strdup(path
);
195 PURPLE_DBUS_REGISTER_POINTER(plugin
, PurplePlugin
);
201 purple_plugin_probe(const char *filename
)
203 #ifdef PURPLE_PLUGINS
204 PurplePlugin
*plugin
= NULL
;
205 PurplePlugin
*loader
;
207 gchar
*basename
= NULL
;
208 gboolean (*purple_init_plugin
)(PurplePlugin
*);
210 purple_debug_misc("plugins", "probing %s\n", filename
);
211 g_return_val_if_fail(filename
!= NULL
, NULL
);
213 if (!g_file_test(filename
, G_FILE_TEST_EXISTS
))
216 /* If this plugin has already been probed then exit */
217 basename
= purple_plugin_get_basename(filename
);
218 plugin
= purple_plugins_find_with_basename(basename
);
222 if (purple_strequal(filename
, plugin
->path
))
224 else if (!purple_plugin_is_unloadable(plugin
))
226 purple_debug_warning("plugins", "Not loading %s. "
227 "Another plugin with the same name (%s) has already been loaded.\n",
228 filename
, plugin
->path
);
233 /* The old plugin was a different file and it was unloadable.
234 * There's no guarantee that this new file with the same name
235 * will be loadable, but unless it fails in one of the silent
236 * ways and the first one didn't, it's not any worse. The user
237 * will still see a greyed-out plugin, which is what we want. */
238 purple_plugin_destroy(plugin
);
242 plugin
= purple_plugin_new(has_file_extension(filename
, G_MODULE_SUFFIX
), filename
);
244 if (plugin
->native_plugin
) {
247 /* Suppress error popups for failing to load plugins */
248 UINT old_error_mode
= SetErrorMode(SEM_FAILCRITICALERRORS
);
252 * We pass G_MODULE_BIND_LOCAL here to prevent symbols from
253 * plugins being added to the global name space.
255 * G_MODULE_BIND_LOCAL was added in glib 2.3.3.
257 plugin
->handle
= g_module_open(filename
, G_MODULE_BIND_LOCAL
);
259 if (plugin
->handle
== NULL
)
261 const char *error
= g_module_error();
262 if (error
!= NULL
&& purple_str_has_prefix(error
, filename
))
264 error
= error
+ strlen(filename
);
266 /* These are just so we don't crash. If we
267 * got this far, they should always be true. */
274 if (error
== NULL
|| !*error
)
276 plugin
->error
= g_strdup(_("Unknown error"));
277 purple_debug_error("plugins", "%s is not loadable: Unknown error\n",
282 plugin
->error
= g_strdup(error
);
283 purple_debug_error("plugins", "%s is not loadable: %s\n",
284 plugin
->path
, plugin
->error
);
286 plugin
->handle
= g_module_open(filename
, G_MODULE_BIND_LAZY
| G_MODULE_BIND_LOCAL
);
288 if (plugin
->handle
== NULL
)
291 /* Restore the original error mode */
292 SetErrorMode(old_error_mode
);
294 purple_plugin_destroy(plugin
);
299 /* We were able to load the plugin with lazy symbol binding.
300 * This means we're missing some symbol. Mark it as
301 * unloadable and keep going so we get the info to display
302 * to the user so they know to rebuild this plugin. */
303 plugin
->unloadable
= TRUE
;
307 if (!g_module_symbol(plugin
->handle
, "purple_init_plugin",
310 purple_debug_error("plugins", "%s is not usable because the "
311 "'purple_init_plugin' symbol could not be "
312 "found. Does the plugin call the "
313 "PURPLE_INIT_PLUGIN() macro?\n", plugin
->path
);
315 g_module_close(plugin
->handle
);
316 error
= g_module_error();
318 purple_debug_error("plugins", "Error closing module %s: %s\n",
319 plugin
->path
, error
);
320 plugin
->handle
= NULL
;
323 /* Restore the original error mode */
324 SetErrorMode(old_error_mode
);
326 purple_plugin_destroy(plugin
);
329 purple_init_plugin
= unpunned
;
332 /* Restore the original error mode */
333 SetErrorMode(old_error_mode
);
337 loader
= find_loader_for_plugin(plugin
);
339 if (loader
== NULL
) {
340 purple_plugin_destroy(plugin
);
344 purple_init_plugin
= PURPLE_PLUGIN_LOADER_INFO(loader
)->probe
;
347 if (!purple_init_plugin(plugin
) || plugin
->info
== NULL
)
349 purple_plugin_destroy(plugin
);
352 else if (plugin
->info
->ui_requirement
&&
353 !purple_strequal(plugin
->info
->ui_requirement
, purple_core_get_ui()))
355 plugin
->error
= g_strdup_printf(_("You are using %s, but this plugin requires %s."),
356 purple_core_get_ui(), plugin
->info
->ui_requirement
);
357 purple_debug_error("plugins", "%s is not loadable: The UI requirement is not met. (%s)\n", plugin
->path
, plugin
->error
);
358 plugin
->unloadable
= TRUE
;
363 * Check to make sure a plugin has defined an id.
364 * Not having this check caused purple_plugin_unload to
365 * enter an infinite loop in certain situations by passing
366 * purple_find_plugin_by_id a NULL value. -- ecoffey
368 if (plugin
->info
->id
== NULL
|| *plugin
->info
->id
== '\0')
370 plugin
->error
= g_strdup(_("This plugin has not defined an ID."));
371 purple_debug_error("plugins", "%s is not loadable: info->id is not defined.\n", plugin
->path
);
372 plugin
->unloadable
= TRUE
;
376 /* Really old plugins. */
377 if (plugin
->info
->magic
!= PURPLE_PLUGIN_MAGIC
)
379 if (plugin
->info
->magic
>= 2 && plugin
->info
->magic
<= 4)
381 struct _PurplePluginInfo2
383 unsigned int api_version
;
384 PurplePluginType type
;
385 char *ui_requirement
;
388 PurplePluginPriority priority
;
398 gboolean (*load
)(PurplePlugin
*plugin
);
399 gboolean (*unload
)(PurplePlugin
*plugin
);
400 void (*destroy
)(PurplePlugin
*plugin
);
404 PurplePluginUiInfo
*prefs_info
;
405 GList
*(*actions
)(PurplePlugin
*plugin
, gpointer context
);
406 } *info2
= (struct _PurplePluginInfo2
*)plugin
->info
;
408 /* This leaks... but only for ancient plugins, so deal with it. */
409 plugin
->info
= g_new0(PurplePluginInfo
, 1);
411 /* We don't really need all these to display the plugin info, but
412 * I'm copying them all for good measure. */
413 plugin
->info
->magic
= info2
->api_version
;
414 plugin
->info
->type
= info2
->type
;
415 plugin
->info
->ui_requirement
= info2
->ui_requirement
;
416 plugin
->info
->flags
= info2
->flags
;
417 plugin
->info
->dependencies
= info2
->dependencies
;
418 plugin
->info
->id
= info2
->id
;
419 plugin
->info
->name
= info2
->name
;
420 plugin
->info
->version
= info2
->version
;
421 plugin
->info
->summary
= info2
->summary
;
422 plugin
->info
->description
= info2
->description
;
423 plugin
->info
->author
= info2
->author
;
424 plugin
->info
->homepage
= info2
->homepage
;
425 plugin
->info
->load
= info2
->load
;
426 plugin
->info
->unload
= info2
->unload
;
427 plugin
->info
->destroy
= info2
->destroy
;
428 plugin
->info
->ui_info
= info2
->ui_info
;
429 plugin
->info
->extra_info
= info2
->extra_info
;
431 if (info2
->api_version
>= 3)
432 plugin
->info
->prefs_info
= info2
->prefs_info
;
434 if (info2
->api_version
>= 4)
435 plugin
->info
->actions
= info2
->actions
;
438 plugin
->error
= g_strdup_printf(_("Plugin magic mismatch %d (need %d)"),
439 plugin
->info
->magic
, PURPLE_PLUGIN_MAGIC
);
440 purple_debug_error("plugins", "%s is not loadable: Plugin magic mismatch %d (need %d)\n",
441 plugin
->path
, plugin
->info
->magic
, PURPLE_PLUGIN_MAGIC
);
442 plugin
->unloadable
= TRUE
;
446 purple_debug_error("plugins", "%s is not loadable: Plugin magic mismatch %d (need %d)\n",
447 plugin
->path
, plugin
->info
->magic
, PURPLE_PLUGIN_MAGIC
);
448 purple_plugin_destroy(plugin
);
452 if (plugin
->info
->major_version
!= PURPLE_MAJOR_VERSION
||
453 plugin
->info
->minor_version
> PURPLE_MINOR_VERSION
)
455 plugin
->error
= g_strdup_printf(_("ABI version mismatch %d.%d.x (need %d.%d.x)"),
456 plugin
->info
->major_version
, plugin
->info
->minor_version
,
457 PURPLE_MAJOR_VERSION
, PURPLE_MINOR_VERSION
);
458 purple_debug_error("plugins", "%s is not loadable: ABI version mismatch %d.%d.x (need %d.%d.x)\n",
459 plugin
->path
, plugin
->info
->major_version
, plugin
->info
->minor_version
,
460 PURPLE_MAJOR_VERSION
, PURPLE_MINOR_VERSION
);
461 plugin
->unloadable
= TRUE
;
465 if (plugin
->info
->type
== PURPLE_PLUGIN_PROTOCOL
)
467 /* If plugin is a PRPL, make sure it implements the required functions */
468 if ((PURPLE_PLUGIN_PROTOCOL_INFO(plugin
)->list_icon
== NULL
) ||
469 (PURPLE_PLUGIN_PROTOCOL_INFO(plugin
)->login
== NULL
) ||
470 (PURPLE_PLUGIN_PROTOCOL_INFO(plugin
)->close
== NULL
))
472 plugin
->error
= g_strdup(_("Plugin does not implement all required functions (list_icon, login and close)"));
473 purple_debug_error("plugins", "%s is not loadable: %s\n",
474 plugin
->path
, plugin
->error
);
475 plugin
->unloadable
= TRUE
;
479 /* For debugging, let's warn about prpl prefs. */
480 if (plugin
->info
->prefs_info
!= NULL
)
482 purple_debug_error("plugins", "%s has a prefs_info, but is a prpl. This is no longer supported.\n",
490 #endif /* !PURPLE_PLUGINS */
493 #ifdef PURPLE_PLUGINS
495 compare_plugins(gconstpointer a
, gconstpointer b
)
497 const PurplePlugin
*plugina
= a
;
498 const PurplePlugin
*pluginb
= b
;
500 return strcmp(plugina
->info
->name
, pluginb
->info
->name
);
502 #endif /* PURPLE_PLUGINS */
505 purple_plugin_load(PurplePlugin
*plugin
)
507 #ifdef PURPLE_PLUGINS
508 GList
*dep_list
= NULL
;
511 g_return_val_if_fail(plugin
!= NULL
, FALSE
);
513 if (purple_plugin_is_loaded(plugin
))
516 if (purple_plugin_is_unloadable(plugin
))
519 g_return_val_if_fail(plugin
->error
== NULL
, FALSE
);
522 * Go through the list of the plugin's dependencies.
524 * First pass: Make sure all the plugins needed are probed.
526 for (l
= plugin
->info
->dependencies
; l
!= NULL
; l
= l
->next
)
528 const char *dep_name
= (const char *)l
->data
;
529 PurplePlugin
*dep_plugin
;
531 dep_plugin
= purple_plugins_find_with_id(dep_name
);
533 if (dep_plugin
== NULL
)
537 tmp
= g_strdup_printf(_("The required plugin %s was not found. "
538 "Please install this plugin and try again."),
541 purple_notify_error(NULL
, NULL
,
542 _("Unable to load the plugin"), tmp
);
545 g_list_free(dep_list
);
550 dep_list
= g_list_append(dep_list
, dep_plugin
);
553 /* Second pass: load all the required plugins. */
554 for (l
= dep_list
; l
!= NULL
; l
= l
->next
)
556 PurplePlugin
*dep_plugin
= (PurplePlugin
*)l
->data
;
558 if (!purple_plugin_is_loaded(dep_plugin
))
560 if (!purple_plugin_load(dep_plugin
))
564 tmp
= g_strdup_printf(_("The required plugin %s was unable to load."),
567 purple_notify_error(NULL
, NULL
,
568 _("Unable to load your plugin."), tmp
);
571 g_list_free(dep_list
);
578 /* Third pass: note that other plugins are dependencies of this plugin.
579 * This is done separately in case we had to bail out earlier. */
580 for (l
= dep_list
; l
!= NULL
; l
= l
->next
)
582 PurplePlugin
*dep_plugin
= (PurplePlugin
*)l
->data
;
583 dep_plugin
->dependent_plugins
= g_list_prepend(dep_plugin
->dependent_plugins
, plugin
->info
->id
);
586 g_list_free(dep_list
);
588 if (plugin
->native_plugin
)
590 if (plugin
->info
!= NULL
&& plugin
->info
->load
!= NULL
)
592 if (!plugin
->info
->load(plugin
))
597 PurplePlugin
*loader
;
598 PurplePluginLoaderInfo
*loader_info
;
600 loader
= find_loader_for_plugin(plugin
);
605 loader_info
= PURPLE_PLUGIN_LOADER_INFO(loader
);
607 if (loader_info
->load
!= NULL
)
609 if (!loader_info
->load(plugin
))
614 loaded_plugins
= g_list_insert_sorted(loaded_plugins
, plugin
, compare_plugins
);
616 plugin
->loaded
= TRUE
;
619 load_cb(plugin
, load_cb_data
);
621 purple_signal_emit(purple_plugins_get_handle(), "plugin-load", plugin
);
627 #endif /* !PURPLE_PLUGINS */
631 purple_plugin_unload(PurplePlugin
*plugin
)
633 #ifdef PURPLE_PLUGINS
637 g_return_val_if_fail(plugin
!= NULL
, FALSE
);
638 g_return_val_if_fail(purple_plugin_is_loaded(plugin
), FALSE
);
640 purple_debug_info("plugins", "Unloading plugin %s\n", plugin
->info
->name
);
642 /* Unload all plugins that depend on this plugin. */
643 for (l
= plugin
->dependent_plugins
; l
!= NULL
; l
= ll
) {
644 const char * dep_name
= (const char *)l
->data
;
645 PurplePlugin
*dep_plugin
;
647 /* Store a pointer to the next element in the list.
648 * This is because we'll be modifying this list in the loop. */
651 dep_plugin
= purple_plugins_find_with_id(dep_name
);
653 if (dep_plugin
!= NULL
&& purple_plugin_is_loaded(dep_plugin
))
655 if (!purple_plugin_unload(dep_plugin
))
657 g_free(plugin
->error
);
658 plugin
->error
= g_strdup_printf(_("%s requires %s, but it failed to unload."),
659 _(plugin
->info
->name
),
660 _(dep_plugin
->info
->name
));
666 /* This isn't necessary. This has already been done when unloading dep_plugin. */
667 plugin
->dependent_plugins
= g_list_delete_link(plugin
->dependent_plugins
, l
);
673 /* Remove this plugin from each dependency's dependent_plugins list. */
674 for (l
= plugin
->info
->dependencies
; l
!= NULL
; l
= l
->next
)
676 const char *dep_name
= (const char *)l
->data
;
677 PurplePlugin
*dependency
;
679 dependency
= purple_plugins_find_with_id(dep_name
);
681 if (dependency
!= NULL
)
682 dependency
->dependent_plugins
= g_list_remove(dependency
->dependent_plugins
, plugin
->info
->id
);
684 purple_debug_error("plugins", "Unable to remove from dependency list for %s\n", dep_name
);
687 if (plugin
->native_plugin
) {
688 if (plugin
->info
->unload
&& !plugin
->info
->unload(plugin
))
691 if (plugin
->info
->type
== PURPLE_PLUGIN_PROTOCOL
) {
692 PurplePluginProtocolInfo
*prpl_info
;
695 prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(plugin
);
697 for (l
= prpl_info
->user_splits
; l
!= NULL
; l
= l
->next
)
698 purple_account_user_split_destroy(l
->data
);
700 for (l
= prpl_info
->protocol_options
; l
!= NULL
; l
= l
->next
)
701 purple_account_option_destroy(l
->data
);
703 if (prpl_info
->user_splits
!= NULL
) {
704 g_list_free(prpl_info
->user_splits
);
705 prpl_info
->user_splits
= NULL
;
708 if (prpl_info
->protocol_options
!= NULL
) {
709 g_list_free(prpl_info
->protocol_options
);
710 prpl_info
->protocol_options
= NULL
;
714 PurplePlugin
*loader
;
715 PurplePluginLoaderInfo
*loader_info
;
717 loader
= find_loader_for_plugin(plugin
);
722 loader_info
= PURPLE_PLUGIN_LOADER_INFO(loader
);
724 if (loader_info
->unload
&& !loader_info
->unload(plugin
))
728 /* cancel any pending dialogs the plugin has */
729 purple_request_close_with_handle(plugin
);
730 purple_notify_close_with_handle(plugin
);
732 purple_signals_disconnect_by_handle(plugin
);
733 purple_plugin_ipc_unregister_all(plugin
);
735 loaded_plugins
= g_list_remove(loaded_plugins
, plugin
);
736 if ((plugin
->info
!= NULL
) && PURPLE_IS_PROTOCOL_PLUGIN(plugin
))
737 protocol_plugins
= g_list_remove(protocol_plugins
, plugin
);
738 plugins_to_disable
= g_list_remove(plugins_to_disable
, plugin
);
739 plugin
->loaded
= FALSE
;
741 /* We wouldn't be anywhere near here if the plugin wasn't loaded, so
742 * if plugin->error is set at all, it had to be from a previous
743 * unload failure. It's obviously okay now.
745 g_free(plugin
->error
);
746 plugin
->error
= NULL
;
748 if (unload_cb
!= NULL
)
749 unload_cb(plugin
, unload_cb_data
);
751 purple_signal_emit(purple_plugins_get_handle(), "plugin-unload", plugin
);
753 purple_prefs_disconnect_by_handle(plugin
);
758 #endif /* PURPLE_PLUGINS */
762 purple_plugin_disable(PurplePlugin
*plugin
)
764 #ifdef PURPLE_PLUGINS
765 g_return_if_fail(plugin
!= NULL
);
767 if (!g_list_find(plugins_to_disable
, plugin
))
768 plugins_to_disable
= g_list_prepend(plugins_to_disable
, plugin
);
773 purple_plugin_reload(PurplePlugin
*plugin
)
775 #ifdef PURPLE_PLUGINS
776 g_return_val_if_fail(plugin
!= NULL
, FALSE
);
777 g_return_val_if_fail(purple_plugin_is_loaded(plugin
), FALSE
);
779 if (!purple_plugin_unload(plugin
))
782 if (!purple_plugin_load(plugin
))
788 #endif /* !PURPLE_PLUGINS */
792 purple_plugin_destroy(PurplePlugin
*plugin
)
794 #ifdef PURPLE_PLUGINS
795 g_return_if_fail(plugin
!= NULL
);
797 if (purple_plugin_is_loaded(plugin
))
798 purple_plugin_unload(plugin
);
800 plugins
= g_list_remove(plugins
, plugin
);
802 if (load_queue
!= NULL
)
803 load_queue
= g_list_remove(load_queue
, plugin
);
805 /* true, this may leak a little memory if there is a major version
806 * mismatch, but it's a lot better than trying to free something
807 * we shouldn't, and crashing while trying to load an old plugin */
808 if(plugin
->info
== NULL
|| plugin
->info
->magic
!= PURPLE_PLUGIN_MAGIC
||
809 plugin
->info
->major_version
!= PURPLE_MAJOR_VERSION
)
812 g_module_close(plugin
->handle
);
814 g_free(plugin
->path
);
815 g_free(plugin
->error
);
817 PURPLE_DBUS_UNREGISTER_POINTER(plugin
);
823 if (plugin
->info
!= NULL
)
824 g_list_free(plugin
->info
->dependencies
);
826 if (plugin
->native_plugin
)
828 if (plugin
->info
!= NULL
&& plugin
->info
->type
== PURPLE_PLUGIN_LOADER
)
830 PurplePluginLoaderInfo
*loader_info
;
831 GList
*exts
, *l
, *next_l
;
834 loader_info
= PURPLE_PLUGIN_LOADER_INFO(plugin
);
836 if (loader_info
!= NULL
&& loader_info
->exts
!= NULL
)
838 for (exts
= PURPLE_PLUGIN_LOADER_INFO(plugin
)->exts
;
842 for (l
= purple_plugins_get_all(); l
!= NULL
; l
= next_l
)
848 if (p2
->path
!= NULL
&&
849 has_file_extension(p2
->path
, exts
->data
))
851 purple_plugin_destroy(p2
);
856 g_list_free(loader_info
->exts
);
857 loader_info
->exts
= NULL
;
860 plugin_loaders
= g_list_remove(plugin_loaders
, plugin
);
863 if (plugin
->info
!= NULL
&& plugin
->info
->destroy
!= NULL
)
864 plugin
->info
->destroy(plugin
);
867 * I find it extremely useful to do this when using valgrind, as
868 * it keeps all the plugins open, meaning that valgrind is able to
869 * resolve symbol names in leak traces from plugins.
871 if (!g_getenv("PURPLE_LEAKCHECK_HELP") && !RUNNING_ON_VALGRIND
)
873 if (plugin
->handle
!= NULL
)
874 g_module_close(plugin
->handle
);
879 PurplePlugin
*loader
;
880 PurplePluginLoaderInfo
*loader_info
;
882 loader
= find_loader_for_plugin(plugin
);
886 loader_info
= PURPLE_PLUGIN_LOADER_INFO(loader
);
888 if (loader_info
->destroy
!= NULL
)
889 loader_info
->destroy(plugin
);
893 g_free(plugin
->path
);
894 g_free(plugin
->error
);
896 PURPLE_DBUS_UNREGISTER_POINTER(plugin
);
899 #endif /* !PURPLE_PLUGINS */
903 purple_plugin_is_loaded(const PurplePlugin
*plugin
)
905 g_return_val_if_fail(plugin
!= NULL
, FALSE
);
907 return plugin
->loaded
;
911 purple_plugin_is_unloadable(const PurplePlugin
*plugin
)
913 g_return_val_if_fail(plugin
!= NULL
, FALSE
);
915 return plugin
->unloadable
;
919 purple_plugin_get_id(const PurplePlugin
*plugin
) {
920 g_return_val_if_fail(plugin
, NULL
);
921 g_return_val_if_fail(plugin
->info
, NULL
);
923 return plugin
->info
->id
;
927 purple_plugin_get_name(const PurplePlugin
*plugin
) {
928 g_return_val_if_fail(plugin
, NULL
);
929 g_return_val_if_fail(plugin
->info
, NULL
);
931 return _(plugin
->info
->name
);
935 purple_plugin_get_version(const PurplePlugin
*plugin
) {
936 g_return_val_if_fail(plugin
, NULL
);
937 g_return_val_if_fail(plugin
->info
, NULL
);
939 return plugin
->info
->version
;
943 purple_plugin_get_summary(const PurplePlugin
*plugin
) {
944 g_return_val_if_fail(plugin
, NULL
);
945 g_return_val_if_fail(plugin
->info
, NULL
);
947 return _(plugin
->info
->summary
);
951 purple_plugin_get_description(const PurplePlugin
*plugin
) {
952 g_return_val_if_fail(plugin
, NULL
);
953 g_return_val_if_fail(plugin
->info
, NULL
);
955 return _(plugin
->info
->description
);
959 purple_plugin_get_author(const PurplePlugin
*plugin
) {
960 g_return_val_if_fail(plugin
, NULL
);
961 g_return_val_if_fail(plugin
->info
, NULL
);
963 return _(plugin
->info
->author
);
967 purple_plugin_get_homepage(const PurplePlugin
*plugin
) {
968 g_return_val_if_fail(plugin
, NULL
);
969 g_return_val_if_fail(plugin
->info
, NULL
);
971 return plugin
->info
->homepage
;
974 /**************************************************************************
976 **************************************************************************/
978 destroy_ipc_info(void *data
)
980 PurplePluginIpcCommand
*ipc_command
= (PurplePluginIpcCommand
*)data
;
983 if (ipc_command
->params
!= NULL
)
985 for (i
= 0; i
< ipc_command
->num_params
; i
++)
986 purple_value_destroy(ipc_command
->params
[i
]);
988 g_free(ipc_command
->params
);
991 if (ipc_command
->ret_value
!= NULL
)
992 purple_value_destroy(ipc_command
->ret_value
);
998 purple_plugin_ipc_register(PurplePlugin
*plugin
, const char *command
,
999 PurpleCallback func
, PurpleSignalMarshalFunc marshal
,
1000 PurpleValue
*ret_value
, int num_params
, ...)
1002 PurplePluginIpcInfo
*ipc_info
;
1003 PurplePluginIpcCommand
*ipc_command
;
1005 g_return_val_if_fail(plugin
!= NULL
, FALSE
);
1006 g_return_val_if_fail(command
!= NULL
, FALSE
);
1007 g_return_val_if_fail(func
!= NULL
, FALSE
);
1008 g_return_val_if_fail(marshal
!= NULL
, FALSE
);
1010 if (plugin
->ipc_data
== NULL
)
1012 ipc_info
= plugin
->ipc_data
= g_new0(PurplePluginIpcInfo
, 1);
1013 ipc_info
->commands
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1014 g_free
, destroy_ipc_info
);
1017 ipc_info
= (PurplePluginIpcInfo
*)plugin
->ipc_data
;
1019 ipc_command
= g_new0(PurplePluginIpcCommand
, 1);
1020 ipc_command
->func
= func
;
1021 ipc_command
->marshal
= marshal
;
1022 ipc_command
->num_params
= num_params
;
1023 ipc_command
->ret_value
= ret_value
;
1030 ipc_command
->params
= g_new0(PurpleValue
*, num_params
);
1032 va_start(args
, num_params
);
1034 for (i
= 0; i
< num_params
; i
++)
1035 ipc_command
->params
[i
] = va_arg(args
, PurpleValue
*);
1040 g_hash_table_replace(ipc_info
->commands
, g_strdup(command
), ipc_command
);
1042 ipc_info
->command_count
++;
1048 purple_plugin_ipc_unregister(PurplePlugin
*plugin
, const char *command
)
1050 PurplePluginIpcInfo
*ipc_info
;
1052 g_return_if_fail(plugin
!= NULL
);
1053 g_return_if_fail(command
!= NULL
);
1055 ipc_info
= (PurplePluginIpcInfo
*)plugin
->ipc_data
;
1057 if (ipc_info
== NULL
||
1058 g_hash_table_lookup(ipc_info
->commands
, command
) == NULL
)
1060 purple_debug_error("plugins",
1061 "IPC command '%s' was not registered for plugin %s\n",
1062 command
, plugin
->info
->name
);
1066 g_hash_table_remove(ipc_info
->commands
, command
);
1068 ipc_info
->command_count
--;
1070 if (ipc_info
->command_count
== 0)
1072 g_hash_table_destroy(ipc_info
->commands
);
1075 plugin
->ipc_data
= NULL
;
1080 purple_plugin_ipc_unregister_all(PurplePlugin
*plugin
)
1082 PurplePluginIpcInfo
*ipc_info
;
1084 g_return_if_fail(plugin
!= NULL
);
1086 if (plugin
->ipc_data
== NULL
)
1087 return; /* Silently ignore it. */
1089 ipc_info
= (PurplePluginIpcInfo
*)plugin
->ipc_data
;
1091 g_hash_table_destroy(ipc_info
->commands
);
1094 plugin
->ipc_data
= NULL
;
1098 purple_plugin_ipc_get_params(PurplePlugin
*plugin
, const char *command
,
1099 PurpleValue
**ret_value
, int *num_params
,
1100 PurpleValue
***params
)
1102 PurplePluginIpcInfo
*ipc_info
;
1103 PurplePluginIpcCommand
*ipc_command
;
1105 g_return_val_if_fail(plugin
!= NULL
, FALSE
);
1106 g_return_val_if_fail(command
!= NULL
, FALSE
);
1108 ipc_info
= (PurplePluginIpcInfo
*)plugin
->ipc_data
;
1110 if (ipc_info
== NULL
||
1111 (ipc_command
= g_hash_table_lookup(ipc_info
->commands
,
1114 purple_debug_error("plugins",
1115 "IPC command '%s' was not registered for plugin %s\n",
1116 command
, plugin
->info
->name
);
1121 if (num_params
!= NULL
)
1122 *num_params
= ipc_command
->num_params
;
1125 *params
= ipc_command
->params
;
1127 if (ret_value
!= NULL
)
1128 *ret_value
= ipc_command
->ret_value
;
1134 purple_plugin_ipc_call(PurplePlugin
*plugin
, const char *command
,
1137 PurplePluginIpcInfo
*ipc_info
;
1138 PurplePluginIpcCommand
*ipc_command
;
1145 g_return_val_if_fail(plugin
!= NULL
, NULL
);
1146 g_return_val_if_fail(command
!= NULL
, NULL
);
1148 ipc_info
= (PurplePluginIpcInfo
*)plugin
->ipc_data
;
1150 if (ipc_info
== NULL
||
1151 (ipc_command
= g_hash_table_lookup(ipc_info
->commands
,
1154 purple_debug_error("plugins",
1155 "IPC command '%s' was not registered for plugin %s\n",
1156 command
, plugin
->info
->name
);
1162 ipc_command
->marshal(ipc_command
->func
, args
, NULL
, &ret_value
);
1171 /**************************************************************************
1173 **************************************************************************/
1175 purple_plugins_get_handle(void) {
1182 purple_plugins_init(void) {
1183 void *handle
= purple_plugins_get_handle();
1185 purple_plugins_add_search_path(LIBDIR
);
1187 purple_signal_register(handle
, "plugin-load",
1188 purple_marshal_VOID__POINTER
,
1190 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1191 PURPLE_SUBTYPE_PLUGIN
));
1192 purple_signal_register(handle
, "plugin-unload",
1193 purple_marshal_VOID__POINTER
,
1195 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1196 PURPLE_SUBTYPE_PLUGIN
));
1200 purple_plugins_uninit(void)
1202 void *handle
= purple_plugins_get_handle();
1204 purple_signals_disconnect_by_handle(handle
);
1205 purple_signals_unregister_by_instance(handle
);
1207 while (search_paths
) {
1208 g_free(search_paths
->data
);
1209 search_paths
= g_list_delete_link(search_paths
, search_paths
);
1213 /**************************************************************************
1215 **************************************************************************/
1217 purple_plugins_add_search_path(const char *path
)
1219 g_return_if_fail(path
!= NULL
);
1221 if (g_list_find_custom(search_paths
, path
, (GCompareFunc
)strcmp
))
1224 search_paths
= g_list_append(search_paths
, g_strdup(path
));
1228 purple_plugins_get_search_paths()
1230 return search_paths
;
1234 purple_plugins_unload_all(void)
1236 #ifdef PURPLE_PLUGINS
1238 while (loaded_plugins
!= NULL
)
1239 purple_plugin_unload(loaded_plugins
->data
);
1241 #endif /* PURPLE_PLUGINS */
1245 purple_plugins_unload(PurplePluginType type
)
1247 #ifdef PURPLE_PLUGINS
1250 for (l
= plugins
; l
; l
= l
->next
) {
1251 PurplePlugin
*plugin
= l
->data
;
1252 if (plugin
->info
->type
== type
&& purple_plugin_is_loaded(plugin
))
1253 purple_plugin_unload(plugin
);
1256 #endif /* PURPLE_PLUGINS */
1260 purple_plugins_destroy_all(void)
1262 #ifdef PURPLE_PLUGINS
1264 while (plugins
!= NULL
)
1265 purple_plugin_destroy(plugins
->data
);
1267 #endif /* PURPLE_PLUGINS */
1271 purple_plugins_save_loaded(const char *key
)
1273 #ifdef PURPLE_PLUGINS
1275 GList
*files
= NULL
;
1277 for (pl
= purple_plugins_get_loaded(); pl
!= NULL
; pl
= pl
->next
) {
1278 PurplePlugin
*plugin
= pl
->data
;
1280 if (plugin
->info
->type
!= PURPLE_PLUGIN_PROTOCOL
&&
1281 plugin
->info
->type
!= PURPLE_PLUGIN_LOADER
&&
1282 !g_list_find(plugins_to_disable
, plugin
)) {
1283 files
= g_list_append(files
, plugin
->path
);
1287 purple_prefs_set_path_list(key
, files
);
1293 purple_plugins_load_saved(const char *key
)
1295 #ifdef PURPLE_PLUGINS
1298 g_return_if_fail(key
!= NULL
);
1300 files
= purple_prefs_get_path_list(key
);
1302 for (f
= files
; f
; f
= f
->next
)
1306 PurplePlugin
*plugin
;
1308 if (f
->data
== NULL
)
1314 * We don't know if the filename uses Windows or Unix path
1315 * separators (because people might be sharing a prefs.xml
1316 * file across systems), so we find the last occurrence
1319 basename
= strrchr(filename
, '/');
1320 if ((basename
== NULL
) || (basename
< strrchr(filename
, '\\')))
1321 basename
= strrchr(filename
, '\\');
1322 if (basename
!= NULL
)
1325 /* Strip the extension */
1327 basename
= purple_plugin_get_basename(basename
);
1329 if (((plugin
= purple_plugins_find_with_filename(filename
)) != NULL
) ||
1330 (basename
&& (plugin
= purple_plugins_find_with_basename(basename
)) != NULL
) ||
1331 ((plugin
= purple_plugin_probe(filename
)) != NULL
))
1333 purple_debug_info("plugins", "Loading saved plugin %s\n",
1335 purple_plugin_load(plugin
);
1339 purple_debug_error("plugins", "Unable to find saved plugin %s\n",
1349 #endif /* PURPLE_PLUGINS */
1354 purple_plugins_probe(const char *ext
)
1356 #ifdef PURPLE_PLUGINS
1360 PurplePlugin
*plugin
;
1362 const char *search_path
;
1364 if (!g_module_supported())
1368 for (cur
= search_paths
; cur
!= NULL
; cur
= cur
->next
)
1370 search_path
= cur
->data
;
1372 dir
= g_dir_open(search_path
, 0, NULL
);
1376 while ((file
= g_dir_read_name(dir
)) != NULL
)
1378 path
= g_build_filename(search_path
, file
, NULL
);
1380 if (ext
== NULL
|| has_file_extension(file
, ext
))
1381 plugin
= purple_plugin_probe(path
);
1390 /* See if we have any plugins waiting to load */
1391 while (load_queue
!= NULL
)
1393 plugin
= (PurplePlugin
*)load_queue
->data
;
1395 load_queue
= g_list_remove(load_queue
, plugin
);
1397 if (plugin
== NULL
|| plugin
->info
== NULL
)
1400 if (plugin
->info
->type
== PURPLE_PLUGIN_LOADER
)
1402 /* We'll just load this right now. */
1403 if (!purple_plugin_load(plugin
))
1405 purple_plugin_destroy(plugin
);
1410 plugin_loaders
= g_list_append(plugin_loaders
, plugin
);
1412 for (cur
= PURPLE_PLUGIN_LOADER_INFO(plugin
)->exts
;
1416 purple_plugins_probe(cur
->data
);
1419 else if (plugin
->info
->type
== PURPLE_PLUGIN_PROTOCOL
)
1421 /* We'll just load this right now. */
1422 if (!purple_plugin_load(plugin
))
1424 purple_plugin_destroy(plugin
);
1429 /* Make sure we don't load two PRPLs with the same name? */
1430 if (purple_find_prpl(plugin
->info
->id
))
1432 /* Nothing to see here--move along, move along */
1433 purple_plugin_destroy(plugin
);
1438 protocol_plugins
= g_list_insert_sorted(protocol_plugins
, plugin
,
1439 (GCompareFunc
)compare_prpl
);
1443 if (probe_cb
!= NULL
)
1444 probe_cb(probe_cb_data
);
1446 #endif /* PURPLE_PLUGINS */
1450 purple_plugin_register(PurplePlugin
*plugin
)
1452 g_return_val_if_fail(plugin
!= NULL
, FALSE
);
1454 /* If this plugin has been registered already then exit */
1455 if (g_list_find(plugins
, plugin
))
1458 /* Ensure the plugin has the requisite information */
1459 if (plugin
->info
->type
== PURPLE_PLUGIN_LOADER
)
1461 PurplePluginLoaderInfo
*loader_info
;
1463 loader_info
= PURPLE_PLUGIN_LOADER_INFO(plugin
);
1465 if (loader_info
== NULL
)
1467 purple_debug_error("plugins", "%s is not loadable, loader plugin missing loader_info\n",
1472 else if (plugin
->info
->type
== PURPLE_PLUGIN_PROTOCOL
)
1474 PurplePluginProtocolInfo
*prpl_info
;
1476 prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(plugin
);
1478 if (prpl_info
== NULL
)
1480 purple_debug_error("plugins", "%s is not loadable, protocol plugin missing prpl_info\n",
1486 #ifdef PURPLE_PLUGINS
1487 /* This plugin should be probed and maybe loaded--add it to the queue */
1488 load_queue
= g_list_append(load_queue
, plugin
);
1490 if (plugin
->info
!= NULL
)
1492 if (plugin
->info
->type
== PURPLE_PLUGIN_PROTOCOL
)
1493 protocol_plugins
= g_list_insert_sorted(protocol_plugins
, plugin
,
1494 (GCompareFunc
)compare_prpl
);
1495 if (plugin
->info
->load
!= NULL
)
1496 if (!plugin
->info
->load(plugin
))
1501 plugins
= g_list_append(plugins
, plugin
);
1507 purple_plugins_enabled(void)
1509 #ifdef PURPLE_PLUGINS
1517 purple_plugins_register_probe_notify_cb(void (*func
)(void *), void *data
)
1520 probe_cb_data
= data
;
1524 purple_plugins_unregister_probe_notify_cb(void (*func
)(void *))
1527 probe_cb_data
= NULL
;
1531 purple_plugins_register_load_notify_cb(void (*func
)(PurplePlugin
*, void *),
1535 load_cb_data
= data
;
1539 purple_plugins_unregister_load_notify_cb(void (*func
)(PurplePlugin
*, void *))
1542 load_cb_data
= NULL
;
1546 purple_plugins_register_unload_notify_cb(void (*func
)(PurplePlugin
*, void *),
1550 unload_cb_data
= data
;
1554 purple_plugins_unregister_unload_notify_cb(void (*func
)(PurplePlugin
*, void *))
1557 unload_cb_data
= NULL
;
1561 purple_plugins_find_with_name(const char *name
)
1563 PurplePlugin
*plugin
;
1566 for (l
= plugins
; l
!= NULL
; l
= l
->next
) {
1569 if (purple_strequal(plugin
->info
->name
, name
))
1577 purple_plugins_find_with_filename(const char *filename
)
1579 PurplePlugin
*plugin
;
1582 for (l
= plugins
; l
!= NULL
; l
= l
->next
) {
1585 if (purple_strequal(plugin
->path
, filename
))
1593 purple_plugins_find_with_basename(const char *basename
)
1595 #ifdef PURPLE_PLUGINS
1596 PurplePlugin
*plugin
;
1600 g_return_val_if_fail(basename
!= NULL
, NULL
);
1602 for (l
= plugins
; l
!= NULL
; l
= l
->next
)
1604 plugin
= (PurplePlugin
*)l
->data
;
1606 if (plugin
->path
!= NULL
) {
1607 tmp
= purple_plugin_get_basename(plugin
->path
);
1608 if (purple_strequal(tmp
, basename
))
1617 #endif /* PURPLE_PLUGINS */
1623 purple_plugins_find_with_id(const char *id
)
1625 PurplePlugin
*plugin
;
1628 g_return_val_if_fail(id
!= NULL
, NULL
);
1630 for (l
= plugins
; l
!= NULL
; l
= l
->next
)
1634 if (purple_strequal(plugin
->info
->id
, id
))
1642 purple_plugins_get_loaded(void)
1644 return loaded_plugins
;
1648 purple_plugins_get_protocols(void)
1650 return protocol_plugins
;
1654 purple_plugins_get_all(void)
1660 PurplePluginAction
*
1661 purple_plugin_action_new(const char* label
, void (*callback
)(PurplePluginAction
*))
1663 PurplePluginAction
*act
= g_new0(PurplePluginAction
, 1);
1665 act
->label
= g_strdup(label
);
1666 act
->callback
= callback
;
1672 purple_plugin_action_free(PurplePluginAction
*action
)
1674 g_return_if_fail(action
!= NULL
);
1676 g_free(action
->label
);