Fix SF bug #2721107 (Gline lifetime changes from servers change the reason.)
[ircu2.10.12-pk.git] / ircd / m_gline.c
index f3b436762d781aa2afa60cf7c034bdb436763054..549ae140db667df9ebac742cc6efb0ec0ec25635 100644 (file)
@@ -1,4 +1,4 @@
-/*
+\/*
  * IRC - Internet Relay Chat, ircd/m_gline.c
  * Copyright (C) 1990 Jarkko Oikarinen and
  *                    University of Oulu, Computing Center
 #include <stdlib.h>
 #include <string.h>
 
+#define PASTWATCH      157680000       /* number of seconds in 5 years */
+
+/*
+ * If the expiration value, interpreted as an absolute timestamp, is
+ * more recent than 5 years in the past, we interpret it as an
+ * absolute timestamp; otherwise, we assume it's relative and convert
+ * it to an absolute timestamp.  Either way, the output of this macro
+ * is an absolute timestamp--not guaranteed to be a *valid* timestamp,
+ * but you can't have everything in a macro ;)
+ */
+#define abs_expire(exp)                                                        \
+  ((exp) >= CurrentTime - PASTWATCH ? (exp) : (exp) + CurrentTime)
+
 /*
  * ms_gline - server message handler
  *
@@ -118,7 +131,7 @@ ms_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
   struct Gline *agline = 0;
   unsigned int flags = 0;
   enum GlineAction action = GLINE_MODIFY;
-  time_t expire_off = 0, lastmod = 0, lifetime = 0;
+  time_t expire = 0, lastmod = 0, lifetime = 0;
   char *mask = parv[2], *target = parv[1], *reason = "No reason", *tmp = 0;
 
   if (parc < 3)
@@ -170,13 +183,13 @@ ms_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
   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",
+          "to a remote server; target %s, mask %s, operforce %s, action %c",
           target, mask, flags & GLINE_OPERFORCE ? "YES" : "NO",
-          action == GLINE_LOCAL_ACTIVATE ? ">" : "<"));
+          action == GLINE_LOCAL_ACTIVATE ? '>' : '<'));
 
     sendcmdto_one(sptr, CMD_GLINE, acptr, "%C %s%c%s", acptr,
                  flags & GLINE_OPERFORCE ? "!" : "",
-                 action == GLINE_LOCAL_ACTIVATE ? ">" : "<", mask);
+                 action == GLINE_LOCAL_ACTIVATE ? '>' : '<', mask);
 
     return 0; /* all done */
   }
@@ -203,7 +216,8 @@ ms_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
       if (parc < 5) /* check parameter count... */
        return need_more_params(sptr, "GLINE");
 
-      expire_off = atoi(parv[3]); /* get expiration... */
+      expire = atoi(parv[3]); /* get expiration... */
+      expire = abs_expire(expire); /* convert to absolute... */
       reason = parv[parc - 1]; /* and reason */
 
       if (IsMe(acptr)) {
@@ -214,9 +228,9 @@ ms_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
        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));
+              action == GLINE_ACTIVATE ? "+" : "-", expire, reason));
 
-       return gline_add(cptr, sptr, mask, reason, expire_off, lastmod,
+       return gline_add(cptr, sptr, mask, reason, expire, lastmod,
                         lifetime, flags | GLINE_ACTIVE);
       }
     } else if (IsMe(acptr)) { /* destroying a local G-line */
@@ -241,16 +255,16 @@ ms_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
     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, "
+          "target %s, mask %s, operforce %s, action %c, expire %Tu, "
           "lastmod %Tu, reason: %s", target, mask,
           flags & GLINE_OPERFORCE ? "YES" : "NO",
-          action == GLINE_ACTIVATE ? "+" :  "-", expire_off, CurrentTime,
+          action == GLINE_ACTIVATE ? '+' :  '-', expire, 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);
+                 action == GLINE_ACTIVATE ? '+' : '-', mask,
+                 expire - CurrentTime, CurrentTime, reason);
 
     return 0; /* all done */
   }
@@ -265,6 +279,7 @@ ms_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
   case GLINE_LOCAL_DEACTIVATE: /* locally deactivating a G-line */
     if (!agline) /* no G-line to locally activate or deactivate? */
       return send_reply(sptr, ERR_NOSUCHGLINE, mask);
+    lastmod = agline->gl_lastmod;
     break; /* no additional parameters to manipulate */
 
   case GLINE_ACTIVATE: /* activating a G-line */
@@ -278,10 +293,8 @@ ms_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
   case GLINE_MODIFY: /* modifying a G-line */
     /* convert expire and lastmod, look for lifetime and reason */
     if (parc > 4) { /* protect against fall-through from 4-param form */
-      if (parc < 5)
-       return need_more_params(sptr, "GLINE");
-
-      expire_off = atoi(parv[3]); /* convert expiration and lastmod */
+      expire = atoi(parv[3]); /* convert expiration and lastmod */
+      expire = abs_expire(expire);
       lastmod = atoi(parv[4]);
 
       flags |= GLINE_EXPIRE; /* we have an expiration time update */
@@ -295,7 +308,7 @@ ms_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
        if (!agline || /* gline creation, has to be the reason */
            /* trial-convert as lifetime, and if it doesn't fully convert,
             * it must be the reason */
-           ((lifetime = strtoul(parv[5], &tmp, 10)) && !*tmp)) {
+           (!(lifetime = strtoul(parv[5], &tmp, 10)) && !*tmp)) {
          lifetime = 0;
          reason = parv[5];
 
@@ -306,6 +319,9 @@ ms_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
     }
   }
 
+  if (!lastmod) /* must have a lastmod parameter by now */
+    return need_more_params(sptr, "GLINE");
+
   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 "
@@ -315,7 +331,7 @@ ms_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
         (action == GLINE_DEACTIVATE ? "-" :
          (action == GLINE_LOCAL_ACTIVATE ? ">" :
           (action == GLINE_LOCAL_DEACTIVATE ? "<" : "(MODIFY)"))),
-        expire_off, lastmod, lifetime, reason,
+        expire, lastmod, lifetime, reason,
         agline ? "EXISTS" : "does not exist",
         flags & GLINE_EXPIRE ? "expire" : "",
         flags & GLINE_LIFETIME ? "lifetime" : "",
@@ -325,14 +341,26 @@ ms_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
    * Let's actually do the action!
    */
   if (agline)
-    return gline_modify(cptr, sptr, agline, action, reason, expire_off,
+    return gline_modify(cptr, sptr, agline, action, reason, expire,
                        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,
+  if (!expire) { /* Cannot *add* a G-line we don't have, but try hard */
+    Debug((DEBUG_DEBUG, "Propagating G-line %s for G-line we don't have",
+          action == GLINE_ACTIVATE ? "activation" : "deactivation"));
+
+    /* propagate the G-line, even though we don't have it */
+    sendcmdto_serv_butone(sptr, CMD_GLINE, cptr, "* %c%s %Tu",
+                         action == GLINE_ACTIVATE ? '+' : '-',
+                         mask, lastmod);
+
+    return 0;
+  }
+
+  return gline_add(cptr, sptr, mask, reason, expire, lastmod, lifetime,
                   flags | ((action == GLINE_ACTIVATE) ? GLINE_ACTIVE : 0));
 }
 
@@ -351,8 +379,8 @@ mo_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
   struct Gline *agline = 0;
   unsigned int flags = 0;
   enum GlineAction action = GLINE_MODIFY;
-  time_t expire_off = 0;
-  char *mask = parv[1], *target = 0, *reason = 0;
+  time_t expire = 0;
+  char *mask = parv[1], *target = 0, *reason = 0, *end;
 
   if (parc < 2)
     return gline_list(sptr, 0);
@@ -395,12 +423,14 @@ mo_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
       return need_more_params(sptr, "GLINE");
 
     target = parv[2]; /* get the target... */
-    expire_off = atoi(parv[3]); /* and the expiration */
+    expire = strtol(parv[3], &end, 10) + CurrentTime; /* and the expiration */
+    if (*end != '\0')
+      return send_reply(sptr, SND_EXPLICIT | ERR_BADEXPIRE, "%s :Bad expire time", parv[3]);
 
     flags |= GLINE_EXPIRE; /* remember that we got an expire time */
 
     if (parc > 4) { /* also got a reason... */
-      reason = parv[4];
+      reason = parv[parc - 1];
       flags |= GLINE_REASON;
     }
 
@@ -415,8 +445,11 @@ mo_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
 
   case GLINE_LOCAL_ACTIVATE: /* locally activate a G-line */
   case GLINE_LOCAL_DEACTIVATE: /* locally deactivate a G-line */
-    if (parc > 2) /* if target is available, pick it */
+    if (parc > 2) /* if target is available, pick it */
       target = parv[2];
+      if (target[0] == '*' && target[1] == '\0')
+        return send_reply(sptr, ERR_NOSUCHSERVER, target);
+    }
     break;
 
   case GLINE_ACTIVATE: /* activating/adding a G-line */
@@ -426,8 +459,10 @@ mo_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
 
     if (parc > 3) {
       /* get expiration and target */
-      expire_off = atoi(parv[parc - 2]);
       reason = parv[parc - 1];
+      expire = strtol(parv[parc - 2], &end, 10) + CurrentTime;
+      if (*end != '\0')
+        return send_reply(sptr, SND_EXPLICIT | ERR_BADEXPIRE, "%s :Bad expire time", parv[parc - 2]);
 
       flags |= GLINE_EXPIRE | GLINE_REASON; /* remember that we got 'em */
 
@@ -460,14 +495,20 @@ mo_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
   /* 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)) {
+    /* check for permissions... */
+    if (!feature_bool(FEAT_CONFIG_OPERCMDS))
+      return send_reply(sptr, ERR_DISABLED, "GLINE");
+    else if (!HasPriv(sptr, PRIV_GLINE))
+      return send_reply(sptr, ERR_NOPRIVILEGES);
+
     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",
+          "to a remote server; target %s, mask %s, operforce %s, action %c",
           cli_name(acptr), mask, flags & GLINE_OPERFORCE ? "YES" : "NO",
-          action == GLINE_LOCAL_ACTIVATE ? ">" : "<"));
+          action == GLINE_LOCAL_ACTIVATE ? '>' : '<'));
 
     sendcmdto_one(sptr, CMD_GLINE, acptr, "%C %s%c%s", acptr,
-                 flags & GLINE_OPERFORCE ? "!" : "",
-                 action == GLINE_LOCAL_ACTIVATE ? ">" : "<", mask);
+                  flags & GLINE_OPERFORCE ? "!" : "",
+                  action == GLINE_LOCAL_ACTIVATE ? '>' : '<', mask);
 
     return 0; /* all done */
   }
@@ -498,20 +539,30 @@ mo_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
      */
 
     if (!IsMe(acptr)) {
+      /* check for permissions... */
+      if (!feature_bool(FEAT_CONFIG_OPERCMDS))
+       return send_reply(sptr, ERR_DISABLED, "GLINE");
+      else if (!HasPriv(sptr, PRIV_GLINE))
+       return send_reply(sptr, ERR_NOPRIVILEGES);
+
       Debug((DEBUG_DEBUG, "I am forwarding a local G-line to a remote "
-            "server; target %s, mask %s, operforce %s, action %s, "
+            "server; target %s, mask %s, operforce %s, action %c, "
             "expire %Tu, reason %s", target, mask,
             flags & GLINE_OPERFORCE ? "YES" : "NO",
-            action == GLINE_ACTIVATE ? "+" : "-", expire_off, reason));
+            action == GLINE_ACTIVATE ? '+' : '-', expire, 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);
+                   action == GLINE_ACTIVATE ? '+' : '-', mask,
+                   expire - CurrentTime, CurrentTime, reason);
 
       return 0; /* all done */
     }
 
+    /* check local G-line permissions... */
+    if (!HasPriv(sptr, PRIV_LOCAL_GLINE))
+      return send_reply(sptr, ERR_NOPRIVILEGES);
+
     /* let's handle activation... */
     if (action == GLINE_ACTIVATE) {
       if (agline) /* G-line already exists, so let's ignore it... */
@@ -521,9 +572,9 @@ mo_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
       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));
+            action == GLINE_ACTIVATE ? "+" : "-", expire, reason));
 
-      return gline_add(cptr, sptr, mask, reason, expire_off, 0, 0,
+      return gline_add(cptr, sptr, mask, reason, expire, 0, 0,
                       flags | GLINE_ACTIVE);
     } else { /* OK, it's a deactivation/destruction */
       if (!agline) /* G-line doesn't exist, so let's complain... */
@@ -539,12 +590,26 @@ mo_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
     }
   }
 
-  /* can't modify a G-line that doesn't exist... */
+  /* can't modify a G-line that doesn't exist...
+   * (and if we are creating a new one, we need a reason and expiration)
+   */
   if (!agline &&
       (action == GLINE_MODIFY || action == GLINE_LOCAL_ACTIVATE ||
-       action == GLINE_LOCAL_DEACTIVATE))
+       action == GLINE_LOCAL_DEACTIVATE || !reason || !expire))
     return send_reply(sptr, ERR_NOSUCHGLINE, mask);
 
+  /* check for G-line permissions... */
+  if (action == GLINE_LOCAL_ACTIVATE || action == GLINE_LOCAL_DEACTIVATE) {
+    /* only need local privileges for locally-limited status changes */
+    if (!HasPriv(sptr, PRIV_LOCAL_GLINE))
+      return send_reply(sptr, ERR_NOPRIVILEGES);
+  } else { /* global privileges required */
+    if (!feature_bool(FEAT_CONFIG_OPERCMDS))
+      return send_reply(sptr, ERR_DISABLED, "GLINE");
+    else if (!HasPriv(sptr, PRIV_GLINE))
+      return send_reply(sptr, ERR_NOPRIVILEGES);
+  }
+
   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, 
@@ -553,12 +618,12 @@ mo_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
         (action == GLINE_DEACTIVATE ? "-" :
          (action == GLINE_LOCAL_ACTIVATE ? ">" :
           (action == GLINE_LOCAL_DEACTIVATE ? "<" : "(MODIFY)"))),
-        expire_off, reason, agline ? "EXISTS" : "does not exist",
+        expire, 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,
+    return gline_modify(cptr, sptr, agline, action, reason, expire,
                        CurrentTime, 0, flags);
 
   assert(action != GLINE_LOCAL_ACTIVATE);
@@ -566,7 +631,7 @@ mo_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
   assert(action != GLINE_MODIFY);
 
   /* create a new G-line */
-  return gline_add(cptr, sptr, mask, reason, expire_off, CurrentTime, 0,
+  return gline_add(cptr, sptr, mask, reason, expire, CurrentTime, 0,
                   flags | ((action == GLINE_ACTIVATE) ? GLINE_ACTIVE : 0));
 }