+/** Send a deactivation request for a locally unknown G-line.
+ * @param[in] cptr Client that sent us the G-line modification.
+ * @param[in] sptr Client that originated the G-line modification.
+ * @param[in] userhost Text representation of G-line target.
+ * @param[in] expire Expiration time of G-line.
+ * @param[in] lastmod Last modification time of G-line.
+ * @param[in] lifetime Lifetime of G-line.
+ * @param[in] flags Bitwise combination of GLINE_* flags.
+ * @return Zero.
+ */
+int
+gline_forward_deactivation(struct Client *cptr, struct Client *sptr,
+ char *userhost, time_t expire, time_t lastmod,
+ time_t lifetime, unsigned int flags)
+{
+ char *msg = "deactivating unknown global";
+
+ if (!lifetime)
+ lifetime = expire;
+
+ /* Inform ops and log it */
+ sendto_opmask_butone(0, SNO_GLINE, "%s %s GLINE for %s, expiring at %Tu",
+ (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
+ cli_name(sptr) : cli_name((cli_user(sptr))->server),
+ msg, userhost, expire + TSoffset);
+
+ log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE,
+ "%#C %s GLINE for %s, expiring at %Tu", sptr, msg, userhost,
+ expire);
+
+ sendcmdto_serv_butone(sptr, CMD_GLINE, cptr, "* -%s %Tu %Tu %Tu",
+ userhost, expire, lastmod, lifetime);
+
+ return 0;
+}
+
+/** Modify a global G-line.
+ * @param[in] cptr Client that sent us the G-line modification.
+ * @param[in] sptr Client that originated the G-line modification.
+ * @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 Expiration time of G-line.
+ * @param[in] lastmod Last modification time of G-line.
+ * @param[in] lifetime Lifetime of G-line.
+ * @param[in] flags Bitwise combination of GLINE_* flags.
+ * @return Zero 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 <= CurrentTime || expire > CurrentTime + GLINE_MAX_EXPIRE)) {
+ if (!IsServer(sptr) && MyConnect(sptr)) /* bad expiration time */
+ send_reply(sptr, ERR_BADEXPIRE, expire);
+ return 0;
+ }
+ } 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 = 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, 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>
+ * <dt>GLINE_ANY</dt><dd>Search both BadChans and user G-lines.</dd>
+ * <dt>GLINE_BADCHAN</dt><dd>Search BadChans.</dd>
+ * <dt>GLINE_GLOBAL</dt><dd>Only match global G-lines.</dd>
+ * <dt>GLINE_LOCAL</dt><dd>Only match local G-lines.</dd>
+ * <dt>GLINE_LASTMOD</dt><dd>Only match G-lines with a last modification time.</dd>
+ * <dt>GLINE_EXACT</dt><dd>Require an exact match of G-line mask.</dd>
+ * <dt>anything else</dt><dd>Search user G-lines.</dd>
+ * </dl>
+ * @param[in] userhost Mask to search for.
+ * @param[in] flags Bitwise combination of GLINE_* flags.
+ * @return First matching G-line, or NULL if none are found.
+ */