added FEAT_CHMODE_A_NOSET to prevent +a from being set by users
[ircu2.10.12-pk.git] / ircd / channel.c
index 1bba9b8e070c89cdd2ec1c2f81ffbb678452a783..41e2cdb6dda2d6bfd51dcd95e250451ee8361137 100644 (file)
@@ -374,7 +374,7 @@ struct Membership* find_channel_member(struct Client* cptr, struct Channel* chpt
  * @param[in] banlist The list of bans to test.
  * @return Pointer to a matching ban, or NULL if none exit.
  */
-struct Ban *find_ban(struct Client *cptr, struct Ban *banlist)
+static struct Ban *find_ban_with_nick(struct Client *cptr, const char *nick, struct Ban *banlist)
 {
   char        nu[NICKLEN + USERLEN + 2];
   char        tmphost[HOSTLEN + 1];
@@ -386,7 +386,7 @@ struct Ban *find_ban(struct Client *cptr, struct Ban *banlist)
 
   /* Build nick!user and alternate host names. */
   ircd_snprintf(0, nu, sizeof(nu), "%s!%s",
-                cli_name(cptr), cli_user(cptr)->username);
+                (nick ? nick : cli_name(cptr)), cli_user(cptr)->username);
   ircd_ntoa_r(iphost, &cli_ip(cptr));
 
   /* Check for all three possible hosts:
@@ -439,6 +439,10 @@ struct Ban *find_ban(struct Client *cptr, struct Ban *banlist)
   return found;
 }
 
+struct Ban *find_ban(struct Client *cptr, struct Ban *banlist) {
+    return find_ban_with_nick(cptr, NULL, banlist);
+}
+
 /**
  * This function returns true if the user is banned on the said channel.
  * This function will check the ban cache if applicable, otherwise will
@@ -462,6 +466,13 @@ static int is_banned(struct Membership* member)
   }
 }
 
+static int is_banned_with_nick(struct Membership* member, const char *nick) {
+  if (find_ban_with_nick(member->user, nick, member->channel->banlist))
+    return 1;
+  else
+    return 0;
+}
+
 /** add a user to a channel.
  * adds a user to a channel by adding another link to the channels member
  * chain.
@@ -645,6 +656,24 @@ int is_chan_op(struct Client *cptr, struct Channel *chptr)
   return 0;
 }
 
+/** Check if this user is a legitimate halfop
+ *
+ * @param cptr Client to check
+ * @param chptr        Channel to check
+ *
+ * @returns True if the user is a halfop (And not a zombie), False otherwise.
+ * @see \ref zombie
+ */
+int is_halfop(struct Client *cptr, struct Channel *chptr)
+{
+  struct Membership* member;
+  assert(chptr);
+  if ((member = find_member_link(chptr, cptr)))
+    return (!IsZombie(member) && IsHalfOp(member));
+
+  return 0;
+}
+
 /** Check if a user is a Zombie on a specific channel.
  *
  * @param cptr         The client to check.
@@ -783,7 +812,8 @@ int client_can_send_to_channel(struct Client *cptr, struct Channel *chptr, int r
    */
   if (!member) {
     if ((chptr->mode.mode & (MODE_NOPRIVMSGS|MODE_MODERATED)) ||
-       ((chptr->mode.mode & MODE_REGONLY) && !IsAccount(cptr)))
+       ((chptr->mode.mode & MODE_REGONLY) && !IsAccount(cptr)) ||
+    ((chptr->mode.mode & MODE_SSLCHAN) && !IsSSL(cptr)))
       return 0;
     else
       return !find_ban(cptr, chptr->banlist);
@@ -796,11 +826,12 @@ int client_can_send_to_channel(struct Client *cptr, struct Channel *chptr, int r
  * the name of the first channel banned on.
  *
  * @param cptr         The client
+ * @param new_nick The new nick of the client
  *
  * @returns the name of the first channel banned on, or NULL if the user
  *          can change nicks.
  */
-const char* find_no_nickchange_channel(struct Client* cptr)
+const char* find_no_nickchange_channel(struct Client* cptr, const char *new_nick)
 {
   if (MyUser(cptr)) {
     struct Membership* member;
@@ -810,7 +841,8 @@ const char* find_no_nickchange_channel(struct Client* cptr)
         continue;
       if ((member->channel->mode.mode & MODE_MODERATED)
           || (member->channel->mode.mode & MODE_REGONLY && !IsAccount(cptr))
-          || is_banned(member))
+          || (member->channel->mode.mode & MODE_SSLCHAN && !IsSSL(cptr))
+          || is_banned(member) || (new_nick && is_banned_with_nick(member, new_nick)))
         return member->channel->chname;
     }
   }
@@ -877,6 +909,8 @@ void channel_modes(struct Client *cptr, char *mbuf, char *pbuf, int buflen,
     *mbuf++ = 'Q';
   if (chptr->mode.mode & MODE_AUDITORIUM)
     *mbuf++ = 'u';
+  if (chptr->mode.mode & MODE_SSLCHAN)
+    *mbuf++ = 'S';
   if (chptr->mode.limit) {
     *mbuf++ = 'l';
     ircd_snprintf(0, pbuf, buflen, "%u", chptr->mode.limit);
@@ -907,7 +941,7 @@ void channel_modes(struct Client *cptr, char *mbuf, char *pbuf, int buflen,
     *mbuf++ = 'k';
     if (previous_parameter)
       strcat(pbuf, " ");
-    if (is_chan_op(cptr, chptr) || IsServer(cptr) || IsOper(cptr)) {
+    if ((member && IsChanOpOrHalfOp(member)) || IsServer(cptr) || IsOper(cptr)) {
       strcat(pbuf, chptr->mode.key);
     } else
       strcat(pbuf, "*");
@@ -927,7 +961,7 @@ void channel_modes(struct Client *cptr, char *mbuf, char *pbuf, int buflen,
     *mbuf++ = 'U';
     if (previous_parameter)
       strcat(pbuf, " ");
-    if (IsServer(cptr) || (member && IsChanOp(member) && OpLevel(member) == 0) || IsOper(cptr)) {
+    if (IsServer(cptr) || (member && IsChanOpOrHalfOp(member) && OpLevel(member) == 0) || IsOper(cptr)) {
       strcat(pbuf, chptr->mode.upass);
     } else
       strcat(pbuf, "*");
@@ -963,8 +997,8 @@ int compare_member_oplevel(const void *mp1, const void *mp2)
 void send_channel_modes(struct Client *cptr, struct Channel *chptr)
 {
   /* The order in which modes are generated is now mandatory */
-  static unsigned int current_flags[4] =
-      { 0, CHFL_VOICE, CHFL_CHANOP, CHFL_CHANOP | CHFL_VOICE };
+  static unsigned int current_flags[8] =
+      { 0, CHFL_VOICE, CHFL_HALFOP, CHFL_VOICE | CHFL_HALFOP, CHFL_CHANOP, CHFL_CHANOP | CHFL_VOICE, CHFL_CHANOP | CHFL_HALFOP, CHFL_CHANOP | CHFL_VOICE | CHFL_HALFOP };
   int                first = 1;
   int                full  = 1;
   int                flag_cnt = 0;
@@ -1022,7 +1056,7 @@ void send_channel_modes(struct Client *cptr, struct Channel *chptr)
      * Then run 2 times over all opped members (which are ordered
      * by op-level) to also group voice and non-voice together.
      */
-    for (first = 1; flag_cnt < 4; new_mode = 1, ++flag_cnt)
+    for (first = 1; flag_cnt < 8; new_mode = 1, ++flag_cnt)
     {
       while (member)
       {
@@ -1077,6 +1111,8 @@ void send_channel_modes(struct Client *cptr, struct Channel *chptr)
 
            if (HasVoice(member))       /* flag_cnt == 1 or 3 */
              tbuf[loc++] = 'v';
+        if (IsHalfOp(member))
+             tbuf[loc++] = 'h';
            if (IsChanOp(member))       /* flag_cnt == 2 or 3 */
            {
               /* append the absolute value of the oplevel */
@@ -1103,7 +1139,7 @@ void send_channel_modes(struct Client *cptr, struct Channel *chptr)
          }
        }
        /* Go to the next `member'. */
-       if (flag_cnt < 2)
+       if (flag_cnt < 2 || !(current_flags[flag_cnt] & CHFL_CHANOP))
          member = member->next_member;
        else
          member = opped_members[++opped_members_index];
@@ -1114,7 +1150,6 @@ void send_channel_modes(struct Client *cptr, struct Channel *chptr)
       /* Point `member' at the start of the list again. */
       if (flag_cnt == 0)
       {
-       member = chptr->members;
        /* Now, after one loop, we know the number of ops and can
         * allocate the dynamic array with pointer to the ops. */
        opped_members = (struct Membership**)
@@ -1129,10 +1164,12 @@ void send_channel_modes(struct Client *cptr, struct Channel *chptr)
        if (flag_cnt == 1)
          qsort(opped_members, number_of_ops,
                sizeof(struct Membership*), compare_member_oplevel);
-       /* The third and fourth loop run only over the opped members. */
-       member = opped_members[(opped_members_index = 0)];
       }
-
+      if(!(current_flags[flag_cnt+1] & CHFL_CHANOP)) {
+        member = chptr->members;
+      } else
+        member = opped_members[(opped_members_index = 0)];
+      
     } /* loop over 0,+v,+o,+ov */
 
     if (!full)
@@ -1280,36 +1317,12 @@ static void send_ban_list(struct Client* cptr, struct Channel* chptr)
   assert(0 != cptr);
   assert(0 != chptr);
 
-  for (lp = chptr->banlist; lp; lp = lp->next) {
-    if(!(lp->flags & BAN_EXCEPTION))
-        send_reply(cptr, RPL_BANLIST, chptr->chname, lp->banstr,
-                   lp->who, lp->when);
-  }
+  for (lp = chptr->banlist; lp; lp = lp->next)
+    send_reply(cptr, RPL_BANLIST, chptr->chname, lp->banstr, lp->who, lp->when);
 
   send_reply(cptr, RPL_ENDOFBANLIST, chptr->chname);
 }
 
-/** send an exceptionlist to a client for a channel
- *
- * @param cptr Client to send the exceptionlist to.
- * @param chptr        Channel whose exceptionlist to send.
- */
-static void send_exception_list(struct Client* cptr, struct Channel* chptr)
-{
-  struct Ban* lp;
-
-  assert(0 != cptr);
-  assert(0 != chptr);
-
-  for (lp = chptr->banlist; lp; lp = lp->next) {
-    if(lp->flags & BAN_EXCEPTION)
-        send_reply(cptr, RPL_EXCEPTIONLIST, chptr->chname, lp->banstr,
-                   lp->who, lp->when);
-  }
-
-  send_reply(cptr, RPL_ENDOFEXCEPTIONLIST, chptr->chname);
-}
-
 /** Get a channel block, creating if necessary.
  *  Get Channel block for chname (and allocate a new channel
  *  block, if it didn't exists before).
@@ -1611,7 +1624,6 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
     MODE_PERSIST,      'z',
     MODE_NOCOLOUR,  'c',
     MODE_NOCTCP,    'C',
-/*  MODE_EXCEPTION, 'e', */
 /*  MODE_ALTCHAN,   'F', */
 /*  MODE_NOFLOOD,   'f', */
     MODE_ACCESS,    'a',
@@ -1619,6 +1631,7 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
        MODE_NONOTICE,  'N',
        MODE_QUARANTINE,  'Q',
        MODE_AUDITORIUM,  'u',
+    MODE_SSLCHAN,     'S',
     0x0, 0x0
   };
   static ulong64 local_flags[] = {
@@ -1701,23 +1714,28 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
       bufptr_i = &rembuf_i;
     }
 
-    if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_VOICE)) {
+    if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_HALFOP | MODE_VOICE)) {
       tmp = strlen(cli_name(MB_CLIENT(mbuf, i)));
 
       if ((totalbuflen - IRCD_MAX(9, tmp)) <= 0) /* don't overflow buffer */
        MB_TYPE(mbuf, i) |= MODE_SAVE; /* save for later */
       else {
-       bufptr[(*bufptr_i)++] = MB_TYPE(mbuf, i) & MODE_CHANOP ? 'o' : 'v';
+        if((MB_TYPE(mbuf, i) & MODE_CHANOP))
+          bufptr[(*bufptr_i)++] = 'o';
+        else if((MB_TYPE(mbuf, i) & MODE_HALFOP))
+          bufptr[(*bufptr_i)++] = 'h';
+        else
+          bufptr[(*bufptr_i)++] = 'v';
        totalbuflen -= IRCD_MAX(9, tmp) + 1;
       }
-    } else if (MB_TYPE(mbuf, i) & (MODE_BAN | MODE_EXCEPTION | MODE_APASS | MODE_UPASS | MODE_ALTCHAN | MODE_NOFLOOD)) {
+    } else if (MB_TYPE(mbuf, i) & (MODE_BAN | MODE_APASS | MODE_UPASS | MODE_ALTCHAN | MODE_NOFLOOD)) {
       tmp = strlen(MB_STRING(mbuf, i));
 
       if ((totalbuflen - tmp) <= 0) /* don't overflow buffer */
        MB_TYPE(mbuf, i) |= MODE_SAVE; /* save for later */
       else {
        char mode_char;
-       switch(MB_TYPE(mbuf, i) & (MODE_BAN | MODE_EXCEPTION | MODE_APASS | MODE_UPASS | MODE_ALTCHAN | MODE_NOFLOOD))
+       switch(MB_TYPE(mbuf, i) & (MODE_BAN | MODE_APASS | MODE_UPASS | MODE_ALTCHAN | MODE_NOFLOOD))
        {
          case MODE_APASS:
            mode_char = 'A';
@@ -1725,9 +1743,6 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
          case MODE_UPASS:
            mode_char = 'U';
            break;
-      case MODE_EXCEPTION:
-        mode_char = 'e';
-        break;
       case MODE_ALTCHAN:
                mode_char = 'F';
                break;
@@ -1807,11 +1822,11 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
       }
 
       /* deal with clients... */
-      if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_VOICE))
+      if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_HALFOP | MODE_VOICE))
        build_string(strptr, strptr_i, cli_name(MB_CLIENT(mbuf, i)), 0, ' ');
 
       /* deal with bans... */
-      else if (MB_TYPE(mbuf, i) & (MODE_BAN | MODE_EXCEPTION))
+      else if (MB_TYPE(mbuf, i) & (MODE_BAN))
        build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0, ' ');
 
       /* deal with keys... */
@@ -1923,11 +1938,11 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all)
                                      MB_OPLEVEL(mbuf, i));
 
       /* deal with other modes that take clients */
-      else if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_VOICE))
+      else if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_HALFOP | MODE_VOICE))
        build_string(strptr, strptr_i, NumNick(MB_CLIENT(mbuf, i)), ' ');
 
       /* deal with modes that take strings */
-      else if (MB_TYPE(mbuf, i) & (MODE_KEY | MODE_BAN | MODE_EXCEPTION | MODE_APASS | MODE_UPASS | MODE_ALTCHAN | MODE_NOFLOOD))
+      else if (MB_TYPE(mbuf, i) & (MODE_KEY | MODE_BAN | MODE_APASS | MODE_UPASS | MODE_ALTCHAN | MODE_NOFLOOD))
        build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0, ' ');
 
       /*
@@ -2063,7 +2078,7 @@ modebuf_mode(struct ModeBuf *mbuf, ulong64 mode)
           MODE_TOPICLIMIT | MODE_INVITEONLY | MODE_NOPRIVMSGS | MODE_REGONLY |
            MODE_DELJOINS | MODE_WASDELJOINS | MODE_REGISTERED | MODE_PERSIST |
            MODE_NOCOLOUR | MODE_NOCTCP | MODE_NOAMSGS | MODE_NONOTICE | 
-                  MODE_QUARANTINE | MODE_AUDITORIUM);
+                  MODE_QUARANTINE | MODE_AUDITORIUM | MODE_SSLCHAN);
 
   if (!(mode & ~(MODE_ADD | MODE_DEL))) /* don't add empty modes... */
     return;
@@ -2235,13 +2250,13 @@ modebuf_extract(struct ModeBuf *mbuf, char *buf)
     MODE_PERSIST,       'z',
     MODE_NOCOLOUR,  'c',
     MODE_NOCTCP,    'C',
-/*  MODE_EXCEPTION, 'e', */
     MODE_NOAMSGS,   'M',
        MODE_NONOTICE,  'N',
        MODE_QUARANTINE,  'Q',
        MODE_ALTCHAN,    'F',
        MODE_ACCESS,    'a',
        MODE_AUDITORIUM, 'u',
+    MODE_SSLCHAN,    'S',
        MODE_NOFLOOD,   'f',
     0x0, 0x0
   };
@@ -2351,7 +2366,6 @@ mode_invite_clear(struct Channel *chan)
 #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 */
-#define DONE_EXCEPTIONLIST 0x400 /**< We've sent the exception list */
 #define DONE_ALTCHAN   0x800   /**< We've set the altchan */
 #define DONE_ACCESS    0x1000  /**< We've set the access */
 #define DONE_NOFLOOD   0x2000  /**< We've set the noflood options */
@@ -2499,6 +2513,9 @@ mode_parse_access(struct ParseState *state, ulong64 *flag_p)
         return;
     }
     
+    if(feature_bool(FEAT_CHMODE_A_NOSET) && !(state->flags & MODE_PARSE_FORCE)) /* mode can'T be set. */
+        return;
+    
     if (!(state->flags & MODE_PARSE_WIPEOUT) &&
        (!t_access || t_access == state->chptr->mode.access))
       return;
@@ -2567,15 +2584,17 @@ mode_parse_altchan(struct ParseState *state, ulong64 *flag_p)
     if (!IsChannelName(t_str) || !strIsIrcCh(t_str) || strlen(t_str) > IRCD_MIN(CHANNELLEN, feature_int(FEAT_CHANNELLEN)) || t_str[0] == '&') /* only parse it if it's a valid channel name! */
       return;
     
-    struct Channel *chptr;
-    struct Membership *member;
-    if (!(chptr = FindChannel(t_str)))
+    if(!(state->flags & MODE_PARSE_FORCE)) {
+      struct Channel *chptr;
+      struct Membership *member;
+      if (!(chptr = FindChannel(t_str)))
         return;
-    if(!(member = find_member_link(chptr, state->sptr)))
+      if(!(member = find_member_link(chptr, state->sptr)))
         return;
-    if(!IsChanOp(member)) {
+      if(!IsChanOpOrHalfOp(member)) {
         send_notoper(state);
         return;
+      }
     }
     
     if (!(state->flags & MODE_PARSE_WIPEOUT) &&
@@ -2619,10 +2638,13 @@ mode_parse_altchan(struct ParseState *state, ulong64 *flag_p)
     modebuf_mode_string(state->mbuf, state->dir | flag_p[0], t_str, 0);
 
   if (state->flags & MODE_PARSE_SET) {
-    if (state->dir == MODE_DEL) /* remove the old altchan */
+    if (state->dir == MODE_DEL) /* remove the old altchan */
       *state->chptr->mode.altchan = '\0';
-    else
+      state->chptr->mode.mode &= ~flag_p[0];
+    } else {
       ircd_strncpy(state->chptr->mode.altchan, t_str, CHANNELLEN);
+      state->chptr->mode.mode |= flag_p[0];
+    }
   }
 }
 
@@ -2654,8 +2676,9 @@ mode_parse_noflood(struct ParseState *state, ulong64 *flag_p)
         else t_str++; //simply ignore if it's not an opmode
         tmp++;
     }
-    if(tmp[0] == '+' || tmp[0] == '@') {
+    if(tmp[0] == '+' || tmp[0] == '%' || tmp[0] == '@') {
         if(tmp[0] == '+') flags |= FLFL_VOICE;
+        if(tmp[0] == '%') flags |= FLFL_HALFOP;
         if(tmp[0] == '@') flags |= FLFL_CHANOP;
         tmp++;
     }
@@ -2714,21 +2737,25 @@ mode_parse_noflood(struct ParseState *state, ulong64 *flag_p)
     modebuf_mode_string(state->mbuf, state->dir | flag_p[0], t_str, 0);
 
   if (state->flags & MODE_PARSE_SET) {
-    if (state->dir == MODE_DEL) /* remove the old noflood */
+    if (state->dir == MODE_DEL) /* remove the old noflood */
       *state->chptr->mode.noflood = '\0';
-    else
+      state->chptr->mode.mode &= ~flag_p[0];
+    } else {
       ircd_strncpy(state->chptr->mode.noflood, t_str, CHANNELLEN);
+      state->chptr->mode.mode |= flag_p[0];
+    }
   }
   
   if (state->dir == MODE_ADD) {
     unsigned int noflood_value = time;
     noflood_value <<= 10;
     noflood_value |= count;
-    noflood_value <<= 3;
+    noflood_value <<= 4;
     noflood_value |= flags;
     state->chptr->mode.noflood_value = noflood_value;
   } else {
     //removed the mode so free all flood objects
+    state->chptr->mode.noflood_value = 0;
     struct Membership *member;
     for(member = state->chptr->members; member; member = member->next_member) {
         struct MemberFlood *floodnode;
@@ -3271,13 +3298,13 @@ static void audit_chan_users(struct ParseState *state, ulong64 *flag_p) {
        struct Membership *member;
        if (state->dir == MODE_ADD) {
                for(member = state->chptr->members; member; member = member->next_member) {
-                       if(!IsChanOp(member) && !HasVoice(member)) {
+                       if(!IsChanOpOrHalfOp(member) && !HasVoice(member)) {
                                sendcmdto_channel_butserv_butone(member->user, CMD_PART, member->channel, member->user, SKIP_OPS, "%H :%s", member->channel, "mode +u set.");
                        }
                }
        } else {
                for(member = state->chptr->members; member; member = member->next_member) {
-                       if(!IsChanOp(member) && !HasVoice(member)) {
+                       if(!IsChanOpOrHalfOp(member) && !HasVoice(member)) {
                                sendcmdto_channel_butserv_butone(member->user, CMD_JOIN, member->channel, member->user, SKIP_OPS, ":%H", member->channel);
                        }
                }
@@ -3295,19 +3322,9 @@ mode_parse_ban(struct ParseState *state, ulong64 *flag_p)
   struct Ban *ban, *newban;
 
   if (state->parc <= 0) { /* Not enough args, send ban list */
-    if (MyUser(state->sptr)) {
-      if (*flag_p == MODE_EXCEPTION) {
-        if (!(state->done & DONE_EXCEPTIONLIST)) {
-          send_exception_list(state->sptr, state->chptr);
-          state->done |= DONE_EXCEPTIONLIST;
-        }
-      }
-      else {
-        if (!(state->done & DONE_BANLIST)) {
-          send_ban_list(state->sptr, state->chptr);
-          state->done |= DONE_BANLIST;
-        }
-      }
+    if (MyUser(state->sptr) && !(state->done & DONE_BANLIST)) {
+      send_ban_list(state->sptr, state->chptr);
+      state->done |= DONE_BANLIST;
     }
     return;
   }
@@ -3384,8 +3401,7 @@ mode_process_bans(struct ParseState *state)
     } else if (ban->flags & BAN_DEL) { /* Deleted a ban? */
       char *bandup;
       DupString(bandup, ban->banstr);
-      modebuf_mode_string(state->mbuf, MODE_DEL | ((ban->flags & BAN_EXCEPTION) ? MODE_EXCEPTION : MODE_BAN),
-                         bandup, 1);
+      modebuf_mode_string(state->mbuf, MODE_DEL | MODE_BAN, bandup, 1);
 
       if (state->flags & MODE_PARSE_SET) { /* Ok, make it take effect */
        if (prevban) /* clip it out of the list... */
@@ -3424,14 +3440,13 @@ mode_process_bans(struct ParseState *state)
           char *bandup;
          /* add the ban to the buffer */
           DupString(bandup, ban->banstr);
-          modebuf_mode_string(state->mbuf, MODE_ADD | ((ban->flags & BAN_EXCEPTION) ? MODE_EXCEPTION : MODE_BAN),
-                             bandup, 1);
+          modebuf_mode_string(state->mbuf, MODE_ADD | MODE_BAN, bandup, 1);
 
          if (state->flags & MODE_PARSE_SET) { /* create a new ban */
            newban = make_ban(ban->banstr);
             strcpy(newban->who, ban->who);
            newban->when = ban->when;
-           newban->flags = ban->flags & (BAN_IPMASK | BAN_EXCEPTION);
+           newban->flags = ban->flags & BAN_IPMASK;
 
            newban->next = state->chptr->banlist; /* and link it in */
            state->chptr->banlist = newban;
@@ -3484,7 +3499,7 @@ mode_parse_client(struct ParseState *state, ulong64 *flag_p)
     if (colon != NULL) {
       *colon++ = '\0';
       req_oplevel = atoi(colon);
-      if (*flag_p == CHFL_VOICE || state->dir == MODE_DEL) {
+      if (*flag_p == CHFL_VOICE || *flag_p == CHFL_HALFOP || state->dir == MODE_DEL) {
         /* Ignore the colon and its argument. */
       } else if (!(state->flags & MODE_PARSE_FORCE)
           && state->member
@@ -3499,6 +3514,10 @@ mode_parse_client(struct ParseState *state, ulong64 *flag_p)
       } else if (req_oplevel <= MAXOPLEVEL)
         oplevel = req_oplevel;
     }
+    if(*flag_p == CHFL_CHANOP && state->member && !IsChanOp(state->member)) {
+        send_notoper(state);
+        return;
+    }
     /* find client we're manipulating */
     acptr = find_chasing(state->sptr, t_str, NULL);
   } else {
@@ -3647,12 +3666,12 @@ mode_process_clients(struct ParseState *state)
         if (IsDelayedJoin(member) && !IsZombie(member))
           RevealDelayedJoin(member);
        member->status |= (state->cli_change[i].flag &
-                          (MODE_CHANOP | MODE_VOICE));
+                          (MODE_CHANOP | MODE_HALFOP | MODE_VOICE));
        if (state->cli_change[i].flag & MODE_CHANOP)
          ClearDeopped(member);
       } else
        member->status &= ~(state->cli_change[i].flag &
-                           (MODE_CHANOP | MODE_VOICE));
+                           (MODE_CHANOP | MODE_HALFOP | MODE_VOICE));
     }
 
     /* accumulate the change */
@@ -3667,21 +3686,21 @@ mode_process_clients(struct ParseState *state)
                } else if(!(member->status & CHFL_VOICED_OR_OPPED) && user_visible) {
                        sendcmdto_channel_butserv_butone(member->user, CMD_PART, member->channel, member->user, SKIP_OPS, "%H :%s", member->channel, "user deoped/devoiced on a +u channel.");
                }
-               if(MyUser(member->user) && (state->cli_change[i].flag & MODE_CHANOP)) {
+               if(MyUser(member->user) && (state->cli_change[i].flag & (MODE_CHANOP | MODE_HALFOP))) {
                        //do_names(member->user, member->channel, NAMES_ALL|NAMES_EON|((member->status & MODE_CHANOP) ? 0 : NAMES_OPS));
                        //this is not working for all users :(  so we have to send join/part events
                        struct Membership *member2;
                        if (state->cli_change[i].flag & MODE_ADD) {
                                //JOIN events
                                for(member2 = state->chptr->members; member2; member2 = member2->next_member) {
-                                       if(!IsChanOp(member2) && !HasVoice(member2)) {
+                                       if(!IsChanOpOrHalfOp(member2) && !HasVoice(member2)) {
                                                sendcmdto_one(member2->user, CMD_JOIN, member->user, ":%H", member->channel);
                                        }
                                }
                        } else {
                                //PART events
                                for(member2 = state->chptr->members; member2; member2 = member2->next_member) {
-                                       if(!IsChanOp(member2) && !HasVoice(member2) && member != member2) {
+                                       if(!IsChanOpOrHalfOp(member2) && !HasVoice(member2) && member != member2) {
                                                sendcmdto_one(member2->user, CMD_PART, member->user, "%H :%s", member->channel, "invisible user on +u channel.");
                                        }
                                }
@@ -3755,6 +3774,7 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
 {
   static ulong64 chan_flags[] = {
     MODE_CHANOP,       'o',
+    MODE_HALFOP,    'h',
     MODE_VOICE,                'v',
     MODE_PRIVATE,      'p',
     MODE_SECRET,       's',
@@ -3774,13 +3794,13 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
     MODE_PERSIST,       'z',
     MODE_NOCOLOUR,      'c',
     MODE_NOCTCP,        'C',
-/*    MODE_EXCEPTION,     'e',*/
     MODE_NOAMSGS,       'M',
        MODE_NONOTICE,      'N',
        MODE_QUARANTINE,      'Q',
        MODE_ALTCHAN,        'F',
     MODE_ACCESS,        'a',
        MODE_AUDITORIUM,    'u',
+    MODE_SSLCHAN,       'S',
        MODE_NOFLOOD,       'f',
     MODE_ADD,          '+',
     MODE_DEL,          '-',
@@ -3871,9 +3891,6 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
         if (IsServer(cptr) || feature_bool(FEAT_OPLEVELS))
        mode_parse_upass(&state, flag_p);
        break;
-
-/*      case 'e':
-        if(MyUser(state.sptr) && !(state.flags & MODE_PARSE_FORCE) && !feature_bool(FEAT_EXCEPT_ENABLE)) break;*/
       case 'b': /* deal with bans */
        mode_parse_ban(&state, flag_p);
         break;
@@ -3886,7 +3903,10 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
       case 'v':
        mode_parse_client(&state, flag_p);
        break;
-
+      case 'h':
+        if (IsServer(cptr) || feature_bool(FEAT_HALFOP))
+    mode_parse_client(&state, flag_p);
+       break;
       case 'z': /* remote clients are allowed to change +z */
         if(!MyUser(state.sptr) || (state.flags & MODE_PARSE_FORCE))
           mode_parse_mode(&state, flag_p);
@@ -4329,7 +4349,7 @@ int ext_amsg_block(struct Client *cptr, struct Channel *chptr, const char *msg)
     int p_pos = 0;
     int is_visible = 1, is_ccode = 0, i = 0, j = 0;
     char codes[5];
-    for(i = 0; p != '\n'; p = stripped_message[++i]) {
+    for(i = 0; p != '\n' && p != 0; p = stripped_message[++i]) {
       if(p == 3) {
         j = 0;
         is_ccode = 1;
@@ -4410,19 +4430,21 @@ int ext_amsg_block(struct Client *cptr, struct Channel *chptr, const char *msg)
  * --pk910 2011/7/1
  */
 int ext_noflood_block(struct Client *cptr, struct Channel *chptr) {
-  if(chptr->mode.noflood == NULL || !chptr->mode.noflood) return 0;
+  if(!*chptr->mode.noflood) return 0;
   struct Membership *member = find_member_link(chptr, cptr);
   if(!member) return 0; //TODO: we've no check for -n channels implemented, yet
   //check if this user is really affected by +f
-  unsigned int flags = (chptr->mode.noflood_value & 0x00000007);        //0000 0000 0000 0000 0000 0000 0000 0111 = 0x00000007 >> 0
-  unsigned int count = (chptr->mode.noflood_value & 0x00001ff8) >> 3;   //0000 0000 0000 0000 0001 1111 1111 1000 = 0x00001ff8 >> 3
-  int time           = (chptr->mode.noflood_value & 0x07ffe000) >> 13;  //0000 0111 1111 1111 1110 0000 0000 0000 = 0x07ffe000 >> 13
+  unsigned int flags = (chptr->mode.noflood_value & 0x0000000f);        //0000 0000 0000 0000 0000 0000 0000 1111 = 0x0000000f >> 0
+  unsigned int count = (chptr->mode.noflood_value & 0x00002ff0) >> 4;   //0000 0000 0000 0000 0011 1111 1111 0000 = 0x00002ff0 >> 4
+  int time           = (chptr->mode.noflood_value & 0x0fffc000) >> 14;  //0000 1111 1111 1111 1100 0000 0000 0000 = 0x0fffc000 >> 14
   if(count == 0 || time == 0) return 0;
   if(!(flags & FLFL_NOFLOOD) && HasPriv(cptr, PRIV_FLOOD))
     return 0;
   if(!(flags & FLFL_CHANOP) && (member->status & CHFL_CHANOP)) 
     return 0;
-  if(!(flags & (FLFL_CHANOP | FLFL_VOICE)) && (member->status & CHFL_VOICE)) 
+  if(!(flags & (FLFL_CHANOP | FLFL_HALFOP)) && (member->status & CHFL_HALFOP)) 
+    return 0;
+  if(!(flags & (FLFL_CHANOP | FLFL_HALFOP | FLFL_VOICE)) && (member->status & CHFL_VOICE)) 
     return 0;
   int floodcount = 0;
   struct MemberFlood *floodnode, *prev_floodnode;