Fix SF bug #1640796; add a test for it.
[ircu2.10.12-pk.git] / ircd / channel.c
index b97b89a1d57b110b777dbe3ede4169e12535070e..355b99d958b3063e4fb37f553c61dc86021f6adc 100644 (file)
@@ -914,7 +914,7 @@ void send_channel_modes(struct Client *cptr, struct Channel *chptr)
   int                 opped_members_index = 0;
   struct Membership** opped_members = NULL;
   int                 last_oplevel = 0;
-  int                 feat_oplevels = (chptr->mode.apass[0]) != '\0';
+  int                 send_oplevels = 0;
 
   assert(0 != cptr);
   assert(0 != chptr); 
@@ -972,6 +972,9 @@ void send_channel_modes(struct Client *cptr, struct Channel *chptr)
            ++number_of_ops;
          else
            opped_members[opped_members_index++] = member;
+          /* We also send oplevels if anyone is below the weakest level.  */
+          if (OpLevel(member) < MAXOPLEVEL)
+            send_oplevels = 1;
        }
        /* Only handle the members with the flags that we are interested in. */
         if ((member->status & CHFL_VOICED_OR_OPPED) == current_flags[flag_cnt])
@@ -1012,7 +1015,7 @@ void send_channel_modes(struct Client *cptr, struct Channel *chptr)
            if (IsChanOp(member))       /* flag_cnt == 2 or 3 */
            {
               /* append the absolute value of the oplevel */
-              if (feat_oplevels)
+              if (send_oplevels)
                 loc += ircd_snprintf(0, tbuf + loc, sizeof(tbuf) - loc, "%u", last_oplevel = member->oplevel);
               else
                 tbuf[loc++] = 'o';
@@ -1021,7 +1024,7 @@ void send_channel_modes(struct Client *cptr, struct Channel *chptr)
            msgq_append(&me, mb, tbuf);
            new_mode = 0;
          }
-         else if (feat_oplevels && flag_cnt > 1 && last_oplevel != member->oplevel)
+         else if (send_oplevels && flag_cnt > 1 && last_oplevel != member->oplevel)
          {
            /*
             * This can't be the first member of a (continued) BURST
@@ -1806,8 +1809,7 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
       }
 
       /* if we're changing oplevels and we know the oplevel, pass it on */
-      if (mbuf->mb_channel->mode.apass[0]
-          && (MB_TYPE(mbuf, i) & MODE_CHANOP)
+      if ((MB_TYPE(mbuf, i) & MODE_CHANOP)
           && MB_OPLEVEL(mbuf, i) < MAXOPLEVEL)
           *strptr_i += ircd_snprintf(0, strptr + *strptr_i, BUFSIZE - *strptr_i,
                                      " %s%s:%d",
@@ -2047,6 +2049,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.
@@ -2056,19 +2072,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);
@@ -2685,8 +2704,12 @@ bmatch(struct Ban *old_ban, struct Ban *new_ban)
   old_ban->banstr[old_ban->nu_len] = new_ban->banstr[new_ban->nu_len] = '@';
   if (res)
     return res;
-  /* Compare the addresses. */
-  return !ipmask_check(&new_ban->address, &old_ban->address, old_ban->addrbits);
+  /* If the old ban's mask mismatches, cannot be a superset. */
+  if (!ipmask_check(&new_ban->address, &old_ban->address, old_ban->addrbits))
+    return 1;
+  /* Otherwise it depends on whether the old ban's text is a superset
+   * of the new. */
+  return mmatch(old_ban->banstr, new_ban->banstr);
 }
 
 /** Add a ban from a ban list and mark bans that should be removed
@@ -3079,8 +3102,8 @@ mode_process_clients(struct ParseState *state)
         SetOpLevel(member, state->cli_change[i].oplevel);
       else if (!state->member)
         SetOpLevel(member, MAXOPLEVEL);
-      else if (!state->chptr->mode.apass[0] || OpLevel(state->member) == MAXOPLEVEL)
-        SetOpLevel(member, MAXOPLEVEL);
+      else if (OpLevel(state->member) >= MAXOPLEVEL)
+          SetOpLevel(member, OpLevel(state->member));
       else
         SetOpLevel(member, OpLevel(state->member) + 1);
     }
@@ -3132,10 +3155,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];
@@ -3570,18 +3589,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);
   }
 }