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;
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++;
}
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,
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.
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) :
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) {
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 */
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);
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 */
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 */
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 */
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 */
}
}
}
- /* 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... */
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,
--- /dev/null
+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