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