1 /*
2  * hook.c: Does those naughty hook functions.
3  *
4  * Written By Michael Sandrof
5  *
6  * Copyright (c) 1990 Michael Sandrof.
7  * Copyright (c) 1991, 1992 Troy Rollo.
8  * Copyright (c) 1992-2000 Matthew R. Green.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include "irc.h"
36 IRCII_RCSID("@(#)$Id: hook.c,v 1.48 2000/04/04 13:49:40 mrg Exp $");
37 
38 #include "hook.h"
39 #include "vars.h"
40 #include "ircaux.h"
41 #include "alias.h"
42 #include "list.h"
43 #include "window.h"
44 #include "server.h"
45 #include "output.h"
46 #include "edit.h"
47 
48 #include "dma.h"
49 
50 #ifdef	INCLUDE_UNUSED_FUNCTIONS
51 static  void	flush_on_hooks _((void));
52 #endif /* INCLUDE_UNUSED_FUNCTIONS */
53 static	u_char	*fill_it_out _((u_char *, int));
54 static	void	setup_struct _((int, int, int, int));
55 static	int	Add_Remove_Check _((List *, u_char *));
56 static	int	Add_Remove_Check_List _((List *, List*));
57 static	void	add_numeric_hook _((int, u_char *, u_char *, int, int, int, int));
58 static	void	add_hook _((int, u_char *, u_char *, int, int, int, int));
59 static	int	show_numeric_list _((int));
60 static	int	show_list _((int));
61 static	void	remove_numeric_hook _((int, u_char *, int, int, int));
62 static	void	write_hook _((FILE *, Hook *, char *));
63 
64 #define SILENT 0
65 #define QUIET 1
66 #define NORMAL 2
67 #define NOISY 3
68 
69 /*
70  * The various ON levels: SILENT means the DISPLAY will be OFF and it will
71  * suppress the default action of the event, QUIET means the display will be
72  * OFF but the default action will still take place, NORMAL means you will be
73  * notified when an action takes place and the default action still occurs,
74  * NOISY means you are notified when an action occur plus you see the action
75  * in the display and the default actions still occurs
76  */
77 static	char	*noise_level[] = { "SILENT", "QUIET", "NORMAL", "NOISY" };
78 
79 #define	HS_NOGENERIC	0x1000
80 #define	HF_LOOKONLY	0x0001
81 #define HF_NORECURSE	0x0002
82 #define HF_GLOBAL	0x0004
83 
84 extern	int	load_depth;
85 
86 	int	in_on_who = 0;
87 
88 	NumericList *numeric_list = (NumericList *) 0;
89 /* hook_functions: the list of all hook functions available */
90 	HookFunc FAR hook_functions[] =
91 {
92 	{ "ACTION",		(Hook *) 0,	3,	0,	0 },
93 	{ "CHANNEL_NICK",	(Hook *) 0,	3,	0,	0 },
94 	{ "CHANNEL_SIGNOFF",	(Hook *) 0,	3,	0,	0 },
95 	{ "CONNECT",		(Hook *) 0,	1,	0,	0 },
96 	{ "CTCP",		(Hook *) 0,	4,	0,	0 },
97 	{ "CTCP_REPLY",		(Hook *) 0,	3,	0,	0 },
98 	{ "DCC_CHAT",		(Hook *) 0,	2,	0,	0 },
99         { "DCC_CONNECT",        (Hook *) 0,     2,      0,      0 },
100         { "DCC_ERROR",          (Hook *) 0,     6,      0,      0 },
101         { "DCC_LOST",           (Hook *) 0,     2,      0,      0 },
102 	{ "DCC_RAW",		(Hook *) 0,	3,	0,	0 },
103         { "DCC_REQUEST",        (Hook *) 0,     4,      0,      0 },
104 	{ "DISCONNECT",		(Hook *) 0,	1,	0,	0 },
105         { "ENCRYPTED_NOTICE",   (Hook *) 0,     3,      0,      0 },
106         { "ENCRYPTED_PRIVMSG",  (Hook *) 0,     3,      0,      0 },
107 	{ "EXEC",		(Hook *) 0,	2,	0,	0 },
108 	{ "EXEC_ERRORS",	(Hook *) 0,	2,	0,	0 },
109 	{ "EXEC_EXIT",		(Hook *) 0,	3,	0,	0 },
110 	{ "EXEC_PROMPT",	(Hook *) 0,	2,	0,	0 },
111         { "EXIT",               (Hook *) 0,     1,      0,      0 },
112 	{ "FLOOD",		(Hook *) 0,	3,	0,	0 },
113 	{ "HELP",		(Hook *) 0,	2,	0,	0 },
114 	{ "HOOK",		(Hook *) 0,	1,	0,	0 },
115 #ifdef SUPPORT_ICB
116 	{ "ICB_STATUS",		(Hook *) 0,	2,	0,	0 },
117 	{ "ICB_ERROR",		(Hook *) 0,	1,	0,	0 },
118 #endif
119 	{ "IDLE",		(Hook *) 0,	1,	0,	0 },
120 	{ "INPUT",		(Hook *) 0,	1,	0,	0 },
121 	{ "INVITE",		(Hook *) 0,	2,	0,	0 },
122 	{ "JOIN",		(Hook *) 0,	3,	0,	0 },
123 	{ "KICK",		(Hook *) 0,	3,	0,	HF_LOOKONLY },
124 	{ "LEAVE",		(Hook *) 0,	2,	0,	0 },
125 	{ "LIST",		(Hook *) 0,	3,	0,	HF_LOOKONLY },
126 	{ "MAIL",		(Hook *) 0,	2,	0,	0 },
127 	{ "MODE",		(Hook *) 0,	3,	0,	0 },
128 	{ "MSG",		(Hook *) 0,	2,	0,	0 },
129 	{ "MSG_GROUP",		(Hook *) 0,	3,	0,	0 },
130 	{ "NAMES",		(Hook *) 0,	2,	0,	HF_LOOKONLY },
131 	{ "NICKNAME",		(Hook *) 0,	2,	0,	0 },
132 	{ "NOTE",		(Hook *) 0,	10,	0,	0 },
133 	{ "NOTICE",		(Hook *) 0,	2,	0,	0 },
134 	{ "NOTIFY_SIGNOFF",	(Hook *) 0,	3,	0,	0 },
135 	{ "NOTIFY_SIGNON",	(Hook *) 0,	4,	0,	0 },
136 	{ "PUBLIC",		(Hook *) 0,	3,	0,	0 },
137 	{ "PUBLIC_MSG",		(Hook *) 0,	3,	0,	0 },
138 	{ "PUBLIC_NOTICE",	(Hook *) 0,	3,	0,	0 },
139 	{ "PUBLIC_OTHER",	(Hook *) 0,	3,	0,	0 },
140 	{ "RAW_IRC",		(Hook *) 0,	1,	0,	0 },
141 	{ "SEND_ACTION",	(Hook *) 0,	2,	0,	0 },
142 	{ "SEND_DCC_CHAT",	(Hook *) 0,	2,	0,	0 },
143 	{ "SEND_MSG",		(Hook *) 0,	2,	0,	0 },
144 	{ "SEND_NOTICE",	(Hook *) 0,	2,	0,	0 },
145 	{ "SEND_PUBLIC",	(Hook *) 0,	2,	0,	0 },
146 	{ "SEND_TALK",		(Hook *) 0,	2,	0,	0 },
147 	{ "SERVER_NOTICE",	(Hook *) 0,	1,	0,	0 },
148 	{ "SIGNOFF",		(Hook *) 0,	1,	0,	0 },
149 	{ "TALK",		(Hook *) 0,	2,	0,	0 },
150 	{ "TIMER",		(Hook *) 0,	1,	0,	0 },
151 	{ "TOPIC",		(Hook *) 0,	2,	0,	0 },
152 	{ "WALL",		(Hook *) 0,	2,	0,	HF_LOOKONLY },
153 	{ "WALLOP",		(Hook *) 0,	3,	0,	HF_LOOKONLY },
154 	{ "WHO",		(Hook *) 0,	6,	0,	HF_LOOKONLY },
155 	{ "WIDELIST",		(Hook *) 0,	1,	0,	HF_LOOKONLY },
156 	{ "WINDOW",		(Hook *) 0,	2,	0,	HF_NORECURSE },
157 	{ "WINDOW_KILL",	(Hook *) 0,	1,	0,	0 },
158 	{ "WINDOW_SWAP",	(Hook *) 0,	2,	0,	0 }
159 };
160 
161 static u_char	*
fill_it_out(str,params)162 fill_it_out(str, params)
163 	u_char	*str;
164 	int	params;
165 {
166 	u_char	lbuf[BIG_BUFFER_SIZE + 1];
167 	u_char	*arg,
168 		*free_ptr = (u_char *) 0,
169 		*ptr;
170 	int	i = 0;
171 
172 	malloc_strcpy(&free_ptr, str);
173 	ptr = free_ptr;
174 	*lbuf = (u_char) 0;
175 	while ((arg = next_arg(ptr, &ptr)) != NULL)
176 	{
177 		if (*lbuf)
178 			my_strmcat(lbuf, " ", BIG_BUFFER_SIZE);
179 		my_strmcat(lbuf, arg, BIG_BUFFER_SIZE);
180 		if (++i == params)
181 			break;
182 	}
183 	for (; i < params; i++)
184 		my_strmcat(lbuf, (i < params-1) ? " %" : " *", BIG_BUFFER_SIZE);
185 	if (*ptr)
186 	{
187 		my_strmcat(lbuf, " ", BIG_BUFFER_SIZE);
188 		my_strmcat(lbuf, ptr, BIG_BUFFER_SIZE);
189 	}
190 	malloc_strcpy(&free_ptr, lbuf);
191 	return (free_ptr);
192 }
193 
194 
195 /*
196  * A variety of comparison functions used by the hook routines follow.
197  */
198 
199 struct	CmpInfoStruc
200 {
201 	int	ServerRequired;
202 	int	SkipSerialNum;
203 	int	SerialNumber;
204 	int	Flags;
205 }	cmp_info;
206 
207 #define	CIF_NOSERNUM	0x0001
208 #define	CIF_SKIP	0x0002
209 
210 int     cmpinfodone = 0;
211 
212 static void
setup_struct(ServReq,SkipSer,SerNum,flags)213 setup_struct(ServReq, SkipSer, SerNum, flags)
214 	int	ServReq;
215 	int	SkipSer;
216 	int	SerNum;
217 	int	flags;
218 {
219 	cmp_info.ServerRequired = ServReq;
220 	cmp_info.SkipSerialNum = SkipSer;
221 	cmp_info.SerialNumber = SerNum;
222 	cmp_info.Flags = flags;
223 }
224 
225 static	int
Add_Remove_Check(_Item,Name)226 Add_Remove_Check(_Item, Name)
227 	List	*_Item;
228 	u_char	*Name;
229 {
230 	int	comp;
231 	Hook	*Item = (Hook *)_Item;
232 
233 	if (cmp_info.SerialNumber != Item->sernum)
234 		return (Item->sernum > cmp_info.SerialNumber) ? 1 : -1;
235 	if ((comp = my_stricmp(Item->nick, Name)) != 0)
236 			return comp;
237 	if (Item->server != cmp_info.ServerRequired)
238 		return (Item->server > cmp_info.ServerRequired) ? 1 : -1;
239 	return 0;
240 }
241 
242 static	int
Add_Remove_Check_List(_Item,_Item2)243 Add_Remove_Check_List(_Item, _Item2)
244 	List	*_Item;
245 	List	*_Item2;
246 {
247 	return Add_Remove_Check(_Item, _Item->name);
248 }
249 
250 static	void
add_numeric_hook(numeric,nick,stuff,noisy,not,server,sernum)251 add_numeric_hook(numeric, nick, stuff, noisy, not, server, sernum)
252 	int	numeric;
253 	u_char	*nick,
254 		*stuff;
255 	int	noisy,
256 		not;
257 	int	server,
258 		sernum;
259 {
260 	NumericList *entry;
261 	Hook	*new;
262 	u_char	buf[8];
263 
264 	snprintf(CP(buf), sizeof(buf)-1, "%3.3u", numeric);
265 	buf[sizeof(buf)-1] = '\0';
266 	if ((entry = (NumericList *) find_in_list((List **) &numeric_list, buf, 0)) ==
267 			(NumericList *) 0)
268 	{
269 		entry = (NumericList *) new_malloc(sizeof(NumericList));
270 		entry->name = (char *) 0;
271 		entry->list = (Hook *) 0;
272 		malloc_strcpy((u_char **) &(entry->name), buf);
273 		add_to_list((List **) &numeric_list, (List *) entry);
274 	}
275 
276 	setup_struct((server==-1) ? -1 : (server & ~HS_NOGENERIC), sernum-1, sernum, 0);
277 	if ((new = (Hook *) remove_from_list_ext((List **) &(entry->list), nick, Add_Remove_Check)) != NULL)
278 	{
279 		new->not = 1;
280 		new_free(&(new->nick));
281 		new_free(&(new->stuff));
282 		wait_new_free((u_char **) &new);
283 	}
284 	new = (Hook *) new_malloc(sizeof(Hook));
285 	new->nick = (u_char *) 0;
286 	new->noisy = noisy;
287 	new->server = server;
288 	new->sernum = sernum;
289 	new->not = not;
290 	new->global = loading_global;
291 	new->stuff = (u_char *) 0;
292 	malloc_strcpy(&new->nick, nick);
293 	malloc_strcpy(&new->stuff, stuff);
294 	upper(new->nick);
295 	add_to_list_ext((List **) &(entry->list), (List *) new, Add_Remove_Check_List);
296 }
297 
298 /*
299  * add_hook: Given an index into the hook_functions array, this adds a new
300  * entry to the list as specified by the rest of the parameters.  The new
301  * entry is added in alphabetical order (by nick).
302  */
303 static	void
add_hook(which,nick,stuff,noisy,not,server,sernum)304 add_hook(which, nick, stuff, noisy, not, server, sernum)
305 	int	which;
306 	u_char	*nick,
307 		*stuff;
308 	int	noisy,
309 		not;
310 	int	server,
311 		sernum;
312 {
313 	Hook	*new;
314 
315 	if (which < 0)
316 	{
317 		add_numeric_hook(-which, nick, stuff, noisy, not, server,
318 			sernum);
319 		return;
320 	}
321 	setup_struct((server == -1) ? -1 : (server & ~HS_NOGENERIC), sernum-1, sernum, 0);
322 	if ((new = (Hook *) remove_from_list_ext((List **) &(hook_functions[which].list), nick, Add_Remove_Check)) != NULL)
323 	{
324 		new->not = 1;
325 		new_free(&(new->nick));
326 		new_free(&(new->stuff));
327 		wait_new_free((u_char **) &new);
328 	}
329 	new = (Hook *) new_malloc(sizeof(Hook));
330 	new->nick = (u_char *) 0;
331 	new->noisy = noisy;
332 	new->server = server;
333 	new->sernum = sernum;
334 	new->not = not;
335 	new->stuff = (u_char *) 0;
336 	new->global = loading_global;
337 	malloc_strcpy(&new->nick, nick);
338 	malloc_strcpy(&new->stuff, stuff);
339 	upper(new->nick);
340 	add_to_list_ext((List **) &(hook_functions[which].list), (List *) new, Add_Remove_Check_List);
341 }
342 
343 /* show_hook shows a single hook */
344 extern	void
345 show_hook(list, name)
346 	Hook	*list;
347 	char	*name;
348 {
349 	if (list->server != -1)
350 		say("On %s from \"%s\" do %s [%s] <%d> (Server %d)%s",
351 		    name, list->nick,
352 		    (list->not ? (u_char *) "nothing" : list->stuff),
353 		    noise_level[list->noisy], list->sernum,
354 		    list->server&~HS_NOGENERIC,
355 		    (list->server&HS_NOGENERIC) ? (u_char *) " Exclusive" : empty_string);
356 	else
357 		say("On %s from \"%s\" do %s [%s] <%d>",
358 		    name, list->nick,
359 		    (list->not ? (u_char *) "nothing" : list->stuff),
360 		    noise_level[list->noisy],
361 		    list->sernum);
362 }
363 
364 /*
365  * show_numeric_list: If numeric is 0, then all numeric lists are displayed.
366  * If numeric is non-zero, then that particular list is displayed.  The total
367  * number of entries displayed is returned
368  */
369 static	int
show_numeric_list(numeric)370 show_numeric_list(numeric)
371 	int	numeric;
372 {
373 	NumericList *tmp;
374 	Hook	*list;
375 	u_char	buf[8];
376 	int	cnt = 0;
377 
378 	if (numeric)
379 	{
380 		snprintf(CP(buf), sizeof(buf)-1, "%3.3u", numeric);
381 	   	buf[sizeof(buf)-1] = '\0';
382 		if ((tmp = (NumericList *) find_in_list((List **) &numeric_list, buf, 0))
383 				!= NULL)
384 		{
385 			for (list = tmp->list; list; list = list->next, cnt++)
386 				show_hook(list, tmp->name);
387 		}
388 	}
389 	else
390 	{
391 		for (tmp = numeric_list; tmp; tmp = tmp->next)
392 		{
393 			for (list = tmp->list; list; list = list->next, cnt++)
394 				show_hook(list, tmp->name);
395 		}
396 	}
397 	return (cnt);
398 }
399 
400 /*
401  * show_list: Displays the contents of the list specified by the index into
402  * the hook_functions array.  This function returns the number of entries in
403  * the list displayed
404  */
405 static	int
show_list(which)406 show_list(which)
407 	int	which;
408 {
409 	Hook	*list;
410 	int	cnt = 0;
411 
412 	/* Less garbage when issueing /on without args. (lynx) */
413 	for (list = hook_functions[which].list; list; list = list->next, cnt++)
414 		show_hook(list, hook_functions[which].name);
415 	return (cnt);
416 }
417 
418 /*
419  * do_hook: This is what gets called whenever a MSG, INVITES, WALL, (you get
420  * the idea) occurs.  The nick is looked up in the appropriate list. If a
421  * match is found, the stuff field from that entry in the list is treated as
422  * if it were a command. First it gets expanded as though it were an alias
423  * (with the args parameter used as the arguments to the alias).  After it
424  * gets expanded, it gets parsed as a command.  This will return as its value
425  * the value of the noisy field of the found entry, or -1 if not found.
426  */
427 /* huh-huh.. this sucks.. im going to re-write it so that it works */
428 /*VARARGS*/
429 int
430 #ifdef HAVE_STDARG_H
do_hook(int which,char * format,...)431 do_hook(int which, char *format, ...)
432 {
433 	va_list vl;
434 #else
435 do_hook(which, format, arg1, arg2, arg3, arg4, arg5, arg6)
436 	int	which;
437 	char	*format;
438 	char	*arg1,
439 		*arg2,
440 		*arg3,
441 		*arg4,
442 		*arg5,
443 		*arg6;
444 {
445 #endif /* HAVE_STDARG_H */
446 	Hook	*tmp, **list;
447 	u_char	lbuf[(BIG_BUFFER_SIZE*4) + 1]; 	/* huge buffer ;) */
448 	char	*name = (char *) 0;
449 	int	RetVal = 1;
450 	unsigned int	display;
451 	int	i,
452 		old_in_on_who;
453 	Hook	*hook_array[2048];
454 	int	hook_num = 0;
455 	static	int hook_level = 0;
456 	size_t	len;
457 	u_char	*foo;
458 
459 	hook_level++;
460 	bzero(lbuf, sizeof(lbuf));
461 
462 #ifdef HAVE_STDARG_H
463 	va_start(vl, format);
464 	vsnprintf(CP(lbuf), sizeof(lbuf)-1, format, vl);
465 	va_end(vl);
466 #else
467 	snprintf(CP(lbuf), sizeof(lbuf)-1, format, arg1, arg2, arg3, arg4, arg5, arg6);
468 #endif /* HAVE_STDARG_H */
469 	if (which < 0)
470 	{
471 		NumericList *hook;
472 		u_char	buf[8];
473 
474 		snprintf(CP(buf), sizeof(buf)-1, "%3.3u", -which);
475 	   	buf[sizeof(buf)-1] = '\0';
476 		if ((hook = (NumericList *) find_in_list((List **) &numeric_list, buf, 0))
477 				!= NULL)
478 		{
479 			name = hook->name;
480 			list = &hook->list;
481 		}
482 		else
483 			list = (Hook **) 0;
484 	}
485 	else
486 	{
487 		if (hook_functions[which].mark && (hook_functions[which].flags & HF_NORECURSE))
488 			list = (Hook **) 0;
489 		else
490 		{
491 			list = &(hook_functions[which].list);
492 			name = hook_functions[which].name;
493 		}
494 	}
495 	if (!list)
496 		return really_free(--hook_level), 1;
497 
498 	if (which >= 0)
499 		hook_functions[which].mark++;
500 			/* not attached, so dont "fix" it */
501 	{
502 		int currser = 0, oldser = 0;
503 		int currmatch = 0, oldmatch = 0;
504 		Hook *bestmatch = (Hook *) 0;
505 		int nomorethisserial = 0;
506 
507 		for (tmp = *list;tmp;tmp = tmp->next)
508 		{
509 			currser = tmp->sernum;
510 			if (currser != oldser)      /* new serial number */
511 			{
512                         	oldser = currser;
513 				currmatch = oldmatch = nomorethisserial = 0;
514 				if (bestmatch)
515 					hook_array[hook_num++] = bestmatch;
516 				bestmatch = (Hook *) 0;
517 			}
518 
519 			if (nomorethisserial)
520 				continue;
521 					/* if there is a specific server
522 			   		hook and it doesnt match, then
523 			   		we make sure nothing from
524 			   		this serial number gets hooked */
525 			if ((tmp->server != -1) &&
526 		   	    (tmp->server & HS_NOGENERIC) &&
527 		   	    (tmp->server != (from_server & HS_NOGENERIC)))
528 			{
529 				nomorethisserial = 1;
530 				bestmatch = (Hook *) 0;
531 				continue;
532 			}
533 			currmatch = wild_match(tmp->nick,lbuf);
534 			if (currmatch > oldmatch)
535 			{
536 				oldmatch = currmatch;
537 				bestmatch = tmp;
538 			}
539 		}
540 		if (bestmatch)
541 			hook_array[hook_num++] = bestmatch;
542 	}
543 
544 	for (i = 0; i < hook_num; i++)
545 	{
546 		tmp = hook_array[i];
547 		if (!tmp)
548 		{
549 			if (which >= 0)
550 				hook_functions[which].mark--;
551 			return really_free(--hook_level), RetVal;
552 		}
553 		if (tmp->not)
554 			continue;
555 		send_text_flag = which;
556 		if (tmp->noisy > QUIET)
557 			say("%s activated by \"%s\"", name, lbuf);
558 		display = window_display;
559 		if (tmp->noisy < NOISY)
560 			window_display = 0;
561 
562 		save_message_from();
563 		old_in_on_who = in_on_who;
564 		if (which == WHO_LIST || (which <= -311 && which >= -318))
565 			in_on_who = 1;
566 		len = my_strlen(tmp->stuff) + 1;
567 		foo = new_malloc(len);
568 		bcopy(tmp->stuff, foo, len);
569 		parse_line((u_char *) 0, foo, lbuf, 0, 0, 1);
570 		new_free(&foo);
571 		in_on_who = old_in_on_who;
572 		window_display = display;
573 		send_text_flag = -1;
574 		restore_message_from();
575 		if (!tmp->noisy && !tmp->sernum)
576 			RetVal = 0;
577 	}
578 	if (which >= 0)
579 		hook_functions[which].mark--;
580 	return really_free(--hook_level), RetVal;
581 }
582 
583 static	void
584 remove_numeric_hook(numeric, nick, server, sernum, quiet)
585 	int	numeric;
586 	u_char	*nick;
587 	int	server;
588 	int	sernum;
589 	int	quiet;
590 {
591 	NumericList *hook;
592 	Hook	*tmp,
593 		*next;
594 	u_char	buf[8];
595 
596 	snprintf(CP(buf), sizeof(buf)-1, "%3.3u", numeric);
597    	buf[sizeof(buf)-1] = '\0';
598 	if ((hook = (NumericList *) find_in_list((List **) &numeric_list, buf,0)) != NULL)
599 	{
600 		if (nick)
601 		{
602 			setup_struct((server == -1) ? -1 :
603 			    (server & ~HS_NOGENERIC), sernum - 1, sernum, 0);
604 			if ((tmp = (Hook *) remove_from_list((List **) &(hook->list), nick)) != NULL)
605 			{
606 				if (!quiet)
607 					say("\"%s\" removed from %s list", nick, buf);
608 				tmp->not = 1;
609 				new_free(&(tmp->nick));
610 				new_free(&(tmp->stuff));
611 				wait_new_free((u_char **) &tmp);
612 				if (hook->list == (Hook *) 0)
613 				{
614 					if ((hook = (NumericList *) remove_from_list((List **) &numeric_list, buf)) != NULL)
615 					{
616 						new_free(&(hook->name));
617 						new_free(&hook);
618 					}
619 				}
620 				return;
621 			}
622 		}
623 		else
624 		{
625 			for (tmp = hook->list; tmp; tmp = next)
626 			{
627 				next = tmp->next;
628 				tmp->not = 1;
629 				new_free(&(tmp->nick));
630 				new_free(&(tmp->stuff));
631 				wait_new_free((u_char **) &tmp);
632 			}
633 			hook->list = (Hook *) 0;
634 			if (!quiet)
635 				say("The %s list is empty", buf);
636 			return;
637 		}
638 	}
639 	if (quiet)
640 		return;
641 	if (nick)
642 		say("\"%s\" is not on the %s list", nick, buf);
643 	else
644 		say("The %s list is empty", buf);
645 }
646 
647 #ifdef	INCLUDE_UNUSED_FUNCTIONS
648 static  void
649 flush_on_hooks()
650 {
651         int	x;
652         int	old_display = window_display;
653 
654         window_display = 0;
655         for (x = 100 ; x < 999; x++)
656                 remove_numeric_hook(x, (u_char *) 0, 1, x, 0);
657         for (x = 0 ; x < NUMBER_OF_LISTS; x++)
658                 remove_hook(x, (u_char *) 0, 1, x, 0);
659         window_display = old_display;
660 }
661 #endif /* INCLUDE_UNUSED_FUNCTIONS */
662 
663 extern	void
664 remove_hook(which, nick, server, sernum, quiet)
665 	int	which;
666 	u_char	*nick;
667 	int	server,
668 		sernum,
669 		quiet;
670 {
671 	Hook	*tmp,
672 		*next;
673 
674 	if (which < 0)
675 	{
676 		remove_numeric_hook(-which, nick, server, sernum, quiet);
677 		return;
678 	}
679 	if (nick)
680 	{
681 		setup_struct((server == -1) ? -1 : (server & ~HS_NOGENERIC),
682 			sernum-1, sernum, 0);
683 		if ((tmp = (Hook *) remove_from_list_ext((List **) &hook_functions[which].list, nick, Add_Remove_Check)) != NULL)
684 		{
685 			if (!quiet)
686 				say("\"%s\" removed from %s list", nick, hook_functions[which].name);
687 			tmp->not = 1;
688 			new_free(&(tmp->nick));
689 			new_free(&(tmp->stuff));
690 			wait_new_free((u_char **) &tmp);
691 		}
692 		else if (!quiet)
693 			say("\"%s\" is not on the %s list", nick, hook_functions[which].name);
694 	}
695 	else
696 	{
697 		for(tmp = hook_functions[which].list; tmp; tmp=next)
698 		{
699 			next = tmp->next;
700 			tmp->not = 1;
701 			new_free(&(tmp->nick));
702 			new_free(&(tmp->stuff));
703 			wait_new_free((u_char **) &tmp);
704 		}
705 		hook_functions[which].list = (Hook *) 0;
706 		if (!quiet)
707 			say("The %s list is empty", hook_functions[which].name);
708 	}
709 }
710 
711 /* on: The ON command */
712 /*ARGSUSED*/
713 void
714 on(command, args, subargs)
715 	u_char	*command,
716 		*args,
717 		*subargs;
718 {
719 	u_char	*func,
720 		*nick,
721 		*serial,
722 		*cmd = (u_char *) 0;
723 	/* int noisy = NORMAL, not = 0, do_remove = 0, -not used */
724 	int	noisy,
725 		not,
726 		server,
727 		sernum,
728 		do_remove,
729 		which = 0,
730 		cnt,
731 		i;
732 	size_t	len;
733 
734 	if (get_int_var(NOVICE_VAR) && !load_depth)
735 	{
736 	    yell("*** You may not type ON commands when you have the NOVICE");
737 	    yell("*** variable set to ON. Some ON commands may cause a");
738 	    yell("*** security breach on your machine, or enable another");
739 	    yell("*** user to control your IRC session. Read the help files");
740 	    yell("*** in /HELP ON before using ON");
741 	    return;
742 	}
743 	if ((func = next_arg(args, &args)) != NULL)
744 	{
745 		if (*func == '#')
746 		{
747 			if (!(serial = next_arg(args, &args)))
748 			{
749 				say("No serial number specified");
750 				return;
751 			}
752 			sernum = my_atoi(serial);
753 			func++;
754 		}
755 		else
756 			sernum = 0;
757 		switch (*func)
758 		{
759 		case '&':
760 			server = from_server;
761 			func++;
762 			break;
763 		case '@':
764 			server = from_server|HS_NOGENERIC;
765 			func++;
766 			break;
767 		default:
768 			server = -1;
769 			break;
770 		}
771 		switch (*func)
772 		{
773 		case '-':
774 			noisy = QUIET;
775 			func++;
776 			break;
777 		case '^':
778 			noisy = SILENT;
779 			func++;
780 			break;
781 		case '+':
782 			noisy = NOISY;
783 			func++;
784 			break;
785 		default:
786 			noisy = NORMAL;
787 			break;
788 		}
789 		if ((len = my_strlen(func)) == 0)
790 		{
791 			say("You must specify an event type!");
792 			return;
793 		}
794 		malloc_strcpy(&cmd, func);
795 		upper(cmd);
796 		for (cnt = 0, i = 0; i < NUMBER_OF_LISTS; i++)
797 		{
798 			if (!my_strncmp(cmd, hook_functions[i].name, len))
799 			{
800 				if (my_strlen(hook_functions[i].name) == len)
801 				{
802 					cnt = 1;
803 					which = i;
804 					break;
805 				}
806 				else
807 				{
808 					cnt++;
809 					which = i;
810 				}
811 			}
812 			else if (cnt)
813 				break;
814 		}
815 		if (cnt == 0)
816 		{
817 			if (is_number(cmd))
818 			{
819 				which = my_atoi(cmd);
820 				if ((which < 0) || (which > 999))
821 				{
822 					say("Numerics must be between 001 and 999");
823 					goto out;
824 				}
825 				which = -which;
826 			}
827 			else
828 			{
829 				say("No such ON function: %s", func);
830 				goto out;
831 			}
832 		}
833 		else if (cnt > 1)
834 		{
835 			say("Ambiguous ON function: %s", func);
836 			goto out;
837 		}
838 		else
839 		{
840 			if (get_int_var(INPUT_PROTECTION_VAR) && !my_strnicmp(UP(hook_functions[which].name), UP("INPUT"), 5))
841 			{
842 				say("You cannot use /ON INPUT with INPUT_PROTECTION set");
843 				say("Please read /HELP ON INPUT, and /HELP SET INPUT_PROTECTION");
844 				goto out;
845 			}
846 		}
847 		do_remove = 0;
848 		not = 0;
849 		switch (*args)
850 		{
851 		case '-':
852 			do_remove = 1;
853 			args++;
854 			break;
855 		case '^':
856 			not = 1;
857 			args++;
858 			break;
859 		}
860 		if ((nick = new_next_arg(args, &args)) != NULL)
861 		{
862 			if (which < 0)
863 				nick = fill_it_out(nick, 1);
864 			else
865 				nick = fill_it_out(nick,
866 					hook_functions[which].params);
867 			if (do_remove)
868 			{
869 				if (my_strlen(nick) == 0)
870 					say("No expression specified");
871 				else
872 					remove_hook(which, nick, server,
873 						sernum, 0);
874 			}
875 			else
876 			{
877 				if (not)
878 					args = empty_string;
879 				if (*nick)
880 				{
881 					if (*args == LEFT_BRACE)
882 					{
883 						u_char	*ptr;
884 
885 						ptr = MatchingBracket(++args,
886 								LEFT_BRACE, RIGHT_BRACE);
887 						if (!ptr)
888 						{
889 							say("Unmatched brace in ON");
890 							new_free(&nick);
891 							goto out;
892 						}
893 						else if (ptr[1])
894 						{
895 							say("Junk after closing brace in ON");
896 							new_free(&nick);
897 							goto out;
898 						}
899 						else
900 							*ptr = '\0';
901 					}
902 					add_hook(which, nick, args, noisy, not, server, sernum);
903 					if (which < 0)
904 						say("On %3.3u from \"%s\" do %s [%s] <%d>",
905 						    -which, nick, (not ? (u_char *) "nothing" : args),
906 						    noise_level[noisy], sernum);
907 					else
908 						say("On %s from \"%s\" do %s [%s] <%d>",
909 							hook_functions[which].name, nick,
910 							(not ? (u_char *) "nothing" : args),
911 							noise_level[noisy], sernum);
912 				}
913 			}
914 			new_free(&nick);
915 		}
916 		else
917 		{
918 			if (do_remove)
919 				remove_hook(which, (u_char *) 0, server,
920 					sernum, 0);
921 			else
922 			{
923 				if (which < 0)
924 				{
925 					if (show_numeric_list(-which) == 0)
926 						say("The %3.3u list is empty.",
927 							-which);
928 				}
929 				else if (show_list(which) == 0)
930 					say("The %s list is empty.",
931 						hook_functions[which].name);
932 			}
933 		}
934 	}
935 	else
936 	{
937 		int	total = 0;
938 
939 		say("ON listings:");
940 		for (which = 0; which < NUMBER_OF_LISTS; which++)
941 			total += show_list(which);
942 		total += show_numeric_list(0);
943 		if (total == 0)
944 			say("All ON lists are empty.");
945 	}
946 out:
947 	new_free(&cmd);
948 }
949 
950 static	void
951 write_hook(fp, hook, name)
952 	FILE	*fp;
953 	Hook	*hook;
954 	char	*name;
955 {
956 	char	*stuff = (char *) 0;
957 
958 	if (hook->server!=-1)
959 		return;
960 	switch (hook->noisy)
961 	{
962 	case SILENT:
963 		stuff = "^";
964 		break;
965 	case QUIET:
966 		stuff = "-";
967 		break;
968 	case NORMAL:
969 		stuff = CP(empty_string);
970 		break;
971 	case NOISY:
972 		stuff = "+";
973 		break;
974 	}
975 	if (hook->sernum)
976 		fprintf(fp, "ON #%s%s %d \"%s\"", stuff, name, hook->sernum,
977 			hook->nick);
978 	else
979 		fprintf(fp, "ON %s%s \"%s\"", stuff, name, hook->nick);
980 	fprintf(fp, " %s\n", hook->stuff);
981 }
982 
983 /*
984  * save_hooks: for use by the SAVE command to write the hooks to a file so it
985  * can be interpreted by the LOAD command
986  */
987 void
988 save_hooks(fp, do_all)
989 	FILE	*fp;
990 	int	do_all;
991 {
992 	Hook	*list;
993 	NumericList *numeric;
994 	int	which;
995 
996 	for (which = 0; which < NUMBER_OF_LISTS; which++)
997 	{
998 		for (list = hook_functions[which].list; list; list = list->next)
999 			if (!list->global || do_all)
1000 				write_hook(fp,list, hook_functions[which].name);
1001 	}
1002 	for (numeric = numeric_list; numeric; numeric = numeric->next)
1003 	{
1004 		for (list = numeric->list; list; list = list->next)
1005 			if (!list->global)
1006 				write_hook(fp, list, numeric->name);
1007 	}
1008 }
1009