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"
43 } PurplePluginIpcInfo
;
48 PurpleSignalMarshalFunc marshal
;
52 PurpleValue
*ret_value
;
54 } PurplePluginIpcCommand
;
56 static GList
*search_paths
= NULL
;
57 static GList
*plugins
= NULL
;
58 static GList
*loaded_plugins
= NULL
;
59 static GList
*protocol_plugins
= NULL
;
61 static GList
*load_queue
= NULL
;
62 static GList
*plugin_loaders
= NULL
;
63 static GList
*plugins_to_disable
= NULL
;
66 static void (*probe_cb
)(void *) = NULL
;
67 static void *probe_cb_data
= NULL
;
68 static void (*load_cb
)(PurplePlugin
*, void *) = NULL
;
69 static void *load_cb_data
= NULL
;
70 static void (*unload_cb
)(PurplePlugin
*, void *) = NULL
;
71 static void *unload_cb_data
= NULL
;
76 has_file_extension(const char *filename
, const char *ext
)
80 if (filename
== NULL
|| *filename
== '\0' || ext
== NULL
)
84 len
= strlen(filename
) - extlen
;
89 return (strncmp(filename
+ len
, ext
, extlen
) == 0);
93 is_native(const char *filename
)
95 const char *last_period
;
97 last_period
= strrchr(filename
, '.');
98 if (last_period
== NULL
)
101 return !(strcmp(last_period
, ".dll") &
102 strcmp(last_period
, ".sl") &
103 strcmp(last_period
, ".so"));
107 purple_plugin_get_basename(const char *filename
)
109 const char *basename
;
110 const char *last_period
;
112 basename
= strrchr(filename
, G_DIR_SEPARATOR
);
113 if (basename
!= NULL
)
118 if (is_native(basename
) &&
119 ((last_period
= strrchr(basename
, '.')) != NULL
))
120 return g_strndup(basename
, (last_period
- basename
));
122 return g_strdup(basename
);
126 loader_supports_file(PurplePlugin
*loader
, const char *filename
)
130 for (exts
= PURPLE_PLUGIN_LOADER_INFO(loader
)->exts
; exts
!= NULL
; exts
= exts
->next
) {
131 if (has_file_extension(filename
, (char *)exts
->data
)) {
139 static PurplePlugin
*
140 find_loader_for_plugin(const PurplePlugin
*plugin
)
142 PurplePlugin
*loader
;
145 if (plugin
->path
== NULL
)
148 for (l
= purple_plugins_get_loaded(); l
!= NULL
; l
= l
->next
) {
151 if (loader
->info
->type
== PURPLE_PLUGIN_LOADER
&&
152 loader_supports_file(loader
, plugin
->path
)) {
163 #endif /* PURPLE_PLUGINS */
166 * Negative if a before b, 0 if equal, positive if a after b.
169 compare_prpl(PurplePlugin
*a
, PurplePlugin
*b
)
171 if(PURPLE_IS_PROTOCOL_PLUGIN(a
)) {
172 if(PURPLE_IS_PROTOCOL_PLUGIN(b
))
173 return strcmp(a
->info
->name
, b
->info
->name
);
177 if(PURPLE_IS_PROTOCOL_PLUGIN(b
))
185 purple_plugin_new(gboolean native
, const char *path
)
187 PurplePlugin
*plugin
;
189 plugin
= g_new0(PurplePlugin
, 1);
191 plugin
->native_plugin
= native
;
192 plugin
->path
= g_strdup(path
);
194 PURPLE_DBUS_REGISTER_POINTER(plugin
, PurplePlugin
);
200 purple_plugin_probe(const char *filename
)
202 #ifdef PURPLE_PLUGINS
203 PurplePlugin
*plugin
= NULL
;
204 PurplePlugin
*loader
;
206 gchar
*basename
= NULL
;
207 gboolean (*purple_init_plugin
)(PurplePlugin
*);
209 purple_debug_misc("plugins", "probing %s\n", filename
);
210 g_return_val_if_fail(filename
!= NULL
, NULL
);
212 if (!g_file_test(filename
, G_FILE_TEST_EXISTS
))
215 /* If this plugin has already been probed then exit */
216 basename
= purple_plugin_get_basename(filename
);
217 plugin
= purple_plugins_find_with_basename(basename
);
221 if (!strcmp(filename
, plugin
->path
))
223 else if (!purple_plugin_is_unloadable(plugin
))
225 purple_debug_info("plugins", "Not loading %s. "
226 "Another plugin with the same name (%s) has already been loaded.\n",
227 filename
, plugin
->path
);
232 /* The old plugin was a different file and it was unloadable.
233 * There's no guarantee that this new file with the same name
234 * will be loadable, but unless it fails in one of the silent
235 * ways and the first one didn't, it's not any worse. The user
236 * will still see a greyed-out plugin, which is what we want. */
237 purple_plugin_destroy(plugin
);
241 plugin
= purple_plugin_new(has_file_extension(filename
, G_MODULE_SUFFIX
), filename
);
243 if (plugin
->native_plugin
) {
246 /* Suppress error popups for failing to load plugins */
247 UINT old_error_mode
= SetErrorMode(SEM_FAILCRITICALERRORS
);
251 * We pass G_MODULE_BIND_LOCAL here to prevent symbols from
252 * plugins being added to the global name space.
254 * G_MODULE_BIND_LOCAL was added in glib 2.3.3.
256 #if GLIB_CHECK_VERSION(2,3,3)
257 plugin
->handle
= g_module_open(filename
, G_MODULE_BIND_LOCAL
);
259 plugin
->handle
= g_module_open(filename
, 0);
262 if (plugin
->handle
== NULL
)
264 const char *error
= g_module_error();
265 if (error
!= NULL
&& purple_str_has_prefix(error
, filename
))
267 error
= error
+ strlen(filename
);
269 /* These are just so we don't crash. If we
270 * got this far, they should always be true. */
277 if (error
== NULL
|| !*error
)
279 plugin
->error
= g_strdup(_("Unknown error"));
280 purple_debug_error("plugins", "%s is not loadable: Unknown error\n",
285 plugin
->error
= g_strdup(error
);
286 purple_debug_error("plugins", "%s is not loadable: %s\n",
287 plugin
->path
, plugin
->error
);
289 #if GLIB_CHECK_VERSION(2,3,3)
290 plugin
->handle
= g_module_open(filename
, G_MODULE_BIND_LAZY
| G_MODULE_BIND_LOCAL
);
292 plugin
->handle
= g_module_open(filename
, G_MODULE_BIND_LAZY
);
295 if (plugin
->handle
== NULL
)
298 /* Restore the original error mode */
299 SetErrorMode(old_error_mode
);
301 purple_plugin_destroy(plugin
);
306 /* We were able to load the plugin with lazy symbol binding.
307 * This means we're missing some symbol. Mark it as
308 * unloadable and keep going so we get the info to display
309 * to the user so they know to rebuild this plugin. */
310 plugin
->unloadable
= TRUE
;
314 if (!g_module_symbol(plugin
->handle
, "purple_init_plugin",
317 purple_debug_error("plugins", "%s is not usable because the "
318 "'purple_init_plugin' symbol could not be "
319 "found. Does the plugin call the "
320 "PURPLE_INIT_PLUGIN() macro?\n", plugin
->path
);
322 g_module_close(plugin
->handle
);
323 error
= g_module_error();
325 purple_debug_error("plugins", "Error closing module %s: %s\n",
326 plugin
->path
, error
);
327 plugin
->handle
= NULL
;
330 /* Restore the original error mode */
331 SetErrorMode(old_error_mode
);
333 purple_plugin_destroy(plugin
);
336 purple_init_plugin
= unpunned
;
339 /* Restore the original error mode */
340 SetErrorMode(old_error_mode
);
344 loader
= find_loader_for_plugin(plugin
);
346 if (loader
== NULL
) {
347 purple_plugin_destroy(plugin
);
351 purple_init_plugin
= PURPLE_PLUGIN_LOADER_INFO(loader
)->probe
;
354 if (!purple_init_plugin(plugin
) || plugin
->info
== NULL
)
356 purple_plugin_destroy(plugin
);
359 else if (plugin
->info
->ui_requirement
&&
360 strcmp(plugin
->info
->ui_requirement
, purple_core_get_ui()))
362 plugin
->error
= g_strdup_printf(_("You are using %s, but this plugin requires %s."),
363 purple_core_get_ui(), plugin
->info
->ui_requirement
);
364 purple_debug_error("plugins", "%s is not loadable: The UI requirement is not met. (%s)\n", plugin
->path
, plugin
->error
);
365 plugin
->unloadable
= TRUE
;
370 * Check to make sure a plugin has defined an id.
371 * Not having this check caused purple_plugin_unload to
372 * enter an infinite loop in certain situations by passing
373 * purple_find_plugin_by_id a NULL value. -- ecoffey
375 if (plugin
->info
->id
== NULL
|| *plugin
->info
->id
== '\0')
377 plugin
->error
= g_strdup(_("This plugin has not defined an ID."));
378 purple_debug_error("plugins", "%s is not loadable: info->id is not defined.\n", plugin
->path
);
379 plugin
->unloadable
= TRUE
;
383 /* Really old plugins. */
384 if (plugin
->info
->magic
!= PURPLE_PLUGIN_MAGIC
)
386 if (plugin
->info
->magic
>= 2 && plugin
->info
->magic
<= 4)
388 struct _PurplePluginInfo2
390 unsigned int api_version
;
391 PurplePluginType type
;
392 char *ui_requirement
;
395 PurplePluginPriority priority
;
405 gboolean (*load
)(PurplePlugin
*plugin
);
406 gboolean (*unload
)(PurplePlugin
*plugin
);
407 void (*destroy
)(PurplePlugin
*plugin
);
411 PurplePluginUiInfo
*prefs_info
;
412 GList
*(*actions
)(PurplePlugin
*plugin
, gpointer context
);
413 } *info2
= (struct _PurplePluginInfo2
*)plugin
->info
;
415 /* This leaks... but only for ancient plugins, so deal with it. */
416 plugin
->info
= g_new0(PurplePluginInfo
, 1);
418 /* We don't really need all these to display the plugin info, but
419 * I'm copying them all for good measure. */
420 plugin
->info
->magic
= info2
->api_version
;
421 plugin
->info
->type
= info2
->type
;
422 plugin
->info
->ui_requirement
= info2
->ui_requirement
;
423 plugin
->info
->flags
= info2
->flags
;
424 plugin
->info
->dependencies
= info2
->dependencies
;
425 plugin
->info
->id
= info2
->id
;
426 plugin
->info
->name
= info2
->name
;
427 plugin
->info
->version
= info2
->version
;
428 plugin
->info
->summary
= info2
->summary
;
429 plugin
->info
->description
= info2
->description
;
430 plugin
->info
->author
= info2
->author
;
431 plugin
->info
->homepage
= info2
->homepage
;
432 plugin
->info
->load
= info2
->load
;
433 plugin
->info
->unload
= info2
->unload
;
434 plugin
->info
->destroy
= info2
->destroy
;
435 plugin
->info
->ui_info
= info2
->ui_info
;
436 plugin
->info
->extra_info
= info2
->extra_info
;
438 if (info2
->api_version
>= 3)
439 plugin
->info
->prefs_info
= info2
->prefs_info
;
441 if (info2
->api_version
>= 4)
442 plugin
->info
->actions
= info2
->actions
;
445 plugin
->error
= g_strdup_printf(_("Plugin magic mismatch %d (need %d)"),
446 plugin
->info
->magic
, PURPLE_PLUGIN_MAGIC
);
447 purple_debug_error("plugins", "%s is not loadable: Plugin magic mismatch %d (need %d)\n",
448 plugin
->path
, plugin
->info
->magic
, PURPLE_PLUGIN_MAGIC
);
449 plugin
->unloadable
= TRUE
;
453 purple_debug_error("plugins", "%s is not loadable: Plugin magic mismatch %d (need %d)\n",
454 plugin
->path
, plugin
->info
->magic
, PURPLE_PLUGIN_MAGIC
);
455 purple_plugin_destroy(plugin
);
459 if (plugin
->info
->major_version
!= PURPLE_MAJOR_VERSION
||
460 plugin
->info
->minor_version
> PURPLE_MINOR_VERSION
)
462 plugin
->error
= g_strdup_printf(_("ABI version mismatch %d.%d.x (need %d.%d.x)"),
463 plugin
->info
->major_version
, plugin
->info
->minor_version
,
464 PURPLE_MAJOR_VERSION
, PURPLE_MINOR_VERSION
);
465 purple_debug_error("plugins", "%s is not loadable: ABI version mismatch %d.%d.x (need %d.%d.x)\n",
466 plugin
->path
, plugin
->info
->major_version
, plugin
->info
->minor_version
,
467 PURPLE_MAJOR_VERSION
, PURPLE_MINOR_VERSION
);
468 plugin
->unloadable
= TRUE
;
472 if (plugin
->info
->type
== PURPLE_PLUGIN_PROTOCOL
)
474 /* If plugin is a PRPL, make sure it implements the required functions */
475 if ((PURPLE_PLUGIN_PROTOCOL_INFO(plugin
)->list_icon
== NULL
) ||
476 (PURPLE_PLUGIN_PROTOCOL_INFO(plugin
)->login
== NULL
) ||
477 (PURPLE_PLUGIN_PROTOCOL_INFO(plugin
)->close
== NULL
))
479 plugin
->error
= g_strdup(_("Plugin does not implement all required functions (list_icon, login and close)"));
480 purple_debug_error("plugins", "%s is not loadable: %s\n",
481 plugin
->path
, plugin
->error
);
482 plugin
->unloadable
= TRUE
;
486 /* For debugging, let's warn about prpl prefs. */
487 if (plugin
->info
->prefs_info
!= NULL
)
489 purple_debug_error("plugins", "%s has a prefs_info, but is a prpl. This is no longer supported.\n",
497 #endif /* !PURPLE_PLUGINS */
500 #ifdef PURPLE_PLUGINS
502 compare_plugins(gconstpointer a
, gconstpointer b
)
504 const PurplePlugin
*plugina
= a
;
505 const PurplePlugin
*pluginb
= b
;
507 return strcmp(plugina
->info
->name
, pluginb
->info
->name
);
509 #endif /* PURPLE_PLUGINS */
512 purple_plugin_load(PurplePlugin
*plugin
)
514 #ifdef PURPLE_PLUGINS
515 GList
*dep_list
= NULL
;
518 g_return_val_if_fail(plugin
!= NULL
, FALSE
);
520 if (purple_plugin_is_loaded(plugin
))
523 if (purple_plugin_is_unloadable(plugin
))
526 g_return_val_if_fail(plugin
->error
== NULL
, FALSE
);
529 * Go through the list of the plugin's dependencies.
531 * First pass: Make sure all the plugins needed are probed.
533 for (l
= plugin
->info
->dependencies
; l
!= NULL
; l
= l
->next
)
535 const char *dep_name
= (const char *)l
->data
;
536 PurplePlugin
*dep_plugin
;
538 dep_plugin
= purple_plugins_find_with_id(dep_name
);
540 if (dep_plugin
== NULL
)
544 tmp
= g_strdup_printf(_("The required plugin %s was not found. "
545 "Please install this plugin and try again."),
548 purple_notify_error(NULL
, NULL
,
549 _("Unable to load the plugin"), tmp
);
552 g_list_free(dep_list
);
557 dep_list
= g_list_append(dep_list
, dep_plugin
);
560 /* Second pass: load all the required plugins. */
561 for (l
= dep_list
; l
!= NULL
; l
= l
->next
)
563 PurplePlugin
*dep_plugin
= (PurplePlugin
*)l
->data
;
565 if (!purple_plugin_is_loaded(dep_plugin
))
567 if (!purple_plugin_load(dep_plugin
))
571 tmp
= g_strdup_printf(_("The required plugin %s was unable to load."),
574 purple_notify_error(NULL
, NULL
,
575 _("Unable to load your plugin."), tmp
);
578 g_list_free(dep_list
);
585 /* Third pass: note that other plugins are dependencies of this plugin.
586 * This is done separately in case we had to bail out earlier. */
587 for (l
= dep_list
; l
!= NULL
; l
= l
->next
)
589 PurplePlugin
*dep_plugin
= (PurplePlugin
*)l
->data
;
590 dep_plugin
->dependent_plugins
= g_list_prepend(dep_plugin
->dependent_plugins
, plugin
->info
->id
);
593 g_list_free(dep_list
);
595 if (plugin
->native_plugin
)
597 if (plugin
->info
!= NULL
&& plugin
->info
->load
!= NULL
)
599 if (!plugin
->info
->load(plugin
))
604 PurplePlugin
*loader
;
605 PurplePluginLoaderInfo
*loader_info
;
607 loader
= find_loader_for_plugin(plugin
);
612 loader_info
= PURPLE_PLUGIN_LOADER_INFO(loader
);
614 if (loader_info
->load
!= NULL
)
616 if (!loader_info
->load(plugin
))
621 loaded_plugins
= g_list_insert_sorted(loaded_plugins
, plugin
, compare_plugins
);
623 plugin
->loaded
= TRUE
;
626 load_cb(plugin
, load_cb_data
);
628 purple_signal_emit(purple_plugins_get_handle(), "plugin-load", plugin
);
634 #endif /* !PURPLE_PLUGINS */
638 purple_plugin_unload(PurplePlugin
*plugin
)
640 #ifdef PURPLE_PLUGINS
644 g_return_val_if_fail(plugin
!= NULL
, FALSE
);
645 g_return_val_if_fail(purple_plugin_is_loaded(plugin
), FALSE
);
647 purple_debug_info("plugins", "Unloading plugin %s\n", plugin
->info
->name
);
649 /* Unload all plugins that depend on this plugin. */
650 for (l
= plugin
->dependent_plugins
; l
!= NULL
; l
= ll
) {
651 const char * dep_name
= (const char *)l
->data
;
652 PurplePlugin
*dep_plugin
;
654 /* Store a pointer to the next element in the list.
655 * This is because we'll be modifying this list in the loop. */
658 dep_plugin
= purple_plugins_find_with_id(dep_name
);
660 if (dep_plugin
!= NULL
&& purple_plugin_is_loaded(dep_plugin
))
662 if (!purple_plugin_unload(dep_plugin
))
664 g_free(plugin
->error
);
665 plugin
->error
= g_strdup_printf(_("%s requires %s, but it failed to unload."),
666 _(plugin
->info
->name
),
667 _(dep_plugin
->info
->name
));
673 /* This isn't necessary. This has already been done when unloading dep_plugin. */
674 plugin
->dependent_plugins
= g_list_delete_link(plugin
->dependent_plugins
, l
);
680 /* Remove this plugin from each dependency's dependent_plugins list. */
681 for (l
= plugin
->info
->dependencies
; l
!= NULL
; l
= l
->next
)
683 const char *dep_name
= (const char *)l
->data
;
684 PurplePlugin
*dependency
;
686 dependency
= purple_plugins_find_with_id(dep_name
);
688 if (dependency
!= NULL
)
689 dependency
->dependent_plugins
= g_list_remove(dependency
->dependent_plugins
, plugin
->info
->id
);
691 purple_debug_error("plugins", "Unable to remove from dependency list for %s\n", dep_name
);
694 if (plugin
->native_plugin
) {
695 if (plugin
->info
->unload
&& !plugin
->info
->unload(plugin
))
698 if (plugin
->info
->type
== PURPLE_PLUGIN_PROTOCOL
) {
699 PurplePluginProtocolInfo
*prpl_info
;
702 prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(plugin
);
704 for (l
= prpl_info
->user_splits
; l
!= NULL
; l
= l
->next
)
705 purple_account_user_split_destroy(l
->data
);
707 for (l
= prpl_info
->protocol_options
; l
!= NULL
; l
= l
->next
)
708 purple_account_option_destroy(l
->data
);
710 if (prpl_info
->user_splits
!= NULL
) {
711 g_list_free(prpl_info
->user_splits
);
712 prpl_info
->user_splits
= NULL
;
715 if (prpl_info
->protocol_options
!= NULL
) {
716 g_list_free(prpl_info
->protocol_options
);
717 prpl_info
->protocol_options
= NULL
;
721 PurplePlugin
*loader
;
722 PurplePluginLoaderInfo
*loader_info
;
724 loader
= find_loader_for_plugin(plugin
);
729 loader_info
= PURPLE_PLUGIN_LOADER_INFO(loader
);
731 if (loader_info
->unload
&& !loader_info
->unload(plugin
))
735 /* cancel any pending dialogs the plugin has */
736 purple_request_close_with_handle(plugin
);
737 purple_notify_close_with_handle(plugin
);
739 purple_signals_disconnect_by_handle(plugin
);
740 purple_plugin_ipc_unregister_all(plugin
);
742 loaded_plugins
= g_list_remove(loaded_plugins
, plugin
);
743 if ((plugin
->info
!= NULL
) && PURPLE_IS_PROTOCOL_PLUGIN(plugin
))
744 protocol_plugins
= g_list_remove(protocol_plugins
, plugin
);
745 plugins_to_disable
= g_list_remove(plugins_to_disable
, plugin
);
746 plugin
->loaded
= FALSE
;
748 /* We wouldn't be anywhere near here if the plugin wasn't loaded, so
749 * if plugin->error is set at all, it had to be from a previous
750 * unload failure. It's obviously okay now.
752 g_free(plugin
->error
);
753 plugin
->error
= NULL
;
755 if (unload_cb
!= NULL
)
756 unload_cb(plugin
, unload_cb_data
);
758 purple_signal_emit(purple_plugins_get_handle(), "plugin-unload", plugin
);
760 purple_prefs_disconnect_by_handle(plugin
);
765 #endif /* PURPLE_PLUGINS */
769 purple_plugin_disable(PurplePlugin
*plugin
)
771 #ifdef PURPLE_PLUGINS
772 g_return_if_fail(plugin
!= NULL
);
774 if (!g_list_find(plugins_to_disable
, plugin
))
775 plugins_to_disable
= g_list_prepend(plugins_to_disable
, plugin
);
780 purple_plugin_reload(PurplePlugin
*plugin
)
782 #ifdef PURPLE_PLUGINS
783 g_return_val_if_fail(plugin
!= NULL
, FALSE
);
784 g_return_val_if_fail(purple_plugin_is_loaded(plugin
), FALSE
);
786 if (!purple_plugin_unload(plugin
))
789 if (!purple_plugin_load(plugin
))
795 #endif /* !PURPLE_PLUGINS */
799 purple_plugin_destroy(PurplePlugin
*plugin
)
801 #ifdef PURPLE_PLUGINS
802 g_return_if_fail(plugin
!= NULL
);
804 if (purple_plugin_is_loaded(plugin
))
805 purple_plugin_unload(plugin
);
807 plugins
= g_list_remove(plugins
, plugin
);
809 if (load_queue
!= NULL
)
810 load_queue
= g_list_remove(load_queue
, plugin
);
812 /* true, this may leak a little memory if there is a major version
813 * mismatch, but it's a lot better than trying to free something
814 * we shouldn't, and crashing while trying to load an old plugin */
815 if(plugin
->info
== NULL
|| plugin
->info
->magic
!= PURPLE_PLUGIN_MAGIC
||
816 plugin
->info
->major_version
!= PURPLE_MAJOR_VERSION
)
819 g_module_close(plugin
->handle
);
821 g_free(plugin
->path
);
822 g_free(plugin
->error
);
824 PURPLE_DBUS_UNREGISTER_POINTER(plugin
);
830 if (plugin
->info
!= NULL
)
831 g_list_free(plugin
->info
->dependencies
);
833 if (plugin
->native_plugin
)
835 if (plugin
->info
!= NULL
&& plugin
->info
->type
== PURPLE_PLUGIN_LOADER
)
837 PurplePluginLoaderInfo
*loader_info
;
838 GList
*exts
, *l
, *next_l
;
841 loader_info
= PURPLE_PLUGIN_LOADER_INFO(plugin
);
843 if (loader_info
!= NULL
&& loader_info
->exts
!= NULL
)
845 for (exts
= PURPLE_PLUGIN_LOADER_INFO(plugin
)->exts
;
849 for (l
= purple_plugins_get_all(); l
!= NULL
; l
= next_l
)
855 if (p2
->path
!= NULL
&&
856 has_file_extension(p2
->path
, exts
->data
))
858 purple_plugin_destroy(p2
);
863 g_list_free(loader_info
->exts
);
866 plugin_loaders
= g_list_remove(plugin_loaders
, plugin
);
869 if (plugin
->info
!= NULL
&& plugin
->info
->destroy
!= NULL
)
870 plugin
->info
->destroy(plugin
);
872 if (plugin
->handle
!= NULL
)
873 g_module_close(plugin
->handle
);
877 PurplePlugin
*loader
;
878 PurplePluginLoaderInfo
*loader_info
;
880 loader
= find_loader_for_plugin(plugin
);
884 loader_info
= PURPLE_PLUGIN_LOADER_INFO(loader
);
886 if (loader_info
->destroy
!= NULL
)
887 loader_info
->destroy(plugin
);
891 g_free(plugin
->path
);
892 g_free(plugin
->error
);
894 PURPLE_DBUS_UNREGISTER_POINTER(plugin
);
897 #endif /* !PURPLE_PLUGINS */
901 purple_plugin_is_loaded(const PurplePlugin
*plugin
)
903 g_return_val_if_fail(plugin
!= NULL
, FALSE
);
905 return plugin
->loaded
;
909 purple_plugin_is_unloadable(const PurplePlugin
*plugin
)
911 g_return_val_if_fail(plugin
!= NULL
, FALSE
);
913 return plugin
->unloadable
;
917 purple_plugin_get_id(const PurplePlugin
*plugin
) {
918 g_return_val_if_fail(plugin
, NULL
);
919 g_return_val_if_fail(plugin
->info
, NULL
);
921 return plugin
->info
->id
;
925 purple_plugin_get_name(const PurplePlugin
*plugin
) {
926 g_return_val_if_fail(plugin
, NULL
);
927 g_return_val_if_fail(plugin
->info
, NULL
);
929 return _(plugin
->info
->name
);
933 purple_plugin_get_version(const PurplePlugin
*plugin
) {
934 g_return_val_if_fail(plugin
, NULL
);
935 g_return_val_if_fail(plugin
->info
, NULL
);
937 return plugin
->info
->version
;
941 purple_plugin_get_summary(const PurplePlugin
*plugin
) {
942 g_return_val_if_fail(plugin
, NULL
);
943 g_return_val_if_fail(plugin
->info
, NULL
);
945 return _(plugin
->info
->summary
);
949 purple_plugin_get_description(const PurplePlugin
*plugin
) {
950 g_return_val_if_fail(plugin
, NULL
);
951 g_return_val_if_fail(plugin
->info
, NULL
);
953 return _(plugin
->info
->description
);
957 purple_plugin_get_author(const PurplePlugin
*plugin
) {
958 g_return_val_if_fail(plugin
, NULL
);
959 g_return_val_if_fail(plugin
->info
, NULL
);
961 return _(plugin
->info
->author
);
965 purple_plugin_get_homepage(const PurplePlugin
*plugin
) {
966 g_return_val_if_fail(plugin
, NULL
);
967 g_return_val_if_fail(plugin
->info
, NULL
);
969 return plugin
->info
->homepage
;
972 /**************************************************************************
974 **************************************************************************/
976 destroy_ipc_info(void *data
)
978 PurplePluginIpcCommand
*ipc_command
= (PurplePluginIpcCommand
*)data
;
981 if (ipc_command
->params
!= NULL
)
983 for (i
= 0; i
< ipc_command
->num_params
; i
++)
984 purple_value_destroy(ipc_command
->params
[i
]);
986 g_free(ipc_command
->params
);
989 if (ipc_command
->ret_value
!= NULL
)
990 purple_value_destroy(ipc_command
->ret_value
);
996 purple_plugin_ipc_register(PurplePlugin
*plugin
, const char *command
,
997 PurpleCallback func
, PurpleSignalMarshalFunc marshal
,
998 PurpleValue
*ret_value
, int num_params
, ...)
1000 PurplePluginIpcInfo
*ipc_info
;
1001 PurplePluginIpcCommand
*ipc_command
;
1003 g_return_val_if_fail(plugin
!= NULL
, FALSE
);
1004 g_return_val_if_fail(command
!= NULL
, FALSE
);
1005 g_return_val_if_fail(func
!= NULL
, FALSE
);
1006 g_return_val_if_fail(marshal
!= NULL
, FALSE
);
1008 if (plugin
->ipc_data
== NULL
)
1010 ipc_info
= plugin
->ipc_data
= g_new0(PurplePluginIpcInfo
, 1);
1011 ipc_info
->commands
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1012 g_free
, destroy_ipc_info
);
1015 ipc_info
= (PurplePluginIpcInfo
*)plugin
->ipc_data
;
1017 ipc_command
= g_new0(PurplePluginIpcCommand
, 1);
1018 ipc_command
->func
= func
;
1019 ipc_command
->marshal
= marshal
;
1020 ipc_command
->num_params
= num_params
;
1021 ipc_command
->ret_value
= ret_value
;
1028 ipc_command
->params
= g_new0(PurpleValue
*, num_params
);
1030 va_start(args
, num_params
);
1032 for (i
= 0; i
< num_params
; i
++)
1033 ipc_command
->params
[i
] = va_arg(args
, PurpleValue
*);
1038 g_hash_table_replace(ipc_info
->commands
, g_strdup(command
), ipc_command
);
1040 ipc_info
->command_count
++;
1046 purple_plugin_ipc_unregister(PurplePlugin
*plugin
, const char *command
)
1048 PurplePluginIpcInfo
*ipc_info
;
1050 g_return_if_fail(plugin
!= NULL
);
1051 g_return_if_fail(command
!= NULL
);
1053 ipc_info
= (PurplePluginIpcInfo
*)plugin
->ipc_data
;
1055 if (ipc_info
== NULL
||
1056 g_hash_table_lookup(ipc_info
->commands
, command
) == NULL
)
1058 purple_debug_error("plugins",
1059 "IPC command '%s' was not registered for plugin %s\n",
1060 command
, plugin
->info
->name
);
1064 g_hash_table_remove(ipc_info
->commands
, command
);
1066 ipc_info
->command_count
--;
1068 if (ipc_info
->command_count
== 0)
1070 g_hash_table_destroy(ipc_info
->commands
);
1073 plugin
->ipc_data
= NULL
;
1078 purple_plugin_ipc_unregister_all(PurplePlugin
*plugin
)
1080 PurplePluginIpcInfo
*ipc_info
;
1082 g_return_if_fail(plugin
!= NULL
);
1084 if (plugin
->ipc_data
== NULL
)
1085 return; /* Silently ignore it. */
1087 ipc_info
= (PurplePluginIpcInfo
*)plugin
->ipc_data
;
1089 g_hash_table_destroy(ipc_info
->commands
);
1092 plugin
->ipc_data
= NULL
;
1096 purple_plugin_ipc_get_params(PurplePlugin
*plugin
, const char *command
,
1097 PurpleValue
**ret_value
, int *num_params
,
1098 PurpleValue
***params
)
1100 PurplePluginIpcInfo
*ipc_info
;
1101 PurplePluginIpcCommand
*ipc_command
;
1103 g_return_val_if_fail(plugin
!= NULL
, FALSE
);
1104 g_return_val_if_fail(command
!= NULL
, FALSE
);
1106 ipc_info
= (PurplePluginIpcInfo
*)plugin
->ipc_data
;
1108 if (ipc_info
== NULL
||
1109 (ipc_command
= g_hash_table_lookup(ipc_info
->commands
,
1112 purple_debug_error("plugins",
1113 "IPC command '%s' was not registered for plugin %s\n",
1114 command
, plugin
->info
->name
);
1119 if (num_params
!= NULL
)
1120 *num_params
= ipc_command
->num_params
;
1123 *params
= ipc_command
->params
;
1125 if (ret_value
!= NULL
)
1126 *ret_value
= ipc_command
->ret_value
;
1132 purple_plugin_ipc_call(PurplePlugin
*plugin
, const char *command
,
1135 PurplePluginIpcInfo
*ipc_info
;
1136 PurplePluginIpcCommand
*ipc_command
;
1143 g_return_val_if_fail(plugin
!= NULL
, NULL
);
1144 g_return_val_if_fail(command
!= NULL
, NULL
);
1146 ipc_info
= (PurplePluginIpcInfo
*)plugin
->ipc_data
;
1148 if (ipc_info
== NULL
||
1149 (ipc_command
= g_hash_table_lookup(ipc_info
->commands
,
1152 purple_debug_error("plugins",
1153 "IPC command '%s' was not registered for plugin %s\n",
1154 command
, plugin
->info
->name
);
1160 ipc_command
->marshal(ipc_command
->func
, args
, NULL
, &ret_value
);
1169 /**************************************************************************
1171 **************************************************************************/
1173 purple_plugins_get_handle(void) {
1180 purple_plugins_init(void) {
1181 void *handle
= purple_plugins_get_handle();
1183 purple_plugins_add_search_path(LIBDIR
);
1185 purple_signal_register(handle
, "plugin-load",
1186 purple_marshal_VOID__POINTER
,
1188 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1189 PURPLE_SUBTYPE_PLUGIN
));
1190 purple_signal_register(handle
, "plugin-unload",
1191 purple_marshal_VOID__POINTER
,
1193 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1194 PURPLE_SUBTYPE_PLUGIN
));
1198 purple_plugins_uninit(void)
1200 void *handle
= purple_plugins_get_handle();
1202 purple_signals_disconnect_by_handle(handle
);
1203 purple_signals_unregister_by_instance(handle
);
1206 /**************************************************************************
1208 **************************************************************************/
1210 purple_plugins_add_search_path(const char *path
)
1212 g_return_if_fail(path
!= NULL
);
1214 if (g_list_find_custom(search_paths
, path
, (GCompareFunc
)strcmp
))
1217 search_paths
= g_list_append(search_paths
, g_strdup(path
));
1221 purple_plugins_unload_all(void)
1223 #ifdef PURPLE_PLUGINS
1225 while (loaded_plugins
!= NULL
)
1226 purple_plugin_unload(loaded_plugins
->data
);
1228 #endif /* PURPLE_PLUGINS */
1232 purple_plugins_destroy_all(void)
1234 #ifdef PURPLE_PLUGINS
1236 while (plugins
!= NULL
)
1237 purple_plugin_destroy(plugins
->data
);
1239 #endif /* PURPLE_PLUGINS */
1243 purple_plugins_save_loaded(const char *key
)
1245 #ifdef PURPLE_PLUGINS
1247 GList
*files
= NULL
;
1249 for (pl
= purple_plugins_get_loaded(); pl
!= NULL
; pl
= pl
->next
) {
1250 PurplePlugin
*plugin
= pl
->data
;
1252 if (plugin
->info
->type
!= PURPLE_PLUGIN_PROTOCOL
&&
1253 plugin
->info
->type
!= PURPLE_PLUGIN_LOADER
&&
1254 !g_list_find(plugins_to_disable
, plugin
)) {
1255 files
= g_list_append(files
, plugin
->path
);
1259 purple_prefs_set_path_list(key
, files
);
1265 purple_plugins_load_saved(const char *key
)
1267 #ifdef PURPLE_PLUGINS
1270 g_return_if_fail(key
!= NULL
);
1272 files
= purple_prefs_get_path_list(key
);
1274 for (f
= files
; f
; f
= f
->next
)
1278 PurplePlugin
*plugin
;
1280 if (f
->data
== NULL
)
1286 * We don't know if the filename uses Windows or Unix path
1287 * separators (because people might be sharing a prefs.xml
1288 * file across systems), so we find the last occurrence
1291 basename
= strrchr(filename
, '/');
1292 if ((basename
== NULL
) || (basename
< strrchr(filename
, '\\')))
1293 basename
= strrchr(filename
, '\\');
1294 if (basename
!= NULL
)
1297 /* Strip the extension */
1299 basename
= purple_plugin_get_basename(basename
);
1301 if (((plugin
= purple_plugins_find_with_filename(filename
)) != NULL
) ||
1302 (basename
&& (plugin
= purple_plugins_find_with_basename(basename
)) != NULL
) ||
1303 ((plugin
= purple_plugin_probe(filename
)) != NULL
))
1305 purple_debug_info("plugins", "Loading saved plugin %s\n",
1307 purple_plugin_load(plugin
);
1311 purple_debug_error("plugins", "Unable to find saved plugin %s\n",
1321 #endif /* PURPLE_PLUGINS */
1326 purple_plugins_probe(const char *ext
)
1328 #ifdef PURPLE_PLUGINS
1332 PurplePlugin
*plugin
;
1334 const char *search_path
;
1336 if (!g_module_supported())
1340 for (cur
= search_paths
; cur
!= NULL
; cur
= cur
->next
)
1342 search_path
= cur
->data
;
1344 dir
= g_dir_open(search_path
, 0, NULL
);
1348 while ((file
= g_dir_read_name(dir
)) != NULL
)
1350 path
= g_build_filename(search_path
, file
, NULL
);
1352 if (ext
== NULL
|| has_file_extension(file
, ext
))
1353 plugin
= purple_plugin_probe(path
);
1362 /* See if we have any plugins waiting to load */
1363 while (load_queue
!= NULL
)
1365 plugin
= (PurplePlugin
*)load_queue
->data
;
1367 load_queue
= g_list_remove(load_queue
, plugin
);
1369 if (plugin
== NULL
|| plugin
->info
== NULL
)
1372 if (plugin
->info
->type
== PURPLE_PLUGIN_LOADER
)
1374 /* We'll just load this right now. */
1375 if (!purple_plugin_load(plugin
))
1377 purple_plugin_destroy(plugin
);
1382 plugin_loaders
= g_list_append(plugin_loaders
, plugin
);
1384 for (cur
= PURPLE_PLUGIN_LOADER_INFO(plugin
)->exts
;
1388 purple_plugins_probe(cur
->data
);
1391 else if (plugin
->info
->type
== PURPLE_PLUGIN_PROTOCOL
)
1393 /* We'll just load this right now. */
1394 if (!purple_plugin_load(plugin
))
1396 purple_plugin_destroy(plugin
);
1401 /* Make sure we don't load two PRPLs with the same name? */
1402 if (purple_find_prpl(plugin
->info
->id
))
1404 /* Nothing to see here--move along, move along */
1405 purple_plugin_destroy(plugin
);
1410 protocol_plugins
= g_list_insert_sorted(protocol_plugins
, plugin
,
1411 (GCompareFunc
)compare_prpl
);
1415 if (probe_cb
!= NULL
)
1416 probe_cb(probe_cb_data
);
1418 #endif /* PURPLE_PLUGINS */
1422 purple_plugin_register(PurplePlugin
*plugin
)
1424 g_return_val_if_fail(plugin
!= NULL
, FALSE
);
1426 /* If this plugin has been registered already then exit */
1427 if (g_list_find(plugins
, plugin
))
1430 /* Ensure the plugin has the requisite information */
1431 if (plugin
->info
->type
== PURPLE_PLUGIN_LOADER
)
1433 PurplePluginLoaderInfo
*loader_info
;
1435 loader_info
= PURPLE_PLUGIN_LOADER_INFO(plugin
);
1437 if (loader_info
== NULL
)
1439 purple_debug_error("plugins", "%s is not loadable, loader plugin missing loader_info\n",
1444 else if (plugin
->info
->type
== PURPLE_PLUGIN_PROTOCOL
)
1446 PurplePluginProtocolInfo
*prpl_info
;
1448 prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(plugin
);
1450 if (prpl_info
== NULL
)
1452 purple_debug_error("plugins", "%s is not loadable, protocol plugin missing prpl_info\n",
1458 #ifdef PURPLE_PLUGINS
1459 /* This plugin should be probed and maybe loaded--add it to the queue */
1460 load_queue
= g_list_append(load_queue
, plugin
);
1462 if (plugin
->info
!= NULL
)
1464 if (plugin
->info
->type
== PURPLE_PLUGIN_PROTOCOL
)
1465 protocol_plugins
= g_list_insert_sorted(protocol_plugins
, plugin
,
1466 (GCompareFunc
)compare_prpl
);
1467 if (plugin
->info
->load
!= NULL
)
1468 if (!plugin
->info
->load(plugin
))
1473 plugins
= g_list_append(plugins
, plugin
);
1479 purple_plugins_enabled(void)
1481 #ifdef PURPLE_PLUGINS
1489 purple_plugins_register_probe_notify_cb(void (*func
)(void *), void *data
)
1492 probe_cb_data
= data
;
1496 purple_plugins_unregister_probe_notify_cb(void (*func
)(void *))
1499 probe_cb_data
= NULL
;
1503 purple_plugins_register_load_notify_cb(void (*func
)(PurplePlugin
*, void *),
1507 load_cb_data
= data
;
1511 purple_plugins_unregister_load_notify_cb(void (*func
)(PurplePlugin
*, void *))
1514 load_cb_data
= NULL
;
1518 purple_plugins_register_unload_notify_cb(void (*func
)(PurplePlugin
*, void *),
1522 unload_cb_data
= data
;
1526 purple_plugins_unregister_unload_notify_cb(void (*func
)(PurplePlugin
*, void *))
1529 unload_cb_data
= NULL
;
1533 purple_plugins_find_with_name(const char *name
)
1535 PurplePlugin
*plugin
;
1538 for (l
= plugins
; l
!= NULL
; l
= l
->next
) {
1541 if (!strcmp(plugin
->info
->name
, name
))
1549 purple_plugins_find_with_filename(const char *filename
)
1551 PurplePlugin
*plugin
;
1554 for (l
= plugins
; l
!= NULL
; l
= l
->next
) {
1557 if (plugin
->path
!= NULL
&& !strcmp(plugin
->path
, filename
))
1565 purple_plugins_find_with_basename(const char *basename
)
1567 #ifdef PURPLE_PLUGINS
1568 PurplePlugin
*plugin
;
1572 g_return_val_if_fail(basename
!= NULL
, NULL
);
1574 for (l
= plugins
; l
!= NULL
; l
= l
->next
)
1576 plugin
= (PurplePlugin
*)l
->data
;
1578 if (plugin
->path
!= NULL
) {
1579 tmp
= purple_plugin_get_basename(plugin
->path
);
1580 if (!strcmp(tmp
, basename
))
1589 #endif /* PURPLE_PLUGINS */
1595 purple_plugins_find_with_id(const char *id
)
1597 PurplePlugin
*plugin
;
1600 g_return_val_if_fail(id
!= NULL
, NULL
);
1602 for (l
= plugins
; l
!= NULL
; l
= l
->next
)
1606 if (plugin
->info
->id
!= NULL
&& !strcmp(plugin
->info
->id
, id
))
1614 purple_plugins_get_loaded(void)
1616 return loaded_plugins
;
1620 purple_plugins_get_protocols(void)
1622 return protocol_plugins
;
1626 purple_plugins_get_all(void)
1632 PurplePluginAction
*
1633 purple_plugin_action_new(const char* label
, void (*callback
)(PurplePluginAction
*))
1635 PurplePluginAction
*act
= g_new0(PurplePluginAction
, 1);
1637 act
->label
= g_strdup(label
);
1638 act
->callback
= callback
;
1644 purple_plugin_action_free(PurplePluginAction
*action
)
1646 g_return_if_fail(action
!= NULL
);
1648 g_free(action
->label
);