1 /*
2  * who.c -- The WHO queue.  The ISON queue.  The USERHOST queue.
3  *
4  * Copyright 1996, 2003 EPIC Software Labs.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notices, the above paragraph (the one permitting redistribution),
14  *    this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The names of the author(s) may not be used to endorse or promote
17  *    products derived from this software without specific prior written
18  *    permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #define NEED_SERVER_LIST
34 #include "irc.h"
35 #include "commands.h"
36 #include "ircaux.h"
37 #include "who.h"
38 #include "server.h"
39 #include "window.h"
40 #include "vars.h"
41 #include "hook.h"
42 #include "output.h"
43 #include "numbers.h"
44 #include "parse.h"
45 #include "ifcmd.h"
46 #include "names.h"
47 #include "words.h"
48 #include "reg.h"
49 #include "log.h"
50 #include "timer.h"
51 #include "alias.h"
52 
53 /* XXXX - only debugging stuff for adm.  Remove later */
54 static	FILE *	who_log = NULL;
55 static	int	who_global_refnum = 0;
56 static	char	who_timeref[] = "WHOTIM";
57 
58 
59 static int	who_queue_debug (void *unused);
60 
WHO_DEBUG(const char * format,...)61 static void	WHO_DEBUG (const char *format, ...)
62 {
63 	va_list	args;
64 
65 	if (who_log == NULL && (x_debug & DEBUG_WHO_QUEUE))
66 	{
67 		add_timer(1, who_timeref, 5, -1, who_queue_debug,
68 				NULL, NULL, GENERAL_TIMER, -1, 0, 0);
69 		do_log(1, "who.log", &who_log);
70 	}
71 
72 	if (who_log)
73 	{
74 		time_t	t;
75 		char	my_buffer[256];
76 		struct tm *ugh;
77 
78 		time(&t);
79 		ugh = localtime(&t);
80 		strftime(my_buffer, 255, "%H:%M:%S ", ugh);
81 		fprintf(who_log, "%s", my_buffer);
82 
83 		va_start(args, format);
84 		vfprintf(who_log, format, args);
85 		fputc('\n', who_log);
86 		fflush(who_log);
87 		va_end(args);
88 	}
89 
90 	if (who_log && !(x_debug & DEBUG_WHO_QUEUE))
91 	{
92 		remove_timer(who_timeref);
93 		do_log(0, "who.log", &who_log);
94 	}
95 }
96 
97 
98 /*
99  *
100  *
101  *
102  * 				WHO QUEUE
103  *
104  *
105  *
106  */
107 
108 /* flags used by who queue */
109 #define WHO_OPS		0x0001
110 #define WHO_NAME	0x0002
111 #define WHO_ZERO	0x0004
112 #define WHO_CHOPS	0x0008
113 #define WHO_FILE	0x0010
114 #define WHO_HOST	0x0020
115 #define WHO_SERVER	0x0040
116 #define	WHO_HERE	0x0080
117 #define	WHO_AWAY	0x0100
118 #define	WHO_NICK	0x0200
119 #define	WHO_LUSERS	0x0400
120 #define	WHO_REAL	0x0800
121 #define WHO_NOCHOPS	0x1000
122 
123 /*
124  * This one requires an explanation.  As of u2.10.04, operators have to
125  * specify a specific special flag to the WHO protocol command in order to
126  * see invisible users.  This is so they can be all paranoid and pedantic
127  * about just who is looking at invisible users and when.  Sheesh.  This
128  * flag has *no effect* in any other context than this.
129  */
130 #define WHO_INVISIBLE	0x2000
131 #define WHO_OPERSPY	0x4000
132 
133 #define S(x) (((x) != NULL) ? (x) : empty_string)
134 
who_item_full_desc(WhoEntry * item)135 static char *who_item_full_desc (WhoEntry *item)
136 {
137 	static char retval[10240];
138 
139 	if (item)
140 	    snprintf(retval, sizeof retval,
141 		"refnum [%d] "
142 		"dirty [%d], piggyback [%d], unet [%d], unet_args [%s], "
143 		"dalnet [%d], dalnet_args [%s], who_mask [%d], "
144 		"who_target [%s], who_name [%s], who_host [%s], "
145 		"who_server [%s], who_nick [%s], who_real [%s], "
146 		"who_stuff [%s], who_end [%s], next [%p], line [%p], "
147 		"end [%p], requested ["INTMAX_FORMAT"], dirty ["INTMAX_FORMAT"]",
148 			item->refnum,
149 			item->dirty, item->piggyback, item->undernet_extended,
150 				S(item->undernet_extended_args),
151 			item->dalnet_extended, S(item->dalnet_extended_args),
152 				item->who_mask,
153 			S(item->who_target), S(item->who_name),
154 				S(item->who_host),
155 			S(item->who_server), S(item->who_nick),
156 				S(item->who_real),
157 			S(item->who_stuff), S(item->who_end),
158 				item->next, item->line,
159 			item->end,
160 			(intmax_t) item->request_time.tv_sec,
161 			(intmax_t) item->dirty_time.tv_sec);
162 	else
163 	    snprintf(retval, sizeof retval, "<none>");
164 
165 	return retval;
166 }
167 
who_item_desc(WhoEntry * item)168 static char *who_item_desc (WhoEntry *item)
169 {
170 	static char retval[10240];
171 
172 	if (item)
173 	    snprintf(retval, sizeof retval,
174 		"refnum [%d] dirty [%d], piggyback [%d], next [%p]",
175 			item->refnum,
176 			item->dirty, item->piggyback, item->next);
177 	else
178 	    snprintf(retval, sizeof retval, "<none>");
179 
180 	return retval;
181 }
182 
who_queue_top(int refnum)183 static WhoEntry *who_queue_top (int refnum)
184 {
185 	Server *s;
186 
187 	if (!(s = get_server(refnum)))
188 		return NULL;
189 
190 	WHO_DEBUG("Returning top of who queue for server %d [%s]",
191 			refnum, who_item_desc(s->who_queue));
192 	return s->who_queue;
193 }
194 
195 /*
196  * This is tricky -- this doesnt get the LAST one, it gets the
197  * next to the last one.  Why?  Because the LAST one is the one
198  * asking, and they want to know who is LAST (before them)
199  * So it sucks.  Sue me.
200  */
who_previous_query(int refnum,WhoEntry * me)201 static WhoEntry *who_previous_query (int refnum, WhoEntry *me)
202 {
203 	WhoEntry *what;
204 	Server *s;
205 
206 	if (!get_server(refnum))
207 		return NULL;
208 
209 	what = who_queue_top(refnum);
210 	while (what && what->next != me)
211 		what = what->next;
212 
213 	WHO_DEBUG("Returning item previous to [%d(%d)] - [%s]",
214 			me->refnum, refnum, who_item_desc(what));
215 	return what;
216 }
217 
who_queue_add(int refnum,WhoEntry * item)218 static void who_queue_add (int refnum, WhoEntry *item)
219 {
220 	WhoEntry *bottom;
221 	Server *s;
222 
223 	if (!(s = get_server(refnum)))
224 		return;
225 
226 	bottom = who_queue_top(refnum);
227 	while (bottom && bottom->next)
228 		bottom = bottom->next;
229 
230 	if (!bottom)
231 		s->who_queue = item;
232 	else
233 		bottom->next = item;
234 
235 	get_time(&item->request_time);
236 
237 	WHO_DEBUG("Adding item to who queue for server %d [%s]",
238 			refnum, who_item_full_desc(item));
239 	return;
240 }
241 
delete_who_item(WhoEntry * save)242 static void delete_who_item (WhoEntry *save)
243 {
244 	Timeval t;
245 	double	d;
246 
247 	get_time(&t);
248 	d = time_diff(save->request_time, t);
249 	WHO_DEBUG("Final deletion of item [%s], alive for [%f] seconds",
250 			who_item_full_desc(save), d);
251 
252 	new_free(&save->who_target);
253 	new_free(&save->who_name);
254 	new_free(&save->who_host);
255 	new_free(&save->who_server);
256 	new_free(&save->who_nick);
257 	new_free(&save->who_real);
258 	new_free(&save->who_stuff);
259 	new_free(&save->who_end);
260 	new_free((char **)&save);
261 }
262 
who_queue_pop(int refnum)263 static void who_queue_pop (int refnum)
264 {
265 	WhoEntry *save;
266 	int	piggyback;
267 	Server *s;
268 
269 	WHO_DEBUG("Popping first item off of server [%d]", refnum);
270 
271 	if (!(s = get_server(refnum)))
272 		return;
273 
274 	do
275 	{
276 		if (!(save = s->who_queue))
277 			break;
278 
279 		piggyback = save->piggyback;
280 		s->who_queue = save->next;
281 		delete_who_item(save);
282 	}
283 	while (piggyback);
284 
285 	if (s->who_queue == NULL)
286 		WHO_DEBUG("WHO QUEUE for server [%d] is now empty", refnum);
287 	return;
288 }
289 
290 
get_new_who_entry(void)291 static WhoEntry *get_new_who_entry (void)
292 {
293 	WhoEntry *new_w = (WhoEntry *)new_malloc(sizeof(WhoEntry));
294 	new_w->refnum = ++who_global_refnum;
295 	new_w->dirty = 0;
296 	new_w->piggyback = 0;
297 	new_w->who_mask = 0;
298 	new_w->who_target = NULL;
299 	new_w->who_host = NULL;
300 	new_w->who_name = NULL;
301 	new_w->who_server = NULL;
302 	new_w->who_nick = NULL;
303 	new_w->who_real = NULL;
304 	new_w->who_stuff = NULL;
305 	new_w->who_end = NULL;
306 	new_w->next = NULL;
307 	new_w->undernet_extended = 0;
308 	new_w->undernet_extended_args = NULL;
309 	new_w->dalnet_extended = 0;
310 	new_w->dalnet_extended_args = NULL;
311 	new_w->request_time.tv_sec = 0;
312 	new_w->request_time.tv_usec = 0;
313 	new_w->dirty_time.tv_sec = 0;
314 	new_w->dirty_time.tv_usec = 0;
315 
316 	WHO_DEBUG("Creating new who item with refnum [%d]", new_w->refnum);
317 	return new_w;
318 }
319 
who_queue_list(int refnum)320 static void who_queue_list (int refnum)
321 {
322 	WhoEntry *item;
323 	int count = 0;
324 	Server *s;
325 
326 	WHO_DEBUG("Listing queue for server [%d]", refnum);
327 
328 	if (!(s = get_server(refnum)))
329 		return;
330 
331 	for (item = s->who_queue; item; item = item->next)
332 	{
333 		yell("[%d] %s", count, who_item_full_desc(item));
334 		WHO_DEBUG("[%d] %s", count, who_item_full_desc(item));
335 		count++;
336 	}
337 }
338 
who_queue_flush(int refnum)339 static void who_queue_flush (int refnum)
340 {
341 	Server *s;
342 
343 	WHO_DEBUG("Flushing who queue for server [%d]",  refnum);
344 
345 	if (!(s = get_server(refnum)))
346 		return;
347 
348 	while (s->who_queue)
349 		who_queue_pop(refnum);
350 
351 	yell("Who queue for server [%d] purged", refnum);
352 	WHO_DEBUG("done");
353 }
354 
355 
356 /*
357  * who: the /WHO command. Parses the who switches and sets the who_mask and
358  * whoo_stuff accordingly.  Who_mask and who_stuff are used in whoreply() in
359  * parse.c
360  */
BUILT_IN_COMMAND(whocmd)361 BUILT_IN_COMMAND(whocmd)
362 {
363 	whobase(from_server, args, NULL, NULL);
364 }
365 
366 
367 /*
368  * whobase: What does all the work.
369  */
whobase(int refnum,char * args,void (* line)(int,const char *,const char *,const char **),void (* end)(int,const char *,const char *,const char **))370 void 	whobase (int refnum, char *args, void (*line) (int, const char *, const char *, const char **), void (*end) (int, const char *, const char *, const char **))
371 {
372 	char *		arg;
373 	const char *	channel = NULL;
374 	int		no_args = 1,
375 			len;
376 	WhoEntry *	new_w, *old;
377 
378 	/* Maybe should output a warning? */
379 	if (!is_server_registered(refnum))
380 	{
381 		WHO_DEBUG("WHOBASE: server [%d] is not connected", refnum);
382 		return;
383 	}
384 
385 	new_w = get_new_who_entry();
386 	new_w->line = line;
387 	new_w->end = end;
388 
389 	while ((arg = next_arg(args, &args)) != NULL)
390 	{
391 	    no_args = 0;
392 
393 	    if (*arg == '-' || *arg == '/')
394 	    {
395 		lower(arg);
396 		arg++;
397 		if ((len = strlen(arg)) == 0)
398 		{
399 			say("Unknown or missing flag");
400 			WHO_DEBUG("WHOBASE: empty argument. punting.");
401 			delete_who_item(new_w);
402 			return;
403 		}
404 
405 		else if (!strncmp(arg, "away", MAX(len, 1)))
406 		{
407 			new_w->who_mask |= WHO_AWAY;
408 			WHO_DEBUG("WHOBASE: Setting WHO_AWAY flag");
409 		}
410 		else if (!strncmp(arg, "chops", MAX(len, 2)))
411 		{
412 			new_w->who_mask |= WHO_CHOPS;
413 			WHO_DEBUG("WHOBASE: Setting WHO_CHOPS flag");
414 		}
415 		else if (!strncmp(arg, "diagnose", MAX(len, 1)))
416 		{
417 			WHO_DEBUG("WHOBASE: Listing the who queue");
418 			who_queue_list(refnum);
419 			delete_who_item(new_w);
420 			return;
421 		}
422 		else if (!strncmp(arg, "dx", MAX(len, 2)))
423 		{
424 			new_w->dalnet_extended = 1;
425 			new_w->dalnet_extended_args = new_next_arg(args, &args);
426 			channel = args;		/* Grab the rest of args */
427 			WHO_DEBUG("WHOBASE: setting -dx flag [%s]", new_w->dalnet_extended_args, channel);
428 			args = NULL;
429 		}
430 		else if (!strncmp(arg, "end", MAX(len, 3)))
431 		{
432 			char *stuff;
433 
434 			if ((stuff = next_expr(&args, '{')))
435 				malloc_strcpy(&new_w->who_end, stuff);
436 			else
437 				say("Need {...} argument for -END argument.");
438 			WHO_DEBUG("WHOBASE: setting who_end [%s]", new_w->who_end);
439 		}
440 		else if (!strncmp(arg, "flush", MAX(len, 1)))
441 		{
442 			WHO_DEBUG("WHOBASE: flushing who queue [%d]", refnum);
443 			who_queue_flush(refnum);
444 			delete_who_item(new_w);
445 			return;
446 		}
447 	 	else if (!strncmp(arg, "here", MAX(len, 2)))
448 		{
449 			WHO_DEBUG("WHOBASE: setting WHO_HERE flag");
450 			new_w->who_mask |= WHO_HERE;
451 		}
452 		else if (!strncmp(arg, "hosts", MAX(len, 2)))
453 		{
454 			if ((arg = next_arg(args, &args)) == NULL)
455 			{
456 				WHO_DEBUG("WHOBASE: -HOST missing argument");
457 				say("WHO -HOST: missing argument");
458 				delete_who_item(new_w);
459 				return;
460 			}
461 
462 			new_w->who_mask |= WHO_HOST;
463 			malloc_strcpy(&new_w->who_host, arg);
464 			channel = new_w->who_host;
465 			WHO_DEBUG("WHOBASE: Setting -HOST argument [%s]",
466 					new_w->who_host);
467 		}
468 
469 		else if (!strncmp(arg, "line", MAX(len, 4)))
470 		{
471 			char *stuff;
472 
473 			if ((stuff = next_expr(&args, '{')))
474 				malloc_strcpy(&new_w->who_stuff, stuff);
475 			else
476 				say("Need {...} argument for -LINE argument.");
477 			WHO_DEBUG("WHOBASE: setting -line [%s]", new_w->who_end);
478 		}
479 		else if (!strncmp(arg, "literal", MAX(len, 3)))
480 		{
481 			/* Hope for the best */
482 			new_w->who_mask = 0;	/* For safety reasons */
483 			new_w->dalnet_extended = 0;
484 			new_w->undernet_extended = 0;
485 			new_free(&new_w->who_stuff);
486 			new_free(&new_w->who_end);
487 
488 			WHO_DEBUG("WHOBASE: Doing -LITERAL [%s]", args);
489 			who_queue_add(refnum, new_w);
490 
491 			send_to_aserver(refnum, "WHO %s", args);
492 			return;
493 		}
494 		else if (!strncmp(arg, "lusers", MAX(len, 2)))
495 		{
496 			new_w->who_mask |= WHO_LUSERS;
497 			WHO_DEBUG("WHOBASE: Setting WHO_LUSERS flag", args);
498 		}
499 		else if (!strncmp(arg, "name", MAX(len, 2)))
500 		{
501 			if ((arg = next_arg(args, &args)) == NULL)
502 			{
503 				WHO_DEBUG("WHOBASE: -NAME missing argument");
504 				say("WHO -NAME: missing arguement");
505 				delete_who_item(new_w);
506 				return;
507 			}
508 
509 			new_w->who_mask |= WHO_NAME;
510 			malloc_strcpy(&new_w->who_name, arg);
511 			channel = new_w->who_name;
512 			WHO_DEBUG("WHOBASE: Setting -NAME argument [%s]",
513 					new_w->who_name);
514 		}
515 		else if (!strncmp(arg, "nick", MAX(len, 2)))
516 		{
517 			if ((arg = next_arg(args, &args)) == NULL)
518 			{
519 				WHO_DEBUG("WHOBASE: -NICK missing argument");
520 				say("WHO -NICK: missing arguement");
521 				delete_who_item(new_w);
522 				return;
523 			}
524 
525 			new_w->who_mask |= WHO_NICK;
526 			malloc_strcpy(&new_w->who_nick, arg);
527 			channel = new_w->who_nick;
528 			WHO_DEBUG("WHOBASE: Setting -NICK argument [%s]",
529 					new_w->who_nick);
530 		}
531 		else if (!strncmp(arg, "nochops", MAX(len, 2)))
532 		{
533 			new_w->who_mask |= WHO_NOCHOPS;
534 			WHO_DEBUG("WHOBASE: Setting WHO_NOCHOPS flag");
535 		}
536 		else if (!strncmp(arg, "oper", MAX(len, 1)))
537 		{
538 			new_w->who_mask |= WHO_OPS;
539 			WHO_DEBUG("WHOBASE: Setting WHO_OPS flag");
540 		}
541 		else if (!strncmp(arg, "operspy", MAX(len, 5)))
542 		{
543 			new_w->who_mask |= WHO_OPERSPY;
544 			WHO_DEBUG("WHOBASE: Setting WHO_OPERSPY flag");
545 		}
546 		else if (!strncmp(arg, "realname", MAX(len, 1)))
547 		{
548 			if ((arg = next_arg(args, &args)) == NULL)
549 			{
550 				WHO_DEBUG("WHOBASE: -REALNAME missing argument");
551 				say("WHO -REALNAME: missing arguement");
552 				delete_who_item(new_w);
553 				return;
554 			}
555 
556 			new_w->who_mask |= WHO_REAL;
557 			malloc_strcpy(&new_w->who_real, arg);
558 			channel = new_w->who_real;
559 			WHO_DEBUG("WHOBASE: Setting -REALNAME [%s]", new_w->who_real);
560 		}
561 		else if (!strncmp(arg, "servers", MAX(len, 1)))
562 		{
563 			if ((arg = next_arg(args, &args)) == NULL)
564 			{
565 				WHO_DEBUG("WHOBASE: -SERVERS missing argument");
566 				say("WHO -SERVER: missing arguement");
567 				delete_who_item(new_w);
568 				return;
569 			}
570 
571 			new_w->who_mask |= WHO_SERVER;
572 			malloc_strcpy(&new_w->who_server, arg);
573 			channel = new_w->who_server;
574 			WHO_DEBUG("WHOBASE: Setting -SERVERS [%s]", new_w->who_server);
575 		}
576 		else if (!strncmp(arg, "u-i", MAX(len, 3)))
577 		{
578 			new_w->who_mask |= WHO_INVISIBLE;
579 			WHO_DEBUG("WHOBASE: Setting WHO_INVISIBLE flag");
580 		}
581 		else if (!strncmp(arg, "ux", MAX(len, 2)))
582 		{
583 			new_w->undernet_extended = 1;
584 			new_w->undernet_extended_args = args;
585 			args = NULL;
586 			WHO_DEBUG("WHOBASE: Setting undernet flag [%s]",
587 					new_w->undernet_extended_args);
588 		}
589 		else
590 		{
591 			WHO_DEBUG("WHOBASE: Unknown flag [%s]", arg);
592 			say("Unknown or missing flag");
593 			delete_who_item(new_w);
594 			return;
595 		}
596 	    }
597 	    else if (strcmp(arg, "*") == 0)
598 	    {
599 		channel = get_echannel_by_refnum(0);
600 		if (!channel || !*channel)
601 		{
602 			WHO_DEBUG("WHOBASE: WHO *, but not on channel");
603 			say("You are not on a channel.  "
604 			    "Use /WHO ** to see everybody.");
605 			delete_who_item(new_w);
606 			return;
607 		}
608 		else
609 			WHO_DEBUG("WHOBASE: WHO * -> WHO %s", channel);
610 	    }
611 	    else
612 	    {
613 		channel = arg;
614 		WHO_DEBUG("WHOBASE: WHO %s", channel);
615 	    }
616 	}
617 
618 	if (no_args)
619 	{
620 		WHO_DEBUG("WHOBASE: No arguments");
621 		say("No argument specified");
622 		delete_who_item(new_w);
623 		return;
624 	}
625 
626 	if (!channel && (new_w->who_mask & WHO_OPS))
627 	{
628 		channel = "*.*";
629 		WHO_DEBUG("WHOBASE: Fallback to WHO %s", channel);
630 	}
631 	new_w->who_target = malloc_strdup(channel);
632 	WHO_DEBUG("WHOBASE: Target is [%s]", new_w->who_target);
633 
634 	who_queue_add(refnum, new_w);
635 
636 	/*
637 	 * Check to see if we can piggyback
638 	 */
639 	old = who_previous_query(refnum, new_w);
640 	if (old && !old->dirty && old->who_target && channel &&
641 		!strcmp(old->who_target, channel))
642 	{
643 		old->piggyback = 1;
644 		WHO_DEBUG("WHOBASE: Piggybacking onto refnum [%d]",
645 					old->refnum);
646 		if (x_debug & DEBUG_OUTBOUND)
647 			yell("Piggybacking this WHO onto last one.");
648 	}
649 	else if (new_w->undernet_extended)
650 	{
651 		WHO_DEBUG("UNET QUERY: [%d] WHO %s %s%s%s",
652 			refnum, new_w->who_target,
653 			(new_w->who_mask & WHO_OPS) ?  "o" : "",
654 			(new_w->who_mask & WHO_INVISIBLE) ? "x" : "",
655 			new_w->undernet_extended_args ?
656 				new_w->undernet_extended_args : "");
657 
658 		send_to_aserver(refnum, "WHO %s %s%s%s",
659 			new_w->who_target,
660 			(new_w->who_mask & WHO_OPS) ?  "o" : "",
661 			(new_w->who_mask & WHO_INVISIBLE) ? "x" : "",
662 			new_w->undernet_extended_args ?
663 				new_w->undernet_extended_args : "");
664 	}
665 	else if (new_w->dalnet_extended)
666 	{
667 		WHO_DEBUG("DALNET QUERY: [%d] WHO %s %s",
668 			refnum, new_w->dalnet_extended_args,
669 			new_w->who_target);
670 
671 		send_to_aserver(refnum, "WHO %s %s",
672 			new_w->dalnet_extended_args,
673 			new_w->who_target);
674 	}
675 	else if (new_w->who_mask & WHO_OPERSPY)
676 	{
677 		WHO_DEBUG("OPERSPY QUERY: [%d] OPERSPY WHO %s %s%s",
678 			refnum, new_w->who_target,
679 			(new_w->who_mask & WHO_OPS) ?  "o" : "",
680 			(new_w->who_mask & WHO_INVISIBLE) ? "x" : "");
681 
682 		send_to_aserver(refnum, "OPERSPY WHO %s %s%s",
683 			new_w->who_target,
684 			(new_w->who_mask & WHO_OPS) ?  "o" : "",
685 			(new_w->who_mask & WHO_INVISIBLE) ? "x" : "");
686 	}
687 	else
688 	{
689 		WHO_DEBUG("STD WHO: [%d] WHO %s %s%s",
690 			refnum, new_w->who_target,
691 			(new_w->who_mask & WHO_OPS) ?  "o" : "",
692 			(new_w->who_mask & WHO_INVISIBLE) ? "x" : "");
693 
694 		send_to_aserver(refnum, "WHO %s %s%s",
695 			new_w->who_target,
696 			(new_w->who_mask & WHO_OPS) ?  "o" : "",
697 			(new_w->who_mask & WHO_INVISIBLE) ? "x" : "");
698 	}
699 }
700 
whoreply(int refnum,const char * from,const char * comm,const char ** ArgList)701 void	whoreply (int refnum, const char *from, const char *comm, const char **ArgList)
702 {
703 static	char	format[50];
704 static	int	last_width = -1;
705 	int	ok = 1;
706 	const char	*channel,
707 			*user,
708 			*host,
709 			*server,
710 			*nick,
711 			*status,
712 			*ircname;
713 	char 	*name;
714 	WhoEntry *new_w = who_queue_top(refnum);
715 	int	l;
716 	char 	*extra = NULL;
717 
718 	if (!new_w)
719 	{
720 		WHO_DEBUG("WHOREPLY: server [%d] queue empty.", refnum);
721                 new_w = get_new_who_entry();
722                 new_w->line = NULL;
723                 new_w->end = NULL;
724                 new_w->who_target = malloc_strdup(star);
725                 who_queue_add(refnum, new_w);
726 	}
727 
728 	if (new_w->undernet_extended)
729 	{
730 		WHO_DEBUG("WHOREPLY: Server [%d], who refnum [%d], "
731 			"Unet request, Std reply.", refnum, new_w->refnum);
732 		yell("### You asked for an extended undernet request but "
733 			"didn't get one back. ###");
734 	}
735 
736 	/* Who replies always go to the current window. */
737 	l = message_from(new_w->who_target, LEVEL_OTHER);
738 
739 do
740 {
741 	/*
742 	 * We have recieved a reply to this query -- its too late to
743 	 * piggyback it now!
744 	 */
745 	if (new_w->dirty == 0)
746 	{
747 		WHO_DEBUG("WHOREPLY: Server [%d], who_refnum [%d]: "
748 				"Reply is now dirty", refnum, new_w->refnum);
749 		new_w->dirty = 1;
750 	}
751 	else
752 		WHO_DEBUG("WHOREPLY: Server [%d], who_refnum [%d]: Processing",
753 				refnum, new_w->refnum);
754 
755 	/*
756 	 * We dont always want to use this function.
757 	 * If another function is supposed to do the work for us,
758 	 * we yield to them.
759 	 */
760 	if (new_w->line)
761 	{
762 		WHO_DEBUG("WHOREPLY: Dispatching to callback", new_w->refnum);
763 		new_w->line(refnum, from, comm, ArgList);
764 		continue;
765 	}
766 
767 	if (last_width != get_int_var(CHANNEL_NAME_WIDTH_VAR))
768 	{
769 		if ((last_width = get_int_var(CHANNEL_NAME_WIDTH_VAR)) != 0)
770 		    snprintf(format, sizeof format,
771 				"%%-%u.%us %%-9s %%-3s %%s@%%s (%%s)",
772 					(unsigned) last_width,
773 					(unsigned) last_width);
774 		else
775 		    strlcpy(format, "%s\t%-9s %-3s %s@%s (%s)", sizeof format);
776 	}
777 
778 	if (!(channel = ArgList[0]))
779 		{ rfc1459_odd(from, comm, ArgList); break; }
780 	if (!(user    = ArgList[1]))
781 		{ rfc1459_odd(from, comm, ArgList); break; }
782 	if (!(host    = ArgList[2]))
783 		{ rfc1459_odd(from, comm, ArgList); break; }
784 	if (!(server  = ArgList[3]))
785 		{ rfc1459_odd(from, comm, ArgList); break; }
786 	if (!(nick    = ArgList[4]))
787 		{ rfc1459_odd(from, comm, ArgList); break; }
788 	if (!(status  = ArgList[5]))
789 		{ rfc1459_odd(from, comm, ArgList); break; }
790 	PasteArgs(ArgList, 6);
791 	if (!(ircname  = ArgList[6]))
792 		{ rfc1459_odd(from, comm, ArgList); break; }
793 	name = LOCAL_COPY(ircname);
794 
795 	if (*status == 'S')	/* this only true for the header WHOREPLY */
796 	{
797 		char buffer[1024];
798 
799 		channel = "Channel";
800 		snprintf(buffer, 1024, "%s %s %s %s %s %s %s", channel,
801 				nick, status, user, host, server, name);
802 
803 		if (new_w->who_stuff)
804 			;			/* munch it */
805 		else if (do_hook(WHO_LIST, "%s", buffer))
806 			put_it(format, channel, nick, status, user, host, name);
807 
808 		return;
809 	}
810 
811 	if (new_w && new_w->who_mask)
812 	{
813 		if (new_w->who_mask & WHO_HERE)
814 			ok = ok && (*status == 'H');
815 		if (new_w->who_mask & WHO_AWAY)
816 			ok = ok && (*status == 'G');
817 		if (new_w->who_mask & WHO_OPS)
818 			ok = ok && (*(status + 1) == '*');
819 		if (new_w->who_mask & WHO_LUSERS)
820 			ok = ok && (*(status + 1) != '*');
821 		if (new_w->who_mask & WHO_CHOPS)
822 			ok = ok && ((*(status + 1) == '@') ||
823 				    (*(status + 2) == '@') ||
824 				    (*(status + 3) == '@'));
825 		if (new_w->who_mask & WHO_NOCHOPS)
826 			ok = ok && ((*(status + 1) != '@') &&
827 				    (*(status + 2) != '@') &&
828 				    (*(status + 3) != '@'));
829 		if (new_w->who_mask & WHO_NAME)
830 			ok = ok && wild_match(new_w->who_name, user);
831 		if (new_w->who_mask & WHO_NICK)
832 			ok = ok && wild_match(new_w->who_nick, nick);
833 		if (new_w->who_mask & WHO_HOST)
834 			ok = ok && wild_match(new_w->who_host, host);
835 		if (new_w->who_mask & WHO_REAL)
836 		{
837 			char *copy;
838 
839 			/* First match, including the hopcount */
840 			ok = ok && wild_match(new_w->who_real, name);
841 
842 			/* Then remove the hopcount and try again. */
843 			copy = LOCAL_COPY(name);
844 			new_next_arg(name, &name);
845 			ok = ok && wild_match(new_w->who_real, name);
846 			name = copy;
847 		}
848 		if (new_w->who_mask & WHO_SERVER)
849 			ok = ok && wild_match(new_w->who_server, server);
850 	}
851 
852 	if (ok)
853 	{
854 		char buffer[1024];
855 
856 		snprintf(buffer, 1023, "%s %s %s %s %s %s %s", channel,
857 				nick, status, user, host, server, name);
858 
859 		if (new_w->who_stuff)
860 			call_lambda_command("WHO", new_w->who_stuff, buffer);
861 
862 		else if (do_hook(WHO_LIST, "%s", buffer))
863 		    if (do_hook(current_numeric, "%s", buffer))
864 			put_it(format, channel, nick, status, user, host, name);
865 	}
866 }
867 while (new_w->piggyback && (new_w = new_w->next));
868 
869 	WHO_DEBUG("WHOREPLY: Done processing who reply server [%d]", refnum);
870 	pop_message_from(l);
871 }
872 
873 /* Undernet's 354 numeric reply. */
xwhoreply(int refnum,const char * from,const char * comm,const char ** ArgList)874 void	xwhoreply (int refnum, const char *from, const char *comm, const char **ArgList)
875 {
876 	WhoEntry *new_w = who_queue_top(refnum);
877 	int	l;
878 
879 	if (!ArgList[0])
880 		{ rfc1459_odd(from, comm, ArgList); return; }
881 
882 	if (!new_w)
883 	{
884 		new_w = get_new_who_entry();
885 		new_w->line = NULL;
886 		new_w->end = NULL;
887 		new_w->who_target = malloc_strdup(star);
888 		new_w->undernet_extended = 1;
889 		who_queue_add(refnum, new_w);
890 	}
891 
892 	if (!new_w->undernet_extended)
893 		yell("### You got an extended undernet request back "
894 			"even though you didn't ask for one. ###");
895 
896 	/* Who replies always go to the current window */
897 	l = message_from(new_w->who_target, LEVEL_OTHER);
898 	PasteArgs(ArgList, 0);
899 	if (new_w->who_stuff)
900 		call_lambda_command("WHO", new_w->who_stuff, ArgList[0]);
901 	else if (do_hook(current_numeric, "%s", ArgList[0]))
902 		put_it("%s %s", banner(), ArgList[0]);
903 	pop_message_from(l);
904 }
905 
906 
who_end(int refnum,const char * from,const char * comm,const char ** ArgList)907 void	who_end (int refnum, const char *from, const char *comm, const char **ArgList)
908 {
909 	WhoEntry 	*new_w = who_queue_top(refnum);
910 	char 		buffer[1025];
911 	char		*target;
912 	int		l;
913 
914 	if (!ArgList[0])
915 		{ rfc1459_odd(from, comm, ArgList); return; }
916 
917 	if (!new_w || !new_w->who_target)
918 	{
919 		WHO_DEBUG("WHOEND: Server [%d], queue is empty.", refnum);
920 		return;
921 	}
922 
923 	target = malloc_strdup(ArgList[0]);
924 
925 	PasteArgs(ArgList, 0);
926 
927 	l = message_from(new_w->who_target, LEVEL_OTHER);
928 	do
929 	{
930 		/* Defer to another function, if neccesary.  */
931 		if (new_w->end)
932 			new_w->end(refnum, from, comm, ArgList);
933 		else
934 		{
935 			snprintf(buffer, 1024, "%s %s", from, ArgList[0]);
936 			if (new_w->who_end)
937 			    call_lambda_command("WHO_END", new_w->who_end, buffer);
938 			else
939 			    if (do_hook(current_numeric, "%s", buffer))
940 				put_it("%s %s", banner(), buffer);
941 		}
942 
943 		if (strchr(new_w->who_target, ',') && !strchr(target, ','))
944 		{
945 		    WHO_DEBUG("WHOEND: Removing one response [%s] from comma-set [%s]", target, new_w->who_target);
946 		    if (!remove_from_comma_list(new_w->who_target, target))
947 		    {
948 			WHO_DEBUG("WHOEND: Server [%d], end of who for refnum [%d]/[%s], (target was not included in comma-set: [%s])!", refnum, new_w->refnum, new_w->who_target, target);
949 			*new_w->who_target = 0;
950 		    }
951 		}
952 		else if (strcmp(target, new_w->who_target))
953 		{
954 			WHO_DEBUG("WHOEND: Server [%d], end of who for refnum [%d]/[%s], (target was wrong: [%s])!", refnum, new_w->refnum, new_w->who_target, target);
955 			*new_w->who_target = 0;
956 		}
957 		else
958 		{
959 			WHO_DEBUG("WHOEND: Server [%d], end of who for refnum [%d]/[%s]", refnum, new_w->refnum, new_w->who_target);
960 			*new_w->who_target = 0;
961 		}
962 	}
963 	while (new_w->piggyback && (new_w = new_w->next));
964 	pop_message_from(l);
965 
966 	if (new_w)
967 	{
968 	   if (!*new_w->who_target)
969 	   {
970 		WHO_DEBUG("Popping off for server [%d]", refnum);
971 		who_queue_pop(refnum);
972 	   }
973 	   else
974 	   {
975 		WHO_DEBUG("NOT POPPING OFF FOR SERVER [%d] BECAUSE THE TOP OF THE WHO QUEUE HAS [%s] LEFT IN THE TARGET LIST", refnum, new_w->who_target);
976 		yell("WHOEND: Caution -- not popping off who queue -- [%s] is left on target list", new_w->who_target);
977 	   }
978 	}
979 	/* XXX - There should probably be an else here, to cover when new_w had a piggyback, but there was nothing to piggyback on */
980 
981 	new_free(&target);
982 }
983 
984 /*
985  * Thanks be to the Dalnet coding team who, in their infinite wisdom,
986  * decided to start returning error codes, rather than empty who replies,
987  * for who requests that refered to channels or server or whatnot that
988  * do not exist.  So as to avoid corrupting the WHO queue, this function is
989  * called to give us the opportunity to clean up after any who requests that
990  * may be canceled because of the above lamage.
991  *
992  * Thanks be to the Dalnet coding team for returning error codes that do not
993  * tell you what the original request was, making it neigh unto impossible
994  * to correctly match up error codes to requests.  Gee whiz, you wouldn't
995  * think it would be that hard to get this right.
996  */
fake_who_end(int refnum,const char * from,const char * comm,const char * who_target)997 int	fake_who_end (int refnum, const char *from, const char *comm, const char *who_target)
998 {
999 	WhoEntry 	*new_w = who_queue_top(refnum);
1000 	int		l;
1001 
1002 	if (!new_w)
1003 		return 0;
1004 
1005 	/* Only honor these for dalnet extended requests. */
1006 	if (new_w->dalnet_extended == 0)
1007 		return 0;
1008 
1009 	if (who_target != NULL)
1010 	{
1011 		char *target;
1012 
1013 		target = LOCAL_COPY(who_target);
1014 		while (last_char(target) == ' ')
1015 			chop(target, 1);
1016 
1017 		/*
1018 		 * So 'who_target' isn't NULL here.  Make sure it's a
1019 		 * legitimate match to our current top of who request.
1020 		 */
1021 		if (strncmp(new_w->who_target, target, strlen(target)))
1022 			return 0;
1023 
1024 		/*who_target = target; */
1025 	}
1026 
1027 	l = message_from(new_w->who_target, LEVEL_OTHER);
1028 	do
1029 	{
1030 		/* Defer to another function, if neccesary.  */
1031 		if (new_w->end)
1032 		{
1033 			const char *fake_ArgList[3];
1034 
1035 			/* Fabricate a fake argument list */
1036 			fake_ArgList[0] = new_w->who_target;
1037 			fake_ArgList[1] = "fake_who_end";
1038 			fake_ArgList[2] = NULL;
1039 			new_w->end(refnum, from, comm, fake_ArgList);
1040 		}
1041 		else if (new_w->who_end)
1042 		{
1043 		    char	buffer[1025];
1044 
1045 		    snprintf(buffer, 1024, "%s %s",
1046 				from, new_w->who_target);
1047 		    call_lambda_command("WHO_END", new_w->who_end, buffer);
1048 		}
1049 	}
1050 	while (new_w->piggyback && (new_w = new_w->next));
1051 	pop_message_from(l);
1052 
1053 	who_queue_pop(refnum);
1054 	return 1;
1055 }
1056 
1057 
1058 /*
1059  *
1060  *
1061  *
1062  * 				ISON QUEUE
1063  *
1064  *
1065  *
1066  */
ison_queue_add(int refnum,IsonEntry * item,int next)1067 static void ison_queue_add (int refnum, IsonEntry *item, int next)
1068 {
1069 	Server *s;
1070 	IsonEntry *bottom;
1071 
1072 	if (!(s = get_server(refnum)))
1073 		return;
1074 
1075 	if (next)
1076 	{
1077 		item->next = s->ison_wait;
1078 		s->ison_wait = item;
1079 		return;
1080 	}
1081 
1082 	bottom = s->ison_wait;
1083 	while (bottom && bottom->next)
1084 		bottom = bottom->next;
1085 
1086 	if (!bottom)
1087 		s->ison_wait = item;
1088 	else
1089 		bottom->next = item;
1090 
1091 	return;
1092 }
1093 
ison_queue_send(int refnum)1094 static void ison_queue_send (int refnum)
1095 {
1096 	int count = 1;
1097 	Server *s;
1098 	IsonEntry *save, *bottom;
1099 
1100 	if (!(s = get_server(refnum)))
1101 		return;
1102 
1103 	if (!(save = s->ison_wait))
1104 		return;
1105 
1106 	bottom = s->ison_queue;
1107 	while (bottom)
1108 	{
1109 		if (s->ison_max && ++count > s->ison_max)
1110 			return;
1111 		else if (bottom->next)
1112 			bottom = bottom->next;
1113 		else
1114 			break;
1115 	}
1116 
1117 	s->ison_wait = save->next;
1118 
1119 	if (bottom)
1120 		bottom->next = save;
1121 	else
1122 		s->ison_queue = save;
1123 
1124 	save->next = NULL;
1125 
1126 	send_to_aserver(refnum, "ISON %s", save->ison_asked);
1127 }
1128 
ison_entry_pop(IsonEntry ** entry)1129 static void ison_entry_pop (IsonEntry **entry)
1130 {
1131 	IsonEntry *save = (*entry)->next;
1132 
1133 	new_free(&(*entry)->ison_asked);
1134 	new_free(&(*entry)->ison_got);
1135 	new_free(&(*entry)->oncmd);
1136 	new_free(&(*entry)->offcmd);
1137 	new_free(&(*entry)->endcmd);
1138 	new_free((char **)entry);
1139 	*entry = save;
1140 	return;
1141 }
1142 
ison_queue_pop(int refnum)1143 static void ison_queue_pop (int refnum)
1144 {
1145 	Server *s;
1146 
1147 	if (!(s = get_server(refnum)))
1148 		return;
1149 
1150 	ison_entry_pop(&(s->ison_queue));
1151 }
1152 
ison_wait_pop(int refnum)1153 static void ison_wait_pop (int refnum)
1154 {
1155 	Server *s;
1156 
1157 	if (!(s = get_server(refnum)))
1158 		return;
1159 
1160 	ison_entry_pop(&(s->ison_wait));
1161 }
ison_queue_top(int refnum)1162 static IsonEntry *ison_queue_top (int refnum)
1163 {
1164 	Server *s;
1165 
1166 	if (!(s = get_server(refnum)))
1167 		return NULL;
1168 
1169 	return s->ison_queue;
1170 }
1171 
ison_wait_top(int refnum)1172 static IsonEntry *ison_wait_top (int refnum)
1173 {
1174 	Server *s;
1175 
1176 	if (!(s = get_server(refnum)))
1177 		return NULL;
1178 
1179 	return s->ison_wait;
1180 }
1181 
get_new_ison_entry(int refnum,int next)1182 static IsonEntry *get_new_ison_entry (int refnum, int next)
1183 {
1184 	Server *s;
1185 	IsonEntry *new_w;
1186 
1187 	if (!get_server(refnum))
1188 		return NULL;
1189 
1190 	new_w = (IsonEntry *)new_malloc(sizeof(IsonEntry));
1191 	new_w->ison_asked = NULL;
1192 	new_w->ison_got = NULL;
1193 	new_w->next = NULL;
1194 	new_w->line = NULL;
1195 	new_w->oncmd = NULL;
1196 	new_w->offcmd = NULL;
1197 	new_w->endcmd = NULL;
1198 	ison_queue_add(refnum, new_w, next);
1199 	return new_w;
1200 }
1201 
ison_queue_list(int refnum)1202 static void ison_queue_list (int refnum)
1203 {
1204 	Server *s;
1205 	IsonEntry *item;
1206 	int count = 0;
1207 
1208 	if (!(s = get_server(refnum)))
1209 		return;
1210 
1211 	for (item = s->ison_queue; item; item = item->next, count++)
1212 	{
1213 		yell("[%d] [%s] [%p]", count, item->ison_asked,
1214 				item->line);
1215 	}
1216 
1217 	for (item = s->ison_wait; item; item = item->next, count++)
1218 	{
1219 		yell("[%d] [%s] [%p] (pending)", count, item->ison_asked,
1220 				item->line);
1221 	}
1222 }
1223 
BUILT_IN_COMMAND(isoncmd)1224 BUILT_IN_COMMAND(isoncmd)
1225 {
1226 	if (!args || !*args)
1227 		args = LOCAL_COPY(get_server_nickname(from_server));
1228 
1229 	isonbase(from_server, args, NULL);
1230 }
1231 
isonbase(int refnum,char * args,void (* line)(int,char *,char *))1232 void	isonbase (int refnum, char *args, void (*line) (int, char *, char *))
1233 {
1234 	IsonEntry 	*new_i;
1235 	char 		*next = NULL, *end, *p;
1236 	char		*on_cmd = NULL, *offcmd = NULL, *endcmd = NULL;
1237 	int		sendnext = 0;
1238 
1239 	/* Maybe should output a warning? */
1240 	if (!is_server_registered(refnum))
1241 		return;
1242 
1243 	while (args && *args == '-')
1244 	{
1245 		char *arg = next_arg(args, &args);
1246 		char *stuff;
1247 
1248 		if (!my_stricmp(arg, "-d"))
1249 		{
1250 			ison_queue_list(refnum);
1251 		}
1252 		if (!my_stricmp(arg, "-f"))
1253 		{
1254 			while (ison_queue_top(refnum))
1255 				ison_queue_pop(refnum);
1256 			while (ison_wait_top(refnum))
1257 				ison_wait_pop(refnum);
1258 		}
1259 		if (!my_stricmp(arg, "-s"))
1260 		{
1261 			ison_queue_send(refnum);
1262 		}
1263 		if (!my_stricmp(arg, "-n"))
1264 		{
1265 			sendnext++;
1266 		}
1267 		if (!my_stricmp(arg, "-e"))
1268 		{
1269 			if (get_server(refnum))
1270 				if (get_server(refnum)->ison_wait)
1271 					return;
1272 		}
1273 		if (!my_stricmp(arg, "-len"))
1274 		{
1275 			if ((stuff = next_arg(args, &args)))
1276 			{
1277 			     if (get_server(refnum))
1278 				get_server(refnum)->ison_len = MAX(100, atol(stuff));
1279 			}
1280 			else
1281 				say("Need numeric argument for -LEN argument.");
1282 		}
1283 		if (!my_stricmp(arg, "-max"))
1284 		{
1285 			if ((stuff = next_arg(args, &args)))
1286 			{
1287 			    if (get_server(refnum))
1288 				get_server(refnum)->ison_max = atol(stuff);
1289 			}
1290 			else
1291 				say("Need numeric argument for -MAX argument.");
1292 		}
1293 		if (!my_stricmp(arg, "-oncmd"))
1294 		{
1295 			if ((stuff = next_expr(&args, '{')))
1296 				on_cmd = stuff;
1297 			else
1298 				say("Need {...} argument for -ONCMD argument.");
1299 		}
1300 		if (!my_stricmp(arg, "-offcmd"))
1301 		{
1302 			if ((stuff = next_expr(&args, '{')))
1303 				offcmd = stuff;
1304 			else
1305 				say("Need {...} argument for -OFFCMD argument.");
1306 		}
1307 		if (!my_stricmp(arg, "-end"))
1308 		{
1309 			if ((stuff = next_expr(&args, '{')))
1310 				endcmd = stuff;
1311 			else
1312 				say("Need {...} argument for -END argument.");
1313 		}
1314 	}
1315 
1316 	ison_queue_send(refnum);
1317 	if (!args)
1318 		return;
1319 
1320 
1321 	/* So now we need to go through 'args', collecting the nicks into groups
1322 	 * of at most ison_len and sending those off.  Two things conspire
1323 	 * to make this harder than it sounds, however: an individual nick might
1324 	 * be longer than ison_len on its own, in which case we have to ignore it;
1325 	 * and if an endcmd was supplied then we must only add it to the _last_
1326 	 * queued ison command.  We don't know if a given queued ison command
1327 	 * is going to be the last one until we look ahead and hit the end of
1328 	 * the arguments.
1329 	 */
1330 	new_i = NULL;
1331 	end = NULL;
1332 	p = args;
1333 
1334 	while (*p)
1335 	{
1336 		/* Loop pre-conditions:
1337 		 *
1338 		 * p is pointing at start of next argument to examine, or at the
1339 		 * terminating null.
1340 		 *
1341 		 * end is pointing at the end of the current list of nicks to send,
1342 		 * or NULL if that list is empty.
1343 		 *
1344 		 * if end != NULL, next is pointing at start of the current list of
1345 		 * nicks to send.
1346 		 *
1347 		 * if new_i != NULL && end != NULL, new_i is pointing at the ison
1348 		 * entry for the current list of nicks to send (new_i->ison_asked is
1349 		 * is NOT set yet).
1350 		 *
1351 		 * if new_i != NULL && end == NULL then new_i is pointing at the ison
1352 		 * entry for the previous list of nicks to send (new_i->ison_asked is
1353 		 * set).
1354 		 */
1355 		if (!end)
1356 			next = p;
1357 		while (*p && !isspace((unsigned char)*p))
1358 		    p++;
1359 
1360 		/* You know, just in case something happened... */
1361 		if (get_server(refnum))
1362 		{
1363 		    if (p - next < get_server(refnum)->ison_len)
1364 		    {
1365 			/* can add this nick to the current list. */
1366 			if (!end)
1367 			{
1368 				/* This is the first nick of a new ison list, so now we know that
1369 				 * the previous list, if there was one, wasn't the last, so we can
1370 				 * send it off without an endcmd. */
1371 				if (new_i)
1372 					ison_queue_send(refnum);
1373 
1374 				if (!(new_i = get_new_ison_entry(refnum, sendnext)))
1375 				{
1376 					privileged_yell("isonbase: Could not create a new ison entry for %d [%d]", refnum, sendnext);
1377 					continue;
1378 				}
1379 				new_i->line = line;
1380 				malloc_strcpy(&new_i->oncmd, on_cmd);
1381 				malloc_strcpy(&new_i->offcmd, offcmd);
1382 			}
1383 
1384 			end = p;
1385 		    }
1386 		    else
1387 		    {
1388 			/* This nick is too long to add to the current list. */
1389 			if (end)
1390 			{
1391 				/* ..but there are some preceding words that we can send */
1392 				char restore = *end;
1393 
1394 				*end = 0;
1395 				malloc_strcpy(&new_i->ison_asked, next);
1396 				*end = restore;
1397 				/* Note that we do NOT send the ison queue yet, because we
1398 				 * don't yet know if we should set endcmd or not. */
1399 
1400 				p = end;
1401 				end = NULL;
1402 			}
1403 		    }
1404 		}
1405 
1406 		/* Advance to next nick */
1407 		while (isspace((unsigned char)*p))
1408 			p++;
1409 	}
1410 
1411 	if (new_i)
1412 	{
1413 		/* Send the last list - this one has endcmd set */
1414 		if (end)
1415 		{
1416 			/* ison_asked hasn't been set yet */
1417 			char restore = *end;
1418 
1419 			*end = 0;
1420 			malloc_strcpy(&new_i->ison_asked, next);
1421 			*end = restore;
1422 		}
1423 
1424 		malloc_strcpy(&new_i->endcmd, endcmd);
1425 		ison_queue_send(refnum);
1426 	}
1427 }
1428 
1429 /*
1430  * ison_returned: this is called when numeric 303 is received in
1431  * numbers.c. ISON must always be the property of the WHOIS queue.
1432  * Although we will check first that the top element expected is
1433  * actually an ISON.
1434  */
ison_returned(int refnum,const char * from,const char * comm,const char ** ArgList)1435 void	ison_returned (int refnum, const char *from, const char *comm, const char **ArgList)
1436 {
1437 	IsonEntry *new_i = ison_queue_top(refnum);
1438 	char	*do_off = NULL, *this1, *all1, *this2, *all2;
1439 	size_t	clue = 0;
1440 
1441 	if (!ArgList[0])
1442 		{ rfc1459_odd(from, comm, ArgList); return; }
1443 
1444 	if (!new_i)
1445 	{
1446 		/* XXX Hack to work around rogue /quote ison's */
1447 		if (do_hook(current_numeric, "%s", ArgList[0]))
1448 			put_it("%s Currently online: %s", banner(), ArgList[0]);
1449 		return;
1450 	}
1451 
1452 	all1 = LOCAL_COPY(new_i->ison_asked);
1453 	all2 = LOCAL_COPY(ArgList[0]);
1454 	if (new_i->offcmd)
1455 		while ((this2 = next_arg(all2, &all2)))
1456 			while ((this1 = next_arg(all1, &all1)) && my_stricmp(this1, this2))
1457 				malloc_strcat_wordlist_c(&do_off, space, this1, &clue);
1458 	malloc_strcat_wordlist_c(&do_off, space, all1, &clue);
1459 
1460 	PasteArgs(ArgList, 0);
1461 	if (new_i->line)
1462 	{
1463 		char *ison_ret = LOCAL_COPY(ArgList[0]);
1464 		new_i->line(refnum, new_i->ison_asked, ison_ret);
1465 	}
1466 	else
1467 	{
1468 		if (new_i->oncmd && ArgList[0] && ArgList[0][0])
1469 			call_lambda_command("ISON", new_i->oncmd, ArgList[0]);
1470 		if (new_i->offcmd && do_off && *do_off)
1471 			call_lambda_command("ISON", new_i->offcmd, do_off);
1472 		if (new_i->endcmd)
1473 			call_lambda_command("ISON", new_i->endcmd, NULL);
1474 		if (!new_i->oncmd && !new_i->offcmd &&
1475 				do_hook(current_numeric, "%s", ArgList[0]))
1476 			put_it("%s Currently online: %s", banner(), ArgList[0]);
1477 	}
1478 
1479 	new_free(&do_off);
1480 	ison_queue_pop(refnum);
1481 	ison_queue_send(refnum);
1482 	return;
1483 }
1484 
1485 
1486 /*
1487  *
1488  *
1489  *
1490  *
1491  *				USERHOST QUEUE
1492  *
1493  *
1494  *
1495  *
1496  */
userhost_queue_top(int refnum)1497 static UserhostEntry *userhost_queue_top (int refnum)
1498 {
1499 	Server *s;
1500 
1501 	if (!(s = get_server(refnum)))
1502 		return NULL;
1503 
1504 	return s->userhost_queue;
1505 }
1506 
userhost_wait_top(int refnum)1507 static UserhostEntry *userhost_wait_top (int refnum)
1508 {
1509 	Server *s;
1510 
1511 	if (!(s = get_server(refnum)))
1512 		return NULL;
1513 
1514 	return s->userhost_wait;
1515 }
1516 
userhost_queue_add(int refnum,UserhostEntry * item)1517 static void userhost_queue_add (int refnum, UserhostEntry *item)
1518 {
1519 	UserhostEntry *bottom;
1520 	Server *s;
1521 
1522 	if (!(s = get_server(refnum)))
1523 		return;
1524 
1525 	bottom = s->userhost_wait;
1526 	while (bottom && bottom->next)
1527 		bottom = bottom->next;
1528 
1529 	if (!bottom)
1530 		s->userhost_wait = item;
1531 	else
1532 		bottom->next = item;
1533 
1534 	return;
1535 }
1536 
userhost_queue_send(int refnum)1537 static void userhost_queue_send (int refnum)
1538 {
1539 	int count = 1;
1540 	Server *s;
1541 	UserhostEntry *save, *bottom;
1542 
1543 	if (!(s = get_server(refnum)))
1544 		return;
1545 
1546 	if (!(save = s->userhost_wait))
1547 		return;
1548 
1549 	bottom = s->userhost_queue;
1550 	while (bottom)
1551 	{
1552 		if (s->userhost_max && ++count > s->userhost_max)
1553 			return;
1554 		else if (bottom->next)
1555 			bottom = bottom->next;
1556 		else
1557 			break;
1558 	}
1559 
1560 	s->userhost_wait = save->next;
1561 
1562 	if (bottom)
1563 		bottom->next = save;
1564 	else
1565 		s->userhost_queue = save;
1566 
1567 	save->next = NULL;
1568 
1569 	send_to_aserver(refnum, save->format, save->userhost_asked);
1570 }
1571 
userhost_entry_pop(UserhostEntry ** entry)1572 static void userhost_entry_pop (UserhostEntry **entry)
1573 {
1574 	UserhostEntry *save = (*entry)->next;
1575 
1576 	/* XXX But what if *entry is null? */
1577 	new_free(&(*entry)->userhost_asked);
1578 	new_free(&(*entry)->text);
1579 	new_free((char **)entry);
1580 	*entry = save;
1581 	return;
1582 }
1583 
userhost_queue_pop(int refnum)1584 static void userhost_queue_pop (int refnum)
1585 {
1586 	Server *s;
1587 
1588 	if (!(s = get_server(refnum)))
1589 		return;
1590 
1591 	userhost_entry_pop(&(s->userhost_queue));
1592 }
1593 
userhost_wait_pop(int refnum)1594 static void userhost_wait_pop (int refnum)
1595 {
1596 	Server *s;
1597 
1598 	if (!(s = get_server(refnum)))
1599 		return;
1600 
1601 	userhost_entry_pop(&(s->userhost_wait));
1602 }
1603 
get_new_userhost_entry(int refnum)1604 static UserhostEntry *get_new_userhost_entry (int refnum)
1605 {
1606 	UserhostEntry *new_u = (UserhostEntry *)new_malloc(sizeof(UserhostEntry));
1607 	new_u->format = NULL;
1608 	new_u->userhost_asked = NULL;
1609 	new_u->text = NULL;
1610 	new_u->next = NULL;
1611 	new_u->func = NULL;
1612 	userhost_queue_add(refnum, new_u);
1613 	return new_u;
1614 }
1615 
1616 /*
1617  * userhost: Does the USERHOST command.  Need to split up the queries,
1618  * since the server will only reply to 5 at a time.
1619  */
BUILT_IN_COMMAND(userhostcmd)1620 BUILT_IN_COMMAND(userhostcmd)
1621 {
1622 	userhostbase(from_server, args, subargs, NULL, 1);
1623 }
1624 
BUILT_IN_COMMAND(useripcmd)1625 BUILT_IN_COMMAND(useripcmd)
1626 {
1627 	userhostbase(from_server, args, subargs, NULL, 0);
1628 }
1629 
BUILT_IN_COMMAND(usripcmd)1630 BUILT_IN_COMMAND(usripcmd)
1631 {
1632 	userhostbase(from_server, args, subargs, NULL, 2);
1633 }
1634 
userhostbase(int refnum,char * args,const char * subargs,void (* line)(int,UserhostItem *,const char *,const char *),int do_userhost)1635 void userhostbase (int refnum, char *args, const char *subargs, void (*line) (int, UserhostItem *, const char *, const char *), int do_userhost)
1636 {
1637 	int	total = 0,
1638 		userhost_cmd = 0;
1639 	int	server_query_reqd = 0;
1640 	char	*nick;
1641 	char	buffer[BIG_BUFFER_SIZE + 1];
1642 	char 	*ptr,
1643 		*next_ptr,
1644 		*body = NULL;
1645 	int	count = 5;
1646 	char	*extra = NULL;
1647 
1648 	/* Maybe should output a warning? */
1649 	if (!is_server_registered(refnum))
1650 		return;
1651 
1652 	*buffer = 0;
1653 	while ((nick = next_arg(args, &args)) != NULL)
1654 	{
1655 		if (check_nickname(nick, 1) || is_number(nick))
1656 		{
1657 			total++;
1658 			if (!fetch_userhost(refnum, NULL, nick))
1659 				server_query_reqd++;
1660 
1661 			if (*buffer)
1662 				strlcat(buffer, " ", sizeof buffer);
1663 			strlcat(buffer, nick, sizeof buffer);
1664 		}
1665 
1666 		else if (!my_strnicmp(nick, "-direct", 2))
1667 			server_query_reqd++;
1668 
1669 		else if (!my_strnicmp(nick, "-count", 3))
1670 			count = atol(safe_new_next_arg(args, &args));
1671 
1672 		else if (!my_strnicmp(nick, "-cmd", 2))
1673 		{
1674 			if (!total)
1675 			{
1676 				if (do_userhost == 1)
1677 					say("USERHOST -cmd with no nicks specified");
1678 				else if (do_userhost == 0)
1679 					say("USERIP -cmd with no nicks specified");
1680 				else
1681 					say("USRIP -cmd with no nicks specified");
1682 				return;
1683 			}
1684 
1685 			while (my_isspace(*args))
1686 				args++;
1687 
1688 			if (!(body = next_expr_failok(&args, '{'))) /* } */
1689 				body = args;
1690 
1691 			userhost_cmd = 1;
1692 			break;
1693 		}
1694 
1695 		else if (!my_strnicmp(nick, "-extra", 2))
1696 		{
1697 			char *extravar = next_arg(args, &args);
1698 			if (empty(extravar))
1699 			{
1700 				say("Need argument to /userhost -extra");
1701 				break;
1702 			}
1703 			if (extra)
1704 				new_free(&extra);
1705 			/* XXX But what if extravar contains []s? */
1706 			extra = get_variable(extravar);
1707 		}
1708 		else if (!my_strnicmp(nick, "-flush", 2))
1709 		{
1710 			while (userhost_wait_top(refnum))
1711 				userhost_wait_pop(refnum);
1712 			return;
1713 		}
1714 	}
1715 
1716 	if (!userhost_cmd && !total)
1717 	{
1718 		server_query_reqd++;
1719 		strlcpy(buffer, get_server_nickname(refnum), sizeof buffer);
1720 	}
1721 
1722 	ptr = buffer;
1723 
1724 	if (server_query_reqd || (!line && !userhost_cmd))
1725 	{
1726 		ptr = buffer;
1727 		while (ptr && *ptr)
1728 		{
1729 			UserhostEntry *new_u = get_new_userhost_entry(refnum);
1730 
1731 			move_to_abs_word(ptr, (const char **)&next_ptr, count);
1732 
1733 			if (next_ptr && *next_ptr && next_ptr > ptr)
1734 				next_ptr[-1] = 0;
1735 
1736 			new_u->userhost_asked = malloc_strdup(ptr);
1737 			if (do_userhost == 1)
1738 				new_u->format = "USERHOST %s";
1739 			else if (do_userhost == 0)
1740 				new_u->format = "USERIP %s";
1741 			else
1742 				new_u->format = "USRIP %s";
1743 
1744 			userhost_queue_send(refnum);
1745 
1746 			if (userhost_cmd)
1747 				new_u->text = malloc_strdup(body);
1748 
1749 			if (line)
1750 				new_u->func = line;
1751 			else if (userhost_cmd)
1752 				new_u->func = userhost_cmd_returned;
1753 			else
1754 				new_u->func = NULL;
1755 
1756 			new_u->extra = extra;
1757 			ptr = next_ptr;
1758 		}
1759 	}
1760 	else
1761 	{
1762 		while (ptr && *ptr)
1763 		{
1764 			char *my_nick = next_arg(ptr, &ptr);
1765 			const char *ouh = fetch_userhost(refnum, NULL, my_nick);
1766 			char *uh, *host;
1767 			UserhostItem item = {NULL, 0, 0, 0, NULL, NULL, NULL};
1768 
1769 			uh = LOCAL_COPY(ouh);
1770 			item.nick = my_nick;
1771 			item.oper = 0;
1772 			item.connected = 1;
1773 			item.away = 0;
1774 			item.user = uh;
1775 			item.extra = extra;
1776 			host = strchr(uh, '@');
1777 			if (host) {
1778 				*host++ = 0;
1779 				item.host = host;
1780 			} else
1781 				item.host = "<UNKNOWN>";
1782 
1783 			if (line)
1784 				line(refnum, &item, my_nick, body);
1785 			else if (userhost_cmd)
1786 				userhost_cmd_returned(refnum, &item, my_nick, body);
1787 			else
1788 				yell("Yowza!  I dont know what to do here!");
1789 		}
1790 		new_free(&extra);
1791 	}
1792 }
1793 
1794 /*
1795  * userhost_returned: this is called when numeric 302 is received in
1796  * numbers.c. USERHOST must always remain the property of the userhost
1797  * queue.  Sending out USERHOST requests to the server without going
1798  * through this queue will cause it to be corrupted and the client will
1799  * go higgledy-piggledy.
1800  */
userhost_returned(int refnum,const char * from,const char * comm,const char ** ArgList)1801 void	userhost_returned (int refnum, const char *from, const char *comm, const char **ArgList)
1802 {
1803 	UserhostEntry *top = userhost_queue_top(refnum);
1804 	char *ptr;
1805 	char *results;
1806 
1807 	if (!ArgList[0])
1808 		{ rfc1459_odd(from, comm, ArgList); return; }
1809 
1810 	if (!top)
1811 	{
1812 		yell("### Please don't /quote a server command that returns the 302 numeric.");
1813 		return;
1814 	}
1815 
1816 	PasteArgs(ArgList, 0);
1817 	results = LOCAL_COPY(ArgList[0]);
1818 	ptr = top->userhost_asked;
1819 
1820 	/*
1821 	 * Go through the nicknames that were requested...
1822 	 */
1823 	while (ptr && *ptr)
1824 	{
1825 		/*
1826 		 * Grab the next nickname
1827 		 */
1828 		char *	cnick;
1829 		size_t	len;
1830 
1831 		cnick = next_arg(ptr, &ptr);
1832 		len = strlen(cnick);
1833 
1834 		/*
1835 		 * Now either it is present at the next argument
1836 		 * or its not.  If it is, it will match the first
1837 		 * part of ArgList, and the following char will
1838 		 * either be a * or an = (eg, nick*= or nick=)
1839 		 */
1840 		if (results && (!my_strnicmp(cnick, results, len)
1841 	            && (results[len] == '*' || results[len] == '=')))
1842 		{
1843 			UserhostItem item;
1844 			char *nick, *user, *host;
1845 
1846 			/* Extract all the interesting info */
1847 			item.connected = 1;
1848 			nick = next_arg(results, &results);
1849 			user = strchr(nick, '=');
1850 			if (!user)
1851 			{
1852 				yell("Can't parse useless USERHOST reply [%s]",
1853 						ArgList[0]);
1854 				userhost_queue_pop(refnum);
1855 				return;
1856 			}
1857 
1858 			if (user[-1] == '*')
1859 			{
1860 				user[-1] = 0;
1861 				item.oper = 1;
1862 			}
1863 			else
1864 				item.oper = 0;
1865 
1866 			if (user[1] == '+')
1867 				item.away = 0;
1868 			else
1869 				item.away = 1;
1870 
1871 			*user++ = 0;
1872 			user++;
1873 
1874 			host = strchr(user, '@');
1875 			if (!host)
1876 			{
1877 				yell("Can't parse useless USERHOST reply [%s]",
1878 						ArgList[0]);
1879 				userhost_queue_pop(refnum);
1880 				return;
1881 			}
1882 			*host++ = 0;
1883 
1884 			item.nick = nick;
1885 			item.user = user;
1886 			item.host = host;
1887 			item.extra = SAFE(top->extra);
1888 
1889 			/*
1890 			 * If the user wanted a callback, then
1891 			 * feed the callback with the info.
1892 			 */
1893 			if (top->func)
1894 				top->func(refnum, &item, cnick, top->text);
1895 
1896 			/*
1897 			 * Otherwise, the user just did /userhost,
1898 			 * so we offer the numeric, and if the user
1899 			 * doesnt bite, we output to the screen.
1900 			 */
1901 			else if (do_hook(current_numeric, "%s %s %s %s %s %s",
1902 						item.nick,
1903 						item.oper ? "+" : "-",
1904 						item.away ? "-" : "+",
1905 						item.user, item.host,
1906 						item.extra))
1907 				put_it("%s %s is %s@%s%s%s %s", banner(),
1908 						item.nick, item.user, item.host,
1909 						item.oper ?  " (Is an IRC operator)" : empty_string,
1910 						item.away ? " (away)" : empty_string,
1911 						item.extra ? item.extra : empty_string);
1912 		}
1913 
1914 		/*
1915 		 * If ArgList isnt the current nick, then the current nick
1916 		 * must not be on irc.  So we whip up a dummy UserhostItem
1917 		 * and send it on its way.  We DO NOT HOOK the 302 numeric
1918 		 * with this bogus entry, because thats the historical
1919 		 * behavior.  This can cause a problem if you do a USERHOST
1920 		 * and wait on the 302 numeric.  I think waiting on the 302
1921 		 * numeric is stupid, anyhow.
1922 		 */
1923 		else
1924 		{
1925 			/*
1926 			 * Of course, only if the user asked for a callback
1927 			 * via /userhost -cmd or a direct call to userhostbase.
1928 			 * If the user just did /userhost, and the nicks arent
1929 			 * on, then we just dont display anything.
1930 			 */
1931 			if (top->func)
1932 			{
1933 				UserhostItem item;
1934 
1935 				item.nick = cnick;
1936 				item.user = item.host = "<UNKNOWN>";
1937 				item.oper = item.away = 0;
1938 				item.connected = 1;
1939 				item.extra = top->extra;
1940 
1941 				top->func(refnum, &item, cnick, top->text);
1942 			}
1943 		}
1944 	}
1945 
1946 	userhost_queue_pop(refnum);
1947 	userhost_queue_send(refnum);
1948 }
1949 
userhost_cmd_returned(int refnum,UserhostItem * stuff,const char * nick,const char * text)1950 void	userhost_cmd_returned (int refnum, UserhostItem *stuff, const char *nick, const char *text)
1951 {
1952 	char	*args = NULL;
1953 	size_t	clue = 0;
1954 
1955 	/* This should be safe, though its playing it fast and loose */
1956 	malloc_strcat_c(&args, stuff->nick ? stuff->nick : empty_string, &clue);
1957 	malloc_strcat_c(&args, stuff->oper ? " + " : " - ", &clue);
1958 	malloc_strcat_c(&args, stuff->away ? "+ " : "- ", &clue);
1959 	malloc_strcat_c(&args, stuff->user ? stuff->user : empty_string, &clue);
1960 	malloc_strcat_c(&args, space, &clue);
1961 	malloc_strcat_c(&args, stuff->host ? stuff->host : empty_string, &clue);
1962 	malloc_strcat_c(&args, space, &clue);
1963 	malloc_strcat_c(&args, stuff->extra ? stuff->extra : empty_string, &clue);
1964 	call_lambda_command("USERHOST", text, args);
1965 
1966 	new_free(&args);
1967 }
1968 
clean_server_queues(int i)1969 void	clean_server_queues (int i)
1970 {
1971 	while (who_queue_top(i))
1972 		who_queue_pop(i);
1973 
1974 	while (ison_queue_top(i))
1975 		ison_queue_pop(i);
1976 
1977 	while (ison_wait_top(i))
1978 		ison_wait_pop(i);
1979 
1980 	while (userhost_queue_top(i))
1981 		userhost_queue_pop(i);
1982 
1983 	while (userhost_wait_top(i))
1984 		userhost_wait_pop(i);
1985 }
1986 
1987 
1988 /* XXXX */
1989 
who_queue_debug(void * unused)1990 static int	who_queue_debug (void *unused)
1991 {
1992 	Server *s;
1993 	int	refnum;
1994 	WhoEntry *item;
1995 	double d;
1996 static	int	last_refnum_checked = 0;
1997 
1998 	for (refnum = 0; refnum < number_of_servers; refnum++)
1999 	{
2000 	    if (!(s = get_server(refnum)))
2001 		continue;
2002 
2003 	    for (item = s->who_queue; item; item = item->next)
2004 	    {
2005 		if (item->refnum >= last_refnum_checked + 100)
2006 		{
2007 			WHO_DEBUG("WATCHER: Who refnum is up to [%d]",
2008 					item->refnum);
2009 			last_refnum_checked = item->refnum;
2010 		}
2011 
2012 		if (item->dirty == 0)
2013 		{
2014 		    d = time_diff(item->request_time, now);
2015 		    if (d >= 15)
2016 		    {
2017 			yell("Warning: who item [%d] (server %d) is not dirty and > 15 seconds old", item->refnum, refnum);
2018 			WHO_DEBUG("WATCHER: Who item [%d] is not dirty, > 15 seconds old", item->refnum);
2019 			WHO_DEBUG("WATCHER: [%s]", who_item_full_desc(item));
2020 			yell("Flushing who queue for server [%d] -- check logs", refnum);
2021 			who_queue_flush(refnum);
2022 		    }
2023 		}
2024 		else
2025 		{
2026 		    d = time_diff(item->request_time, now);
2027 		    if (d >= 30)
2028 		    {
2029 			yell("Warning: who item [%d] (server %d) is dirty and > 30 seconds old", item->refnum, refnum);
2030 			WHO_DEBUG("WATCHER: Who item [%d] is dirty, > 30 seconds old", item->refnum);
2031 			WHO_DEBUG("WATCHER: [%s]", who_item_full_desc(item));
2032 			yell("Flushing who queue for server [%d] -- check logs", refnum);
2033 			who_queue_flush(refnum);
2034 		    }
2035 		    else
2036 		    {
2037 		        d = time_diff(item->dirty_time, now);
2038 		        if (d >= 15)
2039 		        {
2040 			   yell("Warning: who item [%d] (server %d) is dirty for > 15 seconds", item->refnum, refnum);
2041 			   WHO_DEBUG("WATCHER: Who item [%d] is dirty > 15 seconds", item->refnum);
2042 			   WHO_DEBUG("WATCHER: [%s]", who_item_full_desc(item));
2043 			}
2044 			yell("Flushing who queue for server [%d] -- check logs", refnum);
2045 			who_queue_flush(refnum);
2046 		    }
2047 		}
2048 	    }
2049 	}
2050 
2051 	return 0;
2052 }
2053 
2054 
2055