Bump PATCHLEVEL for u2.10.12.11 release.
[ircu2.10.12-pk.git] / ircd / channel.c
index 355b99d958b3063e4fb37f553c61dc86021f6adc..d6e72c8a7c41496eef2037c487485d2a8a3637d1 100644 (file)
@@ -2172,9 +2172,10 @@ modebuf_extract(struct ModeBuf *mbuf, char *buf)
   return;
 }
 
-/** Simple function to invalidate bans
+/** Simple function to invalidate a channel's ban cache.
  *
- * This function sets all bans as being valid.
+ * This function marks all members of the channel as being neither
+ * banned nor banned.
  *
  * @param chan The channel to operate on.
  */
@@ -2203,12 +2204,15 @@ mode_invite_clear(struct Channel *chan)
 
 /* What we've done for mode_parse so far... */
 #define DONE_LIMIT     0x01    /**< We've set the limit */
-#define DONE_KEY       0x02    /**< We've set the key */
+#define DONE_KEY_ADD   0x02    /**< We've set the key */
 #define DONE_BANLIST   0x04    /**< We've sent the ban list */
 #define DONE_NOTOPER   0x08    /**< We've sent a "Not oper" error */
 #define DONE_BANCLEAN  0x10    /**< We've cleaned bans... */
-#define DONE_UPASS     0x20    /**< We've set user pass */
-#define DONE_APASS     0x40    /**< We've set admin pass */
+#define DONE_UPASS_ADD 0x20    /**< We've set user pass */
+#define DONE_APASS_ADD 0x40    /**< We've set admin pass */
+#define DONE_KEY_DEL    0x80    /**< We've removed the key */
+#define DONE_UPASS_DEL  0x100   /**< We've removed the user pass */
+#define DONE_APASS_DEL  0x200   /**< We've removed the admin pass */
 
 struct ParseState {
   struct ModeBuf *mbuf;
@@ -2361,9 +2365,19 @@ mode_parse_key(struct ParseState *state, int *flag_p)
     return;
   }
 
-  if (state->done & DONE_KEY) /* allow key to be set only once */
-    return;
-  state->done |= DONE_KEY;
+  /* allow removing and then adding key, but not adding and then removing */
+  if (state->dir == MODE_ADD)
+  {
+    if (state->done & DONE_KEY_ADD)
+      return;
+    state->done |= DONE_KEY_ADD;
+  }
+  else
+  {
+    if (state->done & (DONE_KEY_ADD | DONE_KEY_DEL))
+      return;
+    state->done |= DONE_KEY_DEL;
+  }
 
   /* clean up the key string */
   clean_key(t_str);
@@ -2463,9 +2477,19 @@ mode_parse_upass(struct ParseState *state, int *flag_p)
     return;
   }
 
-  if (state->done & DONE_UPASS) /* allow upass to be set only once */
-    return;
-  state->done |= DONE_UPASS;
+  /* allow removing and then adding upass, but not adding and then removing */
+  if (state->dir == MODE_ADD)
+  {
+    if (state->done & DONE_UPASS_ADD)
+      return;
+    state->done |= DONE_UPASS_ADD;
+  }
+  else
+  {
+    if (state->done & (DONE_UPASS_ADD | DONE_UPASS_DEL))
+      return;
+    state->done |= DONE_UPASS_DEL;
+  }
 
   /* clean up the upass string */
   clean_key(t_str);
@@ -2600,9 +2624,19 @@ mode_parse_apass(struct ParseState *state, int *flag_p)
     }
   }
 
-  if (state->done & DONE_APASS) /* allow apass to be set only once */
-    return;
-  state->done |= DONE_APASS;
+  /* allow removing and then adding apass, but not adding and then removing */
+  if (state->dir == MODE_ADD)
+  {
+    if (state->done & DONE_APASS_ADD)
+      return;
+    state->done |= DONE_APASS_ADD;
+  }
+  else
+  {
+    if (state->done & (DONE_APASS_ADD | DONE_APASS_DEL))
+      return;
+    state->done |= DONE_APASS_DEL;
+  }
 
   /* clean up the apass string */
   clean_key(t_str);
@@ -2733,9 +2767,9 @@ int apply_ban(struct Ban **banlist, struct Ban *newban, int do_free)
   assert(newban->flags & (BAN_ADD|BAN_DEL));
   if (newban->flags & BAN_ADD) {
     size_t totlen = 0;
-    /* If a less specific entry is found, fail.  */
+    /* If a less specific *active* entry is found, fail.  */
     for (ban = *banlist; ban; ban = ban->next) {
-      if (!bmatch(ban, newban)) {
+      if (!bmatch(ban, newban) && !(ban->flags & BAN_DEL)) {
         if (do_free)
           free_ban(newban);
         return 1;
@@ -3376,13 +3410,13 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
     if (state.chptr->mode.limit && !(state.done & DONE_LIMIT))
       modebuf_mode_uint(state.mbuf, MODE_DEL | MODE_LIMIT,
                        state.chptr->mode.limit);
-    if (*state.chptr->mode.key && !(state.done & DONE_KEY))
+    if (*state.chptr->mode.key && !(state.done & DONE_KEY_DEL))
       modebuf_mode_string(state.mbuf, MODE_DEL | MODE_KEY,
                          state.chptr->mode.key, 0);
-    if (*state.chptr->mode.upass && !(state.done & DONE_UPASS))
+    if (*state.chptr->mode.upass && !(state.done & DONE_UPASS_DEL))
       modebuf_mode_string(state.mbuf, MODE_DEL | MODE_UPASS,
                          state.chptr->mode.upass, 0);
-    if (*state.chptr->mode.apass && !(state.done & DONE_APASS))
+    if (*state.chptr->mode.apass && !(state.done & DONE_APASS_DEL))
       modebuf_mode_string(state.mbuf, MODE_DEL | MODE_APASS,
                          state.chptr->mode.apass, 0);
   }