]> jfr.im git - irc/quakenet/newserv.git/commitdiff
Merge shroudcloak.
authorGunnar Beutner <redacted>
Sat, 20 Jul 2013 20:11:08 +0000 (22:11 +0200)
committerGunnar Beutner <redacted>
Sat, 20 Jul 2013 20:11:08 +0000 (22:11 +0200)
25 files changed:
chanserv/chancmds/addchan.c
chanserv/chancmds/adduser.c
chanserv/chancmds/chanlev.c
chanserv/chanserv.h
chanserv/chanserv_messages.h
core/modules.c
fakeq/fakeq.c
invalidbans/Makefile.in [new file with mode: 0644]
invalidbans/invalidbans.c [new file with mode: 0644]
newsearch/ns-gline.c
nick/nickhandlers.c
request/Makefile.in
request/lrequest.c
request/request.c
request/request.h
request/request_block.c
request/request_block.h
request/request_fasttrack.c [new file with mode: 0644]
request/request_fasttrack.h [new file with mode: 0644]
request/sqrequest.c
request/user.c [deleted file]
request/user.h [deleted file]
whowas/Makefile.in [new file with mode: 0644]
whowas/whowas.c [new file with mode: 0644]
whowas/whowas.h [new file with mode: 0644]

index ec4e13cbd3c8957d59128a62cf379c2fa8c7816d..a77e2258505883a801a529a55f30737dbeb102ba 100644 (file)
@@ -37,7 +37,7 @@ int csc_doaddchan(void *source, int cargc, char **cargv) {
   reguser *founder;
   flag_t flags;
   short type=0;
-  unsigned int i;
+  unsigned int i, count;
   void *args[3];
   
   if (!rup)
@@ -102,7 +102,19 @@ int csc_doaddchan(void *source, int cargc, char **cargv) {
       chanservstdmessage(notify, QM_ALREADYREGISTERED, cip->name->content);
     return CMD_ERROR;
   }
-  
+
+  count = 0;
+
+  for (rcup=founder->knownon;rcup;rcup=rcup->nextbyuser)
+    count++; 
+
+  if (count > MAXCHANNELS) {
+    chanservstdmessage(sender, QM_TOOMANYCHANNELS, cip->name->content);
+    if (notify)
+      chanservstdmessage(sender, QM_TOOMANYCHANNELS, cip->name->content);
+    return CMD_ERROR;
+  }
+
   /* Initialise the channel */ 
   rcp=getregchan();
   
index 556b32ac93739ddbebbbaacb63cbec1e88411594..b5e544f13c0bb6c50be79c7b914512fb28ac35ff 100644 (file)
 int csc_doadduser(void *source, int cargc, char **cargv) {
   nick *sender=source;
   chanindex *cip;
-  regchanuser *rcup, *rcuplist;
+  regchanuser *rcup, *trcup, *rcuplist;
   regchan *rcp;
   reguser *rup;
   flag_t addflags;
   char *flagbuf;
-  unsigned int count=0;
+  unsigned int chanlevcount=0, channelcount=0;
   int added=0;
   int i, foundflags=0;
   void *args[3];
@@ -82,9 +82,9 @@ int csc_doadduser(void *source, int cargc, char **cargv) {
   flagbuf=printflags(addflags, rcuflags);  
 
   /* ugh */
-  for (count=i=0;i<REGCHANUSERHASHSIZE;i++)
+  for (chanlevcount=i=0;i<REGCHANUSERHASHSIZE;i++)
     for (rcuplist=rcp->regusers[i];rcuplist;rcuplist=rcuplist->nextbychan)
-      count++;
+      chanlevcount++;
 
   /* If we found flags don't try to add them as a user as well.. */
   for (i=1+foundflags;i<cargc;i++) {
@@ -96,12 +96,21 @@ int csc_doadduser(void *source, int cargc, char **cargv) {
       continue;
     }
 
-    if(count++ >= MAXCHANLEVS) {
+    if(chanlevcount++ >= MAXCHANLEVS) {
       chanservstdmessage(sender, QM_TOOMANYCHANLEVS);
       chanservstdmessage(sender, QM_DONE);
       return CMD_OK;
     }
 
+    channelcount=0;
+    for (trcup=rup->knownon;trcup;trcup=trcup->nextbyuser)
+      channelcount++;
+
+    if(channelcount >= MAXCHANNELS) {
+      chanservstdmessage(sender, QM_TOOMANYCHANNELS);
+      return CMD_ERROR;
+    }
+
     rcup=getregchanuser();
     rcup->chan=rcp;
     rcup->user=rup;
index ee0588e74662996afacfcc6bb078da6d7c0660e6..3a9e66782d715438e1f8914c659cbfd57fdc7c47 100644 (file)
@@ -90,7 +90,7 @@ int csc_dochanlev(void *source, int cargc, char **cargv) {
   nick *sender=source;
   chanindex *cip;
   regchan *rcp;
-  regchanuser *rcup, *rcuplist;
+  regchanuser *rcup, *trcup, *rcuplist;
   regchanuser **rusers;
   reguser *rup=getreguserfromnick(sender), *target;
   char time1[TIMELEN],time2[TIMELEN];
@@ -267,17 +267,26 @@ int csc_dochanlev(void *source, int cargc, char **cargv) {
       
       if (!rcuplist) {
         /* new user, we could store a count instead... that's probably better... */
-        unsigned int count;
+        unsigned int chanlevcount, channelcount;
 
-        for (count=i=0;i<REGCHANUSERHASHSIZE;i++)
+        for (chanlevcount=i=0;i<REGCHANUSERHASHSIZE;i++)
           for (rcuplist=rcp->regusers[i];rcuplist;rcuplist=rcuplist->nextbychan)
-            count++;
+            chanlevcount++;
 
-        if(count >= MAXCHANLEVS) {
+        if(chanlevcount >= MAXCHANLEVS) {
           chanservstdmessage(sender, QM_TOOMANYCHANLEVS);
           return CMD_ERROR;
         }
-   
+
+        channelcount=0;
+        for (trcup=target->knownon;trcup;trcup=trcup->nextbyuser)
+          channelcount++;
+
+        if(channelcount >= MAXCHANNELS) {
+          chanservstdmessage(sender, QM_TOOMANYCHANNELS);
+          return CMD_ERROR;
+        } 
+
        rcuplist=getregchanuser();
        rcuplist->user=target;
        rcuplist->chan=rcp;
index e9539b87ca79228f098f77da9171282ef4339dba..280a3977db7bbf44b697d88d6c0627af444a7ec0 100644 (file)
 #define MAXCHANLEVS  500
 #define MAXBANS      50
 
+/* Maximum number of channels a user may be known on */
+#define MAXCHANNELS  500
+
 /* Sources of entropy and standard length defines */
 #define ENTROPYSOURCE "/dev/urandom"
 #define ENTROPYLEN    8
index d31b09c96b4a5978e2924a77845c8693e86398b9..e18dbac8b2553a927c2fd486f83b8c97d94fa85e 100644 (file)
@@ -56,6 +56,7 @@ BeginMessages() {
   msg(QM_NOCHANBANAUTOREMOVE, "Bans on $0 will not be automatically removed.", "s"),
   msg(QM_INVALIDCHANNAME, "$0 is not a valid channel name.", "s"),
   msg(QM_ALREADYREGISTERED, "$0 is already registered.", "s"),
+  msg(QM_TOOMANYCHANNELS, "User is known on too many channels.", ""),
   msg(QM_CURUSERFLAGS, "User flags for $0: $1", "ss"),
   msg(QM_WELCOMEMESSAGEIS, "Welcome message for $0: $1", "ss"),
   msg(QM_GLOBALINFO, "Default info line: $0", "s"),
index 08a1749710b593ba1e5098fd65164d5f746d81a8..6cdf2e42f3dd854967b581a0ebcf89b4a70a5986 100644 (file)
@@ -22,7 +22,7 @@
 
 #define DEPFILE        "modules.dep"
 
-#define MAXMODULES 100
+#define MAXMODULES 200
 
 /* Module dependency stuff.
  *
index 42ed422d4e0905de211575de2d1897dc551e6c80..b8792deb9ab2fd52edc931f62678f3cec15b3176 100644 (file)
@@ -1,3 +1,4 @@
+#define CS_NODB
 #include "../chanserv/chanserv.h"
 
 static nick *fakeq;
diff --git a/invalidbans/Makefile.in b/invalidbans/Makefile.in
new file mode 100644 (file)
index 0000000..d6c3686
--- /dev/null
@@ -0,0 +1,6 @@
+@include@ @includel@../build.mk@includel@
+
+.PHONY: all
+all: invalidbans.so
+
+invalidbans.so: invalidbans.o
diff --git a/invalidbans/invalidbans.c b/invalidbans/invalidbans.c
new file mode 100644 (file)
index 0000000..5abc427
--- /dev/null
@@ -0,0 +1,149 @@
+#include <string.h>
+#include "../core/schedule.h"
+#include "../control/control.h"
+#include "../lib/irc_string.h"
+#include "../localuser/localuserchannel.h"
+#include "../lib/version.h"
+
+MODULE_VERSION("");
+
+static int ib_nickext;
+
+typedef struct ibnick {
+  time_t timeout;
+  void *sched;
+} ibnick;
+
+static void ib_clear_ext(void *arg) {
+  nick *np = arg;
+
+  if (!np->exts[ib_nickext])
+    return;
+
+  free(np->exts[ib_nickext]);
+  np->exts[ib_nickext] = NULL;
+}
+
+static void ib_hook_newnick(int hooknum, void *arg) {
+  nick *np = arg;
+  np->exts[ib_nickext] = NULL;
+}
+
+static void ib_hook_lostnick(int hooknum, void *arg) {
+  nick *np = arg;
+  ibnick *inp = np->exts[ib_nickext];
+
+  if (!inp)
+    return;
+
+  deleteschedule(inp->sched, ib_clear_ext, np);
+  free(np->exts[ib_nickext]);
+}
+
+static void ib_hook_modechange(int hooknum, void *arg) {
+  void **arglist=(void **)arg;
+  channel *cp=(channel *)arglist[0];
+  nick *np=(nick *)arglist[1];
+  long changeflags=(long)arglist[2];
+  chanban *cbp;
+  const char *p;
+  int colons, colonsnext;
+  modechanges changes;
+  ibnick *inp;
+  char *mask, *pos;
+  int slot, i;
+  array bans;
+
+  if (!(changeflags & MODECHANGE_BANS))
+    return;
+
+  inp = np->exts[ib_nickext];
+
+  /* Ignore the mode change if the same user has recently
+   * set/unset a channel ban. */
+  if (inp && inp->timeout > time(NULL))
+    return;
+
+  if (inp) {
+    deleteschedule(inp->sched, ib_clear_ext, np);
+    free(inp);
+    np->exts[ib_nickext] = NULL;
+  }
+
+  array_init(&bans, 512);
+
+  for (cbp = cp->bans; cbp; cbp = cbp->next) {
+    if (!cbp->host)
+      continue;
+
+    colons = 0;
+    colonsnext = 0;
+
+    for (p = cbp->host->content; *p; p++) {
+      if (*p == ':') {
+        colons++;
+
+        if (*(p + 1) == ':')
+          colonsnext = 1;
+      }
+    }
+
+    if (colons - colonsnext >= 8) {
+      slot = array_getfreeslot(&bans);
+      mask = ((char *)bans.content) + slot * 512;
+      strncpy(mask, bantostring(cbp), 512);
+    }
+  }
+
+  if (bans.cursi > 0) {
+    localsetmodeinit(&changes, cp, NULL);
+
+    for (i = 0; i < bans.cursi; i++) {
+      mask = ((char *)bans.content) + i * 512;
+
+      pos = strchr(mask, '@');
+
+      if (!pos)
+        continue; /* This should never happen. */
+
+      pos++; /* Skip over the @ sign. */
+
+      for (; *pos; pos++) {
+        if (*pos != ':') {
+          *pos = '?';
+          break;
+        }
+      }
+
+      localdosetmode_ban(&changes, mask, MCB_ADD);
+    }
+
+    localsetmodeflush(&changes, 1);
+
+    /* Ignore the user for a short amount of time. If it's a bot
+     * it'll probably try re-setting the ban immediately. */
+    inp = malloc(sizeof(ibnick));
+    inp->timeout = time(NULL) + 15;
+    inp->sched = scheduleoneshot(inp->timeout + 1, ib_clear_ext, np);
+    np->exts[ib_nickext] = inp;
+  }
+
+  array_free(&bans);
+}
+
+void _init() {
+  registerhook(HOOK_NICK_NEWNICK, &ib_hook_newnick);
+  registerhook(HOOK_NICK_LOSTNICK, &ib_hook_lostnick);
+  registerhook(HOOK_CHANNEL_MODECHANGE, &ib_hook_modechange);
+
+  ib_nickext = registernickext("invalidbans");
+}
+
+void _fini () {
+  deregisterhook(HOOK_NICK_NEWNICK, &ib_hook_newnick);
+  deregisterhook(HOOK_NICK_LOSTNICK, &ib_hook_lostnick);
+  deregisterhook(HOOK_CHANNEL_MODECHANGE, &ib_hook_modechange);
+
+  releasenickext(ib_nickext);
+}
+
index 30b11337ba545a63226f98d7a67ce790a0c5f818..55dbcab4961a3e8b07a001e9f9d644abc57d1343 100644 (file)
@@ -134,7 +134,8 @@ void *gline_exe(searchCtx *ctx, struct searchNode *thenode, void *theinput) {
   if (ctx->searchcmd == reg_chansearch) {
     cip = (chanindex *)theinput;
     cip->marker = localdata->marker;
-    localdata->count += cip->channel->users->totalusers;
+    if (cip->channel != NULL)
+      localdata->count += cip->channel->users->totalusers;
   }
   else {
     np = (nick *)theinput;
index ad45b24a875e13279ac71a28c7698e609c2835eb..db4963194074f41d43a21f170dea40695b71411a 100644 (file)
@@ -128,10 +128,6 @@ int handlenickmsg(void *source, int cargc, char **cargv) {
     }
 
     base64toip(cargv[cargc-3], &ipaddress);
-    if (!irc_in_addr_valid(&ipaddress)) {
-      Error("nick",ERR_ERROR,"Received NICK with invalid ipaddress for %s from %s.",cargv[0],sender);
-      return CMD_ERROR;
-    }
 
     /* At this stage the nick is cleared to proceed */
     np=newnick();
index 2e1b6aa878c10c88fa4aa6d38dc3722476b4e09a..22f02d885c88a102bd0024a2b1b227b4da9c7111 100644 (file)
@@ -6,4 +6,4 @@ LDFLAGS+=$(LIBPGSQL)
 .PHONY: all
 all: request.so
 
-request.so: lrequest.o request.o request_block.o sqrequest.o user.o 
+request.so: lrequest.o request.o request_block.o request_fasttrack.o sqrequest.o
index f8da1b2a67db9ba6259285bb63bb3579c6a3375b..51d7ebd86008606c395d05d250131dffd09ae557 100644 (file)
@@ -5,13 +5,12 @@
 #include "request.h"
 #include "lrequest.h"
 #include "request_block.h"
+#include "request_fasttrack.h"
 #include "../localuser/localuser.h"
 
 /* stats counters */
-int lr_noregops = 0;
-int lr_scoretoolow = 0;
 int lr_top5 = 0;
-int lr_floodattempts = 0;
+int lr_notargets = 0;
 
 #define min(a,b) ((a > b) ? b : a)
 
@@ -30,56 +29,38 @@ int lr_requestl(nick *svc, nick *np, channel *cp, nick *qnick) {
 
   cf = cf_findchanfix(cp->index);
 
-  if (cf == NULL) {
-    sendnoticetouser(svc, np, "Sorry, your channel '%s' was created recently. "
-          "Please try again in an hour.", cp->index->name->content);
+  if(cf) {
+    rocount = cf_getsortedregops(cf, LR_TOPX, rolist);
 
-    lr_noregops++;
+    ro = NULL;
 
-    return RQ_ERROR;
-  }
-
-  rocount = cf_getsortedregops(cf, LR_TOPX, rolist);
-
-  ro = NULL;
-
-  for (i = 0; i < min(LR_TOPX, rocount); i++) {
-    if (cf_cmpregopnick(rolist[i], np)) {
-      ro = rolist[i];
-      break;
+    for (i = 0; i < min(LR_TOPX, rocount); i++) {
+      if (cf_cmpregopnick(rolist[i], np)) {
+        ro = rolist[i];
+        break;
+      }
     }
-  }
 
-  if (ro == NULL) {
-    sendnoticetouser(svc, np, "Sorry, you must be one of the top %d ops "
-          "for the channel '%s'.", LR_TOPX, cp->index->name->content);
+    if (ro == NULL) {
+      sendnoticetouser(svc, np, "Sorry, you must be one of the top %d ops "
+            "for the channel '%s'.", LR_TOPX, cp->index->name->content);
 
-    lr_top5++;
-
-    return RQ_ERROR;
-  }
-
-  /* treat blocked users as if their score is too low */
-  if (ro->score < LR_CFSCORE || rq_findblock(np->authname)) {
-    if (rq_isspam(np)) {
-      sendnoticetouser(svc, np, "Do not flood the request system. "
-            "Try again in %s.", rq_longtoduration(rq_blocktime(np)));
-
-      lr_floodattempts++;
+      lr_top5++;
 
       return RQ_ERROR;
     }
+  }
 
-    sendnoticetouser(svc, np, "Sorry, you do not meet the "
-          "%s request requirements; please try again in an hour, "
-          "see http://www.quakenet.org/faq/faq.php?c=1&f=6#6", RQ_QNICK);
+  /* treat blocked users as if they're out of targets */
+  if(rq_findblock(np->authname) || !rq_tryfasttrack(np)) {
+    sendnoticetouser(svc, np, "Sorry, you may not request %s for another "
+      "channel at this time. Please try again in an hour.", RQ_QNICK);
 
-    lr_scoretoolow++;
+    lr_notargets++;
 
     return RQ_ERROR;
   }
 
-  
   sendmessagetouser(svc, qnick, "addchan %s #%s +jp upgrade %s", cp->index->name->content,
         np->authname, np->nick);
 
@@ -91,8 +72,6 @@ int lr_requestl(nick *svc, nick *np, channel *cp, nick *qnick) {
 }
 
 void lr_requeststats(nick *rqnick, nick *np) {
-  sendnoticetouser(rqnick, np, "- No registered ops (Q):          %d", lr_noregops);
-  sendnoticetouser(rqnick, np, "- Score too low (Q):              %d", lr_scoretoolow);
+  sendnoticetouser(rqnick, np, "- Too many requests (Q):          %d", lr_notargets);
   sendnoticetouser(rqnick, np, "- Not in top%d (Q):                %d", LR_TOPX, lr_top5);
-  sendnoticetouser(rqnick, np, "- Floods (Q):                     %d", lr_floodattempts);
 }
index 2cd84a36f824be51743445040a9d5162a77758e1..823da2285e02d1476c10e62e0a31f7d35c820da2 100644 (file)
@@ -12,9 +12,9 @@
 #include "../splitlist/splitlist.h"
 #include "request.h"
 #include "request_block.h"
+#include "request_fasttrack.h"
 #include "lrequest.h"
 #include "sqrequest.h"
-#include "user.h"
 
 MODULE_VERSION("");
 
@@ -33,11 +33,6 @@ int rqcmd_listblocks(void *user, int cargc, char **cargv);
 int rqcmd_stats(void *user, int cargc, char **cargv);
 int rqcmd_requestop(void *user, int cargc, char **cargv);
 
-int rqcmd_adduser(void *user, int cargc, char **cargv);
-int rqcmd_deluser(void *user, int cargc, char **cargv);
-int rqcmd_changelev(void *user, int cargc, char **cargv);
-int rqcmd_userlist(void *user, int cargc, char **cargv);
-
 #define min(a,b) ((a > b) ? b : a)
 
 /* stats counters */
@@ -55,6 +50,9 @@ void _init(void) {
   if(!rq_initblocks())
     return;
 
+  if(!rq_initfasttrack())
+    return;
+
   extloaded = 1;
 
   rqcommands = newcommandtree();
@@ -64,18 +62,12 @@ void _init(void) {
   addcommandtotree(rqcommands, "requestspamscan", RQU_ANY, 1, &rqcmd_requestspamscan);
   addcommandtotree(rqcommands, "requestop", RQU_ANY, 2, &rqcmd_requestop);
 
-  addcommandtotree(rqcommands, "addblock", RQU_ACCOUNT, 3, &rqcmd_addblock);
-  addcommandtotree(rqcommands, "delblock", RQU_ACCOUNT, 1, &rqcmd_delblock);
-  addcommandtotree(rqcommands, "listblocks", RQU_ACCOUNT, 1, &rqcmd_listblocks);
-  addcommandtotree(rqcommands, "stats", RQU_ACCOUNT, 1, &rqcmd_stats);
-
-  addcommandtotree(rqcommands, "adduser", RQU_OPER, 2, &rqcmd_adduser);
-  addcommandtotree(rqcommands, "deluser", RQU_OPER, 1, &rqcmd_deluser);
-  addcommandtotree(rqcommands, "changelev", RQU_OPER, 2, &rqcmd_changelev);
-  addcommandtotree(rqcommands, "userlist", RQU_OPER, 1, &rqcmd_userlist);
+  addcommandtotree(rqcommands, "addblock", RQU_OPER, 3, &rqcmd_addblock);
+  addcommandtotree(rqcommands, "delblock", RQU_OPER, 1, &rqcmd_delblock);
+  addcommandtotree(rqcommands, "listblocks", RQU_OPER, 1, &rqcmd_listblocks);
+  addcommandtotree(rqcommands, "stats", RQU_OPER, 1, &rqcmd_stats);
   
   qr_initrequest();
-  ru_load();
 
   rq_logfd = fopen(RQ_LOGFILE, "a");
   
@@ -98,16 +90,11 @@ void _fini(void) {
   deletecommandfromtree(rqcommands, "listblocks", &rqcmd_listblocks);
   deletecommandfromtree(rqcommands, "stats", &rqcmd_stats);
 
-  deletecommandfromtree(rqcommands, "adduser", &rqcmd_adduser);
-  deletecommandfromtree(rqcommands, "deluser", &rqcmd_deluser);
-  deletecommandfromtree(rqcommands, "changelev", &rqcmd_changelev);
-  deletecommandfromtree(rqcommands, "userlist", &rqcmd_userlist);
-
   destroycommandtree(rqcommands);
 
   rq_finiblocks();
+  rq_finifasttrack();
   qr_finirequest();
-  ru_persist();
 
   if (rq_logfd != NULL)
     fclose(rq_logfd);
@@ -119,7 +106,7 @@ void rq_registeruser(void) {
   channel *cp;
 
   rqnick = registerlocaluserflags(RQ_REQUEST_NICK, RQ_REQUEST_USER, RQ_REQUEST_HOST,
-                             RQ_REQUEST_REAL, RQ_REQUEST_AUTH, 1780711, 0,
+                             RQ_REQUEST_REAL, RQ_REQUEST_AUTH, RQ_REQUEST_AUTHID, 0,
                              UMODE_ACCOUNT | UMODE_SERVICE | UMODE_OPER,
                              rq_handler);
 
@@ -175,13 +162,6 @@ void rq_handler(nick *target, int type, void **params) {
         return;
       }
 
-      if ((cmd->level & RQU_ACCOUNT) && (!IsAccount(user) || ru_getlevel(user) == 0) && !IsOper(user)) {
-        sendnoticetouser(rqnick, user, "Sorry, this command is not "
-              "available to you.");
-
-       return;
-      }
-
       if (cargc - 1 > cmd->maxparams)
         rejoinline(cargv[cmd->maxparams], cargc - cmd->maxparams);
 
@@ -214,9 +194,6 @@ int rqcmd_showcommands(void *user, int cargc, char **cargv) {
     if ((cmdlist[i]->level & RQU_OPER) && !IsOper(np))
       continue;
  
-    if ((cmdlist[i]->level & RQU_ACCOUNT) && !(IsOper(np) || (IsAccount(np) && ru_getlevel(np) > 0)))
-      continue;
-
     sendnoticetouser(rqnick, np, "%s", cmdlist[i]->command->content);
   }
 
@@ -567,9 +544,8 @@ int rqcmd_addblock(void *user, int cargc, char **cargv) {
   rq_block *block;
   time_t expires;
   char *account;
-  int level = ru_getlevel(np);
 
-  if (level < 20) {
+  if (!IsOper(np)) {
     sendnoticetouser(rqnick, np, "You do not have access to this command.");
 
     return RQ_ERROR;
@@ -597,12 +573,6 @@ int rqcmd_addblock(void *user, int cargc, char **cargv) {
 
   expires = getnettime() + durationtolong(cargv[1]);
 
-  if (expires > getnettime() + RQU_HELPER_MAXEXPIRE && level < 30) {
-    sendnoticetouser(rqnick, np, "Maximum expiry time is %s.", rq_longtoduration(RQU_HELPER_MAXEXPIRE));
-
-    return RQ_ERROR;
-  }
-  
   rq_addblock(cargv[0], cargv[2], account, 0, expires);
 
   sendnoticetouser(rqnick, np, "Blocked channels/accounts matching '%s' from "
@@ -613,12 +583,9 @@ int rqcmd_addblock(void *user, int cargc, char **cargv) {
 
 int rqcmd_delblock(void *user, int cargc, char **cargv) {
   nick *np = (nick*)user;
-  int result, level;
-  rq_block *block;
+  int result;
 
-  level = ru_getlevel(np);
-  
-  if (level < 20) {
+  if (!IsOper(np)) {
     sendnoticetouser(rqnick, np, "You do not have access to this command.");
 
     return RQ_ERROR;
@@ -630,16 +597,6 @@ int rqcmd_delblock(void *user, int cargc, char **cargv) {
     return RQ_ERROR;
   }
 
-  block = rq_findblock(cargv[0]);
-
-  if (block != NULL && level < 50) {
-    if (ircd_strcmp(block->creator->content, np->authname) != 0) {
-      sendnoticetouser(rqnick, np, "This block was created by someone else. You cannot remove it.");
-      
-      return RQ_ERROR;
-    }
-  }
-
   result = rq_removeblock(cargv[0]);
 
   if (result > 0) {
@@ -656,11 +613,9 @@ int rqcmd_delblock(void *user, int cargc, char **cargv) {
 int rqcmd_listblocks(void *user, int cargc, char **cargv) {
   nick *np = (nick*)user;
   rq_block block;
-  int i, level;
+  int i;
 
-  level = ru_getlevel(np);
-  
-  if (level < 10) {
+  if (!IsOper(np)) {
     sendnoticetouser(rqnick, np, "You do not have access to this command.");
 
     return RQ_ERROR;
@@ -690,9 +645,8 @@ int rqcmd_listblocks(void *user, int cargc, char **cargv) {
 
 int rqcmd_stats(void *user, int cargc, char **cargv) {
   nick *np = (nick*)user;
-  int level = ru_getlevel(np);
 
-  if (level < 10) {
+  if (!IsOper(np)) {
     sendnoticetouser(rqnick, np, "You do not have access to this command.");
 
     return RQ_ERROR;
@@ -709,112 +663,3 @@ int rqcmd_stats(void *user, int cargc, char **cargv) {
   return RQ_OK;
 }
 
-int rqcmd_adduser(void *user, int cargc, char **cargv) {
-  nick *np = (nick*)user;
-  int result, level;
-
-  if (cargc < 2) {
-    sendnoticetouser(rqnick, np, "Syntax: adduser <account> <level>");
-
-    return RQ_ERROR;
-  }
-
-  level = atoi(cargv[1]);
-
-  if (level <= 0) {
-    sendnoticetouser(rqnick, np, "Level must be a positive integer.");
-
-    return RQ_ERROR;
-  }
-
-  result = ru_create(cargv[0], level);
-
-  if (result) {
-    sendnoticetouser(rqnick, np, "User '%s' was added with level '%d'.", cargv[0], level);
-
-    return RQ_OK;
-  } else {
-    sendnoticetouser(rqnick, np, "Something strange happened. Contact shroud.");
-
-    return RQ_ERROR;
-  }
-}
-
-int rqcmd_deluser(void *user, int cargc, char **cargv) {
-  nick *np = (nick*)user;
-  int level;
-
-  if (cargc < 1) {
-    sendnoticetouser(rqnick, np, "Syntax: deluser <account>");
-
-    return RQ_ERROR;
-  }
-
-  level = ru_getlevel_str(cargv[0]);
-
-  if (level <= 0) {
-    sendnoticetouser(rqnick, np, "There is no such user.");
-
-    return RQ_ERROR;
-  }
-
-  ru_destroy(cargv[0]);
-
-  sendnoticetouser(rqnick, np, "Done.");
-
-  return RQ_OK;
-}
-
-int rqcmd_changelev(void *user, int cargc, char **cargv) {
-  nick *np = (nick*)user;
-  int result, level;
-
-  if (cargc < 2) {
-    sendnoticetouser(rqnick, np, "Syntax: changelev <account> <level>");
-
-    return RQ_ERROR;
-  }
-
-  level = atoi(cargv[1]);
-
-  if (level <= 0) {
-    sendnoticetouser(rqnick, np, "Level must be a positive integer.");
-
-    return RQ_ERROR;
-  }
-
-  if (ru_getlevel_str(cargv[0]) <= 0) {
-    sendnoticetouser(rqnick, np, "Unknown user.");
-
-    return RQ_ERROR;
-  }
-  
-  result = ru_setlevel(cargv[0], level);
-  
-  if (result != 0) {
-    sendnoticetouser(rqnick, np, "Done.");
-
-    return RQ_OK;
-  } else {
-    sendnoticetouser(rqnick, np, "Something strange happened. Contact shroud.");
-
-    return RQ_ERROR;
-  }
-}
-
-int rqcmd_userlist(void *user, int cargc, char **cargv) {
-  nick *np = (nick*)user;
-  r_user_t *userp = r_userlist;
-
-  sendnoticetouser(rqnick, np, "User Level");
-
-  while (userp) {
-    sendnoticetouser(rqnick, np, "%s %d", userp->name, userp->level);
-    userp = userp->next;
-  }
-
-  sendnoticetouser(rqnick, np, "--- End of USERS.");
-
-  return RQ_OK;
-}
-
index 2740998069e450d942a0d5823eac0f2cc28706fc..c962fa63a2ac9420209c982ac20e0d033ac9ff0e 100644 (file)
@@ -3,7 +3,7 @@
 #define RQ_QSERVER "CServe.quakenet.org"
 #define RQ_QNICK "Q"
 
-#define RQ_SSERVER "services.de.quakenet.org"
+#define RQ_SSERVER "services2.uk.quakenet.org"
 #define RQ_SNICK "S"
 
 #define RQ_REQUEST_NICK "R"
 #define RQ_REQUEST_HOST "request.quakenet.org"
 #define RQ_REQUEST_REAL "Service Request v0.23"
 #define RQ_REQUEST_AUTH "R"
+#define RQ_REQUEST_AUTHID 1780711
 
 #define RQU_ANY 0
 #define RQU_OPER 1
-#define RQU_ACCOUNT 2
 
-/* one week by default */
-#define RQU_HELPER_MAXEXPIRE 604800
-
-#define RQ_USERFILE "data/rqusers"
 #define RQ_LOGFILE "logs/request.log"
 
 #define RQ_OK 0
index fa7f59de0be26d16ad4cb426452563a38a1b2cb7..779c87f32e2383c20a267ddb04de91d2b034fba8 100644 (file)
@@ -7,19 +7,10 @@
 /* array of blocks */
 array rqblocks;
 
-/* our anti-flood nick extension */
-int rqnext;
-
 /* are we currently loading blocks? */
 int rq_loading;
 
-void rqhook_lostnick(int hook, void *arg);
-
 int rq_initblocks(void) {
-  rqnext = registernickext("request");
-  if(rqnext < 0)
-    return 0;
-
   array_init(&rqblocks, sizeof(rq_block));
   array_setlim1(&rqblocks, 5);
   array_setlim2(&rqblocks, 20);
@@ -31,15 +22,12 @@ int rq_initblocks(void) {
   rq_addblock("#qnet*", "Reserved for QuakeNet use only.", "request", 0, 0);
   rq_addblock("#help*", "Reserved for QuakeNet use only.", "request", 0, 0);
 
-  registerhook(HOOK_NICK_LOSTNICK, &rqhook_lostnick);
-
   return 1;
 }
 
 void rq_finiblocks(void) {
   int i;
   rq_block block;
-  nick *nip;
 
   for (i = 0; i < rqblocks.cursi; i++) {
     block = ((rq_block*)rqblocks.content)[i];
@@ -50,64 +38,6 @@ void rq_finiblocks(void) {
   }
 
   array_free(&rqblocks);
-
-  for (i=0; i<NICKHASHSIZE; i++)
-    for (nip=nicktable[i]; nip; nip=nip->next)
-      free(nip->exts[rqnext]);
-
-  deregisterhook(HOOK_NICK_LOSTNICK, &rqhook_lostnick);
-
-  releasenickext(rqnext);
-}
-
-void rqhook_lostnick(int hook, void *arg) {
-  nick *np = (nick*)arg;
-
-  free(np->exts[rqnext]);
-}
-
-int rq_isspam(nick *np) {
-  rq_flood *lf;
-
-  if (np->exts[rqnext] == NULL) {
-    np->exts[rqnext] = lf = (rq_flood*)malloc(sizeof(rq_flood));
-
-    lf->count = 1;
-    lf->created = getnettime();
-    lf->expire = 0;
-
-    return 0;
-  } else {
-    lf = np->exts[rqnext];
-
-    lf->count -= (getnettime() - lf->created) / (RQ_SPAMBLOCK / RQ_SPAMCOUNT);
-    
-    if (lf->count < 0)
-      lf->count = 0;
-
-    if (lf->count > RQ_SPAMCOUNT && lf->expire > getnettime()) {
-      return 1;
-    } else {
-      lf->count++;
-
-      if (lf->count > RQ_SPAMCOUNT) {
-        lf->expire = getnettime() + RQ_SPAMBLOCK;
-        
-        rq_addblock(np->authname, "Flooding the request system.", "request", 0, getnettime() + 3600);
-
-        return 1;
-      }
-
-      return 0;
-    }
-  }
-}
-
-time_t rq_blocktime(nick *np) {
-  if (np->exts[rqnext] == NULL)
-    return 0;
-  else
-    return ((rq_flood*)np->exts[rqnext])->expire - getnettime();
 }
 
 rq_block *rq_findblock(const char *pattern) {
index 8a73f898f15d5fb467964c5a97ec5fb85ab34de3..afca63fb3062d709be140650b8c5432c751aa4f0 100644 (file)
@@ -1,12 +1,6 @@
 #include "../nick/nick.h"
 #include "../channel/channel.h"
 
-typedef struct {
-  int count;
-  time_t created;
-  time_t expire;
-} rq_flood;
-
 typedef struct {
   sstring *pattern;
   sstring *reason;
@@ -21,9 +15,6 @@ extern array rqblocks;
 #define RQ_BLOCKFILE "data/rqblocks"
 #define RQ_BLOCKLEN 256
 
-#define RQ_SPAMCOUNT 5
-#define RQ_SPAMBLOCK 3600
-
 int rq_initblocks(void);
 void rq_finiblocks(void);
 
@@ -34,7 +25,3 @@ int rq_saveblocks(void);
 rq_block *rq_findblock(const char *pattern);
 void rq_addblock(const char *pattern, const char *reason, const char *creator, time_t created, time_t expires);
 int rq_removeblock(const char *pattern);
-
-/* anti-spam blocks */
-int rq_isspam(nick *np);
-time_t rq_blocktime(nick *np);
diff --git a/request/request_fasttrack.c b/request/request_fasttrack.c
new file mode 100644 (file)
index 0000000..2663c9d
--- /dev/null
@@ -0,0 +1,149 @@
+#include <stdio.h>
+#include <string.h>
+#include "../core/schedule.h"
+#include "../irc/irc.h"
+#include "../lib/irc_string.h"
+#include "request_fasttrack.h"
+
+typedef struct rq_fasttrack {
+  unsigned long userid;
+
+  unsigned int targets;
+  time_t refill_time;
+
+  struct rq_fasttrack *next;
+} rq_fasttrack;
+
+static rq_fasttrack *ftlist;
+
+/* our fast-track extension */
+int rqnext;
+
+static void rq_cleanup_fasttrack(void *arg);
+static void rqhook_account(int hook, void *arg);
+
+int rq_initfasttrack(void) {
+  rqnext = registernickext("request_fasttrack");
+  if(rqnext < 0)
+    return 0;
+
+  registerhook(HOOK_NICK_NEWNICK, &rqhook_account);
+  registerhook(HOOK_NICK_ACCOUNT, &rqhook_account);
+
+  schedulerecurring(time(NULL)+1, 0, 3600, rq_cleanup_fasttrack, NULL);
+
+  return 1;
+}
+
+void rq_finifasttrack(void) {
+  rq_fasttrack *ft, *next;
+
+  deregisterhook(HOOK_NICK_NEWNICK, &rqhook_account);
+  deregisterhook(HOOK_NICK_ACCOUNT, &rqhook_account);
+
+  for(ft=ftlist;ft;) {
+    next = ft->next;
+    free(ft);
+    ft = next;
+  }
+
+  releasenickext(rqnext);
+}
+
+static void rqhook_account(int hook, void *arg) {
+  nick *np = (nick *)arg;
+  rq_fasttrack *ft;
+
+  /* Auth might be null for the newnick hook. */
+  if(!np->auth)
+    return;
+
+  /* Try to find an existing fasttrack record for this user. */
+  for(ft=ftlist;ft;ft=ft->next) {
+    if(np->auth->userid==ft->userid) {
+      np->exts[rqnext] = ft;
+      break;
+    }
+  }
+}
+
+static void rq_cleanup_fasttrack(void *arg) {
+  time_t now = getnettime();
+  rq_fasttrack **pft, *ft;
+  int j;
+  nick *tnp;
+
+  now = getnettime();
+
+  pft = &ftlist;
+
+  for(ft=*pft;*pft;pft=&((*pft)->next)) {
+    int foundnick = 0;
+
+    for(j=0;j<NICKHASHSIZE && !foundnick;j++) {
+      for(tnp=nicktable[j];tnp;tnp=tnp->next) {
+        if(tnp->exts[rqnext]==ft) {
+          foundnick = 1;
+          break;
+        }
+      }
+    }
+
+    if(!foundnick && ft->refill_time < now) {
+      *pft = ft->next;
+      free(ft);
+    }
+
+    if (!*pft)
+      break;
+  }
+}
+
+static rq_fasttrack *rq_getfasttrack(nick *np) {
+  rq_fasttrack *ft;
+
+  /* Use an existing fast-track record if the nick has one. */
+  if(np->exts[rqnext])
+    return np->exts[rqnext];
+
+  if(!np->auth)
+    return NULL;
+
+  ft = malloc(sizeof(rq_fasttrack));
+
+  if(!ft)
+    return NULL;
+
+  ft->userid = np->auth->userid;
+  ft->targets = 0;
+  ft->refill_time = 0;
+
+  ft->next = ftlist;
+  ftlist = ft;
+
+  np->exts[rqnext] = ft;
+
+  return ft;
+}
+
+int rq_tryfasttrack(nick *np) {
+  rq_fasttrack *ft = rq_getfasttrack(np);
+
+  /* Don't fast-track if we can't find a fast-track record. */
+  if(!ft)
+    return 0;
+
+  /* Refill targets if necessary. */
+  if(getnettime() > ft->refill_time) {
+    ft->targets = RQ_FASTTRACK_TARGETS;
+    ft->refill_time = getnettime() + RQ_FASTTRACK_TIMEOUT;
+  }
+
+  /* Check if we have a free target. */
+  if(ft->targets==0)
+    return 0;
+
+  ft->targets--;
+  return 1;
+}
+
diff --git a/request/request_fasttrack.h b/request/request_fasttrack.h
new file mode 100644 (file)
index 0000000..884d8b1
--- /dev/null
@@ -0,0 +1,10 @@
+#include "../nick/nick.h"
+#include "../channel/channel.h"
+
+#define RQ_FASTTRACK_TARGETS 2
+#define RQ_FASTTRACK_TIMEOUT (60 * 60)
+
+int rq_initfasttrack(void);
+void rq_finifasttrack(void);
+
+int rq_tryfasttrack(nick *np);
index 025be5cc6f2255465224b02462bffbe4eb9f22eb..1857b575a3c36b9c24a4658b27f77a5c9b87b004 100644 (file)
@@ -730,21 +730,10 @@ int qr_instantrequestq(nick *sender, channel *cp) {
 
 int qr_requests(nick *rqnick, nick *sender, channel *cp, nick *qnick) {
   chanindex *cip = cp->index;
-  int who = 0;
   requestrec *nextreq, *lastreq;
 
-  if (rq_isspam(sender)) {
-      sendnoticetouser(rqnick, sender, "Do not flood the request system."
-          " Try again in %s.", rq_longtoduration(rq_blocktime(sender)));
-    
-      return RQ_ERROR;
-  }
-
   /* check which service is on the channel */
   if (getnumerichandlefromchanhash(cp->users, qnick->numeric) != NULL) {
-    /* we've found Q */
-    who = QR_Q;
-
     /* Request stats from Q */
     sendmessagetouser(rqnick, qnick, "CHANLEV %s", cip->name->content);
 
@@ -809,10 +798,10 @@ void qr_finirequest(void) {
 
 void qr_requeststats(nick *rqnick, nick *np) {
   sendnoticetouser(rqnick, np, "- Suspended (S):                  %d", qr_suspended);
-  sendnoticetouser(rqnick, np, "- No chanstats (S):             %d", qr_nohist);
-  sendnoticetouser(rqnick, np, "- Too small (S):                %d", qr_toosmall);
-  sendnoticetouser(rqnick, np, "- User was not on chanlev (S):  %d", qr_nochanlev);
-  sendnoticetouser(rqnick, np, "- User was not the owner (S):   %d", qr_notowner);
+  sendnoticetouser(rqnick, np, "- No chanstats (S):               %d", qr_nohist);
+  sendnoticetouser(rqnick, np, "- Too small (S):                  %d", qr_toosmall);
+  sendnoticetouser(rqnick, np, "- User was not on chanlev (S):    %d", qr_nochanlev);
+  sendnoticetouser(rqnick, np, "- User was not the owner (S):     %d", qr_notowner);
   sendnoticetouser(rqnick, np, "- A:                              %d", qr_a);
   sendnoticetouser(rqnick, np, "- B:                              %d", qr_b);
   sendnoticetouser(rqnick, np, "- C:                              %d", qr_c);
diff --git a/request/user.c b/request/user.c
deleted file mode 100644 (file)
index 563591f..0000000
+++ /dev/null
@@ -1,192 +0,0 @@
-#include <stdio.h>
-#include <string.h>
-
-#include "../irc/irc_config.h"
-#include "../lib/irc_string.h"
-#include "../lib/strlfunc.h"
-#include "../nick/nick.h"
-#include "request.h"
-#include "user.h"
-
-r_user_t *r_userlist = NULL;
-int ru_loading = 0;
-
-r_user_t *ru_find(char *name);
-
-int ru_create(char *name, unsigned int level) {
-       r_user_t *user, *trav;
-
-       if (ru_setlevel(name, level) != 0)
-               return 1;
-
-       user = (r_user_t*)malloc(sizeof(r_user_t));
-
-       if (user == NULL)
-               return 0;
-
-       user->next = NULL;
-       strlcpy(user->name, name, sizeof(user->name));
-       user->level = level;
-
-       if (r_userlist == NULL)
-               r_userlist = user;
-       else {
-               trav = r_userlist;
-
-               while (trav->next)
-                       trav = trav->next;
-
-               trav->next = user;
-       }
-
-       ru_persist();
-       
-       return 1;
-}
-
-void ru_destroy(char *name) {
-       r_user_t *puser;
-       r_user_t *user = r_userlist;
-
-       if (user && ircd_strcmp(user->name, name) == 0) {
-               free(r_userlist);
-
-               r_userlist = NULL;
-
-               ru_persist();
-               
-               return;
-       }
-
-       if (user == NULL)
-               return;
-       
-       while (user && user->next) {    
-               if (ircd_strcmp(user->next->name, name) == 0) {
-                       puser = user->next;
-                       user->next = user->next->next;
-
-                       free(puser);
-               }
-
-               user = user->next;
-       }
-
-       ru_persist();
-}
-
-int ru_parseline(char *line) {
-  char name[ACCOUNTLEN];
-  unsigned int level;
-  int result;
-
-  if (sscanf(line, "%s %u", name, &level) < 2)
-    return 0;
-
-  ru_loading = 1;
-  result = ru_create(name, level);
-  ru_loading = 0;
-
-  return result;
-}
-
-int ru_load(void) {
-  char line[4096];
-  FILE *rudata;
-  int count;
-
-  rudata = fopen(RQ_USERFILE, "r");
-
-  if (rudata == NULL)
-    return 0;
-
-  count = 0;
-
-  while (!feof(rudata)) {
-    if (fgets(line, sizeof(line), rudata) == NULL)
-      break;
-
-    if (line[strlen(line) - 1] == '\n')
-      line[strlen(line) - 1] = '\0';
-
-    if (line[strlen(line) - 1] == '\r')
-      line[strlen(line) - 1] = '\0';
-
-    if (line[0] != '\0') {
-      if (ru_parseline(line))
-        count++;
-    }
-  }
-
-  fclose(rudata);
-
-  return count;
-}
-
-int ru_persist(void) {
-  FILE *rudata;
-  int count = 0;
-  r_user_t *user = r_userlist;
-
-  if (ru_loading)
-    return 0;
-
-  rudata = fopen(RQ_USERFILE, "w");
-
-  if (rudata == NULL)
-    return 0;
-
-  while (user) {
-    fprintf(rudata, "%s %u\n", user->name, user->level);
-
-    user = user->next;
-  }
-
-  fclose(rudata);
-
-  return count;
-}
-
-r_user_t *ru_find(char *name) {
-       r_user_t *user = r_userlist;
-
-       while (user) {
-               if (ircd_strcmp(user->name, name) == 0)
-                       return user;
-
-               user = user->next;
-       }
-
-       return NULL;
-}
-
-unsigned int ru_getlevel(nick *np) {
-       if (IsOper(np))
-               return 999;
-       else if (!IsAccount(np))
-               return 0;
-
-       return ru_getlevel_str(np->authname);
-}
-
-unsigned int ru_getlevel_str(char *name) {
-       r_user_t *user = ru_find(name);
-
-       if (user)
-               return user->level;
-       else
-               return 0;
-}
-
-int ru_setlevel(char *name, unsigned int level) {
-       r_user_t *user = ru_find(name);
-
-       if (user) {
-               user->level = level;
-
-               ru_persist();
-
-               return 1;
-       } else
-               return 0;
-}
diff --git a/request/user.h b/request/user.h
deleted file mode 100644 (file)
index 09b0816..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-/* R user system */
-
-typedef struct r_user_s {
-       struct r_user_s *next;
-       char name[ACCOUNTLEN];
-       unsigned int level;
-} r_user_t;
-
-extern r_user_t *r_userlist;
-
-int ru_load(void);
-int ru_persist(void);
-
-int ru_create(char *name, unsigned int level);
-void ru_destroy(char *name);
-unsigned int ru_getlevel(nick *np);
-unsigned int ru_getlevel_str(char *name);
-int ru_setlevel(char *name, unsigned int level);
diff --git a/whowas/Makefile.in b/whowas/Makefile.in
new file mode 100644 (file)
index 0000000..b6f9648
--- /dev/null
@@ -0,0 +1,5 @@
+@include@ @includel@../build.mk@includel@
+.PHONY: all
+all: whowas.so
+
+whowas.so: whowas.o
diff --git a/whowas/whowas.c b/whowas/whowas.c
new file mode 100644 (file)
index 0000000..2132e90
--- /dev/null
@@ -0,0 +1,121 @@
+#include <stdio.h>
+#include <string.h>
+#include "../core/hooks.h"
+#include "../control/control.h"
+#include "../irc/irc.h"
+#include "../lib/irc_string.h"
+#include "whowas.h"
+
+static whowas *wwhead, *wwtail;
+static int wwcount;
+
+int ww_cmdwhowas(void *source, int cargc, char **cargv) {
+  nick *np = source;
+  char *pattern;
+  whowas *ww;
+  char hostmask[WW_MASKLEN+1];
+  char timebuf[30];
+  int matches = 0, limit = 500;
+
+  if(cargc<1)
+    return CMD_USAGE;
+
+  pattern = cargv[0];
+
+  if(cargc>1)
+    limit = strtol(cargv[1], NULL, 10);
+
+  for(ww=wwhead;ww;ww=ww->next) {
+    snprintf(hostmask, sizeof(hostmask), "%s!%s@%s", ww->nick, ww->ident, ww->host);
+
+    if (match2strings(pattern, hostmask)) {
+      matches++;
+
+      if(matches<=limit) {
+        strftime(timebuf, 30, "%d/%m/%y %H:%M:%S", localtime(&(ww->seen)));
+
+        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);
+      }
+    }
+  }
+
+  controlreply(np, "--- Found %d entries.", matches);
+
+  return CMD_OK;
+}
+
+void ww_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);
+
+  /* Clean up old records. */
+  while((ww = wwtail) && (ww->seen < now - WW_MAXAGE || wwcount >= WW_MAXENTRIES)) {
+    wwtail = ww->prev;
+
+    if (ww->prev)
+      ww->prev->next = NULL;
+    else
+      wwhead = ww->prev;
+
+    wwcount--;
+    free(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;
+  }
+
+  ww->reason = getsstring(reason, WW_REASONLEN);
+
+  if(wwhead)
+    wwhead->prev = ww;
+
+  ww->next = wwhead;
+  wwhead = ww;
+
+  ww->prev = NULL;
+
+  if(!wwtail)
+    wwtail = ww;
+
+  wwcount++;
+}
+
+void _init(void) {
+  registerhook(HOOK_NICK_QUIT, ww_handlequitorkill);
+  registerhook(HOOK_NICK_KILL, ww_handlequitorkill);
+
+  registercontrolhelpcmd("whowas", NO_OPER, 2, &ww_cmdwhowas, "Usage: whowas <mask> ?limit?\nLooks up information about recently disconnected users.");
+}
+
+void _fini(void) {
+  whowas *ww;
+
+  deregisterhook(HOOK_NICK_QUIT, ww_handlequitorkill);
+  deregisterhook(HOOK_NICK_KILL, ww_handlequitorkill);
+
+  deregistercontrolcmd("whowas", &ww_cmdwhowas);
+
+  while((ww = wwhead)) {
+    wwhead = ww->next;
+    free(ww);
+  }
+}
diff --git a/whowas/whowas.h b/whowas/whowas.h
new file mode 100644 (file)
index 0000000..f98074b
--- /dev/null
@@ -0,0 +1,17 @@
+#define WW_MAXAGE 3600
+#define WW_MAXENTRIES 100000
+#define WW_MASKLEN (HOSTLEN + USERLEN + NICKLEN)
+#define WW_REASONLEN 512
+
+typedef struct whowas {
+  char nick[NICKLEN+1];
+  char ident[USERLEN+1];
+  char host[HOSTLEN+1];
+  char realname[REALLEN+1];
+  sstring *reason;
+
+  time_t seen;
+
+  struct whowas *next;
+  struct whowas *prev;
+} whowas;