summaryrefslogtreecommitdiff
path: root/libpurple/plugins.c
diff options
context:
space:
mode:
authorAnkit Vani <a@nevitus.org>2014-02-05 21:42:07 +0530
committerAnkit Vani <a@nevitus.org>2014-02-05 21:42:07 +0530
commitdd03d7feede3c566019b13b1df79cb74b3108100 (patch)
tree65fd2f108054463a8d84f185fe54523483c92de2 /libpurple/plugins.c
parent279108a5dd676ea6d6306af5b9a464d5d81adfcd (diff)
parent4c4ba0cead9bd9ff9072d589e70cf5a9fd106a6a (diff)
downloadpidgin-dd03d7feede3c566019b13b1df79cb74b3108100.tar.gz
Merge gtkdoc-conversion
Diffstat (limited to 'libpurple/plugins.c')
-rw-r--r--libpurple/plugins.c1159
1 files changed, 1159 insertions, 0 deletions
diff --git a/libpurple/plugins.c b/libpurple/plugins.c
new file mode 100644
index 0000000000..abdf39d43f
--- /dev/null
+++ b/libpurple/plugins.c
@@ -0,0 +1,1159 @@
+/*
+ * purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
+ */
+#include "internal.h"
+
+#include "core.h"
+#include "debug.h"
+#include "dbus-maybe.h"
+#include "enums.h"
+#include "plugins.h"
+
+#define PURPLE_PLUGIN_INFO_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_PLUGIN_INFO, PurplePluginInfoPrivate))
+
+typedef struct _PurplePluginInfoPrivate PurplePluginInfoPrivate;
+
+/**************************************************************************
+ * Plugin info private data
+ **************************************************************************/
+struct _PurplePluginInfoPrivate {
+ char *ui_requirement; /* ID of UI that is required to load the plugin */
+ char *error; /* Why a plugin is not loadable */
+
+ PurplePluginInfoFlags flags; /* Flags for the plugin */
+
+ /* Callback that returns a list of actions the plugin can perform */
+ PurplePluginActionsCb actions_cb;
+
+ /* Callback that returns extra information about a plugin */
+ PurplePluginExtraCb extra_cb;
+
+ /* Callback that returns a preferences frame for a plugin */
+ PurplePluginPrefFrameCb pref_frame_cb;
+
+ /* Callback that returns a preferences request handle for a plugin */
+ PurplePluginPrefRequestCb pref_request_cb;
+
+ /* TRUE if a plugin has been unloaded at least once. Auto-load
+ * plugins that have been unloaded once will not be auto-loaded again. */
+ gboolean unloaded;
+};
+
+enum
+{
+ PROP_0,
+ PROP_UI_REQUIREMENT,
+ PROP_ACTIONS_CB,
+ PROP_EXTRA_CB,
+ PROP_PREF_FRAME_CB,
+ PROP_PREF_REQUEST_CB,
+ PROP_FLAGS,
+ PROP_LAST
+};
+
+static GObjectClass *parent_class;
+
+/**************************************************************************
+ * Globals
+ **************************************************************************/
+#ifdef PURPLE_PLUGINS
+static GList *loaded_plugins = NULL;
+static GList *plugins_to_disable = NULL;
+#endif
+
+/**************************************************************************
+ * Plugin API
+ **************************************************************************/
+#ifdef PURPLE_PLUGINS
+static gboolean
+plugin_loading_cb(GObject *manager, PurplePlugin *plugin, GError **error,
+ gpointer data)
+{
+ PurplePluginInfo *info;
+ PurplePluginInfoPrivate *priv;
+
+ g_return_val_if_fail(PURPLE_IS_PLUGIN(plugin), FALSE);
+
+ info = purple_plugin_get_info(plugin);
+ if (!info)
+ return TRUE; /* a GPlugin internal plugin */
+
+ priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info);
+
+ if (priv->error) {
+ purple_debug_error("plugins", "Failed to load plugin %s: %s",
+ purple_plugin_get_filename(plugin), priv->error);
+
+ g_set_error(error, PURPLE_PLUGINS_DOMAIN, 0,
+ "Plugin is not loadable: %s", priv->error);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+plugin_loaded_cb(GObject *manager, PurplePlugin *plugin)
+{
+ PurplePluginInfo *info;
+
+ g_return_if_fail(PURPLE_IS_PLUGIN(plugin));
+
+ info = purple_plugin_get_info(plugin);
+ if (!info)
+ return; /* a GPlugin internal plugin */
+
+ loaded_plugins = g_list_prepend(loaded_plugins, plugin);
+
+ purple_debug_info("plugins", "Loaded plugin %s\n",
+ purple_plugin_get_filename(plugin));
+
+ purple_signal_emit(purple_plugins_get_handle(), "plugin-load", plugin);
+}
+
+static gboolean
+plugin_unloading_cb(GObject *manager, PurplePlugin *plugin, GError **error,
+ gpointer data)
+{
+ PurplePluginInfo *info;
+
+ g_return_val_if_fail(PURPLE_IS_PLUGIN(plugin), FALSE);
+
+ info = purple_plugin_get_info(plugin);
+ if (info) {
+ purple_debug_info("plugins", "Unloading plugin %s\n",
+ purple_plugin_get_filename(plugin));
+ }
+
+ return TRUE;
+}
+
+static void
+plugin_unloaded_cb(GObject *manager, PurplePlugin *plugin)
+{
+ PurplePluginInfo *info;
+ PurplePluginInfoPrivate *priv;
+
+ g_return_if_fail(PURPLE_IS_PLUGIN(plugin));
+
+ info = purple_plugin_get_info(plugin);
+ if (!info)
+ return; /* a GPlugin internal plugin */
+
+ priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info);
+
+ /* cancel any pending dialogs the plugin has */
+ purple_request_close_with_handle(plugin);
+ purple_notify_close_with_handle(plugin);
+
+ purple_signals_disconnect_by_handle(plugin);
+ purple_signals_unregister_by_instance(plugin);
+
+ priv->unloaded = TRUE;
+
+ loaded_plugins = g_list_remove(loaded_plugins, plugin);
+ plugins_to_disable = g_list_remove(plugins_to_disable, plugin);
+
+ purple_signal_emit(purple_plugins_get_handle(), "plugin-unload", plugin);
+
+ purple_prefs_disconnect_by_handle(plugin);
+}
+#endif /* PURPLE_PLUGINS */
+
+gboolean
+purple_plugin_load(PurplePlugin *plugin, GError **error)
+{
+#ifdef PURPLE_PLUGINS
+ GError *err = NULL;
+
+ g_return_val_if_fail(plugin != NULL, FALSE);
+
+ if (purple_plugin_is_loaded(plugin))
+ return TRUE;
+
+ if (!gplugin_manager_load_plugin(plugin, &err)) {
+ purple_debug_error("plugins", "Failed to load plugin %s: %s",
+ purple_plugin_get_filename(plugin),
+ (err ? err->message : "Unknown reason"));
+
+ if (error)
+ *error = g_error_copy(err);
+ g_error_free(err);
+
+ return FALSE;
+ }
+
+ return TRUE;
+
+#else
+ g_set_error(error, PURPLE_PLUGINS_DOMAIN, 0, "Plugin support is disabled.");
+ return FALSE;
+#endif /* PURPLE_PLUGINS */
+}
+
+gboolean
+purple_plugin_unload(PurplePlugin *plugin, GError **error)
+{
+#ifdef PURPLE_PLUGINS
+ GError *err = NULL;
+
+ g_return_val_if_fail(plugin != NULL, FALSE);
+
+ if (!purple_plugin_is_loaded(plugin))
+ return TRUE;
+
+ if (!gplugin_manager_unload_plugin(plugin, &err)) {
+ purple_debug_error("plugins", "Failed to unload plugin %s: %s",
+ purple_plugin_get_filename(plugin),
+ (err ? err->message : "Unknown reason"));
+
+ if (error)
+ *error = g_error_copy(err);
+ g_error_free(err);
+
+ return FALSE;
+ }
+
+ return TRUE;
+
+#else
+ g_set_error(error, PURPLE_PLUGINS_DOMAIN, 0, "Plugin support is disabled.");
+ return FALSE;
+#endif /* PURPLE_PLUGINS */
+}
+
+gboolean
+purple_plugin_is_loaded(const PurplePlugin *plugin)
+{
+#ifdef PURPLE_PLUGINS
+ g_return_val_if_fail(plugin != NULL, FALSE);
+
+ return (gplugin_plugin_get_state(plugin) == GPLUGIN_PLUGIN_STATE_LOADED);
+
+#else
+ return FALSE;
+#endif
+}
+
+const gchar *
+purple_plugin_get_filename(const PurplePlugin *plugin)
+{
+#ifdef PURPLE_PLUGINS
+ g_return_val_if_fail(plugin != NULL, NULL);
+
+ return gplugin_plugin_get_filename(plugin);
+
+#else
+ return NULL;
+#endif
+}
+
+PurplePluginInfo *
+purple_plugin_get_info(const PurplePlugin *plugin)
+{
+#ifdef PURPLE_PLUGINS
+ GPluginPluginInfo *info;
+
+ g_return_val_if_fail(plugin != NULL, NULL);
+
+ info = gplugin_plugin_get_info(plugin);
+
+ /* GPlugin refs the plugin info object before returning it. This workaround
+ * is to avoid managing the reference counts everywhere in our codebase
+ * where we use the plugin info. The plugin info instance is guaranteed to
+ * exist as long as the plugin exists. */
+ g_object_unref(info);
+
+ if (PURPLE_IS_PLUGIN_INFO(info))
+ return PURPLE_PLUGIN_INFO(info);
+ else
+ return NULL;
+#else
+ return NULL;
+#endif
+}
+
+void
+purple_plugin_disable(PurplePlugin *plugin)
+{
+#ifdef PURPLE_PLUGINS
+ g_return_if_fail(plugin != NULL);
+
+ if (!g_list_find(plugins_to_disable, plugin))
+ plugins_to_disable = g_list_prepend(plugins_to_disable, plugin);
+#endif
+}
+
+GType
+purple_plugin_register_type(PurplePlugin *plugin, GType parent,
+ const gchar *name, const GTypeInfo *info,
+ GTypeFlags flags)
+{
+#ifdef PURPLE_PLUGINS
+ g_return_val_if_fail(GPLUGIN_IS_NATIVE_PLUGIN(plugin), G_TYPE_INVALID);
+
+ return gplugin_native_plugin_register_type(GPLUGIN_NATIVE_PLUGIN(plugin),
+ parent, name, info, flags);
+
+#else
+ return G_TYPE_INVALID;
+#endif
+}
+
+void
+purple_plugin_add_interface(PurplePlugin *plugin, GType instance_type,
+ GType interface_type,
+ const GInterfaceInfo *interface_info)
+{
+#ifdef PURPLE_PLUGINS
+ g_return_if_fail(GPLUGIN_IS_NATIVE_PLUGIN(plugin));
+
+ gplugin_native_plugin_add_interface(GPLUGIN_NATIVE_PLUGIN(plugin),
+ instance_type, interface_type,
+ interface_info);
+#endif
+}
+
+gboolean
+purple_plugin_is_internal(const PurplePlugin *plugin)
+{
+ PurplePluginInfo *info;
+
+ g_return_val_if_fail(plugin != NULL, FALSE);
+
+ info = purple_plugin_get_info(plugin);
+ if (!info)
+ return TRUE;
+
+ return (purple_plugin_info_get_flags(info) &
+ PURPLE_PLUGIN_INFO_FLAGS_INTERNAL);
+}
+
+GSList *
+purple_plugin_get_dependent_plugins(const PurplePlugin *plugin)
+{
+#warning TODO: Implement this when GPlugin can return dependent plugins.
+
+#ifdef PURPLE_PLUGINS
+ return NULL;
+#else
+ return NULL;
+#endif
+}
+
+/**************************************************************************
+ * GObject code for PurplePluginInfo
+ **************************************************************************/
+/* GObject initialization function */
+static void
+purple_plugin_info_init(GTypeInstance *instance, gpointer klass)
+{
+ PURPLE_DBUS_REGISTER_POINTER(PURPLE_PLUGIN_INFO(instance), PurplePluginInfo);
+}
+
+/* Set method for GObject properties */
+static void
+purple_plugin_info_set_property(GObject *obj, guint param_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ PurplePluginInfo *info = PURPLE_PLUGIN_INFO(obj);
+ PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info);
+
+ switch (param_id) {
+ case PROP_UI_REQUIREMENT:
+ priv->ui_requirement = g_strdup(g_value_get_string(value));
+ break;
+ case PROP_ACTIONS_CB:
+ priv->actions_cb = g_value_get_pointer(value);
+ break;
+ case PROP_EXTRA_CB:
+ priv->extra_cb = g_value_get_pointer(value);
+ break;
+ case PROP_PREF_FRAME_CB:
+ priv->pref_frame_cb = g_value_get_pointer(value);
+ break;
+ case PROP_PREF_REQUEST_CB:
+ priv->pref_request_cb = g_value_get_pointer(value);
+ break;
+ case PROP_FLAGS:
+ priv->flags = g_value_get_flags(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* Get method for GObject properties */
+static void
+purple_plugin_info_get_property(GObject *obj, guint param_id, GValue *value,
+ GParamSpec *pspec)
+{
+ PurplePluginInfo *info = PURPLE_PLUGIN_INFO(obj);
+
+ switch (param_id) {
+ case PROP_ACTIONS_CB:
+ g_value_set_pointer(value,
+ purple_plugin_info_get_actions_cb(info));
+ break;
+ case PROP_EXTRA_CB:
+ g_value_set_pointer(value,
+ purple_plugin_info_get_extra_cb(info));
+ break;
+ case PROP_PREF_FRAME_CB:
+ g_value_set_pointer(value,
+ purple_plugin_info_get_pref_frame_cb(info));
+ break;
+ case PROP_PREF_REQUEST_CB:
+ g_value_set_pointer(value,
+ purple_plugin_info_get_pref_request_cb(info));
+ break;
+ case PROP_FLAGS:
+ g_value_set_flags(value, purple_plugin_info_get_flags(info));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+ break;
+ }
+}
+
+/* Called when done constructing */
+static void
+purple_plugin_info_constructed(GObject *object)
+{
+ PurplePluginInfo *info = PURPLE_PLUGIN_INFO(object);
+ PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info);
+ const char *id = purple_plugin_info_get_id(info);
+ guint32 version;
+
+ parent_class->constructed(object);
+
+ if (id == NULL || *id == '\0')
+ priv->error = g_strdup(_("This plugin has not defined an ID."));
+
+ if (priv->ui_requirement && !purple_strequal(priv->ui_requirement, purple_core_get_ui()))
+ {
+ priv->error = g_strdup_printf(_("You are using %s, but this plugin requires %s."),
+ purple_core_get_ui(), priv->ui_requirement);
+ purple_debug_error("plugins", "%s is not loadable: The UI requirement is not met. (%s)\n",
+ id, priv->error);
+ }
+
+ version = purple_plugin_info_get_abi_version(info);
+ if (PURPLE_PLUGIN_ABI_MAJOR_VERSION(version) != PURPLE_MAJOR_VERSION ||
+ PURPLE_PLUGIN_ABI_MINOR_VERSION(version) > PURPLE_MINOR_VERSION)
+ {
+ priv->error = g_strdup_printf(_("Your libpurple version is %d.%d.x (need %d.%d.x)"),
+ PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION,
+ PURPLE_PLUGIN_ABI_MAJOR_VERSION(version),
+ PURPLE_PLUGIN_ABI_MINOR_VERSION(version));
+ purple_debug_error("plugins", "%s is not loadable: libpurple version is %d.%d.x (need %d.%d.x)\n",
+ id, PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION,
+ PURPLE_PLUGIN_ABI_MAJOR_VERSION(version),
+ PURPLE_PLUGIN_ABI_MINOR_VERSION(version));
+ }
+}
+
+/* GObject finalize function */
+static void
+purple_plugin_info_finalize(GObject *object)
+{
+ PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(object);
+
+ g_free(priv->ui_requirement);
+ g_free(priv->error);
+
+ PURPLE_DBUS_UNREGISTER_POINTER(object);
+
+ parent_class->finalize(object);
+}
+
+/* Class initializer function */
+static void purple_plugin_info_class_init(PurplePluginInfoClass *klass)
+{
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ g_type_class_add_private(klass, sizeof(PurplePluginInfoPrivate));
+
+ obj_class->constructed = purple_plugin_info_constructed;
+ obj_class->finalize = purple_plugin_info_finalize;
+
+ /* Setup properties */
+ obj_class->get_property = purple_plugin_info_get_property;
+ obj_class->set_property = purple_plugin_info_set_property;
+
+ g_object_class_install_property(obj_class, PROP_UI_REQUIREMENT,
+ g_param_spec_string("ui-requirement",
+ "UI Requirement",
+ "ID of UI that is required by this plugin", NULL,
+ G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property(obj_class, PROP_ACTIONS_CB,
+ g_param_spec_pointer("actions-cb",
+ "Plugin actions",
+ "Callback that returns list of plugin's actions",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property(obj_class, PROP_EXTRA_CB,
+ g_param_spec_pointer("extra-cb",
+ "Extra info callback",
+ "Callback that returns extra info about the plugin",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property(obj_class, PROP_PREF_FRAME_CB,
+ g_param_spec_pointer("pref-frame-cb",
+ "Preferences frame callback",
+ "The callback that returns the preferences frame",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property(obj_class, PROP_PREF_REQUEST_CB,
+ g_param_spec_pointer("pref-request-cb",
+ "Preferences request callback",
+ "Callback that returns preferences request handle",
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property(obj_class, PROP_FLAGS,
+ g_param_spec_flags("flags",
+ "Plugin flags",
+ "The flags for the plugin",
+ PURPLE_TYPE_PLUGIN_INFO_FLAGS, 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
+
+/**************************************************************************
+ * PluginInfo API
+ **************************************************************************/
+GType
+purple_plugin_info_get_type(void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY(type == 0)) {
+ static const GTypeInfo info = {
+ .class_size = sizeof(PurplePluginInfoClass),
+ .class_init = (GClassInitFunc)purple_plugin_info_class_init,
+ .instance_size = sizeof(PurplePluginInfo),
+ .instance_init = (GInstanceInitFunc)purple_plugin_info_init,
+ };
+
+ type = g_type_register_static(
+#ifdef PURPLE_PLUGINS
+ GPLUGIN_TYPE_PLUGIN_INFO,
+#else
+ G_TYPE_OBJECT,
+#endif
+ "PurplePluginInfo", &info, 0);
+ }
+
+ return type;
+}
+
+PurplePluginInfo *
+purple_plugin_info_new(const char *first_property, ...)
+{
+ GObject *info;
+ va_list var_args;
+
+ /* at least ID is required */
+ if (!first_property)
+ return NULL;
+
+ va_start(var_args, first_property);
+ info = g_object_new_valist(PURPLE_TYPE_PLUGIN_INFO, first_property,
+ var_args);
+ va_end(var_args);
+
+ return PURPLE_PLUGIN_INFO(info);
+}
+
+const gchar *
+purple_plugin_info_get_id(const PurplePluginInfo *info)
+{
+#ifdef PURPLE_PLUGINS
+ g_return_val_if_fail(info != NULL, NULL);
+
+ return gplugin_plugin_info_get_id(GPLUGIN_PLUGIN_INFO(info));
+
+#else
+ return NULL;
+#endif
+}
+
+const gchar *
+purple_plugin_info_get_name(const PurplePluginInfo *info)
+{
+#ifdef PURPLE_PLUGINS
+ g_return_val_if_fail(info != NULL, NULL);
+
+ return gplugin_plugin_info_get_name(GPLUGIN_PLUGIN_INFO(info));
+
+#else
+ return NULL;
+#endif
+}
+
+const gchar *
+purple_plugin_info_get_version(const PurplePluginInfo *info)
+{
+#ifdef PURPLE_PLUGINS
+ g_return_val_if_fail(info != NULL, NULL);
+
+ return gplugin_plugin_info_get_version(GPLUGIN_PLUGIN_INFO(info));
+
+#else
+ return NULL;
+#endif
+}
+
+const gchar *
+purple_plugin_info_get_category(const PurplePluginInfo *info)
+{
+#ifdef PURPLE_PLUGINS
+ g_return_val_if_fail(info != NULL, NULL);
+
+ return gplugin_plugin_info_get_category(GPLUGIN_PLUGIN_INFO(info));
+
+#else
+ return NULL;
+#endif
+}
+
+const gchar *
+purple_plugin_info_get_summary(const PurplePluginInfo *info)
+{
+#ifdef PURPLE_PLUGINS
+ g_return_val_if_fail(info != NULL, NULL);
+
+ return gplugin_plugin_info_get_summary(GPLUGIN_PLUGIN_INFO(info));
+
+#else
+ return NULL;
+#endif
+}
+
+const gchar *
+purple_plugin_info_get_description(const PurplePluginInfo *info)
+{
+#ifdef PURPLE_PLUGINS
+ g_return_val_if_fail(info != NULL, NULL);
+
+ return gplugin_plugin_info_get_description(GPLUGIN_PLUGIN_INFO(info));
+
+#else
+ return NULL;
+#endif
+}
+
+const gchar * const *
+purple_plugin_info_get_authors(const PurplePluginInfo *info)
+{
+#ifdef PURPLE_PLUGINS
+ g_return_val_if_fail(info != NULL, NULL);
+
+ return gplugin_plugin_info_get_authors(GPLUGIN_PLUGIN_INFO(info));
+
+#else
+ return NULL;
+#endif
+}
+
+const gchar *
+purple_plugin_info_get_website(const PurplePluginInfo *info)
+{
+#ifdef PURPLE_PLUGINS
+ g_return_val_if_fail(info != NULL, NULL);
+
+ return gplugin_plugin_info_get_website(GPLUGIN_PLUGIN_INFO(info));
+
+#else
+ return NULL;
+#endif
+}
+
+const gchar *
+purple_plugin_info_get_icon(const PurplePluginInfo *info)
+{
+#ifdef PURPLE_PLUGINS
+ g_return_val_if_fail(info != NULL, NULL);
+
+ return gplugin_plugin_info_get_icon(GPLUGIN_PLUGIN_INFO(info));
+
+#else
+ return NULL;
+#endif
+}
+
+const gchar *
+purple_plugin_info_get_license_id(const PurplePluginInfo *info)
+{
+#ifdef PURPLE_PLUGINS
+ g_return_val_if_fail(info != NULL, NULL);
+
+ return gplugin_plugin_info_get_license_id(GPLUGIN_PLUGIN_INFO(info));
+
+#else
+ return NULL;
+#endif
+}
+
+const gchar *
+purple_plugin_info_get_license_text(const PurplePluginInfo *info)
+{
+#ifdef PURPLE_PLUGINS
+ g_return_val_if_fail(info != NULL, NULL);
+
+ return gplugin_plugin_info_get_license_text(GPLUGIN_PLUGIN_INFO(info));
+
+#else
+ return NULL;
+#endif
+}
+
+const gchar *
+purple_plugin_info_get_license_url(const PurplePluginInfo *info)
+{
+#ifdef PURPLE_PLUGINS
+ g_return_val_if_fail(info != NULL, NULL);
+
+ return gplugin_plugin_info_get_license_url(GPLUGIN_PLUGIN_INFO(info));
+
+#else
+ return NULL;
+#endif
+}
+
+const gchar * const *
+purple_plugin_info_get_dependencies(const PurplePluginInfo *info)
+{
+#ifdef PURPLE_PLUGINS
+ g_return_val_if_fail(info != NULL, NULL);
+
+ return gplugin_plugin_info_get_dependencies(GPLUGIN_PLUGIN_INFO(info));
+
+#else
+ return NULL;
+#endif
+}
+
+guint32
+purple_plugin_info_get_abi_version(const PurplePluginInfo *info)
+{
+#ifdef PURPLE_PLUGINS
+ g_return_val_if_fail(info != NULL, 0);
+
+ return gplugin_plugin_info_get_abi_version(GPLUGIN_PLUGIN_INFO(info));
+
+#else
+ return 0;
+#endif
+}
+
+PurplePluginActionsCb
+purple_plugin_info_get_actions_cb(const PurplePluginInfo *info)
+{
+ PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->actions_cb;
+}
+
+PurplePluginExtraCb
+purple_plugin_info_get_extra_cb(const PurplePluginInfo *info)
+{
+ PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->extra_cb;
+}
+
+PurplePluginPrefFrameCb
+purple_plugin_info_get_pref_frame_cb(const PurplePluginInfo *info)
+{
+ PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->pref_frame_cb;
+}
+
+PurplePluginPrefRequestCb
+purple_plugin_info_get_pref_request_cb(const PurplePluginInfo *info)
+{
+ PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->pref_request_cb;
+}
+
+PurplePluginInfoFlags
+purple_plugin_info_get_flags(const PurplePluginInfo *info)
+{
+ PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info);
+
+ g_return_val_if_fail(priv != NULL, 0);
+
+ return priv->flags;
+}
+
+const gchar *
+purple_plugin_info_get_error(const PurplePluginInfo *info)
+{
+ PurplePluginInfoPrivate *priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info);
+
+ g_return_val_if_fail(priv != NULL, NULL);
+
+ return priv->error;
+}
+
+void
+purple_plugin_info_set_ui_data(PurplePluginInfo *info, gpointer ui_data)
+{
+ g_return_if_fail(PURPLE_IS_PLUGIN_INFO(info));
+
+ info->ui_data = ui_data;
+}
+
+gpointer
+purple_plugin_info_get_ui_data(const PurplePluginInfo *info)
+{
+ g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
+
+ return info->ui_data;
+}
+
+/**************************************************************************
+ * PluginAction API
+ **************************************************************************/
+PurplePluginAction *
+purple_plugin_action_new(const char* label, PurplePluginActionCb callback)
+{
+ PurplePluginAction *action;
+
+ g_return_val_if_fail(label != NULL && callback != NULL, NULL);
+
+ action = g_new0(PurplePluginAction, 1);
+
+ action->label = g_strdup(label);
+ action->callback = callback;
+
+ return action;
+}
+
+void
+purple_plugin_action_free(PurplePluginAction *action)
+{
+ g_return_if_fail(action != NULL);
+
+ g_free(action->label);
+ g_free(action);
+}
+
+static PurplePluginAction *
+purple_plugin_action_copy(PurplePluginAction *action)
+{
+ PurplePluginAction *action_copy;
+
+ g_return_val_if_fail(action != NULL, NULL);
+
+ action_copy = g_new(PurplePluginAction, 1);
+ *action_copy = *action;
+
+ action_copy->label = g_strdup(action->label);
+
+ return action_copy;
+}
+
+GType
+purple_plugin_action_get_type(void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY(type == 0)) {
+ type = g_boxed_type_register_static("PurplePluginAction",
+ (GBoxedCopyFunc)purple_plugin_action_copy,
+ (GBoxedFreeFunc)purple_plugin_action_free);
+ }
+
+ return type;
+}
+
+/**************************************************************************
+ * Plugins API
+ **************************************************************************/
+GList *
+purple_plugins_find_all(void)
+{
+#ifdef PURPLE_PLUGINS
+ GList *ret = NULL, *ids, *l;
+ GSList *plugins, *ll;
+
+ ids = gplugin_manager_list_plugins();
+
+ for (l = ids; l; l = l->next) {
+ plugins = gplugin_manager_find_plugins(l->data);
+
+ for (ll = plugins; ll; ll = ll->next) {
+ PurplePlugin *plugin = PURPLE_PLUGIN(ll->data);
+ if (purple_plugin_get_info(plugin))
+ ret = g_list_append(ret, plugin);
+ }
+
+ gplugin_manager_free_plugin_list(plugins);
+ }
+ g_list_free(ids);
+
+ return ret;
+
+#else
+ return NULL;
+#endif
+}
+
+GList *
+purple_plugins_get_loaded(void)
+{
+#ifdef PURPLE_PLUGINS
+ return loaded_plugins;
+#else
+ return NULL;
+#endif
+}
+
+void
+purple_plugins_add_search_path(const gchar *path)
+{
+#ifdef PURPLE_PLUGINS
+ gplugin_manager_append_path(path);
+#endif
+}
+
+void
+purple_plugins_refresh(void)
+{
+#ifdef PURPLE_PLUGINS
+ GList *plugins, *l;
+
+ gplugin_manager_refresh();
+
+ plugins = purple_plugins_find_all();
+ for (l = plugins; l != NULL; l = l->next) {
+ PurplePlugin *plugin = PURPLE_PLUGIN(l->data);
+ PurplePluginInfo *info;
+ PurplePluginInfoPrivate *priv;
+
+ if (purple_plugin_is_loaded(plugin))
+ continue;
+
+ info = purple_plugin_get_info(plugin);
+ priv = PURPLE_PLUGIN_INFO_GET_PRIVATE(info);
+
+ if (!priv->unloaded && purple_plugin_info_get_flags(info) &
+ PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD) {
+ purple_debug_info("plugins", "Auto-loading plugin %s\n",
+ purple_plugin_get_filename(plugin));
+ purple_plugin_load(plugin, NULL);
+ }
+ }
+
+ g_list_free(plugins);
+#endif
+}
+
+PurplePlugin *
+purple_plugins_find_plugin(const gchar *id)
+{
+#ifdef PURPLE_PLUGINS
+ PurplePlugin *plugin;
+
+ g_return_val_if_fail(id != NULL && *id != '\0', NULL);
+
+ plugin = gplugin_manager_find_plugin(id);
+
+ /* GPlugin refs the plugin object before returning it. This workaround is
+ * to avoid managing the reference counts everywhere in our codebase where
+ * we use plugin instances. A plugin object will exist till the plugins
+ * subsystem is uninitialized. */
+ g_object_unref(plugin);
+
+ return plugin;
+
+#else
+ return NULL;
+#endif
+}
+
+PurplePlugin *
+purple_plugins_find_by_filename(const char *filename)
+{
+ GList *plugins, *l;
+
+ g_return_val_if_fail(filename != NULL && *filename != '\0', NULL);
+
+ plugins = purple_plugins_find_all();
+
+ for (l = plugins; l != NULL; l = l->next) {
+ PurplePlugin *plugin = PURPLE_PLUGIN(l->data);
+
+ if (purple_strequal(purple_plugin_get_filename(plugin), filename)) {
+ g_list_free(plugins);
+ return plugin;
+ }
+ }
+ g_list_free(plugins);
+
+ return NULL;
+}
+
+void
+purple_plugins_save_loaded(const char *key)
+{
+#ifdef PURPLE_PLUGINS
+ GList *pl;
+ GList *files = NULL;
+
+ g_return_if_fail(key != NULL && *key != '\0');
+
+ for (pl = purple_plugins_get_loaded(); pl != NULL; pl = pl->next) {
+ PurplePlugin *plugin = PURPLE_PLUGIN(pl->data);
+ PurplePluginInfo *info = purple_plugin_get_info(plugin);
+ if (!info)
+ continue;
+
+ if (purple_plugin_info_get_flags(info) &
+ PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD)
+ continue;
+
+ if (!g_list_find(plugins_to_disable, plugin))
+ files = g_list_append(files, (gchar *)purple_plugin_get_filename(plugin));
+ }
+
+ purple_prefs_set_path_list(key, files);
+ g_list_free(files);
+#endif
+}
+
+void
+purple_plugins_load_saved(const char *key)
+{
+#ifdef PURPLE_PLUGINS
+ GList *l, *files;
+
+ g_return_if_fail(key != NULL && *key != '\0');
+
+ files = purple_prefs_get_path_list(key);
+
+ for (l = files; l; l = l->next)
+ {
+ char *file;
+ PurplePlugin *plugin;
+
+ if (l->data == NULL)
+ continue;
+
+ file = l->data;
+ plugin = purple_plugins_find_by_filename(file);
+
+ if (plugin) {
+ purple_debug_info("plugins", "Loading saved plugin %s\n", file);
+ purple_plugin_load(plugin, NULL);
+ } else {
+ purple_debug_error("plugins", "Unable to find saved plugin %s\n", file);
+ }
+
+ g_free(l->data);
+ }
+
+ g_list_free(files);
+#endif /* PURPLE_PLUGINS */
+}
+
+/**************************************************************************
+ * Plugins Subsystem API
+ **************************************************************************/
+void *
+purple_plugins_get_handle(void)
+{
+ static int handle;
+
+ return &handle;
+}
+
+void
+purple_plugins_init(void)
+{
+ void *handle = purple_plugins_get_handle();
+
+ purple_signal_register(handle, "plugin-load",
+ purple_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, PURPLE_TYPE_PLUGIN);
+ purple_signal_register(handle, "plugin-unload",
+ purple_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, PURPLE_TYPE_PLUGIN);
+
+#ifdef PURPLE_PLUGINS
+ gplugin_init();
+ gplugin_manager_add_default_paths();
+
+ purple_plugins_add_search_path(LIBDIR);
+
+ g_signal_connect(gplugin_manager_get_instance(), "loading-plugin",
+ G_CALLBACK(plugin_loading_cb), NULL);
+ g_signal_connect(gplugin_manager_get_instance(), "loaded-plugin",
+ G_CALLBACK(plugin_loaded_cb), NULL);
+ g_signal_connect(gplugin_manager_get_instance(), "unloading-plugin",
+ G_CALLBACK(plugin_unloading_cb), NULL);
+ g_signal_connect(gplugin_manager_get_instance(), "unloaded-plugin",
+ G_CALLBACK(plugin_unloaded_cb), NULL);
+
+ purple_plugins_refresh();
+#endif
+}
+
+void
+purple_plugins_uninit(void)
+{
+ void *handle = purple_plugins_get_handle();
+
+#ifdef PURPLE_PLUGINS
+ purple_debug_info("plugins", "Unloading all plugins\n");
+ while (loaded_plugins != NULL)
+ purple_plugin_unload(loaded_plugins->data, NULL);
+#endif
+
+ purple_signals_disconnect_by_handle(handle);
+ purple_signals_unregister_by_instance(handle);
+
+#ifdef PURPLE_PLUGINS
+ gplugin_uninit();
+#endif
+}