Author: Entrope <mdpoole@troilus.org>
authorMichael Poole <mdpoole@troilus.org>
Tue, 13 Jan 2009 02:30:25 +0000 (02:30 +0000)
committerMichael Poole <mdpoole@troilus.org>
Tue, 13 Jan 2009 02:30:25 +0000 (02:30 +0000)
Description:

Make G-line parsing considerably more robust for unusual input forms.
Fix affected-user counting for G-lines with CIDR host portions.  Add
tests to exercise the new parsing behaviors.

git-svn-id: file:///home/klmitch/undernet-ircu/undernet-ircu-svn/ircu2/branches/u2_10_12_branch@1900 c9e4aea6-c8fd-4c43-8297-357d70d61c8c

ChangeLog
include/gline.h
ircd/gline.c
ircd/m_gline.c
tests/glines.cmd [new file with mode: 0644]
tests/ircd-2.conf
tests/ircd.conf
tests/readme.txt
tests/test-driver.pl

index 378f262b48052ee730ee96b4a0b034826c9b762b..86a672d7870e7f2333bf0405808fb629cfb39876 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,36 @@
+2009-01-12  Michael Poole <mdpoole@troilus.org>
+
+       * include/gline.h (gline_forward_deactivation): Declare.
+
+       * ircd/gline.c (count_users): Use ipmask-based checks too.
+       (gline_add): Require flags to have exactly one of the GLINE_GLOBAL
+       and GLINE_LOCAL bits set.
+       (gline_forward_deactivation): Implement new function.
+       (gline_find): Only require the GLINE_LOCAL flag to be set in the
+       gline structure; infer GLINE_GLOBAL when it is cleared.  (This
+       matches the value and usage of GLINE_MASK.)
+
+       * ircd/m_gline.c (ms_gline): Default lastmod to the known lastmod
+       time for GLINE_LOCAL_{DE,}ACTIVATE.
+       (mo_gline): Check that expiration times parse as expected.  Reject
+       "*" as a target for GLINE_LOCAL_{DE,}ACTIVATE.  Require reason and
+       expiration time for new G-lines.  Allow deactivation of an unknown
+       G-line without creating it ("so-and-so ... creating new G-line" is
+       a confusing message for a deactivation).
+
+       * tests/glines.cmd: New test script for G-line parsing tests.
+
+       * tests/ircd.conf: Enable CONFIG_OPERCMDS for glines.cmd.
+
+       * tests/ircd-2.conf: Likewise.
+
+       * tests/readme.txt: Add section on command syntax.
+
+       * tests/test-driver.pl: Report line numbers more clearly.
+       Fix (somewhat kludgily) the brokenness of consecutive "expect"
+       lines, as demonstrated by the numeric 219 and 281 expects in
+       glines.cmd.
+
 2008-11-17  Michael Poole <mdpoole@troilus.org>
 
        * ircd/m_kick.c (ms_kick): Properly handle crossing net rider and
index 44798a61cd7a29fa19a3b9530599be095cf0863b..e5623f4f3fca2ee44477f87180e6af5d2dc3429e 100644 (file)
@@ -128,6 +128,9 @@ extern int gline_activate(struct Client *cptr, struct Client *sptr,
 extern int gline_deactivate(struct Client *cptr, struct Client *sptr,
                            struct Gline *gline, time_t lastmod,
                            unsigned int flags);
+extern int gline_forward_deactivation(struct Client *cptr, struct Client *sptr,
+                                      char *userhost, time_t expire, time_t lastmod,
+                                      time_t lifetime, unsigned int flags);
 extern int gline_modify(struct Client *cptr, struct Client *sptr,
                        struct Gline *gline, enum GlineAction action,
                        char *reason, time_t expire, time_t lastmod,
index f294d99f456f07694a4c5fd5e22324339c299ebd..487dde94962d28779587bb39f5f7db5d69f4ff8d 100644 (file)
@@ -354,11 +354,15 @@ gline_propagate(struct Client *cptr, struct Client *sptr, struct Gline *gline)
 static int
 count_users(char *mask)
 {
+  struct irc_in_addr ipmask;
   struct Client *acptr;
   int count = 0;
+  int ipmask_valid;
   char namebuf[USERLEN + HOSTLEN + 2];
   char ipbuf[USERLEN + SOCKIPLEN + 2];
+  unsigned char ipmask_len;
 
+  ipmask_valid = ipmask_parse(mask, &ipmask, &ipmask_len);
   for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) {
     if (!IsUser(acptr))
       continue;
@@ -368,7 +372,9 @@ count_users(char *mask)
     ircd_snprintf(0, ipbuf, sizeof(ipbuf), "%s@%s", cli_user(acptr)->username,
                  ircd_ntoa(&cli_ip(acptr)));
 
-    if (!match(mask, namebuf) || !match(mask, ipbuf))
+    if (!match(mask, namebuf)
+        || !match(mask, ipbuf)
+        || (ipmask_valid && ipmask_check(&cli_ip(acptr), &ipmask, ipmask_len)))
       count++;
   }
 
@@ -429,6 +435,8 @@ gline_add(struct Client *cptr, struct Client *sptr, char *userhost,
 
   assert(0 != userhost);
   assert(0 != reason);
+  assert(((flags & (GLINE_GLOBAL | GLINE_LOCAL)) == GLINE_GLOBAL) ||
+         ((flags & (GLINE_GLOBAL | GLINE_LOCAL)) == GLINE_LOCAL));
 
   Debug((DEBUG_DEBUG, "gline_add(\"%s\", \"%s\", \"%s\", \"%s\", %Tu, %Tu "
         "%Tu, 0x%04x)", cli_name(cptr), cli_name(sptr), userhost, reason,
@@ -664,6 +672,42 @@ gline_deactivate(struct Client *cptr, struct Client *sptr, struct Gline *gline,
   return 0;
 }
 
+/** Send a deactivation request for a locally unknown G-line.
+ * @param[in] cptr Client that sent us the G-line modification.
+ * @param[in] sptr Client that originated the G-line modification.
+ * @param[in] userhost Text representation of G-line target.
+ * @param[in] expire Expiration time of G-line.
+ * @param[in] lastmod Last modification time of G-line.
+ * @param[in] lifetime Lifetime of G-line.
+ * @param[in] flags Bitwise combination of GLINE_* flags.
+ * @return Zero.
+ */
+int
+gline_forward_deactivation(struct Client *cptr, struct Client *sptr,
+                           char *userhost, time_t expire, time_t lastmod,
+                           time_t lifetime, unsigned int flags)
+{
+  char *msg = "deactivating unknown global";
+
+  if (!lifetime)
+    lifetime = expire;
+
+  /* Inform ops and log it */
+  sendto_opmask_butone(0, SNO_GLINE, "%s %s GLINE for %s, expiring at %Tu",
+                       (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
+                         cli_name(sptr) : cli_name((cli_user(sptr))->server),
+                      msg, userhost, expire + TSoffset);
+
+  log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE,
+           "%#C %s GLINE for %s, expiring at %Tu", sptr, msg, userhost,
+           expire);
+
+  sendcmdto_serv_butone(sptr, CMD_GLINE, cptr, "* -%s %Tu %Tu %Tu",
+                        userhost, expire, lastmod, lifetime);
+
+  return 0;
+}
+
 /** Modify a global G-line.
  * @param[in] cptr Client that sent us the G-line modification.
  * @param[in] sptr Client that originated the G-line modification.
@@ -918,8 +962,7 @@ gline_find(char *userhost, unsigned int flags)
 
   if (flags & (GLINE_BADCHAN | GLINE_ANY)) {
     gliter(BadChanGlineList, gline, sgline) {
-      if ((flags & GLINE_GLOBAL && gline->gl_flags & GLINE_LOCAL) ||
-         (flags & GLINE_LOCAL && gline->gl_flags & GLINE_GLOBAL) ||
+        if ((flags & (GlineIsLocal(gline) ? GLINE_GLOBAL : GLINE_LOCAL)) ||
          (flags & GLINE_LASTMOD && !gline->gl_lastmod))
        continue;
       else if ((flags & GLINE_EXACT ? ircd_strcmp(gline->gl_user, userhost) :
@@ -936,8 +979,7 @@ gline_find(char *userhost, unsigned int flags)
   canon_userhost(t_uh, &user, &host, "*");
 
   gliter(GlobalGlineList, gline, sgline) {
-    if ((flags & GLINE_GLOBAL && gline->gl_flags & GLINE_LOCAL) ||
-       (flags & GLINE_LOCAL && gline->gl_flags & GLINE_GLOBAL) ||
+    if ((flags & (GlineIsLocal(gline) ? GLINE_GLOBAL : GLINE_LOCAL)) ||
        (flags & GLINE_LASTMOD && !gline->gl_lastmod))
       continue;
     else if (flags & GLINE_EXACT) {
index 95e2ac173b29471b0898194b303e27d3543f98e9..c099a631e9fcea82cbe4ea09d22c0235f0466301 100644 (file)
@@ -279,6 +279,7 @@ ms_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
   case GLINE_LOCAL_DEACTIVATE: /* locally deactivating a G-line */
     if (!agline) /* no G-line to locally activate or deactivate? */
       return send_reply(sptr, ERR_NOSUCHGLINE, mask);
+    lastmod = agline->gl_lastmod;
     break; /* no additional parameters to manipulate */
 
   case GLINE_ACTIVATE: /* activating a G-line */
@@ -379,7 +380,7 @@ mo_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
   unsigned int flags = 0;
   enum GlineAction action = GLINE_MODIFY;
   time_t expire = 0;
-  char *mask = parv[1], *target = 0, *reason = 0;
+  char *mask = parv[1], *target = 0, *reason = 0, *end;
 
   if (parc < 2)
     return gline_list(sptr, 0);
@@ -422,7 +423,9 @@ mo_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
       return need_more_params(sptr, "GLINE");
 
     target = parv[2]; /* get the target... */
-    expire = atoi(parv[3]) + CurrentTime; /* and the expiration */
+    expire = strtol(parv[3], &end, 10) + CurrentTime; /* and the expiration */
+    if (*end != '\0')
+      return send_reply(sptr, SND_EXPLICIT | ERR_BADEXPIRE, "%s :Bad expire time", parv[3]);
 
     flags |= GLINE_EXPIRE; /* remember that we got an expire time */
 
@@ -442,8 +445,11 @@ mo_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
 
   case GLINE_LOCAL_ACTIVATE: /* locally activate a G-line */
   case GLINE_LOCAL_DEACTIVATE: /* locally deactivate a G-line */
-    if (parc > 2) /* if target is available, pick it */
+    if (parc > 2) /* if target is available, pick it */
       target = parv[2];
+      if (target[0] == '*' && target[1] == '\0')
+        return send_reply(sptr, ERR_NOSUCHSERVER, target);
+    }
     break;
 
   case GLINE_ACTIVATE: /* activating/adding a G-line */
@@ -453,8 +459,10 @@ mo_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
 
     if (parc > 3) {
       /* get expiration and target */
-      expire = atoi(parv[parc - 2]) + CurrentTime;
       reason = parv[parc - 1];
+      expire = strtol(parv[parc - 2], &end, 10) + CurrentTime;
+      if (*end != '\0')
+        return send_reply(sptr, SND_EXPLICIT | ERR_BADEXPIRE, "%s :Bad expire time", parv[parc - 2]);
 
       flags |= GLINE_EXPIRE | GLINE_REASON; /* remember that we got 'em */
 
@@ -499,8 +507,8 @@ mo_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
           action == GLINE_LOCAL_ACTIVATE ? '>' : '<'));
 
     sendcmdto_one(sptr, CMD_GLINE, acptr, "%C %s%c%s", acptr,
-                 flags & GLINE_OPERFORCE ? "!" : "",
-                 action == GLINE_LOCAL_ACTIVATE ? '>' : '<', mask);
+                  flags & GLINE_OPERFORCE ? "!" : "",
+                  action == GLINE_LOCAL_ACTIVATE ? '>' : '<', mask);
 
     return 0; /* all done */
   }
@@ -582,10 +590,12 @@ mo_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
     }
   }
 
-  /* can't modify a G-line that doesn't exist... */
+  /* can't modify a G-line that doesn't exist...
+   * (and if we are creating a new one, we need a reason and expiration)
+   */
   if (!agline &&
       (action == GLINE_MODIFY || action == GLINE_LOCAL_ACTIVATE ||
-       action == GLINE_LOCAL_DEACTIVATE))
+       action == GLINE_LOCAL_DEACTIVATE || !reason || !expire))
     return send_reply(sptr, ERR_NOSUCHGLINE, mask);
 
   /* check for G-line permissions... */
@@ -600,6 +610,12 @@ mo_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
       return send_reply(sptr, ERR_NOPRIVILEGES);
   }
 
+  /* If globally disabling a G-line that we do not already have, avoid
+   * creating a temporary one. */
+  if (!agline && action == GLINE_DEACTIVATE) {
+    return gline_forward_deactivation(cptr, sptr, mask, expire, CurrentTime, 0, flags);
+  }
+
   Debug((DEBUG_DEBUG, "I have a global G-line I am acting upon now; "
         "target %s, mask %s, operforce %s, action %s, expire %Tu, "
         "reason: %s; gline %s!  (fields present: %s %s)", target, 
diff --git a/tests/glines.cmd b/tests/glines.cmd
new file mode 100644 (file)
index 0000000..f7a3f12
--- /dev/null
@@ -0,0 +1,330 @@
+define srv1 localhost:7601
+define srv1-name irc.example.net
+define srv2 localhost:7611
+define srv2-name irc-2.example.net
+define cl1-nick Op3rm4n
+define cl2-nick Monitor
+
+# Connect a client, oper it up and force G-line server notices on (0x8200).
+connect cl1 %cl1-nick% oper %srv1% :Some IRC Operator
+:cl1 oper oper oper
+:cl1 mode %cl1-nick% +s +33280
+
+# Do the same for the second server, for monitoring snotices.
+# This is useful while debugging the remote operations in this script.
+connect cl2 %cl2-nick% oper %srv2% :Some IRC Operator
+:cl2 oper oper oper
+:cl2 mode %cl2-nick% +s +33280
+
+# For an operator, the syntax is:
+#  GLINE [[!][+|-|>|<]<mask> [<target>] [<expiration> [:<reason>]]]
+# By itself, that's 2 * 5 * 3 (target missing/self, other or global)
+#  * 3 = 90 possibilities.
+#
+# In each case, we would want to try it with various combinations:
+# - Local gline for <mask> absent or present
+# - Global gline for <mask> absent, or present but locally and/or globally activated or deactivated (5 combinations)
+# So ten pre-existing states for each syntax variant.
+# For sanity's sake, we don't do all 900 combinations.
+#
+# Perl code to generate a fairly complete list:
+# foreach my $operator (split(//, ' +-><')) {
+#     foreach my $mask ('test@example.com') {
+#         foreach my $target ('', '%srv1-name%', '%srv2-name%', '*') {
+#             foreach my $expiration ('', '100000') {
+#                 foreach my $reason ('', ':foo') {
+#                     my $str = "GLINE ${operator}${mask} ${target} ${expiration} ${reason}";
+#                     $str =~ s/ +/ /g;
+#                     $str =~ s/ +$//;
+#                     print "$str\n";
+#                 }
+#             }
+#         }
+#         print "\n";
+#     }
+# }
+
+# Initial query: verify that our target G-line does not exist.
+:cl1 raw :GLINE test@example.com
+:cl1 expect %srv1-name% 512 test@example.com :No such gline
+
+# Try a bunch of operations, only one of which should generate an actual G-line.
+:cl1 raw :GLINE test@example.com :foo
+:cl1 expect %srv1-name% 461 GLINE :Not enough parameters
+:cl1 raw :GLINE test@example.com 100000
+:cl1 expect %srv1-name% 461 GLINE :Not enough parameters
+:cl1 raw :GLINE test@example.com 100000 :foo
+:cl1 expect %srv1-name% 515 foo :Bad expire time
+:cl1 raw :GLINE test@example.com %srv1-name%
+:cl1 expect %srv1-name% 461 GLINE :Not enough parameters
+:cl1 raw :GLINE test@example.com %srv1-name% :foo
+:cl1 expect %srv1-name% 515 foo :Bad expire time
+:cl1 raw :GLINE test@example.com %srv1-name% 100000
+:cl1 expect %srv1-name% 461 GLINE :Not enough parameters
+
+# Check that we still have no G-line, and that we create it as expected.
+:cl1 raw :GLINE test@example.com
+:cl1 expect %srv1-name% 512 test@example.com :No such gline
+:cl1 raw :GLINE test@example.com %srv1-name% 100000 :foo
+:cl1 expect %srv1-name% NOTICE :\\*\\*\\* Notice -- %cl1-nick% adding local GLINE for test@example.com, expiring at \\d+: foo
+:cl1 raw :GLINE test@example.com
+:cl1 expect %srv1-name% 280 test@example.com \\d+ 0 \\d+ %srv1-name% \\+ :foo
+:cl1 expect %srv1-name% 281 :End of G-line List
+# Now remove it (and verify removal).
+:cl1 raw :GLINE -test@example.com 100000 :foo
+:cl1 expect %srv1-name% NOTICE :\\*\\*\\* Notice -- %cl1-nick% removing local GLINE for test@example.com
+:cl1 raw :GLINE test@example.com
+:cl1 expect %srv1-name% 512 test@example.com :No such gline
+
+# Try doing remote operations.
+:cl1 raw :GLINE test@example.com %srv2-name%
+:cl1 expect %srv1-name% 461 GLINE :Not enough parameters
+:cl1 raw :GLINE test@example.com %srv2-name% :foo
+:cl1 expect %srv1-name% 515 foo :Bad expire time
+:cl1 raw :GLINE test@example.com %srv2-name% 100000
+:cl1 expect %srv1-name% 461 GLINE :Not enough parameters
+:cl1 raw :GLINE test@example.com
+:cl1 expect %srv1-name% 512 test@example.com :No such gline
+:cl1 raw :GLINE test@example.com %srv2-name% 100000 :foo
+# No response expected for remote commands; do a remote stats query to check.
+:cl1 raw :STATS g %srv2-name%
+:cl1 expect %srv2-name% 247 G test@example.com \\d+ 0 \\d+ \\+ :foo
+:cl1 expect %srv2-name% 219 g :End of /STATS report
+:cl1 raw :GLINE -test@example.com %srv2-name% 100000 :foo
+:cl1 raw :STATS g %srv2-name%
+:cl1 expect %srv2-name% 219 g :End of /STATS report
+
+# Try doing network-wide operations.
+:cl1 raw :GLINE test@example.com *
+:cl1 expect %srv1-name% 461 GLINE :Not enough parameters
+# These should fail because no existing G-line matches.
+:cl1 raw :GLINE test@example.com * :foo
+:cl1 expect %srv1-name% 515 foo :Bad expire time
+:cl1 raw :GLINE test@example.com * 100000
+:cl1 expect %srv1-name% 512 test@example.com :No such gline
+:cl1 raw :GLINE test@example.com * 100000 :foo
+:cl1 expect %srv1-name% 512 test@example.com :No such gline
+
+# Try explicit create/activate operations.
+:cl1 raw :GLINE +test@example.com
+:cl1 expect %srv1-name% 461 GLINE :Not enough parameters
+:cl1 raw :GLINE +test@example.com :foo
+:cl1 expect %srv1-name% 461 GLINE :Not enough parameters
+:cl1 raw :GLINE +test@example.com 100000
+:cl1 expect %srv1-name% 461 GLINE :Not enough parameters
+# This next one should create the G-line.
+:cl1 raw :GLINE test@example.com
+:cl1 expect %srv1-name% 512 test@example.com :No such gline
+:cl1 raw :GLINE +test@example.com 100000 :foo
+:cl1 expect %srv1-name% NOTICE :\\*\\*\\* Notice -- %cl1-nick% adding local GLINE for test@example.com, expiring at \\d+: foo
+:cl1 raw :GLINE test@example.com
+:cl1 expect %srv1-name% 280 test@example.com \\d+ 0 \\d+ %srv1-name% \\+ :foo
+:cl1 expect %srv1-name% 281 :End of G-line List
+:cl1 raw :GLINE -test@example.com 100000 :foo
+:cl1 expect %srv1-name% NOTICE :\\*\\*\\* Notice -- %cl1-nick% removing local GLINE for test@example.com
+
+# Local create/activate operations?
+:cl1 raw :GLINE +test@example.com %srv1-name%
+:cl1 expect %srv1-name% 461 GLINE :Not enough parameters
+:cl1 raw :GLINE +test@example.com %srv1-name% :foo
+:cl1 expect %srv1-name% 515 .+ :Bad expire time
+:cl1 raw :GLINE +test@example.com %srv1-name% 100000
+:cl1 expect %srv1-name% 515 .+ :Bad expire time
+:cl1 raw :GLINE +test@example.com %srv1-name% 100000 :foo
+:cl1 expect %srv1-name% NOTICE :\\*\\*\\* Notice -- %cl1-nick% adding local GLINE for test@example.com, expiring at \\d+: foo
+:cl1 raw :GLINE test@example.com
+:cl1 expect %srv1-name% 280 test@example.com \\d+ 0 \\d+ %srv1-name% \\+ :foo
+:cl1 expect %srv1-name% 281 :End of G-line List
+:cl1 raw :GLINE -test@example.com 100000 :foo
+:cl1 expect %srv1-name% NOTICE :\\*\\*\\* Notice -- %cl1-nick% removing local GLINE for test@example.com
+:cl1 raw :GLINE +test@example.com %srv1-name% 100000 :foo
+:cl1 raw :GLINE -test@example.com 100000 :foo
+:cl1 expect %srv1-name% NOTICE :\\*\\*\\* Notice -- %cl1-nick% removing local GLINE for test@example.com
+:cl1 raw :GLINE +test@example.com %srv1-name% 100000 :foo
+:cl1 raw :GLINE -test@example.com %srv1-name% 100000 :foo
+:cl1 expect %srv1-name% NOTICE :\\*\\*\\* Notice -- %cl1-nick% removing local GLINE for test@example.com
+
+# Remote create/activate operations?
+:cl1 raw :GLINE +test@example.com %srv2-name%
+:cl1 expect %srv1-name% 461 GLINE :Not enough parameters
+:cl1 raw :GLINE +test@example.com %srv2-name% :foo
+:cl1 expect %srv1-name% 515 .+ :Bad expire time
+:cl1 raw :GLINE +test@example.com %srv2-name% 100000
+:cl1 expect %srv1-name% 515 .+ :Bad expire time
+:cl1 raw :GLINE +test@example.com %srv2-name% 100000 :foo
+# No response expected for remote commands; do a remote stats query to check.
+:cl1 raw :STATS g %srv2-name%
+:cl1 expect %srv2-name% 247 G test@example.com \\d+ 0 \\d+ \\+ :foo
+:cl1 expect %srv2-name% 219 g :End of /STATS report
+:cl1 raw :GLINE -test@example.com %srv2-name% 100000 :foo
+:cl1 raw :STATS g %srv2-name%
+:cl1 expect %srv2-name% 219 g :End of /STATS report
+
+# Global create/activate operations?
+:cl1 raw :GLINE +test@example.com *
+:cl1 expect %srv1-name% 512 test@example.com :No such gline
+:cl1 raw :GLINE +test@example.com * :foo
+:cl1 expect %srv1-name% 515 .+ :Bad expire time
+:cl1 raw :GLINE +test@example.com * 100000
+:cl1 expect %srv1-name% 515 .+ :Bad expire time
+
+# Local G-line deactivation?
+:cl1 raw :GLINE -test@example.com
+:cl1 expect %srv1-name% 461 GLINE :Not enough parameters
+:cl1 raw :GLINE -test@example.com :foo
+:cl1 expect %srv1-name% 461 GLINE :Not enough parameters
+:cl1 raw :GLINE -test@example.com 100000
+:cl1 expect %srv1-name% 461 GLINE :Not enough parameters
+:cl1 raw :GLINE -test@example.com 100000 :foo
+:cl1 expect %srv1-name% 512 test@example.com :No such gline
+
+# .. with a specified server?
+:cl1 raw :GLINE -test@example.com %srv1-name%
+:cl1 expect %srv1-name% 461 GLINE :Not enough parameters
+:cl1 raw :GLINE -test@example.com %srv1-name% :foo
+:cl1 expect %srv1-name% 515 %srv1-name% :Bad expire time
+:cl1 raw :GLINE -test@example.com %srv1-name% 100000
+:cl1 expect %srv1-name% 515 %srv1-name% :Bad expire time
+:cl1 raw :GLINE -test@example.com %srv1-name% 100000 :foo
+:cl1 expect %srv1-name% 512 test@example.com :No such gline
+:cl1 raw :GLINE -test@example.com %srv2-name%
+:cl1 expect %srv1-name% 461 GLINE :Not enough parameters
+:cl1 raw :GLINE -test@example.com %srv2-name% :foo
+:cl1 expect %srv1-name% 515 %srv2-name% :Bad expire time
+:cl1 raw :GLINE -test@example.com %srv2-name% 100000
+:cl1 expect %srv1-name% 515 %srv2-name% :Bad expire time
+:cl1 raw :GLINE -test@example.com %srv2-name% 100000 :foo
+:cl1 expect %srv2-name% 512 test@example.com :No such gline
+
+# Global deactivations?
+:cl1 raw :GLINE -test@example.com *
+:cl1 expect %srv1-name% 512 test@example.com :No such gline
+:cl1 raw :GLINE -test@example.com * :foo
+:cl1 expect %srv1-name% 515 \\* :Bad expire time
+:cl1 raw :GLINE -test@example.com * 100000
+:cl1 expect %srv1-name% 515 \\* :Bad expire time
+:cl1 raw :GLINE -test@example.com * 100000 :foo
+:cl1 expect %srv1-name% NOTICE :\\*\\*\\* Notice -- %cl1-nick% deactivating unknown global GLINE for test@example.com
+
+# Now start with the operations that create or need a global G-line.
+
+# Global activations and deactivations?
+:cl1 raw :GLINE +test@example.com * 100000 :foo
+:cl1 expect %srv1-name% NOTICE :\\*\\*\\* Notice -- %cl1-nick% adding global GLINE for test@example.com
+:cl1 raw :GLINE test@example.com
+:cl1 expect %srv1-name% 280 test@example.com \\d+ \\d+ \\d+ \\* \\+ :foo
+:cl1 raw :GLINE test@example.com * 100000
+:cl1 expect %srv1-name% NOTICE :\\*\\*\\* Notice -- %cl1-nick% modifying global GLINE for test@example.com: changing expiration time to \\d+; and extending record lifetime to \\d+
+:cl1 raw :GLINE test@example.com * 100000 :food
+:cl1 expect %srv1-name% NOTICE :\\*\\*\\* Notice -- %cl1-nick% modifying global GLINE for test@example.com: changing expiration time to \\d+; extending record lifetime to \\d+; and changing reason to "food"
+:cl1 raw :GLINE -test@example.com *
+:cl1 expect %srv1-name% NOTICE :\\*\\*\\* Notice -- %cl1-nick% modifying global GLINE for test@example.com: globally deactivating G-line
+:cl1 raw :GLINE -test@example.com * 100000 :foo
+:cl1 expect %srv1-name% NOTICE :\\*\\*\\* Notice -- %cl1-nick% modifying global GLINE for test@example.com: changing expiration time to \\d+; extending record lifetime to \\d+; and changing reason to "foo"
+:cl1 raw :GLINE test@example.com
+:cl1 expect %srv1-name% 280 test@example.com \\d+ \\d+ \\d+ \\* - :foo
+
+# Failed local activations and deactivations?
+:cl1 raw :GLINE >test@example.com
+:cl1 expect %srv1-name% NOTICE :\\*\\*\\* Notice -- %cl1-nick% modifying global GLINE for test@example.com: locally activating G-line
+:cl1 raw :GLINE <test@example.com
+:cl1 expect %srv1-name% NOTICE :\\*\\*\\* Notice -- %cl1-nick% modifying global GLINE for test@example.com: locally deactivating G-line
+:cl1 raw :GLINE >test@example.com :foo
+:cl1 expect %srv1-name% 402 foo :No such server
+:cl1 raw :GLINE <test@example.com :foo
+:cl1 expect %srv1-name% 402 foo :No such server
+:cl1 raw :GLINE >test@example.com 100000
+:cl1 expect %srv1-name% 402 100000 :No such server
+:cl1 raw :GLINE <test@example.com 100000 :foo
+:cl1 expect %srv1-name% 402 100000 :No such server
+:cl1 raw :GLINE <test@example.com 100000 :foo
+:cl1 expect %srv1-name% 402 100000 :No such server
+:cl1 raw :GLINE >test@2.example.com
+:cl1 expect %srv1-name% 512 test@2.example.com :No such gline
+:cl1 raw :GLINE >test@example.com %srv1-name%
+
+# What about successes for the local server?
+# (For simplicity, single-server activations and deactivations are not
+# allowed to change any other parameters.)
+:cl1 expect %srv1-name% NOTICE :\\*\\*\\* Notice -- %cl1-nick% modifying global GLINE for test@example.com: locally activating G-line
+:cl1 raw :GLINE <test@example.com %srv1-name%
+:cl1 expect %srv1-name% NOTICE :\\*\\*\\* Notice -- %cl1-nick% modifying global GLINE for test@example.com: locally deactivating G-line
+:cl1 raw :GLINE >test@example.com %srv1-name% :foo
+:cl1 expect %srv1-name% NOTICE :\\*\\*\\* Notice -- %cl1-nick% modifying global GLINE for test@example.com: locally activating G-line
+:cl1 raw :GLINE <test@example.com %srv1-name% :foo
+:cl1 expect %srv1-name% NOTICE :\\*\\*\\* Notice -- %cl1-nick% modifying global GLINE for test@example.com: locally deactivating G-line
+:cl1 raw :GLINE >test@example.com %srv1-name% 100000
+:cl1 expect %srv1-name% NOTICE :\\*\\*\\* Notice -- %cl1-nick% modifying global GLINE for test@example.com: locally activating G-line
+:cl1 raw :GLINE <test@example.com %srv1-name% 100000
+:cl1 expect %srv1-name% NOTICE :\\*\\*\\* Notice -- %cl1-nick% modifying global GLINE for test@example.com: locally deactivating G-line
+:cl1 raw :GLINE >test@example.com %srv1-name% 100000 :foo
+:cl1 expect %srv1-name% NOTICE :\\*\\*\\* Notice -- %cl1-nick% modifying global GLINE for test@example.com: locally activating G-line
+:cl1 raw :GLINE <test@example.com %srv1-name% 100000 :foo
+:cl1 expect %srv1-name% NOTICE :\\*\\*\\* Notice -- %cl1-nick% modifying global GLINE for test@example.com: locally deactivating G-line
+:cl1 raw :GLINE >test@example.com %srv1-name% 100000 :foo
+:cl1 expect %srv1-name% NOTICE :\\*\\*\\* Notice -- %cl1-nick% modifying global GLINE for test@example.com: locally activating G-line
+:cl1 raw :GLINE <test@example.com %srv1-name% 100000 :foo
+:cl1 expect %srv1-name% NOTICE :\\*\\*\\* Notice -- %cl1-nick% modifying global GLINE for test@example.com: locally deactivating G-line
+
+# And successful activations/deactiations for a (single) remote server?
+# First make sure the global G-line is globally activated.
+:cl1 raw :GLINE +test@example.com * 100000 :foo
+# Form: "GLINE <gline@mask server.name"
+:cl1 raw :STATS g %srv2-name%
+:cl1 expect %srv2-name% 247 G test@example.com \\d+ \\d+ \\d+ \\+ :foo
+:cl1 expect %srv2-name% 219 g :End of /STATS report
+:cl1 raw :GLINE <test@example.com %srv2-name%
+:cl1 raw :STATS g %srv2-name%
+:cl1 expect %srv2-name% 247 G test@example.com \\d+ \\d+ \\d+ <\\+ :foo
+:cl1 expect %srv2-name% 219 g :End of /STATS report
+:cl1 raw :GLINE >test@example.com %srv2-name%
+:cl1 raw :STATS g %srv2-name%
+:cl1 expect %srv2-name% 247 G test@example.com \\d+ \\d+ \\d+ >\\+ :foo
+:cl1 expect %srv2-name% 219 g :End of /STATS report
+# Form: "GLINE <gline@mask server.name :foo"
+:cl1 raw :GLINE <test@example.com %srv2-name% :foo
+:cl1 raw :STATS g %srv2-name%
+:cl1 expect %srv2-name% 247 G test@example.com \\d+ \\d+ \\d+ <\\+ :foo
+:cl1 expect %srv2-name% 219 g :End of /STATS report
+:cl1 raw :GLINE >test@example.com %srv2-name% :foo
+:cl1 raw :STATS g %srv2-name%
+:cl1 expect %srv2-name% 247 G test@example.com \\d+ \\d+ \\d+ >\\+ :foo
+:cl1 expect %srv2-name% 219 g :End of /STATS report
+# Form: "GLINE <gline@mask server.name 100000"
+:cl1 raw :GLINE <test@example.com %srv2-name% 100000
+:cl1 raw :STATS g %srv2-name%
+:cl1 expect %srv2-name% 247 G test@example.com \\d+ \\d+ \\d+ <\\+ :foo
+:cl1 expect %srv2-name% 219 g :End of /STATS report
+:cl1 raw :GLINE >test@example.com %srv2-name% 100000
+:cl1 raw :STATS g %srv2-name%
+:cl1 expect %srv2-name% 247 G test@example.com \\d+ \\d+ \\d+ >\\+ :foo
+:cl1 expect %srv2-name% 219 g :End of /STATS report
+# Form: "GLINE <gline@mask server.name 100000 :foo"
+:cl1 raw :GLINE <test@example.com %srv2-name% 100000 :foo
+:cl1 raw :STATS g %srv2-name%
+:cl1 expect %srv2-name% 247 G test@example.com \\d+ \\d+ \\d+ <\\+ :foo
+:cl1 expect %srv2-name% 219 g :End of /STATS report
+:cl1 raw :GLINE >test@example.com %srv2-name% 100000 :foo
+:cl1 raw :STATS g %srv2-name%
+:cl1 expect %srv2-name% 247 G test@example.com \\d+ \\d+ \\d+ >\\+ :foo
+:cl1 expect %srv2-name% 219 g :End of /STATS report
+
+# What about activations/deactivations that might go across the whole network?
+# (These are not permitted because "global local [de]activation" does not
+# make sense: just globally [de]activate the G-line.)
+:cl1 raw :GLINE >test@example.com *
+:cl1 expect %srv1-name% 402 \\* :No such server
+:cl1 raw :GLINE <test@example.com *
+:cl1 expect %srv1-name% 402 \\* :No such server
+:cl1 raw :GLINE >test@example.com * :foo
+:cl1 expect %srv1-name% 402 \\* :No such server
+:cl1 raw :GLINE <test@example.com * :foo
+:cl1 expect %srv1-name% 402 \\* :No such server
+:cl1 raw :GLINE >test@example.com * 100000
+:cl1 expect %srv1-name% 402 \\* :No such server
+:cl1 raw :GLINE <test@example.com * 100000
+:cl1 expect %srv1-name% 402 \\* :No such server
+:cl1 raw :GLINE >test@example.com * 100000 :foo
+:cl1 expect %srv1-name% 402 \\* :No such server
+:cl1 raw :GLINE <test@example.com * 100000 :foo
+:cl1 expect %srv1-name% 402 \\* :No such server
index 45882d6d4fa57ed6b21f92036b820f4e993512e1..e05e389fad944866d5c1dee4bc7aae1e1e14a922 100644 (file)
@@ -35,4 +35,5 @@ Port { port = 7611; };
 
 Features {
         "PPATH" = "ircd-2.pid";
+        "CONFIG_OPERCMDS" = "TRUE";
 };
index eedadc781aeeec12842d84928618f57466ee2d53..3f6b529ecbd0ca5235cc7ceabd8bd6f0c8e1b3d2 100644 (file)
@@ -37,4 +37,5 @@ IAuth { program = "../tests/iauth-test"; };
 
 Features {
         "HUB" = "TRUE";
+        "CONFIG_OPERCMDS" = "TRUE";
 };
index c3f618077e23f9191447143e130c3b144e2ad8c7..98466d186766e987b61c339ca370c5a04e4d5bc3 100644 (file)
@@ -19,3 +19,89 @@ The test-driver.pl script accepts several command-line options:
 
 The normal output is one dot for each line that is executed.  Using
 the -D and -V options generates much more output.
+
+Command Syntax
+==============
+
+A test script typically starts with a set of variable definitions for
+strings that are used throughout the script or that may be changed to
+match changes ircu's configuration file.  These have the syntax:
+       define <variable> <replacement text>
+
+A variable is expanded by writing %variablename% in later commands.
+If a variable is dereferenced without a definition, the test will
+abort.
+
+Following the variable definitions is usually one or more "connect"
+statements.  These have the syntax:
+       connect <tag> <nickname> <username> <server:port> :<Real Name or GECOS>
+This creates a client and starts a connection to an IRC server.  The
+tag is used to issue commands for that client in the rest of the file.
+The remaining fields have their usual meanings for IRC.
+
+A number of IRC commands are supported natively, including:
+       :<tag> join <channel>
+       :<tag> mode <nick/channel> [<modes> ...]
+       :<tag> nick <nickname>
+       :<tag> notice <target> :<text>
+       :<tag> oper <name> <password>
+       :<tag> part <channel> [:<message>]
+       :<tag> privmsg <target> :<text>
+       :<tag> quit [:<message>]
+       :<tag> raw :<irc line to send>
+
+Other commands are used to implement tests:
+       :<tag> expect <irc line contents>
+       :<tag> sleep <seconds>
+       :<tag> wait <tag2,tag3>
+
+The test commands are discussed at more length below.
+
+expect Syntax
+=============
+
+The command to look for data coming from the irc server is "expect":
+       :<tag> expect <irc line contents>
+
+The contents are treated as a regular expression and matched against
+the start of the line.  If the line from the IRC server began with
+':', that is removed before the match is performed.
+
+Because the contents are a regular expression, and because \ is used
+as an escape character both in parsing both the script line and the
+regular expression, some common things become awkward to match:
+       :cl1 mode %channel% +D
+       :cl1 expect %cl1-nick% mode %channel% \\+D
+or a more drastic example:
+       :cl1 mode %channel% +b *!*@*.bar.example.*
+       :cl1 mode %channel% +b
+       :cl1 expect %srv1-name% 367 %channel% \\*!\\*@\\*\\.bar\\.example\\.* %cl1-nick% \\d+
+
+sleep Syntax
+============
+
+The command to make a client stop operating for a fixed period of time
+is "sleep":
+       :<tag> sleep <seconds>
+
+This will deactivate the identified client for at least <seconds>
+seconds (which may be a floating point number).  Other clients will
+continue to process commands, but if another command for the
+identified client is encountered, it will block execution until the
+time expires.
+
+wait Syntax
+===========
+
+The command to synchronize one client to another is "wait":
+       :<tag> wait <tag2[,tag3...]>
+
+This is syntactic sugar for something like this:
+       :<tag> expect <nick2> NOTICE <nick> :SYNC
+       :<nick2> notice <nick> :SYNC
+       :<tag> expect <nick3> NOTICE <nick> :SYNC
+       :<nick3> notice <nick> :SYNC
+
+In other words, the wait command uses in-IRC messages to make sure
+that other clients have already executed commands up to a certain
+point in the test script.
index e20745cda33a58400bc513a0a7171c10c7cfd1a3..8ba77a280cad873a0c4a557619421fa4e0af9a4e 100755 (executable)
@@ -164,7 +164,7 @@ sub drv_heartbeat {
 
     # expand any macros in the line
     $line =~ s/(?<=[^\\])%(\S+?)%/$heap->{macros}->{$1}
-      or die "Use of undefined macro $1 at $heap->{lineno}\n"/eg;
+      or die "Use of undefined macro $1 at line $heap->{lineno}\n"/eg;
     # remove any \-escapes
     $line =~ s/\\(.)/$1/g;
     # figure out the type of line
@@ -235,7 +235,7 @@ sub drv_heartbeat {
         my $client = $heap->{clients}->{$c};
         if (not $client) {
           print "ERROR: Unknown session name $c (line $heap->{lineno}; ignoring)\n";
-        } elsif (($used->{$c} and not $zero_time->{$cmd}) or not $client->{ready}) {
+        } elsif (($used->{$c} and not $zero_time->{$cmd}) or ($cmd ne 'expect' and not $client->{ready})) {
           push @unavail, $c;
         } else {
           push @avail, $c;
@@ -276,8 +276,8 @@ sub drv_heartbeat {
 }
 
 sub drv_timeout_expect {
-  my ($kernel, $session, $client) = @_[KERNEL, SESSION, ARG0];
-  print "ERROR: Dropping timed-out expectation by $client->{name}: ".join(',', @{$client->{expect}->[0]})."\n";
+  my ($kernel, $session, $client, $heap) = @_[KERNEL, SESSION, ARG0, HEAP];
+  print "\nERROR: Dropping timed-out expectation by $client->{name} (line $heap->{expect_lineno}): ".join(',', @{$client->{expect}->[0]})."\n";
   $client->{expect_alarms}->[0] = undef;
   unexpect($kernel, $session, $client);
 }
@@ -370,6 +370,7 @@ sub cmd_wait {
 sub cmd_expect {
   my ($kernel, $session, $heap, $client, $args) = @_[KERNEL, SESSION, HEAP, ARG0, ARG1];
   die "Missing argument" unless $#$args >= 0;
+  $heap->{expect_lineno} = $heap->{lineno};
   push @{$client->{expect}}, $args;
   push @{$client->{expect_alarms}}, $kernel->delay_set('timeout_expect', EXPECT_TIMEOUT, $client);
   $kernel->call($session, 'disable_client', $client);