]> jfr.im git - irc/quakenet/newserv.git/blobdiff - glines/glines_buf.c
CHANSERV: fix issue where chanserv_relay doesn't wait for db to be loaded before...
[irc/quakenet/newserv.git] / glines / glines_buf.c
index 3c906c1f896c64db46b2b0a49721e7ed64b5f22b..7053853171bdc98276f26e816d896c0777ff397a 100644 (file)
@@ -1,6 +1,7 @@
 #include <stdarg.h>
 #include <stdio.h>
 #include <string.h>
+#include <assert.h>
 #include "../lib/array.h"
 #include "../lib/irc_string.h"
 #include "../irc/irc.h"
@@ -12,18 +13,20 @@ static int nextglinebufid = 1;
 glinebuf *glinebuflog[MAXGLINELOG] = {};
 int glinebuflogoffset = 0;
 
-void glinebufinit(glinebuf *gbuf, int merge) {
-  gbuf->id = 0;
+void glinebufinit(glinebuf *gbuf, int id) {
+  gbuf->id = id;
   gbuf->comment = NULL;
+  gbuf->commit = 0;
+  gbuf->amend = 0;
   gbuf->glines = NULL;
-  gbuf->merge = merge;
+  gbuf->hitsvalid = 1;
   gbuf->userhits = 0;
   gbuf->channelhits = 0;
   array_init(&gbuf->hits, sizeof(sstring *));
 }
 
 gline *glinebufadd(glinebuf *gbuf, const char *mask, const char *creator, const char *reason, time_t expire, time_t lastmod, time_t lifetime) {
-  gline *gl, *sgl, **pnext;
+  gline *gl;
 
   gl = makegline(mask); /* sets up nick,user,host,node and flags */
 
@@ -33,29 +36,6 @@ gline *glinebufadd(glinebuf *gbuf, const char *mask, const char *creator, const
     return 0;
   }
 
-  if (gbuf->merge) {
-    /* Check if an existing gline supercedes this mask */
-    for (sgl = gbuf->glines; sgl; sgl = sgl->next) {
-      if (gline_match_mask(sgl, gl)) {
-        freegline(gl);
-        return sgl;
-      }
-    }
-
-    /* Remove older glines this gline matches */
-    for (pnext = &gbuf->glines; *pnext; pnext = &((*pnext)->next)) {
-      sgl = *pnext;
-
-      if (gline_match_mask(gl, sgl)) {
-        *pnext = sgl->next;
-        freegline(sgl);
-        
-        if (!*pnext)
-          break;
-      }
-    }
-  }
-
   gl->creator = getsstring(creator, 255);
 
   /* it's not unreasonable to assume gline is active, if we're adding a deactivated gline, we can remove this later */
@@ -69,6 +49,8 @@ gline *glinebufadd(glinebuf *gbuf, const char *mask, const char *creator, const
   gl->next = gbuf->glines;
   gbuf->glines = gl;
 
+  gbuf->hitsvalid = 0;
+
   return gl;
 }
 
@@ -97,7 +79,7 @@ void glinebufaddbyip(glinebuf *gbuf, const char *user, struct irc_in_addr *ip, u
   if (nodebits < bits)
     bits = nodebits;
 
-  snprintf(mask, sizeof(mask), "%s@%s", user, trusts_cidr2str(ip, bits));
+  snprintf(mask, sizeof(mask), "%s@%s", user, (bits == 128) ? IPtostr(*ip) : CIDRtostr(*ip, bits));
 
   glinebufadd(gbuf, mask, creator, reason, expire, lastmod, lifetime);
 }
@@ -108,11 +90,23 @@ void glinebufaddbynick(glinebuf *gbuf, nick *np, int flags, const char *creator,
     snprintf(mask, sizeof(mask), "%s!*@*", np->nick);
     glinebufadd(gbuf, mask, creator, reason, expire, lastmod, lifetime);
   } else {
-    glinebufaddbyip(gbuf, np->ident, &np->p_ipaddr, 128, flags, creator, reason, expire, lastmod, lifetime);
+    glinebufaddbyip(gbuf, np->ident, &np->ipaddress, 128, flags, creator, reason, expire, lastmod, lifetime);
   }
 }
 
-void glinebufcounthits(glinebuf *gbuf, int *users, int *channels, nick *spewto) {
+void glinebufaddbywhowas(glinebuf *gbuf, whowas *ww, int flags, const char *creator, const char *reason, time_t expire, time_t lastmod, time_t lifetime) {
+  nick *np = &ww->nick;
+
+  if (flags & GLINE_ALWAYS_NICK) {
+    char mask[512];
+    snprintf(mask, sizeof(mask), "%s!*@*", np->nick);
+    glinebufadd(gbuf, mask, creator, reason, expire, lastmod, lifetime);
+  } else {
+    glinebufaddbyip(gbuf, np->ident, &np->ipaddress, 128, flags, creator, reason, expire, lastmod, lifetime);
+  }
+}
+
+void glinebufcounthits(glinebuf *gbuf, int *users, int *channels) {
   gline *gl;
   int i, hit, slot;
   chanindex *cip;
@@ -120,6 +114,11 @@ void glinebufcounthits(glinebuf *gbuf, int *users, int *channels, nick *spewto)
   nick *np;
   char uhmask[512];
 
+#if 0 /* Let's just do a new hit check anyway. */
+  if (gbuf->hitsvalid)
+    return;
+#endif
+
   gbuf->userhits = 0;
   gbuf->channelhits = 0;
 
@@ -148,9 +147,6 @@ void glinebufcounthits(glinebuf *gbuf, int *users, int *channels, nick *spewto)
       if (hit) {
         snprintf(uhmask, sizeof(uhmask), "channel: %s", cip->name->content);
 
-        if (spewto)
-          controlreply(spewto, "%s", uhmask);
-
         gbuf->channelhits++;
 
         slot = array_getfreeslot(&gbuf->hits);
@@ -171,10 +167,8 @@ void glinebufcounthits(glinebuf *gbuf, int *users, int *channels, nick *spewto)
       }
 
       if (hit) {
-        snprintf(uhmask, sizeof(uhmask), "user: %s!%s@%s r(%s)", np->nick, np->ident, np->host->name->content, np->realname->name->content);
-
-        if (spewto)
-          controlreply(spewto, "%s", uhmask);
+        snprintf(uhmask, sizeof(uhmask), "user: %s!%s@%s%s%s r(%s)", np->nick, np->ident, np->host->name->content,
+          (np->auth) ? "/" : "", (np->auth) ? np->authname : "", np->realname->name->content);
 
         gbuf->userhits++;
 
@@ -183,7 +177,9 @@ void glinebufcounthits(glinebuf *gbuf, int *users, int *channels, nick *spewto)
       }
     }
   }
-  
+
+  gbuf->hitsvalid = 1;  
+
   if (users)
     *users = gbuf->userhits;
   
@@ -191,12 +187,12 @@ void glinebufcounthits(glinebuf *gbuf, int *users, int *channels, nick *spewto)
     *channels = gbuf->channelhits;
 }
 
-int glinebufchecksane(glinebuf *gbuf, nick *spewto, int overridesanity, int overridelimit, int spewhits) {
+int glinebufchecksane(glinebuf *gbuf, nick *spewto, int overridesanity, int overridelimit) {
   gline *gl;
   int users, channels, good;
   const char *hint;
 
-  glinebufcounthits(gbuf, &users, &channels, spewhits ? spewto : NULL);
+  glinebufcounthits(gbuf, &users, &channels);
 
   if (!overridelimit) {
     if (channels > MAXUSERGLINECHANNELHITS) {
@@ -226,57 +222,105 @@ int glinebufchecksane(glinebuf *gbuf, nick *spewto, int overridesanity, int over
 
 void glinebufspew(glinebuf *gbuf, nick *spewto) {
   gline *gl;
-  time_t ref;
   int i;
-  
-  ref = (gbuf->flush) ? gbuf->flush : getnettime();
+  char timebuf[30], lastmod[30];
+
+  if (!gbuf->hitsvalid)
+    glinebufcounthits(gbuf, NULL, NULL);
+
+  if (gbuf->id == 0)
+    controlreply(spewto, "Uncommitted G-Line transaction.");
+  else
+    controlreply(spewto, "G-Line transaction ID %d", gbuf->id);
+
+  controlreply(spewto, "Comment: %s", (gbuf->comment) ? gbuf->comment->content : "(no comment)");
 
-  controlreply(spewto, "Mask                                     Duration             Creator              Reason");
+  if (gbuf->commit) {
+    strftime(timebuf, sizeof(timebuf), "%d/%m/%y %H:%M:%S", localtime(&gbuf->commit));
+    controlreply(spewto, "Committed at: %s", timebuf);
+  }
+
+  if (gbuf->amend) {
+    strftime(timebuf, sizeof(timebuf), "%d/%m/%y %H:%M:%S", localtime(&gbuf->amend));
+    controlreply(spewto, "Amended at: %s", timebuf);
+  }
+
+  controlreply(spewto, "Mask                                     Expiry               Last modified        Creator                   Reason");
   
-  for (gl = gbuf->glines; gl; gl = gl->next)
-    controlreply(spewto, "%-40s %-20s %-20s %s", glinetostring(gl), longtoduration(gl->expire - ref, 0), gl->creator->content, gl->reason->content);
+  for (gl = gbuf->glines; gl; gl = gl->next) {
+    strftime(timebuf, sizeof(timebuf), "%d/%m/%y %H:%M:%S", localtime(&gl->expire));
+    if (gl->lastmod == 0)
+      strncpy(lastmod, "<ulined>", sizeof(lastmod));
+    else
+      strftime(lastmod, sizeof(lastmod), "%d/%m/%y %H:%M:%S", localtime(&gl->lastmod));
+
+    controlreply(spewto, "%-40s %-20s %-20s %-25s %s", glinetostring(gl), timebuf, lastmod, gl->creator->content, gl->reason ? gl->reason->content : "");
+  }
 
-  controlreply(spewto, "Hit");
+  controlreply(spewto, "Hits");
 
-  for (i = 0; i < gbuf->hits.cursi; i++)
+  for (i = 0; i < gbuf->hits.cursi; i++) {
     controlreply(spewto, "%s", ((sstring **)gbuf->hits.content)[i]->content);
+
+    if (i >= 500) {
+      controlreply(spewto, "More than 500 hits, list truncated.");
+      break;
+    }
+  }
+
+  if (i == 0)
+    controlreply(spewto, "(no hits)");
+}
+
+void glinebufmerge(glinebuf *gbuf) {
+  /* TODO: reimplement */
+
+/*
+  if (gbuf->merge) {
+    /-* Check if an existing gline supercedes this mask *-/
+    for (sgl = gbuf->glines; sgl; sgl = sgl->next) {
+      if (gline_match_mask(sgl, gl)) {
+        freegline(gl);
+        return sgl;
+      }
+    }
+
+    /-* Remove older glines this gline matches *-/
+    for (pnext = &gbuf->glines; *pnext; pnext = &((*pnext)->next)) {
+      sgl = *pnext;
+
+      if (gline_match_mask(gl, sgl)) {
+        *pnext = sgl->next;
+        freegline(sgl);
+
+        if (!*pnext)
+          break;
+      }
+    }
+  }
+*/
 }
 
-void glinebufcommit(glinebuf *gbuf, int propagate) {
+int glinebufcommit(glinebuf *gbuf, int propagate) {
   gline *gl, *next, *sgl;
-  glinebuf *gbl;
-  int users, channels, i, slot;
+  int users, channels, id;
 
   /* Sanity check */
-  glinebufcounthits(gbuf, &users, &channels, NULL);
+  glinebufcounthits(gbuf, &users, &channels);
 
   if (propagate && (users > MAXGLINEUSERHITS || channels > MAXGLINECHANNELHITS)) {
-    controlwall(NO_OPER, NL_GLINES, "G-Line buffer would hit %d users/%d channels. Not setting G-Lines.");
+    controlwall(NO_OPER, NL_GLINES_AUTO, "G-Line buffer would hit %d users/%d channels. Not setting G-Lines.");
     glinebufabort(gbuf);
-    return;
+    return 0;
   }
 
-  time(&gbuf->flush);
+  /* Record the commit time */
+  time(&gbuf->commit);
 
-  if (propagate) {
-    /* Make a copy of the glinebuf for the log */
-    gbl = malloc(sizeof(glinebuf));
-    gbl->id = nextglinebufid++;
-    gbl->comment = (gbuf->comment) ? getsstring(gbuf->comment->content, 512) : NULL;
-    gbl->flush = gbuf->flush;
-    gbl->merge = gbuf->merge;
-    gbl->glines = NULL; /* going to set this later */
-    gbl->userhits = gbuf->userhits;
-    gbl->channelhits = gbuf->channelhits;
-
-    array_init(&gbl->hits, sizeof(sstring *));
-
-    for (i = 0; i < gbuf->hits.cursi; i++) {
-      slot = array_getfreeslot(&gbl->hits);
-      ((sstring **)gbl->hits.content)[slot] = getsstring(((sstring **)gbuf->hits.content)[i]->content, 512);
-    }
-  }
+  id = glinebufwritelog(gbuf, propagate);
 
+  /* Move glines to the global gline list */
   for (gl = gbuf->glines; gl; gl = next) {
     next = gl->next;
 
@@ -309,31 +353,18 @@ void glinebufcommit(glinebuf *gbuf, int propagate) {
       glinelist = gl;
     }
 
-    if (propagate) {
-      gline_propagate(gl);
+    gl->glinebufid = id;
 
-      sgl = glinedup(gl);
-      sgl->next = gbl->glines;
-      gbl->glines = sgl;
-    }
+    if (propagate)
+      gline_propagate(gl);
   }
 
-  /* We've moved all glines to the gline list. Clear glines link in the glinebuf. */  
+  /* We've moved all glines to the global gline list. Clear glines link in the glinebuf. */  
   gbuf->glines = NULL;
 
-  if (propagate && gbl->glines) {
-    glinebuflogoffset++;
-
-    if (glinebuflogoffset >= MAXGLINELOG)
-      glinebuflogoffset = 0;
-
-    if (glinebuflog[glinebuflogoffset])
-      glinebufabort(glinebuflog[glinebuflogoffset]);
-
-    glinebuflog[glinebuflogoffset]= gbl;
-  }
-
   glinebufabort(gbuf);
+
+  return id;
 }
 
 void glinebufabort(glinebuf *gbuf) {
@@ -370,7 +401,9 @@ int glinebufundo(int id) {
       
       if (!sgl)
         continue;
-      
+
+      sgl->glinebufid = 0;
+
       gline_deactivate(sgl, 0, 1);
     }
     
@@ -415,3 +448,101 @@ void glinebufcommentv(glinebuf *gbuf, const char *prefix, int cargc, char **carg
   
   gbuf->comment = getsstring(comment, 512);
 }
+
+int glinebufwritelog(glinebuf *gbuf, int propagating) {
+  int i, slot;
+  gline *gl, *sgl;
+  glinebuf *gbl;
+
+  if (!gbuf->glines)
+    return 0; /* Don't waste log IDs on empty gline buffers */
+
+  gbl = NULL;
+
+  /* Find an existing log buffer with the same id */
+  if (gbuf->id != 0) {
+    for (i = 0; i < MAXGLINELOG; i++) {
+      if (!glinebuflog[i])
+        continue;
+
+      if (glinebuflog[i]->id == gbuf->id) {
+        gbl = glinebuflog[i];
+        gbl->amend = gbuf->commit;
+
+        /* We're going to re-insert this glinebuf, so remove it for now */
+        glinebuflog[i] = NULL;
+
+        break;
+      }
+    }
+  }
+
+  /* Find a recent glinebuf that's a close match */
+  if (!gbl && !propagating) {
+    for (i = 0; i < MAXGLINELOG; i++) {
+      if (!glinebuflog[i])
+        continue;
+
+      if (glinebuflog[i]->commit < getnettime() - 5 && glinebuflog[i]->amend < getnettime() - 5)
+        continue;
+
+      assert(glinebuflog[i]->glines);
+
+      if (strcmp(glinebuflog[i]->glines->creator->content, gbuf->glines->creator->content) != 0)
+        continue;
+
+      gbl = glinebuflog[i];
+      gbl->amend = gbuf->commit;
+
+      /* We're going to re-insert this glinebuf, so remove it for now */
+      glinebuflog[i] = NULL;
+
+      break;
+    }
+  }
+
+  /* Make a new glinebuf for the log */
+  if (!gbl && gbuf->id == 0) {
+    gbl = malloc(sizeof(glinebuf));
+    glinebufinit(gbl, (gbuf->id == 0) ? nextglinebufid++ : gbuf->id);
+
+    assert(gbl->hitsvalid);
+
+    if (gbuf->comment)
+      glinebufcommentf(gbl, "%s", gbuf->comment->content);
+    else if (!propagating)
+      glinebufcommentf(gbl, "G-Lines set by %s", gbuf->glines->creator->content);
+
+    gbl->commit = gbuf->commit;
+  }
+
+  /* Save a duplicate of the glines in the log buffer */
+  for (gl = gbuf->glines; gl; gl = gl->next) {
+    sgl = glinedup(gl);
+    sgl->next = gbl->glines;
+    gbl->glines = sgl;
+  }
+
+  gbl->userhits += gbuf->userhits;
+  gbl->channelhits += gbuf->channelhits;
+
+  assert(gbuf->userhits + gbuf->channelhits == gbuf->hits.cursi);
+
+  for (i = 0; i < gbuf->hits.cursi; i++) {
+    slot = array_getfreeslot(&gbl->hits);
+    ((sstring **)gbl->hits.content)[slot] = getsstring(((sstring **)gbuf->hits.content)[i]->content, 512);
+  }
+
+  /* Log the transaction */
+  glinebuflogoffset++;
+
+  if (glinebuflogoffset >= MAXGLINELOG)
+    glinebuflogoffset = 0;
+
+  if (glinebuflog[glinebuflogoffset])
+    glinebufabort(glinebuflog[glinebuflogoffset]);
+
+  glinebuflog[glinebuflogoffset] = gbl;
+
+  return gbl->id;
+}