diff options
Diffstat (limited to 'src/common/plugin.c')
-rw-r--r-- | src/common/plugin.c | 1569 |
1 files changed, 1569 insertions, 0 deletions
diff --git a/src/common/plugin.c b/src/common/plugin.c new file mode 100644 index 00000000..ada4d3be --- /dev/null +++ b/src/common/plugin.c @@ -0,0 +1,1569 @@ +/* X-Chat + * Copyright (C) 2002 Peter Zelezny. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <stdio.h> + +#include "xchat.h" +#include "fe.h" +#include "util.h" +#include "outbound.h" +#include "cfgfiles.h" +#include "ignore.h" +#include "server.h" +#include "servlist.h" +#include "modes.h" +#include "notify.h" +#include "text.h" +#define PLUGIN_C +typedef struct session xchat_context; +#include "xchat-plugin.h" +#include "plugin.h" + + +#include "xchatc.h" + +/* the USE_PLUGIN define only removes libdl stuff */ + +#ifdef USE_PLUGIN +#ifdef USE_GMODULE +#include <gmodule.h> +#else +#include <dlfcn.h> +#endif +#endif + +#define DEBUG(x) {x;} + +/* crafted to be an even 32 bytes */ +struct _xchat_hook +{ + xchat_plugin *pl; /* the plugin to which it belongs */ + char *name; /* "xdcc" */ + void *callback; /* pointer to xdcc_callback */ + char *help_text; /* help_text for commands only */ + void *userdata; /* passed to the callback */ + int tag; /* for timers & FDs only */ + int type; /* HOOK_* */ + int pri; /* fd */ /* priority / fd for HOOK_FD only */ +}; + +struct _xchat_list +{ + int type; /* LIST_* */ + GSList *pos; /* current pos */ + GSList *next; /* next pos */ + GSList *head; /* for LIST_USERS only */ + struct notify_per_server *notifyps; /* notify_per_server * */ +}; + +typedef int (xchat_cmd_cb) (char *word[], char *word_eol[], void *user_data); +typedef int (xchat_serv_cb) (char *word[], char *word_eol[], void *user_data); +typedef int (xchat_print_cb) (char *word[], void *user_data); +typedef int (xchat_fd_cb) (int fd, int flags, void *user_data); +typedef int (xchat_timer_cb) (void *user_data); +typedef int (xchat_init_func) (xchat_plugin *, char **, char **, char **, char *); +typedef int (xchat_deinit_func) (xchat_plugin *); + +enum +{ + LIST_CHANNELS, + LIST_DCC, + LIST_IGNORE, + LIST_NOTIFY, + LIST_USERS +}; + +enum +{ + HOOK_COMMAND, /* /command */ + HOOK_SERVER, /* PRIVMSG, NOTICE, numerics */ + HOOK_PRINT, /* All print events */ + HOOK_TIMER, /* timeouts */ + HOOK_FD, /* sockets & fds */ + HOOK_DELETED /* marked for deletion */ +}; + +GSList *plugin_list = NULL; /* export for plugingui.c */ +static GSList *hook_list = NULL; + +extern const struct prefs vars[]; /* cfgfiles.c */ + + +/* unload a plugin and remove it from our linked list */ + +static int +plugin_free (xchat_plugin *pl, int do_deinit, int allow_refuse) +{ + GSList *list, *next; + xchat_hook *hook; + xchat_deinit_func *deinit_func; + + /* fake plugin added by xchat_plugingui_add() */ + if (pl->fake) + goto xit; + + /* run the plugin's deinit routine, if any */ + if (do_deinit && pl->deinit_callback != NULL) + { + deinit_func = pl->deinit_callback; + if (!deinit_func (pl) && allow_refuse) + return FALSE; + } + + /* remove all of this plugin's hooks */ + list = hook_list; + while (list) + { + hook = list->data; + next = list->next; + if (hook->pl == pl) + xchat_unhook (NULL, hook); + list = next; + } + +#ifdef USE_PLUGIN + if (pl->handle) +#ifdef USE_GMODULE + g_module_close (pl->handle); +#else + dlclose (pl->handle); +#endif +#endif + +xit: + if (pl->free_strings) + { + if (pl->name) + free (pl->name); + if (pl->desc) + free (pl->desc); + if (pl->version) + free (pl->version); + } + if (pl->filename) + free ((char *)pl->filename); + free (pl); + + plugin_list = g_slist_remove (plugin_list, pl); + +#ifdef USE_PLUGIN + fe_pluginlist_update (); +#endif + + return TRUE; +} + +static xchat_plugin * +plugin_list_add (xchat_context *ctx, char *filename, const char *name, + const char *desc, const char *version, void *handle, + void *deinit_func, int fake, int free_strings) +{ + xchat_plugin *pl; + + pl = malloc (sizeof (xchat_plugin)); + pl->handle = handle; + pl->filename = filename; + pl->context = ctx; + pl->name = (char *)name; + pl->desc = (char *)desc; + pl->version = (char *)version; + pl->deinit_callback = deinit_func; + pl->fake = fake; + pl->free_strings = free_strings; /* free() name,desc,version? */ + + plugin_list = g_slist_prepend (plugin_list, pl); + + return pl; +} + +static void * +xchat_dummy (xchat_plugin *ph) +{ + return NULL; +} + +#ifdef WIN32 +static int +xchat_read_fd (xchat_plugin *ph, GIOChannel *source, char *buf, int *len) +{ + return g_io_channel_read (source, buf, *len, len); +} +#endif + +/* Load a static plugin */ + +void +plugin_add (session *sess, char *filename, void *handle, void *init_func, + void *deinit_func, char *arg, int fake) +{ + xchat_plugin *pl; + char *file; + + file = NULL; + if (filename) + file = strdup (filename); + + pl = plugin_list_add (sess, file, file, NULL, NULL, handle, deinit_func, + fake, FALSE); + + if (!fake) + { + /* win32 uses these because it doesn't have --export-dynamic! */ + pl->xchat_hook_command = xchat_hook_command; + pl->xchat_hook_server = xchat_hook_server; + pl->xchat_hook_print = xchat_hook_print; + pl->xchat_hook_timer = xchat_hook_timer; + pl->xchat_hook_fd = xchat_hook_fd; + pl->xchat_unhook = xchat_unhook; + pl->xchat_print = xchat_print; + pl->xchat_printf = xchat_printf; + pl->xchat_command = xchat_command; + pl->xchat_commandf = xchat_commandf; + pl->xchat_nickcmp = xchat_nickcmp; + pl->xchat_set_context = xchat_set_context; + pl->xchat_find_context = xchat_find_context; + pl->xchat_get_context = xchat_get_context; + pl->xchat_get_info = xchat_get_info; + pl->xchat_get_prefs = xchat_get_prefs; + pl->xchat_list_get = xchat_list_get; + pl->xchat_list_free = xchat_list_free; + pl->xchat_list_fields = xchat_list_fields; + pl->xchat_list_str = xchat_list_str; + pl->xchat_list_next = xchat_list_next; + pl->xchat_list_int = xchat_list_int; + pl->xchat_plugingui_add = xchat_plugingui_add; + pl->xchat_plugingui_remove = xchat_plugingui_remove; + pl->xchat_emit_print = xchat_emit_print; +#ifdef WIN32 + pl->xchat_read_fd = (void *) xchat_read_fd; +#else + pl->xchat_read_fd = xchat_dummy; +#endif + pl->xchat_list_time = xchat_list_time; + pl->xchat_gettext = xchat_gettext; + pl->xchat_send_modes = xchat_send_modes; + pl->xchat_strip = xchat_strip; + pl->xchat_free = xchat_free; + + /* incase new plugins are loaded on older xchat */ + pl->xchat_dummy4 = xchat_dummy; + pl->xchat_dummy3 = xchat_dummy; + pl->xchat_dummy2 = xchat_dummy; + pl->xchat_dummy1 = xchat_dummy; + + /* run xchat_plugin_init, if it returns 0, close the plugin */ + if (((xchat_init_func *)init_func) (pl, &pl->name, &pl->desc, &pl->version, arg) == 0) + { + plugin_free (pl, FALSE, FALSE); + return; + } + } + +#ifdef USE_PLUGIN + fe_pluginlist_update (); +#endif +} + +/* kill any plugin by the given (file) name (used by /unload) */ + +int +plugin_kill (char *name, int by_filename) +{ + GSList *list; + xchat_plugin *pl; + + list = plugin_list; + while (list) + { + pl = list->data; + /* static-plugins (plugin-timer.c) have a NULL filename */ + if ((by_filename && pl->filename && strcasecmp (name, pl->filename) == 0) || + (by_filename && pl->filename && strcasecmp (name, file_part (pl->filename)) == 0) || + (!by_filename && strcasecmp (name, pl->name) == 0)) + { + /* statically linked plugins have a NULL filename */ + if (pl->filename != NULL && !pl->fake) + { + if (plugin_free (pl, TRUE, TRUE)) + return 1; + return 2; + } + } + list = list->next; + } + + return 0; +} + +/* kill all running plugins (at shutdown) */ + +void +plugin_kill_all (void) +{ + GSList *list, *next; + xchat_plugin *pl; + + list = plugin_list; + while (list) + { + pl = list->data; + next = list->next; + if (!pl->fake) + plugin_free (list->data, TRUE, FALSE); + list = next; + } +} + +#ifdef USE_PLUGIN + +/* load a plugin from a filename. Returns: NULL-success or an error string */ + +char * +plugin_load (session *sess, char *filename, char *arg) +{ + void *handle; + xchat_init_func *init_func; + xchat_deinit_func *deinit_func; + +#ifdef USE_GMODULE + /* load the plugin */ + handle = g_module_open (filename, 0); + if (handle == NULL) + return (char *)g_module_error (); + + /* find the init routine xchat_plugin_init */ + if (!g_module_symbol (handle, "xchat_plugin_init", (gpointer *)&init_func)) + { + g_module_close (handle); + return _("No xchat_plugin_init symbol; is this really an xchat plugin?"); + } + + /* find the plugin's deinit routine, if any */ + if (!g_module_symbol (handle, "xchat_plugin_deinit", (gpointer *)&deinit_func)) + deinit_func = NULL; + +#else + char *error; + char *filepart; + +/* OpenBSD lacks this! */ +#ifndef RTLD_GLOBAL +#define RTLD_GLOBAL 0 +#endif + +#ifndef RTLD_NOW +#define RTLD_NOW 0 +#endif + + /* get the filename without path */ + filepart = file_part (filename); + + /* load the plugin */ + if (filepart && + /* xsys draws in libgtk-1.2, causing crashes, so force RTLD_LOCAL */ + (strstr (filepart, "local") || strncmp (filepart, "libxsys-1", 9) == 0) + ) + handle = dlopen (filename, RTLD_NOW); + else + handle = dlopen (filename, RTLD_GLOBAL | RTLD_NOW); + if (handle == NULL) + return (char *)dlerror (); + dlerror (); /* Clear any existing error */ + + /* find the init routine xchat_plugin_init */ + init_func = dlsym (handle, "xchat_plugin_init"); + error = (char *)dlerror (); + if (error != NULL) + { + dlclose (handle); + return _("No xchat_plugin_init symbol; is this really an xchat plugin?"); + } + + /* find the plugin's deinit routine, if any */ + deinit_func = dlsym (handle, "xchat_plugin_deinit"); + error = (char *)dlerror (); +#endif + + /* add it to our linked list */ + plugin_add (sess, filename, handle, init_func, deinit_func, arg, FALSE); + + return NULL; +} + +static session *ps; + +static void +plugin_auto_load_cb (char *filename) +{ + char *pMsg; + +#ifndef WIN32 /* black listed */ + if (!strcmp (file_part (filename), "dbus.so")) + return; +#endif + + pMsg = plugin_load (ps, filename, NULL); + if (pMsg) + { + PrintTextf (ps, "AutoLoad failed for: %s\n", filename); + PrintText (ps, pMsg); + } +} + +void +plugin_auto_load (session *sess) +{ + ps = sess; +#ifdef WIN32 + for_files ("./plugins", "*.dll", plugin_auto_load_cb); + for_files (get_xdir_fs (), "*.dll", plugin_auto_load_cb); +#else +#if defined(__hpux) + for_files (XCHATLIBDIR"/plugins", "*.sl", plugin_auto_load_cb); + for_files (get_xdir_fs (), "*.sl", plugin_auto_load_cb); +#else + for_files (XCHATLIBDIR"/plugins", "*.so", plugin_auto_load_cb); + for_files (get_xdir_fs (), "*.so", plugin_auto_load_cb); +#endif +#endif +} + +#endif + +static GSList * +plugin_hook_find (GSList *list, int type, char *name) +{ + xchat_hook *hook; + + while (list) + { + hook = list->data; + if (hook->type == type) + { + if (strcasecmp (hook->name, name) == 0) + return list; + + if (type == HOOK_SERVER) + { + if (strcasecmp (hook->name, "RAW LINE") == 0) + return list; + } + } + list = list->next; + } + + return NULL; +} + +/* check for plugin hooks and run them */ + +static int +plugin_hook_run (session *sess, char *name, char *word[], char *word_eol[], int type) +{ + GSList *list, *next; + xchat_hook *hook; + int ret, eat = 0; + + list = hook_list; + while (1) + { + list = plugin_hook_find (list, type, name); + if (!list) + goto xit; + + hook = list->data; + next = list->next; + hook->pl->context = sess; + + /* run the plugin's callback function */ + switch (type) + { + case HOOK_COMMAND: + ret = ((xchat_cmd_cb *)hook->callback) (word, word_eol, hook->userdata); + break; + case HOOK_SERVER: + ret = ((xchat_serv_cb *)hook->callback) (word, word_eol, hook->userdata); + break; + default: /*case HOOK_PRINT:*/ + ret = ((xchat_print_cb *)hook->callback) (word, hook->userdata); + break; + } + + if ((ret & XCHAT_EAT_XCHAT) && (ret & XCHAT_EAT_PLUGIN)) + { + eat = 1; + goto xit; + } + if (ret & XCHAT_EAT_PLUGIN) + goto xit; /* stop running plugins */ + if (ret & XCHAT_EAT_XCHAT) + eat = 1; /* eventually we'll return 1, but continue running plugins */ + + list = next; + } + +xit: + /* really remove deleted hooks now */ + list = hook_list; + while (list) + { + hook = list->data; + next = list->next; + if (hook->type == HOOK_DELETED) + { + hook_list = g_slist_remove (hook_list, hook); + free (hook); + } + list = next; + } + + return eat; +} + +/* execute a plugged in command. Called from outbound.c */ + +int +plugin_emit_command (session *sess, char *name, char *word[], char *word_eol[]) +{ + return plugin_hook_run (sess, name, word, word_eol, HOOK_COMMAND); +} + +/* got a server PRIVMSG, NOTICE, numeric etc... */ + +int +plugin_emit_server (session *sess, char *name, char *word[], char *word_eol[]) +{ + return plugin_hook_run (sess, name, word, word_eol, HOOK_SERVER); +} + +/* see if any plugins are interested in this print event */ + +int +plugin_emit_print (session *sess, char *word[]) +{ + return plugin_hook_run (sess, word[0], word, NULL, HOOK_PRINT); +} + +int +plugin_emit_dummy_print (session *sess, char *name) +{ + char *word[32]; + int i; + + word[0] = name; + for (i = 1; i < 32; i++) + word[i] = "\000"; + + return plugin_hook_run (sess, name, word, NULL, HOOK_PRINT); +} + +int +plugin_emit_keypress (session *sess, unsigned int state, unsigned int keyval, + int len, char *string) +{ + char *word[PDIWORDS]; + char keyval_str[16]; + char state_str[16]; + char len_str[16]; + int i; + + if (!hook_list) + return 0; + + sprintf (keyval_str, "%u", keyval); + sprintf (state_str, "%u", state); + sprintf (len_str, "%d", len); + + word[0] = "Key Press"; + word[1] = keyval_str; + word[2] = state_str; + word[3] = string; + word[4] = len_str; + for (i = 5; i < PDIWORDS; i++) + word[i] = "\000"; + + return plugin_hook_run (sess, word[0], word, NULL, HOOK_PRINT); +} + +static int +plugin_timeout_cb (xchat_hook *hook) +{ + int ret; + + /* timer_cb's context starts as front-most-tab */ + hook->pl->context = current_sess; + + /* call the plugin's timeout function */ + ret = ((xchat_timer_cb *)hook->callback) (hook->userdata); + + /* the callback might have already unhooked it! */ + if (!g_slist_find (hook_list, hook) || hook->type == HOOK_DELETED) + return 0; + + if (ret == 0) + { + hook->tag = 0; /* avoid fe_timeout_remove, returning 0 is enough! */ + xchat_unhook (hook->pl, hook); + } + + return ret; +} + +/* insert a hook into hook_list according to its priority */ + +static void +plugin_insert_hook (xchat_hook *new_hook) +{ + GSList *list; + xchat_hook *hook; + + list = hook_list; + while (list) + { + hook = list->data; + if (hook->type == new_hook->type && hook->pri <= new_hook->pri) + { + hook_list = g_slist_insert_before (hook_list, list, new_hook); + return; + } + list = list->next; + } + + hook_list = g_slist_append (hook_list, new_hook); +} + +static gboolean +plugin_fd_cb (GIOChannel *source, GIOCondition condition, xchat_hook *hook) +{ + int flags = 0, ret; + typedef int (xchat_fd_cb2) (int fd, int flags, void *user_data, GIOChannel *); + + if (condition & G_IO_IN) + flags |= XCHAT_FD_READ; + if (condition & G_IO_OUT) + flags |= XCHAT_FD_WRITE; + if (condition & G_IO_PRI) + flags |= XCHAT_FD_EXCEPTION; + + ret = ((xchat_fd_cb2 *)hook->callback) (hook->pri, flags, hook->userdata, source); + + /* the callback might have already unhooked it! */ + if (!g_slist_find (hook_list, hook) || hook->type == HOOK_DELETED) + return 0; + + if (ret == 0) + { + hook->tag = 0; /* avoid fe_input_remove, returning 0 is enough! */ + xchat_unhook (hook->pl, hook); + } + + return ret; +} + +/* allocate and add a hook to our list. Used for all 4 types */ + +static xchat_hook * +plugin_add_hook (xchat_plugin *pl, int type, int pri, const char *name, + const char *help_text, void *callb, int timeout, void *userdata) +{ + xchat_hook *hook; + + hook = malloc (sizeof (xchat_hook)); + memset (hook, 0, sizeof (xchat_hook)); + + hook->type = type; + hook->pri = pri; + if (name) + hook->name = strdup (name); + if (help_text) + hook->help_text = strdup (help_text); + hook->callback = callb; + hook->pl = pl; + hook->userdata = userdata; + + /* insert it into the linked list */ + plugin_insert_hook (hook); + + if (type == HOOK_TIMER) + hook->tag = fe_timeout_add (timeout, plugin_timeout_cb, hook); + + return hook; +} + +GList * +plugin_command_list(GList *tmp_list) +{ + xchat_hook *hook; + GSList *list = hook_list; + + while (list) + { + hook = list->data; + if (hook->type == HOOK_COMMAND) + tmp_list = g_list_prepend(tmp_list, hook->name); + list = list->next; + } + return tmp_list; +} + +void +plugin_command_foreach (session *sess, void *userdata, + void (*cb) (session *sess, void *userdata, char *name, char *help)) +{ + GSList *list; + xchat_hook *hook; + + list = hook_list; + while (list) + { + hook = list->data; + if (hook->type == HOOK_COMMAND && hook->name[0]) + { + cb (sess, userdata, hook->name, hook->help_text); + } + list = list->next; + } +} + +int +plugin_show_help (session *sess, char *cmd) +{ + GSList *list; + xchat_hook *hook; + + list = plugin_hook_find (hook_list, HOOK_COMMAND, cmd); + if (list) + { + hook = list->data; + if (hook->help_text) + { + PrintText (sess, hook->help_text); + return 1; + } + } + + return 0; +} + +/* ========================================================= */ +/* ===== these are the functions plugins actually call ===== */ +/* ========================================================= */ + +void * +xchat_unhook (xchat_plugin *ph, xchat_hook *hook) +{ + /* perl.c trips this */ + if (!g_slist_find (hook_list, hook) || hook->type == HOOK_DELETED) + return NULL; + + if (hook->type == HOOK_TIMER && hook->tag != 0) + fe_timeout_remove (hook->tag); + + if (hook->type == HOOK_FD && hook->tag != 0) + fe_input_remove (hook->tag); + + hook->type = HOOK_DELETED; /* expunge later */ + + if (hook->name) + free (hook->name); /* NULL for timers & fds */ + if (hook->help_text) + free (hook->help_text); /* NULL for non-commands */ + + return hook->userdata; +} + +xchat_hook * +xchat_hook_command (xchat_plugin *ph, const char *name, int pri, + xchat_cmd_cb *callb, const char *help_text, void *userdata) +{ + return plugin_add_hook (ph, HOOK_COMMAND, pri, name, help_text, callb, 0, + userdata); +} + +xchat_hook * +xchat_hook_server (xchat_plugin *ph, const char *name, int pri, + xchat_serv_cb *callb, void *userdata) +{ + return plugin_add_hook (ph, HOOK_SERVER, pri, name, 0, callb, 0, userdata); +} + +xchat_hook * +xchat_hook_print (xchat_plugin *ph, const char *name, int pri, + xchat_print_cb *callb, void *userdata) +{ + return plugin_add_hook (ph, HOOK_PRINT, pri, name, 0, callb, 0, userdata); +} + +xchat_hook * +xchat_hook_timer (xchat_plugin *ph, int timeout, xchat_timer_cb *callb, + void *userdata) +{ + return plugin_add_hook (ph, HOOK_TIMER, 0, 0, 0, callb, timeout, userdata); +} + +xchat_hook * +xchat_hook_fd (xchat_plugin *ph, int fd, int flags, + xchat_fd_cb *callb, void *userdata) +{ + xchat_hook *hook; + + hook = plugin_add_hook (ph, HOOK_FD, 0, 0, 0, callb, 0, userdata); + hook->pri = fd; + /* plugin hook_fd flags correspond exactly to FIA_* flags (fe.h) */ + hook->tag = fe_input_add (fd, flags, plugin_fd_cb, hook); + + return hook; +} + +void +xchat_print (xchat_plugin *ph, const char *text) +{ + if (!is_session (ph->context)) + { + DEBUG(PrintTextf(0, "%s\txchat_print called without a valid context.\n", ph->name)); + return; + } + + PrintText (ph->context, (char *)text); +} + +void +xchat_printf (xchat_plugin *ph, const char *format, ...) +{ + va_list args; + char *buf; + + va_start (args, format); + buf = g_strdup_vprintf (format, args); + va_end (args); + + xchat_print (ph, buf); + g_free (buf); +} + +void +xchat_command (xchat_plugin *ph, const char *command) +{ + char *conv; + int len = -1; + + if (!is_session (ph->context)) + { + DEBUG(PrintTextf(0, "%s\txchat_command called without a valid context.\n", ph->name)); + return; + } + + /* scripts/plugins continue to send non-UTF8... *sigh* */ + conv = text_validate ((char **)&command, &len); + handle_command (ph->context, (char *)command, FALSE); + g_free (conv); +} + +void +xchat_commandf (xchat_plugin *ph, const char *format, ...) +{ + va_list args; + char *buf; + + va_start (args, format); + buf = g_strdup_vprintf (format, args); + va_end (args); + + xchat_command (ph, buf); + g_free (buf); +} + +int +xchat_nickcmp (xchat_plugin *ph, const char *s1, const char *s2) +{ + return ((session *)ph->context)->server->p_cmp (s1, s2); +} + +xchat_context * +xchat_get_context (xchat_plugin *ph) +{ + return ph->context; +} + +int +xchat_set_context (xchat_plugin *ph, xchat_context *context) +{ + if (is_session (context)) + { + ph->context = context; + return 1; + } + return 0; +} + +xchat_context * +xchat_find_context (xchat_plugin *ph, const char *servname, const char *channel) +{ + GSList *slist, *clist, *sessions = NULL; + server *serv; + session *sess; + char *netname; + + if (servname == NULL && channel == NULL) + return current_sess; + + slist = serv_list; + while (slist) + { + serv = slist->data; + netname = server_get_network (serv, TRUE); + + if (servname == NULL || + rfc_casecmp (servname, serv->servername) == 0 || + strcasecmp (servname, serv->hostname) == 0 || + strcasecmp (servname, netname) == 0) + { + if (channel == NULL) + return serv->front_session; + + clist = sess_list; + while (clist) + { + sess = clist->data; + if (sess->server == serv) + { + if (rfc_casecmp (channel, sess->channel) == 0) + { + if (sess->server == ph->context->server) + { + g_slist_free (sessions); + return sess; + } else + { + sessions = g_slist_prepend (sessions, sess); + } + } + } + clist = clist->next; + } + } + slist = slist->next; + } + + if (sessions) + { + sessions = g_slist_reverse (sessions); + sess = sessions->data; + g_slist_free (sessions); + return sess; + } + + return NULL; +} + +const char * +xchat_get_info (xchat_plugin *ph, const char *id) +{ + session *sess; + guint32 hash; + + /* 1234567890 */ + if (!strncmp (id, "event_text", 10)) + { + char *e = (char *)id + 10; + if (*e == ' ') e++; /* 2.8.0 only worked without a space */ + return text_find_format_string (e); + } + + hash = str_hash (id); + /* do the session independant ones first */ + switch (hash) + { + case 0x325acab5: /* libdirfs */ + return XCHATLIBDIR; + + case 0x14f51cd8: /* version */ + return PACKAGE_VERSION; + + case 0xdd9b1abd: /* xchatdir */ + return get_xdir_utf8 (); + + case 0xe33f6c4a: /* xchatdirfs */ + return get_xdir_fs (); + } + + sess = ph->context; + if (!is_session (sess)) + { + DEBUG(PrintTextf(0, "%s\txchat_get_info called without a valid context.\n", ph->name)); + return NULL; + } + + switch (hash) + { + case 0x2de2ee: /* away */ + if (sess->server->is_away) + return sess->server->last_away_reason; + return NULL; + + case 0x2c0b7d03: /* channel */ + return sess->channel; + + case 0x2c0d614c: /* charset */ + { + const char *locale; + + if (sess->server->encoding) + return sess->server->encoding; + + locale = NULL; + g_get_charset (&locale); + return locale; + } + + case 0x30f5a8: /* host */ + return sess->server->hostname; + + case 0x1c0e99c1: /* inputbox */ + return fe_get_inputbox_contents (sess); + + case 0x633fb30: /* modes */ + return sess->current_modes; + + case 0x6de15a2e: /* network */ + return server_get_network (sess->server, FALSE); + + case 0x339763: /* nick */ + return sess->server->nick; + + case 0x438fdf9: /* nickserv */ + if (sess->server->network) + return ((ircnet *)sess->server->network)->nickserv; + return NULL; + + case 0xca022f43: /* server */ + if (!sess->server->connected) + return NULL; + return sess->server->servername; + + case 0x696cd2f: /* topic */ + return sess->topic; + + case 0x3419f12d: /* gtkwin_ptr */ + return fe_gui_info_ptr (sess, 1); + + case 0x506d600b: /* native win_ptr */ + return fe_gui_info_ptr (sess, 0); + + case 0x6d3431b5: /* win_status */ + switch (fe_gui_info (sess, 0)) /* check window status */ + { + case 0: return "normal"; + case 1: return "active"; + case 2: return "hidden"; + } + return NULL; + } + + return NULL; +} + +int +xchat_get_prefs (xchat_plugin *ph, const char *name, const char **string, int *integer) +{ + int i = 0; + + /* some special run-time info (not really prefs, but may aswell throw it in here) */ + switch (str_hash (name)) + { + case 0xf82136c4: /* state_cursor */ + *integer = fe_get_inputbox_cursor (ph->context); + return 2; + + case 0xd1b: /* id */ + *integer = ph->context->server->id; + return 2; + } + + do + { + if (!strcasecmp (name, vars[i].name)) + { + switch (vars[i].type) + { + case TYPE_STR: + *string = ((char *) &prefs + vars[i].offset); + return 1; + + case TYPE_INT: + *integer = *((int *) &prefs + vars[i].offset); + return 2; + + default: + /*case TYPE_BOOL:*/ + if (*((int *) &prefs + vars[i].offset)) + *integer = 1; + else + *integer = 0; + return 3; + } + } + i++; + } + while (vars[i].name); + + return 0; +} + +xchat_list * +xchat_list_get (xchat_plugin *ph, const char *name) +{ + xchat_list *list; + + list = malloc (sizeof (xchat_list)); + list->pos = NULL; + + switch (str_hash (name)) + { + case 0x556423d0: /* channels */ + list->type = LIST_CHANNELS; + list->next = sess_list; + break; + + case 0x183c4: /* dcc */ + list->type = LIST_DCC; + list->next = dcc_list; + break; + + case 0xb90bfdd2: /* ignore */ + list->type = LIST_IGNORE; + list->next = ignore_list; + break; + + case 0xc2079749: /* notify */ + list->type = LIST_NOTIFY; + list->next = notify_list; + list->head = (void *)ph->context; /* reuse this pointer */ + break; + + case 0x6a68e08: /* users */ + if (is_session (ph->context)) + { + list->type = LIST_USERS; + list->head = list->next = userlist_flat_list (ph->context); + fe_userlist_set_selected (ph->context); + break; + } /* fall through */ + + default: + free (list); + return NULL; + } + + return list; +} + +void +xchat_list_free (xchat_plugin *ph, xchat_list *xlist) +{ + if (xlist->type == LIST_USERS) + g_slist_free (xlist->head); + free (xlist); +} + +int +xchat_list_next (xchat_plugin *ph, xchat_list *xlist) +{ + if (xlist->next == NULL) + return 0; + + xlist->pos = xlist->next; + xlist->next = xlist->pos->next; + + /* NOTIFY LIST: Find the entry which matches the context + of the plugin when list_get was originally called. */ + if (xlist->type == LIST_NOTIFY) + { + xlist->notifyps = notify_find_server_entry (xlist->pos->data, + ((session *)xlist->head)->server); + if (!xlist->notifyps) + return 0; + } + + return 1; +} + +const char * const * +xchat_list_fields (xchat_plugin *ph, const char *name) +{ + static const char * const dcc_fields[] = + { + "iaddress32","icps", "sdestfile","sfile", "snick", "iport", + "ipos", "iposhigh", "iresume", "iresumehigh", "isize", "isizehigh", "istatus", "itype", NULL + }; + static const char * const channels_fields[] = + { + "schannel", "schantypes", "pcontext", "iflags", "iid", "ilag", "imaxmodes", + "snetwork", "snickmodes", "snickprefixes", "iqueue", "sserver", "itype", "iusers", + NULL + }; + static const char * const ignore_fields[] = + { + "iflags", "smask", NULL + }; + static const char * const notify_fields[] = + { + "iflags", "snetworks", "snick", "toff", "ton", "tseen", NULL + }; + static const char * const users_fields[] = + { + "iaway", "shost", "tlasttalk", "snick", "sprefix", "srealname", "iselected", NULL + }; + static const char * const list_of_lists[] = + { + "channels", "dcc", "ignore", "notify", "users", NULL + }; + + switch (str_hash (name)) + { + case 0x556423d0: /* channels */ + return channels_fields; + case 0x183c4: /* dcc */ + return dcc_fields; + case 0xb90bfdd2: /* ignore */ + return ignore_fields; + case 0xc2079749: /* notify */ + return notify_fields; + case 0x6a68e08: /* users */ + return users_fields; + case 0x6236395: /* lists */ + return list_of_lists; + } + + return NULL; +} + +time_t +xchat_list_time (xchat_plugin *ph, xchat_list *xlist, const char *name) +{ + guint32 hash = str_hash (name); + gpointer data; + + switch (xlist->type) + { + case LIST_NOTIFY: + if (!xlist->notifyps) + return (time_t) -1; + switch (hash) + { + case 0x1ad6f: /* off */ + return xlist->notifyps->lastoff; + case 0xddf: /* on */ + return xlist->notifyps->laston; + case 0x35ce7b: /* seen */ + return xlist->notifyps->lastseen; + } + break; + + case LIST_USERS: + data = xlist->pos->data; + switch (hash) + { + case 0xa9118c42: /* lasttalk */ + return ((struct User *)data)->lasttalk; + } + } + + return (time_t) -1; +} + +const char * +xchat_list_str (xchat_plugin *ph, xchat_list *xlist, const char *name) +{ + guint32 hash = str_hash (name); + gpointer data = ph->context; + int type = LIST_CHANNELS; + + /* a NULL xlist is a shortcut to current "channels" context */ + if (xlist) + { + data = xlist->pos->data; + type = xlist->type; + } + + switch (type) + { + case LIST_CHANNELS: + switch (hash) + { + case 0x2c0b7d03: /* channel */ + return ((session *)data)->channel; + case 0x577e0867: /* chantypes */ + return ((session *)data)->server->chantypes; + case 0x38b735af: /* context */ + return data; /* this is a session * */ + case 0x6de15a2e: /* network */ + return server_get_network (((session *)data)->server, FALSE); + case 0x8455e723: /* nickprefixes */ + return ((session *)data)->server->nick_prefixes; + case 0x829689ad: /* nickmodes */ + return ((session *)data)->server->nick_modes; + case 0xca022f43: /* server */ + return ((session *)data)->server->servername; + } + break; + + case LIST_DCC: + switch (hash) + { + case 0x3d9ad31e: /* destfile */ + return ((struct DCC *)data)->destfile; + case 0x2ff57c: /* file */ + return ((struct DCC *)data)->file; + case 0x339763: /* nick */ + return ((struct DCC *)data)->nick; + } + break; + + case LIST_IGNORE: + switch (hash) + { + case 0x3306ec: /* mask */ + return ((struct ignore *)data)->mask; + } + break; + + case LIST_NOTIFY: + switch (hash) + { + case 0x4e49ec05: /* networks */ + return ((struct notify *)data)->networks; + case 0x339763: /* nick */ + return ((struct notify *)data)->name; + } + break; + + case LIST_USERS: + switch (hash) + { + case 0x339763: /* nick */ + return ((struct User *)data)->nick; + case 0x30f5a8: /* host */ + return ((struct User *)data)->hostname; + case 0xc594b292: /* prefix */ + return ((struct User *)data)->prefix; + case 0xccc6d529: /* realname */ + return ((struct User *)data)->realname; + } + break; + } + + return NULL; +} + +int +xchat_list_int (xchat_plugin *ph, xchat_list *xlist, const char *name) +{ + guint32 hash = str_hash (name); + gpointer data = ph->context; + int tmp, type = LIST_CHANNELS; + + /* a NULL xlist is a shortcut to current "channels" context */ + if (xlist) + { + data = xlist->pos->data; + type = xlist->type; + } + + switch (type) + { + case LIST_DCC: + switch (hash) + { + case 0x34207553: /* address32 */ + return ((struct DCC *)data)->addr; + case 0x181a6: /* cps */ + return ((struct DCC *)data)->cps; + case 0x349881: /* port */ + return ((struct DCC *)data)->port; + case 0x1b254: /* pos */ + return ((struct DCC *)data)->pos & 0xffffffff; + case 0xe8a945f6: /* poshigh */ + return (((struct DCC *)data)->pos >> 32) & 0xffffffff; + case 0xc84dc82d: /* resume */ + return ((struct DCC *)data)->resumable & 0xffffffff; + case 0xded4c74f: /* resumehigh */ + return (((struct DCC *)data)->resumable >> 32) & 0xffffffff; + case 0x35e001: /* size */ + return ((struct DCC *)data)->size & 0xffffffff; + case 0x3284d523: /* sizehigh */ + return (((struct DCC *)data)->size >> 32) & 0xffffffff; + case 0xcacdcff2: /* status */ + return ((struct DCC *)data)->dccstat; + case 0x368f3a: /* type */ + return ((struct DCC *)data)->type; + } + break; + + case LIST_IGNORE: + switch (hash) + { + case 0x5cfee87: /* flags */ + return ((struct ignore *)data)->type; + } + break; + + case LIST_CHANNELS: + switch (hash) + { + case 0xd1b: /* id */ + return ((struct session *)data)->server->id; + case 0x5cfee87: /* flags */ + tmp = ((struct session *)data)->alert_taskbar; /* bit 10 */ + tmp <<= 1; + tmp |= ((struct session *)data)->alert_tray; /* 9 */ + tmp <<= 1; + tmp |= ((struct session *)data)->alert_beep; /* 8 */ + tmp <<= 1; + /*tmp |= ((struct session *)data)->color_paste;*/ /* 7 */ + tmp <<= 1; + tmp |= ((struct session *)data)->text_hidejoinpart; /* 6 */ + tmp <<= 1; + tmp |= ((struct session *)data)->server->have_idmsg; /* 5 */ + tmp <<= 1; + tmp |= ((struct session *)data)->server->have_whox; /* 4 */ + tmp <<= 1; + tmp |= ((struct session *)data)->server->end_of_motd;/* 3 */ + tmp <<= 1; + tmp |= ((struct session *)data)->server->is_away; /* 2 */ + tmp <<= 1; + tmp |= ((struct session *)data)->server->connecting; /* 1 */ + tmp <<= 1; + tmp |= ((struct session *)data)->server->connected; /* 0 */ + return tmp; + case 0x1a192: /* lag */ + return ((struct session *)data)->server->lag; + case 0x1916144c: /* maxmodes */ + return ((struct session *)data)->server->modes_per_line; + case 0x66f1911: /* queue */ + return ((struct session *)data)->server->sendq_len; + case 0x368f3a: /* type */ + return ((struct session *)data)->type; + case 0x6a68e08: /* users */ + return ((struct session *)data)->total; + } + break; + + case LIST_NOTIFY: + if (!xlist->notifyps) + return -1; + switch (hash) + { + case 0x5cfee87: /* flags */ + return xlist->notifyps->ison; + } + + case LIST_USERS: + switch (hash) + { + case 0x2de2ee: /* away */ + return ((struct User *)data)->away; + case 0x4705f29b: /* selected */ + return ((struct User *)data)->selected; + } + break; + + } + + return -1; +} + +void * +xchat_plugingui_add (xchat_plugin *ph, const char *filename, + const char *name, const char *desc, + const char *version, char *reserved) +{ +#ifdef USE_PLUGIN + ph = plugin_list_add (NULL, strdup (filename), strdup (name), strdup (desc), + strdup (version), NULL, NULL, TRUE, TRUE); + fe_pluginlist_update (); +#endif + + return ph; +} + +void +xchat_plugingui_remove (xchat_plugin *ph, void *handle) +{ +#ifdef USE_PLUGIN + plugin_free (handle, FALSE, FALSE); +#endif +} + +int +xchat_emit_print (xchat_plugin *ph, const char *event_name, ...) +{ + va_list args; + /* currently only 4 because no events use more than 4. + This can be easily expanded without breaking the API. */ + char *argv[4] = {NULL, NULL, NULL, NULL}; + int i = 0; + + va_start (args, event_name); + while (1) + { + argv[i] = va_arg (args, char *); + if (!argv[i]) + break; + i++; + if (i >= 4) + break; + } + + i = text_emit_by_name ((char *)event_name, ph->context, argv[0], argv[1], + argv[2], argv[3]); + va_end (args); + + return i; +} + +char * +xchat_gettext (xchat_plugin *ph, const char *msgid) +{ + /* so that plugins can use xchat's internal gettext strings. */ + /* e.g. The EXEC plugin uses this on Windows. */ + return _(msgid); +} + +void +xchat_send_modes (xchat_plugin *ph, const char **targets, int ntargets, int modes_per_line, char sign, char mode) +{ + char tbuf[514]; /* modes.c needs 512 + null */ + + send_channel_modes (ph->context, tbuf, (char **)targets, 0, ntargets, sign, mode, modes_per_line); +} + +char * +xchat_strip (xchat_plugin *ph, const char *str, int len, int flags) +{ + return strip_color ((char *)str, len, flags); +} + +void +xchat_free (xchat_plugin *ph, void *ptr) +{ + g_free (ptr); +} |