Make DESTRUCT crossing CREATE or BURST not send a BURST.
[ircu2.10.12-pk.git] / ircd / channel.c
index 5502bb305360de0624ffa81587e27469761a903c..ae27daedc4c6f98187d3fa8369655a9a0c5d07b9 100644 (file)
@@ -760,9 +760,11 @@ const char* find_no_nickchange_channel(struct Client* cptr)
     struct Membership* member;
     for (member = (cli_user(cptr))->channel; member;
         member = member->next_channel) {
-        if (!IsVoicedOrOpped(member) &&
-            (is_banned(member) ||
-             (member->channel->mode.mode & MODE_MODERATED)))
+      if (IsVoicedOrOpped(member))
+        continue;
+      if ((member->channel->mode.mode & MODE_MODERATED)
+          || (member->channel->mode.mode & MODE_REGONLY && !IsAccount(cptr))
+          || is_banned(member))
         return member->channel->chname;
     }
   }
@@ -1518,7 +1520,6 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
     MODE_NOPRIVMSGS,   'n',
     MODE_REGONLY,      'r',
     MODE_DELJOINS,      'D',
-    MODE_WASDELJOINS,   'd',
 /*  MODE_KEY,          'k', */
 /*  MODE_BAN,          'b', */
     MODE_LIMIT,                'l',
@@ -1526,15 +1527,19 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
 /*  MODE_UPASS,                'U', */
     0x0, 0x0
   };
+  static int local_flags[] = {
+    MODE_WASDELJOINS,   'd',
+    0x0, 0x0
+  };
   int i;
   int *flag_p;
 
   struct Client *app_source; /* where the MODE appears to come from */
 
-  char addbuf[20]; /* accumulates +psmtin, etc. */
-  int addbuf_i = 0;
-  char rembuf[20]; /* accumulates -psmtin, etc. */
-  int rembuf_i = 0;
+  char addbuf[20], addbuf_local[20]; /* accumulates +psmtin, etc. */
+  int addbuf_i = 0, addbuf_local_i = 0;
+  char rembuf[20], rembuf_local[20]; /* accumulates -psmtin, etc. */
+  int rembuf_i = 0, rembuf_local_i = 0;
   char *bufptr; /* we make use of indirection to simplify the code */
   int *bufptr_i;
 
@@ -1580,6 +1585,14 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
       rembuf[rembuf_i++] = flag_p[1];
   }
 
+  /* Some flags may be for local display only. */
+  for (flag_p = local_flags; flag_p[0]; flag_p += 2) {
+    if (*flag_p & mbuf->mb_add)
+      addbuf_local[addbuf_local_i++] = flag_p[1];
+    else if (*flag_p & mbuf->mb_rem)
+      rembuf_local[rembuf_local_i++] = flag_p[1];
+  }
+
   /* Now go through the modes with arguments... */
   for (i = 0; i < mbuf->mb_count; i++) {
     if (MB_TYPE(mbuf, i) & MODE_ADD) { /* adding or removing? */
@@ -1649,6 +1662,8 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
   /* terminate the mode strings */
   addbuf[addbuf_i] = '\0';
   rembuf[rembuf_i] = '\0';
+  addbuf_local[addbuf_local_i] = '\0';
+  rembuf_local[rembuf_local_i] = '\0';
 
   /* If we're building a user visible MODE or HACK... */
   if (mbuf->mb_dest & (MODEBUF_DEST_CHANNEL | MODEBUF_DEST_HACK2 |
@@ -1736,9 +1751,12 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
 
     if (mbuf->mb_dest & MODEBUF_DEST_CHANNEL)
       sendcmdto_channel_butserv_butone(app_source, CMD_MODE, mbuf->mb_channel, NULL, 0,
-                               "%H %s%s%s%s%s%s", mbuf->mb_channel,
-                               rembuf_i ? "-" : "", rembuf,
-                               addbuf_i ? "+" : "", addbuf, remstr, addstr);
+                                       "%H %s%s%s%s%s%s%s%s", mbuf->mb_channel,
+                                       rembuf_i || rembuf_local_i ? "-" : "",
+                                       rembuf, rembuf_local,
+                                       addbuf_i || addbuf_local_i ? "+" : "",
+                                       addbuf, addbuf_local,
+                                       remstr, addstr);
   }
 
   /* Now are we supposed to propagate to other servers? */
@@ -1753,7 +1771,7 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
      * limit is supressed if we're removing it; we have to figure out which
      * direction is the direction for it to be removed, though...
      */
-    limitdel |= (mbuf->mb_dest & MODEBUF_DEST_HACK2) ? MODE_DEL : MODE_ADD;
+    limitdel |= (mbuf->mb_dest & MODEBUF_DEST_BOUNCE) ? MODE_DEL : MODE_ADD;
 
     for (i = 0; i < mbuf->mb_count; i++) {
       if (MB_TYPE(mbuf, i) & MODE_SAVE)
@@ -1950,7 +1968,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 | MODE_DEL)) {
+  if (mode == (MODE_LIMIT | ((mbuf->mb_dest & MODEBUF_DEST_BOUNCE) ? MODE_ADD : MODE_DEL))) {
       mbuf->mb_rem |= mode;
       return;
   }
@@ -2873,9 +2891,11 @@ static void
 mode_parse_client(struct ParseState *state, int *flag_p)
 {
   char *t_str;
+  char *colon;
   struct Client *acptr;
   struct Membership *member;
   int oplevel = MAXOPLEVEL + 1;
+  int req_oplevel;
   int i;
 
   if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
@@ -2894,9 +2914,27 @@ mode_parse_client(struct ParseState *state, int *flag_p)
     return;
   }
 
-  if (MyUser(state->sptr)) /* find client we're manipulating */
+  if (MyUser(state->sptr)) {
+    colon = strchr(t_str, ':');
+    if (colon != NULL) {
+      *colon++ = '\0';
+      req_oplevel = atoi(colon);
+      if (!(state->flags & MODE_PARSE_FORCE)
+          && state->member
+          && (req_oplevel < OpLevel(state->member)
+              || (req_oplevel == OpLevel(state->member)
+                  && OpLevel(state->member) < MAXOPLEVEL)
+              || req_oplevel > MAXOPLEVEL))
+        send_reply(state->sptr, ERR_NOTLOWEROPLEVEL,
+                   t_str, state->chptr->chname,
+                   OpLevel(state->member), req_oplevel, "op",
+                   OpLevel(state->member) == req_oplevel ? "the same" : "a higher");
+      else if (req_oplevel <= MAXOPLEVEL)
+        oplevel = req_oplevel;
+    }
+    /* find client we're manipulating */
     acptr = find_chasing(state->sptr, t_str, NULL);
-  else {
+  else {
     if (t_str[5] == ':') {
       t_str[5] = '\0';
       oplevel = atoi(t_str + 6);
@@ -2986,11 +3024,14 @@ mode_process_clients(struct ParseState *state)
          continue;
         }
 
-       /* don't allow to deop members with an op level that is <= our own level */
-       if (state->sptr != state->cli_change[i].client          /* but allow to deop oneself */
-            && state->chptr->mode.apass[0]
+       /* Forbid deopping other members with an oplevel less than
+         * one's own level, and other members with an oplevel the same
+         * as one's own unless both are at MAXOPLEVEL. */
+       if (state->sptr != state->cli_change[i].client
             && state->member
-            && OpLevel(member) <= OpLevel(state->member)) {
+            && ((OpLevel(member) < OpLevel(state->member))
+                || (OpLevel(member) == OpLevel(state->member)
+                    && OpLevel(member) < MAXOPLEVEL))) {
            int equal = (OpLevel(member) == OpLevel(state->member));
            send_reply(state->sptr, ERR_NOTLOWEROPLEVEL,
                       cli_name(state->cli_change[i].client),