+2007-03-17 Kevin L. Mitchell <klmitch@mit.edu>
+
+ * ircd/s_err.c: update replies to handle new fields in
+ RPL_STATSGLINE and RPL_GLIST--new fields indicate G-line lastmod,
+ G-line lifetime, and local activation status
+
+ * ircd/m_gline.c: update function documentation for ms_gline();
+ move test for server to before mask processing; don't look up
+ remote server too early; add code to process local activations and
+ deactivations early in ms_gline(); implement adding and destroying
+ local G-lines; don't try to locally activate or deactivate G-lines
+ that don't exist; add code to keep track of which fields were
+ available to ms_gline(); implement G-line modification and
+ creation; remove old ms_gline() code; convert
+ sendwallto_group_butone() calls to Debug() calls; reimplement
+ mo_gline() to take into account new syntax
+
+ * ircd/gline.c: change gl_rexpire to gl_lifetime to better reflect
+ its meaning; make sure to set gl_state to GLOCAL_GLOBAL when
+ G-line expires; add lifetime parameter to make_gline(); disable
+ overlapping G-line check; initialize gl_lifetime from lifetime
+ parameter; initialize gl_state to GLOCAL_GLOBAL; update
+ gline_propagate() to send lifetime parameter; add lifetime
+ parameter to gline_add(); remove some old code in gline_add();
+ figure out lifetime to set on new G-line; remove test for
+ make_gline() returning NULL, since it should never do so now; add
+ modify_gline() for modifying global G-lines; add gline_destroy()
+ for destroying local G-lines; update gline_burst() to send
+ lifetime parameter; update gline_resend() to send lifetime
+ parameter; update gline_list() to add lastmod, lifetime, and local
+ status indicators; update gline_stats() to send lastmod, lifetime,
+ and local status indicators; count BADCHANs in
+ gline_memory_count()
+
+ * include/gline.h: add enum GlineLocalState for keeping track of
+ local state changes to global G-lines; store state in struct
+ Gline; document enum GlineAction; add GLINE_EXPIRE,
+ GLINE_LIFETIME, and GLINE_REASON flags to indicate the presence of
+ those fields to gline_modify(); add GLINE_UPDATE mask for checking
+ for the above flags; update GlineIsActive() to take into account
+ new gl_state field in struct Gline; add lifetime to gline_add();
+ add gline_modify() for modifying existing global G-lines, and
+ gline_destroy() for destroying local G-lines
+
+ * doc/readme.gline: update documentation to reflect changes made
+ to G-line command syntax
+
2007-03-17 Michael Poole <mdpoole@troilus.org>
* ircd/umkpasswd.c (parse_arguments): Exit cleanly rather than
-GLINE documentation, last updated on 18 Mar 2000
+GLINE documentation, last updated on 17 Mar 2007
For an ordinary user, the syntax is:
For an operator, the syntax is:
- GLINE [[!][+|-]<mask> [[<target>] <expiration> :<reason>]]
-
-If <mask> is not given, or if it is not prefixed by "+" or "-", the
-operation is exactly the same as if it were issued by an ordinary
-user, except that a list of all G-lines may be returned. If the "+"
-or "-" prefixes are used, the arguments <target>, <expiration>, and
-<reason> must be given, even if the G-line already exists. If
-<target> is "*" and the currently existing G-line is a local G-line,
-the local G-line will be erased and recreated with the parameters
-given, as described below. Otherwise, if the G-line currently exists,
-a prefix of "+" will cause an inactive G-line to be activated, whereas
-a prefix of "-" will cause an active G-line to be deactivated. If an
-attempt is made to modify a G-line set by a U-lined service such as
-Uworld, the change will be forced to be local. If the mask would not
-be permitted due to it being too wide or affecting too many users
-(governed by the GLINEMAXUSERCOUNT feature), the "!" prefix may be
-used to force the G-line to be set anyway.
-
-If the G-line does not already exist, it is created. The <target>
-parameter is used to select whether the G-line is only to apply to a
-single server (which need not be the local server) or to the whole
-network; if <target> is not given, it is assumed to be the local
-server. This could be useful if a single particular link is having
-problems, for instance. The <expiration> parameter is a number of
-seconds, not to exceed 7 days, for the G-line to exist. The <reason>
-argument is mandatory and should describe why this particular G-line
-was placed. The <mask> parameter must be a user@host mask; the host
-component must contain at least 2 non-wildcarded subdomains or, if it
-is an IP address, at least 16 bits. Normally, the host component may
-not contain *any* wildcards, but that can be overridden with the "!"
-prefix, as indicated above, if the operator has the WIDE_GLINE
-privilege.
+ GLINE [[!][+|-|>|<]<mask> [<target>] [<expiration> [:<reason>]]]
+
+There are a total of 10 basic forms of the GLINE command. If no
+arguments are given, all existing G-lines will be listed; if only
+<mask> is given, the behavior is the same as for an ordinary user.
+The remaining forms allow G-lines to be set, manipulated, or possibly
+destroyed.
+
+* Local G-lines.
+
+Opers may set or remove G-lines that only apply to a specific server.
+When the <target> parameter is not given, the specific server will be
+the local server; otherwise, it will be taken to be a remote server,
+and the G-line operations will take place there, if the oper has the
+GLINE privilege. When <mask> is preceded with the '+' character, the
+G-line will be added, and <expiration> and <reason> are required; when
+<mask> is preceded with the '-' character, the G-line will be removed,
+and <expiration> and <reason> are not required. The '<' and '>'
+character prefixes are not valid for local G-lines.
+
+* Local modifications to global G-lines.
+
+Opers may locally activate or deactivate global G-lines. In this
+mode, <mask> is interpreted as referencing an existing G-line, and
+will be preceded by either '<' (to locally deactivate the G-line) or
+'>' (to locally activate the G-line). This local state overrides the
+global state of the G-line, and persists until there is a global state
+change to the G-line, or until the G-line expires. The <expiration>
+and <reason> arguments are not required, but <target> may be given if
+the oper desires to make the appropriate change on a remote
+server--note that the oper will need the GLINE privilege for this.
+
+* Global G-lines.
+
+Opers may, if they have the GLINE privilege, set and manipulate global
+G-lines on the network. To create a new G-line, the oper must prefix
+the <mask> with either '+' (for globally activated G-lines) or '-'
+(for globally deactivated G-lines). Additionally, <target> must be
+given as "*", and the <expiration> and <reason> parameters are
+required. If the G-line already exists, it will be modified to match
+the new global status, <expiration>, and <reason>.
+
+When the G-line already exists, an oper may activate or deactivate it
+simply by setting <target> to "*" and prefixing the <mask> with either
+"+" (to activate the G-line) or "-" (to deactivate it). If it is
+desired to simply modify the expiration time or reason, without
+changing the activation status, specify <mask> without any prefix, set
+<target> to "*", and provide the updated <expire> and optionally an
+updated <reason>.
+
+* Privilege notes.
+
+Note that, for all locally-restricted G-line changes, such as locally
+activating a G-line or creating a local G-line, the oper must have the
+LOCAL_GLINE privilege. For any other G-line change, including
+locally-restricted changes on remote servers, the server's
+CONFIG_OPERCMDS privilege must be enabled and the oper must have the
+GLINE privilege. There are also restrictions to prevent an oper from
+setting a G-line that is too wide; in some cases, those restrictions
+may be overridden by prefixing the <mask> parameter with the "!"
+character, IF the operator has the WIDE_GLINE privilege.
For a server, the syntax is:
- <prefix> GL <target> (+|-)<mask> <expiration> <lastmod> :<reason>
-
-The <target> may be a server numeric or the character "*", for a
-globally scoped G-line. The <mask> argument is a server name, and
-must be prefixed by one of "+" (to indicate an active G-line) or "-"
-(to indicate an inactive G-line). The parameter <expiration> is a
-total number of seconds the G-line is to live for, and <lastmod> is
-used for versioning. Since GLINEs are propagated during netbursts,
-there must be some way of resolving conflicting states, which is the
-reason for this argument, and is also the reason G-lines cannot be
-deleted, only deactivated. The <reason> parameter indicates the
-reason the G-line was placed.
-
-If a GLINE is received with a <target> of "*", any G-lines with local
-scope are deleted, in preference for the globally scoped version. If
-the G-line already exists, the values of <lastmod> are compared; if
-the received <lastmod> is less than the stored <lastmod>, the existing
-G-line is resent to the server from which the GLINE message was
-received; otherwise, the G-line is activated or deactivated, depending
-on the <mask> prefix. If the G-line does not currently exist, it is
-created with the parameters given.
-
-For a U-lined server, this syntax should be used:
-
- <prefix> GL <target> +<mask> <expiration> :<reason>
- <prefix> GL <target> -<mask>
-
-The <lastmod> parameter will be assumed to be 0.
+ <prefix> GL <target> [!][+|-|>|<]<mask> [<expiration>] [<lastmod>]
+ [<lifetime>] [:<reason>]
+
+There are a total of 8 basic forms of the GL command. The primary
+innovation is the addition of the <lifetime> parameter, which
+specifies a lifetime for the G-line record which may be longer than
+the expiration time. <lifetime> will be monotonically increasing,
+enabling <expiration> to be modified in any way desirable.
+
+* Local G-lines.
+
+Remote servers, or opers on them, may remotely set local G-lines on
+the local server. To create a local G-line, <target> will be set to
+the numeric of the local server, and <mask> must be preceded by '+'
+(optionally preceded by '!' if the origin desires to override some
+safety settings). The <expiration> and <reason> parameters are
+required. The <lastmod> and <lifetime> parameters will be ignored if
+present, allowing backwards compatibility with ircu2.10.12.10 and
+prior versions. Removing local G-lines is similar--<mask> must be
+preceded by '-', and all other parameters are ignored to allow
+backwards compatibility.
+
+* Local modifications to global G-lines.
+
+Remote servers, or opers on them, may also locally activate or
+deactivate a global G-line on the local server. The <target> must be
+set to the numeric of the local server, and <mask> must be preceded by
+either '<' (to locally deactivate the G-line) or '>' (to locally
+activate the G-line). This local state overrides the global state of
+the G-line, and persists until there is a global state change to the
+G-line, or until the G-line expires. No other parameters are
+necessary in this mode, and will be ignored if present.
+
+* Global G-lines.
+
+For creation and manipulation of global G-lines, the <target>
+parameter must be set to "*". If the G-line does not exist, and if
+<expiration> is given, the G-line will be created with the specified
+expiration and <reason> (the latter defaulting to "No reason" if not
+present). Otherwise, the G-line will be updated according to the
+available parameters. The rules are similar to those for oper-issued
+global G-lines, with the addition of a <lastmod> parameter, which is a
+monotonically increasing serial number for the G-line, and an optional
+<lifetime> parameter that specifies a monotonically increasing
+lifetime for the G-line record. Note that, for existing G-lines where
+only state changes (global activation or deactivation) are necessary,
+only <lastmod> is required; <expiration> must be specified for all
+other forms of the GL command.
#define GLINE_MAX_EXPIRE 604800 /**< max expire: 7 days */
+/** Local state of a G-line. */
+enum GlineLocalState {
+ GLOCAL_GLOBAL, /**< G-line state unmodified locally. */
+ GLOCAL_ACTIVATED, /**< G-line state locally activated. */
+ GLOCAL_DEACTIVATED /**< G-line state locally deactivated. */
+};
+
/** Description of a G-line. */
struct Gline {
- struct Gline *gl_next; /**< Next G-line in linked list. */
- struct Gline**gl_prev_p; /**< Previous pointer to this G-line. */
- char *gl_user; /**< Username mask (or channel/realname mask). */
- char *gl_host; /**< Host prtion of mask. */
- char *gl_reason; /**< Reason for G-line. */
- time_t gl_expire; /**< Expiration timestamp. */
- time_t gl_lastmod; /**< Last modification timestamp. */
- time_t gl_rexpire; /**< Record expiration timestamp. */
- struct irc_in_addr gl_addr; /**< IP address (for IP-based G-lines). */
- unsigned char gl_bits; /**< Usable bits in gl_addr. */
- unsigned int gl_flags; /**< G-line status flags. */
+ struct Gline *gl_next; /**< Next G-line in linked list. */
+ struct Gline**gl_prev_p; /**< Previous pointer to this G-line. */
+ char *gl_user; /**< Username mask (or channel/realname mask). */
+ char *gl_host; /**< Host prtion of mask. */
+ char *gl_reason; /**< Reason for G-line. */
+ time_t gl_expire; /**< Expiration timestamp. */
+ time_t gl_lastmod; /**< Last modification timestamp. */
+ time_t gl_lifetime; /**< Record expiration timestamp. */
+ struct irc_in_addr gl_addr; /**< IP address (for IP-based G-lines). */
+ unsigned char gl_bits; /**< Usable bits in gl_addr. */
+ unsigned int gl_flags; /**< G-line status flags. */
+ enum GlineLocalState gl_state;/**< G-line local state. */
};
+/** Action to perform on a G-line. */
enum GlineAction {
- GLINE_ACTIVATE,
- GLINE_DEACTIVATE,
- GLINE_LOCAL_ACTIVATE,
- GLINE_LOCAL_DEACTIVATE,
- GLINE_MODIFY
+ GLINE_ACTIVATE, /**< G-line should be activated. */
+ GLINE_DEACTIVATE, /**< G-line should be deactivated. */
+ GLINE_LOCAL_ACTIVATE, /**< G-line should be locally activated. */
+ GLINE_LOCAL_DEACTIVATE, /**< G-line should be locally deactivated. */
+ GLINE_MODIFY /**< G-line should be modified. */
};
#define GLINE_ACTIVE 0x0001 /**< G-line is active. */
#define GLINE_OPERFORCE 0x0400 /**< Oper forcing G-line to be set. */
#define GLINE_REALNAME 0x0800 /**< G-line matches only the realname field. */
+#define GLINE_EXPIRE 0x1000 /**< Expiration time update */
+#define GLINE_LIFETIME 0x2000 /**< Record lifetime update */
+#define GLINE_REASON 0x4000 /**< Reason update */
+
/** Controllable flags that can be set on an actual G-line. */
#define GLINE_MASK (GLINE_ACTIVE | GLINE_BADCHAN | GLINE_LOCAL | GLINE_REALNAME)
/** Mask for G-line activity flags. */
#define GLINE_ACTMASK (GLINE_ACTIVE | GLINE_LDEACT)
+/** Mask for G-line update flags. */
+#define GLINE_UPDATE (GLINE_EXPIRE | GLINE_LIFETIME | GLINE_REASON)
+
/** Test whether \a g is active. */
-#define GlineIsActive(g) (((g)->gl_flags & GLINE_ACTMASK) == \
- GLINE_ACTIVE)
+#define GlineIsActive(g) ((((g)->gl_flags & GLINE_ACTIVE) && \
+ (g)->gl_state != GLOCAL_DEACTIVATED) || \
+ (g)->gl_state == GLOCAL_ACTIVATED)
/** Test whether \a g is remotely (globally) active. */
#define GlineIsRemActive(g) ((g)->gl_flags & GLINE_ACTIVE)
/** Test whether \a g is an IP-based G-line. */
struct Gline *gline);
extern int gline_add(struct Client *cptr, struct Client *sptr, char *userhost,
char *reason, time_t expire, time_t lastmod,
- unsigned int flags);
+ time_t lifetime, unsigned int flags);
extern int gline_activate(struct Client *cptr, struct Client *sptr,
struct Gline *gline, time_t lastmod,
unsigned int flags);
extern int gline_deactivate(struct Client *cptr, struct Client *sptr,
struct Gline *gline, time_t lastmod,
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,
+ time_t lifetime, unsigned int flags);
+extern int gline_destroy(struct Client *cptr, struct Client *sptr,
+ struct Gline *gline);
extern struct Gline *gline_find(char *userhost, unsigned int flags);
extern struct Gline *gline_lookup(struct Client *cptr, unsigned int flags);
extern void gline_free(struct Gline *gline);
#include "s_stats.h"
#include "send.h"
#include "struct.h"
+#include "sys.h"
#include "msg.h"
#include "numnicks.h"
#include "numeric.h"
/* Figure out the next pointer in list... */ \
if ((((next) = (gl)->gl_next) || 1) && \
/* Then see if it's expired */ \
- (gl)->gl_rexpire <= CurrentTime) \
+ (gl)->gl_lifetime <= CurrentTime) \
/* Record has expired, so free the G-line */ \
gline_free((gl)); \
/* See if we need to expire the G-line */ \
else if (((gl)->gl_expire > CurrentTime || \
- ((gl)->gl_flags &= ~GLINE_ACTIVE)) && 0) \
+ ((gl)->gl_flags &= ~GLINE_ACTIVE) || \
+ ((gl)->gl_state = GLOCAL_GLOBAL)) && 0) \
; /* empty statement */ \
else
*/
static struct Gline *
make_gline(char *user, char *host, char *reason, time_t expire, time_t lastmod,
- unsigned int flags)
+ time_t lifetime, unsigned int flags)
{
- struct Gline *gline, *sgline, *after = 0;
-
- if (!(flags & GLINE_BADCHAN)) { /* search for overlapping glines first */
-
- for (gline = GlobalGlineList; gline; gline = sgline) {
- sgline = gline->gl_next;
-
- if (gline->gl_expire <= CurrentTime)
- gline_free(gline);
- else if (((gline->gl_flags & GLINE_LOCAL) != (flags & GLINE_LOCAL)) ||
- (gline->gl_host && !host) || (!gline->gl_host && host))
- continue;
- else if (!mmatch(gline->gl_user, user) /* gline contains new mask */
- && (gline->gl_host == NULL || !mmatch(gline->gl_host, host))) {
- if (expire <= gline->gl_expire) /* will expire before wider gline */
- return 0;
- else
- after = gline; /* stick new gline after this one */
- } else if (!mmatch(user, gline->gl_user) /* new mask contains gline */
- && (gline->gl_host==NULL || !mmatch(host, gline->gl_host))
- && gline->gl_expire <= expire) /* old expires before new */
- gline_free(gline); /* save some memory */
- }
- }
+ struct Gline *gline, *after = 0;
+
+ /* Disable checking for overlapping G-lines. The problem is that we
+ * may have a wide-mask, long lifetime G-line that we've
+ * deactivated--maybe it was a mistake?--and someone comes along and
+ * wants to set a narrower overlapping G-line with a shorter
+ * lifetime. If we were to leave this logic enabled, there would be
+ * no way to set that narrower G-line.
+ */
+/* if (!(flags & GLINE_BADCHAN)) { /\* search for overlapping glines first *\/ */
+
+/* for (gline = GlobalGlineList; gline; gline = sgline) { */
+/* sgline = gline->gl_next; */
+
+/* if (gline->gl_expire <= CurrentTime) */
+/* gline_free(gline); */
+/* else if (((gline->gl_flags & GLINE_LOCAL) != (flags & GLINE_LOCAL)) || */
+/* (gline->gl_host && !host) || (!gline->gl_host && host)) */
+/* continue; */
+/* else if (!mmatch(gline->gl_user, user) /\* gline contains new mask *\/ */
+/* && (gline->gl_host == NULL || !mmatch(gline->gl_host, host))) { */
+/* if (expire <= gline->gl_expire) /\* will expire before wider gline *\/ */
+/* return 0; */
+/* else */
+/* after = gline; /\* stick new gline after this one *\/ */
+/* } else if (!mmatch(user, gline->gl_user) /\* new mask contains gline *\/ */
+/* && (gline->gl_host==NULL || !mmatch(host, gline->gl_host)) */
+/* && gline->gl_expire <= expire) /\* old expires before new *\/ */
+/* gline_free(gline); /\* save some memory *\/ */
+/* } */
+/* } */
gline = (struct Gline *)MyMalloc(sizeof(struct Gline)); /* alloc memory */
assert(0 != gline);
DupString(gline->gl_reason, reason); /* initialize gline... */
gline->gl_expire = expire;
- gline->gl_rexpire = expire;
+ gline->gl_lifetime = lifetime;
gline->gl_lastmod = lastmod;
gline->gl_flags = flags & GLINE_MASK;
+ gline->gl_state = GLOCAL_GLOBAL; /* not locally modified */
if (flags & GLINE_BADCHAN) { /* set a BADCHAN gline */
DupString(gline->gl_user, user); /* first, remember channel */
int
gline_propagate(struct Client *cptr, struct Client *sptr, struct Gline *gline)
{
- if (GlineIsLocal(gline) || (IsUser(sptr) && !gline->gl_lastmod))
+ if (GlineIsLocal(gline))
return 0;
- if (gline->gl_lastmod)
- sendcmdto_serv_butone(sptr, CMD_GLINE, cptr, "* %c%s%s%s %Tu %Tu :%s",
- GlineIsRemActive(gline) ? '+' : '-', gline->gl_user,
- gline->gl_host ? "@" : "",
- gline->gl_host ? gline->gl_host : "",
- gline->gl_expire - CurrentTime, gline->gl_lastmod,
- gline->gl_reason);
- else
- sendcmdto_serv_butone(sptr, CMD_GLINE, cptr,
- (GlineIsRemActive(gline) ?
- "* +%s%s%s %Tu :%s" : "* -%s%s%s"),
- gline->gl_user,
- gline->gl_host ? "@" : "",
- gline->gl_host ? gline->gl_host : "",
- gline->gl_expire - CurrentTime, gline->gl_reason);
+ assert(gline->gl_lastmod);
+
+ sendcmdto_serv_butone(sptr, CMD_GLINE, cptr, "* %c%s%s%s %Tu %Tu %Tu :%s",
+ GlineIsRemActive(gline) ? '+' : '-', gline->gl_user,
+ gline->gl_host ? "@" : "",
+ gline->gl_host ? gline->gl_host : "",
+ gline->gl_expire - CurrentTime, gline->gl_lastmod,
+ gline->gl_lifetime - CurrentTime, gline->gl_reason);
return 0;
}
* @param[in] reason Reason for G-line.
* @param[in] expire Duration of G-line in seconds.
* @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 or CPTR_KILLED, depending on whether \a sptr is suicidal.
*/
int
gline_add(struct Client *cptr, struct Client *sptr, char *userhost,
- char *reason, time_t expire, time_t lastmod, unsigned int flags)
+ char *reason, time_t expire, time_t lastmod, time_t lifetime,
+ unsigned int flags)
{
struct Gline *agline;
char uhmask[USERLEN + HOSTLEN + 2];
assert(0 != userhost);
assert(0 != reason);
+ Debug((DEBUG_DEBUG, "gline_add(\"%s\", \"%s\", \"%s\", \"%s\", %Tu, %Tu "
+ "%Tu, 0x%04x)", cli_name(cptr), cli_name(sptr), userhost, reason,
+ expire, lastmod, lifetime, flags));
+
if (*userhost == '#' || *userhost == '&') {
if ((flags & GLINE_LOCAL) && !HasPriv(sptr, PRIV_LOCAL_BADCHAN))
return send_reply(sptr, ERR_NOPRIVILEGES);
user = userhost;
host = 0;
} else if (*userhost == '$') {
- switch (*userhost == '$' ? userhost[1] : userhost[3]) {
+ switch (userhost[1]) {
case 'R': flags |= GLINE_REALNAME; break;
default:
/* uh, what to do here? */
}
break;
}
- user = (*userhost =='$' ? userhost : userhost+2);
+ user = userhost;
host = 0;
} else {
canon_userhost(userhost, &user, &host, "*");
expire += CurrentTime; /* convert from lifetime to timestamp */
+ if (!lifetime) /* no lifetime set, use expiration time */
+ lifetime = expire;
+ else /* convert lifetime into timestamp */
+ lifetime += CurrentTime;
+
/* Inform ops... */
sendto_opmask_butone(0, ircd_strncmp(reason, "AUTO", 4) ? SNO_GLINE :
SNO_AUTO, "%s adding %s %s for %s%s%s, expiring at "
expire + TSoffset, reason);
/* make the gline */
- agline = make_gline(user, host, reason, expire, lastmod, flags);
+ agline = make_gline(user, host, reason, expire, lastmod, lifetime, flags);
- if (!agline) /* if it overlapped, silently return */
- return 0;
+ /* since we've disabled overlapped G-line checking, agline should
+ * never be NULL...
+ */
+ assert(agline);
+/* if (!agline) /\* if it overlapped, silently return *\/ */
+/* return 0; */
gline_propagate(cptr, sptr, agline);
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.
+ * @param[in] gline G-line being modified.
+ * @param[in] action Resultant status of the G-line.
+ * @param[in] reason Reason for G-line.
+ * @param[in] expire Duration of G-line in seconds.
+ * @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 or CPTR_KILLED, depending on whether \a sptr is suicidal.
+ */
+int
+gline_modify(struct Client *cptr, struct Client *sptr, struct Gline *gline,
+ enum GlineAction action, char *reason, time_t expire,
+ time_t lastmod, time_t lifetime, unsigned int flags)
+{
+ char buf[BUFSIZE], *op = "";
+ int pos = 0;
+
+ assert(gline);
+ assert(!GlineIsLocal(gline));
+
+ Debug((DEBUG_DEBUG, "gline_modify(\"%s\", \"%s\", \"%s%s%s\", %s, \"%s\", "
+ "%Tu, %Tu, %Tu, 0x%04x)", cli_name(cptr), cli_name(sptr),
+ gline->gl_user, gline->gl_host ? "@" : "",
+ gline->gl_host ? gline->gl_host : "",
+ action == GLINE_ACTIVATE ? "GLINE_ACTIVATE" :
+ (action == GLINE_DEACTIVATE ? "GLINE_DEACTIVATE" :
+ (action == GLINE_LOCAL_ACTIVATE ? "GLINE_LOCAL_ACTIVATE" :
+ (action == GLINE_LOCAL_DEACTIVATE ? "GLINE_LOCAL_DEACTIVATE" :
+ (action == GLINE_MODIFY ? "GLINE_MODIFY" : "<UNKNOWN>")))),
+ reason, expire, lastmod, lifetime, flags));
+
+ /* First, let's check lastmod... */
+ if (action != GLINE_LOCAL_ACTIVATE && action != GLINE_LOCAL_DEACTIVATE) {
+ if (GlineLastMod(gline) > lastmod) { /* we have a more recent version */
+ if (IsBurstOrBurstAck(cptr))
+ return 0; /* middle of a burst, it'll resync on its own */
+ return gline_resend(cptr, gline); /* resync the server */
+ } else if (GlineLastMod(gline) == lastmod)
+ return 0; /* we have that version of the G-line... */
+ }
+
+ /* All right, we know that there's a change of some sort. What is it? */
+ /* first, check out the expiration time... */
+ if ((flags & GLINE_EXPIRE) && expire) {
+ if (!(flags & GLINE_FORCE) && (expire <= 0 || expire > GLINE_MAX_EXPIRE)) {
+ if (!IsServer(sptr) && MyConnect(sptr)) /* bad expiration time */
+ send_reply(sptr, ERR_BADEXPIRE, expire);
+ return 0;
+ }
+
+ /* convert to a timestamp... */
+ expire += CurrentTime;
+ } else
+ flags &= ~GLINE_EXPIRE;
+
+ /* Now check to see if there's any change... */
+ if ((flags & GLINE_EXPIRE) && expire == gline->gl_expire) {
+ flags &= ~GLINE_EXPIRE; /* no change to expiration time... */
+ expire = 0;
+ }
+
+ /* Next, check out lifetime--this one's a bit trickier... */
+ if ((flags & GLINE_LIFETIME) && lifetime)
+ lifetime += CurrentTime; /* convert to a timestamp */
+ else
+ lifetime = gline->gl_lifetime; /* use G-line lifetime */
+
+ lifetime = IRCD_MAX(lifetime, expire); /* set lifetime to the max */
+
+ /* OK, let's see which is greater... */
+ if (lifetime > gline->gl_lifetime)
+ flags |= GLINE_LIFETIME; /* have to update lifetime */
+ else {
+ flags &= ~GLINE_LIFETIME; /* no change to lifetime */
+ lifetime = 0;
+ }
+
+ /* Finally, let's see if the reason needs to be updated */
+ if ((flags & GLINE_REASON) && reason &&
+ !ircd_strcmp(gline->gl_reason, reason))
+ flags &= ~GLINE_REASON; /* no changes to the reason */
+
+ /* OK, now let's take a look at the action... */
+ if ((action == GLINE_ACTIVATE && (gline->gl_flags & GLINE_ACTIVE)) ||
+ (action == GLINE_DEACTIVATE && !(gline->gl_flags & GLINE_ACTIVE)) ||
+ (action == GLINE_LOCAL_ACTIVATE &&
+ (gline->gl_state == GLOCAL_ACTIVATED)) ||
+ (action == GLINE_LOCAL_DEACTIVATE &&
+ (gline->gl_state == GLOCAL_DEACTIVATED)) ||
+ /* can't activate an expired G-line */
+ IRCD_MAX(gline->gl_expire, expire) <= CurrentTime)
+ action = GLINE_MODIFY; /* no activity state modifications */
+
+ Debug((DEBUG_DEBUG, "About to perform changes; flags 0x%04x, action %s",
+ flags, action == GLINE_ACTIVATE ? "GLINE_ACTIVATE" :
+ (action == GLINE_DEACTIVATE ? "GLINE_DEACTIVATE" :
+ (action == GLINE_LOCAL_ACTIVATE ? "GLINE_LOCAL_ACTIVATE" :
+ (action == GLINE_LOCAL_DEACTIVATE ? "GLINE_LOCAL_DEACTIVATE" :
+ (action == GLINE_MODIFY ? "GLINE_MODIFY" : "<UNKNOWN>"))))));
+
+ /* If there are no changes to perform, do no changes */
+ if (!(flags & GLINE_UPDATE) && action == GLINE_MODIFY)
+ return 0;
+
+ /* Now we know what needs to be changed, so let's process the changes... */
+
+ /* Start by updating lastmod, if indicated... */
+ if (action != GLINE_LOCAL_ACTIVATE && action != GLINE_LOCAL_DEACTIVATE)
+ gline->gl_lastmod = lastmod;
+
+ /* Then move on to activity status changes... */
+ switch (action) {
+ case GLINE_ACTIVATE: /* Globally activating G-line */
+ gline->gl_flags |= GLINE_ACTIVE; /* make it active... */
+ gline->gl_state = GLOCAL_GLOBAL; /* reset local activity state */
+ pos += ircd_snprintf(0, buf, sizeof(buf), " globally activating G-line");
+ op = "+"; /* operation for G-line propagation */
+ break;
+
+ case GLINE_DEACTIVATE: /* Globally deactivating G-line */
+ gline->gl_flags &= ~GLINE_ACTIVE; /* make it inactive... */
+ gline->gl_state = GLOCAL_GLOBAL; /* reset local activity state */
+ pos += ircd_snprintf(0, buf, sizeof(buf), " globally deactivating G-line");
+ op = "-"; /* operation for G-line propagation */
+ break;
+
+ case GLINE_LOCAL_ACTIVATE: /* Locally activating G-line */
+ gline->gl_state = GLOCAL_ACTIVATED; /* make it locally active */
+ pos += ircd_snprintf(0, buf, sizeof(buf), " locally activating G-line");
+ break;
+
+ case GLINE_LOCAL_DEACTIVATE: /* Locally deactivating G-line */
+ gline->gl_state = GLOCAL_DEACTIVATED; /* make it locally inactive */
+ pos += ircd_snprintf(0, buf, sizeof(buf), " locally deactivating G-line");
+ break;
+
+ case GLINE_MODIFY: /* no change to activity status */
+ break;
+ }
+
+ /* Handle expiration changes... */
+ if (flags & GLINE_EXPIRE) {
+ gline->gl_expire = expire; /* save new expiration time */
+ if (pos < BUFSIZE)
+ pos += ircd_snprintf(0, buf + pos, sizeof(buf) - pos,
+ "%s%s changing expiration time to %Tu",
+ pos ? ";" : "",
+ pos && !(flags & (GLINE_LIFETIME | GLINE_REASON)) ?
+ " and" : "", expire);
+ }
+
+ /* Next, handle lifetime changes... */
+ if (flags & GLINE_LIFETIME) {
+ gline->gl_lifetime = lifetime; /* save new lifetime */
+ if (pos < BUFSIZE)
+ pos += ircd_snprintf(0, buf + pos, sizeof(buf) - pos,
+ "%s%s extending record lifetime to %Tu",
+ pos ? ";" : "", pos && !(flags & GLINE_REASON) ?
+ " and" : "", lifetime);
+ }
+
+ /* Now, handle reason changes... */
+ if (flags & GLINE_REASON) {
+ MyFree(gline->gl_reason); /* release old reason */
+ DupString(gline->gl_reason, reason); /* store new reason */
+ if (pos < BUFSIZE)
+ pos += ircd_snprintf(0, buf + pos, sizeof(buf) - pos,
+ "%s%s changing reason to \"%s\"",
+ pos ? ";" : "", pos ? " and" : "", reason);
+ }
+
+ /* All right, inform ops... */
+ sendto_opmask_butone(0, SNO_GLINE, "%s modifying global %s for %s%s%s:%s",
+ (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
+ cli_name(sptr) : cli_name((cli_user(sptr))->server),
+ GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
+ gline->gl_user, gline->gl_host ? "@" : "",
+ gline->gl_host ? gline->gl_host : "", buf);
+
+ /* and log the change */
+ log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE,
+ "%#C modifying global %s for %s%s%s:%s", sptr,
+ GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", gline->gl_user,
+ gline->gl_host ? "@" : "", gline->gl_host ? gline->gl_host : "",
+ buf);
+
+ /* We'll be simple for this release, but we can update this to change
+ * the propagation syntax on future updates
+ */
+ if (action != GLINE_LOCAL_ACTIVATE && action != GLINE_LOCAL_DEACTIVATE)
+ sendcmdto_serv_butone(sptr, CMD_GLINE, cptr,
+ "* %s%s%s%s%s %Tu %Tu %Tu :%s",
+ flags & GLINE_OPERFORCE ? "!" : "", op,
+ gline->gl_user, gline->gl_host ? "@" : "",
+ gline->gl_host ? gline->gl_host : "",
+ gline->gl_expire - CurrentTime, gline->gl_lastmod,
+ gline->gl_lifetime - CurrentTime, gline->gl_reason);
+
+ /* OK, let's do the G-line... */
+ return do_gline(cptr, sptr, gline);
+}
+
+/** Destroy a local G-line.
+ * @param[in] cptr Peer that gave us the message.
+ * @param[in] sptr Client that initiated the destruction.
+ * @param[in] gline G-line to destroy.
+ * @return Zero.
+ */
+int
+gline_destroy(struct Client *cptr, struct Client *sptr, struct Gline *gline)
+{
+ assert(gline);
+ assert(GlineIsLocal(gline));
+
+ /* Inform ops and log it */
+ sendto_opmask_butone(0, SNO_GLINE, "%s removing local %s for %s%s%s",
+ (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
+ cli_name(sptr) : cli_name((cli_user(sptr))->server),
+ GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
+ gline->gl_user, gline->gl_host ? "@" : "",
+ gline->gl_host ? gline->gl_host : "");
+ log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE,
+ "%#C removing local %s for %s%s%s", sptr,
+ GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", gline->gl_user,
+ gline->gl_host ? "@" : "", gline->gl_host ? gline->gl_host : "");
+
+ gline_free(gline); /* get rid of the G-line */
+
+ return 0; /* convenience return */
+}
+
/** Find a G-line for a particular mask, guided by certain flags.
* Certain bits in \a flags are interpreted specially:
* <dl>
gliter(GlobalGlineList, gline, sgline) {
if (!GlineIsLocal(gline) && gline->gl_lastmod)
- sendcmdto_one(&me, CMD_GLINE, cptr, "* %c%s%s%s %Tu %Tu :%s",
+ sendcmdto_one(&me, CMD_GLINE, cptr, "* %c%s%s%s %Tu %Tu %Tu :%s",
GlineIsRemActive(gline) ? '+' : '-', gline->gl_user,
gline->gl_host ? "@" : "",
gline->gl_host ? gline->gl_host : "",
gline->gl_expire - CurrentTime, gline->gl_lastmod,
- gline->gl_reason);
+ gline->gl_lifetime - CurrentTime, gline->gl_reason);
}
gliter(BadChanGlineList, gline, sgline) {
if (!GlineIsLocal(gline) && gline->gl_lastmod)
- sendcmdto_one(&me, CMD_GLINE, cptr, "* %c%s %Tu %Tu :%s",
+ sendcmdto_one(&me, CMD_GLINE, cptr, "* %c%s %Tu %Tu %Tu :%s",
GlineIsRemActive(gline) ? '+' : '-', gline->gl_user,
gline->gl_expire - CurrentTime, gline->gl_lastmod,
- gline->gl_reason);
+ gline->gl_lifetime - CurrentTime, gline->gl_reason);
}
}
if (GlineIsLocal(gline) || !gline->gl_lastmod)
return 0;
- sendcmdto_one(&me, CMD_GLINE, cptr, "* %c%s%s%s %Tu %Tu :%s",
+ sendcmdto_one(&me, CMD_GLINE, cptr, "* %c%s%s%s %Tu %Tu %Tu :%s",
GlineIsRemActive(gline) ? '+' : '-', gline->gl_user,
gline->gl_host ? "@" : "",
gline->gl_host ? gline->gl_host : "",
gline->gl_expire - CurrentTime, gline->gl_lastmod,
- gline->gl_reason);
+ gline->gl_lifetime - CurrentTime, gline->gl_reason);
return 0;
}
send_reply(sptr, RPL_GLIST, gline->gl_user,
gline->gl_host ? "@" : "",
gline->gl_host ? gline->gl_host : "",
- gline->gl_expire + TSoffset,
+ gline->gl_expire + TSoffset, gline->gl_lastmod,
+ gline->gl_lifetime + TSoffset,
GlineIsLocal(gline) ? cli_name(&me) : "*",
- GlineIsActive(gline) ? '+' : '-', gline->gl_reason);
+ gline->gl_state == GLOCAL_ACTIVATED ? ">" :
+ (gline->gl_state == GLOCAL_DEACTIVATED ? "<" : ""),
+ GlineIsRemActive(gline) ? '+' : '-', gline->gl_reason);
} else {
gliter(GlobalGlineList, gline, sgline) {
send_reply(sptr, RPL_GLIST, gline->gl_user,
gline->gl_host ? "@" : "",
gline->gl_host ? gline->gl_host : "",
- gline->gl_expire + TSoffset,
+ gline->gl_expire + TSoffset, gline->gl_lastmod,
+ gline->gl_lifetime + TSoffset,
GlineIsLocal(gline) ? cli_name(&me) : "*",
- GlineIsActive(gline) ? '+' : '-', gline->gl_reason);
+ gline->gl_state == GLOCAL_ACTIVATED ? ">" :
+ (gline->gl_state == GLOCAL_DEACTIVATED ? "<" : ""),
+ GlineIsRemActive(gline) ? '+' : '-', gline->gl_reason);
}
gliter(BadChanGlineList, gline, sgline) {
send_reply(sptr, RPL_GLIST, gline->gl_user, "", "",
- gline->gl_expire + TSoffset,
+ gline->gl_expire + TSoffset, gline->gl_lastmod,
+ gline->gl_lifetime + TSoffset,
GlineIsLocal(gline) ? cli_name(&me) : "*",
- GlineIsActive(gline) ? '+' : '-', gline->gl_reason);
+ gline->gl_state == GLOCAL_ACTIVATED ? ">" :
+ (gline->gl_state == GLOCAL_DEACTIVATED ? "<" : ""),
+ GlineIsRemActive(gline) ? '+' : '-', gline->gl_reason);
}
}
send_reply(sptr, RPL_STATSGLINE, 'G', gline->gl_user,
gline->gl_host ? "@" : "",
gline->gl_host ? gline->gl_host : "",
- gline->gl_expire + TSoffset,
- GlineIsActive(gline) ? '+' : '-',
+ gline->gl_expire + TSoffset, gline->gl_lastmod,
+ gline->gl_lifetime + TSoffset,
+ gline->gl_state == GLOCAL_ACTIVATED ? ">" :
+ (gline->gl_state == GLOCAL_DEACTIVATED ? "<" : ""),
+ GlineIsRemActive(gline) ? '+' : '-',
gline->gl_reason);
}
}
struct Gline *gline;
unsigned int gl = 0;
- for (gline = GlobalGlineList; gline; gline = gline->gl_next)
- {
+ for (gline = GlobalGlineList; gline; gline = gline->gl_next) {
+ gl++;
+ *gl_size += sizeof(struct Gline);
+ *gl_size += gline->gl_user ? (strlen(gline->gl_user) + 1) : 0;
+ *gl_size += gline->gl_host ? (strlen(gline->gl_host) + 1) : 0;
+ *gl_size += gline->gl_reason ? (strlen(gline->gl_reason) + 1) : 0;
+ }
+
+ for (gline = BadChanGlineList; gline; gline = gline->gl_next) {
gl++;
*gl_size += sizeof(struct Gline);
*gl_size += gline->gl_user ? (strlen(gline->gl_user) + 1) : 0;
*gl_size += gline->gl_host ? (strlen(gline->gl_host) + 1) : 0;
*gl_size += gline->gl_reason ? (strlen(gline->gl_reason) + 1) : 0;
}
+
return gl;
}
#include "numeric.h"
#include "numnicks.h"
#include "s_conf.h"
+#include "s_debug.h"
#include "s_misc.h"
#include "send.h"
* parv[0] = Sender prefix
* parv[1] = Target: server numeric
* parv[2] = (+|-)<G-line mask>
- * parv[3] = G-line lifetime
- *
- * From Uworld:
- *
- * parv[4] = Comment
- *
- * From somewhere else:
- *
- * parv[4] = Last modification time
- * parv[5] = Comment
*
+ * For other parameters, see doc/readme.gline.
*/
int
ms_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
if (parc < 3)
return need_more_params(sptr, "GLINE");
+ if (IsServer(sptr))
+ flags |= GLINE_FORCE;
+
if (*mask == '!') {
mask++;
flags |= GLINE_OPERFORCE; /* assume oper had WIDE_GLINE */
- } else if (IsServer(sptr))
- flags |= GLINE_FORCE;
+ }
switch (*mask) { /* handle +, -, <, and > */
case '+': /* activate the G-line */
if (action == GLINE_LOCAL_ACTIVATE || action == GLINE_LOCAL_DEACTIVATE ||
(target[0] == '*' && target[1] == '\0'))
flags |= GLINE_GLOBAL;
- else {
- if (!(acptr = FindNServer(target)))
- return 0; /* no such server, jump out */
-
+ else
flags |= GLINE_LOCAL;
+
+ /* now figure out if we need to resolve a server */
+ if ((action == GLINE_LOCAL_ACTIVATE || action == GLINE_LOCAL_DEACTIVATE ||
+ (flags & GLINE_LOCAL)) && !(acptr = FindNServer(target)))
+ return 0; /* no such server, jump out */
+
+ /* If it's a local activate/deactivate and server isn't me, propagate it */
+ if ((action == GLINE_LOCAL_ACTIVATE || action == GLINE_LOCAL_DEACTIVATE) &&
+ !IsMe(acptr)) {
+ Debug((DEBUG_DEBUG, "I am forwarding a local change to a global gline "
+ "to a remote server; target %s, mask %s, operforce %s, action %s",
+ target, mask, flags & GLINE_OPERFORCE ? "YES" : "NO",
+ action == GLINE_LOCAL_ACTIVATE ? ">" : "<"));
+
+ sendcmdto_one(sptr, CMD_GLINE, acptr, "%C %s%c%s", acptr,
+ flags & GLINE_OPERFORCE ? "!" : "",
+ action == GLINE_LOCAL_ACTIVATE ? ">" : "<", mask);
+
+ return 0; /* all done */
}
/* Next, try to find the G-line... */
if ((flags & GLINE_GLOBAL) || IsMe(acptr)) /* don't bother if it's not me! */
agline = gline_find(mask, flags | GLINE_ANY | GLINE_EXACT);
- /* We now have all the pieces to tell us what we've got; let's
- * put it all together and convert the rest of the arguments.
+ /* We now have all the pieces to tell us what we've got; let's put
+ * it all together and convert the rest of the arguments.
*/
/* Handle the local G-lines first... */
reason = parv[parc - 1]; /* and reason */
if (IsMe(acptr)) {
- /* XXX and create the local G-line */
- sendwallto_group_butone(&me, WALL_DESYNCH, NULL,
- "I would create a local G-line here; target "
- "%s, mask %s, operforce %s, action %s, "
- "expire %Tu, reason: %s", target, mask,
- flags & GLINE_OPERFORCE ? "YES" : "NO",
- action == GLINE_ACTIVATE ? "+" : "-",
- expire_off, reason);
+ if (agline) /* G-line already exists, so let's ignore it... */
+ return 0;
+
+ /* OK, create the local G-line */
+ Debug((DEBUG_DEBUG, "I am creating a local G-line here; target %s, "
+ "mask %s, operforce %s, action %s, expire %Tu, reason: %s",
+ target, mask, flags & GLINE_OPERFORCE ? "YES" : "NO",
+ action == GLINE_ACTIVATE ? "+" : "-", expire_off, reason));
+
+ return gline_add(cptr, sptr, mask, reason, expire_off, lastmod,
+ lifetime, flags | GLINE_ACTIVE);
}
} else if (IsMe(acptr)) { /* destroying a local G-line */
- /* XXX destroy the G-line */;
- sendwallto_group_butone(&me, WALL_DESYNCH, NULL,
- "I would destroy a local G-line here; target "
- "%s, mask %s, operforce %s, action %s", target,
- mask, flags & GLINE_OPERFORCE ? "YES" : "NO",
- action == GLINE_ACTIVATE ? "+" : "-");
+ if (!agline) /* G-line doesn't exist, so let's complain... */
+ return send_reply(sptr, ERR_NOSUCHGLINE, mask);
+
+ /* Let's now destroy the G-line */;
+ Debug((DEBUG_DEBUG, "I am destroying a local G-line here; target %s, "
+ "mask %s, operforce %s, action %s", target, mask,
+ flags & GLINE_OPERFORCE ? "YES" : "NO",
+ action == GLINE_ACTIVATE ? "+" : "-"));
+
+ return gline_destroy(cptr, sptr, agline);
}
/* OK, we've converted arguments; if it's not for us, forward */
* <lastmod> for GLINE_ACTIVATE and to omit <expire>, <lastmod>,
* and <reason> for GLINE_DEACTIVATE.
*/
- if (!IsMe(acptr)) {
- sendwallto_group_butone(&me, WALL_DESYNCH, NULL,
- "I am forwarding a local G-line to a remote "
- "server; target %s, mask %s, operforce %s, "
- "action %s, expire %Tu, lastmod %Tu, reason: %s",
- target, mask,
- flags & GLINE_OPERFORCE ? "YES" : "NO",
- action == GLINE_ACTIVATE ? "+" : "-",
- expire_off, CurrentTime, reason);
- sendcmdto_one(sptr, CMD_GLINE, acptr, "%C %s%c%s %Tu %Tu :%s",
- acptr, flags & GLINE_OPERFORCE ? "!" : "",
- action == GLINE_ACTIVATE ? '+' : '-', mask, expire_off,
- CurrentTime, reason);
- }
+ assert(!IsMe(acptr));
+
+ Debug((DEBUG_DEBUG, "I am forwarding a local G-line to a remote server; "
+ "target %s, mask %s, operforce %s, action %s, expire %Tu, "
+ "lastmod %Tu, reason: %s", target, mask,
+ flags & GLINE_OPERFORCE ? "YES" : "NO",
+ action == GLINE_ACTIVATE ? "+" : "-", expire_off, CurrentTime,
+ reason));
+
+ sendcmdto_one(sptr, CMD_GLINE, acptr, "%C %s%c%s %Tu %Tu :%s",
+ acptr, flags & GLINE_OPERFORCE ? "!" : "",
+ action == GLINE_ACTIVATE ? '+' : '-', mask, expire_off,
+ CurrentTime, reason);
return 0; /* all done */
}
switch (action) {
case GLINE_LOCAL_ACTIVATE: /* locally activating a G-line */
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);
break; /* no additional parameters to manipulate */
case GLINE_ACTIVATE: /* activating a G-line */
expire_off = atoi(parv[3]); /* convert expiration and lastmod */
lastmod = atoi(parv[4]);
+ flags |= GLINE_EXPIRE; /* we have an expiration time update */
+
if (parc > 6) { /* no question, have a lifetime and reason */
lifetime = atoi(parv[5]);
reason = parv[parc - 1];
+
+ flags |= GLINE_LIFETIME | GLINE_REASON;
} else if (parc == 6) { /* either a lifetime or a reason */
if (!agline || /* gline creation, has to be the reason */
/* trial-convert as lifetime, and if it doesn't fully convert,
((lifetime = strtoul(parv[5], &tmp, 10)) && !*tmp)) {
lifetime = 0;
reason = parv[5];
- }
+
+ flags |= GLINE_REASON; /* have a reason update */
+ } else if (lifetime)
+ flags |= GLINE_LIFETIME; /* have a lifetime update */
}
}
}
- sendwallto_group_butone(&me, WALL_DESYNCH, NULL,
- "I have a global G-line I would act upon now; "
- "target %s, mask %s, operforce %s, action %s, "
- "expire %Tu, lastmod %Tu, lifetime %Tu, "
- "reason: %s; gline %s!",
- target, mask, flags & GLINE_OPERFORCE ? "YES" : "NO",
- action == GLINE_ACTIVATE ? "+" :
- (action == GLINE_DEACTIVATE ? "-" :
- (action == GLINE_LOCAL_ACTIVATE ? ">" :
- (action == GLINE_LOCAL_DEACTIVATE ? "<" :
- "(MODIFY)"))), expire_off, lastmod, lifetime,
- reason, agline ? "EXISTS" : "does not exist");
+ Debug((DEBUG_DEBUG, "I have a global G-line I am acting upon now; "
+ "target %s, mask %s, operforce %s, action %s, expire %Tu, "
+ "lastmod %Tu, lifetime %Tu, reason: %s; gline %s! (fields "
+ "present: %s %s %s)", target, mask,
+ flags & GLINE_OPERFORCE ? "YES" : "NO",
+ action == GLINE_ACTIVATE ? "+" :
+ (action == GLINE_DEACTIVATE ? "-" :
+ (action == GLINE_LOCAL_ACTIVATE ? ">" :
+ (action == GLINE_LOCAL_DEACTIVATE ? "<" : "(MODIFY)"))),
+ expire_off, lastmod, lifetime, reason,
+ agline ? "EXISTS" : "does not exist",
+ flags & GLINE_EXPIRE ? "expire" : "",
+ flags & GLINE_LIFETIME ? "lifetime" : "",
+ flags & GLINE_REASON ? "reason" : ""));
/* OK, at this point, we have converted all available parameters.
* Let's actually do the action!
*/
if (agline)
- /* XXX modify the G-line */;
-
- /* XXX create the G-line */return 0;
-
-
-
-
-
-/* if ((parc == 3 && *mask == '-') || parc == 5) */
-/* { */
-/* if (!find_conf_byhost(cli_confs(cptr), cli_name(sptr), CONF_UWORLD)) */
-/* return need_more_params(sptr, "GLINE"); */
-
-/* flags |= GLINE_FORCE; */
-/* } */
-/* else if (parc > 5) */
-/* lastmod = atoi(parv[4]); */
-/* else */
-/* return need_more_params(sptr, "GLINE"); */
-
-/* if (parc > 4) */
-/* reason = parv[parc - 1]; */
-
-/* if (IsServer(sptr)) */
-/* flags |= GLINE_FORCE; */
-
-/* if (!(target[0] == '*' && target[1] == '\0')) { */
-/* if (!(acptr = FindNServer(target))) */
-/* return 0; /\* no such server *\/ */
-
-/* if (!IsMe(acptr)) { /\* manually propagate *\/ */
-/* if (!lastmod) */
-/* sendcmdto_one(sptr, CMD_GLINE, acptr, */
-/* (parc == 3) ? "%C %s" : "%C %s %s :%s", acptr, mask, */
-/* parv[3], reason); */
-/* else */
-/* sendcmdto_one(sptr, CMD_GLINE, acptr, "%C %s%s %s %s :%s", acptr, */
-/* flags & GLINE_OPERFORCE ? "!" : "", mask, parv[3], */
-/* parv[4], reason); */
-
-/* return 0; */
-/* } */
-
-/* flags |= GLINE_LOCAL; */
-/* } */
-
-/* if (*mask == '-') */
-/* mask++; */
-/* else if (*mask == '+') { */
-/* flags |= GLINE_ACTIVE; */
-/* mask++; */
-/* } else */
-/* flags |= GLINE_ACTIVE; */
-
-/* expire_off = parc < 5 ? 0 : atoi(parv[3]); */
-
-/* agline = gline_find(mask, GLINE_ANY | GLINE_EXACT); */
-
-/* if (agline) { */
-/* if (GlineIsLocal(agline) && !(flags & GLINE_LOCAL)) /\* global over local *\/ */
-/* gline_free(agline); */
-/* else if (!lastmod && ((flags & GLINE_ACTIVE) == GlineIsRemActive(agline))) */
-/* return gline_propagate(cptr, sptr, agline); */
-/* else if (!lastmod || GlineLastMod(agline) < lastmod) { /\* new mod *\/ */
-/* if (flags & GLINE_ACTIVE) */
-/* return gline_activate(cptr, sptr, agline, lastmod, flags); */
-/* else */
-/* return gline_deactivate(cptr, sptr, agline, lastmod, flags); */
-/* } else if (GlineLastMod(agline) == lastmod || IsBurstOrBurstAck(cptr)) */
-/* return 0; */
-/* else */
-/* return gline_resend(cptr, agline); /\* other server desynched WRT gline *\/ */
-/* } else if (parc == 3 && !(flags & GLINE_ACTIVE)) { */
-/* /\* U-lined server removing a G-line we don't have; propagate the removal */
-/* * anyway. */
-/* *\/ */
-/* if (!(flags & GLINE_LOCAL)) */
-/* sendcmdto_serv_butone(sptr, CMD_GLINE, cptr, "* -%s", mask); */
-/* return 0; */
-/* } else if (parc < 5) */
-/* return need_more_params(sptr, "GLINE"); */
-
-/* return gline_add(cptr, sptr, mask, reason, expire_off, lastmod, flags); */
+ return gline_modify(cptr, sptr, agline, action, reason, expire_off,
+ lastmod, lifetime, flags);
+
+ assert(action != GLINE_LOCAL_ACTIVATE);
+ assert(action != GLINE_LOCAL_DEACTIVATE);
+ assert(action != GLINE_MODIFY);
+
+ return gline_add(cptr, sptr, mask, reason, expire_off, lastmod, lifetime,
+ flags | ((action == GLINE_ACTIVATE) ? GLINE_ACTIVE : 0));
}
/*
* parv[0] = Sender prefix
* parv[1] = [[+|-]<G-line mask>]
*
- * Local (to me) style:
- *
- * parv[2] = [Expiration offset]
- * parv[3] = [Comment]
- *
- * Global (or remote local) style:
- *
- * parv[2] = [target]
- * parv[3] = [Expiration offset]
- * parv[4] = [Comment]
- *
+ * For other parameters, see doc/readme.gline.
*/
int
mo_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
{
struct Client *acptr = 0;
- struct Gline *agline;
+ struct Gline *agline = 0;
unsigned int flags = 0;
- time_t expire_off;
- char *mask = parv[1], *target = 0, *reason;
+ enum GlineAction action = GLINE_MODIFY;
+ time_t expire_off = 0;
+ char *mask = parv[1], *target = 0, *reason = 0;
if (parc < 2)
return gline_list(sptr, 0);
flags |= GLINE_OPERFORCE;
}
- if (*mask == '+') {
- flags |= GLINE_ACTIVE;
+ switch (*mask) { /* handle +, -, <, and > */
+ case '+': /* activate the G-line */
+ action = GLINE_ACTIVATE;
mask++;
+ break;
- } else if (*mask == '-')
+ case '-': /* deactivate the G-line */
+ action = GLINE_DEACTIVATE;
mask++;
- else
- return gline_list(sptr, mask);
+ break;
- if (parc == 4) {
- expire_off = atoi(parv[2]);
- reason = parv[3];
- flags |= GLINE_LOCAL;
- } else if (parc > 4) {
- target = parv[2];
- expire_off = atoi(parv[3]);
- reason = parv[4];
- } else
- return need_more_params(sptr, "GLINE");
+ case '>': /* locally activate the G-line */
+ action = GLINE_LOCAL_ACTIVATE;
+ mask++;
+ break;
- if (target)
- {
- if (!(target[0] == '*' && target[1] == '\0'))
- {
- if (!(acptr = find_match_server(target)))
- return send_reply(sptr, ERR_NOSUCHSERVER, target);
-
- /* manually propagate, since we don't set it */
- if (!IsMe(acptr))
- {
- if (!feature_bool(FEAT_CONFIG_OPERCMDS))
- return send_reply(sptr, ERR_DISABLED, "GLINE");
-
- if (!HasPriv(sptr, PRIV_GLINE))
- return send_reply(sptr, ERR_NOPRIVILEGES);
-
- sendcmdto_one(sptr, CMD_GLINE, acptr, "%C %s%c%s %s %Tu :%s", acptr,
- flags & GLINE_OPERFORCE ? "!" : "",
- flags & GLINE_ACTIVE ? '+' : '-', mask, parv[3],
- TStime(), reason);
- return 0;
- }
- flags |= GLINE_LOCAL;
+ case '<': /* locally deactivate the G-line */
+ action = GLINE_LOCAL_DEACTIVATE;
+ mask++;
+ break;
+ }
+
+ /* OK, let's figure out the parameters... */
+ switch (action) {
+ case GLINE_MODIFY: /* no specific action on the G-line... */
+ if (parc == 2) /* user wants a listing of a specific G-line */
+ return gline_list(sptr, mask);
+ else if (parc < 4) /* must have target and expire, minimum */
+ return need_more_params(sptr, "GLINE");
+
+ target = parv[2]; /* get the target... */
+ expire_off = atoi(parv[3]); /* and the expiration */
+
+ flags |= GLINE_EXPIRE; /* remember that we got an expire time */
+
+ if (parc > 4) { /* also got a reason... */
+ reason = parv[4];
+ flags |= GLINE_REASON;
+ }
+
+ /* target is not global, interpolate action and require reason */
+ if (target[0] != '*' || target[1] != '\0') {
+ if (!reason) /* have to have a reason for this */
+ return need_more_params(sptr, "GLINE");
+
+ action = GLINE_ACTIVATE;
+ }
+ break;
+
+ 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 */
+ target = parv[2];
+ break;
+
+ case GLINE_ACTIVATE: /* activating/adding a G-line */
+ case GLINE_DEACTIVATE: /* deactivating/removing a G-line */
+ if (parc < 3)
+ return need_more_params(sptr, "GLINE");
+
+ if (parc > 3) {
+ /* get expiration and target */
+ expire_off = atoi(parv[parc - 2]);
+ reason = parv[parc - 1];
+
+ flags |= GLINE_EXPIRE | GLINE_REASON; /* remember that we got 'em */
+
+ if (parc > 4) /* also have a target! */
+ target = parv[2];
+ } else {
+ target = parv[2]; /* target has to be present, and has to be '*' */
+
+ if (target[0] != '*' || target[1] != '\0')
+ return need_more_params(sptr, "GLINE");
}
+ break;
}
- if (!(flags & GLINE_LOCAL) && !feature_bool(FEAT_CONFIG_OPERCMDS))
- return send_reply(sptr, ERR_DISABLED, "GLINE");
+ /* Now let's figure out which is the target server */
+ if (!target) /* no target, has to be me... */
+ acptr = &me;
+ /* if it's not '*', look up the server */
+ else if ((target[0] != '*' || target[1] != '\0') &&
+ !(acptr = find_match_server(target)))
+ return send_reply(sptr, ERR_NOSUCHSERVER, target);
- if (!HasPriv(sptr, (flags & GLINE_LOCAL ? PRIV_LOCAL_GLINE : PRIV_GLINE)))
- return send_reply(sptr, ERR_NOPRIVILEGES);
+ /* Now, is the G-line local or global? */
+ if (action == GLINE_LOCAL_ACTIVATE || action == GLINE_LOCAL_DEACTIVATE ||
+ !acptr)
+ flags |= GLINE_GLOBAL;
+ else /* it's some form of local G-line */
+ flags |= GLINE_LOCAL;
+
+ /* If it's a local activate/deactivate and server isn't me, propagate it */
+ if ((action == GLINE_LOCAL_ACTIVATE || action == GLINE_LOCAL_DEACTIVATE) &&
+ !IsMe(acptr)) {
+ Debug((DEBUG_DEBUG, "I am forwarding a local change to a global gline "
+ "to a remote server; target %s, mask %s, operforce %s, action %s",
+ cli_name(acptr), mask, flags & GLINE_OPERFORCE ? "YES" : "NO",
+ action == GLINE_LOCAL_ACTIVATE ? ">" : "<"));
- agline = gline_find(mask, GLINE_ANY | GLINE_EXACT);
+ sendcmdto_one(sptr, CMD_GLINE, acptr, "%C %s%c%s", acptr,
+ flags & GLINE_OPERFORCE ? "!" : "",
+ action == GLINE_LOCAL_ACTIVATE ? ">" : "<", mask);
- if (agline) {
- if (GlineIsLocal(agline) && !(flags & GLINE_LOCAL)) /* global over local */
- gline_free(agline);
- else {
- if (!GlineLastMod(agline)) /* force mods to Uworld-set G-lines local */
- flags |= GLINE_LOCAL;
+ return 0; /* all done */
+ }
+
+ /* Next, try to find the G-line... */
+ if ((flags & GLINE_GLOBAL) || IsMe(acptr)) /* don't bother if it's not me! */
+ agline = gline_find(mask, flags | GLINE_ANY | GLINE_EXACT);
+
+ /* We now have all the pieces to tell us what we've got; let's put
+ * it all together and convert the rest of the arguments.
+ */
+
+ /* Handle the local G-lines first... */
+ if (flags & GLINE_LOCAL) {
+ assert(acptr);
+
+ /* normalize the action, first */
+ if (action == GLINE_LOCAL_ACTIVATE || action == GLINE_MODIFY)
+ action = GLINE_ACTIVATE;
+ else if (action == GLINE_LOCAL_DEACTIVATE)
+ action = GLINE_DEACTIVATE;
+
+ /* If it's not for us, forward */
+ /* UPDATE NOTE: Once all servers are updated to u2.10.12.11, the
+ * format string in this sendcmdto_one() may be updated to omit
+ * <lastmod> for GLINE_ACTIVATE and to omit <expire>, <lastmod>,
+ * and <reason> for GLINE_DEACTIVATE.
+ */
+
+ if (!IsMe(acptr)) {
+ Debug((DEBUG_DEBUG, "I am forwarding a local G-line to a remote "
+ "server; target %s, mask %s, operforce %s, action %s, "
+ "expire %Tu, reason %s", target, mask,
+ flags & GLINE_OPERFORCE ? "YES" : "NO",
+ action == GLINE_ACTIVATE ? "+" : "-", expire_off, reason));
+
+ sendcmdto_one(sptr, CMD_GLINE, acptr, "%C %s%c%s %Tu %Tu :%s",
+ acptr, flags & GLINE_OPERFORCE ? "!" : "",
+ action == GLINE_ACTIVATE ? "+" : "-", mask, expire_off,
+ CurrentTime, reason);
+
+ return 0; /* all done */
+ }
+
+ /* let's handle activation... */
+ if (action == GLINE_ACTIVATE) {
+ if (agline) /* G-line already exists, so let's ignore it... */
+ return 0;
- if (flags & GLINE_ACTIVE)
- return gline_activate(cptr, sptr, agline,
- GlineLastMod(agline) ? TStime() : 0, flags);
- else
- return gline_deactivate(cptr, sptr, agline,
- GlineLastMod(agline) ? TStime() : 0, flags);
+ /* OK, create the local G-line */
+ Debug((DEBUG_DEBUG, "I am creating a local G-line here; target %s, "
+ "mask %s, operforce %s, action %s, expire %Tu, reason: %s",
+ target, mask, flags & GLINE_OPERFORCE ? "YES" : "NO",
+ action == GLINE_ACTIVATE ? "+" : "-", expire_off, reason));
+
+ return gline_add(cptr, sptr, mask, reason, expire_off, 0, 0,
+ flags | GLINE_ACTIVE);
+ } else { /* OK, it's a deactivation/destruction */
+ if (!agline) /* G-line doesn't exist, so let's complain... */
+ return send_reply(sptr, ERR_NOSUCHGLINE, mask);
+
+ /* Let's now destroy the G-line */
+ Debug((DEBUG_DEBUG, "I am destroying a local G-line here; target %s, "
+ "mask %s, operforce %s, action %s", target, mask,
+ flags & GLINE_OPERFORCE ? "YES" : "NO",
+ action == GLINE_ACTIVATE ? "+" : "-"));
+
+ return gline_destroy(cptr, sptr, agline);
}
}
- return gline_add(cptr, sptr, mask, reason, expire_off, TStime(), flags);
+ /* can't modify a G-line that doesn't exist... */
+ if (!agline &&
+ (action == GLINE_MODIFY || action == GLINE_LOCAL_ACTIVATE ||
+ action == GLINE_LOCAL_DEACTIVATE))
+ return send_reply(sptr, ERR_NOSUCHGLINE, mask);
+
+ 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,
+ mask, flags & GLINE_OPERFORCE ? "YES" : "NO",
+ action == GLINE_ACTIVATE ? "+" :
+ (action == GLINE_DEACTIVATE ? "-" :
+ (action == GLINE_LOCAL_ACTIVATE ? ">" :
+ (action == GLINE_LOCAL_DEACTIVATE ? "<" : "(MODIFY)"))),
+ expire_off, reason, agline ? "EXISTS" : "does not exist",
+ flags & GLINE_EXPIRE ? "expire" : "",
+ flags & GLINE_REASON ? "reason" : ""));
+
+ if (agline) /* modifying an existing G-line */
+ return gline_modify(cptr, sptr, agline, action, reason, expire_off,
+ CurrentTime, 0, flags);
+
+ assert(action != GLINE_LOCAL_ACTIVATE);
+ assert(action != GLINE_LOCAL_DEACTIVATE);
+ assert(action != GLINE_MODIFY);
+
+ /* create a new G-line */
+ return gline_add(cptr, sptr, mask, reason, expire_off, CurrentTime, 0,
+ flags | ((action == GLINE_ACTIVATE) ? GLINE_ACTIVE : 0));
}
/*
/* 246 */
{ RPL_STATSTLINE, "%c %s %s", "246" },
/* 247 */
- { RPL_STATSGLINE, "%c %s%s%s %Tu %c :%s", "247" },
+ { RPL_STATSGLINE, "%c %s%s%s %Tu %Tu %Tu %s%c :%s", "247" },
/* 248 */
{ RPL_STATSULINE, "U %s", "248" },
/* 249 */
/* 279 */
{ 0 },
/* 280 */
- { RPL_GLIST, "%s%s%s %Tu %s %c :%s", "280" },
+ { RPL_GLIST, "%s%s%s %Tu %Tu %Tu %s %s%c :%s", "280" },
/* 281 */
{ RPL_ENDOFGLIST, ":End of G-line List", "281" },
/* 282 */