-#include <stdio.h>
#include <string.h>
+#include <stdio.h>
#include "../core/hooks.h"
#include "../control/control.h"
#include "../irc/irc.h"
#include "../lib/irc_string.h"
+#include "../lib/version.h"
+#include "../core/config.h"
#include "whowas.h"
-static whowas *wwhead, *wwtail;
-static int wwcount;
+MODULE_VERSION("");
-int ww_cmdwhowas(void *source, int cargc, char **cargv) {
- nick *np = source;
- char *pattern;
+whowas *whowasrecs;
+int whowasoffset = 0;
+int whowasmax;
+
+whowas *whowas_fromnick(nick *np, int standalone) {
whowas *ww;
- char hostmask[WW_MASKLEN+1];
- char timebuf[30];
- int matches = 0, limit = 500;
+ nick *wnp;
+ struct irc_in_addr ipaddress_canonical;
+ void *args[2];
- if(cargc<1)
- return CMD_USAGE;
+ /* Create a new record. */
+ if (standalone)
+ ww = malloc(sizeof(whowas));
+ else {
+ ww = &whowasrecs[whowasoffset];
+ whowas_clean(ww);
+ whowasoffset = (whowasoffset + 1) % whowasmax;
+ }
- pattern = cargv[0];
+ memset(ww, 0, sizeof(whowas));
- if(cargc>1)
- limit = strtol(cargv[1], NULL, 10);
+ wnp = &ww->nick;
+ memset(wnp, 0, sizeof(nick));
+ strncpy(wnp->nick, np->nick, NICKLEN + 1);
+ wnp->numeric = np->numeric;
+ strncpy(wnp->ident, np->ident, USERLEN + 1);
- for(ww=wwhead;ww;ww=ww->next) {
- snprintf(hostmask, sizeof(hostmask), "%s!%s@%s", ww->nick, ww->ident, ww->host);
+ wnp->host = newhost();
+ memset(wnp->host, 0, sizeof(host));
+ wnp->host->name = getsstring(np->host->name->content, HOSTLEN);
- if (match2strings(pattern, hostmask)) {
- matches++;
+ wnp->realname = newrealname();
+ memset(wnp->realname, 0, sizeof(realname));
+ wnp->realname->name = getsstring(np->realname->name->content, REALLEN);
+ wnp->shident = np->shident ? getsstring(np->shident->content, 512) : NULL;
+ wnp->sethost = np->sethost ? getsstring(np->sethost->content, 512) : NULL;
+ wnp->opername = np->opername ? getsstring(np->opername->content, 512) : NULL;
+ wnp->umodes = np->umodes;
+ if (np->auth) {
+ wnp->auth = newauthname();
+ memset(wnp->auth, 0, sizeof(authname));
+ wnp->auth->userid = np->auth->userid;
+ strncpy(wnp->auth->name, np->auth->name, ACCOUNTLEN + 1);
+ wnp->authname = wnp->auth->name;
+ }
+ wnp->timestamp = np->timestamp;
+ wnp->accountts = np->accountts;
+ wnp->away = np->away ? getsstring(np->away->content, 512) : NULL;
- if(matches<=limit) {
- strftime(timebuf, 30, "%d/%m/%y %H:%M:%S", localtime(&(ww->seen)));
+ memcpy(&wnp->ipaddress, &np->ipaddress, sizeof(struct irc_in_addr));
- controlreply(np, "[%s] %s (%s): %s", timebuf, hostmask, ww->realname, ww->reason->content);
- } else if(matches==limit+1) {
- controlreply(np, "--- More than %d matches, skipping the rest", limit);
- }
- }
- }
+ ip_canonicalize_tunnel(&ipaddress_canonical, &np->ipaddress);
+ wnp->ipnode = refnode(iptree, &ipaddress_canonical, PATRICIA_MAXBITS);
+
+ wnp->next = (nick *)ww; /* Yuck. */
+
+ ww->timestamp = getnettime();
+ ww->type = WHOWAS_USED;
+
+ args[0] = ww;
+ args[1] = np;
+ triggerhook(HOOK_WHOWAS_NEWRECORD, args);
+
+ return ww;
+}
+
+void whowas_clean(whowas *ww) {
+ nick *np;
+
+ if (!ww || ww->type == WHOWAS_UNUSED)
+ return;
- controlreply(np, "--- Found %d entries.", matches);
+ triggerhook(HOOK_WHOWAS_LOSTRECORD, ww);
- return CMD_OK;
+ np = &ww->nick;
+ freesstring(np->host->name);
+ freehost(np->host);
+ freesstring(np->realname->name);
+ freerealname(np->realname);
+ freesstring(np->shident);
+ freesstring(np->sethost);
+ freesstring(np->opername);
+ freeauthname(np->auth);
+ freesstring(np->away);
+ derefnode(iptree, np->ipnode);
+ freesstring(ww->reason);
+ freesstring(ww->newnick);
+ ww->type = WHOWAS_UNUSED;
}
-void ww_handlequitorkill(int hooknum, void *arg) {
- void **args=arg;
- nick *np=args[0];
- char *reason=args[1];
+void whowas_free(whowas *ww) {
+ whowas_clean(ww);
+ free(ww);
+}
+
+static void whowas_handlequitorkill(int hooknum, void *arg) {
+ void **args = arg;
+ nick *np = args[0];
+ char *reason = args[1];
char *rreason;
char resbuf[512];
whowas *ww;
- time_t now;
- time(&now);
+ /* Create a new record. */
+ ww = whowas_fromnick(np, 0);
- /* Clean up old records. */
- while((ww = wwtail) && (ww->seen < now - WW_MAXAGE || wwcount >= WW_MAXENTRIES)) {
- wwtail = ww->prev;
+ if (hooknum == HOOK_NICK_KILL) {
+ if ((rreason = strchr(reason, ' '))) {
+ sprintf(resbuf, "Killed%s", rreason);
+ reason = resbuf;
+ }
- if (ww->prev)
- ww->prev->next = NULL;
+ ww->type = WHOWAS_KILL;
+ } else {
+ if (strncmp(reason, "G-lined", 7) == 0)
+ ww->type = WHOWAS_KILL;
else
- wwhead = ww->prev;
+ ww->type = WHOWAS_QUIT;
+ }
- wwcount--;
- free(ww);
+ ww->reason = getsstring(reason, WW_REASONLEN);
+}
+
+static void whowas_handlerename(int hooknum, void *arg) {
+ void **args = arg;
+ nick *np = args[0];
+ char *oldnick = args[1];
+ whowas *ww;
+ nick *wnp;
+
+ ww = whowas_fromnick(np, 0);
+ ww->type = WHOWAS_RENAME;
+ wnp = &ww->nick;
+ ww->newnick = getsstring(wnp->nick, NICKLEN);
+ strncpy(wnp->nick, oldnick, NICKLEN + 1);
+}
+
+whowas *whowas_chase(const char *target, int maxage) {
+ whowas *ww;
+ nick *wnp;
+ time_t now;
+ int i;
+
+ now = getnettime();
+
+ for (i = whowasoffset + whowasmax - 1; i >= whowasoffset; i--) {
+ ww = &whowasrecs[i % whowasmax];
+
+ if (ww->type == WHOWAS_UNUSED)
+ continue;
+
+ wnp = &ww->nick;
+
+ if (ww->timestamp < now - maxage)
+ break; /* records are in timestamp order, we're done */
+
+ if (ircd_strcmp(wnp->nick, target) == 0)
+ return ww;
}
- /* Create a new record. */
- ww = malloc(sizeof(whowas));
- strncpy(ww->nick, np->nick, NICKLEN);
- strncpy(ww->ident, np->ident, USERLEN);
- strncpy(ww->host, np->host->name->content, HOSTLEN);
- strncpy(ww->realname, np->realname->name->content, REALLEN);
- ww->seen = getnettime();
-
- if(hooknum==HOOK_NICK_KILL && (rreason=strchr(reason,' '))) {
- sprintf(resbuf,"Killed%s",rreason);
- reason=resbuf;
+ return NULL;
+}
+
+const char *whowas_format(whowas *ww) {
+ nick *np = &ww->nick;
+ static char buf[512];
+ char timebuf[30];
+ char hostmask[512];
+
+ snprintf(hostmask, sizeof(hostmask), "%s!%s@%s%s%s [%s] (%s)",
+ np->nick, np->ident, np->host->name->content,
+ np->auth ? "/" : "", np->auth ? np->authname : "",
+ IPtostr(np->ipaddress),
+ printflags(np->umodes, umodeflags));
+ strftime(timebuf, sizeof(timebuf), "%d/%m/%y %H:%M:%S", localtime(&(ww->timestamp)));
+
+ if (ww->type == WHOWAS_RENAME)
+ snprintf(buf, sizeof(buf), "[%s] NICK %s r(%s) -> %s", timebuf, hostmask, np->realname->name->content, ww->newnick->content);
+ else
+ snprintf(buf, sizeof(buf), "[%s] %s %s r(%s): %s", timebuf, (ww->type == WHOWAS_QUIT) ? "QUIT" : "KILL", hostmask, np->realname->name->content, ww->reason->content);
+
+ return buf;
+}
+
+const char *whowas_formatchannels(whowas *ww) {
+ static char buf[512];
+ int i, first = 1;
+
+ strcpy(buf, "Channels: ");
+
+ for (i = 0; i < WW_MAXCHANNELS; i++) {
+ if (!ww->channels[i])
+ break;
+
+ if (!first)
+ strncat(buf, ", ", sizeof(buf));
+ else
+ first = 0;
+
+ strncat(buf, ww->channels[i]->name->content, sizeof(buf));
}
- ww->reason = getsstring(reason, WW_REASONLEN);
+ if (!ww->channels[0])
+ strncat(buf, "(No channels.)", sizeof(buf));
- if(wwhead)
- wwhead->prev = ww;
+ buf[sizeof(buf) - 1] = '\0';
- ww->next = wwhead;
- wwhead = ww;
+ return buf;
+}
- ww->prev = NULL;
+unsigned int nextwhowasmarker() {
+ whowas *ww;
+ int i;
+ static unsigned int whowasmarker=0;
- if(!wwtail)
- wwtail = ww;
+ whowasmarker++;
- wwcount++;
+ if (!whowasmarker) {
+ /* If we wrapped to zero, zap the marker on all records */
+ for (i = 0; i < whowasmax; i++) {
+ ww = &whowasrecs[i % whowasmax];
+ ww->marker=0;
+ }
+
+ whowasmarker++;
+ }
+
+ return whowasmarker;
}
void _init(void) {
- registerhook(HOOK_NICK_QUIT, ww_handlequitorkill);
- registerhook(HOOK_NICK_KILL, ww_handlequitorkill);
+ {
+ sstring *temp = getcopyconfigitem("whowas", "maxentries", XStringify(WW_DEFAULT_MAXENTRIES), 10);
+ whowasmax = atoi(temp->content);
+ freesstring(temp);
+ }
+ whowasrecs = calloc(whowasmax, sizeof(whowas));
- registercontrolhelpcmd("whowas", NO_OPER, 2, &ww_cmdwhowas, "Usage: whowas <mask> ?limit?\nLooks up information about recently disconnected users.");
+ registerhook(HOOK_NICK_QUIT, whowas_handlequitorkill);
+ registerhook(HOOK_NICK_KILL, whowas_handlequitorkill);
+ registerhook(HOOK_NICK_RENAME, whowas_handlerename);
}
void _fini(void) {
+ int i;
whowas *ww;
- deregisterhook(HOOK_NICK_QUIT, ww_handlequitorkill);
- deregisterhook(HOOK_NICK_KILL, ww_handlequitorkill);
-
- deregistercontrolcmd("whowas", &ww_cmdwhowas);
+ deregisterhook(HOOK_NICK_QUIT, whowas_handlequitorkill);
+ deregisterhook(HOOK_NICK_KILL, whowas_handlequitorkill);
+ deregisterhook(HOOK_NICK_RENAME, whowas_handlerename);
- while((ww = wwhead)) {
- wwhead = ww->next;
- free(ww);
+ for (i = 0; i < whowasmax; i++) {
+ ww = &whowasrecs[i];
+ whowas_clean(ww);
}
+
+ free(whowasrecs);
}