diff options
author | jgeboski <jgeboski@gmail.com> | 2016-05-15 14:17:34 -0400 |
---|---|---|
committer | jgeboski <jgeboski@gmail.com> | 2016-05-25 22:48:08 -0400 |
commit | d28fe1c4f463314a79f0a71f6f8a01db53e37253 (patch) | |
tree | 70a8a2b8b86ad62a4bfc2da57608ca45404b5bb4 | |
parent | 0e48e549e7693f665b43bcad5e14ef26447bfe5b (diff) |
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 <alias@domain.tld>", /* 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.
-rw-r--r-- | bitlbee.h | 1 | ||||
-rw-r--r-- | doc/user-guide/commands.xml | 12 | ||||
-rw-r--r-- | otr.c | 16 | ||||
-rw-r--r-- | protocols/nogaim.c | 67 | ||||
-rw-r--r-- | protocols/nogaim.h | 14 | ||||
-rw-r--r-- | protocols/skype/skype.c | 14 | ||||
-rw-r--r-- | root_commands.c | 33 |
7 files changed, 156 insertions, 1 deletions
@@ -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 @@ </description> </bitlbee-command> + <bitlbee-command name="plugins"> + <short-description>List all the external plugins</short-description> + <syntax>plugins</syntax> + + <description> + <para> + This gives you a list of all the external plugins. + </para> + </description> + + </bitlbee-command> + <bitlbee-command name="qlist"> <short-description>List all the unanswered questions root asked</short-description> <syntax>qlist</syntax> @@ -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 }, |