]> jfr.im git - irc/quakenet/newserv.git/blobdiff - whowas/whowas.c
TRUSTS: require sqlite
[irc/quakenet/newserv.git] / whowas / whowas.c
index 2132e90f7a5048a0aa0812e89ecadf5487febb25..9e2cdb3c74d6f9e753a3e3445a24a2549e514a51 100644 (file)
-#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);
 }