From 5dac8d5087c52b5d957e1cba78605dacf7f8efbd Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" Date: Sat, 17 Mar 2007 22:45:35 +0000 Subject: [PATCH] Author: Kev Log message: Finish changes to G-line. These changes enable "/stats g" and "/gline" to show G-line lastmod, lifetime, and local activation status, as well as allowing G-lines to be modified in virtually any way imaginable. Note that overlapping G-lines are now to be kept, as a potentially masking G-line may actually be disabled and so not actually be masking a new G-line... git-svn-id: file:///home/klmitch/undernet-ircu/undernet-ircu-svn/ircu2/branches/u2_10_12_branch@1780 c9e4aea6-c8fd-4c43-8297-357d70d61c8c --- ChangeLog | 47 +++++ doc/readme.gline | 171 ++++++++++------ include/gline.h | 61 ++++-- ircd/gline.c | 404 +++++++++++++++++++++++++++++++------- ircd/m_gline.c | 501 +++++++++++++++++++++++++++-------------------- ircd/s_err.c | 4 +- 6 files changed, 828 insertions(+), 360 deletions(-) diff --git a/ChangeLog b/ChangeLog index d8bd643..908bc5f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,50 @@ +2007-03-17 Kevin L. Mitchell + + * 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 * ircd/umkpasswd.c (parse_arguments): Exit cleanly rather than diff --git a/doc/readme.gline b/doc/readme.gline index 0526898..bbc00b4 100644 --- a/doc/readme.gline +++ b/doc/readme.gline @@ -1,4 +1,4 @@ -GLINE documentation, last updated on 18 Mar 2000 +GLINE documentation, last updated on 17 Mar 2007 For an ordinary user, the syntax is: @@ -10,66 +10,115 @@ an error is returned. For an operator, the syntax is: - GLINE [[!][+|-] [[] :]] - -If 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 , , and - must be given, even if the G-line already exists. If - 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 -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 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 parameter is a number of -seconds, not to exceed 7 days, for the G-line to exist. The -argument is mandatory and should describe why this particular G-line -was placed. The 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 [[!][+|-|>|<] [] [ [:]]] + +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 + 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 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 is preceded with the '+' character, the +G-line will be added, and and are required; when + is preceded with the '-' character, the G-line will be removed, +and and 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, 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 +and arguments are not required, but 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 with either '+' (for globally activated G-lines) or '-' +(for globally deactivated G-lines). Additionally, must be +given as "*", and the and parameters are +required. If the G-line already exists, it will be modified to match +the new global status, , and . + +When the G-line already exists, an oper may activate or deactivate it +simply by setting to "*" and prefixing the 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 without any prefix, set + to "*", and provide the updated and optionally an +updated . + +* 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 parameter with the "!" +character, IF the operator has the WIDE_GLINE privilege. For a server, the syntax is: - GL (+|-) : - -The may be a server numeric or the character "*", for a -globally scoped G-line. The 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 is a -total number of seconds the G-line is to live for, and 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 parameter indicates the -reason the G-line was placed. - -If a GLINE is received with a 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 are compared; if -the received is less than the stored , 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 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: - - GL + : - GL - - -The parameter will be assumed to be 0. + GL [!][+|-|>|<] [] [] + [] [:] + +There are a total of 8 basic forms of the GL command. The primary +innovation is the addition of the parameter, which +specifies a lifetime for the G-line record which may be longer than +the expiration time. will be monotonically increasing, +enabling 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, will be set to +the numeric of the local server, and must be preceded by '+' +(optionally preceded by '!' if the origin desires to override some +safety settings). The and parameters are +required. The and parameters will be ignored if +present, allowing backwards compatibility with ircu2.10.12.10 and +prior versions. Removing local G-lines is similar-- 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 must be +set to the numeric of the local server, and 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 +parameter must be set to "*". If the G-line does not exist, and if + is given, the G-line will be created with the specified +expiration and (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 parameter, which is a +monotonically increasing serial number for the G-line, and an optional + 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 is required; must be specified for all +other forms of the GL command. diff --git a/include/gline.h b/include/gline.h index 0fa3aba..a2acdd2 100644 --- a/include/gline.h +++ b/include/gline.h @@ -38,27 +38,36 @@ struct StatDesc; #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. */ @@ -74,14 +83,22 @@ enum GlineAction { #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. */ @@ -106,13 +123,19 @@ extern int gline_propagate(struct Client *cptr, struct Client *sptr, 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); diff --git a/ircd/gline.c b/ircd/gline.c index f7cb619..5ae577e 100644 --- a/ircd/gline.c +++ b/ircd/gline.c @@ -40,6 +40,7 @@ #include "s_stats.h" #include "send.h" #include "struct.h" +#include "sys.h" #include "msg.h" #include "numnicks.h" #include "numeric.h" @@ -82,12 +83,13 @@ struct Gline* BadChanGlineList = 0; /* 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 @@ -133,41 +135,49 @@ canon_userhost(char *userhost, char **user_p, char **host_p, char *def_user) */ 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 */ @@ -355,24 +365,17 @@ gline_checkmask(char *mask) 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; } @@ -390,12 +393,14 @@ gline_propagate(struct Client *cptr, struct Client *sptr, struct Gline *gline) * @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]; @@ -405,6 +410,10 @@ gline_add(struct Client *cptr, struct Client *sptr, char *userhost, 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); @@ -413,7 +422,7 @@ gline_add(struct Client *cptr, struct Client *sptr, char *userhost, 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? */ @@ -427,7 +436,7 @@ gline_add(struct Client *cptr, struct Client *sptr, char *userhost, } break; } - user = (*userhost =='$' ? userhost : userhost+2); + user = userhost; host = 0; } else { canon_userhost(userhost, &user, &host, "*"); @@ -463,6 +472,11 @@ gline_add(struct Client *cptr, struct Client *sptr, char *userhost, 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 " @@ -486,10 +500,14 @@ gline_add(struct Client *cptr, struct Client *sptr, char *userhost, 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); @@ -626,6 +644,240 @@ gline_deactivate(struct Client *cptr, struct Client *sptr, struct Gline *gline, 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" : "")))), + 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" : "")))))); + + /* 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: *
@@ -764,20 +1016,20 @@ gline_burst(struct Client *cptr) 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); } } @@ -792,12 +1044,12 @@ gline_resend(struct Client *cptr, struct Gline *gline) 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; } @@ -823,24 +1075,33 @@ gline_list(struct Client *sptr, char *userhost) 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); } } @@ -864,8 +1125,11 @@ gline_stats(struct Client *sptr, const struct StatDesc *sd, 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); } } @@ -880,13 +1144,21 @@ gline_memory_count(size_t *gl_size) 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; } diff --git a/ircd/m_gline.c b/ircd/m_gline.c index 546c1b1..f3b4367 100644 --- a/ircd/m_gline.c +++ b/ircd/m_gline.c @@ -94,6 +94,7 @@ #include "numeric.h" #include "numnicks.h" #include "s_conf.h" +#include "s_debug.h" #include "s_misc.h" #include "send.h" @@ -107,17 +108,8 @@ * parv[0] = Sender prefix * parv[1] = Target: server numeric * parv[2] = (+|-) - * 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[]) @@ -132,11 +124,13 @@ 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 */ @@ -164,19 +158,35 @@ ms_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) 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... */ @@ -197,22 +207,29 @@ ms_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) 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 */ @@ -221,20 +238,19 @@ ms_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) * for GLINE_ACTIVATE and to omit , , * and 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 */ } @@ -247,6 +263,8 @@ ms_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) 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 */ @@ -266,9 +284,13 @@ ms_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) 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, @@ -276,111 +298,42 @@ ms_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) ((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)); } /* @@ -389,26 +342,17 @@ ms_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) * parv[0] = Sender prefix * parv[1] = [[+|-]] * - * 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); @@ -420,77 +364,210 @@ mo_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) 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 + * for GLINE_ACTIVATE and to omit , , + * and 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)); } /* diff --git a/ircd/s_err.c b/ircd/s_err.c index dd36800..cbe6b8d 100644 --- a/ircd/s_err.c +++ b/ircd/s_err.c @@ -526,7 +526,7 @@ static Numeric replyTable[] = { /* 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 */ @@ -592,7 +592,7 @@ static Numeric replyTable[] = { /* 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 */ -- 2.20.1