From d28fe1c4f463314a79f0a71f6f8a01db53e37253 Mon Sep 17 00:00:00 2001 From: jgeboski Date: Sun, 15 May 2016 14:17:34 -0400 Subject: Implemented plugin information for external plugins As of now, bitlbee will load any plugin regardless of the ABI it was built against. This is really problematic when structures or symbols are changed within bitlbee. This often leads to the plugin not loading or the plugin acting in an undefined way. Typically a simple rebuild of the plugin will resolve such issues, but many users have no idea that this is required after they have updated bitlbee. Furthermore, it is often times impossible to determine the version of a plugin, without relying on the package manager of the system. This is quite a problem when users are reporting bugs for external plugins, and they have no idea what version of the plugin they are running. This is also an opportunity to provide additional metadata for each plugin that can then be displayed to the user. Solving these issues is done by adding a new required function to each plugin. The init_plugin_info() function must now be implemented along with the init_plugin() function. This function then returns a static structure, which retains all of the metadata for the plugin. Then this is used by bitlbee to check the ABI version and provide information to the user. The introduction of the new function is required as bitlbee needs to obtain the ABI version before calling init_plugin(). The boiler-plate implementation of init_plugin_info(): #ifdef BITLBEE_ABI_VERSION_CODE struct plugin_info *init_plugin_info(void) { static struct plugin_info info = { BITLBEE_ABI_VERSION_CODE, /* Required */ "plugin-name", /* Required */ "1.3.3.7", /* Required */ "A short description of the plugin", /* Optional */ "First Last ", /* Optional */ "http://www.domain.tld" /* Optional */ }; return &info; } #endif The example wraps the function declaration in an if block for backwards compatibility with older bitlbee versions. Displaying the plugin metadata is done via the newly added "plugins" command, which simply dumps formatted data to the root channel. --- bitlbee.h | 1 + doc/user-guide/commands.xml | 12 ++++++++ otr.c | 16 +++++++++++ protocols/nogaim.c | 67 ++++++++++++++++++++++++++++++++++++++++++++- protocols/nogaim.h | 14 ++++++++++ protocols/skype/skype.c | 14 ++++++++++ root_commands.c | 33 ++++++++++++++++++++++ 7 files changed, 156 insertions(+), 1 deletion(-) diff --git a/bitlbee.h b/bitlbee.h index 26a1c982..de54d05c 100644 --- a/bitlbee.h +++ b/bitlbee.h @@ -39,6 +39,7 @@ extern "C" { #define VERSION BITLBEE_VERSION #define BITLBEE_VER(a, b, c) (((a) << 16) + ((b) << 8) + (c)) #define BITLBEE_VERSION_CODE BITLBEE_VER(3, 4, 2) +#define BITLBEE_ABI_VERSION_CODE 1 #define MAX_STRING 511 diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 6f66a39d..260e6abe 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -1834,6 +1834,18 @@ + + List all the external plugins + plugins + + + + This gives you a list of all the external plugins. + + + + + List all the unanswered questions root asked qlist diff --git a/otr.c b/otr.c index 4888691a..0328f478 100644 --- a/otr.c +++ b/otr.c @@ -267,6 +267,22 @@ void init_plugin(void) register_irc_plugin(&otr_plugin); } +#ifndef OTR_BI +struct plugin_info *init_plugin_info(void) +{ + static struct plugin_info info = { + BITLBEE_ABI_VERSION_CODE, + "otr", + BITLBEE_VERSION, + "Off-the-Record communication", + NULL, + NULL + }; + + return &info; +} +#endif + gboolean otr_irc_new(irc_t *irc) { set_t *s; diff --git a/protocols/nogaim.c b/protocols/nogaim.c index c9cbb033..9ed3b64b 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -39,24 +39,84 @@ GSList *connections; #ifdef WITH_PLUGINS +GList *plugins = NULL; + +static gint pluginscmp(gconstpointer a, gconstpointer b, gpointer data) +{ + const struct plugin_info *ia = a; + const struct plugin_info *ib = b; + + return g_strcasecmp(ia->name, ib->name); +} + gboolean load_plugin(char *path) { + GList *l; + struct plugin_info *i; + struct plugin_info *info; + struct plugin_info * (*info_function) (void) = NULL; void (*init_function) (void); GModule *mod = g_module_open(path, G_MODULE_BIND_LAZY); + gboolean loaded = FALSE; if (!mod) { log_message(LOGLVL_ERROR, "Error loading plugin `%s': %s\n", path, g_module_error()); return FALSE; } + if (g_module_symbol(mod, "init_plugin_info", (gpointer *) &info_function)) { + info = info_function(); + + if (info->abiver != BITLBEE_ABI_VERSION_CODE) { + log_message(LOGLVL_ERROR, + "`%s' uses ABI %u but %u is required\n", + path, info->abiver, + BITLBEE_ABI_VERSION_CODE); + g_module_close(mod); + return FALSE; + } + + if (!info->name || !info->version) { + log_message(LOGLVL_ERROR, + "Name or version missing from the " + "plugin info in `%s'\n", path); + g_module_close(mod); + return FALSE; + } + + for (l = plugins; l; l = l->next) { + i = l->data; + + if (g_strcasecmp(i->name, info->name) == 0) { + loaded = TRUE; + break; + } + } + + if (loaded) { + log_message(LOGLVL_WARNING, + "%s plugin already loaded\n", + info->name); + g_module_close(mod); + return FALSE; + } + } else { + log_message(LOGLVL_WARNING, "Can't find function `init_plugin_info' in `%s'\n", path); + } + if (!g_module_symbol(mod, "init_plugin", (gpointer *) &init_function)) { log_message(LOGLVL_WARNING, "Can't find function `init_plugin' in `%s'\n", path); + g_module_close(mod); return FALSE; } - init_function(); + if (info_function) { + plugins = g_list_insert_sorted_with_data(plugins, info, + pluginscmp, NULL); + } + init_function(); return TRUE; } @@ -86,6 +146,11 @@ void load_plugins(void) g_dir_close(dir); } } + +GList *get_plugins() +{ + return plugins; +} #endif GList *protocols = NULL; diff --git a/protocols/nogaim.h b/protocols/nogaim.h index 40b1ed7b..f5b1f212 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -270,6 +270,20 @@ struct prpl { void *resv5; }; +struct plugin_info +{ + guint abiver; + const char *name; + const char *version; + const char *description; + const char *author; + const char *url; +}; + +#ifdef WITH_PLUGINS +G_MODULE_EXPORT GList *get_plugins(); +#endif + /* im_api core stuff. */ void nogaim_init(); G_MODULE_EXPORT GSList *get_connections(); diff --git a/protocols/skype/skype.c b/protocols/skype/skype.c index a21af8ef..bd8a1850 100644 --- a/protocols/skype/skype.c +++ b/protocols/skype/skype.c @@ -1762,3 +1762,17 @@ void init_plugin(void) #endif register_protocol(ret); } + +struct plugin_info *init_plugin_info(void) +{ + static struct plugin_info info = { + BITLBEE_ABI_VERSION_CODE, + "skype", + BITLBEE_VERSION, + "Skype protocol plugin", + NULL, + NULL + }; + + return &info; +} diff --git a/root_commands.c b/root_commands.c index dcf7a7ed..25c5c49e 100644 --- a/root_commands.c +++ b/root_commands.c @@ -1106,6 +1106,36 @@ static void cmd_blist(irc_t *irc, char **cmd) } } +#ifdef WITH_PLUGINS +static void cmd_plugins(irc_t *irc, char **cmd) +{ + GList *l; + struct plugin_info *info; + + for (l = get_plugins(); l; l = l->next) { + info = l->data; + irc_rootmsg(irc, "%s:", info->name); + irc_rootmsg(irc, " Version: %s", info->version); + + if (info->description) { + irc_rootmsg(irc, " Description: %s", info->description); + } + + if (info->author) { + irc_rootmsg(irc, " Author: %s", info->author); + } + + if (info->url) { + irc_rootmsg(irc, " URL: %s", info->url); + } + + if (l->next) { + irc_rootmsg(irc, ""); + } + } +} +#endif + static void cmd_qlist(irc_t *irc, char **cmd) { query_t *q = irc->queries; @@ -1357,6 +1387,9 @@ command_t root_commands[] = { { "info", 1, cmd_info, 0 }, { "nick", 1, cmd_nick, 0 }, { "no", 0, cmd_yesno, 0 }, +#ifdef WITH_PLUGINS + { "plugins", 0, cmd_plugins, 0 }, +#endif { "qlist", 0, cmd_qlist, 0 }, { "register", 0, cmd_register, 0 }, { "remove", 1, cmd_remove, 0 }, -- cgit v1.2.3