/* * who.c -- The WHO queue. The ISON queue. The USERHOST queue. * * Copyright 1996, 2003 EPIC Software Labs. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notices, the above paragraph (the one permitting redistribution), * this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The names of the author(s) may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #define NEED_SERVER_LIST #include "irc.h" #include "commands.h" #include "ircaux.h" #include "who.h" #include "server.h" #include "window.h" #include "vars.h" #include "hook.h" #include "output.h" #include "numbers.h" #include "parse.h" #include "ifcmd.h" #include "names.h" #include "words.h" #include "reg.h" #include "log.h" #include "timer.h" #include "alias.h" /* XXXX - only debugging stuff for adm. Remove later */ static FILE * who_log = NULL; static int who_global_refnum = 0; static char who_timeref[] = "WHOTIM"; static int who_queue_debug (void *unused); static void WHO_DEBUG (const char *format, ...) { va_list args; if (who_log == NULL && (x_debug & DEBUG_WHO_QUEUE)) { add_timer(1, who_timeref, 5, -1, who_queue_debug, NULL, NULL, GENERAL_TIMER, -1, 0, 0); do_log(1, "who.log", &who_log); } if (who_log) { time_t t; char my_buffer[256]; struct tm *ugh; time(&t); ugh = localtime(&t); strftime(my_buffer, 255, "%H:%M:%S ", ugh); fprintf(who_log, "%s", my_buffer); va_start(args, format); vfprintf(who_log, format, args); fputc('\n', who_log); fflush(who_log); va_end(args); } if (who_log && !(x_debug & DEBUG_WHO_QUEUE)) { remove_timer(who_timeref); do_log(0, "who.log", &who_log); } } /* * * * * WHO QUEUE * * * */ /* flags used by who queue */ #define WHO_OPS 0x0001 #define WHO_NAME 0x0002 #define WHO_ZERO 0x0004 #define WHO_CHOPS 0x0008 #define WHO_FILE 0x0010 #define WHO_HOST 0x0020 #define WHO_SERVER 0x0040 #define WHO_HERE 0x0080 #define WHO_AWAY 0x0100 #define WHO_NICK 0x0200 #define WHO_LUSERS 0x0400 #define WHO_REAL 0x0800 #define WHO_NOCHOPS 0x1000 /* * This one requires an explanation. As of u2.10.04, operators have to * specify a specific special flag to the WHO protocol command in order to * see invisible users. This is so they can be all paranoid and pedantic * about just who is looking at invisible users and when. Sheesh. This * flag has *no effect* in any other context than this. */ #define WHO_INVISIBLE 0x2000 #define WHO_OPERSPY 0x4000 #define S(x) (((x) != NULL) ? (x) : empty_string) static char *who_item_full_desc (WhoEntry *item) { static char retval[10240]; if (item) snprintf(retval, sizeof retval, "refnum [%d] " "dirty [%d], piggyback [%d], unet [%d], unet_args [%s], " "dalnet [%d], dalnet_args [%s], who_mask [%d], " "who_target [%s], who_name [%s], who_host [%s], " "who_server [%s], who_nick [%s], who_real [%s], " "who_stuff [%s], who_end [%s], next [%p], line [%s], " "end [%s], requested ["INTMAX_FORMAT"], dirty ["INTMAX_FORMAT"]", item->refnum, item->dirty, item->piggyback, item->undernet_extended, S(item->undernet_extended_args), item->dalnet_extended, S(item->dalnet_extended_args), item->who_mask, S(item->who_target), S(item->who_name), S(item->who_host), S(item->who_server), S(item->who_nick), S(item->who_real), S(item->who_stuff), S(item->who_end), (void *)item->next, item->line ? "" : "", item->end ? "" : "", (intmax_t) item->request_time.tv_sec, (intmax_t) item->dirty_time.tv_sec); else snprintf(retval, sizeof retval, ""); return retval; } static char *who_item_desc (WhoEntry *item) { static char retval[10240]; if (item) snprintf(retval, sizeof retval, "refnum [%d] dirty [%d], piggyback [%d], next [%p]", item->refnum, item->dirty, item->piggyback, (void *)item->next); else snprintf(retval, sizeof retval, ""); return retval; } static WhoEntry *who_queue_top (int refnum) { Server *s; if (!(s = get_server(refnum))) return NULL; WHO_DEBUG("Returning top of who queue for server %d [%s]", refnum, who_item_desc(s->who_queue)); return s->who_queue; } /* * This is tricky -- this doesnt get the LAST one, it gets the * next to the last one. Why? Because the LAST one is the one * asking, and they want to know who is LAST (before them) * So it sucks. Sue me. */ static WhoEntry *who_previous_query (int refnum, WhoEntry *me) { WhoEntry *what; if (!get_server(refnum)) return NULL; what = who_queue_top(refnum); while (what && what->next != me) what = what->next; WHO_DEBUG("Returning item previous to [%d(%d)] - [%s]", me->refnum, refnum, who_item_desc(what)); return what; } static void who_queue_add (int refnum, WhoEntry *item) { WhoEntry *bottom; Server *s; if (!(s = get_server(refnum))) return; bottom = who_queue_top(refnum); while (bottom && bottom->next) bottom = bottom->next; if (!bottom) s->who_queue = item; else bottom->next = item; get_time(&item->request_time); WHO_DEBUG("Adding item to who queue for server %d [%s]", refnum, who_item_full_desc(item)); return; } static void delete_who_item (WhoEntry *save) { Timeval t; double d; get_time(&t); d = time_diff(save->request_time, t); WHO_DEBUG("Final deletion of item [%s], alive for [%f] seconds", who_item_full_desc(save), d); new_free(&save->who_target); new_free(&save->who_name); new_free(&save->who_host); new_free(&save->who_server); new_free(&save->who_nick); new_free(&save->who_real); new_free(&save->who_stuff); new_free(&save->who_end); new_free((char **)&save); } static void who_queue_pop (int refnum) { WhoEntry *save; int piggyback; Server *s; WHO_DEBUG("Popping first item off of server [%d]", refnum); if (!(s = get_server(refnum))) return; do { if (!(save = s->who_queue)) break; piggyback = save->piggyback; s->who_queue = save->next; delete_who_item(save); } while (piggyback); if (s->who_queue == NULL) WHO_DEBUG("WHO QUEUE for server [%d] is now empty", refnum); return; } static WhoEntry *get_new_who_entry (void) { WhoEntry *new_w = (WhoEntry *)new_malloc(sizeof(WhoEntry)); new_w->refnum = ++who_global_refnum; new_w->dirty = 0; new_w->piggyback = 0; new_w->who_mask = 0; new_w->who_target = NULL; new_w->who_host = NULL; new_w->who_name = NULL; new_w->who_server = NULL; new_w->who_nick = NULL; new_w->who_real = NULL; new_w->who_stuff = NULL; new_w->who_end = NULL; new_w->next = NULL; new_w->undernet_extended = 0; new_w->undernet_extended_args = NULL; new_w->dalnet_extended = 0; new_w->dalnet_extended_args = NULL; new_w->request_time.tv_sec = 0; new_w->request_time.tv_usec = 0; new_w->dirty_time.tv_sec = 0; new_w->dirty_time.tv_usec = 0; WHO_DEBUG("Creating new who item with refnum [%d]", new_w->refnum); return new_w; } static void who_queue_list (int refnum) { WhoEntry *item; int count = 0; Server *s; WHO_DEBUG("Listing queue for server [%d]", refnum); if (!(s = get_server(refnum))) return; for (item = s->who_queue; item; item = item->next) { yell("[%d] %s", count, who_item_full_desc(item)); WHO_DEBUG("[%d] %s", count, who_item_full_desc(item)); count++; } } static void who_queue_flush (int refnum) { Server *s; WHO_DEBUG("Flushing who queue for server [%d]", refnum); if (!(s = get_server(refnum))) return; while (s->who_queue) who_queue_pop(refnum); yell("Who queue for server [%d] purged", refnum); WHO_DEBUG("done"); } /* * who: the /WHO command. Parses the who switches and sets the who_mask and * whoo_stuff accordingly. Who_mask and who_stuff are used in whoreply() in * parse.c */ BUILT_IN_COMMAND(whocmd) { whobase(from_server, args, NULL, NULL); } /* * whobase: What does all the work. */ void whobase (int refnum, char *args, void (*line) (int, const char *, const char *, const char **), void (*end) (int, const char *, const char *, const char **)) { char * arg; const char * channel = NULL; int no_args = 1, len; WhoEntry * new_w, *old; /* Maybe should output a warning? */ if (!is_server_registered(refnum)) { WHO_DEBUG("WHOBASE: server [%d] is not connected", refnum); return; } new_w = get_new_who_entry(); new_w->line = line; new_w->end = end; while ((arg = next_arg(args, &args)) != NULL) { no_args = 0; if (*arg == '-' || *arg == '/') { lower(arg); arg++; if ((len = strlen(arg)) == 0) { say("Unknown or missing flag"); WHO_DEBUG("WHOBASE: empty argument. punting."); delete_who_item(new_w); return; } else if (!strncmp(arg, "away", MAX(len, 1))) { new_w->who_mask |= WHO_AWAY; WHO_DEBUG("WHOBASE: Setting WHO_AWAY flag"); } else if (!strncmp(arg, "chops", MAX(len, 2))) { new_w->who_mask |= WHO_CHOPS; WHO_DEBUG("WHOBASE: Setting WHO_CHOPS flag"); } else if (!strncmp(arg, "diagnose", MAX(len, 1))) { WHO_DEBUG("WHOBASE: Listing the who queue"); who_queue_list(refnum); delete_who_item(new_w); return; } else if (!strncmp(arg, "dx", MAX(len, 2))) { new_w->dalnet_extended = 1; new_w->dalnet_extended_args = new_next_arg(args, &args); channel = args; /* Grab the rest of args */ WHO_DEBUG("WHOBASE: setting -dx flag [%s]", new_w->dalnet_extended_args, channel); args = NULL; } else if (!strncmp(arg, "end", MAX(len, 3))) { char *stuff; if ((stuff = next_expr(&args, '{'))) malloc_strcpy(&new_w->who_end, stuff); else say("Need {...} argument for -END argument."); WHO_DEBUG("WHOBASE: setting who_end [%s]", new_w->who_end); } else if (!strncmp(arg, "flush", MAX(len, 1))) { WHO_DEBUG("WHOBASE: flushing who queue [%d]", refnum); who_queue_flush(refnum); delete_who_item(new_w); return; } else if (!strncmp(arg, "here", MAX(len, 2))) { WHO_DEBUG("WHOBASE: setting WHO_HERE flag"); new_w->who_mask |= WHO_HERE; } else if (!strncmp(arg, "hosts", MAX(len, 2))) { if ((arg = next_arg(args, &args)) == NULL) { WHO_DEBUG("WHOBASE: -HOST missing argument"); say("WHO -HOST: missing argument"); delete_who_item(new_w); return; } new_w->who_mask |= WHO_HOST; malloc_strcpy(&new_w->who_host, arg); channel = new_w->who_host; WHO_DEBUG("WHOBASE: Setting -HOST argument [%s]", new_w->who_host); } else if (!strncmp(arg, "line", MAX(len, 4))) { char *stuff; if ((stuff = next_expr(&args, '{'))) malloc_strcpy(&new_w->who_stuff, stuff); else say("Need {...} argument for -LINE argument."); WHO_DEBUG("WHOBASE: setting -line [%s]", new_w->who_end); } else if (!strncmp(arg, "literal", MAX(len, 3))) { /* Hope for the best */ new_w->who_mask = 0; /* For safety reasons */ new_w->dalnet_extended = 0; new_w->undernet_extended = 0; new_free(&new_w->who_stuff); new_free(&new_w->who_end); WHO_DEBUG("WHOBASE: Doing -LITERAL [%s]", args); who_queue_add(refnum, new_w); send_to_aserver(refnum, "WHO %s", args); return; } else if (!strncmp(arg, "lusers", MAX(len, 2))) { new_w->who_mask |= WHO_LUSERS; WHO_DEBUG("WHOBASE: Setting WHO_LUSERS flag", args); } else if (!strncmp(arg, "name", MAX(len, 2))) { if ((arg = next_arg(args, &args)) == NULL) { WHO_DEBUG("WHOBASE: -NAME missing argument"); say("WHO -NAME: missing arguement"); delete_who_item(new_w); return; } new_w->who_mask |= WHO_NAME; malloc_strcpy(&new_w->who_name, arg); channel = new_w->who_name; WHO_DEBUG("WHOBASE: Setting -NAME argument [%s]", new_w->who_name); } else if (!strncmp(arg, "nick", MAX(len, 2))) { if ((arg = next_arg(args, &args)) == NULL) { WHO_DEBUG("WHOBASE: -NICK missing argument"); say("WHO -NICK: missing arguement"); delete_who_item(new_w); return; } new_w->who_mask |= WHO_NICK; malloc_strcpy(&new_w->who_nick, arg); channel = new_w->who_nick; WHO_DEBUG("WHOBASE: Setting -NICK argument [%s]", new_w->who_nick); } else if (!strncmp(arg, "nochops", MAX(len, 2))) { new_w->who_mask |= WHO_NOCHOPS; WHO_DEBUG("WHOBASE: Setting WHO_NOCHOPS flag"); } else if (!strncmp(arg, "oper", MAX(len, 1))) { new_w->who_mask |= WHO_OPS; WHO_DEBUG("WHOBASE: Setting WHO_OPS flag"); } else if (!strncmp(arg, "operspy", MAX(len, 5))) { new_w->who_mask |= WHO_OPERSPY; WHO_DEBUG("WHOBASE: Setting WHO_OPERSPY flag"); } else if (!strncmp(arg, "realname", MAX(len, 1))) { if ((arg = next_arg(args, &args)) == NULL) { WHO_DEBUG("WHOBASE: -REALNAME missing argument"); say("WHO -REALNAME: missing arguement"); delete_who_item(new_w); return; } new_w->who_mask |= WHO_REAL; malloc_strcpy(&new_w->who_real, arg); channel = new_w->who_real; WHO_DEBUG("WHOBASE: Setting -REALNAME [%s]", new_w->who_real); } else if (!strncmp(arg, "servers", MAX(len, 1))) { if ((arg = next_arg(args, &args)) == NULL) { WHO_DEBUG("WHOBASE: -SERVERS missing argument"); say("WHO -SERVER: missing arguement"); delete_who_item(new_w); return; } new_w->who_mask |= WHO_SERVER; malloc_strcpy(&new_w->who_server, arg); channel = new_w->who_server; WHO_DEBUG("WHOBASE: Setting -SERVERS [%s]", new_w->who_server); } else if (!strncmp(arg, "u-i", MAX(len, 3))) { new_w->who_mask |= WHO_INVISIBLE; WHO_DEBUG("WHOBASE: Setting WHO_INVISIBLE flag"); } else if (!strncmp(arg, "ux", MAX(len, 2))) { new_w->undernet_extended = 1; new_w->undernet_extended_args = args; args = NULL; WHO_DEBUG("WHOBASE: Setting undernet flag [%s]", new_w->undernet_extended_args); } else { WHO_DEBUG("WHOBASE: Unknown flag [%s]", arg); say("Unknown or missing flag"); delete_who_item(new_w); return; } } else if (strcmp(arg, "*") == 0) { channel = get_window_echannel(0); if (!channel || !*channel) { WHO_DEBUG("WHOBASE: WHO *, but not on channel"); say("You are not on a channel. " "Use /WHO ** to see everybody."); delete_who_item(new_w); return; } else WHO_DEBUG("WHOBASE: WHO * -> WHO %s", channel); } else { channel = arg; WHO_DEBUG("WHOBASE: WHO %s", channel); } } if (no_args) { WHO_DEBUG("WHOBASE: No arguments"); say("No argument specified"); delete_who_item(new_w); return; } if (!channel && (new_w->who_mask & WHO_OPS)) { channel = "*.*"; WHO_DEBUG("WHOBASE: Fallback to WHO %s", channel); } new_w->who_target = malloc_strdup(channel); WHO_DEBUG("WHOBASE: Target is [%s]", new_w->who_target); who_queue_add(refnum, new_w); /* * Check to see if we can piggyback */ old = who_previous_query(refnum, new_w); if (old && !old->dirty && old->who_target && channel && !strcmp(old->who_target, channel)) { old->piggyback = 1; WHO_DEBUG("WHOBASE: Piggybacking onto refnum [%d]", old->refnum); if (x_debug & DEBUG_OUTBOUND) yell("Piggybacking this WHO onto last one."); } else if (new_w->undernet_extended) { WHO_DEBUG("UNET QUERY: [%d] WHO %s %s%s%s", refnum, new_w->who_target, (new_w->who_mask & WHO_OPS) ? "o" : "", (new_w->who_mask & WHO_INVISIBLE) ? "x" : "", new_w->undernet_extended_args ? new_w->undernet_extended_args : ""); send_to_aserver(refnum, "WHO %s %s%s%s", new_w->who_target, (new_w->who_mask & WHO_OPS) ? "o" : "", (new_w->who_mask & WHO_INVISIBLE) ? "x" : "", new_w->undernet_extended_args ? new_w->undernet_extended_args : ""); } else if (new_w->dalnet_extended) { WHO_DEBUG("DALNET QUERY: [%d] WHO %s %s", refnum, new_w->dalnet_extended_args, new_w->who_target); send_to_aserver(refnum, "WHO %s %s", new_w->dalnet_extended_args, new_w->who_target); } else if (new_w->who_mask & WHO_OPERSPY) { WHO_DEBUG("OPERSPY QUERY: [%d] OPERSPY WHO %s %s%s", refnum, new_w->who_target, (new_w->who_mask & WHO_OPS) ? "o" : "", (new_w->who_mask & WHO_INVISIBLE) ? "x" : ""); send_to_aserver(refnum, "OPERSPY WHO %s %s%s", new_w->who_target, (new_w->who_mask & WHO_OPS) ? "o" : "", (new_w->who_mask & WHO_INVISIBLE) ? "x" : ""); } else { WHO_DEBUG("STD WHO: [%d] WHO %s %s%s", refnum, new_w->who_target, (new_w->who_mask & WHO_OPS) ? "o" : "", (new_w->who_mask & WHO_INVISIBLE) ? "x" : ""); send_to_aserver(refnum, "WHO %s %s%s", new_w->who_target, (new_w->who_mask & WHO_OPS) ? "o" : "", (new_w->who_mask & WHO_INVISIBLE) ? "x" : ""); } } void whoreply (int refnum, const char *from, const char *comm, const char **ArgList) { static char format[50]; static int last_width = -1; int ok = 1; const char *channel, *user, *host, *server, *nick, *status, *ircname; char *name; WhoEntry *new_w = who_queue_top(refnum); int l; if (!new_w) { WHO_DEBUG("WHOREPLY: server [%d] queue empty.", refnum); new_w = get_new_who_entry(); new_w->line = NULL; new_w->end = NULL; new_w->who_target = malloc_strdup(star); who_queue_add(refnum, new_w); } if (new_w->undernet_extended) { WHO_DEBUG("WHOREPLY: Server [%d], who refnum [%d], " "Unet request, Std reply.", refnum, new_w->refnum); yell("### You asked for an extended undernet request but " "didn't get one back. ###"); } /* Who replies always go to the current window. */ l = message_from(new_w->who_target, LEVEL_OTHER); do { /* * We have recieved a reply to this query -- its too late to * piggyback it now! */ if (new_w->dirty == 0) { WHO_DEBUG("WHOREPLY: Server [%d], who_refnum [%d]: " "Reply is now dirty", refnum, new_w->refnum); new_w->dirty = 1; } else WHO_DEBUG("WHOREPLY: Server [%d], who_refnum [%d]: Processing", refnum, new_w->refnum); /* * We dont always want to use this function. * If another function is supposed to do the work for us, * we yield to them. */ if (new_w->line) { WHO_DEBUG("WHOREPLY: Dispatching to callback", new_w->refnum); new_w->line(refnum, from, comm, ArgList); continue; } if (last_width != get_int_var(CHANNEL_NAME_WIDTH_VAR)) { if ((last_width = get_int_var(CHANNEL_NAME_WIDTH_VAR)) != 0) snprintf(format, sizeof format, "%%-%u.%us %%-9s %%-3s %%s@%%s (%%s)", (unsigned) last_width, (unsigned) last_width); else strlcpy(format, "%s\t%-9s %-3s %s@%s (%s)", sizeof format); } if (!(channel = ArgList[0])) { rfc1459_odd(from, comm, ArgList); break; } if (!(user = ArgList[1])) { rfc1459_odd(from, comm, ArgList); break; } if (!(host = ArgList[2])) { rfc1459_odd(from, comm, ArgList); break; } if (!(server = ArgList[3])) { rfc1459_odd(from, comm, ArgList); break; } if (!(nick = ArgList[4])) { rfc1459_odd(from, comm, ArgList); break; } if (!(status = ArgList[5])) { rfc1459_odd(from, comm, ArgList); break; } PasteArgs(ArgList, 6); if (!(ircname = ArgList[6])) { rfc1459_odd(from, comm, ArgList); break; } name = LOCAL_COPY(ircname); if (*status == 'S') /* this only true for the header WHOREPLY */ { char buffer[1024]; channel = "Channel"; snprintf(buffer, 1024, "%s %s %s %s %s %s %s", channel, nick, status, user, host, server, name); if (new_w->who_stuff) ; /* munch it */ else if (do_hook(WHO_LIST, "%s", buffer)) put_it(format, channel, nick, status, user, host, name); return; } if (new_w && new_w->who_mask) { if (new_w->who_mask & WHO_HERE) ok = ok && (*status == 'H'); if (new_w->who_mask & WHO_AWAY) ok = ok && (*status == 'G'); if (new_w->who_mask & WHO_OPS) ok = ok && (*(status + 1) == '*'); if (new_w->who_mask & WHO_LUSERS) ok = ok && (*(status + 1) != '*'); if (new_w->who_mask & WHO_CHOPS) ok = ok && ((*(status + 1) == '@') || (*(status + 2) == '@') || (*(status + 3) == '@')); if (new_w->who_mask & WHO_NOCHOPS) ok = ok && ((*(status + 1) != '@') && (*(status + 2) != '@') && (*(status + 3) != '@')); if (new_w->who_mask & WHO_NAME) ok = ok && wild_match(new_w->who_name, user); if (new_w->who_mask & WHO_NICK) ok = ok && wild_match(new_w->who_nick, nick); if (new_w->who_mask & WHO_HOST) ok = ok && wild_match(new_w->who_host, host); if (new_w->who_mask & WHO_REAL) { char *copy; /* First match, including the hopcount */ ok = ok && wild_match(new_w->who_real, name); /* Then remove the hopcount and try again. */ copy = LOCAL_COPY(name); new_next_arg(name, &name); ok = ok && wild_match(new_w->who_real, name); name = copy; } if (new_w->who_mask & WHO_SERVER) ok = ok && wild_match(new_w->who_server, server); } if (ok) { char buffer[1024]; snprintf(buffer, 1023, "%s %s %s %s %s %s %s", channel, nick, status, user, host, server, name); if (new_w->who_stuff) call_lambda_command("WHO", new_w->who_stuff, buffer); else if (do_hook(WHO_LIST, "%s", buffer)) if (do_hook(current_numeric, "%s", buffer)) put_it(format, channel, nick, status, user, host, name); } } while (new_w->piggyback && (new_w = new_w->next)); WHO_DEBUG("WHOREPLY: Done processing who reply server [%d]", refnum); pop_message_from(l); } /* Undernet's 354 numeric reply. */ void xwhoreply (int refnum, const char *from, const char *comm, const char **ArgList) { WhoEntry *new_w = who_queue_top(refnum); int l; if (!ArgList[0]) { rfc1459_odd(from, comm, ArgList); return; } if (!new_w) { new_w = get_new_who_entry(); new_w->line = NULL; new_w->end = NULL; new_w->who_target = malloc_strdup(star); new_w->undernet_extended = 1; who_queue_add(refnum, new_w); } if (!new_w->undernet_extended) yell("### You got an extended undernet request back " "even though you didn't ask for one. ###"); /* Who replies always go to the current window */ l = message_from(new_w->who_target, LEVEL_OTHER); PasteArgs(ArgList, 0); if (new_w->who_stuff) call_lambda_command("WHO", new_w->who_stuff, ArgList[0]); else if (do_hook(current_numeric, "%s", ArgList[0])) put_it("%s %s", banner(), ArgList[0]); pop_message_from(l); } void who_end (int refnum, const char *from, const char *comm, const char **ArgList) { WhoEntry *new_w = who_queue_top(refnum); char buffer[1025]; char *target; int l; if (!ArgList[0]) { rfc1459_odd(from, comm, ArgList); return; } if (!new_w || !new_w->who_target) { WHO_DEBUG("WHOEND: Server [%d], queue is empty.", refnum); return; } target = malloc_strdup(ArgList[0]); PasteArgs(ArgList, 0); l = message_from(new_w->who_target, LEVEL_OTHER); do { /* Defer to another function, if neccesary. */ if (new_w->end) new_w->end(refnum, from, comm, ArgList); else { snprintf(buffer, 1024, "%s %s", from, ArgList[0]); if (new_w->who_end) call_lambda_command("WHO_END", new_w->who_end, buffer); else if (do_hook(current_numeric, "%s", buffer)) put_it("%s %s", banner(), buffer); } if (strchr(new_w->who_target, ',') && !strchr(target, ',')) { WHO_DEBUG("WHOEND: Removing one response [%s] from comma-set [%s]", target, new_w->who_target); if (!remove_from_comma_list(new_w->who_target, target)) { 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); *new_w->who_target = 0; } } else if (strcmp(target, new_w->who_target)) { 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); *new_w->who_target = 0; } else { WHO_DEBUG("WHOEND: Server [%d], end of who for refnum [%d]/[%s]", refnum, new_w->refnum, new_w->who_target); *new_w->who_target = 0; } } while (new_w->piggyback && (new_w = new_w->next)); pop_message_from(l); if (new_w) { if (!*new_w->who_target) { WHO_DEBUG("Popping off for server [%d]", refnum); who_queue_pop(refnum); } else { 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); yell("WHOEND: Caution -- not popping off who queue -- [%s] is left on target list", new_w->who_target); } } /* XXX - There should probably be an else here, to cover when new_w had a piggyback, but there was nothing to piggyback on */ new_free(&target); } /* * Thanks be to the Dalnet coding team who, in their infinite wisdom, * decided to start returning error codes, rather than empty who replies, * for who requests that refered to channels or server or whatnot that * do not exist. So as to avoid corrupting the WHO queue, this function is * called to give us the opportunity to clean up after any who requests that * may be canceled because of the above lamage. * * Thanks be to the Dalnet coding team for returning error codes that do not * tell you what the original request was, making it neigh unto impossible * to correctly match up error codes to requests. Gee whiz, you wouldn't * think it would be that hard to get this right. */ int fake_who_end (int refnum, const char *from, const char *comm, const char *who_target) { WhoEntry *new_w = who_queue_top(refnum); int l; if (!new_w) return 0; /* Only honor these for dalnet extended requests. */ if (new_w->dalnet_extended == 0) return 0; if (who_target != NULL) { char *target; target = LOCAL_COPY(who_target); while (last_char(target) == ' ') chop(target, 1); /* * So 'who_target' isn't NULL here. Make sure it's a * legitimate match to our current top of who request. */ if (strncmp(new_w->who_target, target, strlen(target))) return 0; /*who_target = target; */ } l = message_from(new_w->who_target, LEVEL_OTHER); do { /* Defer to another function, if neccesary. */ if (new_w->end) { const char *fake_ArgList[3]; /* Fabricate a fake argument list */ fake_ArgList[0] = new_w->who_target; fake_ArgList[1] = "fake_who_end"; fake_ArgList[2] = NULL; new_w->end(refnum, from, comm, fake_ArgList); } else if (new_w->who_end) { char buffer[1025]; snprintf(buffer, 1024, "%s %s", from, new_w->who_target); call_lambda_command("WHO_END", new_w->who_end, buffer); } } while (new_w->piggyback && (new_w = new_w->next)); pop_message_from(l); who_queue_pop(refnum); return 1; } /* * * * * ISON QUEUE * * * */ static void ison_queue_add (int refnum, IsonEntry *item, int next) { Server *s; IsonEntry *bottom; if (!(s = get_server(refnum))) return; if (next) { item->next = s->ison_wait; s->ison_wait = item; return; } bottom = s->ison_wait; while (bottom && bottom->next) bottom = bottom->next; if (!bottom) s->ison_wait = item; else bottom->next = item; return; } static void ison_queue_send (int refnum) { int count = 1; Server *s; IsonEntry *save, *bottom; if (!(s = get_server(refnum))) return; if (!(save = s->ison_wait)) return; bottom = s->ison_queue; while (bottom) { if (s->ison_max && ++count > s->ison_max) return; else if (bottom->next) bottom = bottom->next; else break; } s->ison_wait = save->next; if (bottom) bottom->next = save; else s->ison_queue = save; save->next = NULL; send_to_aserver(refnum, "ISON %s", save->ison_asked); } static void ison_entry_pop (IsonEntry **entry) { IsonEntry *save = (*entry)->next; new_free(&(*entry)->ison_asked); new_free(&(*entry)->ison_got); new_free(&(*entry)->oncmd); new_free(&(*entry)->offcmd); new_free(&(*entry)->endcmd); new_free((char **)entry); *entry = save; return; } static void ison_queue_pop (int refnum) { Server *s; if (!(s = get_server(refnum))) return; ison_entry_pop(&(s->ison_queue)); } static void ison_wait_pop (int refnum) { Server *s; if (!(s = get_server(refnum))) return; ison_entry_pop(&(s->ison_wait)); } static IsonEntry *ison_queue_top (int refnum) { Server *s; if (!(s = get_server(refnum))) return NULL; return s->ison_queue; } static IsonEntry *ison_wait_top (int refnum) { Server *s; if (!(s = get_server(refnum))) return NULL; return s->ison_wait; } static IsonEntry *get_new_ison_entry (int refnum, int next) { IsonEntry *new_w; if (!get_server(refnum)) return NULL; new_w = (IsonEntry *)new_malloc(sizeof(IsonEntry)); new_w->ison_asked = NULL; new_w->ison_got = NULL; new_w->next = NULL; new_w->line = NULL; new_w->oncmd = NULL; new_w->offcmd = NULL; new_w->endcmd = NULL; ison_queue_add(refnum, new_w, next); return new_w; } static void ison_queue_list (int refnum) { Server *s; IsonEntry *item; int count = 0; if (!(s = get_server(refnum))) return; for (item = s->ison_queue; item; item = item->next, count++) { yell("[%d] [%s] ["UINTMAX_HEX_FORMAT"]", count, item->ison_asked, (uintmax_t)item->line); } for (item = s->ison_wait; item; item = item->next, count++) { yell("[%d] [%s] ["UINTMAX_HEX_FORMAT"] (pending)", count, item->ison_asked, (uintmax_t)item->line); } } BUILT_IN_COMMAND(isoncmd) { if (!args || !*args) args = LOCAL_COPY(get_server_nickname(from_server)); isonbase(from_server, args, NULL); } void isonbase (int refnum, char *args, void (*line) (int, char *, char *)) { IsonEntry *new_i; char *next = NULL, *end, *p; char *on_cmd = NULL, *offcmd = NULL, *endcmd = NULL; int sendnext = 0, nicks; /* Maybe should output a warning? */ if (!is_server_registered(refnum)) return; while (args && *args == '-') { char *arg = next_arg(args, &args); char *stuff; if (!my_stricmp(arg, "-d")) { ison_queue_list(refnum); } if (!my_stricmp(arg, "-f")) { while (ison_queue_top(refnum)) ison_queue_pop(refnum); while (ison_wait_top(refnum)) ison_wait_pop(refnum); } if (!my_stricmp(arg, "-s")) { ison_queue_send(refnum); } if (!my_stricmp(arg, "-n")) { sendnext++; } if (!my_stricmp(arg, "-e")) { if (get_server(refnum)) if (get_server(refnum)->ison_wait) return; } if (!my_stricmp(arg, "-len")) { if ((stuff = next_arg(args, &args))) { if (get_server(refnum)) get_server(refnum)->ison_len = MAX(10, atol(stuff)); } else say("Need numeric argument for -LEN argument."); } if (!my_stricmp(arg, "-nicks")) { if ((stuff = next_arg(args, &args))) { if (get_server(refnum)) get_server(refnum)->ison_nicks = MAX(5, atol(stuff)); } else say("Need numeric argument for -NICKS argument."); } if (!my_stricmp(arg, "-max")) { if ((stuff = next_arg(args, &args))) { if (get_server(refnum)) get_server(refnum)->ison_max = atol(stuff); } else say("Need numeric argument for -MAX argument."); } if (!my_stricmp(arg, "-oncmd")) { if ((stuff = next_expr(&args, '{'))) on_cmd = stuff; else say("Need {...} argument for -ONCMD argument."); } if (!my_stricmp(arg, "-offcmd")) { if ((stuff = next_expr(&args, '{'))) offcmd = stuff; else say("Need {...} argument for -OFFCMD argument."); } if (!my_stricmp(arg, "-end")) { if ((stuff = next_expr(&args, '{'))) endcmd = stuff; else say("Need {...} argument for -END argument."); } } ison_queue_send(refnum); if (!args) return; /* So now we need to go through 'args', collecting the nicks into groups * of at most ison_len and sending those off. Two things conspire * to make this harder than it sounds, however: an individual nick might * be longer than ison_len on its own, in which case we have to ignore it; * and if an endcmd was supplied then we must only add it to the _last_ * queued ison command. We don't know if a given queued ison command * is going to be the last one until we look ahead and hit the end of * the arguments. */ new_i = NULL; end = NULL; p = args; nicks = 0; while (*p) { /* Loop pre-conditions: * * p is pointing at start of next argument to examine, or at the * terminating null. * * end is pointing at the end of the current list of nicks to send, * or NULL if that list is empty. * * if end != NULL, next is pointing at start of the current list of * nicks to send. * * if new_i != NULL && end != NULL, new_i is pointing at the ison * entry for the current list of nicks to send (new_i->ison_asked is * is NOT set yet). * * if new_i != NULL && end == NULL then new_i is pointing at the ison * entry for the previous list of nicks to send (new_i->ison_asked is * set). */ if (!end) next = p; while (*p && !isspace((unsigned char)*p)) p++; /* You know, just in case something happened... */ if (get_server(refnum)) { if ((p - next < get_server(refnum)->ison_len) && (++nicks <= get_server(refnum)->ison_nicks)) { /* can add this nick to the current list. */ if (!end) { /* This is the first nick of a new ison list, so now we know that * the previous list, if there was one, wasn't the last, so we can * send it off without an endcmd. */ if (new_i) ison_queue_send(refnum); if (!(new_i = get_new_ison_entry(refnum, sendnext))) { privileged_yell("isonbase: Could not create a new ison entry for %d [%d]", refnum, sendnext); continue; } new_i->line = line; malloc_strcpy(&new_i->oncmd, on_cmd); malloc_strcpy(&new_i->offcmd, offcmd); } end = p; } else { /* This nick is too long to add to the current list. */ if (end) { /* ..but there are some preceding words that we can send */ char restore = *end; *end = 0; malloc_strcpy(&new_i->ison_asked, next); *end = restore; /* Note that we do NOT send the ison queue yet, because we * don't yet know if we should set endcmd or not. */ p = end; end = NULL; nicks = 0; } } } /* Advance to next nick */ while (isspace((unsigned char)*p)) p++; } if (new_i) { /* Send the last list - this one has endcmd set */ if (end) { /* ison_asked hasn't been set yet */ char restore = *end; *end = 0; malloc_strcpy(&new_i->ison_asked, next); *end = restore; } malloc_strcpy(&new_i->endcmd, endcmd); ison_queue_send(refnum); } } /* * ison_returned: this is called when numeric 303 is received in * numbers.c. ISON must always be the property of the WHOIS queue. * Although we will check first that the top element expected is * actually an ISON. */ void ison_returned (int refnum, const char *from, const char *comm, const char **ArgList) { IsonEntry *new_i = ison_queue_top(refnum); char *do_off = NULL, *this1, *all1, *this2, *all2; size_t clue = 0; if (!ArgList[0]) { rfc1459_odd(from, comm, ArgList); return; } if (!new_i) { /* XXX Hack to work around rogue /quote ison's */ if (do_hook(current_numeric, "%s", ArgList[0])) put_it("%s Currently online: %s", banner(), ArgList[0]); return; } all1 = LOCAL_COPY(new_i->ison_asked); all2 = LOCAL_COPY(ArgList[0]); if (new_i->offcmd) while ((this2 = next_arg(all2, &all2))) while ((this1 = next_arg(all1, &all1)) && my_stricmp(this1, this2)) malloc_strcat_wordlist_c(&do_off, space, this1, &clue); malloc_strcat_wordlist_c(&do_off, space, all1, &clue); PasteArgs(ArgList, 0); if (new_i->line) { char *ison_ret = LOCAL_COPY(ArgList[0]); new_i->line(refnum, new_i->ison_asked, ison_ret); } else { if (new_i->oncmd && ArgList[0] && ArgList[0][0]) call_lambda_command("ISON", new_i->oncmd, ArgList[0]); if (new_i->offcmd && do_off && *do_off) call_lambda_command("ISON", new_i->offcmd, do_off); if (new_i->endcmd) call_lambda_command("ISON", new_i->endcmd, NULL); if (!new_i->oncmd && !new_i->offcmd && do_hook(current_numeric, "%s", ArgList[0])) put_it("%s Currently online: %s", banner(), ArgList[0]); } new_free(&do_off); ison_queue_pop(refnum); ison_queue_send(refnum); return; } /* * * * * * USERHOST QUEUE * * * * */ static UserhostEntry *userhost_queue_top (int refnum) { Server *s; if (!(s = get_server(refnum))) return NULL; return s->userhost_queue; } static UserhostEntry *userhost_wait_top (int refnum) { Server *s; if (!(s = get_server(refnum))) return NULL; return s->userhost_wait; } static void userhost_queue_add (int refnum, UserhostEntry *item) { UserhostEntry *bottom; Server *s; if (!(s = get_server(refnum))) return; bottom = s->userhost_wait; while (bottom && bottom->next) bottom = bottom->next; if (!bottom) s->userhost_wait = item; else bottom->next = item; return; } static void userhost_queue_send (int refnum) { int count = 1; Server *s; UserhostEntry *save, *bottom; if (!(s = get_server(refnum))) return; if (!(save = s->userhost_wait)) return; bottom = s->userhost_queue; while (bottom) { if (s->userhost_max && ++count > s->userhost_max) return; else if (bottom->next) bottom = bottom->next; else break; } s->userhost_wait = save->next; if (bottom) bottom->next = save; else s->userhost_queue = save; save->next = NULL; send_to_aserver(refnum, save->format, save->userhost_asked); } static void userhost_entry_pop (UserhostEntry **entry) { UserhostEntry *save = (*entry)->next; /* XXX But what if *entry is null? */ new_free(&(*entry)->userhost_asked); new_free(&(*entry)->text); new_free((char **)entry); *entry = save; return; } static void userhost_queue_pop (int refnum) { Server *s; if (!(s = get_server(refnum))) return; userhost_entry_pop(&(s->userhost_queue)); } static void userhost_wait_pop (int refnum) { Server *s; if (!(s = get_server(refnum))) return; userhost_entry_pop(&(s->userhost_wait)); } static UserhostEntry *get_new_userhost_entry (int refnum) { UserhostEntry *new_u = (UserhostEntry *)new_malloc(sizeof(UserhostEntry)); new_u->format = NULL; new_u->userhost_asked = NULL; new_u->text = NULL; new_u->next = NULL; new_u->func = NULL; userhost_queue_add(refnum, new_u); return new_u; } /* * userhost: Does the USERHOST command. Need to split up the queries, * since the server will only reply to 5 at a time. */ BUILT_IN_COMMAND(userhostcmd) { userhostbase(from_server, args, subargs, NULL, 1); } BUILT_IN_COMMAND(useripcmd) { userhostbase(from_server, args, subargs, NULL, 0); } BUILT_IN_COMMAND(usripcmd) { userhostbase(from_server, args, subargs, NULL, 2); } void userhostbase (int refnum, char *args, const char *subargs, void (*line) (int, UserhostItem *, const char *, const char *), int do_userhost) { int total = 0, userhost_cmd = 0; int server_query_reqd = 0; char *nick; char buffer[BIG_BUFFER_SIZE + 1]; char *ptr, *next_ptr, *body = NULL; int count = 5; char *extra = NULL; /* Maybe should output a warning? */ if (!is_server_registered(refnum)) return; *buffer = 0; while ((nick = next_arg(args, &args)) != NULL) { if (check_nickname(nick, 1) || is_number(nick)) { total++; if (!fetch_userhost(refnum, NULL, nick)) server_query_reqd++; if (*buffer) strlcat(buffer, " ", sizeof buffer); strlcat(buffer, nick, sizeof buffer); } else if (!my_strnicmp(nick, "-direct", 2)) server_query_reqd++; else if (!my_strnicmp(nick, "-count", 3)) count = atol(safe_new_next_arg(args, &args)); else if (!my_strnicmp(nick, "-cmd", 2)) { if (!total) { if (do_userhost == 1) say("USERHOST -cmd with no nicks specified"); else if (do_userhost == 0) say("USERIP -cmd with no nicks specified"); else say("USRIP -cmd with no nicks specified"); return; } while (my_isspace(*args)) args++; if (!(body = next_expr_failok(&args, '{'))) /* } */ body = args; userhost_cmd = 1; break; } else if (!my_strnicmp(nick, "-extra", 2)) { char *extravar = next_arg(args, &args); if (empty(extravar)) { say("Need argument to /userhost -extra"); break; } if (extra) new_free(&extra); /* XXX But what if extravar contains []s? */ extra = get_variable(extravar); } else if (!my_strnicmp(nick, "-flush", 2)) { while (userhost_wait_top(refnum)) userhost_wait_pop(refnum); return; } } if (!userhost_cmd && !total) { server_query_reqd++; strlcpy(buffer, get_server_nickname(refnum), sizeof buffer); } ptr = buffer; if (server_query_reqd || (!line && !userhost_cmd)) { ptr = buffer; while (ptr && *ptr) { UserhostEntry *new_u = get_new_userhost_entry(refnum); move_to_abs_word(ptr, (const char **)&next_ptr, count); if (next_ptr && *next_ptr && next_ptr > ptr) next_ptr[-1] = 0; new_u->userhost_asked = malloc_strdup(ptr); if (do_userhost == 1) new_u->format = "USERHOST %s"; else if (do_userhost == 0) new_u->format = "USERIP %s"; else new_u->format = "USRIP %s"; userhost_queue_send(refnum); if (userhost_cmd) new_u->text = malloc_strdup(body); if (line) new_u->func = line; else if (userhost_cmd) new_u->func = userhost_cmd_returned; else new_u->func = NULL; new_u->extra = extra; ptr = next_ptr; } } else { while (ptr && *ptr) { char *my_nick = next_arg(ptr, &ptr); const char *ouh = fetch_userhost(refnum, NULL, my_nick); char *uh, *host; UserhostItem item = {NULL, 0, 0, 0, NULL, NULL, NULL}; uh = LOCAL_COPY(ouh); item.nick = my_nick; item.oper = 0; item.connected = 1; item.away = 0; item.user = uh; item.extra = extra; host = strchr(uh, '@'); if (host) { *host++ = 0; item.host = host; } else item.host = ""; if (line) line(refnum, &item, my_nick, body); else if (userhost_cmd) userhost_cmd_returned(refnum, &item, my_nick, body); else yell("Yowza! I dont know what to do here!"); } new_free(&extra); } } /* * userhost_returned: this is called when numeric 302 is received in * numbers.c. USERHOST must always remain the property of the userhost * queue. Sending out USERHOST requests to the server without going * through this queue will cause it to be corrupted and the client will * go higgledy-piggledy. */ void userhost_returned (int refnum, const char *from, const char *comm, const char **ArgList) { UserhostEntry *top = userhost_queue_top(refnum); char *ptr; char *results; if (!ArgList[0]) { rfc1459_odd(from, comm, ArgList); return; } if (!top) { yell("### Please don't /quote a server command that returns the 302 numeric."); return; } PasteArgs(ArgList, 0); results = LOCAL_COPY(ArgList[0]); ptr = top->userhost_asked; /* * Go through the nicknames that were requested... */ while (ptr && *ptr) { /* * Grab the next nickname */ char * cnick; size_t len; cnick = next_arg(ptr, &ptr); len = strlen(cnick); /* * Now either it is present at the next argument * or its not. If it is, it will match the first * part of ArgList, and the following char will * either be a * or an = (eg, nick*= or nick=) */ if (results && (!my_strnicmp(cnick, results, len) && (results[len] == '*' || results[len] == '='))) { UserhostItem item; char *nick, *user, *host; /* Extract all the interesting info */ item.connected = 1; nick = next_arg(results, &results); user = strchr(nick, '='); if (!user) { yell("Can't parse useless USERHOST reply [%s]", ArgList[0]); userhost_queue_pop(refnum); return; } if (user[-1] == '*') { user[-1] = 0; item.oper = 1; } else item.oper = 0; if (user[1] == '+') item.away = 0; else item.away = 1; *user++ = 0; user++; host = strchr(user, '@'); if (!host) { yell("Can't parse useless USERHOST reply [%s]", ArgList[0]); userhost_queue_pop(refnum); return; } *host++ = 0; item.nick = nick; item.user = user; item.host = host; item.extra = SAFE(top->extra); /* * If the user wanted a callback, then * feed the callback with the info. */ if (top->func) top->func(refnum, &item, cnick, top->text); /* * Otherwise, the user just did /userhost, * so we offer the numeric, and if the user * doesnt bite, we output to the screen. */ else if (do_hook(current_numeric, "%s %s %s %s %s %s", item.nick, item.oper ? "+" : "-", item.away ? "-" : "+", item.user, item.host, item.extra)) put_it("%s %s is %s@%s%s%s %s", banner(), item.nick, item.user, item.host, item.oper ? " (Is an IRC operator)" : empty_string, item.away ? " (away)" : empty_string, item.extra ? item.extra : empty_string); } /* * If ArgList isnt the current nick, then the current nick * must not be on irc. So we whip up a dummy UserhostItem * and send it on its way. We DO NOT HOOK the 302 numeric * with this bogus entry, because thats the historical * behavior. This can cause a problem if you do a USERHOST * and wait on the 302 numeric. I think waiting on the 302 * numeric is stupid, anyhow. */ else { /* * Of course, only if the user asked for a callback * via /userhost -cmd or a direct call to userhostbase. * If the user just did /userhost, and the nicks arent * on, then we just dont display anything. */ if (top->func) { UserhostItem item; item.nick = cnick; item.user = item.host = ""; item.oper = item.away = 0; item.connected = 1; item.extra = top->extra; top->func(refnum, &item, cnick, top->text); } } } userhost_queue_pop(refnum); userhost_queue_send(refnum); } void userhost_cmd_returned (int refnum, UserhostItem *stuff, const char *nick, const char *text) { char *args = NULL; size_t clue = 0; /* This should be safe, though its playing it fast and loose */ malloc_strcat_c(&args, stuff->nick ? stuff->nick : empty_string, &clue); malloc_strcat_c(&args, stuff->oper ? " + " : " - ", &clue); malloc_strcat_c(&args, stuff->away ? "+ " : "- ", &clue); malloc_strcat_c(&args, stuff->user ? stuff->user : empty_string, &clue); malloc_strcat_c(&args, space, &clue); malloc_strcat_c(&args, stuff->host ? stuff->host : empty_string, &clue); malloc_strcat_c(&args, space, &clue); malloc_strcat_c(&args, stuff->extra ? stuff->extra : empty_string, &clue); call_lambda_command("USERHOST", text, args); new_free(&args); } void clean_server_queues (int i) { while (who_queue_top(i)) who_queue_pop(i); while (ison_queue_top(i)) ison_queue_pop(i); while (ison_wait_top(i)) ison_wait_pop(i); while (userhost_queue_top(i)) userhost_queue_pop(i); while (userhost_wait_top(i)) userhost_wait_pop(i); } /* XXXX */ static int who_queue_debug (void *unused) { Server *s; int refnum; WhoEntry *item; double d; static int last_refnum_checked = 0; for (refnum = 0; refnum < number_of_servers; refnum++) { if (!(s = get_server(refnum))) continue; for (item = s->who_queue; item; item = item->next) { if (item->refnum >= last_refnum_checked + 100) { WHO_DEBUG("WATCHER: Who refnum is up to [%d]", item->refnum); last_refnum_checked = item->refnum; } if (item->dirty == 0) { d = time_diff(item->request_time, now); if (d >= 15) { yell("Warning: who item [%d] (server %d) is not dirty and > 15 seconds old", item->refnum, refnum); WHO_DEBUG("WATCHER: Who item [%d] is not dirty, > 15 seconds old", item->refnum); WHO_DEBUG("WATCHER: [%s]", who_item_full_desc(item)); yell("Flushing who queue for server [%d] -- check logs", refnum); who_queue_flush(refnum); } } else { d = time_diff(item->request_time, now); if (d >= 30) { yell("Warning: who item [%d] (server %d) is dirty and > 30 seconds old", item->refnum, refnum); WHO_DEBUG("WATCHER: Who item [%d] is dirty, > 30 seconds old", item->refnum); WHO_DEBUG("WATCHER: [%s]", who_item_full_desc(item)); yell("Flushing who queue for server [%d] -- check logs", refnum); who_queue_flush(refnum); } else { d = time_diff(item->dirty_time, now); if (d >= 15) { yell("Warning: who item [%d] (server %d) is dirty for > 15 seconds", item->refnum, refnum); WHO_DEBUG("WATCHER: Who item [%d] is dirty > 15 seconds", item->refnum); WHO_DEBUG("WATCHER: [%s]", who_item_full_desc(item)); } yell("Flushing who queue for server [%d] -- check logs", refnum); who_queue_flush(refnum); } } } } return 0; }