# HG changeset patch # User Myhailo Danylenko # Date 1255269712 -7200 # Node ID 14690e624e9deb671684ff6017ce062b8a9d9c1b # Parent d7f26538c24c64490a78cd6f6ec1237130390c1f Add modules diff -r d7f26538c24c -r 14690e624e9d mcabber/configure.ac --- a/mcabber/configure.ac Sun Oct 11 16:01:31 2009 +0200 +++ b/mcabber/configure.ac Sun Oct 11 16:01:52 2009 +0200 @@ -130,13 +130,22 @@ [Define if ncurses has ESCDELAY variable]) fi +AC_ARG_ENABLE(modules, [ --enable-modules enable dynamic modules loading], + enable_modules=$enableval) +if test "x$enable_modules" = "xyes"; then + AC_DEFINE(MODULES_ENABLE, 1, [Define if you want dynamic modules loading]) + gmodule_module=gmodule +else + gmodule_module='' +fi + # Check for glib AM_PATH_GLIB_2_0(2.14.0, [AC_DEFINE([HAVE_GLIB_REGEX], 1, [Define if GLib has regex support])], [AM_PATH_GLIB_2_0(2.0.0, , AC_MSG_ERROR([glib is required]), - [g_list_append])], - [g_regex_new]) + [g_list_append], ["$gmodule_module"])], + [g_regex_new "$gmodule_module"]) # Check for loudmouth PKG_CHECK_MODULES(LOUDMOUTH, loudmouth-1.0 >= 1.4.3) diff -r d7f26538c24c -r 14690e624e9d mcabber/src/commands.c --- a/mcabber/src/commands.c Sun Oct 11 16:01:31 2009 +0200 +++ b/mcabber/src/commands.c Sun Oct 11 16:01:52 2009 +0200 @@ -93,11 +93,47 @@ // Global variable for the commands list static GSList *Commands; +#ifdef MODULES_ENABLE +#include + +static void do_load(char *arg); +static void do_unload(char *arg); + +typedef struct { + char *name; + GModule *module; +} loaded_module_t; + +GSList *loaded_modules = NULL; + +static gint cmd_del_comparator (cmd *a, const char *name) +{ + return strcmp(a->name, name); +} + +gpointer cmd_del(const char *name) +{ + GSList *command = g_slist_find_custom (Commands, name, (GCompareFunc) cmd_del_comparator); + if (command) { + cmd *cmnd = command->data; + gpointer userdata = cmnd->userdata; + Commands = g_slist_delete_link(Commands, command); + compl_del_category_word(COMPL_CMD, cmnd->name); + g_free(cmnd); + return userdata; + } + return NULL; +} // cmd_add() // Adds a command to the commands list and to the CMD completion list +void cmd_add(const char *name, const char *help, guint flags_row1, + guint flags_row2, void (*f)(char*), gpointer userdata) +#define cmd_add(A, B, C, D, E) cmd_add (A, B, C, D, E, NULL); +#else static void cmd_add(const char *name, const char *help, guint flags_row1, guint flags_row2, void (*f)(char*)) +#endif { cmd *n_cmd = g_new0(cmd, 1); strncpy(n_cmd->name, name, 32-1); @@ -105,6 +141,9 @@ n_cmd->completion_flags[0] = flags_row1; n_cmd->completion_flags[1] = flags_row2; n_cmd->func = f; +#ifdef MODULES_ENABLE + n_cmd->userdata = userdata; +#endif Commands = g_slist_append(Commands, n_cmd); // Add to completion CMD category compl_add_category_word(COMPL_CMD, name); @@ -165,6 +204,10 @@ cmd_add("status_to", "Show or set your status for one recipient", COMPL_JID, COMPL_STATUS, &do_status_to); cmd_add("version", "Show mcabber version", 0, 0, &do_version); +#ifdef MODULES_ENABLE + cmd_add("load", "Load module", 0, 0, &do_load); + cmd_add("unload", "Unload module", 0, 0, &do_unload); +#endif // Status category compl_add_category_word(COMPL_STATUS, "online"); @@ -299,6 +342,23 @@ compl_add_category_word(COMPL_COLOR, "mucnick"); } +#ifdef MODULES_ENABLE +void cmd_deinit () +{ + GSList *el = loaded_modules; + while (el) { + loaded_module_t *module = el->data; + if (!g_module_close ((GModule *) module->module)) + scr_LogPrint (LPRINT_LOGNORM, "* Module unloading failed: %s", + g_module_error ()); + g_free (module->name); + g_free (module); + el = g_slist_next (el); + } + g_slist_free (loaded_modules); +} +#endif + // expandalias(line) // If there is one, expand the alias in line and returns a new allocated line // If no alias is found, returns line @@ -435,7 +495,14 @@ p++; // Call command-specific function retval_for_cmds = 0; +#ifdef MODULES_ENABLE + if (curcmd->userdata) + (*(void (*)(char *p, gpointer u))curcmd->func)(p, curcmd->userdata); + else + (*curcmd->func)(p); +#else (*curcmd->func)(p); +#endif g_free(xpline); return retval_for_cmds; } @@ -2902,6 +2969,57 @@ g_slist_free(bm); } +#ifdef MODULES_ENABLE +static void do_load(char *arg) +{ + if (!arg || !*arg) { + scr_LogPrint (LPRINT_LOGNORM, "Missing modulename."); + return; + } + char *mdir = expand_filename (settings_opt_get ("modules_dir")); + char *path = g_module_build_path (mdir, arg); + GModule *mod = g_module_open (path, G_MODULE_BIND_LAZY); + if (!mod) + scr_LogPrint (LPRINT_LOGNORM, "Module %s loading failed: %s", + path, g_module_error ()); + else { + loaded_module_t *module = g_new (loaded_module_t, 1); + module->name = g_strdup (arg); + module->module = mod; + loaded_modules = g_slist_prepend (loaded_modules, module); + scr_LogPrint (LPRINT_LOGNORM, "Loaded module %s", arg); + } + g_free (path); + if (mdir) + g_free (mdir); +} + +static int module_list_comparator (loaded_module_t *module, const char *name) +{ + return g_strcmp0 (module->name, name); +} + +static void do_unload(char *arg) +{ + if (!arg || !*arg) { + scr_LogPrint (LPRINT_LOGNORM, "Missing modulename."); + return; + } + GSList *module = g_slist_find_custom (loaded_modules, arg, (GCompareFunc) module_list_comparator); + if (module) { + loaded_module_t *mod = module->data; + if (!g_module_close ((GModule *) mod->module)) + scr_LogPrint (LPRINT_LOGNORM, "Module unloading failed: %s", + g_module_error ()); + else { + g_free (mod->name); + g_free (mod); + loaded_modules = g_slist_delete_link (loaded_modules, module); + } + } else + scr_LogPrint (LPRINT_LOGNORM, "Module %s not loaded.", arg); +} +#endif static void do_room(char *arg) { diff -r d7f26538c24c -r 14690e624e9d mcabber/src/commands.h --- a/mcabber/src/commands.h Sun Oct 11 16:01:31 2009 +0200 +++ b/mcabber/src/commands.h Sun Oct 11 16:01:52 2009 +0200 @@ -3,12 +3,17 @@ #include +#include "config.h" + // Command structure typedef struct { char name[32]; const char *help; guint completion_flags[2]; void (*func)(char *); +#ifdef MODULES_ENABLE + gpointer userdata; +#endif } cmd; void cmd_init(void); @@ -16,6 +21,11 @@ int process_line(const char *line); int process_command(const char *line, guint iscmd); char *expandalias(const char *line); +#ifdef MODULES_ENABLE +void cmd_deinit(void); +gpointer cmd_del(const char *name); +void cmd_add(const char *name, const char *help, guint flags1, guint flags2, void (*f)(char*), gpointer userdata); +#endif extern char *mcabber_version(void); extern void mcabber_set_terminate_ui(void); diff -r d7f26538c24c -r 14690e624e9d mcabber/src/compl.c --- a/mcabber/src/compl.c Sun Oct 11 16:01:31 2009 +0200 +++ b/mcabber/src/compl.c Sun Oct 11 16:01:52 2009 +0200 @@ -53,6 +53,44 @@ static GSList *Categories; static compl *InputCompl; +#ifdef MODULES_ENABLE +guint registered_cats = COMPL_CMD|COMPL_JID|COMPL_URLJID|COMPL_NAME| \ + COMPL_STATUS|COMPL_FILENAME|COMPL_ROSTER|COMPL_BUFFER| \ + COMPL_GROUP|COMPL_GROUPNAME|COMPL_MULTILINE|COMPL_ROOM| \ + COMPL_RESOURCE|COMPL_AUTH|COMPL_REQUEST|COMPL_EVENTS| \ + COMPL_EVENTSID|COMPL_PGP|COMPL_COLOR| \ + COMPL_OTR|COMPL_OTRPOLICY| \ + 0; + +// compl_new_category() +// Reserves id for new completion category. +// Returns 0, if no more categories can be allocated. +// Note, that user should not make any assumptions about id nature, +// as it is likely to change in future. +guint compl_new_category (void) +{ + guint i = 0; + while ((registered_cats >> i) & 1) + i++; + if (i >= sizeof (guint)*8) + return 0; + else { + guint id = 1 << i; + registered_cats |= id; + return id; + } +} + +// compl_del_category (id) +// Frees reserved id for category. +// Note, that for now it not validates its input, so, be careful +// and specify exactly what you get from compl_new_category. +void compl_del_category (guint id) +{ + registered_cats &= ~id; +} +#endif + // new_completion(prefix, compl_cat) // . prefix = beginning of the word, typed by the user // . compl_cat = pointer to a completion category list (list of *char) diff -r d7f26538c24c -r 14690e624e9d mcabber/src/compl.h --- a/mcabber/src/compl.h Sun Oct 11 16:01:31 2009 +0200 +++ b/mcabber/src/compl.h Sun Oct 11 16:01:52 2009 +0200 @@ -3,6 +3,8 @@ #include +#include "config.h" + #define COMPL_CMD (1U<<0) #define COMPL_JID (1U<<1) #define COMPL_URLJID (1U<<2) // Not implemented yet @@ -24,6 +26,12 @@ #define COMPL_COLOR (1U<<18) #define COMPL_OTR (1U<<19) #define COMPL_OTRPOLICY (1U<<20) +#ifdef MODULES_ENABLE +#define COMPL_MAX_BUILTIN (1U<<20) + +guint compl_new_category (void); +void compl_del_category (guint id); +#endif void compl_add_category_word(guint, const char *command); void compl_del_category_word(guint categ, const char *word); diff -r d7f26538c24c -r 14690e624e9d mcabber/src/events.h --- a/mcabber/src/events.h Sun Oct 11 16:01:31 2009 +0200 +++ b/mcabber/src/events.h Sun Oct 11 16:01:52 2009 +0200 @@ -1,6 +1,7 @@ #ifndef __EVENTS_H__ #define __EVENTS_H__ 1 +#include "config.h" // MODULES_ENABLE #define EVS_DEFAULT_TIMEOUT 90 #define EVS_MAX_TIMEOUT 432000 @@ -11,7 +12,10 @@ typedef enum { EVS_TYPE_SUBSCRIPTION = 1, - EVS_TYPE_INVITATION = 2 + EVS_TYPE_INVITATION = 2, +#ifdef MODULES_ENABLE + EVS_TYPE_USER = 3, +#endif } evs_type; /* Common structure for events (evs) and IQ requests (iqs) */ diff -r d7f26538c24c -r 14690e624e9d mcabber/src/hooks.c --- a/mcabber/src/hooks.c Sun Oct 11 16:01:31 2009 +0200 +++ b/mcabber/src/hooks.c Sun Oct 11 16:01:52 2009 +0200 @@ -36,6 +36,43 @@ #include "commands.h" #include "fifo.h" +#ifdef MODULES_ENABLE +#include + +typedef struct { + hk_handler_t handler; + gpointer userdata; +} hook_list_data_t; + +static GSList *hk_handler_queue = NULL; + +void hk_add_handler (hk_handler_t handler, gpointer userdata) +{ + hook_list_data_t *h = g_new (hook_list_data_t, 1); + h->handler = handler; + h->userdata = userdata; + hk_handler_queue = g_slist_append (hk_handler_queue, h); +} + +static gint hk_queue_search_cb (hook_list_data_t *a, hook_list_data_t *b) +{ + if (a->handler == b->handler && a->userdata == b->userdata) + return 0; + else + return 1; +} + +void hk_del_handler (hk_handler_t handler, gpointer userdata) +{ + hook_list_data_t h = { handler, userdata }; + GSList *el = g_slist_find_custom (hk_handler_queue, &h, (GCompareFunc) hk_queue_search_cb); + if (el) { + g_free (el->data); + hk_handler_queue = g_slist_delete_link (hk_handler_queue, el); + } +} +#endif + static char *extcmd; static const char *COMMAND_ME = "/me "; @@ -209,6 +246,42 @@ if (settings_opt_get_int("eventcmd_use_nickname")) ename = roster_getname(bjid); +#ifdef MODULES_ENABLE + { + GSList *h = hk_handler_queue; + if (h) { +#if 0 + hk_arg_t *args = g_new (hk_arg_t, 5); + args[0].name = "hook"; + args[0].value = "hook-message-in"; + args[1].name = "jid"; + args[1].value = bjid; + args[2].name = "message"; + args[2].value = wmsg; + args[3].name = "groupchat"; + args[3].value = is_groupchat ? "true" : "false"; + args[4].name = NULL; + args[4].value = NULL; +#else + // We can use a const array for keys/static values, so modules + // can do fast known to them args check by just comparing pointers... + hk_arg_t args[] = { + { "hook", "hook-message-in" }, + { "jid", bjid }, + { "message", wmsg }, + { "groupchat", is_groupchat ? "true" : "false" }, + { NULL, NULL }, + }; +#endif + while (h) { + hook_list_data_t *data = h->data; + (data->handler) (args, data->userdata); + h = g_slist_next (h); + } + } + } +#endif + // External command // - We do not call hk_ext_cmd() for history lines in MUC // - We do call hk_ext_cmd() for private messages in a room @@ -287,6 +360,25 @@ if (!nick) hlog_write_message(bjid, timestamp, 1, msg); +#ifdef MODULES_ENABLE + { + GSList *h = hk_handler_queue; + if (h) { + hk_arg_t args[] = { + { "hook", "hook-message-out" }, + { "jid", bjid }, + { "message", wmsg }, + { NULL, NULL }, + }; + while (h) { + hook_list_data_t *data = h->data; + (data->handler) (args, data->userdata); + h = g_slist_next (h); + } + } + } +#endif + // External command hk_ext_cmd(bjid, 'M', 'S', NULL); @@ -357,6 +449,33 @@ buddylist_build(); scr_DrawRoster(); hlog_write_status(bjid, timestamp, status, status_msg); + +#ifdef MODULES_ENABLE + { + GSList *h = hk_handler_queue; + if (h) { + char os[2] = " \0"; + char ns[2] = " \0"; + hk_arg_t args[] = { + { "hook", "hook-status-change" }, + { "jid", bjid }, + { "resource", rn }, + { "old_status", os }, + { "new_status", ns }, + { "message", status_msg ? status_msg : "" }, + { NULL, NULL }, + }; + os[0] = imstatus2char[oldstat]; + ns[0] = imstatus2char[status]; + while (h) { + hook_list_data_t *data = h->data; + (data->handler) (args, data->userdata); + h = g_slist_next (h); + } + } + } +#endif + // External command hk_ext_cmd(ename ? ename : bjid, 'S', imstatus2char[status], NULL); } @@ -367,6 +486,28 @@ scr_LogPrint(LPRINT_LOGNORM, "Your status has been set: [%c>%c] %s", imstatus2char[old_status], imstatus2char[new_status], (msg ? msg : "")); + +#ifdef MODULES_ENABLE + { + GSList *h = hk_handler_queue; + if (h) { + char ns[2] = " \0"; + hk_arg_t args[] = { + { "hook", "hook-my-status-change" }, + { "new_status", ns }, + { "message", msg ? msg : "" }, + { NULL, NULL }, + }; + ns[0] = imstatus2char[new_status]; + while (h) { + hook_list_data_t *data = h->data; + (data->handler) (args, data->userdata); + h = g_slist_next (h); + } + } + } +#endif + //hlog_write_status(NULL, 0, status); } @@ -390,6 +531,23 @@ if (process_command(cmdline, TRUE) == 255) mcabber_set_terminate_ui(); +#ifdef MODULES_ENABLE + { + GSList *h = hk_handler_queue; + if (h) { + hk_arg_t args[] = { + { "hook", hookname }, + { NULL, NULL }, + }; + while (h) { + hook_list_data_t *data = h->data; + (data->handler) (args, data->userdata); + h = g_slist_next (h); + } + } + } +#endif + g_free(cmdline); g_free(buf); } diff -r d7f26538c24c -r 14690e624e9d mcabber/src/hooks.h --- a/mcabber/src/hooks.h Sun Oct 11 16:01:31 2009 +0200 +++ b/mcabber/src/hooks.h Sun Oct 11 16:01:52 2009 +0200 @@ -9,6 +9,21 @@ #define ENCRYPTED_PGP 1 #define ENCRYPTED_OTR 2 +#include "config.h" +#ifdef MODULES_ENABLE +#include + +typedef struct { + const char *name; + const char *value; +} hk_arg_t; + +typedef void (*hk_handler_t) (hk_arg_t *args, gpointer userdata); + +void hk_add_handler (hk_handler_t handler, gpointer userdata); +void hk_del_handler (hk_handler_t handler, gpointer userdata); +#endif + void hk_mainloop(void); void hk_message_in(const char *bjid, const char *resname, time_t timestamp, const char *msg, LmMessageSubType type, diff -r d7f26538c24c -r 14690e624e9d mcabber/src/main.c --- a/mcabber/src/main.c Sun Oct 11 16:01:31 2009 +0200 +++ b/mcabber/src/main.c Sun Oct 11 16:01:52 2009 +0200 @@ -169,6 +169,9 @@ #ifdef HAVE_GPGME puts("Compiled with GPG support."); #endif +#ifdef MODULES_ENABLE + puts ("Compiled with modules support"); +#endif #ifdef HAVE_LIBOTR puts("Compiled with OTR support."); #endif @@ -416,6 +419,9 @@ g_main_loop_run(main_loop); scr_TerminateCurses(); +#ifdef MODULES_ENABLE + cmd_deinit(); +#endif fifo_deinit(); #ifdef HAVE_LIBOTR otr_terminate(); diff -r d7f26538c24c -r 14690e624e9d mcabber/src/settings.c --- a/mcabber/src/settings.c Sun Oct 11 16:01:31 2009 +0200 +++ b/mcabber/src/settings.c Sun Oct 11 16:01:52 2009 +0200 @@ -23,6 +23,7 @@ #include #include +#include "config.h" #include "settings.h" #include "commands.h" #include "logprint.h" @@ -176,12 +177,15 @@ continue; // We only allow assignments line, except for commands "pgp", "source", - // "color" and "otrpolicy", unless we're in runtime (i.e. not startup). + // "color", "load" and "otrpolicy", unless we're in runtime (i.e. not startup). if (runtime || (strchr(line, '=') != NULL) || startswith(line, "pgp ", FALSE) || startswith(line, "source ", FALSE) || startswith(line, "color ", FALSE) || +#ifdef MODULES_ENABLE + startswith(line, "load ", FALSE) || +#endif startswith(line, "otrpolicy", FALSE)) { // Only accept a few "safe" commands if (!runtime && @@ -191,6 +195,9 @@ !startswith(line, "pgp ", FALSE) && !startswith(line, "source ", FALSE) && !startswith(line, "color ", FALSE) && +#ifdef MODULES_ENABLE + !startswith(line, "load ", FALSE) && +#endif !startswith(line, "otrpolicy ", FALSE)) { scr_LogPrint(LPRINT_LOGNORM, "Error in configuration file (l. %d): " "this command can't be used here", ln); diff -r d7f26538c24c -r 14690e624e9d mcabber/src/xmpp_helper.c --- a/mcabber/src/xmpp_helper.c Sun Oct 11 16:01:31 2009 +0200 +++ b/mcabber/src/xmpp_helper.c Sun Oct 11 16:01:52 2009 +0200 @@ -30,6 +30,7 @@ #include "utils.h" #include "caps.h" #include "logprint.h" +#include "config.h" time_t iqlast; // last message/status change time @@ -74,6 +75,37 @@ }; +#ifdef MODULES_ENABLE +static GSList *xmpp_additional_features = NULL; +static char *ver, *ver_notavail; + +void xmpp_add_feature (const char *xmlns) +{ + if (xmlns) { + ver = NULL; + ver_notavail = NULL; + xmpp_additional_features = g_slist_append(xmpp_additional_features, + g_strdup (xmlns)); + } +} + +void xmpp_del_feature (const char *xmlns) +{ + GSList *feature = xmpp_additional_features; + while (feature) { + if (!strcmp(feature->data, xmlns)) { + ver = NULL; + ver_notavail = NULL; + g_free (feature->data); + xmpp_additional_features = g_slist_delete_link(xmpp_additional_features, + feature); + return; + } + feature = g_slist_next (feature); + } +} +#endif + const gchar* lm_message_node_get_child_value(LmMessageNode *node, const gchar *child) { @@ -180,7 +212,9 @@ // number) so that it doesn't conflict with the official client. const char *entity_version(enum imstatus status) { +#ifndef MODULES_ENABLE static char *ver, *ver_notavail; +#endif if (ver && (status != notavail)) return ver; @@ -204,6 +238,15 @@ (!settings_opt_get_int("iq_last_disable_when_notavail") || status != notavail)) caps_add_feature("", NS_LAST); +#ifdef MODULES_ENABLE + { + GSList *el = xmpp_additional_features; + while (el) { + caps_add_feature("", el->data); + el = g_slist_next (el); + } + } +#endif if (status == notavail) { ver_notavail = caps_generate(); diff -r d7f26538c24c -r 14690e624e9d mcabber/src/xmpp_helper.h --- a/mcabber/src/xmpp_helper.h Sun Oct 11 16:01:31 2009 +0200 +++ b/mcabber/src/xmpp_helper.h Sun Oct 11 16:01:52 2009 +0200 @@ -6,6 +6,7 @@ #include "xmpp.h" #include "xmpp_defines.h" +#include "config.h" extern time_t iqlast; /* last message/status change time */ @@ -23,6 +24,11 @@ }; +#ifdef MODULES_ENABLE +void xmpp_add_feature (const char *xmlns); +void xmpp_del_feature (const char *xmlns); +#endif + LmMessageNode *lm_message_node_new(const gchar *name, const gchar *xmlns); LmMessageNode *lm_message_node_find_xmlns(LmMessageNode *node, const char *xmlns);