/* * hook.c: Does those naughty hook functions. * * Written By Michael Sandrof * * Copyright (c) 1990 Michael Sandrof. * Copyright (c) 1991, 1992 Troy Rollo. * Copyright (c) 1992-2006 Matthew R. Green. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "irc.h" IRCII_RCSID("@(#)$eterna: hook.c,v 1.69 2006/07/22 03:50:10 mrg Exp $"); #include "hook.h" #include "vars.h" #include "ircaux.h" #include "alias.h" #include "list.h" #include "window.h" #include "server.h" #include "output.h" #include "edit.h" #include "buffer.h" #ifdef INCLUDE_UNUSED_FUNCTIONS static void flush_on_hooks(void); #endif /* INCLUDE_UNUSED_FUNCTIONS */ static u_char *fill_it_out(u_char *, int); static void setup_struct(int, int, int, int); static int Add_Remove_Check(List *, u_char *); static int Add_Remove_Check_List(List *, List*); static void add_numeric_hook(int, u_char *, u_char *, int, int, int, int); static void add_hook(int, u_char *, u_char *, int, int, int, int); static int show_numeric_list(int); static int show_list(int); static void remove_numeric_hook(int, u_char *, int, int, int); static void write_hook(FILE *, Hook *, u_char *); #define SILENT 0 #define QUIET 1 #define NORMAL 2 #define NOISY 3 /* * The various ON levels: SILENT means the DISPLAY will be OFF and it will * suppress the default action of the event, QUIET means the display will be * OFF but the default action will still take place, NORMAL means you will be * notified when an action takes place and the default action still occurs, * NOISY means you are notified when an action occur plus you see the action * in the display and the default actions still occurs */ static char *noise_level[] = { "SILENT", "QUIET", "NORMAL", "NOISY" }; #define HS_NOGENERIC 0x1000 #define HF_LOOKONLY 0x0001 #define HF_NORECURSE 0x0002 #define HF_GLOBAL 0x0004 extern int load_depth; int in_on_who = 0; NumericList *numeric_list = (NumericList *) 0; /* hook_functions: the list of all hook functions available */ HookFunc FAR hook_functions[] = { /* name, list, number of args, mark, flags */ { UP("ACTION"), (Hook *) 0, 3, 0, 0 }, { UP("CHANNEL_NICK"), (Hook *) 0, 3, 0, 0 }, { UP("CHANNEL_SIGNOFF"),(Hook *) 0, 3, 0, 0 }, { UP("CONNECT"), (Hook *) 0, 1, 0, 0 }, { UP("CTCP"), (Hook *) 0, 4, 0, 0 }, { UP("CTCP_REPLY"), (Hook *) 0, 3, 0, 0 }, { UP("DCC_CHAT"), (Hook *) 0, 2, 0, 0 }, { UP("DCC_CONNECT"), (Hook *) 0, 2, 0, 0 }, { UP("DCC_ERROR"), (Hook *) 0, 6, 0, 0 }, { UP("DCC_LIST"), (Hook *) 0, 6, 0, 0 }, { UP("DCC_LOST"), (Hook *) 0, 2, 0, 0 }, { UP("DCC_RAW"), (Hook *) 0, 3, 0, 0 }, { UP("DCC_REQUEST"), (Hook *) 0, 4, 0, 0 }, { UP("DISCONNECT"), (Hook *) 0, 1, 0, 0 }, { UP("ENCRYPTED_NOTICE"),(Hook *) 0, 3, 0, 0 }, { UP("ENCRYPTED_PRIVMSG"),(Hook *) 0, 3, 0, 0 }, { UP("EXEC"), (Hook *) 0, 2, 0, 0 }, { UP("EXEC_ERRORS"), (Hook *) 0, 2, 0, 0 }, { UP("EXEC_EXIT"), (Hook *) 0, 3, 0, 0 }, { UP("EXEC_PROMPT"), (Hook *) 0, 2, 0, 0 }, { UP("EXIT"), (Hook *) 0, 1, 0, 0 }, { UP("FLOOD"), (Hook *) 0, 3, 0, 0 }, { UP("HELP"), (Hook *) 0, 2, 0, 0 }, { UP("HOOK"), (Hook *) 0, 1, 0, 0 }, { UP("ICB_CMDOUT"), (Hook *) 0, 1, 0, 0 }, { UP("ICB_ERROR"), (Hook *) 0, 1, 0, 0 }, { UP("ICB_STATUS"), (Hook *) 0, 2, 0, 0 }, { UP("ICB_WHO"), (Hook *) 0, 6, 0, 0 }, { UP("IDLE"), (Hook *) 0, 1, 0, 0 }, { UP("INPUT"), (Hook *) 0, 1, 0, 0 }, { UP("INVITE"), (Hook *) 0, 2, 0, 0 }, { UP("JOIN"), (Hook *) 0, 3, 0, 0 }, { UP("KICK"), (Hook *) 0, 3, 0, HF_LOOKONLY }, { UP("LEAVE"), (Hook *) 0, 2, 0, 0 }, { UP("LIST"), (Hook *) 0, 3, 0, HF_LOOKONLY }, { UP("MAIL"), (Hook *) 0, 2, 0, 0 }, { UP("MODE"), (Hook *) 0, 3, 0, 0 }, { UP("MSG"), (Hook *) 0, 2, 0, 0 }, { UP("MSG_GROUP"), (Hook *) 0, 3, 0, 0 }, { UP("NAMES"), (Hook *) 0, 2, 0, HF_LOOKONLY }, { UP("NICKNAME"), (Hook *) 0, 2, 0, 0 }, { UP("NOTE"), (Hook *) 0, 10, 0, 0 }, { UP("NOTICE"), (Hook *) 0, 2, 0, 0 }, { UP("NOTIFY_SIGNOFF"), (Hook *) 0, 1, 0, 0 }, { UP("NOTIFY_SIGNON"), (Hook *) 0, 1, 0, 0 }, { UP("OS_SIGNAL"), (Hook *) 0, 1, 0, 0 }, { UP("PUBLIC"), (Hook *) 0, 3, 0, 0 }, { UP("PUBLIC_MSG"), (Hook *) 0, 3, 0, 0 }, { UP("PUBLIC_NOTICE"), (Hook *) 0, 3, 0, 0 }, { UP("PUBLIC_OTHER"), (Hook *) 0, 3, 0, 0 }, { UP("RAW_IRC"), (Hook *) 0, 1, 0, 0 }, { UP("RAW_SEND"), (Hook *) 0, 1, 0, 0 }, { UP("SEND_ACTION"), (Hook *) 0, 2, 0, 0 }, { UP("SEND_DCC_CHAT"), (Hook *) 0, 2, 0, 0 }, { UP("SEND_MSG"), (Hook *) 0, 2, 0, 0 }, { UP("SEND_NOTICE"), (Hook *) 0, 2, 0, 0 }, { UP("SEND_PUBLIC"), (Hook *) 0, 2, 0, 0 }, { UP("SEND_TALK"), (Hook *) 0, 2, 0, 0 }, { UP("SERVER_NOTICE"), (Hook *) 0, 1, 0, 0 }, { UP("SIGNOFF"), (Hook *) 0, 1, 0, 0 }, { UP("TALK"), (Hook *) 0, 2, 0, 0 }, { UP("TIMER"), (Hook *) 0, 1, 0, 0 }, { UP("TOPIC"), (Hook *) 0, 3, 0, 0 }, { UP("WALL"), (Hook *) 0, 2, 0, HF_LOOKONLY }, { UP("WALLOP"), (Hook *) 0, 3, 0, HF_LOOKONLY }, { UP("WHO"), (Hook *) 0, 6, 0, HF_LOOKONLY }, { UP("WIDELIST"), (Hook *) 0, 1, 0, HF_LOOKONLY }, { UP("WINDOW"), (Hook *) 0, 2, 0, HF_NORECURSE }, { UP("WINDOW_KILL"), (Hook *) 0, 1, 0, 0 }, { UP("WINDOW_LIST"), (Hook *) 0, 8, 0, 0 }, { UP("WINDOW_SWAP"), (Hook *) 0, 2, 0, 0 } }; static u_char * fill_it_out(str, params) u_char *str; int params; { u_char lbuf[BIG_BUFFER_SIZE]; u_char *arg, *free_ptr = (u_char *) 0, *ptr; int i = 0; malloc_strcpy(&free_ptr, str); ptr = free_ptr; *lbuf = (u_char) 0; while ((arg = next_arg(ptr, &ptr)) != NULL) { if (*lbuf) my_strmcat(lbuf, " ", sizeof lbuf); my_strmcat(lbuf, arg, sizeof lbuf); if (++i == params) break; } for (; i < params; i++) my_strmcat(lbuf, (i < params-1) ? " %" : " *", sizeof lbuf); if (*ptr) { my_strmcat(lbuf, " ", sizeof lbuf); my_strmcat(lbuf, ptr, sizeof lbuf); } malloc_strcpy(&free_ptr, lbuf); return (free_ptr); } /* * A variety of comparison functions used by the hook routines follow. */ struct CmpInfoStruc { int ServerRequired; int SkipSerialNum; int SerialNumber; int Flags; } cmp_info; #define CIF_NOSERNUM 0x0001 #define CIF_SKIP 0x0002 int cmpinfodone = 0; static void setup_struct(ServReq, SkipSer, SerNum, flags) int ServReq; int SkipSer; int SerNum; int flags; { cmp_info.ServerRequired = ServReq; cmp_info.SkipSerialNum = SkipSer; cmp_info.SerialNumber = SerNum; cmp_info.Flags = flags; } static int Add_Remove_Check(_Item, Name) List *_Item; u_char *Name; { int comp; Hook *Item = (Hook *)_Item; if (cmp_info.SerialNumber != Item->sernum) return (Item->sernum > cmp_info.SerialNumber) ? 1 : -1; if ((comp = my_stricmp(Item->nick, Name)) != 0) return comp; if (Item->server != cmp_info.ServerRequired) return (Item->server > cmp_info.ServerRequired) ? 1 : -1; return 0; } static int Add_Remove_Check_List(_Item, _Item2) List *_Item; List *_Item2; { return Add_Remove_Check(_Item, _Item->name); } static void add_numeric_hook(numeric, nick, stuff, noisy, not, server, sernum) int numeric; u_char *nick, *stuff; int noisy, not; int server, sernum; { NumericList *entry; Hook *new; u_char buf[4]; snprintf(CP(buf), sizeof buf, "%3.3u", numeric); if ((entry = (NumericList *) find_in_list((List **)(void *)&numeric_list, buf, 0)) == NULL) { entry = (NumericList *) new_malloc(sizeof(NumericList)); entry->name = NULL; entry->list = NULL; malloc_strcpy(&(entry->name), buf); add_to_list((List **)(void *)&numeric_list, (List *) entry); } setup_struct((server==-1) ? -1 : (server & ~HS_NOGENERIC), sernum-1, sernum, 0); if ((new = (Hook *) remove_from_list_ext((List **)(void *)&(entry->list), nick, Add_Remove_Check)) != NULL) { new->not = 1; new_free(&(new->nick)); new_free(&(new->stuff)); wait_new_free((u_char **)(void *)&new); } new = (Hook *) new_malloc(sizeof(Hook)); new->nick = (u_char *) 0; new->noisy = noisy; new->server = server; new->sernum = sernum; new->not = not; new->global = loading_global; new->stuff = (u_char *) 0; malloc_strcpy(&new->nick, nick); malloc_strcpy(&new->stuff, stuff); upper(new->nick); add_to_list_ext((List **)(void *)&(entry->list), (List *) new, Add_Remove_Check_List); } /* * add_hook: Given an index into the hook_functions array, this adds a new * entry to the list as specified by the rest of the parameters. The new * entry is added in alphabetical order (by nick). */ static void add_hook(which, nick, stuff, noisy, not, server, sernum) int which; u_char *nick, *stuff; int noisy, not; int server, sernum; { Hook *new; if (which < 0) { add_numeric_hook(-which, nick, stuff, noisy, not, server, sernum); return; } setup_struct((server == -1) ? -1 : (server & ~HS_NOGENERIC), sernum-1, sernum, 0); if ((new = (Hook *) remove_from_list_ext((List **)(void *)&(hook_functions[which].list), nick, Add_Remove_Check)) != NULL) { new->not = 1; new_free(&(new->nick)); new_free(&(new->stuff)); wait_new_free((u_char **)(void *)&new); } new = (Hook *) new_malloc(sizeof(Hook)); new->nick = (u_char *) 0; new->noisy = noisy; new->server = server; new->sernum = sernum; new->not = not; new->stuff = (u_char *) 0; new->global = loading_global; malloc_strcpy(&new->nick, nick); malloc_strcpy(&new->stuff, stuff); upper(new->nick); add_to_list_ext((List **)(void *)&(hook_functions[which].list), (List *) new, Add_Remove_Check_List); } /* show_hook shows a single hook */ extern void show_hook(list, name) Hook *list; u_char *name; { if (list->server != -1) say("On %s from \"%s\" do %s [%s] <%d> (Server %d)%s", name, list->nick, (list->not ? (u_char *) "nothing" : list->stuff), noise_level[list->noisy], list->sernum, list->server&~HS_NOGENERIC, (list->server&HS_NOGENERIC) ? (u_char *) " Exclusive" : empty_string); else say("On %s from \"%s\" do %s [%s] <%d>", name, list->nick, (list->not ? (u_char *) "nothing" : list->stuff), noise_level[list->noisy], list->sernum); } /* * show_numeric_list: If numeric is 0, then all numeric lists are displayed. * If numeric is non-zero, then that particular list is displayed. The total * number of entries displayed is returned */ static int show_numeric_list(numeric) int numeric; { NumericList *tmp; Hook *list; u_char buf[4]; int cnt = 0; if (numeric) { snprintf(CP(buf), sizeof buf, "%3.3u", numeric); if ((tmp = (NumericList *) find_in_list((List **)(void *)&numeric_list, buf, 0)) != NULL) { for (list = tmp->list; list; list = list->next, cnt++) show_hook(list, tmp->name); } } else { for (tmp = numeric_list; tmp; tmp = tmp->next) { for (list = tmp->list; list; list = list->next, cnt++) show_hook(list, tmp->name); } } return (cnt); } /* * show_list: Displays the contents of the list specified by the index into * the hook_functions array. This function returns the number of entries in * the list displayed */ static int show_list(which) int which; { Hook *list; int cnt = 0; /* Less garbage when issueing /on without args. (lynx) */ for (list = hook_functions[which].list; list; list = list->next, cnt++) show_hook(list, hook_functions[which].name); return (cnt); } /* * do_hook: This is what gets called whenever a MSG, INVITES, WALL, (you get * the idea) occurs. The nick is looked up in the appropriate list. If a * match is found, the stuff field from that entry in the list is treated as * if it were a command. First it gets expanded as though it were an alias * (with the args parameter used as the arguments to the alias). After it * gets expanded, it gets parsed as a command. This will return as its value * the value of the noisy field of the found entry, or -1 if not found. */ /* huh-huh.. this sucks.. im going to re-write it so that it works */ /*VARARGS*/ int do_hook(int which, char *format, ...) { va_list vl; Hook *tmp, **list; u_char *name = NULL; int RetVal = 1; unsigned int display; int i, old_in_on_who; Hook *hook_array[2048]; int hook_num = 0; static int hook_level = 0; size_t len; u_char *foo; PUTBUF_INIT #ifdef NEED_PUTBUF_DECLARED /* make this buffer *much* bigger than needed */ u_char putbuf[2*BIG_BUFFER_SIZE]; putbuf[0] ='\0'; #endif hook_level++; va_start(vl, format); PUTBUF_SPRINTF(format, vl); va_end(vl); if (which < 0) { NumericList *hook; u_char buf[4]; snprintf(CP(buf), sizeof buf, "%3.3u", -which); if ((hook = (NumericList *) find_in_list((List **)(void *)&numeric_list, buf, 0)) != NULL) { name = hook->name; list = &hook->list; } else list = (Hook **) 0; } else { if (hook_functions[which].mark && (hook_functions[which].flags & HF_NORECURSE)) list = (Hook **) 0; else { list = &(hook_functions[which].list); name = hook_functions[which].name; } } if (!list) { RetVal = 1; goto out; } if (which >= 0) hook_functions[which].mark++; /* not attached, so dont "fix" it */ { int currser = 0, oldser = 0; int currmatch = 0, oldmatch = 0; Hook *bestmatch = (Hook *) 0; int nomorethisserial = 0; for (tmp = *list;tmp;tmp = tmp->next) { currser = tmp->sernum; if (currser != oldser) /* new serial number */ { oldser = currser; currmatch = oldmatch = nomorethisserial = 0; if (bestmatch) hook_array[hook_num++] = bestmatch; bestmatch = (Hook *) 0; } if (nomorethisserial) continue; /* if there is a specific server hook and it doesnt match, then we make sure nothing from this serial number gets hooked */ if ((tmp->server != -1) && (tmp->server & HS_NOGENERIC) && (tmp->server != (from_server & HS_NOGENERIC))) { nomorethisserial = 1; bestmatch = (Hook *) 0; continue; } currmatch = wild_match(tmp->nick, putbuf); if (currmatch > oldmatch) { oldmatch = currmatch; bestmatch = tmp; } } if (bestmatch) hook_array[hook_num++] = bestmatch; } for (i = 0; i < hook_num; i++) { tmp = hook_array[i]; if (!tmp) { if (which >= 0) hook_functions[which].mark--; goto out; } if (tmp->not) continue; send_text_flag = which; if (tmp->noisy > QUIET) say("%s activated by \"%s\"", name, putbuf); display = window_display; if (tmp->noisy < NOISY) window_display = 0; save_message_from(); old_in_on_who = in_on_who; if (which == WHO_LIST || (which <= -311 && which >= -318)) in_on_who = 1; len = my_strlen(tmp->stuff) + 1; foo = new_malloc(len); bcopy(tmp->stuff, foo, len); parse_line((u_char *) 0, foo, putbuf, 0, 0, 1); new_free(&foo); in_on_who = old_in_on_who; window_display = display; send_text_flag = -1; restore_message_from(); if (!tmp->noisy && !tmp->sernum) RetVal = 0; } if (which >= 0) hook_functions[which].mark--; out: PUTBUF_END return really_free(--hook_level), RetVal; } static void remove_numeric_hook(numeric, nick, server, sernum, quiet) int numeric; u_char *nick; int server; int sernum; int quiet; { NumericList *hook; Hook *tmp, *next; u_char buf[4]; snprintf(CP(buf), sizeof buf, "%3.3u", numeric); if ((hook = (NumericList *) find_in_list((List **)(void *)&numeric_list, buf,0)) != NULL) { if (nick) { setup_struct((server == -1) ? -1 : (server & ~HS_NOGENERIC), sernum - 1, sernum, 0); if ((tmp = (Hook *) remove_from_list((List **)(void *)&(hook->list), nick)) != NULL) { if (!quiet) say("\"%s\" removed from %s list", nick, buf); tmp->not = 1; new_free(&(tmp->nick)); new_free(&(tmp->stuff)); wait_new_free((u_char **)(void *)&tmp); if (hook->list == (Hook *) 0) { if ((hook = (NumericList *) remove_from_list((List **)(void *)&numeric_list, buf)) != NULL) { new_free(&(hook->name)); new_free(&hook); } } return; } } else { for (tmp = hook->list; tmp; tmp = next) { next = tmp->next; tmp->not = 1; new_free(&(tmp->nick)); new_free(&(tmp->stuff)); wait_new_free((u_char **)(void *)&tmp); } hook->list = (Hook *) 0; if (!quiet) say("The %s list is empty", buf); return; } } if (quiet) return; if (nick) say("\"%s\" is not on the %s list", nick, buf); else say("The %s list is empty", buf); } #ifdef INCLUDE_UNUSED_FUNCTIONS static void flush_on_hooks() { int x; int old_display = window_display; window_display = 0; for (x = 100 ; x < 999; x++) remove_numeric_hook(x, (u_char *) 0, 1, x, 0); for (x = 0 ; x < NUMBER_OF_LISTS; x++) remove_hook(x, (u_char *) 0, 1, x, 0); window_display = old_display; } #endif /* INCLUDE_UNUSED_FUNCTIONS */ extern void remove_hook(which, nick, server, sernum, quiet) int which; u_char *nick; int server, sernum, quiet; { Hook *tmp, *next; if (which < 0) { remove_numeric_hook(-which, nick, server, sernum, quiet); return; } if (nick) { setup_struct((server == -1) ? -1 : (server & ~HS_NOGENERIC), sernum-1, sernum, 0); if ((tmp = (Hook *) remove_from_list_ext((List **)(void *)&hook_functions[which].list, nick, Add_Remove_Check)) != NULL) { if (!quiet) say("\"%s\" removed from %s list", nick, hook_functions[which].name); tmp->not = 1; new_free(&(tmp->nick)); new_free(&(tmp->stuff)); wait_new_free((u_char **)(void *)&tmp); } else if (!quiet) say("\"%s\" is not on the %s list", nick, hook_functions[which].name); } else { for(tmp = hook_functions[which].list; tmp; tmp=next) { next = tmp->next; tmp->not = 1; new_free(&(tmp->nick)); new_free(&(tmp->stuff)); wait_new_free((u_char **)(void *)&tmp); } hook_functions[which].list = (Hook *) 0; if (!quiet) say("The %s list is empty", hook_functions[which].name); } } /* on: The ON command */ /*ARGSUSED*/ void on(command, args, subargs) u_char *command, *args, *subargs; { u_char *func, *nick, *serial, *cmd = (u_char *) 0; /* int noisy = NORMAL, not = 0, do_remove = 0, -not used */ int noisy, not, server, sernum, do_remove, which = 0, cnt, i, ambiguous = 0; size_t len; if (get_int_var(NOVICE_VAR) && !load_depth) { yell("*** You may not type ON commands when you have the NOVICE"); yell("*** variable set to ON. Some ON commands may cause a"); yell("*** security breach on your machine, or enable another"); yell("*** user to control your IRC session. Read the help files"); yell("*** in /HELP ON before using ON"); return; } if ((func = next_arg(args, &args)) != NULL) { if (*func == '#') { if (!(serial = next_arg(args, &args))) { say("No serial number specified"); return; } sernum = my_atoi(serial); func++; } else sernum = 0; switch (*func) { case '&': server = from_server; func++; break; case '@': server = from_server|HS_NOGENERIC; func++; break; default: server = -1; break; } switch (*func) { case '-': noisy = QUIET; func++; break; case '^': noisy = SILENT; func++; break; case '+': noisy = NOISY; func++; break; default: noisy = NORMAL; break; } if ((len = my_strlen(func)) == 0) { say("You must specify an event type!"); return; } malloc_strcpy(&cmd, func); upper(cmd); for (cnt = 0, i = 0; i < NUMBER_OF_LISTS; i++) { if (!my_strncmp(cmd, hook_functions[i].name, len)) { if (my_strlen(hook_functions[i].name) == len) { cnt = 1; which = i; break; } else { cnt++; which = i; } } else if (cnt) break; } if (cnt == 0) { if (is_number(cmd)) { which = my_atoi(cmd); if ((which < 0) || (which > 999)) { say("Numerics must be between 001 and 999"); goto out; } which = -which; } else { say("No such ON function: %s", func); goto out; } } else if (cnt > 1) ambiguous = 1; else { if (get_int_var(INPUT_PROTECTION_VAR) && !my_strnicmp(UP(hook_functions[which].name), UP("INPUT"), 5)) { say("You cannot use /ON INPUT with INPUT_PROTECTION set"); say("Please read /HELP ON INPUT, and /HELP SET INPUT_PROTECTION"); goto out; } } do_remove = 0; not = 0; switch (*args) { case '-': do_remove = 1; args++; break; case '^': not = 1; args++; break; } if ((nick = new_next_arg(args, &args)) != NULL) { if (ambiguous) { say("Ambiguous ON function: %s", func); goto out; } if (which < 0) nick = fill_it_out(nick, 1); else nick = fill_it_out(nick, hook_functions[which].params); if (do_remove) { if (my_strlen(nick) == 0) say("No expression specified"); else remove_hook(which, nick, server, sernum, 0); } else { if (not) args = empty_string; if (*nick) { if (*args == LEFT_BRACE) { u_char *ptr; ptr = MatchingBracket(++args, LEFT_BRACE, RIGHT_BRACE); if (!ptr) { say("Unmatched brace in ON"); new_free(&nick); goto out; } else if (ptr[1]) { say("Junk after closing brace in ON"); new_free(&nick); goto out; } else *ptr = '\0'; } add_hook(which, nick, args, noisy, not, server, sernum); if (which < 0) say("On %3.3u from \"%s\" do %s [%s] <%d>", -which, nick, (not ? (u_char *) "nothing" : args), noise_level[noisy], sernum); else say("On %s from \"%s\" do %s [%s] <%d>", hook_functions[which].name, nick, (not ? (u_char *) "nothing" : args), noise_level[noisy], sernum); } } new_free(&nick); } else { if (do_remove) { if (ambiguous) { say("Ambiguous ON function: %s", func); goto out; } remove_hook(which, (u_char *) 0, server, sernum, 0); } else { if (which < 0) { if (show_numeric_list(-which) == 0) say("The %3.3u list is empty.", -which); } else if (ambiguous) { for (i = 0; i < NUMBER_OF_LISTS; i++) if (!my_strncmp(cmd, hook_functions[i].name, len)) (void)show_list(i); } else if (show_list(which) == 0) say("The %s list is empty.", hook_functions[which].name); } } } else { int total = 0; say("ON listings:"); for (which = 0; which < NUMBER_OF_LISTS; which++) total += show_list(which); total += show_numeric_list(0); if (total == 0) say("All ON lists are empty."); } out: new_free(&cmd); } static void write_hook(fp, hook, name) FILE *fp; Hook *hook; u_char *name; { char *stuff = (char *) 0; if (hook->server!=-1) return; switch (hook->noisy) { case SILENT: stuff = "^"; break; case QUIET: stuff = "-"; break; case NORMAL: stuff = CP(empty_string); break; case NOISY: stuff = "+"; break; } if (hook->sernum) fprintf(fp, "ON #%s%s %d \"%s\"", stuff, name, hook->sernum, hook->nick); else fprintf(fp, "ON %s%s \"%s\"", stuff, name, hook->nick); fprintf(fp, " %s\n", hook->stuff); } /* * save_hooks: for use by the SAVE command to write the hooks to a file so it * can be interpreted by the LOAD command */ void save_hooks(fp, do_all) FILE *fp; int do_all; { Hook *list; NumericList *numeric; int which; for (which = 0; which < NUMBER_OF_LISTS; which++) { for (list = hook_functions[which].list; list; list = list->next) if (!list->global || do_all) write_hook(fp,list, hook_functions[which].name); } for (numeric = numeric_list; numeric; numeric = numeric->next) { for (list = numeric->list; list; list = list->next) if (!list->global) write_hook(fp, list, numeric->name); } }