merge of 'd03c32ee4bd5625c28af8c8b33920944d499eef6'
[pidgin-git.git] / libpurple / plugin.c
blob4f2b4023aba907e5374976fc33dff18f00738256
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 #define _PURPLE_PLUGIN_C_
24 #include "internal.h"
26 #include "accountopt.h"
27 #include "core.h"
28 #include "dbus-maybe.h"
29 #include "debug.h"
30 #include "notify.h"
31 #include "prefs.h"
32 #include "prpl.h"
33 #include "request.h"
34 #include "signals.h"
35 #include "util.h"
36 #include "valgrind.h"
37 #include "version.h"
39 typedef struct
41 GHashTable *commands;
42 size_t command_count;
44 } PurplePluginIpcInfo;
46 typedef struct
48 PurpleCallback func;
49 PurpleSignalMarshalFunc marshal;
51 int num_params;
52 PurpleValue **params;
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;
61 #ifdef PURPLE_PLUGINS
62 static GList *load_queue = NULL;
63 static GList *plugin_loaders = NULL;
64 static GList *plugins_to_disable = NULL;
65 #endif
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;
74 #ifdef PURPLE_PLUGINS
76 static gboolean
77 has_file_extension(const char *filename, const char *ext)
79 int len, extlen;
81 if (filename == NULL || *filename == '\0' || ext == NULL)
82 return 0;
84 extlen = strlen(ext);
85 len = strlen(filename) - extlen;
87 if (len < 0)
88 return 0;
90 return (strncmp(filename + len, ext, extlen) == 0);
93 static gboolean
94 is_native(const char *filename)
96 const char *last_period;
98 last_period = strrchr(filename, '.');
99 if (last_period == NULL)
100 return FALSE;
102 return !(strcmp(last_period, ".dll") &
103 strcmp(last_period, ".sl") &
104 strcmp(last_period, ".so"));
107 static char *
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)
115 basename++;
116 else
117 basename = filename;
119 if (is_native(basename) &&
120 ((last_period = strrchr(basename, '.')) != NULL))
121 return g_strndup(basename, (last_period - basename));
123 return g_strdup(basename);
126 static gboolean
127 loader_supports_file(PurplePlugin *loader, const char *filename)
129 GList *exts;
131 for (exts = PURPLE_PLUGIN_LOADER_INFO(loader)->exts; exts != NULL; exts = exts->next) {
132 if (has_file_extension(filename, (char *)exts->data)) {
133 return TRUE;
137 return FALSE;
140 static PurplePlugin *
141 find_loader_for_plugin(const PurplePlugin *plugin)
143 PurplePlugin *loader;
144 GList *l;
146 if (plugin->path == NULL)
147 return NULL;
149 for (l = purple_plugins_get_loaded(); l != NULL; l = l->next) {
150 loader = l->data;
152 if (loader->info->type == PURPLE_PLUGIN_LOADER &&
153 loader_supports_file(loader, plugin->path)) {
155 return loader;
158 loader = NULL;
161 return NULL;
164 #endif /* PURPLE_PLUGINS */
167 * Negative if a before b, 0 if equal, positive if a after b.
169 static gint
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);
175 else
176 return -1;
177 } else {
178 if(PURPLE_IS_PROTOCOL_PLUGIN(b))
179 return 1;
180 else
181 return 0;
185 PurplePlugin *
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);
197 return plugin;
200 PurplePlugin *
201 purple_plugin_probe(const char *filename)
203 #ifdef PURPLE_PLUGINS
204 PurplePlugin *plugin = NULL;
205 PurplePlugin *loader;
206 gpointer unpunned;
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))
214 return NULL;
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);
219 g_free(basename);
220 if (plugin != NULL)
222 if (purple_strequal(filename, plugin->path))
223 return plugin;
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);
229 return plugin;
231 else
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) {
245 const char *error;
246 #ifdef _WIN32
247 /* Suppress error popups for failing to load plugins */
248 UINT old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
249 #endif
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. */
268 if (*error == ':')
269 error++;
270 if (*error == ' ')
271 error++;
274 if (error == NULL || !*error)
276 plugin->error = g_strdup(_("Unknown error"));
277 purple_debug_error("plugins", "%s is not loadable: Unknown error\n",
278 plugin->path);
280 else
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)
290 #ifdef _WIN32
291 /* Restore the original error mode */
292 SetErrorMode(old_error_mode);
293 #endif
294 purple_plugin_destroy(plugin);
295 return NULL;
297 else
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",
308 &unpunned))
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();
317 if (error != NULL)
318 purple_debug_error("plugins", "Error closing module %s: %s\n",
319 plugin->path, error);
320 plugin->handle = NULL;
322 #ifdef _WIN32
323 /* Restore the original error mode */
324 SetErrorMode(old_error_mode);
325 #endif
326 purple_plugin_destroy(plugin);
327 return NULL;
329 purple_init_plugin = unpunned;
331 #ifdef _WIN32
332 /* Restore the original error mode */
333 SetErrorMode(old_error_mode);
334 #endif
336 else {
337 loader = find_loader_for_plugin(plugin);
339 if (loader == NULL) {
340 purple_plugin_destroy(plugin);
341 return NULL;
344 purple_init_plugin = PURPLE_PLUGIN_LOADER_INFO(loader)->probe;
347 if (!purple_init_plugin(plugin) || plugin->info == NULL)
349 purple_plugin_destroy(plugin);
350 return NULL;
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;
359 return plugin;
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;
373 return plugin;
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;
386 unsigned long flags;
387 GList *dependencies;
388 PurplePluginPriority priority;
390 char *id;
391 char *name;
392 char *version;
393 char *summary;
394 char *description;
395 char *author;
396 char *homepage;
398 gboolean (*load)(PurplePlugin *plugin);
399 gboolean (*unload)(PurplePlugin *plugin);
400 void (*destroy)(PurplePlugin *plugin);
402 void *ui_info;
403 void *extra_info;
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;
443 return plugin;
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);
449 return NULL;
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;
462 return plugin;
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;
476 return plugin;
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",
483 plugin->path);
487 return plugin;
488 #else
489 return NULL;
490 #endif /* !PURPLE_PLUGINS */
493 #ifdef PURPLE_PLUGINS
494 static gint
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 */
504 gboolean
505 purple_plugin_load(PurplePlugin *plugin)
507 #ifdef PURPLE_PLUGINS
508 GList *dep_list = NULL;
509 GList *l;
511 g_return_val_if_fail(plugin != NULL, FALSE);
513 if (purple_plugin_is_loaded(plugin))
514 return TRUE;
516 if (purple_plugin_is_unloadable(plugin))
517 return FALSE;
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)
535 char *tmp;
537 tmp = g_strdup_printf(_("The required plugin %s was not found. "
538 "Please install this plugin and try again."),
539 dep_name);
541 purple_notify_error(NULL, NULL,
542 _("Unable to load the plugin"), tmp);
543 g_free(tmp);
545 g_list_free(dep_list);
547 return FALSE;
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))
562 char *tmp;
564 tmp = g_strdup_printf(_("The required plugin %s was unable to load."),
565 plugin->info->name);
567 purple_notify_error(NULL, NULL,
568 _("Unable to load your plugin."), tmp);
569 g_free(tmp);
571 g_list_free(dep_list);
573 return FALSE;
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))
593 return FALSE;
596 else {
597 PurplePlugin *loader;
598 PurplePluginLoaderInfo *loader_info;
600 loader = find_loader_for_plugin(plugin);
602 if (loader == NULL)
603 return FALSE;
605 loader_info = PURPLE_PLUGIN_LOADER_INFO(loader);
607 if (loader_info->load != NULL)
609 if (!loader_info->load(plugin))
610 return FALSE;
614 loaded_plugins = g_list_insert_sorted(loaded_plugins, plugin, compare_plugins);
616 plugin->loaded = TRUE;
618 if (load_cb != NULL)
619 load_cb(plugin, load_cb_data);
621 purple_signal_emit(purple_plugins_get_handle(), "plugin-load", plugin);
623 return TRUE;
625 #else
626 return TRUE;
627 #endif /* !PURPLE_PLUGINS */
630 gboolean
631 purple_plugin_unload(PurplePlugin *plugin)
633 #ifdef PURPLE_PLUGINS
634 GList *l;
635 GList *ll;
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. */
649 ll = l->next;
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));
661 return FALSE;
663 else
665 #if 0
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);
668 #endif
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);
683 else
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))
689 return FALSE;
691 if (plugin->info->type == PURPLE_PLUGIN_PROTOCOL) {
692 PurplePluginProtocolInfo *prpl_info;
693 GList *l;
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;
713 } else {
714 PurplePlugin *loader;
715 PurplePluginLoaderInfo *loader_info;
717 loader = find_loader_for_plugin(plugin);
719 if (loader == NULL)
720 return FALSE;
722 loader_info = PURPLE_PLUGIN_LOADER_INFO(loader);
724 if (loader_info->unload && !loader_info->unload(plugin))
725 return FALSE;
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);
755 return TRUE;
756 #else
757 return TRUE;
758 #endif /* PURPLE_PLUGINS */
761 void
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);
769 #endif
772 gboolean
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))
780 return FALSE;
782 if (!purple_plugin_load(plugin))
783 return FALSE;
785 return TRUE;
786 #else
787 return TRUE;
788 #endif /* !PURPLE_PLUGINS */
791 void
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)
811 if(plugin->handle)
812 g_module_close(plugin->handle);
814 g_free(plugin->path);
815 g_free(plugin->error);
817 PURPLE_DBUS_UNREGISTER_POINTER(plugin);
819 g_free(plugin);
820 return;
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;
832 PurplePlugin *p2;
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;
839 exts != NULL;
840 exts = exts->next) {
842 for (l = purple_plugins_get_all(); l != NULL; l = next_l)
844 next_l = l->next;
846 p2 = l->data;
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);
877 else
879 PurplePlugin *loader;
880 PurplePluginLoaderInfo *loader_info;
882 loader = find_loader_for_plugin(plugin);
884 if (loader != NULL)
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);
898 g_free(plugin);
899 #endif /* !PURPLE_PLUGINS */
902 gboolean
903 purple_plugin_is_loaded(const PurplePlugin *plugin)
905 g_return_val_if_fail(plugin != NULL, FALSE);
907 return plugin->loaded;
910 gboolean
911 purple_plugin_is_unloadable(const PurplePlugin *plugin)
913 g_return_val_if_fail(plugin != NULL, FALSE);
915 return plugin->unloadable;
918 const gchar *
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;
926 const gchar *
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);
934 const gchar *
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;
942 const gchar *
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);
950 const gchar *
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);
958 const gchar *
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);
966 const gchar *
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 /**************************************************************************
975 * Plugin IPC
976 **************************************************************************/
977 static void
978 destroy_ipc_info(void *data)
980 PurplePluginIpcCommand *ipc_command = (PurplePluginIpcCommand *)data;
981 int i;
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);
994 g_free(ipc_command);
997 gboolean
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);
1016 else
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;
1025 if (num_params > 0)
1027 va_list args;
1028 int i;
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 *);
1037 va_end(args);
1040 g_hash_table_replace(ipc_info->commands, g_strdup(command), ipc_command);
1042 ipc_info->command_count++;
1044 return TRUE;
1047 void
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);
1063 return;
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);
1073 g_free(ipc_info);
1075 plugin->ipc_data = NULL;
1079 void
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);
1092 g_free(ipc_info);
1094 plugin->ipc_data = NULL;
1097 gboolean
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,
1112 command)) == NULL)
1114 purple_debug_error("plugins",
1115 "IPC command '%s' was not registered for plugin %s\n",
1116 command, plugin->info->name);
1118 return FALSE;
1121 if (num_params != NULL)
1122 *num_params = ipc_command->num_params;
1124 if (params != NULL)
1125 *params = ipc_command->params;
1127 if (ret_value != NULL)
1128 *ret_value = ipc_command->ret_value;
1130 return TRUE;
1133 void *
1134 purple_plugin_ipc_call(PurplePlugin *plugin, const char *command,
1135 gboolean *ok, ...)
1137 PurplePluginIpcInfo *ipc_info;
1138 PurplePluginIpcCommand *ipc_command;
1139 va_list args;
1140 void *ret_value;
1142 if (ok != NULL)
1143 *ok = FALSE;
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,
1152 command)) == NULL)
1154 purple_debug_error("plugins",
1155 "IPC command '%s' was not registered for plugin %s\n",
1156 command, plugin->info->name);
1158 return NULL;
1161 va_start(args, ok);
1162 ipc_command->marshal(ipc_command->func, args, NULL, &ret_value);
1163 va_end(args);
1165 if (ok != NULL)
1166 *ok = TRUE;
1168 return ret_value;
1171 /**************************************************************************
1172 * Plugins subsystem
1173 **************************************************************************/
1174 void *
1175 purple_plugins_get_handle(void) {
1176 static int handle;
1178 return &handle;
1181 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,
1189 NULL, 1,
1190 purple_value_new(PURPLE_TYPE_SUBTYPE,
1191 PURPLE_SUBTYPE_PLUGIN));
1192 purple_signal_register(handle, "plugin-unload",
1193 purple_marshal_VOID__POINTER,
1194 NULL, 1,
1195 purple_value_new(PURPLE_TYPE_SUBTYPE,
1196 PURPLE_SUBTYPE_PLUGIN));
1199 void
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 /**************************************************************************
1214 * Plugins API
1215 **************************************************************************/
1216 void
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))
1222 return;
1224 search_paths = g_list_append(search_paths, g_strdup(path));
1227 GList *
1228 purple_plugins_get_search_paths()
1230 return search_paths;
1233 void
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 */
1244 void
1245 purple_plugins_unload(PurplePluginType type)
1247 #ifdef PURPLE_PLUGINS
1248 GList *l;
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 */
1259 void
1260 purple_plugins_destroy_all(void)
1262 #ifdef PURPLE_PLUGINS
1264 while (plugins != NULL)
1265 purple_plugin_destroy(plugins->data);
1267 #endif /* PURPLE_PLUGINS */
1270 void
1271 purple_plugins_save_loaded(const char *key)
1273 #ifdef PURPLE_PLUGINS
1274 GList *pl;
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);
1288 g_list_free(files);
1289 #endif
1292 void
1293 purple_plugins_load_saved(const char *key)
1295 #ifdef PURPLE_PLUGINS
1296 GList *f, *files;
1298 g_return_if_fail(key != NULL);
1300 files = purple_prefs_get_path_list(key);
1302 for (f = files; f; f = f->next)
1304 char *filename;
1305 char *basename;
1306 PurplePlugin *plugin;
1308 if (f->data == NULL)
1309 continue;
1311 filename = f->data;
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
1317 * of either.
1319 basename = strrchr(filename, '/');
1320 if ((basename == NULL) || (basename < strrchr(filename, '\\')))
1321 basename = strrchr(filename, '\\');
1322 if (basename != NULL)
1323 basename++;
1325 /* Strip the extension */
1326 if (basename)
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",
1334 plugin->path);
1335 purple_plugin_load(plugin);
1337 else
1339 purple_debug_error("plugins", "Unable to find saved plugin %s\n",
1340 filename);
1343 g_free(basename);
1345 g_free(f->data);
1348 g_list_free(files);
1349 #endif /* PURPLE_PLUGINS */
1353 void
1354 purple_plugins_probe(const char *ext)
1356 #ifdef PURPLE_PLUGINS
1357 GDir *dir;
1358 const gchar *file;
1359 gchar *path;
1360 PurplePlugin *plugin;
1361 GList *cur;
1362 const char *search_path;
1364 if (!g_module_supported())
1365 return;
1367 /* Probe plugins */
1368 for (cur = search_paths; cur != NULL; cur = cur->next)
1370 search_path = cur->data;
1372 dir = g_dir_open(search_path, 0, NULL);
1374 if (dir != 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);
1383 g_free(path);
1386 g_dir_close(dir);
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)
1398 continue;
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);
1407 continue;
1410 plugin_loaders = g_list_append(plugin_loaders, plugin);
1412 for (cur = PURPLE_PLUGIN_LOADER_INFO(plugin)->exts;
1413 cur != NULL;
1414 cur = cur->next)
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);
1426 continue;
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);
1435 continue;
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 */
1449 gboolean
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))
1456 return TRUE;
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",
1468 plugin->path);
1469 return FALSE;
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",
1481 plugin->path);
1482 return FALSE;
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);
1489 #else
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))
1497 return FALSE;
1499 #endif
1501 plugins = g_list_append(plugins, plugin);
1503 return TRUE;
1506 gboolean
1507 purple_plugins_enabled(void)
1509 #ifdef PURPLE_PLUGINS
1510 return TRUE;
1511 #else
1512 return FALSE;
1513 #endif
1516 void
1517 purple_plugins_register_probe_notify_cb(void (*func)(void *), void *data)
1519 probe_cb = func;
1520 probe_cb_data = data;
1523 void
1524 purple_plugins_unregister_probe_notify_cb(void (*func)(void *))
1526 probe_cb = NULL;
1527 probe_cb_data = NULL;
1530 void
1531 purple_plugins_register_load_notify_cb(void (*func)(PurplePlugin *, void *),
1532 void *data)
1534 load_cb = func;
1535 load_cb_data = data;
1538 void
1539 purple_plugins_unregister_load_notify_cb(void (*func)(PurplePlugin *, void *))
1541 load_cb = NULL;
1542 load_cb_data = NULL;
1545 void
1546 purple_plugins_register_unload_notify_cb(void (*func)(PurplePlugin *, void *),
1547 void *data)
1549 unload_cb = func;
1550 unload_cb_data = data;
1553 void
1554 purple_plugins_unregister_unload_notify_cb(void (*func)(PurplePlugin *, void *))
1556 unload_cb = NULL;
1557 unload_cb_data = NULL;
1560 PurplePlugin *
1561 purple_plugins_find_with_name(const char *name)
1563 PurplePlugin *plugin;
1564 GList *l;
1566 for (l = plugins; l != NULL; l = l->next) {
1567 plugin = l->data;
1569 if (purple_strequal(plugin->info->name, name))
1570 return plugin;
1573 return NULL;
1576 PurplePlugin *
1577 purple_plugins_find_with_filename(const char *filename)
1579 PurplePlugin *plugin;
1580 GList *l;
1582 for (l = plugins; l != NULL; l = l->next) {
1583 plugin = l->data;
1585 if (purple_strequal(plugin->path, filename))
1586 return plugin;
1589 return NULL;
1592 PurplePlugin *
1593 purple_plugins_find_with_basename(const char *basename)
1595 #ifdef PURPLE_PLUGINS
1596 PurplePlugin *plugin;
1597 GList *l;
1598 char *tmp;
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))
1610 g_free(tmp);
1611 return plugin;
1613 g_free(tmp);
1617 #endif /* PURPLE_PLUGINS */
1619 return NULL;
1622 PurplePlugin *
1623 purple_plugins_find_with_id(const char *id)
1625 PurplePlugin *plugin;
1626 GList *l;
1628 g_return_val_if_fail(id != NULL, NULL);
1630 for (l = plugins; l != NULL; l = l->next)
1632 plugin = l->data;
1634 if (purple_strequal(plugin->info->id, id))
1635 return plugin;
1638 return NULL;
1641 GList *
1642 purple_plugins_get_loaded(void)
1644 return loaded_plugins;
1647 GList *
1648 purple_plugins_get_protocols(void)
1650 return protocol_plugins;
1653 GList *
1654 purple_plugins_get_all(void)
1656 return plugins;
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;
1668 return act;
1671 void
1672 purple_plugin_action_free(PurplePluginAction *action)
1674 g_return_if_fail(action != NULL);
1676 g_free(action->label);
1677 g_free(action);