Remove useless comparison
[pidgin-git.git] / libpurple / plugin.c
blobacddd704504896f168216431509453f2b7e55cf7
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 purple_strequal(last_period, ".dll") ||
103 purple_strequal(last_period, ".sl") ||
104 purple_strequal(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->load != NULL && !plugin->info->load(plugin))
591 return FALSE;
593 else {
594 PurplePlugin *loader;
595 PurplePluginLoaderInfo *loader_info;
597 loader = find_loader_for_plugin(plugin);
599 if (loader == NULL)
600 return FALSE;
602 loader_info = PURPLE_PLUGIN_LOADER_INFO(loader);
604 if (loader_info->load != NULL)
606 if (!loader_info->load(plugin))
607 return FALSE;
611 loaded_plugins = g_list_insert_sorted(loaded_plugins, plugin, compare_plugins);
613 plugin->loaded = TRUE;
615 if (load_cb != NULL)
616 load_cb(plugin, load_cb_data);
618 purple_signal_emit(purple_plugins_get_handle(), "plugin-load", plugin);
620 return TRUE;
622 #else
623 return TRUE;
624 #endif /* !PURPLE_PLUGINS */
627 gboolean
628 purple_plugin_unload(PurplePlugin *plugin)
630 #ifdef PURPLE_PLUGINS
631 GList *l;
632 GList *ll;
634 g_return_val_if_fail(plugin != NULL, FALSE);
635 g_return_val_if_fail(purple_plugin_is_loaded(plugin), FALSE);
637 purple_debug_info("plugins", "Unloading plugin %s\n", plugin->info->name);
639 /* Unload all plugins that depend on this plugin. */
640 for (l = plugin->dependent_plugins; l != NULL; l = ll) {
641 const char * dep_name = (const char *)l->data;
642 PurplePlugin *dep_plugin;
644 /* Store a pointer to the next element in the list.
645 * This is because we'll be modifying this list in the loop. */
646 ll = l->next;
648 dep_plugin = purple_plugins_find_with_id(dep_name);
650 if (dep_plugin != NULL && purple_plugin_is_loaded(dep_plugin))
652 if (!purple_plugin_unload(dep_plugin))
654 g_free(plugin->error);
655 plugin->error = g_strdup_printf(_("%s requires %s, but it failed to unload."),
656 _(plugin->info->name),
657 _(dep_plugin->info->name));
658 return FALSE;
660 else
662 #if 0
663 /* This isn't necessary. This has already been done when unloading dep_plugin. */
664 plugin->dependent_plugins = g_list_delete_link(plugin->dependent_plugins, l);
665 #endif
670 /* Remove this plugin from each dependency's dependent_plugins list. */
671 for (l = plugin->info->dependencies; l != NULL; l = l->next)
673 const char *dep_name = (const char *)l->data;
674 PurplePlugin *dependency;
676 dependency = purple_plugins_find_with_id(dep_name);
678 if (dependency != NULL)
679 dependency->dependent_plugins = g_list_remove(dependency->dependent_plugins, plugin->info->id);
680 else
681 purple_debug_error("plugins", "Unable to remove from dependency list for %s\n", dep_name);
684 if (plugin->native_plugin) {
685 if (plugin->info->unload && !plugin->info->unload(plugin))
686 return FALSE;
688 if (plugin->info->type == PURPLE_PLUGIN_PROTOCOL) {
689 PurplePluginProtocolInfo *prpl_info;
690 GList *l;
692 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin);
694 for (l = prpl_info->user_splits; l != NULL; l = l->next)
695 purple_account_user_split_destroy(l->data);
697 for (l = prpl_info->protocol_options; l != NULL; l = l->next)
698 purple_account_option_destroy(l->data);
700 if (prpl_info->user_splits != NULL) {
701 g_list_free(prpl_info->user_splits);
702 prpl_info->user_splits = NULL;
705 if (prpl_info->protocol_options != NULL) {
706 g_list_free(prpl_info->protocol_options);
707 prpl_info->protocol_options = NULL;
710 } else {
711 PurplePlugin *loader;
712 PurplePluginLoaderInfo *loader_info;
714 loader = find_loader_for_plugin(plugin);
716 if (loader == NULL)
717 return FALSE;
719 loader_info = PURPLE_PLUGIN_LOADER_INFO(loader);
721 if (loader_info->unload && !loader_info->unload(plugin))
722 return FALSE;
725 /* cancel any pending dialogs the plugin has */
726 purple_request_close_with_handle(plugin);
727 purple_notify_close_with_handle(plugin);
729 purple_signals_disconnect_by_handle(plugin);
730 purple_plugin_ipc_unregister_all(plugin);
732 loaded_plugins = g_list_remove(loaded_plugins, plugin);
733 if ((plugin->info != NULL) && PURPLE_IS_PROTOCOL_PLUGIN(plugin))
734 protocol_plugins = g_list_remove(protocol_plugins, plugin);
735 plugins_to_disable = g_list_remove(plugins_to_disable, plugin);
736 plugin->loaded = FALSE;
738 /* We wouldn't be anywhere near here if the plugin wasn't loaded, so
739 * if plugin->error is set at all, it had to be from a previous
740 * unload failure. It's obviously okay now.
742 g_free(plugin->error);
743 plugin->error = NULL;
745 if (unload_cb != NULL)
746 unload_cb(plugin, unload_cb_data);
748 purple_signal_emit(purple_plugins_get_handle(), "plugin-unload", plugin);
750 purple_prefs_disconnect_by_handle(plugin);
752 return TRUE;
753 #else
754 return TRUE;
755 #endif /* PURPLE_PLUGINS */
758 void
759 purple_plugin_disable(PurplePlugin *plugin)
761 #ifdef PURPLE_PLUGINS
762 g_return_if_fail(plugin != NULL);
764 if (!g_list_find(plugins_to_disable, plugin))
765 plugins_to_disable = g_list_prepend(plugins_to_disable, plugin);
766 #endif
769 gboolean
770 purple_plugin_reload(PurplePlugin *plugin)
772 #ifdef PURPLE_PLUGINS
773 g_return_val_if_fail(plugin != NULL, FALSE);
774 g_return_val_if_fail(purple_plugin_is_loaded(plugin), FALSE);
776 if (!purple_plugin_unload(plugin))
777 return FALSE;
779 if (!purple_plugin_load(plugin))
780 return FALSE;
782 return TRUE;
783 #else
784 return TRUE;
785 #endif /* !PURPLE_PLUGINS */
788 void
789 purple_plugin_destroy(PurplePlugin *plugin)
791 #ifdef PURPLE_PLUGINS
792 g_return_if_fail(plugin != NULL);
794 if (purple_plugin_is_loaded(plugin))
795 purple_plugin_unload(plugin);
797 plugins = g_list_remove(plugins, plugin);
799 if (load_queue != NULL)
800 load_queue = g_list_remove(load_queue, plugin);
802 /* true, this may leak a little memory if there is a major version
803 * mismatch, but it's a lot better than trying to free something
804 * we shouldn't, and crashing while trying to load an old plugin */
805 if(plugin->info == NULL || plugin->info->magic != PURPLE_PLUGIN_MAGIC ||
806 plugin->info->major_version != PURPLE_MAJOR_VERSION)
808 if(plugin->handle)
809 g_module_close(plugin->handle);
811 g_free(plugin->path);
812 g_free(plugin->error);
814 PURPLE_DBUS_UNREGISTER_POINTER(plugin);
816 g_free(plugin);
817 return;
820 if (plugin->info != NULL)
821 g_list_free(plugin->info->dependencies);
823 if (plugin->native_plugin)
825 if (plugin->info != NULL && plugin->info->type == PURPLE_PLUGIN_LOADER)
827 PurplePluginLoaderInfo *loader_info;
828 GList *exts, *l, *next_l;
829 PurplePlugin *p2;
831 loader_info = PURPLE_PLUGIN_LOADER_INFO(plugin);
833 if (loader_info != NULL && loader_info->exts != NULL)
835 for (exts = PURPLE_PLUGIN_LOADER_INFO(plugin)->exts;
836 exts != NULL;
837 exts = exts->next) {
839 for (l = purple_plugins_get_all(); l != NULL; l = next_l)
841 next_l = l->next;
843 p2 = l->data;
845 if (p2->path != NULL &&
846 has_file_extension(p2->path, exts->data))
848 purple_plugin_destroy(p2);
853 g_list_free(loader_info->exts);
854 loader_info->exts = NULL;
857 plugin_loaders = g_list_remove(plugin_loaders, plugin);
860 if (plugin->info != NULL && plugin->info->destroy != NULL)
861 plugin->info->destroy(plugin);
864 * I find it extremely useful to do this when using valgrind, as
865 * it keeps all the plugins open, meaning that valgrind is able to
866 * resolve symbol names in leak traces from plugins.
868 if (!g_getenv("PURPLE_LEAKCHECK_HELP") && !RUNNING_ON_VALGRIND)
870 if (plugin->handle != NULL)
871 g_module_close(plugin->handle);
874 else
876 PurplePlugin *loader;
877 PurplePluginLoaderInfo *loader_info;
879 loader = find_loader_for_plugin(plugin);
881 if (loader != NULL)
883 loader_info = PURPLE_PLUGIN_LOADER_INFO(loader);
885 if (loader_info->destroy != NULL)
886 loader_info->destroy(plugin);
890 g_free(plugin->path);
891 g_free(plugin->error);
893 PURPLE_DBUS_UNREGISTER_POINTER(plugin);
895 g_free(plugin);
896 #endif /* !PURPLE_PLUGINS */
899 gboolean
900 purple_plugin_is_loaded(const PurplePlugin *plugin)
902 g_return_val_if_fail(plugin != NULL, FALSE);
904 return plugin->loaded;
907 gboolean
908 purple_plugin_is_unloadable(const PurplePlugin *plugin)
910 g_return_val_if_fail(plugin != NULL, FALSE);
912 return plugin->unloadable;
915 const gchar *
916 purple_plugin_get_id(const PurplePlugin *plugin) {
917 g_return_val_if_fail(plugin, NULL);
918 g_return_val_if_fail(plugin->info, NULL);
920 return plugin->info->id;
923 const gchar *
924 purple_plugin_get_name(const PurplePlugin *plugin) {
925 g_return_val_if_fail(plugin, NULL);
926 g_return_val_if_fail(plugin->info, NULL);
928 return _(plugin->info->name);
931 const gchar *
932 purple_plugin_get_version(const PurplePlugin *plugin) {
933 g_return_val_if_fail(plugin, NULL);
934 g_return_val_if_fail(plugin->info, NULL);
936 return plugin->info->version;
939 const gchar *
940 purple_plugin_get_summary(const PurplePlugin *plugin) {
941 g_return_val_if_fail(plugin, NULL);
942 g_return_val_if_fail(plugin->info, NULL);
944 return _(plugin->info->summary);
947 const gchar *
948 purple_plugin_get_description(const PurplePlugin *plugin) {
949 g_return_val_if_fail(plugin, NULL);
950 g_return_val_if_fail(plugin->info, NULL);
952 return _(plugin->info->description);
955 const gchar *
956 purple_plugin_get_author(const PurplePlugin *plugin) {
957 g_return_val_if_fail(plugin, NULL);
958 g_return_val_if_fail(plugin->info, NULL);
960 return _(plugin->info->author);
963 const gchar *
964 purple_plugin_get_homepage(const PurplePlugin *plugin) {
965 g_return_val_if_fail(plugin, NULL);
966 g_return_val_if_fail(plugin->info, NULL);
968 return plugin->info->homepage;
971 /**************************************************************************
972 * Plugin IPC
973 **************************************************************************/
974 static void
975 destroy_ipc_info(void *data)
977 PurplePluginIpcCommand *ipc_command = (PurplePluginIpcCommand *)data;
978 int i;
980 if (ipc_command->params != NULL)
982 for (i = 0; i < ipc_command->num_params; i++)
983 purple_value_destroy(ipc_command->params[i]);
985 g_free(ipc_command->params);
988 if (ipc_command->ret_value != NULL)
989 purple_value_destroy(ipc_command->ret_value);
991 g_free(ipc_command);
994 gboolean
995 purple_plugin_ipc_register(PurplePlugin *plugin, const char *command,
996 PurpleCallback func, PurpleSignalMarshalFunc marshal,
997 PurpleValue *ret_value, int num_params, ...)
999 PurplePluginIpcInfo *ipc_info;
1000 PurplePluginIpcCommand *ipc_command;
1002 g_return_val_if_fail(plugin != NULL, FALSE);
1003 g_return_val_if_fail(command != NULL, FALSE);
1004 g_return_val_if_fail(func != NULL, FALSE);
1005 g_return_val_if_fail(marshal != NULL, FALSE);
1007 if (plugin->ipc_data == NULL)
1009 ipc_info = plugin->ipc_data = g_new0(PurplePluginIpcInfo, 1);
1010 ipc_info->commands = g_hash_table_new_full(g_str_hash, g_str_equal,
1011 g_free, destroy_ipc_info);
1013 else
1014 ipc_info = (PurplePluginIpcInfo *)plugin->ipc_data;
1016 ipc_command = g_new0(PurplePluginIpcCommand, 1);
1017 ipc_command->func = func;
1018 ipc_command->marshal = marshal;
1019 ipc_command->num_params = num_params;
1020 ipc_command->ret_value = ret_value;
1022 if (num_params > 0)
1024 va_list args;
1025 int i;
1027 ipc_command->params = g_new0(PurpleValue *, num_params);
1029 va_start(args, num_params);
1031 for (i = 0; i < num_params; i++)
1032 ipc_command->params[i] = va_arg(args, PurpleValue *);
1034 va_end(args);
1037 g_hash_table_replace(ipc_info->commands, g_strdup(command), ipc_command);
1039 ipc_info->command_count++;
1041 return TRUE;
1044 void
1045 purple_plugin_ipc_unregister(PurplePlugin *plugin, const char *command)
1047 PurplePluginIpcInfo *ipc_info;
1049 g_return_if_fail(plugin != NULL);
1050 g_return_if_fail(command != NULL);
1052 ipc_info = (PurplePluginIpcInfo *)plugin->ipc_data;
1054 if (ipc_info == NULL ||
1055 g_hash_table_lookup(ipc_info->commands, command) == NULL)
1057 purple_debug_error("plugins",
1058 "IPC command '%s' was not registered for plugin %s\n",
1059 command, plugin->info->name);
1060 return;
1063 g_hash_table_remove(ipc_info->commands, command);
1065 ipc_info->command_count--;
1067 if (ipc_info->command_count == 0)
1069 g_hash_table_destroy(ipc_info->commands);
1070 g_free(ipc_info);
1072 plugin->ipc_data = NULL;
1076 void
1077 purple_plugin_ipc_unregister_all(PurplePlugin *plugin)
1079 PurplePluginIpcInfo *ipc_info;
1081 g_return_if_fail(plugin != NULL);
1083 if (plugin->ipc_data == NULL)
1084 return; /* Silently ignore it. */
1086 ipc_info = (PurplePluginIpcInfo *)plugin->ipc_data;
1088 g_hash_table_destroy(ipc_info->commands);
1089 g_free(ipc_info);
1091 plugin->ipc_data = NULL;
1094 gboolean
1095 purple_plugin_ipc_get_params(PurplePlugin *plugin, const char *command,
1096 PurpleValue **ret_value, int *num_params,
1097 PurpleValue ***params)
1099 PurplePluginIpcInfo *ipc_info;
1100 PurplePluginIpcCommand *ipc_command;
1102 g_return_val_if_fail(plugin != NULL, FALSE);
1103 g_return_val_if_fail(command != NULL, FALSE);
1105 ipc_info = (PurplePluginIpcInfo *)plugin->ipc_data;
1107 if (ipc_info == NULL ||
1108 (ipc_command = g_hash_table_lookup(ipc_info->commands,
1109 command)) == NULL)
1111 purple_debug_error("plugins",
1112 "IPC command '%s' was not registered for plugin %s\n",
1113 command, plugin->info->name);
1115 return FALSE;
1118 if (num_params != NULL)
1119 *num_params = ipc_command->num_params;
1121 if (params != NULL)
1122 *params = ipc_command->params;
1124 if (ret_value != NULL)
1125 *ret_value = ipc_command->ret_value;
1127 return TRUE;
1130 void *
1131 purple_plugin_ipc_call(PurplePlugin *plugin, const char *command,
1132 gboolean *ok, ...)
1134 PurplePluginIpcInfo *ipc_info;
1135 PurplePluginIpcCommand *ipc_command;
1136 va_list args;
1137 void *ret_value;
1139 if (ok != NULL)
1140 *ok = FALSE;
1142 g_return_val_if_fail(plugin != NULL, NULL);
1143 g_return_val_if_fail(command != NULL, NULL);
1145 ipc_info = (PurplePluginIpcInfo *)plugin->ipc_data;
1147 if (ipc_info == NULL ||
1148 (ipc_command = g_hash_table_lookup(ipc_info->commands,
1149 command)) == NULL)
1151 purple_debug_error("plugins",
1152 "IPC command '%s' was not registered for plugin %s\n",
1153 command, plugin->info->name);
1155 return NULL;
1158 va_start(args, ok);
1159 ipc_command->marshal(ipc_command->func, args, NULL, &ret_value);
1160 va_end(args);
1162 if (ok != NULL)
1163 *ok = TRUE;
1165 return ret_value;
1168 /**************************************************************************
1169 * Plugins subsystem
1170 **************************************************************************/
1171 void *
1172 purple_plugins_get_handle(void) {
1173 static int handle;
1175 return &handle;
1178 void
1179 purple_plugins_init(void) {
1180 void *handle = purple_plugins_get_handle();
1182 purple_plugins_add_search_path(LIBDIR);
1184 purple_signal_register(handle, "plugin-load",
1185 purple_marshal_VOID__POINTER,
1186 NULL, 1,
1187 purple_value_new(PURPLE_TYPE_SUBTYPE,
1188 PURPLE_SUBTYPE_PLUGIN));
1189 purple_signal_register(handle, "plugin-unload",
1190 purple_marshal_VOID__POINTER,
1191 NULL, 1,
1192 purple_value_new(PURPLE_TYPE_SUBTYPE,
1193 PURPLE_SUBTYPE_PLUGIN));
1196 void
1197 purple_plugins_uninit(void)
1199 void *handle = purple_plugins_get_handle();
1201 purple_signals_disconnect_by_handle(handle);
1202 purple_signals_unregister_by_instance(handle);
1204 while (search_paths) {
1205 g_free(search_paths->data);
1206 search_paths = g_list_delete_link(search_paths, search_paths);
1210 /**************************************************************************
1211 * Plugins API
1212 **************************************************************************/
1213 void
1214 purple_plugins_add_search_path(const char *path)
1216 g_return_if_fail(path != NULL);
1218 if (g_list_find_custom(search_paths, path, (GCompareFunc)strcmp))
1219 return;
1221 search_paths = g_list_append(search_paths, g_strdup(path));
1224 GList *
1225 purple_plugins_get_search_paths()
1227 return search_paths;
1230 void
1231 purple_plugins_unload_all(void)
1233 #ifdef PURPLE_PLUGINS
1235 while (loaded_plugins != NULL)
1236 purple_plugin_unload(loaded_plugins->data);
1238 #endif /* PURPLE_PLUGINS */
1241 void
1242 purple_plugins_unload(PurplePluginType type)
1244 #ifdef PURPLE_PLUGINS
1245 GList *l;
1247 for (l = plugins; l; l = l->next) {
1248 PurplePlugin *plugin = l->data;
1249 if (plugin->info->type == type && purple_plugin_is_loaded(plugin))
1250 purple_plugin_unload(plugin);
1253 #endif /* PURPLE_PLUGINS */
1256 void
1257 purple_plugins_destroy_all(void)
1259 #ifdef PURPLE_PLUGINS
1261 while (plugins != NULL)
1262 purple_plugin_destroy(plugins->data);
1264 #endif /* PURPLE_PLUGINS */
1267 void
1268 purple_plugins_save_loaded(const char *key)
1270 #ifdef PURPLE_PLUGINS
1271 GList *pl;
1272 GList *files = NULL;
1274 for (pl = purple_plugins_get_loaded(); pl != NULL; pl = pl->next) {
1275 PurplePlugin *plugin = pl->data;
1277 if (plugin->info->type != PURPLE_PLUGIN_PROTOCOL &&
1278 plugin->info->type != PURPLE_PLUGIN_LOADER &&
1279 !g_list_find(plugins_to_disable, plugin)) {
1280 files = g_list_append(files, plugin->path);
1284 purple_prefs_set_path_list(key, files);
1285 g_list_free(files);
1286 #endif
1289 void
1290 purple_plugins_load_saved(const char *key)
1292 #ifdef PURPLE_PLUGINS
1293 GList *f, *files;
1295 g_return_if_fail(key != NULL);
1297 files = purple_prefs_get_path_list(key);
1299 for (f = files; f; f = f->next)
1301 char *filename;
1302 char *basename;
1303 PurplePlugin *plugin;
1305 if (f->data == NULL)
1306 continue;
1308 filename = f->data;
1311 * We don't know if the filename uses Windows or Unix path
1312 * separators (because people might be sharing a prefs.xml
1313 * file across systems), so we find the last occurrence
1314 * of either.
1316 basename = strrchr(filename, '/');
1317 if ((basename == NULL) || (basename < strrchr(filename, '\\')))
1318 basename = strrchr(filename, '\\');
1319 if (basename != NULL)
1320 basename++;
1322 /* Strip the extension */
1323 if (basename)
1324 basename = purple_plugin_get_basename(basename);
1326 if (((plugin = purple_plugins_find_with_filename(filename)) != NULL) ||
1327 (basename && (plugin = purple_plugins_find_with_basename(basename)) != NULL) ||
1328 ((plugin = purple_plugin_probe(filename)) != NULL))
1330 purple_debug_info("plugins", "Loading saved plugin %s\n",
1331 plugin->path);
1332 purple_plugin_load(plugin);
1334 else
1336 purple_debug_error("plugins", "Unable to find saved plugin %s\n",
1337 filename);
1340 g_free(basename);
1342 g_free(f->data);
1345 g_list_free(files);
1346 #endif /* PURPLE_PLUGINS */
1350 void
1351 purple_plugins_probe(const char *ext)
1353 #ifdef PURPLE_PLUGINS
1354 GDir *dir;
1355 const gchar *file;
1356 gchar *path;
1357 PurplePlugin *plugin;
1358 GList *cur;
1359 const char *search_path;
1361 if (!g_module_supported())
1362 return;
1364 /* Probe plugins */
1365 for (cur = search_paths; cur != NULL; cur = cur->next)
1367 search_path = cur->data;
1369 dir = g_dir_open(search_path, 0, NULL);
1371 if (dir != NULL)
1373 while ((file = g_dir_read_name(dir)) != NULL)
1375 path = g_build_filename(search_path, file, NULL);
1377 if (ext == NULL || has_file_extension(file, ext))
1378 purple_plugin_probe(path);
1380 g_free(path);
1383 g_dir_close(dir);
1387 /* See if we have any plugins waiting to load */
1388 while (load_queue != NULL)
1390 plugin = (PurplePlugin *)load_queue->data;
1392 load_queue = g_list_remove(load_queue, plugin);
1394 if (plugin == NULL || plugin->info == NULL)
1395 continue;
1397 if (plugin->info->type == PURPLE_PLUGIN_LOADER)
1399 /* We'll just load this right now. */
1400 if (!purple_plugin_load(plugin))
1402 purple_plugin_destroy(plugin);
1404 continue;
1407 plugin_loaders = g_list_append(plugin_loaders, plugin);
1409 for (cur = PURPLE_PLUGIN_LOADER_INFO(plugin)->exts;
1410 cur != NULL;
1411 cur = cur->next)
1413 purple_plugins_probe(cur->data);
1416 else if (plugin->info->type == PURPLE_PLUGIN_PROTOCOL)
1418 /* We'll just load this right now. */
1419 if (!purple_plugin_load(plugin))
1421 purple_plugin_destroy(plugin);
1423 continue;
1426 /* Make sure we don't load two PRPLs with the same name? */
1427 if (purple_find_prpl(plugin->info->id))
1429 /* Nothing to see here--move along, move along */
1430 purple_plugin_destroy(plugin);
1432 continue;
1435 protocol_plugins = g_list_insert_sorted(protocol_plugins, plugin,
1436 (GCompareFunc)compare_prpl);
1440 if (probe_cb != NULL)
1441 probe_cb(probe_cb_data);
1443 #endif /* PURPLE_PLUGINS */
1446 gboolean
1447 purple_plugin_register(PurplePlugin *plugin)
1449 g_return_val_if_fail(plugin != NULL, FALSE);
1451 /* If this plugin has been registered already then exit */
1452 if (g_list_find(plugins, plugin))
1453 return TRUE;
1455 /* Ensure the plugin has the requisite information */
1456 if (plugin->info->type == PURPLE_PLUGIN_LOADER)
1458 PurplePluginLoaderInfo *loader_info;
1460 loader_info = PURPLE_PLUGIN_LOADER_INFO(plugin);
1462 if (loader_info == NULL)
1464 purple_debug_error("plugins", "%s is not loadable, loader plugin missing loader_info\n",
1465 plugin->path);
1466 return FALSE;
1469 else if (plugin->info->type == PURPLE_PLUGIN_PROTOCOL)
1471 PurplePluginProtocolInfo *prpl_info;
1473 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin);
1475 if (prpl_info == NULL)
1477 purple_debug_error("plugins", "%s is not loadable, protocol plugin missing prpl_info\n",
1478 plugin->path);
1479 return FALSE;
1483 #ifdef PURPLE_PLUGINS
1484 /* This plugin should be probed and maybe loaded--add it to the queue */
1485 load_queue = g_list_append(load_queue, plugin);
1486 #else
1487 if (plugin->info != NULL)
1489 if (plugin->info->type == PURPLE_PLUGIN_PROTOCOL)
1490 protocol_plugins = g_list_insert_sorted(protocol_plugins, plugin,
1491 (GCompareFunc)compare_prpl);
1492 if (plugin->info->load != NULL)
1493 if (!plugin->info->load(plugin))
1494 return FALSE;
1496 #endif
1498 plugins = g_list_append(plugins, plugin);
1500 return TRUE;
1503 gboolean
1504 purple_plugins_enabled(void)
1506 #ifdef PURPLE_PLUGINS
1507 return TRUE;
1508 #else
1509 return FALSE;
1510 #endif
1513 void
1514 purple_plugins_register_probe_notify_cb(void (*func)(void *), void *data)
1516 probe_cb = func;
1517 probe_cb_data = data;
1520 void
1521 purple_plugins_unregister_probe_notify_cb(void (*func)(void *))
1523 probe_cb = NULL;
1524 probe_cb_data = NULL;
1527 void
1528 purple_plugins_register_load_notify_cb(void (*func)(PurplePlugin *, void *),
1529 void *data)
1531 load_cb = func;
1532 load_cb_data = data;
1535 void
1536 purple_plugins_unregister_load_notify_cb(void (*func)(PurplePlugin *, void *))
1538 load_cb = NULL;
1539 load_cb_data = NULL;
1542 void
1543 purple_plugins_register_unload_notify_cb(void (*func)(PurplePlugin *, void *),
1544 void *data)
1546 unload_cb = func;
1547 unload_cb_data = data;
1550 void
1551 purple_plugins_unregister_unload_notify_cb(void (*func)(PurplePlugin *, void *))
1553 unload_cb = NULL;
1554 unload_cb_data = NULL;
1557 PurplePlugin *
1558 purple_plugins_find_with_name(const char *name)
1560 PurplePlugin *plugin;
1561 GList *l;
1563 for (l = plugins; l != NULL; l = l->next) {
1564 plugin = l->data;
1566 if (purple_strequal(plugin->info->name, name))
1567 return plugin;
1570 return NULL;
1573 PurplePlugin *
1574 purple_plugins_find_with_filename(const char *filename)
1576 PurplePlugin *plugin;
1577 GList *l;
1579 for (l = plugins; l != NULL; l = l->next) {
1580 plugin = l->data;
1582 if (purple_strequal(plugin->path, filename))
1583 return plugin;
1586 return NULL;
1589 PurplePlugin *
1590 purple_plugins_find_with_basename(const char *basename)
1592 #ifdef PURPLE_PLUGINS
1593 PurplePlugin *plugin;
1594 GList *l;
1595 char *tmp;
1597 g_return_val_if_fail(basename != NULL, NULL);
1599 for (l = plugins; l != NULL; l = l->next)
1601 plugin = (PurplePlugin *)l->data;
1603 if (plugin->path != NULL) {
1604 tmp = purple_plugin_get_basename(plugin->path);
1605 if (purple_strequal(tmp, basename))
1607 g_free(tmp);
1608 return plugin;
1610 g_free(tmp);
1614 #endif /* PURPLE_PLUGINS */
1616 return NULL;
1619 PurplePlugin *
1620 purple_plugins_find_with_id(const char *id)
1622 PurplePlugin *plugin;
1623 GList *l;
1625 g_return_val_if_fail(id != NULL, NULL);
1627 for (l = plugins; l != NULL; l = l->next)
1629 plugin = l->data;
1631 if (purple_strequal(plugin->info->id, id))
1632 return plugin;
1635 return NULL;
1638 GList *
1639 purple_plugins_get_loaded(void)
1641 return loaded_plugins;
1644 GList *
1645 purple_plugins_get_protocols(void)
1647 return protocol_plugins;
1650 GList *
1651 purple_plugins_get_all(void)
1653 return plugins;
1657 PurplePluginAction *
1658 purple_plugin_action_new(const char* label, void (*callback)(PurplePluginAction *))
1660 PurplePluginAction *act = g_new0(PurplePluginAction, 1);
1662 act->label = g_strdup(label);
1663 act->callback = callback;
1665 return act;
1668 void
1669 purple_plugin_action_free(PurplePluginAction *action)
1671 g_return_if_fail(action != NULL);
1673 g_free(action->label);
1674 g_free(action);