Author: Michael Poole <mdpoole@troilus.org>
[ircu2.10.12-pk.git] / ircd / channel.c
index e591dda3a524cff95e8f7ce74e2fc2e4f51e5f8e..8ed3eb9c845011f6d3585f54399e02ae6d3b874b 100644 (file)
@@ -832,6 +832,8 @@ void channel_modes(struct Client *cptr, char *mbuf, char *pbuf, int buflen,
     *mbuf++ = 'D';
   else if (MyUser(cptr) && (chptr->mode.mode & MODE_WASDELJOINS))
     *mbuf++ = 'd';
+  if (chptr->mode.mode & MODE_REGISTERED)
+    *mbuf++ = 'R';
   if (chptr->mode.limit) {
     *mbuf++ = 'l';
     ircd_snprintf(0, pbuf, buflen, "%u", chptr->mode.limit);
@@ -1540,6 +1542,7 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
     MODE_NOPRIVMSGS,   'n',
     MODE_REGONLY,      'r',
     MODE_DELJOINS,      'D',
+    MODE_REGISTERED,   'R',
 /*  MODE_KEY,          'k', */
 /*  MODE_BAN,          'b', */
     MODE_LIMIT,                'l',
@@ -1953,7 +1956,7 @@ modebuf_mode(struct ModeBuf *mbuf, unsigned int mode)
 
   mode &= (MODE_ADD | MODE_DEL | MODE_PRIVATE | MODE_SECRET | MODE_MODERATED |
           MODE_TOPICLIMIT | MODE_INVITEONLY | MODE_NOPRIVMSGS | MODE_REGONLY |
-           MODE_DELJOINS | MODE_WASDELJOINS);
+           MODE_DELJOINS | MODE_WASDELJOINS | MODE_REGISTERED);
 
   if (!(mode & ~(MODE_ADD | MODE_DEL))) /* don't add empty modes... */
     return;
@@ -1983,7 +1986,7 @@ modebuf_mode_uint(struct ModeBuf *mbuf, unsigned int mode, unsigned int uint)
   assert(0 != mbuf);
   assert(0 != (mode & (MODE_ADD | MODE_DEL)));
 
-  if (mode == (MODE_LIMIT | ((mbuf->mb_dest & MODEBUF_DEST_BOUNCE) ? MODE_ADD : MODE_DEL))) {
+  if (mode == (MODE_LIMIT | MODE_DEL)) {
       mbuf->mb_rem |= mode;
       return;
   }
@@ -2049,6 +2052,20 @@ modebuf_mode_client(struct ModeBuf *mbuf, unsigned int mode,
     modebuf_flush_int(mbuf, 0);
 }
 
+/** Check a channel for join-delayed members.
+ * @param[in] chan Channel to search.
+ * @return Non-zero if any members are join-delayed; false if none are.
+ */
+static int
+find_delayed_joins(const struct Channel *chan)
+{
+  const struct Membership *memb;
+  for (memb = chan->members; memb; memb = memb->next_member)
+    if (IsDelayedJoin(memb))
+      return 1;
+  return 0;
+}
+
 /** The exported binding for modebuf_flush()
  *
  * @param mbuf The mode buffer to flush.
@@ -2058,19 +2075,22 @@ modebuf_mode_client(struct ModeBuf *mbuf, unsigned int mode,
 int
 modebuf_flush(struct ModeBuf *mbuf)
 {
-  struct Membership *memb;
-
-  /* Check if MODE_WASDELJOINS should be set */
-  if (!(mbuf->mb_channel->mode.mode & (MODE_DELJOINS | MODE_WASDELJOINS))
-      && (mbuf->mb_rem & MODE_DELJOINS)) {
-    for (memb = mbuf->mb_channel->members; memb; memb = memb->next_member) {
-      if (IsDelayedJoin(memb)) {
-          mbuf->mb_channel->mode.mode |= MODE_WASDELJOINS;
-          mbuf->mb_add |= MODE_WASDELJOINS;
-          mbuf->mb_rem &= ~MODE_WASDELJOINS;
-          break;
-      }
-    }
+  /* Check if MODE_WASDELJOINS should be set: */
+  /* Must be set if going -D and some clients are hidden */
+  if ((mbuf->mb_rem & MODE_DELJOINS)
+      && !(mbuf->mb_channel->mode.mode & (MODE_DELJOINS | MODE_WASDELJOINS))
+      && find_delayed_joins(mbuf->mb_channel)) {
+    mbuf->mb_channel->mode.mode |= MODE_WASDELJOINS;
+    mbuf->mb_add |= MODE_WASDELJOINS;
+    mbuf->mb_rem &= ~MODE_WASDELJOINS;
+  }
+  /* Must be cleared if +D is set */
+  if ((mbuf->mb_add & MODE_DELJOINS)
+      && ((mbuf->mb_channel->mode.mode & (MODE_WASDELJOINS | MODE_WASDELJOINS))
+          == (MODE_WASDELJOINS | MODE_WASDELJOINS))) {
+    mbuf->mb_channel->mode.mode &= ~MODE_WASDELJOINS;
+    mbuf->mb_add &= ~MODE_WASDELJOINS;
+    mbuf->mb_rem |= MODE_WASDELJOINS;
   }
 
   return modebuf_flush_int(mbuf, 1);
@@ -2096,6 +2116,7 @@ modebuf_extract(struct ModeBuf *mbuf, char *buf)
     MODE_KEY,          'k',
     MODE_APASS,                'A',
     MODE_UPASS,                'U',
+    MODE_REGISTERED,   'R',
 /*  MODE_BAN,          'b', */
     MODE_LIMIT,                'l',
     MODE_REGONLY,      'r',
@@ -2155,9 +2176,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.
  */
@@ -2186,12 +2208,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;
@@ -2344,9 +2369,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);
@@ -2446,9 +2481,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);
@@ -2583,9 +2628,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);
@@ -2716,9 +2771,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;
@@ -3127,6 +3182,11 @@ mode_parse_mode(struct ParseState *state, int *flag_p)
   if (!state->mbuf)
     return;
 
+  /* Local users are not permitted to change registration status */
+  if (flag_p[0] == MODE_REGISTERED && !(state->flags & MODE_PARSE_FORCE) &&
+      MyUser(state->sptr))
+    return;
+
   if (state->dir == MODE_ADD) {
     state->add |= flag_p[0];
     state->del &= ~flag_p[0];
@@ -3138,10 +3198,6 @@ mode_parse_mode(struct ParseState *state, int *flag_p)
       state->add &= ~MODE_SECRET;
       state->del |= MODE_SECRET;
     }
-    if (flag_p[0] & MODE_DELJOINS) {
-      state->add &= ~MODE_WASDELJOINS;
-      state->del |= MODE_WASDELJOINS;
-    }
   } else {
     state->add &= ~flag_p[0];
     state->del |= flag_p[0];
@@ -3174,6 +3230,7 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
     MODE_KEY,          'k',
     MODE_APASS,                'A',
     MODE_UPASS,                'U',
+    MODE_REGISTERED,   'R',
     MODE_BAN,          'b',
     MODE_LIMIT,                'l',
     MODE_REGONLY,      'r',
@@ -3363,13 +3420,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);
   }
@@ -3576,18 +3633,9 @@ void RevealDelayedJoin(struct Membership *member)
 
 void CheckDelayedJoins(struct Channel *chan)
 {
-  struct Membership *memb2;
-
-  if (chan->mode.mode & MODE_WASDELJOINS) {
-    for (memb2=chan->members;memb2;memb2=memb2->next_member)
-      if (IsDelayedJoin(memb2))
-        break;
-
-    if (!memb2) {
-      /* clear +d */
-      chan->mode.mode &= ~MODE_WASDELJOINS;
-      sendcmdto_channel_butserv_butone(&his, CMD_MODE, chan, NULL, 0,
-                                       "%H -d", chan);
-    }
+  if ((chan->mode.mode & MODE_WASDELJOINS) && !find_delayed_joins(chan)) {
+    chan->mode.mode &= ~MODE_WASDELJOINS;
+    sendcmdto_channel_butserv_butone(&his, CMD_MODE, chan, NULL, 0,
+                                     "%H -d", chan);
   }
 }