1 /*
2 * hook.c: Does those naughty hook functions.
3 *
4 * Copyright (c) 1990 Michael Sandroff.
5 * Copyright (c) 1991, 1992 Troy Rollo.
6 * Copyright (c) 1992-1996 Matthew Green.
7 * Copyright 1993, 2003 EPIC Software Labs.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notices, the above paragraph (the one permitting redistribution),
17 * this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. The names of the author(s) may not be used to endorse or promote
20 * products derived from this software without specific prior written
21 * permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include "irc.h"
37 #include "hook.h"
38 #include "ircaux.h"
39 #define __need_ArgList_t__
40 #include "alias.h"
41 #include "window.h"
42 #include "output.h"
43 #include "commands.h"
44 #include "ifcmd.h"
45 #include "stack.h"
46 #include "reg.h"
47 #include "functions.h"
48
49 /*
50 * The various ON levels: SILENT means the DISPLAY will be OFF and it will
51 * suppress the default action of the event, QUIET means the display will be
52 * OFF but the default action will still take place, NORMAL means you will be
53 * notified when an action takes place and the default action still occurs,
54 * NOISY means you are notified when an action occur plus you see the action
55 * in the display and the default actions still occurs
56 */
57 struct NoiseInfo {
58 const char *name;
59 int display; /* 0 = suppress display, 1 = don't */
60 int alert; /* 1 = "ON MSG hooked", 0 = shut up */
61 int suppress; /* 0 = don't suppress default action
62 * 1 = suppress default action
63 * -1 = Let on hook decide */
64 int value;
65 char identifier;
66 int custom;
67 };
68
69 struct NoiseInfo noise_info_templates[] = {
70 { "UNKNOWN", 0, 0, -1, 0, '?', 0 },
71 { "SILENT", 0, 0, 1, 1, '^', 0 },
72 { "QUIET", 0, 0, 0, 2, '-', 0 },
73 { "NORMAL", 0, 1, 0, 3, 0, 0 },
74 { "NOISY", 1, 1, 0, 4, '+', 0 },
75 { "SYSTEM", 1, 0, 1, 5, '%', 0 },
76 { NULL, 0, 0, 0, 0, 0, 0 }
77 };
78
79 #define HF_NORECURSE 0x0001
80
81 /* Hook: The structure of the entries of the hook functions lists */
82 typedef struct hook_stru
83 {
84 struct hook_stru *next;
85
86 int type; /* /on #TYPE sernum nick (arglist) stuff */
87 int sernum; /* /on #type SERNUM nick (arglist) stuff */
88 char * nick; /* /on #type sernum NICK (arglist) stuff */
89 ArgList *arglist; /* /on #type sernum nick (ARGLIST) stuff */
90 char * stuff; /* /on #type sernum nick (arglist) STUFF */
91
92 int not; /* /on #type sernum ^NICK */
93 int noisy; /* /on #[^-+]TYPE sernum nick (arglist) stuff */
94 int flexible; /* /on #type sernum 'NICK' (arglist) stuff */
95
96 int userial; /* Unique serial for this hook */
97 int skip; /* hook will be treated like it doesn't exist */
98 char * filename; /* Where it was loaded */
99 } Hook;
100
101 /*
102 * Current executing hook, yay!
103 * Silly name, but that can be fixed, can't it? :P
104 */
105 struct Current_hook
106 {
107 /* Which hook we're "under", if we are under a hook */
108 struct Current_hook *under;
109
110 /* Hook's userial */
111 int userial;
112
113 /* If set to 1, the chain for the current hook should halt */
114 int halt;
115
116 /* The hook's retval */
117 int retval;
118
119 /* The buffer (i.e. the arguments) of the hook */
120 char *buffer;
121
122 /* Set to 1 if buffer has been changed */
123 int buffer_changed;
124
125 /*
126 * User-supplied information.
127 */
128 char *user_supplied_info;
129 };
130
131 /* A list of all the hook functions available */
132 typedef struct Hookables
133 {
134 const char *name; /* The name of the hook type */
135 Hook *list; /* The list of events for type */
136 int params; /* Number of parameters expected */
137 int mark; /* Hook type is currently active */
138 unsigned flags; /* Anything else needed */
139 char * implied; /* Implied output if unhooked */
140 int implied_protect; /* Do not re-expand implied hook */
141 } Hookables;
142
143 Hookables hook_function_templates[] =
144 {
145 { "ACTION", NULL, 3, 0, 0, NULL, 0 },
146 { "CHANNEL_CLAIM", NULL, 2, 0, 0, NULL, 0 },
147 { "CHANNEL_LOST", NULL, 2, 0, 0, NULL, 0 },
148 { "CHANNEL_NICK", NULL, 3, 0, 0, NULL, 0 },
149 { "CHANNEL_SIGNOFF", NULL, 3, 0, 0, NULL, 0 },
150 { "CHANNEL_SYNC", NULL, 3, 0, 0, NULL, 0 },
151 { "CONNECT", NULL, 2, 0, 0, NULL, 0 },
152 { "CTCP", NULL, 4, 0, 0, NULL, 0 },
153 { "CTCP_REPLY", NULL, 4, 0, 0, NULL, 0 },
154 { "CTCP_REQUEST", NULL, 4, 0, 0, NULL, 0 },
155 { "DCC_ACTIVITY", NULL, 1, 0, 0, NULL, 0 },
156 { "DCC_CHAT", NULL, 2, 0, 0, NULL, 0 },
157 { "DCC_CONNECT", NULL, 2, 0, 0, NULL, 0 },
158 { "DCC_LIST", NULL, 8, 0, 0, NULL, 0 },
159 { "DCC_LOST", NULL, 2, 0, 0, NULL, 0 },
160 { "DCC_OFFER", NULL, 2, 0, 0, NULL, 0 },
161 { "DCC_RAW", NULL, 3, 0, 0, NULL, 0 },
162 { "DCC_REQUEST", NULL, 4, 0, 0, NULL, 0 },
163 { "DISCONNECT", NULL, 1, 0, 0, NULL, 0 },
164 { "ENCRYPTED_NOTICE", NULL, 3, 0, 0, NULL, 0 },
165 { "ENCRYPTED_PRIVMSG", NULL, 3, 0, 0, NULL, 0 },
166 { "ERROR", NULL, 1, 0, 0, NULL, 0 },
167 { "EXEC", NULL, 2, 0, 0, NULL, 0 },
168 { "EXEC_ERRORS", NULL, 2, 0, 0, NULL, 0 },
169 { "EXEC_EXIT", NULL, 3, 0, 0, NULL, 0 },
170 { "EXEC_PROMPT", NULL, 2, 0, 0, NULL, 0 },
171 { "EXIT", NULL, 1, 0, 0, NULL, 0 },
172 { "FLOOD", NULL, 5, 0, 0, NULL, 0 },
173 { "GENERAL_NOTICE", NULL, 3, 0, 0, NULL, 0 },
174 { "GENERAL_PRIVMSG", NULL, 3, 0, 0, NULL, 0 },
175 { "HELP", NULL, 2, 0, 0, NULL, 0 },
176 { "HOOK", NULL, 1, 0, 0, NULL, 0 },
177 { "IDLE", NULL, 1, 0, 0, NULL, 0 },
178 { "INPUT", NULL, 1, 0, HF_NORECURSE, NULL, 0 },
179 { "INVITE", NULL, 3, 0, 0, NULL, 0 },
180 { "JOIN", NULL, 4, 0, 0, NULL, 0 },
181 { "KEYBINDING", NULL, 3, 0, 0, NULL, 0 },
182 { "KICK", NULL, 3, 0, 0, NULL, 0 },
183 { "KILL", NULL, 5, 0, 0, NULL, 0 },
184 { "LIST", NULL, 3, 0, 0, NULL, 0 },
185 { "MAIL", NULL, 2, 0, 0, NULL, 0 },
186 { "MODE", NULL, 3, 0, 0, NULL, 0 },
187 { "MODE_STRIPPED", NULL, 3, 0, 0, NULL, 0 },
188 { "MSG", NULL, 2, 0, 0, NULL, 0 },
189 { "MSG_GROUP", NULL, 3, 0, 0, NULL, 0 },
190 { "NAMES", NULL, 2, 0, 0, NULL, 0 },
191 { "NEW_NICKNAME", NULL, 2, 0, HF_NORECURSE, NULL, 0 },
192 { "NICKNAME", NULL, 2, 0, 0, NULL, 0 },
193 { "NOTE", NULL, 3, 0, 0, NULL, 0 },
194 { "NOTICE", NULL, 2, 0, 0, NULL, 0 },
195 { "NOTIFY_SIGNOFF", NULL, 1, 0, 0, NULL, 0 },
196 { "NOTIFY_SIGNON", NULL, 2, 0, 0, NULL, 0 },
197 { "NUMERIC", NULL, 3, 0, 0, NULL, 0 },
198 { "ODD_SERVER_STUFF", NULL, 3, 0, 0, NULL, 0 },
199 { "OPERWALL", NULL, 2, 0, 0, NULL, 0 },
200 { "OPER_NOTICE", NULL, 2, 0, 0, NULL, 0 },
201 { "PART", NULL, 2, 0, 0, NULL, 0 },
202 { "PONG", NULL, 2, 0, 0, NULL, 0 },
203 { "PUBLIC", NULL, 3, 0, 0, NULL, 0 },
204 { "PUBLIC_MSG", NULL, 3, 0, 0, NULL, 0 },
205 { "PUBLIC_NOTICE", NULL, 3, 0, 0, NULL, 0 },
206 { "PUBLIC_OTHER", NULL, 3, 0, 0, NULL, 0 },
207 { "RAW_IRC", NULL, 1, 0, 0, NULL, 0 },
208 { "RAW_IRC_BYTES", NULL, 1, 0, 0, NULL, 0 },
209 { "RECONNECT_REQUIRED", NULL, 1, 0, 0, NULL, 0 },
210 { "REDIRECT", NULL, 2, 0, HF_NORECURSE, NULL, 0 },
211 { "SEND_ACTION", NULL, 2, 0, HF_NORECURSE, NULL, 0 },
212 { "SEND_CTCP", NULL, 3, 0, HF_NORECURSE, NULL, 0 },
213 { "SEND_DCC_CHAT", NULL, 2, 0, HF_NORECURSE, NULL, 0 },
214 { "SEND_EXEC", NULL, 2, 0, HF_NORECURSE, NULL, 0 },
215 { "SEND_MSG", NULL, 2, 0, HF_NORECURSE, NULL, 0 },
216 { "SEND_NOTICE", NULL, 2, 0, HF_NORECURSE, NULL, 0 },
217 { "SEND_PUBLIC", NULL, 2, 0, HF_NORECURSE, NULL, 0 },
218 { "SEND_TO_SERVER", NULL, 3, 0, HF_NORECURSE, NULL, 0 },
219 { "SERVER_ESTABLISHED", NULL, 2, 0, 0, NULL, 0 },
220 { "SERVER_LOST", NULL, 2, 0, 0, NULL, 0 },
221 { "SERVER_NOTICE", NULL, 1, 0, 0, NULL, 0 },
222 { "SERVER_SSL_EVAL", NULL, 6, 0, 0, NULL, 0 },
223 { "SERVER_STATE", NULL, 3, 0, 0, NULL, 0 },
224 { "SERVER_STATUS", NULL, 3, 0, 0, NULL, 0 },
225 { "SET", NULL, 2, 0, 0, NULL, 0 },
226 { "SIGNAL", NULL, 1, 0, 0, NULL, 0 },
227 { "SIGNOFF", NULL, 1, 0, 0, NULL, 0 },
228 { "SILENCE", NULL, 2, 0, 0, NULL, 0 },
229 { "SSL_SERVER_CERT", NULL, 9, 0, 0, NULL, 0 },
230 { "STATUS_UPDATE", NULL, 3, 0, 0, NULL, 0 },
231 { "SWITCH_CHANNELS", NULL, 3, 0, 0, NULL, 0 },
232 { "SWITCH_QUERY", NULL, 3, 0, 0, NULL, 0 },
233 { "SWITCH_WINDOWS", NULL, 4, 0, 0, NULL, 0 },
234 { "TIMER", NULL, 1, 0, 0, NULL, 0 },
235 { "TOPIC", NULL, 2, 0, 0, NULL, 0 },
236 { "UNKNOWN_COMMAND", NULL, 2, 0, HF_NORECURSE, NULL, 0},
237 { "UNKNOWN_SET", NULL, 2, 0, HF_NORECURSE, NULL, 0},
238 { "UNLOAD", NULL, 1, 0, 0, NULL, 0 },
239 { "WALL", NULL, 2, 0, 0, NULL, 0 },
240 { "WALLOP", NULL, 3, 0, 0, NULL, 0 },
241 { "WHO", NULL, 6, 0, 0, NULL, 0 },
242 { "WINDOW", NULL, 2, 0, HF_NORECURSE, NULL, 0 },
243 { "WINDOW_COMMAND", NULL, 1, 0, 0, NULL, 0 },
244 { "WINDOW_CREATE", NULL, 1, 0, 0, NULL, 0 },
245 { "WINDOW_BEFOREKILL", NULL, 1, 0, 0, NULL, 0 },
246 { "WINDOW_KILL", NULL, 2, 0, 0, NULL, 0 },
247 { "WINDOW_NOTIFIED", NULL, 2, 0, HF_NORECURSE, NULL, 0 },
248 { "WINDOW_SERVER", NULL, 3, 0, 0, NULL, 0 },
249 { "YELL", NULL, 1, 0, 0, NULL, 0 },
250 };
251
252 static Hookables *hook_functions = NULL;
253 static int hook_functions_initialized = 0;
254
255 static Hook **hooklist = NULL;
256 static int hooklist_size = 0;
257 static int last_created_hook = -2;
258 static struct Current_hook *current_hook = NULL;
259 /*
260 * If deny_all_hooks is set to 1, no action is taken for any hook.
261 */
262 int deny_all_hooks = 0;
263
264 static struct NoiseInfo ** noise_info = NULL;
265 static int noise_level_num = 0;
266 static int default_noise;
267 static const char * current_implied_on_hook = NULL;
268
269 extern char * function_cparse (char *);
270 static void add_to_list (Hook **list, Hook *item);
271 static Hook * remove_from_list (Hook **list, char *item, int sernum);
272
initialize_hook_functions(void)273 static void initialize_hook_functions (void)
274 {
275 int i, b;
276 char * p;
277
278 hook_functions = malloc(NUMBER_OF_LISTS * sizeof(Hookables));
279 p = malloc(4050);
280
281 for (i = 0; i < FIRST_NAMED_HOOK; i++, p += 4)
282 {
283 snprintf(p, 4, "%03d", i);
284 hook_functions[i].name = p;
285 hook_functions[i].list = NULL;
286 hook_functions[i].params = 1;
287 hook_functions[i].mark = 0;
288 hook_functions[i].flags = 0;
289 hook_functions[i].implied = NULL;
290 hook_functions[i].implied_protect = 0;
291 }
292
293 for (b = 0, i = FIRST_NAMED_HOOK; i < NUMBER_OF_LISTS; b++, i++)
294 {
295 hook_functions[i].name = hook_function_templates[b].name;
296 hook_functions[i].list = hook_function_templates[b].list;
297 hook_functions[i].params = hook_function_templates[b].params;
298 hook_functions[i].mark = hook_function_templates[b].mark;
299 hook_functions[i].flags = hook_function_templates[b].flags;
300 hook_functions[i].implied = NULL;
301 hook_functions[i].implied_protect = 0;
302 }
303
304 if (noise_info == NULL)
305 {
306 for (b = 0; noise_info_templates[b].name != NULL; b++)
307 {
308 noise_level_num++;
309 if (noise_level_num == 1)
310 noise_info = new_malloc(sizeof(struct NoiseInfo *));
311 else
312 RESIZE(noise_info, struct NoiseInfo *, noise_level_num);
313
314 noise_info[b] = new_malloc(sizeof(struct NoiseInfo));
315 noise_info[b]->name = noise_info_templates[b].name;
316 noise_info[b]->display = noise_info_templates[b].display;
317 noise_info[b]->alert = noise_info_templates[b].alert;
318 noise_info[b]->suppress = noise_info_templates[b].suppress;
319 noise_info[b]->value = noise_info_templates[b].value;
320 noise_info[b]->identifier = noise_info_templates[b].identifier;
321 noise_info[b]->custom = 0;
322 if (!strcmp(noise_info[b]->name, "NORMAL"))
323 default_noise = b;
324 }
325 }
326 hook_functions_initialized = 1;
327 }
328
329 /*
330 * Add a hook to the hooklist
331 * Returns -1 on error, or size of list if successful.
332 */
inc_hooklist(int size)333 static int inc_hooklist (int size)
334 {
335 int newsize, n;
336 if (size < 1)
337 return -1;
338 newsize = hooklist_size + size;
339 if (hooklist_size == 0)
340 hooklist = new_malloc(sizeof(Hook) * newsize);
341 else
342 RESIZE(hooklist, Hook, newsize);
343 for (n = hooklist_size; n < newsize; n++)
344 hooklist[n] = NULL;
345 hooklist_size = newsize;
346 return hooklist_size;
347 }
348
349 /*
350 * Removes n NULL-pointers from the end of hooklist.
351 * Returns -1 on error, and size of list if successful
352 */
dec_hooklist(int n)353 static int dec_hooklist (int n)
354 {
355 int size, newsize;
356 if (n < 1)
357 {
358 size = hooklist_size -1;
359 while (hooklist[size] == NULL)
360 size--;
361 n = hooklist_size - size - 1;
362 }
363 size = hooklist_size;
364 newsize = size - n;
365 for (n = newsize; n < hooklist_size; n++)
366 if (hooklist[n] != NULL)
367 return -1;
368 RESIZE(hooklist, Hook, newsize);
369 hooklist_size = newsize;
370 return hooklist_size;
371 }
372
373 /*
374 * Will return the next empty slot in the hooklist
375 */
next_empty_hookslot(void)376 static int next_empty_hookslot (void)
377 {
378 int n;
379 for (n = 0; n < hooklist_size; n++)
380 if (hooklist[n] == NULL)
381 break;
382 return n;
383 }
384
385 /*
386 * find_hook: returns the numerical value for a specified hook name
387 */
find_hook(char * name,int * first,int quiet)388 static int find_hook (char *name, int *first, int quiet)
389 {
390 int which = INVALID_HOOKNUM, i, cnt;
391 size_t len;
392
393 if (first)
394 *first = -1;
395
396 if (!name || !(len = strlen(name)))
397 {
398 if (!quiet)
399 say("You must specify an event type!");
400 return INVALID_HOOKNUM;
401 }
402
403 upper(name);
404
405 for (cnt = 0, i = FIRST_NAMED_HOOK; i < NUMBER_OF_LISTS; i++)
406 {
407 if (!strncmp(name, hook_functions[i].name, len))
408 {
409 if (first && *first == -1)
410 *first = i;
411
412 if (strlen(hook_functions[i].name) == len)
413 {
414 cnt = 1;
415 which = i;
416 break;
417 }
418 else
419 {
420 cnt++;
421 which = i;
422 }
423 }
424 else if (cnt)
425 break;
426 }
427
428 if (cnt == 0)
429 {
430 if (is_number(name))
431 {
432 which = atol(name);
433
434 if ((which < 0) || (which >= FIRST_NAMED_HOOK))
435 {
436 if (!quiet)
437 say("Numerics must be between 001 and %3d", FIRST_NAMED_HOOK - 1);
438 return INVALID_HOOKNUM;
439 }
440 }
441 else
442 {
443 if (!quiet)
444 say("No such ON function: %s", name);
445 return INVALID_HOOKNUM;
446 }
447 }
448 else if (cnt > 1)
449 {
450 if (!quiet)
451 say("Ambiguous ON function: %s", name);
452 return INVALID_HOOKNUM;
453 }
454
455 return which;
456 }
457
458
459
460
461 /* * * * * ADDING A HOOK * * * * * */
462 /*
463 * add_hook: Given an index into the hook_functions array, this adds a new
464 * entry to the list as specified by the rest of the parameters. The new
465 * entry is added in alphabetical order (by nick).
466 */
add_hook(int which,char * nick,ArgList * arglist,char * stuff,int noisy,int not,int sernum,int flexible)467 static int add_hook (int which, char *nick, ArgList *arglist, char *stuff, int noisy, int not, int sernum, int flexible)
468 {
469 Hook *new_h;
470
471 if (!(new_h = remove_from_list(&hook_functions[which].list, nick, sernum)))
472 {
473 new_h = (Hook *)new_malloc(sizeof(Hook));
474 new_h->nick = NULL;
475 new_h->stuff = NULL;
476 new_h->filename = NULL;
477
478 if ((new_h->userial = next_empty_hookslot()) == hooklist_size)
479 inc_hooklist(3);
480 }
481
482 new_h->type = which;
483 malloc_strcpy(&new_h->nick, nick);
484 malloc_strcpy(&new_h->stuff, stuff);
485 new_h->noisy = noisy;
486 new_h->not = not;
487 new_h->sernum = sernum;
488 new_h->flexible = flexible;
489 new_h->skip = 0;
490 new_h->arglist = arglist;
491 if (current_package())
492 malloc_strcpy(&new_h->filename, current_package());
493 new_h->next = NULL;
494
495 upper(new_h->nick);
496
497 hooklist[new_h->userial] = new_h;
498 add_to_list(&hook_functions[which].list, new_h);
499
500 last_created_hook = new_h->userial;
501
502 return new_h->userial;
503 }
504
505
506
507
508 /* * * * * * REMOVING A HOOK * * * * * * * */
remove_hook(int which,char * nick,int sernum,int quiet)509 static void remove_hook (int which, char *nick, int sernum, int quiet)
510 {
511 Hook *tmp,
512 *next;
513 Hook *prev = NULL,
514 *top = NULL;
515
516 if (nick)
517 {
518 if ((tmp = remove_from_list(&hook_functions[which].list, nick, sernum)))
519 {
520 if (!quiet)
521 say("%c%s%c removed from %s list",
522 (tmp->flexible?'\'':'"'), nick,
523 (tmp->flexible?'\'':'"'),
524 hook_functions[which].name);
525
526 new_free(&(tmp->nick));
527 new_free(&(tmp->stuff));
528 new_free(&(tmp->filename));
529 if (tmp->arglist != NULL)
530 destroy_arglist(&(tmp->arglist));
531
532
533 hooklist[tmp->userial] = NULL;
534 if (tmp->userial == hooklist_size -1)
535 {
536 dec_hooklist(0);
537 }
538 tmp->next = NULL;
539 new_free((char **)&tmp); /* XXX why? */
540 }
541 else if (!quiet)
542 say("\"%s\" is not on the %s list", nick,
543 hook_functions[which].name);
544 return;
545 }
546
547 top = hook_functions[which].list;
548 for (tmp = top; tmp; tmp = next)
549 {
550 next = tmp->next;
551
552 /*
553 * If given a non-zero sernum, then we clean out
554 * only those hooks that are at that level.
555 */
556 if (sernum && tmp->sernum != sernum)
557 {
558 prev = tmp;
559 continue;
560 }
561
562 if (prev)
563 prev->next = tmp->next;
564 else
565 top = tmp->next;
566 tmp->not = 1;
567 new_free(&(tmp->nick));
568 new_free(&(tmp->stuff));
569 new_free(&(tmp->filename));
570 tmp->next = NULL;
571
572 new_free((char **)&tmp);
573 }
574 hook_functions[which].list = top;
575 if (!quiet)
576 {
577 if (sernum)
578 say("The %s <%d> list is empty", hook_functions[which].name, sernum);
579 else
580 say("The %s list is empty", hook_functions[which].name);
581 }
582 }
583
584 /* Used to bulk-erase all of the currently scheduled ONs */
flush_on_hooks(void)585 void flush_on_hooks (void)
586 {
587 int x;
588 int old_display = window_display;
589
590 window_display = 0;
591 for (x = 0; x < NUMBER_OF_LISTS; x++)
592 remove_hook(x, NULL, 0, 1);
593 window_display = old_display;
594
595 new_free(&hooklist);
596 hooklist_size = 0;
597 last_created_hook = -2;
598
599 }
600
unload_on_hooks(char * filename)601 void unload_on_hooks (char *filename)
602 {
603 int x;
604 Hook *list, *next;
605
606 int old_display = window_display;
607 window_display = 0;
608
609 for (x = 0; x < NUMBER_OF_LISTS; x++)
610 {
611 for (list = hook_functions[x].list; list; list = next)
612 {
613 next = list->next;
614 if (list->filename && !strcmp(list->filename, filename))
615 remove_hook(x, list->nick, list->sernum, 1);
616 }
617 }
618
619 window_display = old_display;
620 }
621
622
623 /* * * * * * SHOWING A HOOK * * * * * * */
624 /* show_hook shows a single hook */
show_hook(Hook * list,const char * name)625 static void show_hook (Hook *list, const char *name)
626 {
627 char *arglist;
628
629 if ((arglist = print_arglist(list->arglist)))
630 {
631 say ("[%s] On %s from %c%s%c (%s) do %s [%s%s] <%d/#%d>",
632 empty(list->filename) ? "*" : list->filename,
633 name,
634 (list->flexible ? '\'' : '"'),
635 list->nick,
636 (list->flexible ? '\'' : '"'),
637 arglist,
638 (list->not ? "nothing" : list->stuff),
639 noise_info[list->noisy]->name,
640 list->skip ? "/DISABLED" : "",
641 list->sernum, list->userial);
642 new_free(&arglist);
643 }
644 else
645 say("[%s] On %s from %c%s%c do %s [%s%s] <%d/#%d>",
646 empty(list->filename) ? "*" : list->filename,
647 name,
648 (list->flexible ? '\'' : '"'), list->nick,
649 (list->flexible ? '\'' : '"'),
650 (list->not ? "nothing" : list->stuff),
651 noise_info[list->noisy]->name,
652 list->skip ? "/DISABLED" : "",
653 list->sernum, list->userial);
654 }
655
656 /*
657 * show_list: Displays the contents of the list specified by the index into
658 * the hook_functions array. This function returns the number of entries in
659 * the list displayed
660 */
show_list(int which)661 static int show_list (int which)
662 {
663 Hook *list;
664 int cnt = 0;
665
666 /* Less garbage when issueing /on without args. (lynx) */
667 for (list = hook_functions[which].list; list; list = list->next, cnt++)
668 show_hook(list, hook_functions[which].name);
669 return (cnt);
670 }
671
show_all_numerics(int numeric)672 static int show_all_numerics (int numeric)
673 {
674 int cnt = 0;
675 int tot = 0;
676
677 for (cnt = 0; cnt < FIRST_NAMED_HOOK; cnt++)
678 tot += show_list(cnt);
679 return tot;
680 }
681
682
683
684 /* * * * * * * * EXECUTING A HOOK * * * * * * */
685 #define NO_ACTION_TAKEN -1
686 #define SUPPRESS_DEFAULT 0
687 #define DONT_SUPPRESS_DEFAULT 1
688 #define RESULT_PENDING 2
689 static int do_hook_internal (int which, char **result, const char *format, va_list args);
690
691 /*
692 * do_hook: This is what gets called whenever a MSG, INVITES, WALL, (you get
693 * the idea) occurs. The nick is looked up in the appropriate list. If a
694 * match is found, the stuff field from that entry in the list is treated as
695 * if it were a command. First it gets expanded as though it were an alias
696 * (with the args parameter used as the arguments to the alias). After it
697 * gets expanded, it gets parsed as a command. This will return as its value
698 * the value of the noisy field of the found entry, or -1 if not found.
699 */
700 /* huh-huh.. this sucks.. im going to re-write it so that it works */
do_hook(int which,const char * format,...)701 int do_hook (int which, const char *format, ...)
702 {
703 char * result = NULL;
704 int retval;
705 va_list args;
706
707 va_start(args, format);
708 retval = do_hook_internal(which, &result, format, args);
709 new_free(&result);
710 va_end(args);
711 return retval;
712 }
713
do_hook_with_result(int which,char ** result,const char * format,...)714 int do_hook_with_result (int which, char **result, const char *format, ...)
715 {
716 int retval;
717 va_list args;
718
719 va_start(args, format);
720 retval = do_hook_internal(which, result, format, args);
721 va_end(args);
722 return retval;
723 }
724
do_hook_internal(int which,char ** result,const char * format,va_list args)725 static int do_hook_internal (int which, char **result, const char *format, va_list args)
726 {
727 Hook *tmp;
728 const char *name = (char *) 0;
729 int retval;
730 char * buffer = NULL;
731 unsigned display = window_display;
732 char * stuff_copy;
733 int noise, old;
734 char quote;
735 int serial_number;
736 struct Current_hook *hook;
737 Hookables * h;
738
739 *result = NULL;
740
741 if (!hook_functions_initialized)
742 initialize_hook_functions();
743 h = &hook_functions[which];
744
745 /*
746 * Press the buffer using the specified format string and args
747 * We have to do this first because even if there is no /on, the
748 * caller might still want to know what the result of $* is.
749 */
750 if (!format)
751 panic(1, "do_hook: format is NULL (hook type %d)", which);
752
753 malloc_vsprintf(&buffer, format, args);
754
755
756 /*
757 * Decide whether to post this event. Events are suppressed if:
758 * 1) $hookctl(DENY_ALL_HOOKS 1) has been turned on
759 * 2) There are no /on's and no implied hooks
760 * 3) The /on has recursed and that is forbidden.
761 */
762 if (deny_all_hooks ||
763 (!h->list && !h->implied) ||
764 (h->mark && h->flags & HF_NORECURSE))
765 {
766 retval = NO_ACTION_TAKEN;
767 *result = buffer;
768 return retval;
769 }
770
771 /*
772 * If there are no /on's, but there is an implied hook, skip
773 * right to the implied hook.
774 */
775 if (!h->list && h->implied)
776 {
777 retval = NO_ACTION_TAKEN;
778 *result = buffer;
779 goto implied_hook;
780 }
781
782 /*
783 * Set current_hook
784 */
785 hook = new_malloc(sizeof(struct Current_hook));
786 hook->userial = -1;
787 hook->halt = 0;
788 hook->under = current_hook;
789 hook->retval = DONT_SUPPRESS_DEFAULT;
790 hook->buffer = buffer;
791 hook->buffer_changed = 0;
792 hook->user_supplied_info = NULL;
793 current_hook = hook;
794
795 /*
796 * Mark the event as being executed. This is used to suppress
797 * unwanted recursion in some /on's.
798 */
799 if (which >= 0)
800 h->mark++;
801
802 serial_number = INT_MIN;
803 for (;!hook->halt;serial_number++)
804 {
805 for (tmp = h->list; !hook->halt && tmp; tmp = tmp->next)
806 {
807 Hook *besthook = NULL;
808 ArgList *tmp_arglist;
809 char *buffer_copy;
810 int bestmatch = 0;
811 int currmatch;
812
813 if (tmp->sernum < serial_number)
814 continue;
815
816 if (tmp->sernum > serial_number)
817 serial_number = tmp->sernum;
818
819 for (;
820 !hook->halt && tmp && tmp->sernum == serial_number && !tmp->skip;
821 tmp = tmp->next)
822 {
823 if (tmp->flexible)
824 {
825 /* XXX What about context? */
826 char *tmpnick;
827 tmpnick = expand_alias(tmp->nick, hook->buffer);
828 currmatch = wild_match(tmpnick, hook->buffer);
829 new_free(&tmpnick);
830 }
831 else
832 currmatch = wild_match(tmp->nick, hook->buffer);
833
834 if (currmatch > bestmatch)
835 {
836 besthook = tmp;
837 bestmatch = currmatch;
838 }
839 }
840
841 /* If nothing matched, then run the next serial number. */
842 if (!besthook)
843 break;
844
845 /* Run the hook */
846 tmp = besthook;
847
848 /*
849 * If the winning event is a "excepting" event, then move
850 * on to the next serial number.
851 */
852 if (tmp->not)
853 break;
854
855 /* Copy off everything important from 'tmp'. */
856 noise = tmp->noisy;
857 if (!name)
858 name = LOCAL_COPY(h->name);
859 stuff_copy = LOCAL_COPY(tmp->stuff);
860 quote = tmp->flexible ? '\'' : '"';
861
862 hook->userial = tmp->userial;
863 tmp_arglist = clone_arglist(tmp->arglist);
864
865 /*
866 * YOU CAN'T TOUCH ``tmp'' AFTER THIS POINT!!!
867 */
868
869 /*
870 * Check to see if this hook is supposed to supress the
871 * default action for the event.
872 */
873 if (noise_info[noise]->suppress == 1 && serial_number == 0)
874 hook->retval = SUPPRESS_DEFAULT;
875 else if (noise_info[noise]->suppress == -1 && serial_number == 0)
876 hook->retval = RESULT_PENDING;
877
878 /*
879 * If this is a NORMAL or NOISY hook, then we tell the user
880 * that we're going to execute the hook.
881 */
882 if (noise_info[noise]->alert)
883 say("%s #%d activated by %c%s%c",
884 name, hook->userial, quote, hook->buffer, quote);
885
886 /*
887 * Save some information that may be reset in the
888 * execution, turn off the display if the user specified.
889 */
890 if (noise_info[noise]->display == 0)
891 window_display = 0;
892 else
893 window_display = 1;
894 old = system_exception;
895
896 buffer_copy = LOCAL_COPY(hook->buffer);
897
898 if (hook->retval == RESULT_PENDING)
899 {
900 char *xresult;
901
902 xresult = call_user_function(name, stuff_copy,
903 buffer_copy,
904 tmp_arglist);
905
906 if (xresult && atol(xresult))
907 hook->retval = SUPPRESS_DEFAULT;
908 else
909 hook->retval = DONT_SUPPRESS_DEFAULT;
910 if (tmp_arglist)
911 destroy_arglist(&tmp_arglist);
912 new_free(&xresult);
913 }
914 else
915 {
916 /*
917 * Ok. Go and run the code. It is imperitive to note
918 * that "tmp" may be deleted by the code executed here,
919 * so it is absolutely forbidden to reference "tmp"
920 * after this point.
921 */
922 call_user_command(name, stuff_copy,
923 buffer_copy, tmp_arglist);
924 if (tmp_arglist)
925 destroy_arglist(&tmp_arglist);
926 }
927
928 /*
929 * Clean up the stuff that may have been mangled by the
930 * execution.
931 */
932 system_exception = old;
933 window_display = display;
934
935 /* Move onto the next serial number. */
936 break;
937 }
938
939 /* If 'tmp' is null here, we've processed all of them. */
940 if (!tmp)
941 break;
942 }
943
944 /*
945 * Mark the event as not currently being done here.
946 */
947 if (which >= 0)
948 h->mark--;
949
950 /*
951 * Reset current_hook to its previous value.
952 */
953 retval = hook->retval;
954 *result = hook->buffer;
955 hook->buffer = NULL;
956 if (hook->user_supplied_info)
957 new_free(&hook->user_supplied_info);
958 current_hook = hook->under;
959 new_free(&hook);
960
961 /*
962 * And return the user-specified suppression level
963 */
964 if (retval == SUPPRESS_DEFAULT || !h->implied)
965 return retval;
966
967 implied_hook:
968 #ifdef IMPLIED_ON_HOOKS
969 do
970 {
971 char * func_call = NULL;
972 char * func_retval;
973 char my_buffer[BIG_BUFFER_SIZE * 10 + 1];
974 const char *old_current_implied_on_hook = current_implied_on_hook;
975
976 if (!h->implied)
977 break;
978
979 if (!format)
980 panic(1, "do_hook: format is NULL");
981
982 strlcpy(my_buffer, *result, sizeof(my_buffer));
983
984 if (which >= 0)
985 h->mark++;
986
987 current_implied_on_hook = h->name;
988 if (h->implied_protect)
989 {
990 malloc_sprintf(&func_call, "\"%s\" %s", h->implied, my_buffer);
991 func_retval = function_cparse(func_call);
992 }
993 else
994 {
995 malloc_sprintf(&func_call, "cparse(\"%s\" $*)", h->implied);
996 func_retval = call_function(func_call, my_buffer);
997 }
998 current_implied_on_hook = old_current_implied_on_hook;
999
1000 put_echo(func_retval);
1001
1002 new_free(&func_call);
1003 new_free(&func_retval);
1004 retval = SUPPRESS_DEFAULT;
1005
1006 if (which >= 0)
1007 h->mark--;
1008 }
1009 while (0);
1010 #endif
1011
1012 if (!result || !*result)
1013 panic(1, "do_hook: Didn't set result anywhere.");
1014 return retval;
1015 }
1016
1017 /*
1018 * shook: the SHOOK command -- this probably doesnt belong here,
1019 * and shook is probably a stupid name. It simply asserts a fake
1020 * hook event for a given type. Fraught with peril!
1021 */
BUILT_IN_COMMAND(shookcmd)1022 BUILT_IN_COMMAND(shookcmd)
1023 {
1024 int which;
1025 char *arg = next_arg(args, &args);
1026
1027 if ((which = find_hook(arg, NULL, 0)) == INVALID_HOOKNUM)
1028 return;
1029 else
1030 do_hook(which, "%s", args);
1031 }
1032
1033
1034 /* * * * * * SCHEDULING AN EVENT * * * * * * * */
1035 /*
1036 * The ON command:
1037 * Format: /ON [#][+-^]TYPE ['] [SERNUM] NICK ['] [{] STUFF [}]
1038 *
1039 * The "ON" command mainly takes three arguments. The first argument
1040 * is the "type" of callback that you want to schedule. This is either
1041 * a three digit number, of it is one of the strings so enumerated at the
1042 * top of this file in hook_list. The second argument is the "nick" or
1043 * "pattern" that is to be used to match against future events. If the
1044 * "nick" matches the text that is later passed to do_hook() with the given
1045 * "type", then the commands in "stuff" will be executed.
1046 *
1047 * If "nick" is enclosed in single quotes ('), then it is a "flexible"
1048 * pattern, and will be expanded before it is matched against the text
1049 * in do_hook. Otherwise, the string so specified is "static", and is
1050 * used as-is in do_hook().
1051 *
1052 * Within each type, there are at least 65,535 different "serial numbers",
1053 * (there actually are MAX_INT of them, but by convention, only 16 bit
1054 * serial numbers are used, from -32,768 to 32,767) which may be used to
1055 * schedule any number of events at the given serial number.
1056 *
1057 * Each time an assertion occurs for a given "type", at most one of the
1058 * scheduled events is executed for each of the distinct serial numbers that
1059 * are in use for that event. The event to be executed is the one at a
1060 * given serial number that "best" matches the text passed to do_hook().
1061 * While in theory, up to MAX_INT events could be executed for a given single
1062 * assertion, in practice, a hard limit of 2048 events per assertion is
1063 * enforced.
1064 *
1065 * The runtime behavior of the event being scheduled can be modified by
1066 * specifying a character at the beginning of the "type" argument. If you
1067 * want to schedule an event at a serial number, then the first character
1068 * must be a hash (#). The argument immediately FOLLOWING the "type"
1069 * argument, and immediately PRECEEDING the "nick" argument must be an
1070 * integer number, and is used for the serial number for this event.
1071 *
1072 * The "verbosity" of the event may also be modified by specifying at most
1073 * one of the following characters:
1074 * A caret (^) is the SILENT level, and indicates that the event is to
1075 * be executed with no output (window_display is turned off),
1076 * and the "default action" (whatever that is) for the event is
1077 * to be suppressed. The default action is actually only
1078 * suppressed if the SILENT level is specified for serial number
1079 * zero. This is the most common level used for overriding the
1080 * output of most /on's.
1081 * A minus (-) is the QUIET level, and is the same as the SILENT level,
1082 * except that the default action (whatever that is) is not to
1083 * be suppressed.
1084 * No character is the "normal" case, and is the same as the "minus"
1085 * level, with the addition that the client will inform you that
1086 * the event was executed. This is useful for debugging.
1087 * A plus (+) is the same as the "normal" (no character specified),
1088 * except that the output is not suppressed (window_display is
1089 * not changed.)
1090 */
BUILT_IN_COMMAND(oncmd)1091 BUILT_IN_COMMAND(oncmd)
1092 {
1093 char *func,
1094 *nick,
1095 *serial = NULL;
1096 int noisy;
1097 int not = 0,
1098 sernum = 0,
1099 rem = 0,
1100 supp = 0,
1101 which = INVALID_HOOKNUM;
1102 int flex = 0;
1103 int userial;
1104 char type;
1105 int first;
1106 ArgList *arglist = NULL;
1107 char *str;
1108 if (!hook_functions_initialized)
1109 initialize_hook_functions();
1110
1111 /*
1112 * Get the type of event to be scheduled
1113 */
1114 if ((func = next_arg(args, &args)) != NULL)
1115 {
1116 int v;
1117 /*
1118 * Check to see if this has a serial number.
1119 */
1120 if (*func == '#')
1121 {
1122 if (!(serial = next_arg(args, &args)))
1123 {
1124 say("No serial number specified");
1125 return;
1126 }
1127 sernum = atol(serial);
1128 func++;
1129 }
1130
1131 /*
1132 * Get the verbosity level, if any.
1133 */
1134 noisy = default_noise;
1135
1136 for (v = 0; v < noise_level_num; v++)
1137 {
1138 if (noise_info[v]->identifier != 0 &&
1139 noise_info[v]->identifier == *func)
1140 break;
1141 }
1142 if (v != noise_level_num)
1143 {
1144 noisy = noise_info[v]->value;
1145 func++;
1146 }
1147
1148 /*
1149 * Check to see if the event type is valid
1150 */
1151 if ((which = find_hook(func, &first, 0)) == INVALID_HOOKNUM)
1152 {
1153 /*
1154 * Ok. So either the user specified an invalid type
1155 * or they specified an ambiguous type. Either way,
1156 * we're not going to be going anywhere. So we have
1157 * free reign to mangle 'args' at this point.
1158 */
1159
1160 int len;
1161
1162 /*
1163 * If first is -1, then it was an unknown type.
1164 * An error has already been output, just return here
1165 */
1166 if (first == -1)
1167 return;
1168
1169 /*
1170 * Otherwise, its an ambiguous type. If they were
1171 * trying to register the hook, then they've already
1172 * gotten the error message, just return;
1173 */
1174 if (new_new_next_arg_count(args, &args, &type, 1))
1175 return;
1176
1177 /*
1178 * Ok. So they probably want a listing.
1179 */
1180 len = strlen(func);
1181 while (!my_strnicmp(func, hook_functions[first].name, len))
1182 {
1183 if (!show_list(first))
1184 say("The %s list is empty.",
1185 hook_functions[first].name);
1186 first++;
1187 }
1188
1189 return;
1190 }
1191
1192 /*
1193 * If sernum is 0 and serial is "+" or "-" get a serial
1194 * number for the event type in question
1195 */
1196 if (sernum == 0 && serial != NULL) {
1197 if (!strcmp(serial, "+"))
1198 sernum = hook_find_free_serial(1, 0, which);
1199 else if (!strcmp(serial, "-"))
1200 sernum = hook_find_free_serial(-1, 0, which);
1201 }
1202
1203 /*
1204 * Check to see if this is a removal event or if this
1205 * is a negated event.
1206 */
1207 switch (*args)
1208 {
1209 case '-':
1210 rem = 1;
1211 args++;
1212 break;
1213 case '^':
1214 supp = 1;
1215 args++;
1216 break;
1217 case '!':
1218 not = 1;
1219 args++;
1220 break;
1221 }
1222
1223 /*
1224 * Grab the "nick"
1225 */
1226 if ((nick = new_new_next_arg_count(args, &args, &type, 1)))
1227 {
1228 char *exp;
1229
1230 nick = malloc_strdup(nick);
1231
1232 /*
1233 * If nick is empty, something is very wrong.
1234 */
1235 if (!*nick)
1236 {
1237 say("No expression specified");
1238 new_free(&nick);
1239 return;
1240 }
1241
1242 /*
1243 * If we're doing a removal, do the deed.
1244 */
1245 if (rem)
1246 {
1247 remove_hook(which, nick, sernum, 0);
1248 new_free(&nick);
1249 return;
1250 }
1251
1252 /*
1253 * Take a note if its flexible or not.
1254 */
1255 if (type == '\'')
1256 flex = 1;
1257 else
1258 flex = 0;
1259
1260
1261 /*
1262 * If this is a suppressive event, then we dont want
1263 * to take any action for it.
1264 */
1265 if (supp)
1266 args = endstr(args);
1267
1268
1269 /*
1270 * Slurp up any whitespace after the nick
1271 */
1272 while (my_isspace(*args))
1273 args++;
1274
1275
1276 /* Ripping the alias.c-stuff mercyless */
1277 if (*args == '(')
1278 {
1279 ssize_t span;
1280 args++;
1281 if ((span = MatchingBracket(args, '(', ')')) < 0)
1282 {
1283 say("Unmatched lparen in HOOK <-> to be fixed");
1284 new_free(&nick);
1285 return;
1286 }
1287 else
1288 {
1289 exp = args + span;
1290 *exp++ = 0;
1291 while (*exp && my_isspace(*exp))
1292 exp++;
1293 while (*args && my_isspace(*args))
1294 args++;
1295 /* Arglist stuff */
1296 arglist = parse_arglist(args);
1297 args = exp;
1298 }
1299
1300 }
1301
1302 /*
1303 * Then slurp up the body ("text")
1304 */
1305 if (*args == '{') /* } */
1306 {
1307 if (!(exp = next_expr(&args, '{'))) /* } */
1308 {
1309 say("Unmatched brace in ON");
1310 new_free(&nick);
1311 destroy_arglist(&arglist);
1312 return;
1313 }
1314 }
1315 else
1316 exp = args;
1317
1318 /*
1319 * Schedule the event
1320 */
1321 userial = add_hook(which, nick, arglist, exp, noisy, not, sernum, flex);
1322
1323 /*
1324 * Tell the user that we're done.
1325 */
1326 str = print_arglist(arglist);
1327 if (str)
1328 {
1329 if (which < 0)
1330 say ("On %3.3u from %c%s%c (%s) do %s [%s] <%d/#%d>",
1331 -which, type, nick, type, str, (not ? "nothing" : exp),
1332 noise_info[noisy]->name, sernum, userial);
1333 else
1334 say ("On %s from %c%s%c (%s) do %s [%s] <%d/#%d>",
1335 hook_functions[which].name,
1336 type, nick, type, str,
1337 (not ? "nothing" : exp),
1338 noise_info[noisy]->name, sernum, userial);
1339 }
1340 else
1341 {
1342 if (which < 0)
1343 say("On %3.3u from %c%s%c do %s [%s] <%d/#%d>",
1344 -which, type, nick, type,
1345 (not ? "nothing" : exp),
1346 noise_info[noisy]->name, sernum, userial);
1347 else
1348 say("On %s from %c%s%c do %s [%s] <%d/#%d>",
1349 hook_functions[which].name,
1350 type, nick, type,
1351 (not ? "nothing" : exp),
1352 noise_info[noisy]->name, sernum, userial);
1353 }
1354 /*
1355 * Clean up after the nick
1356 */
1357 new_free(&nick);
1358 }
1359
1360 /*
1361 * No "nick" argument was specified. That means the user
1362 * either is deleting all of the events of a type, or it
1363 * wants to list all the events of a type.
1364 */
1365 else
1366 {
1367 /*
1368 * if its a removal, do the deed
1369 */
1370 if (rem)
1371 {
1372 remove_hook(which, (char *) 0, sernum, 0);
1373 return;
1374 }
1375
1376 /*
1377 * The help files say that an "/on 0" shows all
1378 * of the numeric ONs. Since the ACTION hook is
1379 * number 0, we have to check to see if the first
1380 * character of "func" is a zero or not. If it is,
1381 * we output all of the numeric functions.
1382 */
1383 if (!strcmp(func, "0"))
1384 {
1385 if (!show_all_numerics(0))
1386 say("All numeric ON lists are empty.");
1387 }
1388 else if (!show_list(which))
1389 say("The %s list is empty.",
1390 hook_functions[which].name);
1391 }
1392 }
1393
1394 /*
1395 * No "Type" argument was specified. That means the user wants to
1396 * list all of the ONs currently scheduled.
1397 */
1398 else
1399 {
1400 int total = 0;
1401
1402 say("ON listings:");
1403
1404 /*
1405 * Show the named events
1406 */
1407 for (which = FIRST_NAMED_HOOK; which < NUMBER_OF_LISTS; which++)
1408 total += show_list(which);
1409
1410 /*
1411 * Show the numeric events
1412 */
1413 for (which = 0; which < FIRST_NAMED_HOOK; which++)
1414 total += show_list(which);
1415
1416 if (!total)
1417 say("All ON lists are empty.");
1418 }
1419 }
1420
1421
1422 /* * * * * * * * * * STACKING A HOOK * * * * * * * * */
1423 typedef struct onstacklist
1424 {
1425 int which;
1426 Hook *list;
1427 struct onstacklist *next;
1428 } OnStack;
1429
1430 static OnStack * on_stack = NULL;
1431
do_stack_on(int type,char * args)1432 void do_stack_on (int type, char *args)
1433 {
1434 int which;
1435 Hook *list;
1436
1437 if (!on_stack && (type == STACK_POP || type == STACK_LIST))
1438 {
1439 say("ON stack is empty!");
1440 return;
1441 }
1442 if (!args || !*args)
1443 {
1444 say("Missing event type for STACK ON");
1445 return;
1446 }
1447
1448 if ((which = find_hook(args, NULL, 0)) == INVALID_HOOKNUM)
1449 return; /* Error message already outputted */
1450
1451 list = hook_functions[which].list;
1452
1453
1454 if (type == STACK_PUSH)
1455 {
1456 OnStack *new_os;
1457 new_os = (OnStack *) new_malloc(sizeof(OnStack));
1458 new_os->which = which;
1459 new_os->list = list;
1460 new_os->next = on_stack;
1461 on_stack = new_os;
1462 hook_functions[which].list = NULL;
1463 return;
1464 }
1465
1466 else if (type == STACK_POP)
1467 {
1468 OnStack *p, *tmp = (OnStack *) 0;
1469
1470 for (p = on_stack; p; tmp = p, p = p->next)
1471 {
1472 if (p->which == which)
1473 {
1474 if (p == on_stack)
1475 on_stack = p->next;
1476 else
1477 tmp->next = p->next;
1478 break;
1479 }
1480 }
1481 if (!p)
1482 {
1483 say("No %s on the stack", args);
1484 return;
1485 }
1486
1487 hook_functions[which].list = p->list;
1488
1489 new_free((char **)&p);
1490 return;
1491 }
1492
1493 else if (type == STACK_LIST)
1494 {
1495 int slevel = 0;
1496 OnStack *osptr;
1497
1498 for (osptr = on_stack; osptr; osptr = osptr->next)
1499 {
1500 if (osptr->which == which)
1501 {
1502 Hook *hptr;
1503
1504 slevel++;
1505 say("Level %d stack", slevel);
1506 for (hptr = osptr->list; hptr; hptr = hptr->next)
1507 show_hook(hptr, args);
1508 }
1509 }
1510
1511 if (!slevel)
1512 say("The STACK ON %s list is empty", args);
1513 return;
1514 }
1515 say("Unknown STACK ON type ??");
1516 }
1517
1518
1519
1520 /* List manips especially for on's. */
add_to_list(Hook ** list,Hook * item)1521 static void add_to_list (Hook **list, Hook *item)
1522 {
1523 Hook *tmp, *last = NULL;
1524
1525 for (tmp = *list; tmp; last = tmp, tmp = tmp->next)
1526 {
1527 if (tmp->sernum < item->sernum)
1528 continue;
1529 else if ((tmp->sernum == item->sernum) && (my_stricmp(tmp->nick, item->nick) < 0))
1530 continue;
1531 else
1532 break;
1533 }
1534
1535 if (last)
1536 {
1537 item->next = last->next;
1538 last->next = item;
1539 }
1540 else
1541 {
1542 item->next = *list;
1543 *list = item;
1544 }
1545 }
1546
1547
remove_from_list(Hook ** list,char * item,int sernum)1548 static Hook *remove_from_list (Hook **list, char *item, int sernum)
1549 {
1550 Hook *tmp, *last = NULL;
1551
1552 for (tmp = *list; tmp; last = tmp, tmp = tmp->next)
1553 {
1554 if (tmp->sernum == sernum && !my_stricmp(tmp->nick, item))
1555 {
1556 if (last)
1557 last->next = tmp->next;
1558 else
1559 *list = tmp->next;
1560 return tmp;
1561 }
1562 }
1563 return NULL;
1564 }
1565
1566
1567 /* this function traverses all the hooks for all the events in the system
1568 * trying to find a free serial number (one that is unused by any hook on
1569 * any event) in the direction given (either -1 or +1 for - and +) starting
1570 * at the given point. */
hook_find_free_serial(int dir,int from,int which)1571 int hook_find_free_serial(int dir, int from, int which) {
1572 int ser;
1573 Hook *hp;
1574 int wc;
1575
1576 if (from == 0)
1577 from = dir;
1578
1579 /* We iterate through the specified (or all) lists looking for a serial
1580 * number that isn't in use. If we make it through all of our loops
1581 * without breaking out of them, we have found an unused number */
1582 for (ser = from; (dir > 0 ? ser <= 32767 : ser >= -32767); ser += dir) {
1583 if (which != INVALID_HOOKNUM) {
1584 /* a list was specified */
1585 hp = hook_functions[which].list;
1586
1587 while (hp != NULL) {
1588 if (hp->sernum == ser)
1589 break;
1590 hp = hp->next;
1591 }
1592 if (hp == NULL)
1593 break;
1594 } else {
1595 /* no list was specified. start digging */
1596 hp = NULL;
1597 for (wc = 0; wc < NUMBER_OF_LISTS; wc++) {
1598 for (hp = hook_functions[wc].list; hp != NULL; hp = hp->next)
1599 if (hp->sernum == ser)
1600 break;
1601 if (hp != NULL)
1602 break;
1603 }
1604 if (hp == NULL)
1605 break; /* found an unused one */
1606 }
1607 }
1608
1609 return ser;
1610 }
1611
1612 /* get_noise_id() returns identifer for noise chr */
get_noise_id(char * chr)1613 static int get_noise_id (char *chr)
1614 {
1615 int n;
1616 n = atol(chr);
1617 if (n == 0 && chr[0] != '0')
1618 for (n = 0; n < noise_level_num; n++)
1619 if (!my_stricmp(chr, noise_info[n]->name))
1620 break;
1621 return n;
1622 }
1623
1624 enum
1625 {
1626 HOOKCTL_GET_HOOK_NOARG,
1627
1628 HOOKCTL_GET_HOOK,
1629
1630 HOOKCTL_GET_LIST,
1631 HOOKCTL_GET_NOISE,
1632 HOOKCTL_GET_NOISY,
1633 };
1634 enum
1635 {
1636 HOOKCTL_ADD = 1,
1637 HOOKCTL_ARGS,
1638 HOOKCTL_COUNT,
1639 HOOKCTL_CURRENT_IMPLIED_HOOK,
1640 HOOKCTL_DEFAULT_NOISE_LEVEL,
1641 HOOKCTL_DENY_ALL_HOOKS,
1642 HOOKCTL_EMPTY_SLOTS,
1643 HOOKCTL_EXECUTING_HOOKS,
1644 HOOKCTL_FIRST_NAMED_HOOK,
1645 HOOKCTL_GET,
1646 HOOKCTL_HALTCHAIN,
1647 HOOKCTL_HOOKLIST_SIZE,
1648 HOOKCTL_LAST_CREATED_HOOK,
1649 HOOKCTL_LIST,
1650 HOOKCTL_LOOKUP,
1651 HOOKCTL_MATCH,
1652 HOOKCTL_MATCHES,
1653 HOOKCTL_NOISE_LEVELS,
1654 HOOKCTL_NOISE_LEVEL_NUM,
1655 HOOKCTL_NUMBER_OF_LISTS,
1656 HOOKCTL_PACKAGE,
1657 HOOKCTL_POPULATED_LISTS,
1658 HOOKCTL_REMOVE,
1659 HOOKCTL_RETVAL,
1660 HOOKCTL_SERIAL,
1661 HOOKCTL_SET,
1662 HOOKCTL_USERINFO
1663 };
1664
1665 enum
1666 {
1667 HOOKCTL_GET_HOOK_ARGUMENT_LIST = 1,
1668 HOOKCTL_GET_HOOK_FLEXIBLE,
1669 HOOKCTL_GET_HOOK_NICK,
1670 HOOKCTL_GET_HOOK_NOT,
1671 HOOKCTL_GET_HOOK_NOISE,
1672 HOOKCTL_GET_HOOK_NOISY,
1673 HOOKCTL_GET_HOOK_PACKAGE,
1674 HOOKCTL_GET_HOOK_SERIAL,
1675 HOOKCTL_GET_HOOK_SKIP,
1676 HOOKCTL_GET_HOOK_STUFF,
1677 HOOKCTL_GET_HOOK_TYPE,
1678 HOOKCTL_GET_HOOK_STRING
1679 };
1680
1681 /*
1682 * $hookctl() arguments:
1683 * ADD <#!'[NOISETYPE]><list> [[#]<serial>] <nick> [(<argument list>)] <stuff>
1684 * Argument list not yet implemented for $hookctl()
1685 * ADD <#!'[NOISETYPE]><list> [[#]<serial>] <nick> <stuff>
1686 * - Creates a new hook. Returns hook id.
1687 * COUNT
1688 * - See COUNT/LIST
1689 * HALTCHAIN <recursive number>
1690 * - Will set the haltflag for eventchain. May halt the current chain,
1691 * or any chain currently being executed.
1692 * Returns 1 on success, 0 otherwise.
1693 * DEFAULT_NOISE_LEVEL
1694 * - returns the 'default noise level'. It is not currently possible
1695 * to change the current noise level, and probably never will be.
1696 * DENY_ALL_HOOKS <arguments>
1697 * - this sets the deny_all_hooks flag, or gets it's value. If set,
1698 * to anything non negative, all hooks will be "ignored", and the
1699 * default action of any event will be taken. Similar to a /DUMP ON
1700 * but doens't actually remove any hooks.
1701 * EMPTY_SLOTS
1702 * - will return a list of empty slots in the hook-list.
1703 * EXECUTING_HOOKS
1704 * - will return a list of the current executing hooks. This is a
1705 * 'recursive' list, listing the current hook first.
1706 * FIRST_NAMED_HOOK
1707 * - returns FIRST_NAMED_HOOK
1708 * HOOKLIST_SIZE
1709 * - will returns HOOKLIST_SIZE
1710 * LAST_CREATED_HOOK
1711 * - returns the value of LAST_CREATED_HOOK
1712 * LIST
1713 * - See COUNT/LIST
1714 * NOISE_LEVELS <pattern>
1715 * - Returns a list of 'noise-types'. If <pattern> is specified only
1716 * noise levels matching pattern will be returns.
1717 * NOISE_LEVEL_NUM
1718 * - Returns NOISE_LEVEL_NUM
1719 * NUMBER_OF_LISTS
1720 * - Returns NUBER_OF_LISTS
1721 * PACKAGE <package> [<list>]
1722 * - Returns a list of hooks of the given package. If <list> is
1723 * specified, it will return only hooks in list <list>
1724 * RETVAL <recursive number> [<new value>]
1725 * - If recursve number isn't specified, 0 (the current) is specified.
1726 * Will either return the value of retval for the given hook, or
1727 * set it.
1728 * SERIAL <serial> [<list>]
1729 * - Works exactly like PACKAGE.
1730 *
1731 * GET <type> <arg>
1732 * - See GET/SET
1733 * LOOKUP <list> <nick> [<serial>]
1734 * - Returns hook matching given parametres.
1735 * MATCH <list> <pattern>
1736 * - Returns a list of matching hooks.
1737 * REMOVE <hook id>
1738 * - Removes the hook with the given hook ID. Returns 1 on success,
1739 * 0 otherwise.
1740 * SET <type> <arg>
1741 * - See GET/SET
1742 *
1743 * * GET/SET usage
1744 * GET gettype <arguments>
1745 * - will return 'gettype'
1746 * SET gettype <arguments>
1747 * - will set 'gettype' or similar, and return 1 on success. Not all
1748 * 'gettypes' may be set, and not all gettypes will silently ignore
1749 * being set.
1750 *
1751 * It is very important to remember that GET won't ever SET anything(!!!)
1752 * Won't, and shouldn't.
1753 *
1754 * * GET/SET types:
1755 * HOOK <argument>
1756 * - More info on this under GET/SET HOOK
1757 * LIST <arguments>
1758 * - More info on this under GET/SET LIST
1759 * NOISE <argument>
1760 * NOISY <argument>
1761 * - More info on this under GET/SET NOISE/NOISY
1762 * MATCHES <argument>
1763 * - More info on this under GET/SET MATCHES
1764 *
1765 * * GET/SET HOOK usage:
1766 * GET HOOK <hook id> <prop> <arg>
1767 * SET HOOK <hook id> <prop> <arg>
1768 *
1769 * <prop> may be one of the following:
1770 * ARGUMENT_LIST
1771 * - Returns or sets the argument list, if SET and <arg> is empty,
1772 * it will be set to NULL, and therefore not used.
1773 * FLEXIBLE
1774 * - Returns or sets the value of flexible
1775 * NICK
1776 * - Sets or gets the hook's nick. The position of the hook will
1777 * be changed if needed, and it is not possible to change this
1778 * to a "crashing nick"
1779 * NOT
1780 * - Sets or gets the value of NOT.
1781 * NOISE
1782 * NOISY
1783 * - Sets or returns the value of noisy.
1784 * PACKAGE
1785 * - Returns or sets the hook's packagename
1786 * SERIAL
1787 * - Returns or sets the serial for the hook. The hook's position
1788 * in the list will be changed if necesarry, and it is not
1789 * possible to set the serial to a crashing serial.
1790 * SKIP
1791 * - Returns or sets the value of skip.
1792 * STUFF
1793 * - Returns or sets the value of stuff.
1794 * TYPE
1795 * - Returns or sets the type.
1796 *
1797 * * GET/SET LIST usage:
1798 * GET LIST <listname> <prop>
1799 * SET LIST <listname> <prop> - not functional
1800 *
1801 * <prop> may be one of the following:
1802 * COUNT
1803 * - Returns count of hooks
1804 * FLAGS
1805 * - Returns flags
1806 * MARK
1807 * - Returns mark
1808 * NAME
1809 * - Returns name
1810 * PARAMETERS
1811 * PARAMS
1812 * - Returns value of params
1813 *
1814 *
1815 *
1816 * * GET/SET NOISE/NOISY usage:
1817 * GET NOISE <noisename> <prop>
1818 * SET NOISE <noisename> <prop> - not functional
1819 *
1820 * <prop> may be one of the following:
1821 * ALERT
1822 * - returns value of alert.
1823 * CUSTOM
1824 * - returns value of custom.
1825 * DISPLAY
1826 * - returns value of display.
1827 * IDENTIFIER
1828 * - returns value of identifier.
1829 * NAME
1830 * - returns name.
1831 * SUPPRESS
1832 * - returns value of suppress.
1833 * VALUE
1834 * - returns value of value. d'oh!
1835 *
1836 * * GET/SET MATCHES:
1837 * - This function is not ready yet, and will currently RETURN_NULL.
1838 *
1839 * * COUNT/LIST usage:
1840 * COUNT / LIST work doing the same, the only difference is that
1841 * COUNT will return the count of lists/hooks, while list will return
1842 * a list
1843 *
1844 * The following options are permitted:
1845 *
1846 * LISTS <pattern>
1847 * - Will either return all lists available, or only the
1848 * matching ones.
1849 * POPULATED_LISTS <pattern>
1850 * - Works _just_ like LISTS, but will only return "populated"
1851 * lists
1852 * HOOKS <pattern>
1853 * - Will either return all the hooks on the system, or all
1854 * the hooks in the matching lists
1855
1856 */
1857
hookctl(char * input)1858 char *hookctl (char *input)
1859 {
1860 /* ALL the variables are due for a clean up */
1861 int go = 0;
1862 int hooknum = INVALID_HOOKNUM;
1863 int action;
1864 int prop;
1865 int userial;
1866 int tmp_int, tmp_int2;
1867 int set;
1868 int ser;
1869 int is_serial = 0;
1870 int set_noisy = default_noise;
1871 int set_serial = 0;
1872 int set_not = 0;
1873 int serial = 0;
1874 int set_flex = 0;
1875 int bestmatch = 0;
1876 int currmatch;
1877 int sernum;
1878 int halt = 0;
1879 int id;
1880 size_t retlen;
1881 char *nam;
1882 char *str;
1883 char *hookname;
1884 char *name;
1885 char *ret = NULL;
1886 char *tmp;
1887 char *nick;
1888 char *buffer;
1889
1890 struct Current_hook *curhook;
1891
1892 Hookables *hooks = NULL;
1893 Hook *hook = NULL;
1894 Hook *tmp_hook;
1895
1896 ArgList *tmp_arglist = NULL;
1897
1898 if (!hook_functions_initialized)
1899 initialize_hook_functions();
1900
1901 if (!input || !*input)
1902 RETURN_EMPTY;
1903
1904 GET_FUNC_ARG(str, input);
1905 go = vmy_strnicmp (strlen(str), str,
1906 "ADD",
1907 "ARGS",
1908 "COUNT",
1909 "CURRENT_IMPLIED_HOOK",
1910 "DEFAULT_NOISE_LEVEL",
1911 "DENY_ALL_HOOKS",
1912 "EMPTY_SLOTS",
1913 "EXECUTING_HOOKS",
1914 "FIRST_NAMED_HOOK",
1915 "GET",
1916 "HALTCHAIN",
1917 "HOOKLIST_SIZE",
1918 "LAST_CREATED_HOOK",
1919 "LIST",
1920 "LOOKUP",
1921 "MATCH",
1922 "MATCHES",
1923 "NOISE_LEVELS",
1924 "NOISE_LEVEL_NUM",
1925 "NUMBER_OF_LISTS",
1926 "POPULATED_LISTS",
1927 "PACKAGE",
1928 "REMOVE",
1929 "RETVAL",
1930 "SERIAL",
1931 "SET",
1932 "USERINFO",
1933 NULL);
1934
1935 switch (go)
1936 {
1937
1938 /* go-switch */
1939 case 0:
1940 RETURN_EMPTY;
1941 break;
1942
1943 /* go-switch */
1944 case HOOKCTL_LOOKUP:
1945 ser = 0;
1946 if (!input || !*input)
1947 RETURN_EMPTY;
1948 GET_FUNC_ARG(hookname, input);
1949 if ((hooknum = find_hook(hookname, NULL, 1)) == INVALID_HOOKNUM)
1950 RETURN_EMPTY;
1951 if (!input || !*input)
1952 RETURN_EMPTY;
1953 GET_FUNC_ARG(nick, input);
1954 if (input && *input)
1955 GET_INT_ARG(ser, input);
1956 for (
1957 hook = hook_functions[hooknum].list;
1958 hook != NULL;
1959 hook = hook->next)
1960 {
1961 if (hook->sernum != ser || my_stricmp(nick, hook->nick))
1962 continue;
1963 RETURN_INT(hook->userial);
1964 }
1965 RETURN_INT(-1);
1966 break;
1967
1968 /* go-switch */
1969 case HOOKCTL_DEFAULT_NOISE_LEVEL:
1970 RETURN_STR(noise_info[default_noise]->name);
1971 break;
1972
1973 /* go-switch */
1974 case HOOKCTL_FIRST_NAMED_HOOK:
1975 RETURN_INT(FIRST_NAMED_HOOK);
1976 break;
1977
1978 /* go-switch */
1979 case HOOKCTL_HOOKLIST_SIZE:
1980 RETURN_INT(hooklist_size);
1981 break;
1982
1983 /* go-switch */
1984 case HOOKCTL_LAST_CREATED_HOOK:
1985 RETURN_INT(last_created_hook);
1986 break;
1987
1988 /* go-switch */
1989 case HOOKCTL_NOISE_LEVEL_NUM:
1990 RETURN_INT(noise_level_num);
1991 break;
1992
1993 /* go-switch */
1994 case HOOKCTL_NUMBER_OF_LISTS:
1995 RETURN_INT(NUMBER_OF_LISTS);
1996 break;
1997
1998 case HOOKCTL_CURRENT_IMPLIED_HOOK:
1999 RETURN_STR(current_implied_on_hook);
2000 break;
2001
2002 /* go-switch */
2003 case HOOKCTL_RETVAL:
2004 if (!current_hook)
2005 RETURN_EMPTY;
2006 if (!input || !*input)
2007 RETURN_INT(current_hook->retval);
2008 else
2009 {
2010 GET_FUNC_ARG(str, input);
2011 if (!isdigit(str[0]) && str[0] != '-')
2012 tmp_int = -2 + vmy_strnicmp(strlen(str), str,
2013 "NO_ACTION_TAKEN", "SUPPRESS_DEFAULT",
2014 "DONT_SUPPRESS_DEFAULT", "RESULT_PENDING",
2015 NULL
2016 );
2017 else
2018 GET_INT_ARG(tmp_int, str);
2019
2020 if (tmp_int < -1 || tmp_int > 2)
2021 RETURN_INT(0);
2022 current_hook->retval = tmp_int;
2023 RETURN_INT(1);
2024 }
2025 break;
2026
2027 /* go-switch */
2028 case HOOKCTL_DENY_ALL_HOOKS:
2029 if (!input || !*input)
2030 RETURN_INT(deny_all_hooks);
2031 else
2032 {
2033 GET_INT_ARG(tmp_int, input);
2034 deny_all_hooks = tmp_int ? 1 : 0;
2035 RETURN_INT(deny_all_hooks);
2036 }
2037 break;
2038
2039 /* go-switch */
2040 case HOOKCTL_EMPTY_SLOTS:
2041 for (tmp_int = 0; tmp_int < hooklist_size; tmp_int++)
2042 if (hooklist[tmp_int] == NULL)
2043 malloc_strcat_wordlist_c(&ret, space, ltoa(tmp_int), &retlen);
2044
2045 RETURN_MSTR(ret);
2046 break;
2047
2048 /* go-switch */
2049 case HOOKCTL_COUNT:
2050 case HOOKCTL_LIST:
2051 /* Shamelessly using prop here as well. Save the variables! */
2052 if (!input || !*input)
2053 prop = 1;
2054 else
2055 {
2056 GET_FUNC_ARG(str,input);
2057 prop = vmy_strnicmp(strlen(str), str, "LISTS", "POPULATED_LISTS", "HOOKS", NULL);
2058 }
2059 if (input && *input)
2060 {
2061 GET_FUNC_ARG(str, input);
2062 }
2063 else
2064 {
2065 str = NULL;
2066 }
2067 if (prop == 0)
2068 RETURN_EMPTY;
2069 tmp_int2 = 0;
2070 /* Walk through the entire list, starting with the named hooks */
2071 for (tmp_int = 0; tmp_int < NUMBER_OF_LISTS; tmp_int++)
2072 {
2073 if (((prop != 1 && hook_functions[tmp_int].list == NULL)
2074 || (str && !wild_match(str, hook_functions[tmp_int].name))
2075 )
2076 )
2077 continue;
2078 if (prop != 3)
2079 {
2080 if (go == HOOKCTL_COUNT)
2081 tmp_int2++;
2082 else
2083 malloc_strcat_wordlist_c(&ret, space, hook_functions[tmp_int].name,
2084 &retlen);
2085 continue;
2086 }
2087 for (tmp_hook = hook_functions[tmp_int].list;
2088 tmp_hook != NULL;
2089 tmp_hook = tmp_hook->next)
2090 {
2091 if (go == HOOKCTL_COUNT)
2092 tmp_int2++;
2093 else
2094 malloc_strcat_wordlist_c(&ret, space, ltoa(tmp_hook->userial),
2095 &retlen);
2096 }
2097 }
2098 if (go == HOOKCTL_COUNT)
2099 RETURN_INT(tmp_int2);
2100 else
2101 RETURN_MSTR(ret);
2102 break;
2103
2104 /* go-switch */
2105 case HOOKCTL_SERIAL:
2106 case HOOKCTL_PACKAGE:
2107 if (go == HOOKCTL_SERIAL)
2108 is_serial = 1;
2109 if (!input || !*input)
2110 RETURN_EMPTY;
2111 if (is_serial)
2112 {
2113 GET_INT_ARG(serial, input);
2114 }
2115 else
2116 {
2117 GET_FUNC_ARG(str, input);
2118 }
2119 if (input && *input)
2120 {
2121 GET_FUNC_ARG(hookname, input);
2122 if ((hooknum = find_hook (hookname, NULL, 1)) == INVALID_HOOKNUM)
2123 RETURN_EMPTY;
2124 }
2125 if (hooknum == INVALID_HOOKNUM)
2126 {
2127 for (tmp_int = 0; tmp_int < hooklist_size; tmp_int++)
2128 {
2129 if (hooklist[tmp_int] == NULL)
2130 continue;
2131 if ((is_serial && hooklist[tmp_int]->sernum == serial) ||
2132 (!is_serial && hooklist[tmp_int]->filename && !my_stricmp(hooklist[tmp_int]->filename, str)))
2133 {
2134 malloc_strcat_wordlist_c(&ret, space, ltoa(tmp_int), &retlen);
2135 }
2136 }
2137 }
2138 else
2139 {
2140 if (hook_functions[hooknum].list == NULL)
2141 RETURN_EMPTY;
2142 for (
2143 hook = hook_functions[hooknum].list;
2144 hook != NULL;
2145 hook = hook->next)
2146 if ((is_serial && hook->sernum == serial) ||
2147 (!is_serial && hook->filename && !my_stricmp(hook->filename, str))
2148 )
2149 {
2150 malloc_strcat_wordlist_c(&ret, space, ltoa(hook->userial), &retlen);
2151 }
2152 }
2153 RETURN_MSTR(ret);
2154 break;
2155
2156 /* go-switch */
2157 case HOOKCTL_NOISE_LEVELS:
2158 if (!input || !*input)
2159 nam = NULL;
2160 else
2161 {
2162 GET_FUNC_ARG(nam, input);
2163 }
2164 for (tmp_int = 0; tmp_int < noise_level_num; tmp_int++)
2165 {
2166 if (!nam || wild_match(nam, noise_info[tmp_int]->name))
2167 malloc_strcat_wordlist_c(&ret, space, noise_info[tmp_int]->name, &retlen);
2168 }
2169 RETURN_MSTR(ret);
2170 break;
2171
2172 /* go-switch */
2173 case HOOKCTL_EXECUTING_HOOKS:
2174 if (current_hook == NULL)
2175 RETURN_EMPTY;
2176 for (
2177 curhook = current_hook;
2178 curhook != NULL; curhook = curhook->under)
2179 malloc_strcat_wordlist_c(&ret, space, ltoa(curhook->userial), &retlen);
2180 RETURN_MSTR(ret);
2181 break;
2182
2183 /* go-switch */
2184 case HOOKCTL_MATCHES:
2185 /*
2186 * serial, package, stuff, nick, list
2187 */
2188 RETURN_EMPTY;
2189
2190 /* go-switch */
2191 case HOOKCTL_GET:
2192 case HOOKCTL_SET:
2193 set = (go == HOOKCTL_SET);
2194
2195 if (!input || !*input)
2196 RETURN_EMPTY;
2197
2198 if (atoi(input) == 0 && input[0] != '0')
2199 {
2200 GET_FUNC_ARG(str, input);
2201 action = vmy_strnicmp(strlen(str), str,
2202 "HOOK",
2203 "LIST",
2204 "NOISE",
2205 "NOISY",
2206 NULL
2207 );
2208
2209 if (action == 0)
2210 RETURN_EMPTY;
2211 }
2212 else
2213 action = HOOKCTL_GET_HOOK_NOARG;
2214
2215 switch (action)
2216 {
2217 /* action-switch */
2218 case HOOKCTL_GET_HOOK:
2219 case HOOKCTL_GET_HOOK_NOARG:
2220
2221 GET_INT_ARG(userial, input);
2222 if (userial == -1 && current_hook)
2223 userial = current_hook->userial;
2224 if (userial < 0
2225 || hooklist_size <= userial
2226 || hooklist[userial] == NULL)
2227 RETURN_EMPTY;
2228 hook = hooklist[userial];
2229 if (!set && (!input || !*input))
2230 RETURN_STR(hook->stuff);
2231 GET_FUNC_ARG(str, input);
2232
2233 prop = vmy_strnicmp(strlen(str), str,
2234 "ARGUMENT_LIST",
2235 "FLEXIBLE", "NICK", "NOT", "NOISE",
2236 "NOISY", "PACKAGE", "SERIAL", "SKIP",
2237 "STUFF", "TYPE", "STRING", NULL);
2238 if (prop == 0)
2239 RETURN_EMPTY;
2240
2241 if (!input || !*input)
2242 str = LOCAL_COPY(empty_string);
2243 else
2244 str = input;
2245
2246 switch (prop)
2247 {
2248 /* prop-switch */
2249 case HOOKCTL_GET_HOOK_ARGUMENT_LIST:
2250 if (!set)
2251 {
2252 if (!hook->arglist)
2253 RETURN_EMPTY;
2254 str = print_arglist(hook->arglist);
2255 if (!str)
2256 RETURN_EMPTY;
2257 return str;
2258 }
2259
2260 if (hook->arglist)
2261 destroy_arglist(&(hook->arglist));
2262
2263 if (!str || !*str)
2264 {
2265 hook->arglist = NULL;
2266 RETURN_EMPTY;
2267 }
2268
2269 hook->arglist = parse_arglist(str);
2270 str = print_arglist(hook->arglist);
2271 if (!str)
2272 {
2273 destroy_arglist(&(hook->arglist));
2274 RETURN_EMPTY;
2275 }
2276 return str;
2277
2278 break;
2279
2280 case HOOKCTL_GET_HOOK_NICK:
2281 if (!set)
2282 RETURN_STR(hook->nick);
2283 str = malloc_strdup(upper(str));
2284 for (
2285 tmp_hook = hook_functions[hook->type].list;
2286 tmp_hook != NULL;
2287 tmp_hook = tmp_hook->next
2288 )
2289 {
2290 if (!strcmp(tmp_hook->nick, str)
2291 && tmp_hook->sernum == hook->sernum)
2292 {
2293 new_free (&str);
2294 RETURN_INT(0);
2295 }
2296 }
2297 remove_from_list(
2298 &hook_functions[hook->type].list,
2299 hook->nick,
2300 hook->sernum
2301 );
2302 new_free(&hook->nick);
2303 hook->nick = str;
2304 add_to_list(
2305 &hook_functions[hook->type].list,
2306 hook
2307 );
2308 RETURN_INT(1);
2309 break;
2310
2311 /* prop-switch */
2312 case HOOKCTL_GET_HOOK_STUFF:
2313 if (!set)
2314 RETURN_STR(hook->stuff);
2315 new_free (&(hook->stuff));
2316 hook->stuff = malloc_strdup(str);
2317 RETURN_INT(1);
2318 break;
2319
2320 /* prop-switch */
2321 case HOOKCTL_GET_HOOK_NOT:
2322 if (!set)
2323 RETURN_INT(hook->not);
2324 hook->not = atol(str) ? 1 : 0;
2325 RETURN_INT(1);
2326 break;
2327
2328 case HOOKCTL_GET_HOOK_SKIP:
2329 if (!set)
2330 RETURN_INT(hook->skip);
2331 hook->skip = atol(str) ? 1 : 0;
2332 RETURN_INT(1);
2333 break;
2334
2335 /* prop-switch */
2336 case HOOKCTL_GET_HOOK_NOISE:
2337 case HOOKCTL_GET_HOOK_NOISY:
2338 if (!set)
2339 RETURN_STR(noise_info[hook->noisy]->name);
2340 if ((tmp_int = get_noise_id(input)) >= noise_level_num
2341 || tmp_int < 0
2342 )
2343 RETURN_INT(0);
2344 hook->noisy = tmp_int;
2345 RETURN_INT(1);
2346 break;
2347
2348 /* prop-switch */
2349 case HOOKCTL_GET_HOOK_SERIAL:
2350 if (!set)
2351 RETURN_INT(hook->sernum);
2352 tmp_int = atol(str);
2353 for (
2354 tmp_hook = hook_functions[hook->type].list;
2355 tmp_hook != NULL;
2356 tmp_hook = tmp_hook->next
2357 )
2358 {
2359 if (!strcmp(tmp_hook->nick, hook->nick)
2360 && tmp_hook->sernum == tmp_int
2361 )
2362 RETURN_INT(0);
2363 }
2364 remove_from_list(
2365 &hook_functions[hook->type].list,
2366 hook->nick,
2367 hook->sernum
2368 );
2369
2370 hook->sernum = tmp_int;
2371 add_to_list(
2372 &hook_functions[hook->type].list,
2373 hook
2374 );
2375 RETURN_INT(1);
2376 break;
2377
2378 /* prop-switch */
2379 case HOOKCTL_GET_HOOK_FLEXIBLE:
2380 if (!set)
2381 RETURN_INT(hook->flexible);
2382 hook->flexible = atol(str) ? 1 : 0;
2383 RETURN_INT(1);
2384 break;
2385
2386 /* prop-switch */
2387 case HOOKCTL_GET_HOOK_PACKAGE:
2388 if (!set)
2389 RETURN_STR(hook->filename);
2390 new_free (&(hook->filename));
2391 hook->filename = malloc_strdup(str);
2392 RETURN_INT(1);
2393 break;
2394
2395 /* prop-switch */
2396 case HOOKCTL_GET_HOOK_TYPE:
2397 if (!set)
2398 RETURN_STR(hook_functions[hook->type].name);
2399 break;
2400
2401 case HOOKCTL_GET_HOOK_STRING:
2402 {
2403 char *retval = NULL;
2404 size_t clue = 0;
2405 char blah[10];
2406
2407 /* Just to start off */
2408 retval = new_malloc(128);
2409 *retval = 0;
2410
2411 /* ON <SERIAL-INDICATOR><NOISE><TYPE> <SERIAL-NUMBER>
2412 <QUOTE><PATTERN><QUOTE> {<STUFF>} */
2413 malloc_strcat_c(&retval, "ON ", &clue);
2414
2415 if (hook->sernum)
2416 malloc_strcat_c(&retval, "#", &clue);
2417 if (noise_info[hook->noisy]->identifier)
2418 {
2419 blah[0] = noise_info[hook->noisy]->identifier;
2420 blah[1] = 0;
2421 malloc_strcat_c(&retval, blah, &clue);
2422 }
2423 malloc_strcat_c(&retval, hook_functions[hook->type].name, &clue);
2424
2425 if (hook->sernum)
2426 {
2427 snprintf(blah, sizeof blah, " %d", hook->sernum);
2428 malloc_strcat_c(&retval, blah, &clue);
2429 }
2430
2431 malloc_strcat_c(&retval, space, &clue);
2432 if (hook->not)
2433 {
2434 blah[0] = '^';
2435 blah[1] = 0;
2436 malloc_strcat_c(&retval, blah, &clue);
2437 }
2438
2439 if (hook->flexible)
2440 {
2441 blah[0] = '\'';
2442 blah[1] = 0;
2443 }
2444 else
2445 {
2446 blah[0] = '"';
2447 blah[1] = 0;
2448 }
2449 malloc_strcat_c(&retval, blah, &clue);
2450 malloc_strcat_c(&retval, hook->nick, &clue);
2451 malloc_strcat_c(&retval, blah, &clue);
2452 malloc_strcat_c(&retval, space, &clue);
2453
2454 if (hook->arglist)
2455 {
2456 char *arglist = print_arglist(hook->arglist);
2457
2458 blah[0] = '(';
2459 blah[1] = 0;
2460 malloc_strcat_c(&retval, blah, &clue);
2461 malloc_strcat_c(&retval, arglist, &clue);
2462 blah[0] = ')';
2463 malloc_strcat_c(&retval, blah, &clue);
2464 malloc_strcat_c(&retval, space, &clue);
2465 new_free(&arglist);
2466 }
2467
2468 if (!hook->not)
2469 {
2470 blah[0] = '{';
2471 blah[1] = 0;
2472 malloc_strcat_c(&retval, blah, &clue);
2473 malloc_strcat_c(&retval, hook->stuff, &clue);
2474 blah[0] = '}';
2475 malloc_strcat_c(&retval, blah, &clue);
2476 }
2477 RETURN_MSTR(retval);
2478 }
2479 /* end prop-switch */
2480 }
2481 RETURN_EMPTY;
2482 break;
2483
2484 /* action switch */
2485 case HOOKCTL_GET_NOISE:
2486 case HOOKCTL_GET_NOISY:
2487 if (!input || !*input)
2488 RETURN_EMPTY;
2489 if (isdigit(input[0]))
2490 {
2491 GET_INT_ARG(id, input);
2492 if (noise_level_num <= id)
2493 RETURN_EMPTY;
2494 }
2495 else
2496 {
2497 GET_FUNC_ARG(name, input);
2498 for (tmp_int = 0; tmp_int < noise_level_num; tmp_int++)
2499 {
2500 if (!my_stricmp(name, noise_info[tmp_int]->name))
2501 break;
2502 }
2503 if (noise_level_num == tmp_int)
2504 RETURN_EMPTY;
2505 id = tmp_int;
2506 }
2507
2508 if (!input || !*input)
2509 RETURN_EMPTY;
2510
2511 GET_FUNC_ARG(str, input);
2512 switch (vmy_strnicmp(strlen(str), str,
2513 "ALERT", "CUSTOM", "DISPLAY", "IDENTIFIER",
2514 "NAME", "SUPPRESS", "VALUE", NULL)
2515 )
2516 {
2517 case 1: RETURN_INT(noise_info[id]->alert ? 1 : 0);
2518 case 2: RETURN_INT(noise_info[id]->custom ? 1 : 0);
2519 case 3: RETURN_INT(noise_info[id]->display ? 1 : 0);
2520 case 4: RETURN_INT(noise_info[id]->identifier);
2521 case 5: RETURN_STR(noise_info[id]->name);
2522 case 6: RETURN_INT(noise_info[id]->suppress ? 1 : 0);
2523 case 7: RETURN_INT(noise_info[id]->value);
2524 default: RETURN_EMPTY;
2525 }
2526 break;
2527
2528 /* action-switch */
2529 case HOOKCTL_GET_LIST:
2530
2531 GET_FUNC_ARG(hookname, input);
2532 if (atoi(hookname) == -1)
2533 {
2534 if (current_hook == NULL)
2535 RETURN_EMPTY;
2536 hooknum = hooklist[current_hook->userial]->type;
2537 }
2538 else
2539 {
2540 if ((hooknum = find_hook (hookname, NULL, 1)) == INVALID_HOOKNUM)
2541 {
2542 RETURN_EMPTY;
2543 }
2544 }
2545 hooks = &hook_functions[hooknum];
2546 if (!input || !*input)
2547 prop = 2;
2548 else
2549 {
2550 GET_FUNC_ARG(str, input);
2551 prop = vmy_strnicmp(strlen(str), str,
2552 "FLAGS", "MARK", "NAME",
2553 "PARAMETRES", "PARAMETERS", "PARAMS",
2554 "IMPLIED", NULL);
2555 }
2556
2557 switch (prop)
2558 {
2559 case 1: RETURN_INT(hooks->flags);
2560 case 2: RETURN_INT(hooks->mark);
2561 case 3: RETURN_STR(hooks->name);
2562 case 4:
2563 case 5:
2564 case 6: RETURN_INT(hooks->params);
2565 #ifdef IMPLIED_ON_HOOKS
2566 case 7: if (set) {
2567 if (*input == '{')
2568 {
2569 ssize_t span;
2570 span = MatchingBracket(input+1, '{', '}');
2571 if (span >= 0) {
2572 input[span + 1] = 0;
2573 input++;
2574 }
2575 hooks->implied_protect = 1;
2576 }
2577 if (empty(input))
2578 new_free(&hooks->implied);
2579 else
2580 malloc_strcpy(&hooks->implied, input);
2581 RETURN_INT(1);
2582 } else
2583 RETURN_STR(hooks->implied);
2584 #endif
2585 }
2586 RETURN_EMPTY;
2587 break;
2588
2589 /* end action-switch */
2590 }
2591 RETURN_EMPTY;
2592 break;
2593
2594 /* go-switch */
2595 case HOOKCTL_ADD:
2596 if (!input || !*input)
2597 RETURN_INT(-1);
2598 GET_FUNC_ARG(hookname, input);
2599 while (*hookname != '\0')
2600 {
2601 switch (*hookname)
2602 {
2603 case '#':
2604 set_serial = 1;
2605 hookname++;
2606 continue;
2607
2608 case '!':
2609 set_not = 1;
2610 hookname++;
2611 continue;
2612
2613 case '\'':
2614 set_flex = 1;
2615 hookname++;
2616 continue;
2617
2618 default:
2619 for (tmp_int = 0; tmp_int < noise_level_num; tmp_int++)
2620 {
2621 if (noise_info[tmp_int]->identifier != 0 &&
2622 noise_info[tmp_int]->identifier == *hookname)
2623 break;
2624 }
2625 if (tmp_int == noise_level_num)
2626 break;
2627 set_noisy = noise_info[tmp_int]->value;
2628 hookname++;
2629 continue;
2630 }
2631 break;
2632 }
2633 if ((hooknum = find_hook (hookname, NULL, 1)) == INVALID_HOOKNUM)
2634 RETURN_EMPTY;
2635 if (set_serial)
2636 {
2637 if (!input || !*input)
2638 RETURN_INT(-1);
2639 if (input[0] == '#')
2640 input++;
2641 GET_FUNC_ARG(tmp, input);
2642 if (!strcmp(tmp, "-"))
2643 {
2644 serial = hook_find_free_serial(-1, 0, hooknum);
2645 }
2646 else if (!strcmp(tmp, "+"))
2647 {
2648 serial = hook_find_free_serial(1, 0, hooknum);
2649 }
2650 else
2651 {
2652 serial = atoi (tmp);
2653 }
2654 }
2655 if (!input || !*input)
2656 RETURN_INT(-1);
2657 GET_FUNC_ARG(nick, input);
2658 nick = malloc_strdup(nick);
2659 tmp_int = add_hook (hooknum, nick, tmp_arglist, input, set_noisy, set_not, serial, set_flex);
2660 new_free (&nick);
2661 RETURN_INT(tmp_int);
2662 break;
2663
2664 /* go-switch */
2665 case HOOKCTL_REMOVE:
2666 if (!input || !*input)
2667 RETURN_INT(-1);
2668 GET_INT_ARG(id, input);
2669 if (id == -1 && current_hook != NULL)
2670 id = current_hook -> userial;
2671 if (hooklist_size <= id || id < 0 || hooklist[id] == NULL)
2672 RETURN_INT(-1);
2673 hook = hooklist[id];
2674 remove_hook(hook->type, hook->nick, hook->sernum, 1);
2675 RETURN_INT(1);
2676 break;
2677
2678 /* go-switch */
2679 case HOOKCTL_HALTCHAIN:
2680 if (input && *input)
2681 {
2682 GET_INT_ARG(halt, input);
2683 }
2684 curhook = current_hook;
2685 for (tmp_int = 0; curhook && tmp_int < halt; tmp_int++)
2686 curhook = curhook->under;
2687 if (!curhook)
2688 RETURN_INT(0);
2689 curhook->halt = 1;
2690 RETURN_INT(1);
2691 break;
2692
2693 /* go-switch */
2694 case HOOKCTL_ARGS:
2695 if (input && *input)
2696 {
2697 GET_INT_ARG(halt, input);
2698 }
2699 curhook = current_hook;
2700 for (tmp_int = 0; curhook && tmp_int < halt; tmp_int++)
2701 curhook = curhook->under;
2702 if (!curhook)
2703 RETURN_INT(0);
2704 malloc_strcpy(&curhook->buffer, input);
2705 curhook->buffer_changed = 1;
2706 RETURN_INT(1);
2707 break;
2708
2709 /* go-switch */
2710 case HOOKCTL_USERINFO:
2711 if (input && *input)
2712 {
2713 GET_INT_ARG(halt, input);
2714 };
2715 curhook = current_hook;
2716 for (tmp_int = 0; curhook && tmp_int < halt; tmp_int++)
2717 curhook = curhook->under;
2718 if (!curhook)
2719 RETURN_INT(0);
2720 if (!input || !*input)
2721 {
2722 if (curhook->user_supplied_info == NULL)
2723 RETURN_EMPTY;
2724 else
2725 RETURN_STR(curhook->user_supplied_info);
2726 }
2727 malloc_strcpy(&curhook->user_supplied_info, input);
2728 RETURN_INT(1);
2729 break;
2730 /* go-switch */
2731 case HOOKCTL_MATCH:
2732 if (!input || !*input)
2733 RETURN_EMPTY;
2734 GET_FUNC_ARG(hookname, input);
2735 if ((hooknum = find_hook (hookname, NULL, 1)) == INVALID_HOOKNUM)
2736 RETURN_EMPTY;
2737 if (!hook_functions[hooknum].list
2738 || (hook_functions[hooknum].mark
2739 && hook_functions[hooknum].flags & HF_NORECURSE))
2740 RETURN_EMPTY;
2741 buffer = malloc_strdup(!input || !*input ? "" : input);
2742 for (sernum = INT_MIN;sernum < INT_MAX;sernum++)
2743 {
2744 for (hook = hook_functions[hooknum].list; hook; hook = hook->next)
2745 {
2746 Hook *besthook = NULL;
2747 if (hook->sernum < sernum)
2748 continue;
2749
2750 if (hook->sernum > sernum)
2751 sernum = hook->sernum;
2752
2753 for (; hook && hook->sernum == sernum; hook = hook->next)
2754 {
2755 if (hook->flexible)
2756 {
2757 char *tmpnick;
2758 tmpnick = expand_alias(hook->nick, empty_string);
2759 currmatch = wild_match(tmpnick, buffer);
2760 new_free(&tmpnick);
2761 }
2762 else
2763 currmatch = wild_match(hook->nick, buffer);
2764
2765 if (currmatch > bestmatch)
2766 {
2767 besthook = hook;
2768 bestmatch = currmatch;
2769 }
2770 }
2771
2772 if (!besthook || besthook->not)
2773 break;
2774
2775 malloc_strcat_wordlist_c(&ret, space, ltoa(besthook->userial), &retlen);
2776 break;
2777 }
2778 if (!hook)
2779 break;
2780 }
2781 new_free (&buffer);
2782 RETURN_MSTR(ret);
2783 break;
2784
2785 /* go-switch */
2786 default:
2787 yell ("In function hookctl: unknown go: %d", go);
2788 RETURN_EMPTY;
2789 /* The function should never come to this point */
2790
2791 /* end go-switch */
2792 }
2793 RETURN_EMPTY;
2794 }
2795
2796 #if 0
2797 void help_topics_on (FILE *f)
2798 {
2799 int x;
2800
2801 for (x = 0; x < NUMBER_OF_LISTS; x++)
2802 if (!isdigit(*hook_functions[x].name))
2803 fprintf(f, "on %s\n", hook_functions[x].name);
2804 }
2805 #endif
2806
2807