Now inbound_cap_ls() can enable extensions when a bouncer uses a namespace for the...
[rofl0r-ixchat.git] / src / common / plugin.c
blob46c7c3218381d1682754519abf8f8e7b68c1c465
1 /* X-Chat
2 * Copyright (C) 2002 Peter Zelezny.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 #include <stdlib.h>
20 #include <string.h>
21 #include <stdarg.h>
22 #include <stdio.h>
24 #include "xchat.h"
25 #include "fe.h"
26 #include "util.h"
27 #include "outbound.h"
28 #include "cfgfiles.h"
29 #include "ignore.h"
30 #include "server.h"
31 #include "servlist.h"
32 #include "modes.h"
33 #include "notify.h"
34 #include "text.h"
35 #define PLUGIN_C
36 typedef struct session xchat_context;
37 #include "xchat-plugin.h"
38 #include "plugin.h"
41 #include "xchatc.h"
43 /* the USE_PLUGIN define only removes libdl stuff */
45 #ifdef USE_PLUGIN
46 #ifdef USE_GMODULE
47 #include <gmodule.h>
48 #else
49 #include <dlfcn.h>
50 #endif
51 #endif
53 #define DEBUG(x) {x;}
55 /* crafted to be an even 32 bytes */
56 struct _xchat_hook
58 xchat_plugin *pl; /* the plugin to which it belongs */
59 char *name; /* "xdcc" */
60 void *callback; /* pointer to xdcc_callback */
61 char *help_text; /* help_text for commands only */
62 void *userdata; /* passed to the callback */
63 int tag; /* for timers & FDs only */
64 int type; /* HOOK_* */
65 int pri; /* fd */ /* priority / fd for HOOK_FD only */
68 struct _xchat_list
70 int type; /* LIST_* */
71 GSList *pos; /* current pos */
72 GSList *next; /* next pos */
73 GSList *head; /* for LIST_USERS only */
74 struct notify_per_server *notifyps; /* notify_per_server * */
77 typedef int (xchat_cmd_cb) (char *word[], char *word_eol[], void *user_data);
78 typedef int (xchat_serv_cb) (char *word[], char *word_eol[], void *user_data);
79 typedef int (xchat_print_cb) (char *word[], void *user_data);
80 typedef int (xchat_fd_cb) (int fd, int flags, void *user_data);
81 typedef int (xchat_timer_cb) (void *user_data);
82 typedef int (xchat_init_func) (xchat_plugin *, char **, char **, char **, char *);
83 typedef int (xchat_deinit_func) (xchat_plugin *);
85 enum
87 LIST_CHANNELS,
88 LIST_DCC,
89 LIST_IGNORE,
90 LIST_NOTIFY,
91 LIST_USERS
94 enum
96 HOOK_COMMAND, /* /command */
97 HOOK_SERVER, /* PRIVMSG, NOTICE, numerics */
98 HOOK_PRINT, /* All print events */
99 HOOK_TIMER, /* timeouts */
100 HOOK_FD, /* sockets & fds */
101 HOOK_DELETED /* marked for deletion */
104 GSList *plugin_list = NULL; /* export for plugingui.c */
105 static GSList *hook_list = NULL;
107 extern const struct prefs vars[]; /* cfgfiles.c */
110 /* unload a plugin and remove it from our linked list */
112 static int
113 plugin_free (xchat_plugin *pl, int do_deinit, int allow_refuse)
115 GSList *list, *next;
116 xchat_hook *hook;
117 xchat_deinit_func *deinit_func;
119 /* fake plugin added by xchat_plugingui_add() */
120 if (pl->fake)
121 goto xit;
123 /* run the plugin's deinit routine, if any */
124 if (do_deinit && pl->deinit_callback != NULL)
126 deinit_func = pl->deinit_callback;
127 if (!deinit_func (pl) && allow_refuse)
128 return FALSE;
131 /* remove all of this plugin's hooks */
132 list = hook_list;
133 while (list)
135 hook = list->data;
136 next = list->next;
137 if (hook->pl == pl)
138 xchat_unhook (NULL, hook);
139 list = next;
142 #ifdef USE_PLUGIN
143 if (pl->handle)
144 #ifdef USE_GMODULE
145 g_module_close (pl->handle);
146 #else
147 dlclose (pl->handle);
148 #endif
149 #endif
151 xit:
152 if (pl->free_strings)
154 if (pl->name)
155 free (pl->name);
156 if (pl->desc)
157 free (pl->desc);
158 if (pl->version)
159 free (pl->version);
161 if (pl->filename)
162 free ((char *)pl->filename);
163 free (pl);
165 plugin_list = g_slist_remove (plugin_list, pl);
167 #ifdef USE_PLUGIN
168 fe_pluginlist_update ();
169 #endif
171 return TRUE;
174 static xchat_plugin *
175 plugin_list_add (xchat_context *ctx, char *filename, const char *name,
176 const char *desc, const char *version, void *handle,
177 void *deinit_func, int fake, int free_strings)
179 xchat_plugin *pl;
181 pl = malloc (sizeof (xchat_plugin));
182 pl->handle = handle;
183 pl->filename = filename;
184 pl->context = ctx;
185 pl->name = (char *)name;
186 pl->desc = (char *)desc;
187 pl->version = (char *)version;
188 pl->deinit_callback = deinit_func;
189 pl->fake = fake;
190 pl->free_strings = free_strings; /* free() name,desc,version? */
192 plugin_list = g_slist_prepend (plugin_list, pl);
194 return pl;
197 static void *
198 xchat_dummy (xchat_plugin *ph)
200 return NULL;
203 /* Load a static plugin */
205 void
206 plugin_add (session *sess, char *filename, void *handle, void *init_func,
207 void *deinit_func, char *arg, int fake)
209 xchat_plugin *pl;
210 char *file;
212 file = NULL;
213 if (filename)
214 file = strdup (filename);
216 pl = plugin_list_add (sess, file, file, NULL, NULL, handle, deinit_func,
217 fake, FALSE);
219 if (!fake)
221 /* win32 uses these because it doesn't have --export-dynamic! */
222 pl->xchat_hook_command = xchat_hook_command;
223 pl->xchat_hook_server = xchat_hook_server;
224 pl->xchat_hook_print = xchat_hook_print;
225 pl->xchat_hook_timer = xchat_hook_timer;
226 pl->xchat_hook_fd = xchat_hook_fd;
227 pl->xchat_unhook = xchat_unhook;
228 pl->xchat_print = xchat_print;
229 pl->xchat_printf = xchat_printf;
230 pl->xchat_command = xchat_command;
231 pl->xchat_commandf = xchat_commandf;
232 pl->xchat_nickcmp = xchat_nickcmp;
233 pl->xchat_set_context = xchat_set_context;
234 pl->xchat_find_context = xchat_find_context;
235 pl->xchat_get_context = xchat_get_context;
236 pl->xchat_get_info = xchat_get_info;
237 pl->xchat_get_prefs = xchat_get_prefs;
238 pl->xchat_list_get = xchat_list_get;
239 pl->xchat_list_free = xchat_list_free;
240 pl->xchat_list_fields = xchat_list_fields;
241 pl->xchat_list_str = xchat_list_str;
242 pl->xchat_list_next = xchat_list_next;
243 pl->xchat_list_int = xchat_list_int;
244 pl->xchat_plugingui_add = xchat_plugingui_add;
245 pl->xchat_plugingui_remove = xchat_plugingui_remove;
246 pl->xchat_emit_print = xchat_emit_print;
247 pl->xchat_read_fd = xchat_dummy;
248 pl->xchat_list_time = xchat_list_time;
249 pl->xchat_gettext = xchat_gettext;
250 pl->xchat_send_modes = xchat_send_modes;
251 pl->xchat_strip = xchat_strip;
252 pl->xchat_free = xchat_free;
254 /* incase new plugins are loaded on older xchat */
255 pl->xchat_dummy4 = xchat_dummy;
256 pl->xchat_dummy3 = xchat_dummy;
257 pl->xchat_dummy2 = xchat_dummy;
258 pl->xchat_dummy1 = xchat_dummy;
260 /* run xchat_plugin_init, if it returns 0, close the plugin */
261 if (((xchat_init_func *)init_func) (pl, &pl->name, &pl->desc, &pl->version, arg) == 0)
263 plugin_free (pl, FALSE, FALSE);
264 return;
268 #ifdef USE_PLUGIN
269 fe_pluginlist_update ();
270 #endif
273 /* kill any plugin by the given (file) name (used by /unload) */
276 plugin_kill (char *name, int by_filename)
278 GSList *list;
279 xchat_plugin *pl;
281 list = plugin_list;
282 while (list)
284 pl = list->data;
285 /* static-plugins (plugin-timer.c) have a NULL filename */
286 if ((by_filename && pl->filename && strcasecmp (name, pl->filename) == 0) ||
287 (by_filename && pl->filename && strcasecmp (name, file_part (pl->filename)) == 0) ||
288 (!by_filename && strcasecmp (name, pl->name) == 0))
290 /* statically linked plugins have a NULL filename */
291 if (pl->filename != NULL && !pl->fake)
293 if (plugin_free (pl, TRUE, TRUE))
294 return 1;
295 return 2;
298 list = list->next;
301 return 0;
304 /* kill all running plugins (at shutdown) */
306 void
307 plugin_kill_all (void)
309 GSList *list, *next;
310 xchat_plugin *pl;
312 list = plugin_list;
313 while (list)
315 pl = list->data;
316 next = list->next;
317 if (!pl->fake)
318 plugin_free (list->data, TRUE, FALSE);
319 list = next;
323 #ifdef USE_PLUGIN
325 /* load a plugin from a filename. Returns: NULL-success or an error string */
327 char *
328 plugin_load (session *sess, char *filename, char *arg)
330 void *handle;
331 xchat_init_func *init_func;
332 xchat_deinit_func *deinit_func;
334 #ifdef USE_GMODULE
335 /* load the plugin */
336 handle = g_module_open (filename, 0);
337 if (handle == NULL)
338 return (char *)g_module_error ();
340 /* find the init routine xchat_plugin_init */
341 if (!g_module_symbol (handle, "xchat_plugin_init", (gpointer *)&init_func))
343 g_module_close (handle);
344 return _("No xchat_plugin_init symbol; is this really an xchat plugin?");
347 /* find the plugin's deinit routine, if any */
348 if (!g_module_symbol (handle, "xchat_plugin_deinit", (gpointer *)&deinit_func))
349 deinit_func = NULL;
351 #else
352 char *error;
353 char *filepart;
355 /* OpenBSD lacks this! */
356 #ifndef RTLD_GLOBAL
357 #define RTLD_GLOBAL 0
358 #endif
360 #ifndef RTLD_NOW
361 #define RTLD_NOW 0
362 #endif
364 /* get the filename without path */
365 filepart = file_part (filename);
367 /* load the plugin */
368 if (filepart &&
369 /* xsys draws in libgtk-1.2, causing crashes, so force RTLD_LOCAL */
370 (strstr (filepart, "local") || strncmp (filepart, "libxsys-1", 9) == 0)
372 handle = dlopen (filename, RTLD_NOW);
373 else
374 handle = dlopen (filename, RTLD_GLOBAL | RTLD_NOW);
375 if (handle == NULL)
376 return (char *)dlerror ();
377 dlerror (); /* Clear any existing error */
379 /* find the init routine xchat_plugin_init */
380 init_func = dlsym (handle, "xchat_plugin_init");
381 error = (char *)dlerror ();
382 if (error != NULL)
384 dlclose (handle);
385 return _("No xchat_plugin_init symbol; is this really an xchat plugin?");
388 /* find the plugin's deinit routine, if any */
389 deinit_func = dlsym (handle, "xchat_plugin_deinit");
390 error = (char *)dlerror ();
391 #endif
393 /* add it to our linked list */
394 plugin_add (sess, filename, handle, init_func, deinit_func, arg, FALSE);
396 return NULL;
399 static session *ps;
401 static void
402 plugin_auto_load_cb (char *filename)
404 char *pMsg;
406 if (!strcmp (file_part (filename), "dbus.so"))
407 return;
409 pMsg = plugin_load (ps, filename, NULL);
410 if (pMsg)
412 PrintTextf (ps, "AutoLoad failed for: %s\n", filename);
413 PrintText (ps, pMsg);
417 void
418 plugin_auto_load (session *sess)
420 ps = sess;
421 for_files (XCHATLIBDIR"/plugins", "*.so", plugin_auto_load_cb);
422 for_files (get_xdir_fs (), "*.so", plugin_auto_load_cb);
425 #endif
427 static GSList *
428 plugin_hook_find (GSList *list, int type, char *name)
430 xchat_hook *hook;
432 while (list)
434 hook = list->data;
435 if (hook->type == type)
437 if (strcasecmp (hook->name, name) == 0)
438 return list;
440 if (type == HOOK_SERVER)
442 if (strcasecmp (hook->name, "RAW LINE") == 0)
443 return list;
446 list = list->next;
449 return NULL;
452 /* check for plugin hooks and run them */
454 static int
455 plugin_hook_run (session *sess, char *name, char *word[], char *word_eol[], int type)
457 GSList *list, *next;
458 xchat_hook *hook;
459 int ret, eat = 0;
461 list = hook_list;
462 while (1)
464 list = plugin_hook_find (list, type, name);
465 if (!list)
466 goto xit;
468 hook = list->data;
469 next = list->next;
470 hook->pl->context = sess;
472 /* run the plugin's callback function */
473 switch (type)
475 case HOOK_COMMAND:
476 ret = ((xchat_cmd_cb *)hook->callback) (word, word_eol, hook->userdata);
477 break;
478 case HOOK_SERVER:
479 ret = ((xchat_serv_cb *)hook->callback) (word, word_eol, hook->userdata);
480 break;
481 default: /*case HOOK_PRINT:*/
482 ret = ((xchat_print_cb *)hook->callback) (word, hook->userdata);
483 break;
486 if ((ret & XCHAT_EAT_XCHAT) && (ret & XCHAT_EAT_PLUGIN))
488 eat = 1;
489 goto xit;
491 if (ret & XCHAT_EAT_PLUGIN)
492 goto xit; /* stop running plugins */
493 if (ret & XCHAT_EAT_XCHAT)
494 eat = 1; /* eventually we'll return 1, but continue running plugins */
496 list = next;
499 xit:
500 /* really remove deleted hooks now */
501 list = hook_list;
502 while (list)
504 hook = list->data;
505 next = list->next;
506 if (hook->type == HOOK_DELETED)
508 hook_list = g_slist_remove (hook_list, hook);
509 free (hook);
511 list = next;
514 return eat;
517 /* execute a plugged in command. Called from outbound.c */
520 plugin_emit_command (session *sess, char *name, char *word[], char *word_eol[])
522 return plugin_hook_run (sess, name, word, word_eol, HOOK_COMMAND);
525 /* got a server PRIVMSG, NOTICE, numeric etc... */
528 plugin_emit_server (session *sess, char *name, char *word[], char *word_eol[])
530 return plugin_hook_run (sess, name, word, word_eol, HOOK_SERVER);
533 /* see if any plugins are interested in this print event */
536 plugin_emit_print (session *sess, char *word[])
538 return plugin_hook_run (sess, word[0], word, NULL, HOOK_PRINT);
542 plugin_emit_dummy_print (session *sess, char *name)
544 char *word[32];
545 int i;
547 word[0] = name;
548 for (i = 1; i < 32; i++)
549 word[i] = "\000";
551 return plugin_hook_run (sess, name, word, NULL, HOOK_PRINT);
555 plugin_emit_keypress (session *sess, unsigned int state, unsigned int keyval,
556 int len, char *string)
558 char *word[PDIWORDS];
559 char keyval_str[16];
560 char state_str[16];
561 char len_str[16];
562 int i;
564 if (!hook_list)
565 return 0;
567 sprintf (keyval_str, "%u", keyval);
568 sprintf (state_str, "%u", state);
569 sprintf (len_str, "%d", len);
571 word[0] = "Key Press";
572 word[1] = keyval_str;
573 word[2] = state_str;
574 word[3] = string;
575 word[4] = len_str;
576 for (i = 5; i < PDIWORDS; i++)
577 word[i] = "\000";
579 return plugin_hook_run (sess, word[0], word, NULL, HOOK_PRINT);
582 static int
583 plugin_timeout_cb (xchat_hook *hook)
585 int ret;
587 /* timer_cb's context starts as front-most-tab */
588 hook->pl->context = current_sess;
590 /* call the plugin's timeout function */
591 ret = ((xchat_timer_cb *)hook->callback) (hook->userdata);
593 /* the callback might have already unhooked it! */
594 if (!g_slist_find (hook_list, hook) || hook->type == HOOK_DELETED)
595 return 0;
597 if (ret == 0)
599 hook->tag = 0; /* avoid fe_timeout_remove, returning 0 is enough! */
600 xchat_unhook (hook->pl, hook);
603 return ret;
606 /* insert a hook into hook_list according to its priority */
608 static void
609 plugin_insert_hook (xchat_hook *new_hook)
611 GSList *list;
612 xchat_hook *hook;
614 list = hook_list;
615 while (list)
617 hook = list->data;
618 if (hook->type == new_hook->type && hook->pri <= new_hook->pri)
620 hook_list = g_slist_insert_before (hook_list, list, new_hook);
621 return;
623 list = list->next;
626 hook_list = g_slist_append (hook_list, new_hook);
629 static gboolean
630 plugin_fd_cb (GIOChannel *source, GIOCondition condition, xchat_hook *hook)
632 int flags = 0, ret;
633 typedef int (xchat_fd_cb2) (int fd, int flags, void *user_data, GIOChannel *);
635 if (condition & G_IO_IN)
636 flags |= XCHAT_FD_READ;
637 if (condition & G_IO_OUT)
638 flags |= XCHAT_FD_WRITE;
639 if (condition & G_IO_PRI)
640 flags |= XCHAT_FD_EXCEPTION;
642 ret = ((xchat_fd_cb2 *)hook->callback) (hook->pri, flags, hook->userdata, source);
644 /* the callback might have already unhooked it! */
645 if (!g_slist_find (hook_list, hook) || hook->type == HOOK_DELETED)
646 return 0;
648 if (ret == 0)
650 hook->tag = 0; /* avoid fe_input_remove, returning 0 is enough! */
651 xchat_unhook (hook->pl, hook);
654 return ret;
657 /* allocate and add a hook to our list. Used for all 4 types */
659 static xchat_hook *
660 plugin_add_hook (xchat_plugin *pl, int type, int pri, const char *name,
661 const char *help_text, void *callb, int timeout, void *userdata)
663 xchat_hook *hook;
665 hook = malloc (sizeof (xchat_hook));
666 memset (hook, 0, sizeof (xchat_hook));
668 hook->type = type;
669 hook->pri = pri;
670 if (name)
671 hook->name = strdup (name);
672 if (help_text)
673 hook->help_text = strdup (help_text);
674 hook->callback = callb;
675 hook->pl = pl;
676 hook->userdata = userdata;
678 /* insert it into the linked list */
679 plugin_insert_hook (hook);
681 if (type == HOOK_TIMER)
682 hook->tag = fe_timeout_add (timeout, plugin_timeout_cb, hook);
684 return hook;
687 GList *
688 plugin_command_list(GList *tmp_list)
690 xchat_hook *hook;
691 GSList *list = hook_list;
693 while (list)
695 hook = list->data;
696 if (hook->type == HOOK_COMMAND)
697 tmp_list = g_list_prepend(tmp_list, hook->name);
698 list = list->next;
700 return tmp_list;
703 void
704 plugin_command_foreach (session *sess, void *userdata,
705 void (*cb) (session *sess, void *userdata, char *name, char *help))
707 GSList *list;
708 xchat_hook *hook;
710 list = hook_list;
711 while (list)
713 hook = list->data;
714 if (hook->type == HOOK_COMMAND && hook->name[0])
716 cb (sess, userdata, hook->name, hook->help_text);
718 list = list->next;
723 plugin_show_help (session *sess, char *cmd)
725 GSList *list;
726 xchat_hook *hook;
728 list = plugin_hook_find (hook_list, HOOK_COMMAND, cmd);
729 if (list)
731 hook = list->data;
732 if (hook->help_text)
734 PrintText (sess, hook->help_text);
735 return 1;
739 return 0;
742 /* ========================================================= */
743 /* ===== these are the functions plugins actually call ===== */
744 /* ========================================================= */
746 void *
747 xchat_unhook (xchat_plugin *ph, xchat_hook *hook)
749 /* perl.c trips this */
750 if (!g_slist_find (hook_list, hook) || hook->type == HOOK_DELETED)
751 return NULL;
753 if (hook->type == HOOK_TIMER && hook->tag != 0)
754 fe_timeout_remove (hook->tag);
756 if (hook->type == HOOK_FD && hook->tag != 0)
757 fe_input_remove (hook->tag);
759 hook->type = HOOK_DELETED; /* expunge later */
761 if (hook->name)
762 free (hook->name); /* NULL for timers & fds */
763 if (hook->help_text)
764 free (hook->help_text); /* NULL for non-commands */
766 return hook->userdata;
769 xchat_hook *
770 xchat_hook_command (xchat_plugin *ph, const char *name, int pri,
771 xchat_cmd_cb *callb, const char *help_text, void *userdata)
773 return plugin_add_hook (ph, HOOK_COMMAND, pri, name, help_text, callb, 0,
774 userdata);
777 xchat_hook *
778 xchat_hook_server (xchat_plugin *ph, const char *name, int pri,
779 xchat_serv_cb *callb, void *userdata)
781 return plugin_add_hook (ph, HOOK_SERVER, pri, name, 0, callb, 0, userdata);
784 xchat_hook *
785 xchat_hook_print (xchat_plugin *ph, const char *name, int pri,
786 xchat_print_cb *callb, void *userdata)
788 return plugin_add_hook (ph, HOOK_PRINT, pri, name, 0, callb, 0, userdata);
791 xchat_hook *
792 xchat_hook_timer (xchat_plugin *ph, int timeout, xchat_timer_cb *callb,
793 void *userdata)
795 return plugin_add_hook (ph, HOOK_TIMER, 0, 0, 0, callb, timeout, userdata);
798 xchat_hook *
799 xchat_hook_fd (xchat_plugin *ph, int fd, int flags,
800 xchat_fd_cb *callb, void *userdata)
802 xchat_hook *hook;
804 hook = plugin_add_hook (ph, HOOK_FD, 0, 0, 0, callb, 0, userdata);
805 hook->pri = fd;
806 /* plugin hook_fd flags correspond exactly to FIA_* flags (fe.h) */
807 hook->tag = fe_input_add (fd, flags, plugin_fd_cb, hook);
809 return hook;
812 void
813 xchat_print (xchat_plugin *ph, const char *text)
815 if (!is_session (ph->context))
817 DEBUG(PrintTextf(0, "%s\txchat_print called without a valid context.\n", ph->name));
818 return;
821 PrintText (ph->context, (char *)text);
824 void
825 xchat_printf (xchat_plugin *ph, const char *format, ...)
827 va_list args;
828 char *buf;
830 va_start (args, format);
831 buf = g_strdup_vprintf (format, args);
832 va_end (args);
834 xchat_print (ph, buf);
835 g_free (buf);
838 void
839 xchat_command (xchat_plugin *ph, const char *command)
841 char *conv;
842 int len = -1;
844 if (!is_session (ph->context))
846 DEBUG(PrintTextf(0, "%s\txchat_command called without a valid context.\n", ph->name));
847 return;
850 /* scripts/plugins continue to send non-UTF8... *sigh* */
851 conv = text_validate ((char **)&command, &len);
852 handle_command (ph->context, (char *)command, FALSE);
853 g_free (conv);
856 void
857 xchat_commandf (xchat_plugin *ph, const char *format, ...)
859 va_list args;
860 char *buf;
862 va_start (args, format);
863 buf = g_strdup_vprintf (format, args);
864 va_end (args);
866 xchat_command (ph, buf);
867 g_free (buf);
871 xchat_nickcmp (xchat_plugin *ph, const char *s1, const char *s2)
873 return ((session *)ph->context)->server->p_cmp (s1, s2);
876 xchat_context *
877 xchat_get_context (xchat_plugin *ph)
879 return ph->context;
883 xchat_set_context (xchat_plugin *ph, xchat_context *context)
885 if (is_session (context))
887 ph->context = context;
888 return 1;
890 return 0;
893 xchat_context *
894 xchat_find_context (xchat_plugin *ph, const char *servname, const char *channel)
896 GSList *slist, *clist, *sessions = NULL;
897 server *serv;
898 session *sess;
899 char *netname;
901 if (servname == NULL && channel == NULL)
902 return current_sess;
904 slist = serv_list;
905 while (slist)
907 serv = slist->data;
908 netname = server_get_network (serv, TRUE);
910 if (servname == NULL ||
911 rfc_casecmp (servname, serv->servername) == 0 ||
912 strcasecmp (servname, serv->hostname) == 0 ||
913 strcasecmp (servname, netname) == 0)
915 if (channel == NULL)
916 return serv->front_session;
918 clist = sess_list;
919 while (clist)
921 sess = clist->data;
922 if (sess->server == serv)
924 if (rfc_casecmp (channel, sess->channel) == 0)
926 if (sess->server == ph->context->server)
928 g_slist_free (sessions);
929 return sess;
930 } else
932 sessions = g_slist_prepend (sessions, sess);
936 clist = clist->next;
939 slist = slist->next;
942 if (sessions)
944 sessions = g_slist_reverse (sessions);
945 sess = sessions->data;
946 g_slist_free (sessions);
947 return sess;
950 return NULL;
953 const char *
954 xchat_get_info (xchat_plugin *ph, const char *id)
956 session *sess;
957 guint32 hash;
959 /* 1234567890 */
960 if (!strncmp (id, "event_text", 10))
962 char *e = (char *)id + 10;
963 if (*e == ' ') e++; /* 2.8.0 only worked without a space */
964 return text_find_format_string (e);
967 hash = str_hash (id);
968 /* do the session independant ones first */
969 switch (hash)
971 case 0x325acab5: /* libdirfs */
972 return XCHATLIBDIR;
974 case 0x14f51cd8: /* version */
975 return PACKAGE_VERSION;
977 case 0xdd9b1abd: /* xchatdir */
978 return get_xdir_utf8 ();
980 case 0xe33f6c4a: /* xchatdirfs */
981 return get_xdir_fs ();
984 sess = ph->context;
985 if (!is_session (sess))
987 DEBUG(PrintTextf(0, "%s\txchat_get_info called without a valid context.\n", ph->name));
988 return NULL;
991 switch (hash)
993 case 0x2de2ee: /* away */
994 if (sess->server->is_away)
995 return sess->server->last_away_reason;
996 return NULL;
998 case 0x2c0b7d03: /* channel */
999 return sess->channel;
1001 case 0x2c0d614c: /* charset */
1003 const char *locale;
1005 if (sess->server->encoding)
1006 return sess->server->encoding;
1008 locale = NULL;
1009 g_get_charset (&locale);
1010 return locale;
1013 case 0x30f5a8: /* host */
1014 return sess->server->hostname;
1016 case 0x1c0e99c1: /* inputbox */
1017 return fe_get_inputbox_contents (sess);
1019 case 0x633fb30: /* modes */
1020 return sess->current_modes;
1022 case 0x6de15a2e: /* network */
1023 return server_get_network (sess->server, FALSE);
1025 case 0x339763: /* nick */
1026 return sess->server->nick;
1028 case 0xca022f43: /* server */
1029 if (!sess->server->connected)
1030 return NULL;
1031 return sess->server->servername;
1033 case 0x696cd2f: /* topic */
1034 return sess->topic;
1036 case 0x3419f12d: /* gtkwin_ptr */
1037 return fe_gui_info_ptr (sess, 1);
1039 case 0x506d600b: /* native win_ptr */
1040 return fe_gui_info_ptr (sess, 0);
1042 case 0x6d3431b5: /* win_status */
1043 switch (fe_gui_info (sess, 0)) /* check window status */
1045 case 0: return "normal";
1046 case 1: return "active";
1047 case 2: return "hidden";
1049 return NULL;
1052 return NULL;
1056 xchat_get_prefs (xchat_plugin *ph, const char *name, const char **string, int *integer)
1058 int i = 0;
1060 /* some special run-time info (not really prefs, but may aswell throw it in here) */
1061 switch (str_hash (name))
1063 case 0xf82136c4: /* state_cursor */
1064 *integer = fe_get_inputbox_cursor (ph->context);
1065 return 2;
1067 case 0xd1b: /* id */
1068 *integer = ph->context->server->id;
1069 return 2;
1074 if (!strcasecmp (name, vars[i].name))
1076 switch (vars[i].type)
1078 case TYPE_STR:
1079 *string = ((char *) &prefs + vars[i].offset);
1080 return 1;
1082 case TYPE_INT:
1083 *integer = *((int *) &prefs + vars[i].offset);
1084 return 2;
1086 default:
1087 /*case TYPE_BOOL:*/
1088 if (*((int *) &prefs + vars[i].offset))
1089 *integer = 1;
1090 else
1091 *integer = 0;
1092 return 3;
1095 i++;
1097 while (vars[i].name);
1099 return 0;
1102 xchat_list *
1103 xchat_list_get (xchat_plugin *ph, const char *name)
1105 xchat_list *list;
1107 list = malloc (sizeof (xchat_list));
1108 list->pos = NULL;
1110 switch (str_hash (name))
1112 case 0x556423d0: /* channels */
1113 list->type = LIST_CHANNELS;
1114 list->next = sess_list;
1115 break;
1117 case 0x183c4: /* dcc */
1118 list->type = LIST_DCC;
1119 list->next = dcc_list;
1120 break;
1122 case 0xb90bfdd2: /* ignore */
1123 list->type = LIST_IGNORE;
1124 list->next = ignore_list;
1125 break;
1127 case 0xc2079749: /* notify */
1128 list->type = LIST_NOTIFY;
1129 list->next = notify_list;
1130 list->head = (void *)ph->context; /* reuse this pointer */
1131 break;
1133 case 0x6a68e08: /* users */
1134 if (is_session (ph->context))
1136 list->type = LIST_USERS;
1137 list->head = list->next = userlist_flat_list (ph->context);
1138 fe_userlist_set_selected (ph->context);
1139 break;
1140 } /* fall through */
1142 default:
1143 free (list);
1144 return NULL;
1147 return list;
1150 void
1151 xchat_list_free (xchat_plugin *ph, xchat_list *xlist)
1153 if (xlist->type == LIST_USERS)
1154 g_slist_free (xlist->head);
1155 free (xlist);
1159 xchat_list_next (xchat_plugin *ph, xchat_list *xlist)
1161 if (xlist->next == NULL)
1162 return 0;
1164 xlist->pos = xlist->next;
1165 xlist->next = xlist->pos->next;
1167 /* NOTIFY LIST: Find the entry which matches the context
1168 of the plugin when list_get was originally called. */
1169 if (xlist->type == LIST_NOTIFY)
1171 xlist->notifyps = notify_find_server_entry (xlist->pos->data,
1172 ((session *)xlist->head)->server);
1173 if (!xlist->notifyps)
1174 return 0;
1177 return 1;
1180 const char * const *
1181 xchat_list_fields (xchat_plugin *ph, const char *name)
1183 static const char * const dcc_fields[] =
1185 "iaddress32","icps", "sdestfile","sfile", "snick", "iport",
1186 "ipos", "iposhigh", "iresume", "iresumehigh", "isize", "isizehigh", "istatus", "itype", NULL
1188 static const char * const channels_fields[] =
1190 "schannel", "schantypes", "pcontext", "iflags", "iid", "ilag", "imaxmodes",
1191 "snetwork", "snickmodes", "snickprefixes", "iqueue", "sserver", "itype", "iusers",
1192 NULL
1194 static const char * const ignore_fields[] =
1196 "iflags", "smask", NULL
1198 static const char * const notify_fields[] =
1200 "iflags", "snetworks", "snick", "toff", "ton", "tseen", NULL
1202 static const char * const users_fields[] =
1204 "iaway", "shost", "tlasttalk", "snick", "sprefix", "srealname", "iselected", NULL
1206 static const char * const list_of_lists[] =
1208 "channels", "dcc", "ignore", "notify", "users", NULL
1211 switch (str_hash (name))
1213 case 0x556423d0: /* channels */
1214 return channels_fields;
1215 case 0x183c4: /* dcc */
1216 return dcc_fields;
1217 case 0xb90bfdd2: /* ignore */
1218 return ignore_fields;
1219 case 0xc2079749: /* notify */
1220 return notify_fields;
1221 case 0x6a68e08: /* users */
1222 return users_fields;
1223 case 0x6236395: /* lists */
1224 return list_of_lists;
1227 return NULL;
1230 time_t
1231 xchat_list_time (xchat_plugin *ph, xchat_list *xlist, const char *name)
1233 guint32 hash = str_hash (name);
1234 gpointer data;
1236 switch (xlist->type)
1238 case LIST_NOTIFY:
1239 if (!xlist->notifyps)
1240 return (time_t) -1;
1241 switch (hash)
1243 case 0x1ad6f: /* off */
1244 return xlist->notifyps->lastoff;
1245 case 0xddf: /* on */
1246 return xlist->notifyps->laston;
1247 case 0x35ce7b: /* seen */
1248 return xlist->notifyps->lastseen;
1250 break;
1252 case LIST_USERS:
1253 data = xlist->pos->data;
1254 switch (hash)
1256 case 0xa9118c42: /* lasttalk */
1257 return ((struct User *)data)->lasttalk;
1261 return (time_t) -1;
1264 const char *
1265 xchat_list_str (xchat_plugin *ph, xchat_list *xlist, const char *name)
1267 guint32 hash = str_hash (name);
1268 gpointer data = ph->context;
1269 int type = LIST_CHANNELS;
1271 /* a NULL xlist is a shortcut to current "channels" context */
1272 if (xlist)
1274 data = xlist->pos->data;
1275 type = xlist->type;
1278 switch (type)
1280 case LIST_CHANNELS:
1281 switch (hash)
1283 case 0x2c0b7d03: /* channel */
1284 return ((session *)data)->channel;
1285 case 0x577e0867: /* chantypes */
1286 return ((session *)data)->server->chantypes;
1287 case 0x38b735af: /* context */
1288 return data; /* this is a session * */
1289 case 0x6de15a2e: /* network */
1290 return server_get_network (((session *)data)->server, FALSE);
1291 case 0x8455e723: /* nickprefixes */
1292 return ((session *)data)->server->nick_prefixes;
1293 case 0x829689ad: /* nickmodes */
1294 return ((session *)data)->server->nick_modes;
1295 case 0xca022f43: /* server */
1296 return ((session *)data)->server->servername;
1298 break;
1300 case LIST_DCC:
1301 switch (hash)
1303 case 0x3d9ad31e: /* destfile */
1304 return ((struct DCC *)data)->destfile;
1305 case 0x2ff57c: /* file */
1306 return ((struct DCC *)data)->file;
1307 case 0x339763: /* nick */
1308 return ((struct DCC *)data)->nick;
1310 break;
1312 case LIST_IGNORE:
1313 switch (hash)
1315 case 0x3306ec: /* mask */
1316 return ((struct ignore *)data)->mask;
1318 break;
1320 case LIST_NOTIFY:
1321 switch (hash)
1323 case 0x4e49ec05: /* networks */
1324 return ((struct notify *)data)->networks;
1325 case 0x339763: /* nick */
1326 return ((struct notify *)data)->name;
1328 break;
1330 case LIST_USERS:
1331 switch (hash)
1333 case 0x339763: /* nick */
1334 return ((struct User *)data)->nick;
1335 case 0x30f5a8: /* host */
1336 return ((struct User *)data)->hostname;
1337 case 0xc594b292: /* prefix */
1338 return ((struct User *)data)->prefix;
1339 case 0xccc6d529: /* realname */
1340 return ((struct User *)data)->realname;
1342 break;
1345 return NULL;
1349 xchat_list_int (xchat_plugin *ph, xchat_list *xlist, const char *name)
1351 guint32 hash = str_hash (name);
1352 gpointer data = ph->context;
1353 int tmp, type = LIST_CHANNELS;
1355 /* a NULL xlist is a shortcut to current "channels" context */
1356 if (xlist)
1358 data = xlist->pos->data;
1359 type = xlist->type;
1362 switch (type)
1364 case LIST_DCC:
1365 switch (hash)
1367 case 0x34207553: /* address32 */
1368 return ((struct DCC *)data)->addr;
1369 case 0x181a6: /* cps */
1370 return ((struct DCC *)data)->cps;
1371 case 0x349881: /* port */
1372 return ((struct DCC *)data)->port;
1373 case 0x1b254: /* pos */
1374 return ((struct DCC *)data)->pos & 0xffffffff;
1375 case 0xe8a945f6: /* poshigh */
1376 #ifdef USE_DCC64
1377 return (((struct DCC *)data)->pos >> 32) & 0xffffffff;
1378 #else
1379 return (((struct DCC *)data)->pos);
1380 #endif
1381 case 0xc84dc82d: /* resume */
1382 return ((struct DCC *)data)->resumable & 0xffffffff;
1383 case 0xded4c74f: /* resumehigh */
1384 #ifdef USE_DCC64
1385 return (((struct DCC *)data)->resumable >> 32) & 0xffffffff;
1386 #else
1387 return (((struct DCC *)data)->resumable);
1388 #endif
1389 case 0x35e001: /* size */
1390 return ((struct DCC *)data)->size & 0xffffffff;
1391 case 0x3284d523: /* sizehigh */
1392 #ifdef USE_DCC64
1393 return (((struct DCC *)data)->size >> 32) & 0xffffffff;
1394 #else
1395 return (((struct DCC *)data)->size);
1396 #endif
1397 case 0xcacdcff2: /* status */
1398 return ((struct DCC *)data)->dccstat;
1399 case 0x368f3a: /* type */
1400 return ((struct DCC *)data)->type;
1402 break;
1404 case LIST_IGNORE:
1405 switch (hash)
1407 case 0x5cfee87: /* flags */
1408 return ((struct ignore *)data)->type;
1410 break;
1412 case LIST_CHANNELS:
1413 switch (hash)
1415 case 0xd1b: /* id */
1416 return ((struct session *)data)->server->id;
1417 case 0x5cfee87: /* flags */
1418 tmp = ((struct session *)data)->alert_taskbar; /* bit 10 */
1419 tmp <<= 1;
1420 tmp |= ((struct session *)data)->alert_tray; /* 9 */
1421 tmp <<= 1;
1422 tmp |= ((struct session *)data)->alert_beep; /* 8 */
1423 tmp <<= 1;
1424 /*tmp |= ((struct session *)data)->color_paste;*/ /* 7 */
1425 tmp <<= 1;
1426 tmp |= ((struct session *)data)->text_hidejoinpart; /* 6 */
1427 tmp <<= 1;
1428 tmp |= ((struct session *)data)->server->have_idmsg; /* 5 */
1429 tmp <<= 1;
1430 tmp |= ((struct session *)data)->server->have_whox; /* 4 */
1431 tmp <<= 1;
1432 tmp |= ((struct session *)data)->server->end_of_motd;/* 3 */
1433 tmp <<= 1;
1434 tmp |= ((struct session *)data)->server->is_away; /* 2 */
1435 tmp <<= 1;
1436 tmp |= ((struct session *)data)->server->connecting; /* 1 */
1437 tmp <<= 1;
1438 tmp |= ((struct session *)data)->server->connected; /* 0 */
1439 return tmp;
1440 case 0x1a192: /* lag */
1441 return ((struct session *)data)->server->lag;
1442 case 0x1916144c: /* maxmodes */
1443 return ((struct session *)data)->server->modes_per_line;
1444 case 0x66f1911: /* queue */
1445 return ((struct session *)data)->server->sendq_len;
1446 case 0x368f3a: /* type */
1447 return ((struct session *)data)->type;
1448 case 0x6a68e08: /* users */
1449 return ((struct session *)data)->total;
1451 break;
1453 case LIST_NOTIFY:
1454 if (!xlist->notifyps)
1455 return -1;
1456 switch (hash)
1458 case 0x5cfee87: /* flags */
1459 return xlist->notifyps->ison;
1462 case LIST_USERS:
1463 switch (hash)
1465 case 0x2de2ee: /* away */
1466 return ((struct User *)data)->away;
1467 case 0x4705f29b: /* selected */
1468 return ((struct User *)data)->selected;
1470 break;
1474 return -1;
1477 void *
1478 xchat_plugingui_add (xchat_plugin *ph, const char *filename,
1479 const char *name, const char *desc,
1480 const char *version, char *reserved)
1482 #ifdef USE_PLUGIN
1483 ph = plugin_list_add (NULL, strdup (filename), strdup (name), strdup (desc),
1484 strdup (version), NULL, NULL, TRUE, TRUE);
1485 fe_pluginlist_update ();
1486 #endif
1488 return ph;
1491 void
1492 xchat_plugingui_remove (xchat_plugin *ph, void *handle)
1494 #ifdef USE_PLUGIN
1495 plugin_free (handle, FALSE, FALSE);
1496 #endif
1500 xchat_emit_print (xchat_plugin *ph, const char *event_name, ...)
1502 va_list args;
1503 /* currently only 4 because no events use more than 4.
1504 This can be easily expanded without breaking the API. */
1505 char *argv[4] = {NULL, NULL, NULL, NULL};
1506 int i = 0;
1508 va_start (args, event_name);
1509 while (1)
1511 argv[i] = va_arg (args, char *);
1512 if (!argv[i])
1513 break;
1514 i++;
1515 if (i >= 4)
1516 break;
1519 i = text_emit_by_name ((char *)event_name, ph->context, argv[0], argv[1],
1520 argv[2], argv[3]);
1521 va_end (args);
1523 return i;
1526 char *
1527 xchat_gettext (xchat_plugin *ph, const char *msgid)
1529 /* so that plugins can use xchat's internal gettext strings. */
1530 /* e.g. The EXEC plugin uses this on Windows. */
1531 return _(msgid);
1534 void
1535 xchat_send_modes (xchat_plugin *ph, const char **targets, int ntargets, int modes_per_line, char sign, char mode)
1537 char tbuf[514]; /* modes.c needs 512 + null */
1539 send_channel_modes (ph->context, tbuf, (char **)targets, 0, ntargets, sign, mode, modes_per_line);
1542 char *
1543 xchat_strip (xchat_plugin *ph, const char *str, int len, int flags)
1545 return strip_color ((char *)str, len, flags);
1548 void
1549 xchat_free (xchat_plugin *ph, void *ptr)
1551 g_free (ptr);