Sun, 28 Jul 2013 18:27:54 +0530
Started the new GObject plugin API
| libpurple/plugin.c | file | annotate | diff | comparison | revisions | |
| libpurple/plugin.h | file | annotate | diff | comparison | revisions |
--- a/libpurple/plugin.c Sat Jul 27 16:46:06 2013 +0530 +++ b/libpurple/plugin.c Sun Jul 28 18:27:54 2013 +0530 @@ -19,1596 +19,118 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#define _PURPLE_PLUGIN_C_ - -#include "internal.h" - -#include "accountopt.h" -#include "core.h" -#include "dbus-maybe.h" -#include "debug.h" -#include "notify.h" -#include "prefs.h" -#include "prpl.h" -#include "request.h" -#include "signals.h" -#include "util.h" -#include "valgrind.h" -#include "version.h" +#include "plugin.h" -typedef struct -{ - GHashTable *commands; - size_t command_count; - -} PurplePluginIpcInfo; +#define PURPLE_PLUGIN_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_PLUGIN, PurplePluginPrivate)) -typedef struct -{ - PurpleCallback func; - PurpleSignalMarshalFunc marshal; - - int num_params; - GType *param_types; - GType ret_type; - -} PurplePluginIpcCommand; +/** @copydoc _PurplePluginPrivate */ +typedef struct _PurplePluginPrivate PurplePluginPrivate; -static GList *search_paths = NULL; -static GList *plugins = NULL; -static GList *loaded_plugins = NULL; -static GList *protocol_plugins = NULL; -#ifdef PURPLE_PLUGINS -static GList *load_queue = NULL; -static GList *plugin_loaders = NULL; -static GList *plugins_to_disable = NULL; -#endif - -#ifdef PURPLE_PLUGINS - -static gboolean -has_file_extension(const char *filename, const char *ext) -{ - int len, extlen; +/************************************************************************** + * Private data + **************************************************************************/ +struct _PurplePluginPrivate { + gboolean unloadable; +}; - if (filename == NULL || *filename == '\0' || ext == NULL) - return 0; - - extlen = strlen(ext); - len = strlen(filename) - extlen; - - if (len < 0) - return 0; +/* Plugin property enums */ +enum +{ + PROP_0, + PROP_UNLOADABLE, + PROP_LAST +}; - return (strncmp(filename + len, ext, extlen) == 0); -} - -static gboolean -is_native(const char *filename) -{ - const char *last_period; - - last_period = strrchr(filename, '.'); - if (last_period == NULL) - return FALSE; +static GPluginPluginImplementationClass *parent_class; - return !(strcmp(last_period, ".dll") & - strcmp(last_period, ".sl") & - strcmp(last_period, ".so")); -} +/************************************************************************** + * Plugin API + **************************************************************************/ -static char * -purple_plugin_get_basename(const char *filename) -{ - const char *basename; - const char *last_period; - - basename = strrchr(filename, G_DIR_SEPARATOR); - if (basename != NULL) - basename++; - else - basename = filename; - if (is_native(basename) && - ((last_period = strrchr(basename, '.')) != NULL)) - return g_strndup(basename, (last_period - basename)); - - return g_strdup(basename); -} - -static gboolean -loader_supports_file(PurplePlugin *loader, const char *filename) -{ - GList *exts; - - for (exts = PURPLE_PLUGIN_LOADER_INFO(loader)->exts; exts != NULL; exts = exts->next) { - if (has_file_extension(filename, (char *)exts->data)) { - return TRUE; - } - } - - return FALSE; -} +/************************************************************************** + * GObject code + **************************************************************************/ +/* GObject Property names */ +#define PROP_UNLOADABLE_S "unloadable" -static PurplePlugin * -find_loader_for_plugin(const PurplePlugin *plugin) +/* Set method for GObject properties */ +static void +purple_plugin_set_property(GObject *obj, guint param_id, const GValue *value, + GParamSpec *pspec) { - PurplePlugin *loader; - GList *l; - - if (plugin->path == NULL) - return NULL; - - for (l = purple_plugins_get_loaded(); l != NULL; l = l->next) { - loader = l->data; - - if (loader->info->type == PURPLE_PLUGIN_LOADER && - loader_supports_file(loader, plugin->path)) { - - return loader; - } - - loader = NULL; - } + PurplePlugin *plugin = PURPLE_PLUGIN(obj); - return NULL; -} - -#endif /* PURPLE_PLUGINS */ - -/** - * Negative if a before b, 0 if equal, positive if a after b. - */ -static gint -compare_prpl(PurplePlugin *a, PurplePlugin *b) -{ - if(PURPLE_IS_PROTOCOL_PLUGIN(a)) { - if(PURPLE_IS_PROTOCOL_PLUGIN(b)) - return strcmp(a->info->name, b->info->name); - else - return -1; - } else { - if(PURPLE_IS_PROTOCOL_PLUGIN(b)) - return 1; - else - return 0; + switch (param_id) { + case PROP_UNLOADABLE: + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); + break; } } -PurplePlugin * -purple_plugin_new(gboolean native, const char *path) -{ - PurplePlugin *plugin; - - plugin = g_new0(PurplePlugin, 1); - - plugin->native_plugin = native; - plugin->path = g_strdup(path); - - PURPLE_DBUS_REGISTER_POINTER(plugin, PurplePlugin); - - return plugin; -} - -PurplePlugin * -purple_plugin_probe(const char *filename) +/* Get method for GObject properties */ +static void +purple_plugin_get_property(GObject *obj, guint param_id, GValue *value, + GParamSpec *pspec) { -#ifdef PURPLE_PLUGINS - PurplePlugin *plugin = NULL; - PurplePlugin *loader; - gpointer unpunned; - gchar *basename = NULL; - gboolean (*purple_init_plugin)(PurplePlugin *); - - purple_debug_misc("plugins", "probing %s\n", filename); - g_return_val_if_fail(filename != NULL, NULL); - - if (!g_file_test(filename, G_FILE_TEST_EXISTS)) - return NULL; - - /* If this plugin has already been probed then exit */ - basename = purple_plugin_get_basename(filename); - plugin = purple_plugins_find_with_basename(basename); - g_free(basename); - if (plugin != NULL) - { - if (purple_strequal(filename, plugin->path)) - return plugin; - else if (!purple_plugin_is_unloadable(plugin)) - { - purple_debug_warning("plugins", "Not loading %s. " - "Another plugin with the same name (%s) has already been loaded.\n", - filename, plugin->path); - return plugin; - } - else - { - /* The old plugin was a different file and it was unloadable. - * There's no guarantee that this new file with the same name - * will be loadable, but unless it fails in one of the silent - * ways and the first one didn't, it's not any worse. The user - * will still see a greyed-out plugin, which is what we want. */ - purple_plugin_destroy(plugin); - } - } - - plugin = purple_plugin_new(has_file_extension(filename, G_MODULE_SUFFIX), filename); - - if (plugin->native_plugin) { - const char *error; -#ifdef _WIN32 - /* Suppress error popups for failing to load plugins */ - UINT old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS); -#endif - - /* - * We pass G_MODULE_BIND_LOCAL here to prevent symbols from - * plugins being added to the global name space. - * - * G_MODULE_BIND_LOCAL was added in glib 2.3.3. - */ - plugin->handle = g_module_open(filename, G_MODULE_BIND_LOCAL); - - if (plugin->handle == NULL) - { - const char *error = g_module_error(); - if (error != NULL && purple_str_has_prefix(error, filename)) - { - error = error + strlen(filename); - - /* These are just so we don't crash. If we - * got this far, they should always be true. */ - if (*error == ':') - error++; - if (*error == ' ') - error++; - } - - if (error == NULL || !*error) - { - plugin->error = g_strdup(_("Unknown error")); - purple_debug_error("plugins", "%s is not loadable: Unknown error\n", - plugin->path); - } - else - { - plugin->error = g_strdup(error); - purple_debug_error("plugins", "%s is not loadable: %s\n", - plugin->path, plugin->error); - } - plugin->handle = g_module_open(filename, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); - - if (plugin->handle == NULL) - { -#ifdef _WIN32 - /* Restore the original error mode */ - SetErrorMode(old_error_mode); -#endif - purple_plugin_destroy(plugin); - return NULL; - } - else - { - /* We were able to load the plugin with lazy symbol binding. - * This means we're missing some symbol. Mark it as - * unloadable and keep going so we get the info to display - * to the user so they know to rebuild this plugin. */ - plugin->unloadable = TRUE; - } - } - - if (!g_module_symbol(plugin->handle, "purple_init_plugin", - &unpunned)) - { - purple_debug_error("plugins", "%s is not usable because the " - "'purple_init_plugin' symbol could not be " - "found. Does the plugin call the " - "PURPLE_INIT_PLUGIN() macro?\n", plugin->path); - - g_module_close(plugin->handle); - error = g_module_error(); - if (error != NULL) - purple_debug_error("plugins", "Error closing module %s: %s\n", - plugin->path, error); - plugin->handle = NULL; - -#ifdef _WIN32 - /* Restore the original error mode */ - SetErrorMode(old_error_mode); -#endif - purple_plugin_destroy(plugin); - return NULL; - } - purple_init_plugin = unpunned; - -#ifdef _WIN32 - /* Restore the original error mode */ - SetErrorMode(old_error_mode); -#endif - } - else { - loader = find_loader_for_plugin(plugin); - - if (loader == NULL) { - purple_plugin_destroy(plugin); - return NULL; - } - - purple_init_plugin = PURPLE_PLUGIN_LOADER_INFO(loader)->probe; - } - - if (!purple_init_plugin(plugin) || plugin->info == NULL) - { - purple_plugin_destroy(plugin); - return NULL; - } - else if (plugin->info->ui_requirement && - !purple_strequal(plugin->info->ui_requirement, purple_core_get_ui())) - { - plugin->error = g_strdup_printf(_("You are using %s, but this plugin requires %s."), - purple_core_get_ui(), plugin->info->ui_requirement); - purple_debug_error("plugins", "%s is not loadable: The UI requirement is not met. (%s)\n", plugin->path, plugin->error); - plugin->unloadable = TRUE; - return plugin; - } - - /* - * Check to make sure a plugin has defined an id. - * Not having this check caused purple_plugin_unload to - * enter an infinite loop in certain situations by passing - * purple_find_plugin_by_id a NULL value. -- ecoffey - */ - if (plugin->info->id == NULL || *plugin->info->id == '\0') - { - plugin->error = g_strdup(_("This plugin has not defined an ID.")); - purple_debug_error("plugins", "%s is not loadable: info->id is not defined.\n", plugin->path); - plugin->unloadable = TRUE; - return plugin; - } - - /* Really old plugins. */ - if (plugin->info->magic != PURPLE_PLUGIN_MAGIC) - { - if (plugin->info->magic >= 2 && plugin->info->magic <= 4) - { - struct _PurplePluginInfo2 - { - unsigned int api_version; - PurplePluginType type; - char *ui_requirement; - unsigned long flags; - GList *dependencies; - PurplePluginPriority priority; - - char *id; - char *name; - char *version; - char *summary; - char *description; - char *author; - char *homepage; - - gboolean (*load)(PurplePlugin *plugin); - gboolean (*unload)(PurplePlugin *plugin); - void (*destroy)(PurplePlugin *plugin); - - void *ui_info; - void *extra_info; - PurplePluginUiInfo *prefs_info; - GList *(*actions)(PurplePlugin *plugin, gpointer context); - } *info2 = (struct _PurplePluginInfo2 *)plugin->info; - - /* This leaks... but only for ancient plugins, so deal with it. */ - plugin->info = g_new0(PurplePluginInfo, 1); - - /* We don't really need all these to display the plugin info, but - * I'm copying them all for good measure. */ - plugin->info->magic = info2->api_version; - plugin->info->type = info2->type; - plugin->info->ui_requirement = info2->ui_requirement; - plugin->info->flags = info2->flags; - plugin->info->dependencies = info2->dependencies; - plugin->info->id = info2->id; - plugin->info->name = info2->name; - plugin->info->version = info2->version; - plugin->info->summary = info2->summary; - plugin->info->description = info2->description; - plugin->info->author = info2->author; - plugin->info->homepage = info2->homepage; - plugin->info->load = info2->load; - plugin->info->unload = info2->unload; - plugin->info->destroy = info2->destroy; - plugin->info->ui_info = info2->ui_info; - plugin->info->extra_info = info2->extra_info; - - if (info2->api_version >= 3) - plugin->info->prefs_info = info2->prefs_info; - - if (info2->api_version >= 4) - plugin->info->actions = info2->actions; - - - plugin->error = g_strdup_printf(_("Plugin magic mismatch %d (need %d)"), - plugin->info->magic, PURPLE_PLUGIN_MAGIC); - purple_debug_error("plugins", "%s is not loadable: Plugin magic mismatch %d (need %d)\n", - plugin->path, plugin->info->magic, PURPLE_PLUGIN_MAGIC); - plugin->unloadable = TRUE; - return plugin; - } - - purple_debug_error("plugins", "%s is not loadable: Plugin magic mismatch %d (need %d)\n", - plugin->path, plugin->info->magic, PURPLE_PLUGIN_MAGIC); - purple_plugin_destroy(plugin); - return NULL; - } - - if (plugin->info->major_version != PURPLE_MAJOR_VERSION || - plugin->info->minor_version > PURPLE_MINOR_VERSION) - { - plugin->error = g_strdup_printf(_("ABI version mismatch %d.%d.x (need %d.%d.x)"), - plugin->info->major_version, plugin->info->minor_version, - PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION); - purple_debug_error("plugins", "%s is not loadable: ABI version mismatch %d.%d.x (need %d.%d.x)\n", - plugin->path, plugin->info->major_version, plugin->info->minor_version, - PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION); - plugin->unloadable = TRUE; - return plugin; - } - - if (plugin->info->type == PURPLE_PLUGIN_PROTOCOL) - { - /* If plugin is a PRPL, make sure it implements the required functions */ - if ((PURPLE_PLUGIN_PROTOCOL_INFO(plugin)->list_icon == NULL) || - (PURPLE_PLUGIN_PROTOCOL_INFO(plugin)->login == NULL) || - (PURPLE_PLUGIN_PROTOCOL_INFO(plugin)->close == NULL)) - { - plugin->error = g_strdup(_("Plugin does not implement all required functions (list_icon, login and close)")); - purple_debug_error("plugins", "%s is not loadable: %s\n", - plugin->path, plugin->error); - plugin->unloadable = TRUE; - return plugin; - } - - /* For debugging, let's warn about prpl prefs. */ - if (plugin->info->prefs_info != NULL) - { - purple_debug_error("plugins", "%s has a prefs_info, but is a prpl. This is no longer supported.\n", - plugin->path); - } - } - - return plugin; -#else - return NULL; -#endif /* !PURPLE_PLUGINS */ -} - -#ifdef PURPLE_PLUGINS -static gint -compare_plugins(gconstpointer a, gconstpointer b) -{ - const PurplePlugin *plugina = a; - const PurplePlugin *pluginb = b; - - return strcmp(plugina->info->name, pluginb->info->name); -} -#endif /* PURPLE_PLUGINS */ - -gboolean -purple_plugin_load(PurplePlugin *plugin) -{ -#ifdef PURPLE_PLUGINS - GList *dep_list = NULL; - GList *l; - - g_return_val_if_fail(plugin != NULL, FALSE); - - if (purple_plugin_is_loaded(plugin)) - return TRUE; - - if (purple_plugin_is_unloadable(plugin)) - return FALSE; - - g_return_val_if_fail(plugin->error == NULL, FALSE); - - /* - * Go through the list of the plugin's dependencies. - * - * First pass: Make sure all the plugins needed are probed. - */ - for (l = plugin->info->dependencies; l != NULL; l = l->next) - { - const char *dep_name = (const char *)l->data; - PurplePlugin *dep_plugin; - - dep_plugin = purple_plugins_find_with_id(dep_name); - - if (dep_plugin == NULL) - { - char *tmp; - - tmp = g_strdup_printf(_("The required plugin %s was not found. " - "Please install this plugin and try again."), - dep_name); - - purple_notify_error(NULL, NULL, - _("Unable to load the plugin"), tmp); - g_free(tmp); - - g_list_free(dep_list); - - return FALSE; - } - - dep_list = g_list_append(dep_list, dep_plugin); - } - - /* Second pass: load all the required plugins. */ - for (l = dep_list; l != NULL; l = l->next) - { - PurplePlugin *dep_plugin = (PurplePlugin *)l->data; - - if (!purple_plugin_is_loaded(dep_plugin)) - { - if (!purple_plugin_load(dep_plugin)) - { - char *tmp; - - tmp = g_strdup_printf(_("The required plugin %s was unable to load."), - plugin->info->name); - - purple_notify_error(NULL, NULL, - _("Unable to load your plugin."), tmp); - g_free(tmp); - - g_list_free(dep_list); - - return FALSE; - } - } - } - - /* Third pass: note that other plugins are dependencies of this plugin. - * This is done separately in case we had to bail out earlier. */ - for (l = dep_list; l != NULL; l = l->next) - { - PurplePlugin *dep_plugin = (PurplePlugin *)l->data; - dep_plugin->dependent_plugins = g_list_prepend(dep_plugin->dependent_plugins, (gpointer)plugin->info->id); - } - - g_list_free(dep_list); - - if (plugin->native_plugin) - { - if (plugin->info->load != NULL && !plugin->info->load(plugin)) - return FALSE; - } - else { - PurplePlugin *loader; - PurplePluginLoaderInfo *loader_info; - - loader = find_loader_for_plugin(plugin); - - if (loader == NULL) - return FALSE; - - loader_info = PURPLE_PLUGIN_LOADER_INFO(loader); - - if (loader_info->load != NULL) - { - if (!loader_info->load(plugin)) - return FALSE; - } - } - - loaded_plugins = g_list_insert_sorted(loaded_plugins, plugin, compare_plugins); - - plugin->loaded = TRUE; - - purple_signal_emit(purple_plugins_get_handle(), "plugin-load", plugin); - - return TRUE; + PurplePlugin *plugin = PURPLE_PLUGIN(obj); -#else - return TRUE; -#endif /* !PURPLE_PLUGINS */ -} - -gboolean -purple_plugin_unload(PurplePlugin *plugin) -{ -#ifdef PURPLE_PLUGINS - GList *l; - GList *ll; - - g_return_val_if_fail(plugin != NULL, FALSE); - g_return_val_if_fail(purple_plugin_is_loaded(plugin), FALSE); - - purple_debug_info("plugins", "Unloading plugin %s\n", plugin->info->name); - - /* Unload all plugins that depend on this plugin. */ - for (l = plugin->dependent_plugins; l != NULL; l = ll) { - const char * dep_name = (const char *)l->data; - PurplePlugin *dep_plugin; - - /* Store a pointer to the next element in the list. - * This is because we'll be modifying this list in the loop. */ - ll = l->next; - - dep_plugin = purple_plugins_find_with_id(dep_name); - - if (dep_plugin != NULL && purple_plugin_is_loaded(dep_plugin)) - { - if (!purple_plugin_unload(dep_plugin)) - { - g_free(plugin->error); - plugin->error = g_strdup_printf(_("%s requires %s, but it failed to unload."), - _(plugin->info->name), - _(dep_plugin->info->name)); - return FALSE; - } - else - { -#if 0 - /* This isn't necessary. This has already been done when unloading dep_plugin. */ - plugin->dependent_plugins = g_list_delete_link(plugin->dependent_plugins, l); -#endif - } - } - } - - /* Remove this plugin from each dependency's dependent_plugins list. */ - for (l = plugin->info->dependencies; l != NULL; l = l->next) - { - const char *dep_name = (const char *)l->data; - PurplePlugin *dependency; - - dependency = purple_plugins_find_with_id(dep_name); - - if (dependency != NULL) - dependency->dependent_plugins = g_list_remove(dependency->dependent_plugins, plugin->info->id); - else - purple_debug_error("plugins", "Unable to remove from dependency list for %s\n", dep_name); - } - - if (plugin->native_plugin) { - if (plugin->info->unload && !plugin->info->unload(plugin)) - return FALSE; - - if (plugin->info->type == PURPLE_PLUGIN_PROTOCOL) { - PurplePluginProtocolInfo *prpl_info; - GList *l; - - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin); - - for (l = prpl_info->user_splits; l != NULL; l = l->next) - purple_account_user_split_destroy(l->data); - - for (l = prpl_info->protocol_options; l != NULL; l = l->next) - purple_account_option_destroy(l->data); - - if (prpl_info->user_splits != NULL) { - g_list_free(prpl_info->user_splits); - prpl_info->user_splits = NULL; - } - - if (prpl_info->protocol_options != NULL) { - g_list_free(prpl_info->protocol_options); - prpl_info->protocol_options = NULL; - } - } - } else { - PurplePlugin *loader; - PurplePluginLoaderInfo *loader_info; - - loader = find_loader_for_plugin(plugin); - - if (loader == NULL) - return FALSE; - - loader_info = PURPLE_PLUGIN_LOADER_INFO(loader); - - if (loader_info->unload && !loader_info->unload(plugin)) - return FALSE; - } - - /* 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_plugin_ipc_unregister_all(plugin); - - loaded_plugins = g_list_remove(loaded_plugins, plugin); - if ((plugin->info != NULL) && PURPLE_IS_PROTOCOL_PLUGIN(plugin)) - protocol_plugins = g_list_remove(protocol_plugins, plugin); - plugins_to_disable = g_list_remove(plugins_to_disable, plugin); - plugin->loaded = FALSE; - - /* We wouldn't be anywhere near here if the plugin wasn't loaded, so - * if plugin->error is set at all, it had to be from a previous - * unload failure. It's obviously okay now. - */ - g_free(plugin->error); - plugin->error = NULL; - - purple_signal_emit(purple_plugins_get_handle(), "plugin-unload", plugin); - - purple_prefs_disconnect_by_handle(plugin); - - return TRUE; -#else - return TRUE; -#endif /* PURPLE_PLUGINS */ -} - -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 -} - -gboolean -purple_plugin_reload(PurplePlugin *plugin) -{ -#ifdef PURPLE_PLUGINS - g_return_val_if_fail(plugin != NULL, FALSE); - g_return_val_if_fail(purple_plugin_is_loaded(plugin), FALSE); - - if (!purple_plugin_unload(plugin)) - return FALSE; - - if (!purple_plugin_load(plugin)) - return FALSE; - - return TRUE; -#else - return TRUE; -#endif /* !PURPLE_PLUGINS */ -} - -void -purple_plugin_destroy(PurplePlugin *plugin) -{ -#ifdef PURPLE_PLUGINS - g_return_if_fail(plugin != NULL); - - if (purple_plugin_is_loaded(plugin)) - purple_plugin_unload(plugin); - - plugins = g_list_remove(plugins, plugin); - - if (load_queue != NULL) - load_queue = g_list_remove(load_queue, plugin); - - /* true, this may leak a little memory if there is a major version - * mismatch, but it's a lot better than trying to free something - * we shouldn't, and crashing while trying to load an old plugin */ - if(plugin->info == NULL || plugin->info->magic != PURPLE_PLUGIN_MAGIC || - plugin->info->major_version != PURPLE_MAJOR_VERSION) - { - if(plugin->handle) - g_module_close(plugin->handle); - - g_free(plugin->path); - g_free(plugin->error); - - PURPLE_DBUS_UNREGISTER_POINTER(plugin); - - g_free(plugin); - return; - } - - if (plugin->info != NULL) - g_list_free(plugin->info->dependencies); - - if (plugin->native_plugin) - { - if (plugin->info != NULL && plugin->info->type == PURPLE_PLUGIN_LOADER) - { - PurplePluginLoaderInfo *loader_info; - GList *exts, *l, *next_l; - PurplePlugin *p2; - - loader_info = PURPLE_PLUGIN_LOADER_INFO(plugin); - - if (loader_info != NULL && loader_info->exts != NULL) - { - for (exts = PURPLE_PLUGIN_LOADER_INFO(plugin)->exts; - exts != NULL; - exts = exts->next) { - - for (l = purple_plugins_get_all(); l != NULL; l = next_l) - { - next_l = l->next; - - p2 = l->data; - - if (p2->path != NULL && - has_file_extension(p2->path, exts->data)) - { - purple_plugin_destroy(p2); - } - } - } - - g_list_free(loader_info->exts); - loader_info->exts = NULL; - } - - plugin_loaders = g_list_remove(plugin_loaders, plugin); - } - - if (plugin->info != NULL && plugin->info->destroy != NULL) - plugin->info->destroy(plugin); - - /* - * I find it extremely useful to do this when using valgrind, as - * it keeps all the plugins open, meaning that valgrind is able to - * resolve symbol names in leak traces from plugins. - */ - if (!g_getenv("PURPLE_LEAKCHECK_HELP") && !RUNNING_ON_VALGRIND) - { - if (plugin->handle != NULL) - g_module_close(plugin->handle); - } - } - else - { - PurplePlugin *loader; - PurplePluginLoaderInfo *loader_info; - - loader = find_loader_for_plugin(plugin); - - if (loader != NULL) - { - loader_info = PURPLE_PLUGIN_LOADER_INFO(loader); - - if (loader_info->destroy != NULL) - loader_info->destroy(plugin); - } - } - - g_free(plugin->path); - g_free(plugin->error); - - PURPLE_DBUS_UNREGISTER_POINTER(plugin); - - g_free(plugin); -#endif /* !PURPLE_PLUGINS */ -} - -gboolean -purple_plugin_is_loaded(const PurplePlugin *plugin) -{ - g_return_val_if_fail(plugin != NULL, FALSE); - - return plugin->loaded; -} - -gboolean -purple_plugin_is_unloadable(const PurplePlugin *plugin) -{ - g_return_val_if_fail(plugin != NULL, FALSE); - - return plugin->unloadable; -} - -const gchar * -purple_plugin_get_id(const PurplePlugin *plugin) { - g_return_val_if_fail(plugin, NULL); - g_return_val_if_fail(plugin->info, NULL); - - return plugin->info->id; -} - -const gchar * -purple_plugin_get_name(const PurplePlugin *plugin) { - g_return_val_if_fail(plugin, NULL); - g_return_val_if_fail(plugin->info, NULL); - - return _(plugin->info->name); -} - -const gchar * -purple_plugin_get_version(const PurplePlugin *plugin) { - g_return_val_if_fail(plugin, NULL); - g_return_val_if_fail(plugin->info, NULL); - - return plugin->info->version; -} - -const gchar * -purple_plugin_get_summary(const PurplePlugin *plugin) { - g_return_val_if_fail(plugin, NULL); - g_return_val_if_fail(plugin->info, NULL); - - return _(plugin->info->summary); -} - -const gchar * -purple_plugin_get_description(const PurplePlugin *plugin) { - g_return_val_if_fail(plugin, NULL); - g_return_val_if_fail(plugin->info, NULL); - - return _(plugin->info->description); -} - -const gchar * -purple_plugin_get_author(const PurplePlugin *plugin) { - g_return_val_if_fail(plugin, NULL); - g_return_val_if_fail(plugin->info, NULL); - - return _(plugin->info->author); -} - -const gchar * -purple_plugin_get_homepage(const PurplePlugin *plugin) { - g_return_val_if_fail(plugin, NULL); - g_return_val_if_fail(plugin->info, NULL); - - return plugin->info->homepage; -} - -/************************************************************************** - * Plugin IPC - **************************************************************************/ -static void -destroy_ipc_info(void *data) -{ - PurplePluginIpcCommand *ipc_command = (PurplePluginIpcCommand *)data; - - g_free(ipc_command->param_types); - g_free(ipc_command); -} - -gboolean -purple_plugin_ipc_register(PurplePlugin *plugin, const char *command, - PurpleCallback func, PurpleSignalMarshalFunc marshal, - GType ret_type, int num_params, ...) -{ - PurplePluginIpcInfo *ipc_info; - PurplePluginIpcCommand *ipc_command; - - g_return_val_if_fail(plugin != NULL, FALSE); - g_return_val_if_fail(command != NULL, FALSE); - g_return_val_if_fail(func != NULL, FALSE); - g_return_val_if_fail(marshal != NULL, FALSE); - - if (plugin->ipc_data == NULL) - { - ipc_info = plugin->ipc_data = g_new0(PurplePluginIpcInfo, 1); - ipc_info->commands = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, destroy_ipc_info); - } - else - ipc_info = (PurplePluginIpcInfo *)plugin->ipc_data; - - ipc_command = g_new0(PurplePluginIpcCommand, 1); - ipc_command->func = func; - ipc_command->marshal = marshal; - ipc_command->num_params = num_params; - ipc_command->ret_type = ret_type; - - if (num_params > 0) - { - va_list args; - int i; - - ipc_command->param_types = g_new0(GType, num_params); - - va_start(args, num_params); - - for (i = 0; i < num_params; i++) - ipc_command->param_types[i] = va_arg(args, GType); - - va_end(args); - } - - g_hash_table_replace(ipc_info->commands, g_strdup(command), ipc_command); - - ipc_info->command_count++; - - return TRUE; -} - -void -purple_plugin_ipc_unregister(PurplePlugin *plugin, const char *command) -{ - PurplePluginIpcInfo *ipc_info; - - g_return_if_fail(plugin != NULL); - g_return_if_fail(command != NULL); - - ipc_info = (PurplePluginIpcInfo *)plugin->ipc_data; - - if (ipc_info == NULL || - g_hash_table_lookup(ipc_info->commands, command) == NULL) - { - purple_debug_error("plugins", - "IPC command '%s' was not registered for plugin %s\n", - command, plugin->info->name); - return; - } - - g_hash_table_remove(ipc_info->commands, command); - - ipc_info->command_count--; - - if (ipc_info->command_count == 0) - { - g_hash_table_destroy(ipc_info->commands); - g_free(ipc_info); - - plugin->ipc_data = NULL; + switch (param_id) { + case PROP_UNLOADABLE: + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); + break; } } -void -purple_plugin_ipc_unregister_all(PurplePlugin *plugin) -{ - PurplePluginIpcInfo *ipc_info; - - g_return_if_fail(plugin != NULL); - - if (plugin->ipc_data == NULL) - return; /* Silently ignore it. */ - - ipc_info = (PurplePluginIpcInfo *)plugin->ipc_data; - - g_hash_table_destroy(ipc_info->commands); - g_free(ipc_info); - - plugin->ipc_data = NULL; -} - -gboolean -purple_plugin_ipc_get_types(PurplePlugin *plugin, const char *command, - GType *ret_type, int *num_params, - GType **param_types) +/* GObject initialization function */ +static void +purple_plugin_init(GTypeInstance *instance, gpointer klass) { - PurplePluginIpcInfo *ipc_info; - PurplePluginIpcCommand *ipc_command; - - g_return_val_if_fail(plugin != NULL, FALSE); - g_return_val_if_fail(command != NULL, FALSE); - - ipc_info = (PurplePluginIpcInfo *)plugin->ipc_data; - - if (ipc_info == NULL || - (ipc_command = g_hash_table_lookup(ipc_info->commands, - command)) == NULL) - { - purple_debug_error("plugins", - "IPC command '%s' was not registered for plugin %s\n", - command, plugin->info->name); - - return FALSE; - } - - if (num_params != NULL) - *num_params = ipc_command->num_params; - - if (param_types != NULL) - *param_types = ipc_command->param_types; - - if (ret_type != NULL) - *ret_type = ipc_command->ret_type; - - return TRUE; -} - -void * -purple_plugin_ipc_call(PurplePlugin *plugin, const char *command, - gboolean *ok, ...) -{ - PurplePluginIpcInfo *ipc_info; - PurplePluginIpcCommand *ipc_command; - va_list args; - void *ret_value; + PurplePluginPrivate *priv = PURPLE_PLUGIN_GET_PRIVATE(instance); - if (ok != NULL) - *ok = FALSE; - - g_return_val_if_fail(plugin != NULL, NULL); - g_return_val_if_fail(command != NULL, NULL); - - ipc_info = (PurplePluginIpcInfo *)plugin->ipc_data; - - if (ipc_info == NULL || - (ipc_command = g_hash_table_lookup(ipc_info->commands, - command)) == NULL) - { - purple_debug_error("plugins", - "IPC command '%s' was not registered for plugin %s\n", - command, plugin->info->name); - - return NULL; - } - - va_start(args, ok); - ipc_command->marshal(ipc_command->func, args, NULL, &ret_value); - va_end(args); - - if (ok != NULL) - *ok = TRUE; - - return ret_value; -} - -/************************************************************************** - * Plugins subsystem - **************************************************************************/ -void * -purple_plugins_get_handle(void) { - static int handle; - - return &handle; -} - -void -purple_plugins_init(void) { - void *handle = purple_plugins_get_handle(); - - purple_plugins_add_search_path(LIBDIR); - - 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); -} - -void -purple_plugins_uninit(void) -{ - void *handle = purple_plugins_get_handle(); - - purple_signals_disconnect_by_handle(handle); - purple_signals_unregister_by_instance(handle); - - while (search_paths) { - g_free(search_paths->data); - search_paths = g_list_delete_link(search_paths, search_paths); - } + priv->unloadable = TRUE; } -/************************************************************************** - * Plugins API - **************************************************************************/ -void -purple_plugins_add_search_path(const char *path) -{ - g_return_if_fail(path != NULL); - - if (g_list_find_custom(search_paths, path, (GCompareFunc)strcmp)) - return; - - search_paths = g_list_append(search_paths, g_strdup(path)); -} - -GList * -purple_plugins_get_search_paths() -{ - return search_paths; -} - -void -purple_plugins_unload_all(void) -{ -#ifdef PURPLE_PLUGINS - - while (loaded_plugins != NULL) - purple_plugin_unload(loaded_plugins->data); - -#endif /* PURPLE_PLUGINS */ -} - -void -purple_plugins_unload(PurplePluginType type) -{ -#ifdef PURPLE_PLUGINS - GList *l; - - for (l = plugins; l; l = l->next) { - PurplePlugin *plugin = l->data; - if (plugin->info->type == type && purple_plugin_is_loaded(plugin)) - purple_plugin_unload(plugin); - } - -#endif /* PURPLE_PLUGINS */ -} - -void -purple_plugins_destroy_all(void) -{ -#ifdef PURPLE_PLUGINS - - while (plugins != NULL) - purple_plugin_destroy(plugins->data); - -#endif /* PURPLE_PLUGINS */ -} - -void -purple_plugins_save_loaded(const char *key) +/* GObject dispose function */ +static void +purple_plugin_dispose(GObject *object) { -#ifdef PURPLE_PLUGINS - GList *pl; - GList *files = NULL; - - for (pl = purple_plugins_get_loaded(); pl != NULL; pl = pl->next) { - PurplePlugin *plugin = pl->data; - - if (plugin->info->type != PURPLE_PLUGIN_PROTOCOL && - plugin->info->type != PURPLE_PLUGIN_LOADER && - !g_list_find(plugins_to_disable, plugin)) { - files = g_list_append(files, plugin->path); - } - } - - purple_prefs_set_path_list(key, files); - g_list_free(files); -#endif -} - -void -purple_plugins_load_saved(const char *key) -{ -#ifdef PURPLE_PLUGINS - GList *f, *files; - - g_return_if_fail(key != NULL); - - files = purple_prefs_get_path_list(key); - - for (f = files; f; f = f->next) - { - char *filename; - char *basename; - PurplePlugin *plugin; - - if (f->data == NULL) - continue; - - filename = f->data; - - /* - * We don't know if the filename uses Windows or Unix path - * separators (because people might be sharing a prefs.xml - * file across systems), so we find the last occurrence - * of either. - */ - basename = strrchr(filename, '/'); - if ((basename == NULL) || (basename < strrchr(filename, '\\'))) - basename = strrchr(filename, '\\'); - if (basename != NULL) - basename++; - - /* Strip the extension */ - if (basename) - basename = purple_plugin_get_basename(basename); - - if (((plugin = purple_plugins_find_with_filename(filename)) != NULL) || - (basename && (plugin = purple_plugins_find_with_basename(basename)) != NULL) || - ((plugin = purple_plugin_probe(filename)) != NULL)) - { - purple_debug_info("plugins", "Loading saved plugin %s\n", - plugin->path); - purple_plugin_load(plugin); - } - else - { - purple_debug_error("plugins", "Unable to find saved plugin %s\n", - filename); - } - - g_free(basename); - - g_free(f->data); - } - - g_list_free(files); -#endif /* PURPLE_PLUGINS */ + G_OBJECT_CLASS(parent_class)->dispose(object); } - -void -purple_plugins_probe(const char *ext) +/* GObject finalize function */ +static void +purple_plugin_finalize(GObject *object) { -#ifdef PURPLE_PLUGINS - GDir *dir; - const gchar *file; - gchar *path; - PurplePlugin *plugin; - GList *cur; - const char *search_path; - - if (!g_module_supported()) - return; - - /* Probe plugins */ - for (cur = search_paths; cur != NULL; cur = cur->next) - { - search_path = cur->data; - - dir = g_dir_open(search_path, 0, NULL); - - if (dir != NULL) - { - while ((file = g_dir_read_name(dir)) != NULL) - { - path = g_build_filename(search_path, file, NULL); - - if (ext == NULL || has_file_extension(file, ext)) - purple_plugin_probe(path); - - g_free(path); - } - - g_dir_close(dir); - } - } - - /* See if we have any plugins waiting to load */ - while (load_queue != NULL) - { - plugin = (PurplePlugin *)load_queue->data; - - load_queue = g_list_remove(load_queue, plugin); - - if (plugin == NULL || plugin->info == NULL) - continue; - - if (plugin->info->type == PURPLE_PLUGIN_LOADER) - { - /* We'll just load this right now. */ - if (!purple_plugin_load(plugin)) - { - purple_plugin_destroy(plugin); - - continue; - } - - plugin_loaders = g_list_append(plugin_loaders, plugin); - - for (cur = PURPLE_PLUGIN_LOADER_INFO(plugin)->exts; - cur != NULL; - cur = cur->next) - { - purple_plugins_probe(cur->data); - } - } - else if (plugin->info->type == PURPLE_PLUGIN_PROTOCOL) - { - /* We'll just load this right now. */ - if (!purple_plugin_load(plugin)) - { - purple_plugin_destroy(plugin); - - continue; - } - - /* Make sure we don't load two PRPLs with the same name? */ - if (purple_find_prpl(plugin->info->id)) - { - /* Nothing to see here--move along, move along */ - purple_plugin_destroy(plugin); - - continue; - } - - protocol_plugins = g_list_insert_sorted(protocol_plugins, plugin, - (GCompareFunc)compare_prpl); - } - } -#endif /* PURPLE_PLUGINS */ + G_OBJECT_CLASS(parent_class)->finalize(object); } -gboolean -purple_plugin_register(PurplePlugin *plugin) +/* Class initializer function */ +static void purple_plugin_class_init(PurplePluginClass *klass) { - g_return_val_if_fail(plugin != NULL, FALSE); - - /* If this plugin has been registered already then exit */ - if (g_list_find(plugins, plugin)) - return TRUE; - - /* Ensure the plugin has the requisite information */ - if (plugin->info->type == PURPLE_PLUGIN_LOADER) - { - PurplePluginLoaderInfo *loader_info; - - loader_info = PURPLE_PLUGIN_LOADER_INFO(plugin); - - if (loader_info == NULL) - { - purple_debug_error("plugins", "%s is not loadable, loader plugin missing loader_info\n", - plugin->path); - return FALSE; - } - } - else if (plugin->info->type == PURPLE_PLUGIN_PROTOCOL) - { - PurplePluginProtocolInfo *prpl_info; - - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin); - - if (prpl_info == NULL) - { - purple_debug_error("plugins", "%s is not loadable, protocol plugin missing prpl_info\n", - plugin->path); - return FALSE; - } - } + GObjectClass *obj_class = G_OBJECT_CLASS(klass); -#ifdef PURPLE_PLUGINS - /* This plugin should be probed and maybe loaded--add it to the queue */ - load_queue = g_list_append(load_queue, plugin); -#else - if (plugin->info != NULL) - { - if (plugin->info->type == PURPLE_PLUGIN_PROTOCOL) - protocol_plugins = g_list_insert_sorted(protocol_plugins, plugin, - (GCompareFunc)compare_prpl); - if (plugin->info->load != NULL) - if (!plugin->info->load(plugin)) - return FALSE; - } -#endif - - plugins = g_list_append(plugins, plugin); - - return TRUE; -} - -gboolean -purple_plugins_enabled(void) -{ -#ifdef PURPLE_PLUGINS - return TRUE; -#else - return FALSE; -#endif -} + parent_class = g_type_class_peek_parent(klass); -PurplePlugin * -purple_plugins_find_with_name(const char *name) -{ - PurplePlugin *plugin; - GList *l; - - for (l = plugins; l != NULL; l = l->next) { - plugin = l->data; - - if (purple_strequal(plugin->info->name, name)) - return plugin; - } - - return NULL; -} - -PurplePlugin * -purple_plugins_find_with_filename(const char *filename) -{ - PurplePlugin *plugin; - GList *l; - - for (l = plugins; l != NULL; l = l->next) { - plugin = l->data; - - if (purple_strequal(plugin->path, filename)) - return plugin; - } - - return NULL; -} + g_type_class_add_private(klass, sizeof(PurplePluginPrivate)); -PurplePlugin * -purple_plugins_find_with_basename(const char *basename) -{ -#ifdef PURPLE_PLUGINS - PurplePlugin *plugin; - GList *l; - char *tmp; - - g_return_val_if_fail(basename != NULL, NULL); - - for (l = plugins; l != NULL; l = l->next) - { - plugin = (PurplePlugin *)l->data; - - if (plugin->path != NULL) { - tmp = purple_plugin_get_basename(plugin->path); - if (purple_strequal(tmp, basename)) - { - g_free(tmp); - return plugin; - } - g_free(tmp); - } - } - -#endif /* PURPLE_PLUGINS */ - - return NULL; -} - -PurplePlugin * -purple_plugins_find_with_id(const char *id) -{ - PurplePlugin *plugin; - GList *l; - - g_return_val_if_fail(id != NULL, NULL); - - for (l = plugins; l != NULL; l = l->next) - { - plugin = l->data; - - if (purple_strequal(plugin->info->id, id)) - return plugin; - } - - return NULL; -} + obj_class->dispose = purple_plugin_dispose; + obj_class->finalize = purple_plugin_finalize; -GList * -purple_plugins_get_loaded(void) -{ - return loaded_plugins; -} - -GList * -purple_plugins_get_protocols(void) -{ - return protocol_plugins; -} - -GList * -purple_plugins_get_all(void) -{ - return plugins; -} - - -PurplePluginAction * -purple_plugin_action_new(const char* label, void (*callback)(PurplePluginAction *)) -{ - PurplePluginAction *act = g_new0(PurplePluginAction, 1); + /* Setup properties */ + obj_class->get_property = purple_plugin_get_property; + obj_class->set_property = purple_plugin_set_property; - act->label = g_strdup(label); - act->callback = callback; - - return act; -} - -void -purple_plugin_action_free(PurplePluginAction *action) -{ - g_return_if_fail(action != NULL); - - g_free(action->label); - g_free(action); -} - -static PurplePlugin * -purple_plugin_copy(PurplePlugin *plugin) -{ - PurplePlugin *plugin_copy; - - g_return_val_if_fail(plugin != NULL, NULL); - - plugin_copy = g_new(PurplePlugin, 1); - *plugin_copy = *plugin; - - return plugin_copy; + g_object_class_install_property(obj_class, PROP_UNLOADABLE, + g_param_spec_boolean(PROP_UNLOADABLE_S, _("Unloadable"), + _("Whether the plugin can be unloaded or not."), TRUE, + G_PARAM_READWRITE) + ); } GType @@ -1616,11 +138,18 @@ { static GType type = 0; - if (type == 0) { - type = g_boxed_type_register_static("PurplePlugin", - (GBoxedCopyFunc)purple_plugin_copy, - (GBoxedFreeFunc)g_free); + if (G_UNLIKELY(type == 0)) { + static const GTypeInfo info = { + .class_size = sizeof(PurplePluginClass), + .class_init = (GClassInitFunc)purple_plugin_class_init, + .instance_size = sizeof(PurplePlugin), + .instance_init = (GInstanceInitFunc)purple_plugin_init, + }; + + type = g_type_register_static(GPLUGIN_TYPE_PLUGIN_IMPLEMENTATION, + "PurplePlugin", &info, 0); } return type; } +
--- a/libpurple/plugin.h Sat Jul 27 16:46:06 2013 +0530 +++ b/libpurple/plugin.h Sun Jul 28 18:27:54 2013 +0530 @@ -1,9 +1,6 @@ /** * @file plugin.h Plugin API * @ingroup core - * @see @ref plugin-signals - * @see @ref plugin-ids - * @see @ref plugin-i18n */ /* purple @@ -29,99 +26,39 @@ #ifndef _PURPLE_PLUGIN_H_ #define _PURPLE_PLUGIN_H_ -#include <glib.h> -#include <gmodule.h> -#include "signals.h" +#include <gplugin.h> -/** Returns the GType for the PurplePlugin boxed structure */ -#define PURPLE_TYPE_PLUGIN (purple_plugin_get_type()) +#define PURPLE_TYPE_PLUGIN (purple_plugin_get_type()) +#define PURPLE_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_PLUGIN, PurplePlugin)) +#define PURPLE_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_PLUGIN, PurplePluginClass)) +#define PURPLE_IS_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PLUGIN)) +#define PURPLE_IS_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_PLUGIN)) +#define PURPLE_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_PLUGIN, PurplePluginClass)) /** @copydoc _PurplePlugin */ -typedef struct _PurplePlugin PurplePlugin; -/** @copydoc _PurplePluginInfo */ -typedef struct _PurplePluginInfo PurplePluginInfo; -/** @copydoc _PurplePluginUiInfo */ -typedef struct _PurplePluginUiInfo PurplePluginUiInfo; -/** @copydoc _PurplePluginLoaderInfo */ -typedef struct _PurplePluginLoaderInfo PurplePluginLoaderInfo; - -/** @copydoc _PurplePluginAction */ -typedef struct _PurplePluginAction PurplePluginAction; - -typedef int PurplePluginPriority; /**< Plugin priority. */ - -#include "pluginpref.h" - -/** - * Plugin types. - */ -typedef enum -{ - PURPLE_PLUGIN_UNKNOWN = -1, /**< Unknown type. */ - PURPLE_PLUGIN_STANDARD = 0, /**< Standard plugin. */ - PURPLE_PLUGIN_LOADER, /**< Loader plugin. */ - PURPLE_PLUGIN_PROTOCOL /**< Protocol plugin. */ - -} PurplePluginType; - -#define PURPLE_PRIORITY_DEFAULT 0 -#define PURPLE_PRIORITY_HIGHEST 9999 -#define PURPLE_PRIORITY_LOWEST -9999 - -#define PURPLE_PLUGIN_FLAG_INVISIBLE 0x01 - -#define PURPLE_PLUGIN_MAGIC 5 /* once we hit 6.0.0 I think we can remove this */ +typedef struct _PurplePlugin PurplePlugin; +/** @copydoc _PurplePluginClass */ +typedef struct _PurplePluginClass PurplePluginClass; /** - * Detailed information about a plugin. + * Represents a plugin that can be loaded/unloaded by libpurple. * - * This is used in the version 2.0 API and up. + * #PurplePlugin inherits #GPluginPluginImplementation, which holds the + * low-level details about the plugin in a #GPluginPlugin instance. */ -struct _PurplePluginInfo -{ - unsigned int magic; - unsigned int major_version; - unsigned int minor_version; - PurplePluginType type; - char *ui_requirement; - unsigned long flags; - GList *dependencies; - PurplePluginPriority priority; - - const char *id; - const char *name; - const char *version; - const char *summary; - const char *description; - const char *author; - const char *homepage; +struct _PurplePlugin { + /*< private >*/ + GPluginPluginImplementation parent; +}; - /** - * If a plugin defines a 'load' function, and it returns FALSE, - * then the plugin will not be loaded. - */ - gboolean (*load)(PurplePlugin *plugin); - gboolean (*unload)(PurplePlugin *plugin); - void (*destroy)(PurplePlugin *plugin); - - void *ui_info; /**< Used only by UI-specific plugins to build a preference screen with a custom UI */ - void *extra_info; - PurplePluginUiInfo *prefs_info; /**< Used by any plugin to display preferences. If #ui_info has been specified, this will be ignored. */ - - /** - * This callback has a different use depending on whether this - * plugin type is PURPLE_PLUGIN_STANDARD or PURPLE_PLUGIN_PROTOCOL. - * - * If PURPLE_PLUGIN_STANDARD then the list of actions will show up - * in the Tools menu, under a submenu with the name of the plugin. - * context will be NULL. - * - * If PURPLE_PLUGIN_PROTOCOL then the list of actions will show up - * in the Accounts menu, under a submenu with the name of the - * account. context will be set to the PurpleConnection for that - * account. This callback will only be called for online accounts. - */ - GList *(*actions)(PurplePlugin *plugin, gpointer context); +/** + * PurplePluginClass: + * + * The base class for all #PurplePlugin's. + */ +struct _PurplePluginClass { + /*< private >*/ + GPluginPluginImplementationClass parent_class; void (*_purple_reserved1)(void); void (*_purple_reserved2)(void); @@ -129,118 +66,6 @@ void (*_purple_reserved4)(void); }; -/** - * Extra information for loader plugins. - */ -struct _PurplePluginLoaderInfo -{ - GList *exts; - - gboolean (*probe)(PurplePlugin *plugin); - gboolean (*load)(PurplePlugin *plugin); - gboolean (*unload)(PurplePlugin *plugin); - void (*destroy)(PurplePlugin *plugin); - - void (*_purple_reserved1)(void); - void (*_purple_reserved2)(void); - void (*_purple_reserved3)(void); - void (*_purple_reserved4)(void); -}; - -/** - * A plugin handle. - */ -struct _PurplePlugin -{ - gboolean native_plugin; /**< Native C plugin. */ - gboolean loaded; /**< The loaded state. */ - void *handle; /**< The module handle. */ - char *path; /**< The path to the plugin. */ - PurplePluginInfo *info; /**< The plugin information. */ - char *error; - void *ipc_data; /**< IPC data. */ - void *extra; /**< Plugin-specific data. */ - gboolean unloadable; /**< Unloadable */ - GList *dependent_plugins; /**< Plugins depending on this */ - - void (*_purple_reserved1)(void); - void (*_purple_reserved2)(void); - void (*_purple_reserved3)(void); - void (*_purple_reserved4)(void); -}; - -#define PURPLE_PLUGIN_LOADER_INFO(plugin) \ - ((PurplePluginLoaderInfo *)(plugin)->info->extra_info) - -struct _PurplePluginUiInfo { - PurplePluginPrefFrame *(*get_plugin_pref_frame)(PurplePlugin *plugin); - - int page_num; /**< Reserved */ - PurplePluginPrefFrame *frame; /**< Reserved */ - - void (*_purple_reserved1)(void); - void (*_purple_reserved2)(void); - void (*_purple_reserved3)(void); - void (*_purple_reserved4)(void); -}; - -#define PURPLE_PLUGIN_HAS_PREF_FRAME(plugin) \ - ((plugin)->info != NULL && (plugin)->info->prefs_info != NULL) - -#define PURPLE_PLUGIN_UI_INFO(plugin) \ - ((PurplePluginUiInfo*)(plugin)->info->prefs_info) - - -/** - * The structure used in the actions member of PurplePluginInfo - */ -struct _PurplePluginAction { - char *label; - void (*callback)(PurplePluginAction *); - - /** set to the owning plugin */ - PurplePlugin *plugin; - - /** NULL for plugin actions menu, set to the PurpleConnection for - account actions menu */ - gpointer context; - - gpointer user_data; -}; - -#define PURPLE_PLUGIN_HAS_ACTIONS(plugin) \ - ((plugin)->info != NULL && (plugin)->info->actions != NULL) - -#define PURPLE_PLUGIN_ACTIONS(plugin, context) \ - (PURPLE_PLUGIN_HAS_ACTIONS(plugin)? \ - (plugin)->info->actions(plugin, context): NULL) - - -/** - * Handles the initialization of modules. - */ -#if !defined(PURPLE_PLUGINS) || defined(PURPLE_STATIC_PRPL) -# define _FUNC_NAME(x) purple_init_##x##_plugin -# define PURPLE_INIT_PLUGIN(pluginname, initfunc, plugininfo) \ - gboolean _FUNC_NAME(pluginname)(void);\ - gboolean _FUNC_NAME(pluginname)(void) { \ - PurplePlugin *plugin = purple_plugin_new(TRUE, NULL); \ - plugin->info = &(plugininfo); \ - initfunc((plugin)); \ - purple_plugin_load((plugin)); \ - return purple_plugin_register(plugin); \ - } -#else /* PURPLE_PLUGINS && !PURPLE_STATIC_PRPL */ -# define PURPLE_INIT_PLUGIN(pluginname, initfunc, plugininfo) \ - G_MODULE_EXPORT gboolean purple_init_plugin(PurplePlugin *plugin); \ - G_MODULE_EXPORT gboolean purple_init_plugin(PurplePlugin *plugin) { \ - plugin->info = &(plugininfo); \ - initfunc((plugin)); \ - return purple_plugin_register(plugin); \ - } -#endif - - G_BEGIN_DECLS /**************************************************************************/ @@ -249,427 +74,12 @@ /*@{*/ /** - * Returns the GType for the PurplePlugin boxed structure. - * TODO Boxing of PurplePlugin is a temporary solution to having a GType for - * plugins. This should rather be a GObject instead of a GBoxed. + * Returns the GType for the PurplePlugin object. */ GType purple_plugin_get_type(void); -/** - * Creates a new plugin structure. - * - * @param native Whether or not the plugin is native. - * @param path The path to the plugin, or @c NULL if statically compiled. - * - * @return A new PurplePlugin structure. - */ -PurplePlugin *purple_plugin_new(gboolean native, const char *path); - -/** - * Probes a plugin, retrieving the information on it and adding it to the - * list of available plugins. - * - * @param filename The plugin's filename. - * - * @return The plugin handle. - * - * @see purple_plugin_load() - * @see purple_plugin_destroy() - */ -PurplePlugin *purple_plugin_probe(const char *filename); - -/** - * Registers a plugin and prepares it for loading. - * - * This shouldn't be called by anything but the internal module code. - * Plugins should use the PURPLE_INIT_PLUGIN() macro to register themselves - * with the core. - * - * @param plugin The plugin to register. - * - * @return @c TRUE if the plugin was registered successfully. Otherwise - * @c FALSE is returned (this happens if the plugin does not contain - * the necessary information). - */ -gboolean purple_plugin_register(PurplePlugin *plugin); - -/** - * Attempts to load a previously probed plugin. - * - * @param plugin The plugin to load. - * - * @return @c TRUE if successful, or @c FALSE otherwise. - * - * @see purple_plugin_reload() - * @see purple_plugin_unload() - */ -gboolean purple_plugin_load(PurplePlugin *plugin); - -/** - * Unloads the specified plugin. - * - * @param plugin The plugin handle. - * - * @return @c TRUE if successful, or @c FALSE otherwise. - * - * @see purple_plugin_load() - * @see purple_plugin_reload() - */ -gboolean purple_plugin_unload(PurplePlugin *plugin); - -/** - * Disable a plugin. - * - * This function adds the plugin to a list of plugins to "disable at the next - * startup" by excluding said plugins from the list of plugins to save. The - * UI needs to call purple_plugins_save_loaded() after calling this for it - * to have any effect. - */ -void purple_plugin_disable(PurplePlugin *plugin); - -/** - * Reloads a plugin. - * - * @param plugin The old plugin handle. - * - * @return @c TRUE if successful, or @c FALSE otherwise. - * - * @see purple_plugin_load() - * @see purple_plugin_unload() - */ -gboolean purple_plugin_reload(PurplePlugin *plugin); - -/** - * Unloads a plugin and destroys the structure from memory. - * - * @param plugin The plugin handle. - */ -void purple_plugin_destroy(PurplePlugin *plugin); - -/** - * Returns whether or not a plugin is currently loaded. - * - * @param plugin The plugin. - * - * @return @c TRUE if loaded, or @c FALSE otherwise. - */ -gboolean purple_plugin_is_loaded(const PurplePlugin *plugin); - -/** - * Returns whether or not a plugin is unloadable. - * - * If this returns @c TRUE, the plugin is guaranteed to not - * be loadable. However, a return value of @c FALSE does not - * guarantee the plugin is loadable. - * - * @param plugin The plugin. - * - * @return @c TRUE if the plugin is known to be unloadable,\ - * @c FALSE otherwise - */ -gboolean purple_plugin_is_unloadable(const PurplePlugin *plugin); - -/** - * Returns a plugin's id. - * - * @param plugin The plugin. - * - * @return The plugin's id. - */ -const gchar *purple_plugin_get_id(const PurplePlugin *plugin); - -/** - * Returns a plugin's name. - * - * @param plugin The plugin. - * - * @return THe name of the plugin, or @c NULL. - */ -const gchar *purple_plugin_get_name(const PurplePlugin *plugin); - -/** - * Returns a plugin's version. - * - * @param plugin The plugin. - * - * @return The plugin's version or @c NULL. - */ -const gchar *purple_plugin_get_version(const PurplePlugin *plugin); - -/** - * Returns a plugin's summary. - * - * @param plugin The plugin. - * - * @return The plugin's summary. - */ -const gchar *purple_plugin_get_summary(const PurplePlugin *plugin); - -/** - * Returns a plugin's description. - * - * @param plugin The plugin. - * - * @return The plugin's description. - */ -const gchar *purple_plugin_get_description(const PurplePlugin *plugin); - -/** - * Returns a plugin's author. - * - * @param plugin The plugin. - * - * @return The plugin's author. - */ -const gchar *purple_plugin_get_author(const PurplePlugin *plugin); - -/** - * Returns a plugin's homepage. - * - * @param plugin The plugin. - * - * @return The plugin's homepage. - */ -const gchar *purple_plugin_get_homepage(const PurplePlugin *plugin); - /*@}*/ -/**************************************************************************/ -/** @name Plugin IPC API */ -/**************************************************************************/ -/*@{*/ - -/** - * Registers an IPC command in a plugin. - * - * @param plugin The plugin to register the command with. - * @param command The name of the command. - * @param func The function to execute. - * @param marshal The marshalling function. - * @param ret_type The return type. - * @param num_params The number of parameters. - * @param ... The parameter types. - * - * @return TRUE if the function was registered successfully, or - * FALSE otherwise. - */ -gboolean purple_plugin_ipc_register(PurplePlugin *plugin, const char *command, - PurpleCallback func, - PurpleSignalMarshalFunc marshal, - GType ret_type, int num_params, ...); - -/** - * Unregisters an IPC command in a plugin. - * - * @param plugin The plugin to unregister the command from. - * @param command The name of the command. - */ -void purple_plugin_ipc_unregister(PurplePlugin *plugin, const char *command); - -/** - * Unregisters all IPC commands in a plugin. - * - * @param plugin The plugin to unregister the commands from. - */ -void purple_plugin_ipc_unregister_all(PurplePlugin *plugin); - -/** - * Returns a list of value types used for an IPC command. - * - * @param plugin The plugin. - * @param command The name of the command. - * @param ret_type The returned return type. - * @param num_params The returned number of parameters. - * @param param_types The returned list of parameter types. - * - * @return TRUE if the command was found, or FALSE otherwise. - */ -gboolean purple_plugin_ipc_get_types(PurplePlugin *plugin, const char *command, - GType *ret_type, int *num_params, - GType **param_types); - -/** - * Executes an IPC command. - * - * @param plugin The plugin to execute the command on. - * @param command The name of the command. - * @param ok TRUE if the call was successful, or FALSE otherwise. - * @param ... The parameters to pass. - * - * @return The return value, which will be NULL if the command doesn't - * return a value. - */ -void *purple_plugin_ipc_call(PurplePlugin *plugin, const char *command, - gboolean *ok, ...); - -/*@}*/ - -/**************************************************************************/ -/** @name Plugins API */ -/**************************************************************************/ -/*@{*/ - -/** - * Add a new directory to search for plugins - * - * @param path The new search path. - */ -void purple_plugins_add_search_path(const char *path); - -/** - * Returns a list of plugin search paths. - * - * @constreturn A list of searched paths. - */ -GList *purple_plugins_get_search_paths(void); - -/** - * Unloads all loaded plugins. - */ -void purple_plugins_unload_all(void); - -/** - * Unloads all plugins of a specific type. - */ -void purple_plugins_unload(PurplePluginType type); - -/** - * Destroys all registered plugins. - */ -void purple_plugins_destroy_all(void); - -/** - * Saves the list of loaded plugins to the specified preference key - * - * @param key The preference key to save the list of plugins to. - */ -void purple_plugins_save_loaded(const char *key); - -/** - * Attempts to load all the plugins in the specified preference key - * that were loaded when purple last quit. - * - * @param key The preference key containing the list of plugins. - */ -void purple_plugins_load_saved(const char *key); - -/** - * Probes for plugins in the registered module paths. - * - * @param ext The extension type to probe for, or @c NULL for all. - * - * @see purple_plugin_set_probe_path() - */ -void purple_plugins_probe(const char *ext); - -/** - * Returns whether or not plugin support is enabled. - * - * @return TRUE if plugin support is enabled, or FALSE otherwise. - */ -gboolean purple_plugins_enabled(void); - -/** - * Finds a plugin with the specified name. - * - * @param name The plugin name. - * - * @return The plugin if found, or @c NULL if not found. - */ -PurplePlugin *purple_plugins_find_with_name(const char *name); - -/** - * Finds a plugin with the specified filename (filename with a path). - * - * @param filename The plugin filename. - * - * @return The plugin if found, or @c NULL if not found. - */ -PurplePlugin *purple_plugins_find_with_filename(const char *filename); - -/** - * Finds a plugin with the specified basename (filename without a path). - * - * @param basename The plugin basename. - * - * @return The plugin if found, or @c NULL if not found. - */ -PurplePlugin *purple_plugins_find_with_basename(const char *basename); - -/** - * Finds a plugin with the specified plugin ID. - * - * @param id The plugin ID. - * - * @return The plugin if found, or @c NULL if not found. - */ -PurplePlugin *purple_plugins_find_with_id(const char *id); - -/** - * Returns a list of all loaded plugins. - * - * @constreturn A list of all loaded plugins. - */ -GList *purple_plugins_get_loaded(void); - -/** - * Returns a list of all valid protocol plugins. A protocol - * plugin is considered invalid if it does not contain the call - * to the PURPLE_INIT_PLUGIN() macro, or if it was compiled - * against an incompatable API version. - * - * @constreturn A list of all protocol plugins. - */ -GList *purple_plugins_get_protocols(void); - -/** - * Returns a list of all plugins, whether loaded or not. - * - * @constreturn A list of all plugins. - */ -GList *purple_plugins_get_all(void); - -/*@}*/ - -/**************************************************************************/ -/** @name Plugins SubSytem API */ -/**************************************************************************/ -/*@{*/ - -/** - * Returns the plugin subsystem handle. - * - * @return The plugin sybsystem handle. - */ -void *purple_plugins_get_handle(void); - -/** - * Initializes the plugin subsystem - */ -void purple_plugins_init(void); - -/** - * Uninitializes the plugin subsystem - */ -void purple_plugins_uninit(void); - -/*@}*/ - -/** - * Allocates and returns a new PurplePluginAction. - * - * @param label The description of the action to show to the user. - * @param callback The callback to call when the user selects this action. - */ -PurplePluginAction *purple_plugin_action_new(const char* label, void (*callback)(PurplePluginAction *)); - -/** - * Frees a PurplePluginAction - * - * @param action The PurplePluginAction to free. - */ -void purple_plugin_action_free(PurplePluginAction *action); - G_END_DECLS #endif /* _PURPLE_PLUGIN_H_ */