]> jfr.im git - irc/quakenet/newserv.git/blobdiff - glines/glines_commands.c
glines: Remove redundant account checks.
[irc/quakenet/newserv.git] / glines / glines_commands.c
index 89da107aac9848fcabde43eb091c449fa3dc4885..481c2309d38098988a8b95d9d666ce5086bfb819 100644 (file)
@@ -18,180 +18,269 @@ MODULE_VERSION("");
 static void registercommands(int, void *);
 static void deregistercommands(int, void *);
 
+static int parse_gline_flags(nick *sender, const char *flagparam, int *overridesanity, int *overridelimit, int *simulate, int *chase, int *coff) {
+  const char *pos;
+
+  *coff = 0;
+  *overridesanity = 0;
+  *overridelimit = 0;
+  *simulate = 0;
+
+  if (chase)
+    *chase = 0;
+
+  if (flagparam[0] == '-') {
+    *coff = 1;
+
+    for (pos = flagparam + 1; *pos; pos++) {
+      switch (*pos) {
+       case 'f':
+          *overridesanity = 1;
+         break;
+       case 'l':
+          *overridelimit = 1;
+         break;
+       case 'S':
+         *simulate = 1;
+         break;
+        case 'c':
+          if (!chase)
+            goto invalid;
+
+          *chase = 1;
+          break;
+       default:
+          goto invalid;
+      }
+    }
+  }
+
+  return 1;
+
+invalid:
+  controlreply(sender, "Invalid flag specified: %c", *pos);
+  return 0;
+}
+
 static int glines_cmdblock(void *source, int cargc, char **cargv) {
   nick *sender = source;
-  nick *target;
-  int hits, duration;
+  nick *target, *wnp;
+  whowas *ww;
+  int hits, duration, id;
+  int coff, overridesanity, overridelimit, simulate, chase;
   char *reason;
-  char creator[32];
+  char creator[128];
+  glinebuf gbuf;
+  int ownww;
 
-  if (cargc < 3)
+  if (cargc < 1)
     return CMD_USAGE;
 
-  target = getnickbynick(cargv[0]);
+  if (!parse_gline_flags(sender, cargv[0], &overridesanity, &overridelimit, &simulate, &chase, &coff))
+    return CMD_ERROR;
 
-  if (!target) {
-    controlreply(sender, "Sorry, couldn't find that user.");
+  if (cargc < 3 + coff)
+    return CMD_USAGE;
+
+  duration = durationtolong(cargv[coff + 1]);
+
+  if (duration <= 0) {
+    controlreply(sender, "Invalid duration specified.");
     return CMD_ERROR;
   }
 
-  duration = durationtolong(cargv[1]);
+  target = getnickbynick(cargv[coff]);
 
-  if (duration <= 0 || duration > MAXUSERGLINEDURATION) {
-    controlreply(sender, "Sorry, glines may not last longer than %s.", longtoduration(MAXUSERGLINEDURATION, 0));
-    return CMD_ERROR;
+  if (!target) {
+    ww = whowas_chase(cargv[coff], 1800);
+   
+    if (!ww) {
+      controlreply(sender, "Sorry, couldn't find that user.");
+      return CMD_ERROR;
+    }
+   
+    ownww = 0;
+
+    controlreply(sender, "Found matching whowas record:");
+    controlreply(sender, "%s", whowas_format(ww));
+  } else {
+    ww = whowas_fromnick(target, 1);
+    ownww = 1;
   }
 
-  reason = cargv[2];
+  wnp = &ww->nick;
 
-  if (strlen(reason) < MINUSERGLINEREASON) {
-    controlreply(sender, "Please specify a proper gline reason.");
+  if (sender != target && (IsService(wnp) || IsOper(wnp) || NickOnServiceServer(wnp))) {
+    controlreply(sender, "Target user '%s' is an oper or a service. Not setting G-Lines.", wnp->nick);
     return CMD_ERROR;
   }
 
+  rejoinline(cargv[coff + 2], cargc - coff - 2);
+  reason = cargv[coff + 2];
+
   snprintf(creator, sizeof(creator), "#%s", sender->authname);
-  hits = glinebynick(target, duration, reason, 0, creator);
 
-  controlwall(NO_OPER, NL_GLINES, "%s BLOCK'ed user '%s!%s@%s' for %s with reason '%s' (%d hits)", controlid(sender), target->nick, target->ident, target->host->name->content, longtoduration(duration, 0), reason, hits);
+  glinebufinit(&gbuf, 0);
+  glinebufcommentv(&gbuf, "BLOCK", cargc + coff - 1, cargv);
+  glinebufaddbywhowas(&gbuf, ww, 0, creator, reason, getnettime() + duration, getnettime(), getnettime() + duration);
+
+  glinebufspew(&gbuf, sender);
+
+  if (!glinebufchecksane(&gbuf, sender, overridesanity, overridelimit)) {
+    glinebufabort(&gbuf);
+    if (ownww)
+      whowas_free(ww);
+    controlreply(sender, "G-Lines failed sanity checks. Not setting G-Lines.");
+    return CMD_ERROR;
+  }
+
+  if (simulate) {
+    glinebufabort(&gbuf);
+    if (ownww)
+      whowas_free(ww);
+    controlreply(sender, "Simulation complete. Not setting G-Lines.");
+    return CMD_ERROR;
+  }
+
+  glinebufcounthits(&gbuf, &hits, NULL);
+  id = glinebufcommit(&gbuf, 1);
 
-  controlreply(sender, "Total hits: %d", hits);
+  controlwall(NO_OPER, NL_GLINES, "%s BLOCK'ed user '%s!%s@%s' for %s with reason '%s' (%d hits)", controlid(sender),
+              wnp->nick, wnp->ident, wnp->host->name->content,
+              longtoduration(duration, 0), reason, hits);
+
+  if (ownww)
+    whowas_free(ww);
+
+  controlreply(sender, "Done. G-Line transaction ID: %d", id);
 
   return CMD_OK;
 }
 
-static int glines_cmdrawgline(void *source, int cargc, char **cargv) {
+static int glines_cmdgline(void *source, int cargc, char **cargv) {
   nick *sender = source;
-  int duration, hits;
+  int duration, users, channels, id;
   char *mask, *reason;
-  char creator[32];
+  char creator[128];
+  int coff, overridesanity, overridelimit, simulate;
+  glinebuf gbuf;
+#if SNIRCD_VERSION < 140
   gline *gl;
+#endif /* SNIRCD_VERSION */
 
-  if (cargc < 3)
+  if (cargc < 1)
     return CMD_USAGE;
 
-  mask = cargv[0];
+  if (!parse_gline_flags(sender, cargv[0], &overridesanity, &overridelimit, &simulate, NULL, &coff))
+    return CMD_ERROR;
 
-  duration = durationtolong(cargv[1]);
+  if (cargc < 3 + coff)
+    return CMD_USAGE;
 
-  if (duration <= 0 || duration > MAXUSERGLINEDURATION) {
-    controlreply(sender, "Sorry, glines may not last longer than %s.", longtoduration(MAXUSERGLINEDURATION, 0));
-    return CMD_ERROR;
-  }
+  mask = cargv[coff];
 
-  reason = cargv[2];
+  duration = durationtolong(cargv[coff + 1]);
 
-  if (strlen(reason) < MINUSERGLINEREASON) {
-    controlreply(sender, "Please specify a proper gline reason.");
+  if (duration <= 0) {
+    controlreply(sender, "Invalid duration specified.");
     return CMD_ERROR;
   }
 
-#ifdef SNIRCD_13
+  rejoinline(cargv[coff + 2], cargc - coff - 2);
+  reason = cargv[coff + 2];
+
+#if SNIRCD_VERSION < 140
   gl = findgline(mask);
 
   if (gl) {
-     /* snircd 1.3:  warn opers that they can't modify this gline */
+     /* warn opers that they can't modify this gline */
     if (gl->flags & GLINE_ACTIVE) {
-      controlreply(sender, "Active G-Line already exists on %s - unable to modify", cargv[0]);
+      controlreply(sender, "Active G-Line already exists on %s - unable to modify", mask);
       return CMD_ERROR;
     }
 
     controlreply(sender, "Reactivating existing gline on %s", mask);
   }
-#endif
-
-  gl = makegline(mask);
-  hits = gline_count_hits(gl);
-  freegline(gl);
-
-  if (gl->flags & GLINE_BADCHAN && hits > MAXGLINECHANNELS) {
-    controlreply(sender, "This G-Line would hit %d channels. Limit is %d. NOT SET.", hits, MAXGLINECHANNELS);
-    return CMD_ERROR;
-  } else if (hits > MAXGLINEUSERS) {
-    controlreply(sender, "This G-Line would hit %d users. Limit is %d. NOT SET.", hits, MAXGLINEUSERS);
-    return CMD_ERROR;
-  }
+#endif /* SNIRCD_VERSION */
 
   snprintf(creator, sizeof(creator), "#%s", sender->authname);
-  glinesetmask(mask, duration, reason, creator);
-
-  controlwall(NO_OPER, NL_GLINES, "%s RAWGLINE'd mask '%s' for %s with reason '%s' (%d hits)", controlid(sender), mask, longtoduration(duration, 0), reason, hits);
-
-
-  controlreply(sender, "Done.");
-
-  return CMD_OK;
-}
 
-static int glines_cmdrawglinesimulate(void *source, int cargc, char **cargv) {
-  nick *sender = source;
-  char *mask, *type;
-  gline *gl;
-  int hits;
+  glinebufinit(&gbuf, 0);
+  glinebufcommentv(&gbuf, "GLINE", cargc + coff - 1, cargv);
 
-  if (cargc < 1)
-    return CMD_USAGE;
+  if (!glinebufadd(&gbuf, mask, creator, reason, getnettime() + duration, getnettime(), getnettime() + duration)) {
+    controlreply(sender, "Invalid G-Line mask.");
+    return CMD_ERROR;
+  }
 
-  mask = cargv[0];
+  glinebufspew(&gbuf, sender);
 
-  gl = makegline(mask);
+  if (!glinebufchecksane(&gbuf, sender, overridesanity, overridelimit)) {
+    glinebufabort(&gbuf);
+    controlreply(sender, "G-Lines failed sanity checks. Not setting G-Lines.");
+    return CMD_ERROR;
+  }
 
-  if (!gl) {
-    controlreply(sender, "Invalid G-Line mask.");
+  if (simulate) {
+    glinebufabort(&gbuf);
+    controlreply(sender, "Simulation complete. Not setting G-Lines.");
     return CMD_ERROR;
   }
 
-  hits = gline_count_hits(gl);
-  freegline(gl);
+  glinebufcounthits(&gbuf, &users, &channels);
+  id = glinebufcommit(&gbuf, 1);
 
-  if (gl->flags & GLINE_BADCHAN)
-    type = "channel";
-  else
-    type = "client";
+  controlwall(NO_OPER, NL_GLINES, "%s GLINE'd mask '%s' for %s with reason '%s' (hits %d users/%d channels)",
+    controlid(sender), mask, longtoduration(duration, 0), reason, users, channels);
 
-  controlreply(sender, "G-Line on '%s' would hit %d %s%s.", mask, hits, type, (hits == 1) ? "" : "s");
+  controlreply(sender, "Done. G-Line transaction ID: %d", id);
 
   return CMD_OK;
 }
 
-static int glines_cmdgline(void *source, int cargc, char **cargv) {
+static int glines_cmdsmartgline(void *source, int cargc, char **cargv) {
   nick *sender = source;
   char *origmask;
   char mask[512];
   char *p, *user, *host;
   struct irc_in_addr ip;
   unsigned char bits;
-  int count, duration;
+  int hits, duration;
+  int coff, overridesanity, overridelimit, simulate, id;
   char *reason;
-  char creator[32];
+  char creator[128];
+  glinebuf gbuf;
 
-  if (cargc < 3)
+  if (cargc < 1)
     return CMD_USAGE;
 
-  origmask = cargv[0];
-
-  if (origmask[0] == '#' || origmask[0] == '&') {
-    controlreply(sender, "Please use rawgline for badchans and regex glines.");
+  if (!parse_gline_flags(sender, cargv[0], &overridesanity, &overridelimit, &simulate, NULL, &coff))
     return CMD_ERROR;
-  }
 
-  duration = durationtolong(cargv[1]);
+  if (cargc < 3 + coff)
+    return CMD_USAGE;
+
+  origmask = cargv[coff];
 
-  if (duration <= 0 || duration > MAXUSERGLINEDURATION) {
-    controlreply(sender, "Sorry, glines may not last longer than %s.", longtoduration(MAXUSERGLINEDURATION, 0));
+  if (origmask[0] == '#' || origmask[0] == '&' || origmask[0] == '$') {
+    controlreply(sender, "Please use \"gline\" for badchan or realname glines.");
     return CMD_ERROR;
   }
 
-  reason = cargv[2];
+  duration = durationtolong(cargv[coff + 1]);
 
-  if (strlen(reason) < MINUSERGLINEREASON) {
-    controlreply(sender, "Please specify a proper gline reason.");
+  if (duration <= 0) {
+    controlreply(sender, "Invalid duration specified.");
     return CMD_ERROR;
   }
 
+  rejoinline(cargv[coff + 2], cargc - coff - 2);
+  reason = cargv[coff + 2];
+
   strncpy(mask, origmask, sizeof(mask));
 
   if (strchr(mask, '!')) {
-    controlreply(sender, "Use rawgline to place nick glines.");
+    controlreply(sender, "Use \"gline\" to place nick glines.");
     return CMD_ERROR;
   }
 
@@ -217,9 +306,32 @@ static int glines_cmdgline(void *source, int cargc, char **cargv) {
   }
 
   snprintf(creator, sizeof(creator), "#%s", sender->authname);
-  count = glinebyip(user, &ip, 128, duration, reason, 0, creator);
 
-  controlreply(sender, "Total hits: %d", count);
+  glinebufinit(&gbuf, 0);
+  glinebufcommentv(&gbuf, "SMARTGLINE", cargc + coff - 1, cargv);
+  glinebufaddbyip(&gbuf, user, &ip, 128, 0, creator, reason, getnettime() + duration, getnettime(), getnettime() + duration);
+
+  glinebufspew(&gbuf, sender);
+
+  if (!glinebufchecksane(&gbuf, sender, overridesanity, overridelimit)) {
+    glinebufabort(&gbuf);
+    controlreply(sender, "G-Lines failed sanity checks. Not setting G-Lines.");
+    return CMD_ERROR;
+  }
+
+  if (simulate) {
+    glinebufabort(&gbuf);
+    controlreply(sender, "Simulation complete. Not setting G-Lines.");
+    return CMD_ERROR;
+  }
+
+  glinebufcounthits(&gbuf, &hits, NULL);
+  id = glinebufcommit(&gbuf, 1);
+
+  controlwall(NO_OPER, NL_GLINES, "%s SMARTGLINE'd mask '%s' for %s with reason '%s' (%d hits)",
+    controlid(sender), cargv[0], longtoduration(duration, 0), reason, hits);
+
+  controlreply(sender, "Done. G-Line transaction ID: %d", id);
 
   return CMD_OK;
 }
@@ -245,59 +357,94 @@ static int glines_cmdungline(void *source, int cargc, char **cargv) {
 
   gline_deactivate(gl, 0, 1);
 
+  controlwall(NO_OPER, NL_GLINES, "%s UNGLINE'd mask '%s'", controlid(sender), cargv[0]);
+
   controlreply(sender, "G-Line deactivated.");
 
   return CMD_OK;
 }
 
+static int glines_cmddestroygline(void *source, int cargc, char **cargv) {
+  nick *sender = source;
+  gline *gl;
+
+  if (cargc < 1)
+    return CMD_USAGE;
+
+  gl = findgline(cargv[0]);
+
+  if (!gl) {
+    controlreply(sender, "No such G-Line.");
+    return CMD_ERROR;
+  }
+
+  gline_destroy(gl, 0, 1);
+
+  controlwall(NO_OPER, NL_GLINES, "%s DESTROYGLINE'd mask '%s'", controlid(sender), cargv[0]);
+
+  controlreply(sender, "G-Line destroyed.");
+
+  return CMD_OK;
+}
+
 static int glines_cmdclearchan(void *source, int cargc, char **cargv) {
   nick *sender = source;
   channel *cp;
   nick *np;
   char *reason = "Clearing channel.";
-  int mode, duration, i, slot;
+  int mode, duration, i, slot, hits, id;
+  int coff, overridesanity, overridelimit, simulate;
   array victims;
-  char creator[32];
+  char creator[128];
+  glinebuf gbuf;
 
-  if (cargc < 2)
+  if (cargc < 1)
     return CMD_USAGE;
 
-  cp = findchannel(cargv[0]);
+  if (!parse_gline_flags(sender, cargv[0], &overridesanity, &overridelimit, &simulate, NULL, &coff))
+    return CMD_ERROR;
+
+  if (cargc < 2 + coff)
+    return CMD_USAGE;
+
+  cp = findchannel(cargv[coff]);
 
   if (!cp) {
     controlreply(sender, "Couldn't find that channel.");
     return CMD_ERROR;
   }
 
-  if (strcmp(cargv[1], "kick") == 0)
+  if (strcmp(cargv[coff + 1], "kick") == 0)
     mode = 0;
-  else if (strcmp(cargv[1], "kill") == 0)
+  else if (strcmp(cargv[coff + 1], "kill") == 0)
     mode = 1;
-  else if (strcmp(cargv[1], "gline") == 0)
+  else if (strcmp(cargv[coff + 1], "gline") == 0)
     mode = 2;
-  else if (strcmp(cargv[1], "glineall") == 0)
+  else if (strcmp(cargv[coff + 1], "glineall") == 0)
     mode = 3;
   else
     return CMD_USAGE;
 
   if (mode == 0 || mode == 1) {
     if (cargc >= 3) {
-      rejoinline(cargv[2], cargc - 2);
-      reason = cargv[2];
+      rejoinline(cargv[coff + 2], cargc - coff - 2);
+      reason = cargv[coff + 2];
     }
   } else {
-    if (cargc < 3)
+    if (cargc < 3 + coff)
       return CMD_USAGE;
 
-    duration = durationtolong(cargv[2]);
+    duration = durationtolong(cargv[coff + 2]);
 
-    if (duration <= 0 || duration > MAXUSERGLINEDURATION) {
-      controlreply(sender, "Sorry, glines may not last longer than %s.", longtoduration(MAXUSERGLINEDURATION, 0));
+    if (duration <= 0) {
+      controlreply(sender, "Invalid duration specified.");
       return CMD_ERROR;
     }
 
-    if (cargc >= 4)
-      reason = cargv[3];
+    if (cargc >= 4 + coff) {
+      rejoinline(cargv[coff + 3], cargc - coff - 3);
+      reason = cargv[coff + 3];
+    }
   }
 
   array_init(&victims, sizeof(nick *));
@@ -314,7 +461,7 @@ static int glines_cmdclearchan(void *source, int cargc, char **cargv) {
     if (!np)
       continue;
 
-    if (IsService(np) || IsOper(np))
+    if (IsService(np) || IsOper(np) || NickOnServiceServer(np))
       continue;
 
     slot = array_getfreeslot(&victims);
@@ -323,31 +470,70 @@ static int glines_cmdclearchan(void *source, int cargc, char **cargv) {
 
   snprintf(creator, sizeof(creator), "#%s", sender->authname);
 
+  glinebufinit(&gbuf, 0);
+  glinebufcommentv(&gbuf, "CLEARCHAN", cargc + coff - 1, cargv);
+
   for (i = 0; i < victims.cursi; i++) {
     np = ((nick **)victims.content)[i];
 
     switch (mode) {
       case 0:
-        localkickuser(NULL, cp, np, reason);
+       if (simulate)
+         controlreply(sender, "user: %s!%s@%s r(%s)", np->nick, np->ident, np->host->name->content, np->realname->name->content);
+       else
+         localkickuser(NULL, cp, np, reason);
+
         break;
       case 1:
-        killuser(NULL, np, "%s", reason);
+       if (simulate)
+         controlreply(sender, "user: %s!%s@%s r(%s)", np->nick, np->ident, np->host->name->content, np->realname->name->content);
+       else
+          killuser(NULL, np, "%s", reason);
+
         break;
       case 2:
         if (IsAccount(np))
           break;
         /* fall through */
       case 3:
-        glinebynick(np, duration, reason, 0, creator);
+        glinebufaddbynick(&gbuf, np, 0, creator, reason, getnettime() + duration, getnettime(), getnettime() + duration);
         break;
       default:
         assert(0);
     }
   }
 
+  if (mode != 0 && mode != 1) {
+    glinebufspew(&gbuf, sender);
+
+    if (!glinebufchecksane(&gbuf, sender, overridesanity, overridelimit)) {
+      glinebufabort(&gbuf);
+      controlreply(sender, "G-Line failed sanity checks. Not setting G-Line.");
+      return CMD_ERROR;
+    }
+  }
+
+  if (simulate) {
+    glinebufabort(&gbuf);
+    controlreply(sender, "Simulation complete. Not clearing channel.");
+    return CMD_ERROR;
+  }
+
+  glinebufmerge(&gbuf);
+  glinebufcounthits(&gbuf, &hits, NULL);
+  id = glinebufcommit(&gbuf, 1);
+
   array_free(&victims);
 
-  controlreply(sender, "Done.");
+  if (mode == 0 || mode == 1) {
+    controlwall(NO_OPER, NL_GLINES, "%s CLEARCHAN'd channel '%s' with mode '%s' and reason '%s'",
+      controlid(sender), cp->index->name->content, cargv[1], reason);
+    controlreply(sender, "Done.");
+  } else {
+    controlwall(NO_OPER, NL_GLINES, "%s CLEARCHAN'd channel '%s' with mode '%s', duration %s and reason '%s' (%d hits)",
+      controlid(sender), cp->index->name->content, cargv[1], longtoduration(duration, 0), reason, hits);
+    controlreply(sender, "Done. G-Line transaction ID: %d", id);
+  }
 
   return CMD_OK;
 }
@@ -356,45 +542,70 @@ static int glines_cmdtrustgline(void *source, int cargc, char **cargv) {
   nick *sender = source;
   trustgroup *tg;
   trusthost *th;
-  int duration, count;
+  int duration, hits;
+  int coff, overridesanity, overridelimit, simulate, id;
   char *reason;
   char mask[512];
-  char creator[32];
+  char creator[128];
+  glinebuf gbuf;
 
-  if (cargc < 4)
+  if (cargc < 1)
     return CMD_USAGE;
 
-  tg = tg_strtotg(cargv[0]);
+  if (!parse_gline_flags(sender, cargv[0], &overridesanity, &overridelimit, &simulate, NULL, &coff))
+    return CMD_ERROR;
+
+  if (cargc < 4 + coff)
+    return CMD_USAGE;
+
+  tg = tg_strtotg(cargv[coff]);
 
   if (!(tg->flags & TRUST_RELIABLE_USERNAME)) {
     controlreply(sender, "Sorry, that trust group does not have the \"reliable username\" flag.");
     return CMD_ERROR;
   }
 
-  duration = durationtolong(cargv[2]);
+  duration = durationtolong(cargv[coff + 2]);
 
-  if (duration <= 0 || duration > MAXUSERGLINEDURATION) {
-    controlreply(sender, "Sorry, glines may not last longer than %s.", longtoduration(MAXUSERGLINEDURATION, 0));
+  if (duration <= 0) {
+    controlreply(sender, "Invalid duration specified.");
     return CMD_ERROR;
   }
 
-  reason = cargv[3];
+  rejoinline(cargv[coff + 3], cargc - coff - 3);
+  reason = cargv[coff + 3];
 
-  if (strlen(reason) < MINUSERGLINEREASON) {
-    controlreply(sender, "Please specify a proper gline reason.");
-    return CMD_ERROR;
+  snprintf(creator, sizeof(creator), "#%s", sender->authname);
+
+  glinebufinit(&gbuf, 0);
+  glinebufcommentv(&gbuf, "TRUSTGLINE", cargc + coff - 1, cargv);
+
+  for(th = tg->hosts; th; th = th->next) {
+    snprintf(mask, sizeof(mask), "*!%s@%s", cargv[1], CIDRtostr(th->ip, th->bits));
+    glinebufadd(&gbuf, mask, creator, reason, getnettime() + duration, getnettime(), getnettime() + duration);
   }
 
-  count = 0;
+  glinebufspew(&gbuf, sender);
 
-  snprintf(creator, sizeof(creator), "#%s", sender->authname);
+  if (!glinebufchecksane(&gbuf, sender, overridesanity, overridelimit)) {
+    glinebufabort(&gbuf);
+    controlreply(sender, "G-Line failed sanity checks. Not setting G-Line.");
+    return CMD_ERROR;
+  }
 
-  for(th = tg->hosts; th; th = th->next) {
-    snprintf(mask, sizeof(mask), "*!%s@%s", cargv[1], trusts_cidr2str(&th->ip, th->bits));
-    count += glinesetmask(mask, duration, reason, creator);
+  if (simulate) {
+    glinebufabort(&gbuf);
+    controlreply(sender, "Simulation complete. Not setting G-Lines.");
+    return CMD_ERROR;
   }
 
-  controlreply(sender, "Done. %d G-Lines set.", count);
+  glinebufcounthits(&gbuf, &hits, NULL);
+  id = glinebufcommit(&gbuf, 1);
+
+  controlwall(NO_OPER, NL_GLINES, "%s TRUSTGLINE'd user '%s' on trust group '%s' for %s with reason '%s' (%d hits)",
+    controlid(sender), cargv[1], tg->name->content, longtoduration(duration, 0), reason, hits);
+
+  controlreply(sender, "Done. G-Line transaction ID: %d", id);
 
   return CMD_OK;
 }
@@ -404,6 +615,7 @@ static int glines_cmdtrustungline(void *source, int cargc, char **cargv) {
   trustgroup *tg;
   trusthost *th;
   char mask[512];
+  gline *gl;
   int count;
 
   if (cargc < 2)
@@ -419,46 +631,54 @@ static int glines_cmdtrustungline(void *source, int cargc, char **cargv) {
   count = 0;
 
   for (th = tg->hosts; th; th = th->next) {
-    snprintf(mask, sizeof(mask), "*!%s@%s", cargv[1], trusts_cidr2str(&th->ip, th->bits));
-    count += glineunsetmask(mask);
+    snprintf(mask, sizeof(mask), "*!%s@%s", cargv[1], CIDRtostr(th->ip, th->bits));
+
+    gl = findgline(mask);
+
+    if (gl && (gl->flags & GLINE_ACTIVE)) {
+      gline_deactivate(gl, 0, 1);
+      count++;
+    }
   }
 
-  controlreply(sender, "Done. %d G-Lines deactivated.", count);
+  controlwall(NO_OPER, NL_GLINES, "%s TRUSTUNGLINE'd user '%s' on trust group '%s' (%d G-Lines deactivated)",
+    controlid(sender), cargv[1], tg->name->content, count);
+
+  controlreply(sender, "Done.");
 
   return CMD_OK;
 }
 
 static int glines_cmdglstats(void *source, int cargc, char **cargv) {
-  nick* sender = (nick*)source;
-  gline* g;
-  gline* sg;
+  nick *sender = (nick*)source;
+  gline *gl, *next;
   time_t curtime = getnettime();
   int glinecount = 0, hostglinecount = 0, ipglinecount = 0, badchancount = 0, rnglinecount = 0;
   int deactivecount = 0, activecount = 0;
 
-  for (g = glinelist; g; g = sg) {
-    sg = g->next;
+  for (gl = glinelist; gl; gl = next) {
+    next = gl->next;
 
-    if (g->lifetime <= curtime) {
-      removegline(g);
+    if (gl->lifetime <= curtime) {
+      removegline(gl);
       continue;
-    } else if (g->expire <= curtime) {
-      g->flags &= ~GLINE_ACTIVE;
+    } else if (gl->expire <= curtime) {
+      gl->flags &= ~GLINE_ACTIVE;
     }
 
-    if (g->flags & GLINE_ACTIVE) {
+    if (gl->flags & GLINE_ACTIVE) {
       activecount++;
     } else {
       deactivecount++;
     }
 
-    if (g->flags & GLINE_IPMASK)
+    if (gl->flags & GLINE_IPMASK)
       ipglinecount++;
-    else if (g->flags & GLINE_HOSTMASK)
+    else if (gl->flags & GLINE_HOSTMASK)
       hostglinecount++;
-    else if (g->flags & GLINE_REALNAME)
+    else if (gl->flags & GLINE_REALNAME)
       rnglinecount++;
-    else if (g->flags & GLINE_BADCHAN)
+    else if (gl->flags & GLINE_BADCHAN)
       badchancount++;
     glinecount++;
   }
@@ -480,14 +700,13 @@ static int glines_cmdglstats(void *source, int cargc, char **cargv) {
 
 static int glines_cmdglist(void *source, int cargc, char **cargv) {
   nick *sender = (nick *)source;
-  gline *g;
-  gline *sg;
+  gline *gl, *next;
   time_t curtime = time(NULL);
   int flags = 0;
   char *mask;
   int count = 0;
   int limit = 500;
-  char tmp[250];
+  char expirestr[250], idstr[250];
 
   if (cargc < 1 || (cargc == 1 && cargv[0][0] == '-')) {
     controlreply(sender, "Syntax: glist [-flags] <mask>");
@@ -502,8 +721,6 @@ static int glines_cmdglist(void *source, int cargc, char **cargv) {
     return CMD_ERROR;
   }
 
-  mask = cargv[0];
-
   if (cargc > 1) {
     char* ch = cargv[0];
 
@@ -555,69 +772,71 @@ static int glines_cmdglist(void *source, int cargc, char **cargv) {
   }
 
   if (!(flags & GLIST_COUNT))
-    controlreply(sender, "%-50s %-19s %-25s %s", "Mask:", "Expires in:", "Creator:", "Reason:");
+    controlreply(sender, "%-50s %-19s %-15s %-25s %s", "Mask:", "Expires in:", "Transaction ID:", "Creator:", "Reason:");
 
   gline *searchgl = makegline(mask);
 
-  for (g = glinelist; g; g = sg) {
-    sg = g->next;
+  for (gl = glinelist; gl; gl = next) {
+    next = gl->next;
 
-    if (g->lifetime <= curtime) {
-      removegline(g);
+    if (gl->lifetime <= curtime) {
+      removegline(gl);
       continue;
-    } else if (g->expire <= curtime) {
-      g->flags &= ~GLINE_ACTIVE;
+    } else if (gl->expire <= curtime) {
+      gl->flags &= ~GLINE_ACTIVE;
     }
 
-    if (!(g->flags & GLINE_ACTIVE)) {
+    if (!(gl->flags & GLINE_ACTIVE)) {
       if (!(flags & GLIST_INACTIVE)) {
         continue;
       }
     }
 
     if (flags & GLIST_REALNAME) {
-      if (!(g->flags & GLINE_REALNAME))
+      if (!(gl->flags & GLINE_REALNAME))
         continue;
       if (flags & GLIST_EXACT) {
-        if (!glineequal(searchgl, g)) {
+        if (!glineequal(searchgl, gl))
           continue;
-        }
       } else if (flags & GLIST_FIND) {
-        if (!gline_match_mask(searchgl, g)) {
+        if (!gline_match_mask(gl, searchgl))
+          continue;
+      } else {
+        if (!match2strings(mask, glinetostring(gl)))
           continue;
-        }
       }
     } else {
-      if (g->flags & GLINE_REALNAME)
+      if (gl->flags & GLINE_REALNAME)
         continue;
 
       if (flags & GLIST_REASON) {
         if (flags & GLIST_EXACT) {
-          if (g->reason && ircd_strcmp(mask, g->reason->content) != 0)
+          if (!gl->reason || ircd_strcmp(mask, gl->reason->content) != 0)
             continue;
         } else if (flags & GLIST_FIND) {
-          if (g->reason && match(g->reason->content, mask))
+          if (!gl->reason || !match2strings(gl->reason->content, mask))
             continue;
-        } else if (g->reason && match(mask, g->reason->content))
+        } else if (!gl->reason || !match2strings(mask, gl->reason->content))
             continue;
       } else if (flags & GLIST_OWNER) {
         if (flags & GLIST_EXACT) {
-          if (g->creator && ircd_strcmp(mask, g->creator->content) != 0)
+          if (!gl->creator || ircd_strcmp(mask, gl->creator->content) != 0)
             continue;
         } else if (flags & GLIST_FIND) {
-          if (g->creator && match(g->creator->content, mask))
+          if (!gl->creator || !match2strings(gl->creator->content, mask))
             continue;
-        } else if (g->creator && match(mask, g->creator->content))
+        } else if (!gl->creator || !match2strings(mask, gl->creator->content))
           continue;
       } else {
         if (flags & GLIST_EXACT) {
-          if (!glineequal(searchgl, g)) {
+          if (!glineequal(searchgl, gl))
             continue;
-          }
         } else if (flags & GLIST_FIND) {
-          if (!gline_match_mask(searchgl, g)) {
+          if (!gline_match_mask(gl, searchgl))
+            continue;
+        } else {
+          if (!match2strings(mask, glinetostring(gl)))
             continue;
-          }
         }
       }
     }
@@ -627,10 +846,16 @@ static int glines_cmdglist(void *source, int cargc, char **cargv) {
 
     count++;
 
-    if (!(flags & GLIST_COUNT) && (count < limit)) {
-      snprintf(tmp, 249, "%s", glinetostring(g));
-      controlreply(sender, "%s%-49s %-19s %-25s %s", g->flags & GLINE_ACTIVE ? "+" : "-", tmp, g->flags & GLINE_ACTIVE ? (char*)longtoduration(g->expire - curtime, 0) : "<inactive>",
-            g->creator ? g->creator->content : "", g->reason ? g->reason->content : "");
+    if (!(flags & GLIST_COUNT) && count < limit) {
+      snprintf(expirestr, sizeof(expirestr), "%s", glinetostring(gl));
+      snprintf(idstr, sizeof(idstr), "%d", gl->glinebufid);
+      controlreply(sender, "%s%-49s %-19s %-15s %-25s %s",
+        (gl->flags & GLINE_ACTIVE) ? "+" : "-",
+        expirestr,
+        (gl->flags & GLINE_ACTIVE) ? (char*)longtoduration(gl->expire - curtime, 0) : "<inactive>",
+        gl->glinebufid ? idstr : "",
+        gl->creator ? gl->creator->content : "",
+        gl->reason ? gl->reason->content : "");
     }
   }
 
@@ -639,6 +864,130 @@ static int glines_cmdglist(void *source, int cargc, char **cargv) {
   return CMD_OK;
 }
 
+static int glines_cmdglinelog(void *source, int cargc, char **cargv) {
+  nick *sender = source;
+  glinebuf *gbl;
+  gline *gl;
+  int i, id, count;
+  char timebuf[30];
+  
+  id = 0;
+  
+  if (cargc > 0) {
+    id = atoi(cargv[0]);
+    
+    if (id == 0) {
+      controlreply(sender, "Invalid log ID.");
+      return CMD_ERROR;
+    }
+  }
+  
+  controlreply(sender, "Time                 ID         G-Lines    User Hits      Channel Hits     Comment");
+  
+  for (i = 0; i < MAXGLINELOG; i++) {
+    gbl = glinebuflog[(i + glinebuflogoffset + 1) % MAXGLINELOG];
+    
+    if (!gbl)
+      continue;
+    
+    if (id == 0 || gbl->id == id) {
+      count = 0;
+      
+      for (gl = gbl->glines; gl; gl = gl->next)
+       count++;
+
+      strftime(timebuf, sizeof(timebuf), "%d/%m/%y %H:%M:%S", localtime((gbl->amend) ? &gbl->amend : &gbl->commit));
+      strncat(timebuf, (gbl->amend) ? "*" : " ", sizeof(timebuf));
+      controlreply(sender, "%-20s %-10d %-10d %-15d %-15d %s", timebuf, gbl->id, count, gbl->userhits, gbl->channelhits, gbl->comment ? gbl->comment->content : "(no comment)");
+    }
+
+    if (id != 0 && gbl->id == id) {
+      glinebufspew(gbl, sender);
+      controlreply(sender, "Done.");
+      return CMD_OK;
+    }
+  }
+  
+  if (id == 0) {
+    controlreply(sender, "Done.");
+  } else {
+    controlreply(sender, "Log entry for ID %d not found.", id);
+  }
+
+  return CMD_OK;
+}
+
+static int glines_cmdglineundo(void *source, int cargc, char **cargv) {
+  nick *sender = source;
+  int id;
+  
+  if (cargc < 1)
+    return CMD_USAGE;
+
+  id = atoi(cargv[0]);
+  
+  if (id == 0 || !glinebufundo(id)) {
+    controlreply(sender, "Invalid log ID.");
+    return CMD_ERROR;
+  }
+
+  controlreply(sender, "Done.");  
+  
+  return CMD_OK;
+}
+
+static int glines_cmdsyncglines(void *source, int cargc, char **cargv) {
+  nick *sender = source;
+  gline *gl;
+  int count;
+
+  count = 0;
+
+  for (gl = glinelist; gl; gl = gl->next) {
+    gline_propagate(gl);
+    count++;
+  }
+  
+  controlwall(NO_OPER, NL_GLINES, "%s SYNCGLINE'd %d G-Lines.",
+    controlid(sender), count);
+
+  controlreply(sender, "Done.");
+
+  return CMD_OK;
+}
+
+static int glines_cmdcleanupglines(void *source, int cargc, char **cargv) {
+  nick *sender = source;
+  gline **pnext, *gl;
+  int count;
+  time_t now;
+  
+  count = 0;
+  time(&now);
+  
+  for (pnext = &glinelist; *pnext;) {
+    gl = *pnext;
+    
+    /* Remove inactivate glines that have been last changed more than a week ago */
+    if (!(gl->flags & GLINE_ACTIVE) && gl->lastmod < now - 7 * 24 * 60 * 60) {
+      gline_destroy(gl, 0, 1);
+      count++;
+    } else {
+      pnext = &((*pnext)->next);
+    }
+    
+    if (!*pnext)
+      break;
+  }
+  
+  controlwall(NO_OPER, NL_GLINES, "%s CLEANUPGLINES'd %d G-Lines.",
+    controlid(sender), count);
+  
+  controlreply(sender, "Done.");
+  
+  return CMD_OK;
+}
+
 static int commandsregistered;
 
 static void registercommands(int hooknum, void *arg) {
@@ -646,16 +995,20 @@ static void registercommands(int hooknum, void *arg) {
     return;
   commandsregistered = 1;
 
-  registercontrolhelpcmd("block", NO_OPER, 3, glines_cmdblock, "Usage: block <nick> <duration> <reason>\nSets a gline using an appropriate mask given the user's nickname.");
-  registercontrolhelpcmd("rawgline", NO_OPER, 3, glines_cmdrawgline, "Usage: rawgline <mask> <duration> <reason>\nSets a gline.");
-  registercontrolhelpcmd("rawglinesimulate", NO_OPER, 1, glines_cmdrawglinesimulate, "Usage: rawglinesimulate <mask>\nSimulates what happens when a gline is set.");
-  registercontrolhelpcmd("gline", NO_OPER, 3, glines_cmdgline, "Usage: gline <user@host> <duration> <reason>\nSets a gline. Automatically adjusts the mask so as not to hit unrelated trusted users.");
+  registercontrolhelpcmd("block", NO_OPER, 4, glines_cmdblock, "Usage: block ?flags? <nick> <duration> <reason>\nSets a gline using an appropriate mask given the user's nickname.\nFlags can be one or more of:\n-f - bypass sanity checks\n-l - bypass hit limits\n-S - simulate who the glines would hit\n-c - chase nick across quits/kills/nick changes");
+  registercontrolhelpcmd("gline", NO_OPER, 4, glines_cmdgline, "Usage: gline ?flags? <mask> <duration> <reason>\nSets a gline.\nFlags can be one or more of:\n-f - bypass sanity checks\n-l - bypass hit limits\n-S - simulate who the glines would hit");
+  registercontrolhelpcmd("smartgline", NO_OPER, 4, glines_cmdsmartgline, "Usage: smartgline ?flags? <user@host> <duration> <reason>\nSets a gline. Automatically adjusts the mask depending on whether the specified mask is trusted.\nFlags can be one or more of:\n-f - bypass sanity checks\n-l - bypass hit limits\n-S - simulate who the glines would hit");
   registercontrolhelpcmd("ungline", NO_OPER, 1, glines_cmdungline, "Usage: ungline <mask>\nDeactivates a gline.");
-  registercontrolhelpcmd("clearchan", NO_OPER, 4, glines_cmdclearchan, "Usage: clearchan <#channel> <how> <duration> ?reason?\nClears a channel.\nhow can be one of:\nkick - Kicks users.\nkill - Kills users.\ngline - Glines non-authed users (using an appropriate mask).\nglineall - Glines users.\nDuration is only valid when glining users. Reason defaults to \"Clearing channel.\".");
-  registercontrolhelpcmd("trustgline", NO_OPER, 4, glines_cmdtrustgline, "Usage: trustgline <#id|name> <user> <duration> <reason>\nSets a gline on the specified username for each host in the specified trust group. The username may contain wildcards.");
+  registercontrolhelpcmd("destroygline", NO_DEVELOPER, 1, glines_cmddestroygline, "Usage: destroygline <mask>\nDestroys a gline.");
+  registercontrolhelpcmd("clearchan", NO_OPER, 5, glines_cmdclearchan, "Usage: clearchan ?flags? <#channel> <how> <duration> ?reason?\nClears a channel.\nhow can be one of:\nkick - Kicks users.\nkill - Kills users.\ngline - Glines non-authed users (using an appropriate mask).\nglineall - Glines users.\nDuration is only valid when glining users. Reason defaults to \"Clearing channel.\".\nFlags (for glines) can be one or more of:\n-f - bypass sanity checks\n-l - bypass hit limits\n-S - simulate who the glines would hit");
+  registercontrolhelpcmd("trustgline", NO_OPER, 5, glines_cmdtrustgline, "Usage: trustgline ?flags? <#id|name> <user> <duration> <reason>\nSets a gline on the specified username for each host in the specified trust group. The username may contain wildcards.\nFlags can be one or more of:\n-f - bypass sanity checks\n-l - bypass hit limits\n-S - simulate who the glines would hit");
   registercontrolhelpcmd("trustungline", NO_OPER, 2, glines_cmdtrustungline, "Usage: trustungline <#id|name> <user>\nRemoves a gline that was previously set with trustgline.");
   registercontrolhelpcmd("glstats", NO_OPER, 0, glines_cmdglstats, "Usage: glstat\nShows statistics about G-Lines.");
   registercontrolhelpcmd("glist", NO_OPER, 2, glines_cmdglist, "Usage: glist [-flags] <mask>\nLists matching G-Lines.\nValid flags are:\n-c: Count G-Lines.\n-f: Find G-Lines active on <mask>.\n-x: Find G-Lines matching <mask> exactly.\n-R: Find G-lines on realnames.\n-o: Search for glines by owner.\n-r: Search for glines by reason.\n-i: Include inactive glines.");
+  registercontrolhelpcmd("glinelog", NO_OPER, 1, glines_cmdglinelog, "Usage: glinelog ?id?\nShows information about previous gline transactions.");
+  registercontrolhelpcmd("glineundo", NO_OPER, 1, glines_cmdglineundo, "Usage: glineundo ?id?\nUndoes a gline transaction.");
+  registercontrolhelpcmd("syncglines", NO_DEVELOPER, 0, glines_cmdsyncglines, "Usage: syncglines\nSends all G-Lines to all other servers.");
+  registercontrolhelpcmd("cleanupglines", NO_DEVELOPER, 0, glines_cmdcleanupglines, "Usage: cleanupglines\nDestroys all deactivated G-Lines.");
 }
 
 static void deregistercommands(int hooknum, void *arg) {
@@ -664,15 +1017,19 @@ static void deregistercommands(int hooknum, void *arg) {
   commandsregistered = 0;
 
   deregistercontrolcmd("block", glines_cmdblock);
-  deregistercontrolcmd("rawgline", glines_cmdrawgline);
-  deregistercontrolcmd("rawglinesimulate", glines_cmdrawglinesimulate);
   deregistercontrolcmd("gline", glines_cmdgline);
+  deregistercontrolcmd("smartgline", glines_cmdsmartgline);
   deregistercontrolcmd("ungline", glines_cmdungline);
+  deregistercontrolcmd("destroygline", glines_cmddestroygline);
   deregistercontrolcmd("clearchan", glines_cmdclearchan);
   deregistercontrolcmd("trustgline", glines_cmdtrustgline);
   deregistercontrolcmd("trustungline", glines_cmdtrustungline);
   deregistercontrolcmd("glstats", glines_cmdglstats);
   deregistercontrolcmd("glist", glines_cmdglist);
+  deregistercontrolcmd("glinelog", glines_cmdglinelog);
+  deregistercontrolcmd("glineundo", glines_cmdglineundo);
+  deregistercontrolcmd("syncglines", glines_cmdsyncglines);
+  deregistercontrolcmd("cleanupglines", glines_cmdcleanupglines);
 }
 
 void _init(void) {