added MODE_HALFOP (+h, %)
[ircu2.10.12-pk.git] / ircd / channel.c
index e22935c4c10c585a0c8ef348938a1a7dce68dd8b..7e9bb09d14b05c1fb3a70fad2e850281f2587940 100644 (file)
@@ -656,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.
@@ -923,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, "*");
@@ -943,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, "*");
@@ -979,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;
@@ -1038,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)
       {
@@ -1093,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 */
@@ -1119,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];
@@ -1130,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**)
@@ -1145,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)
@@ -1693,13 +1714,18 @@ 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_APASS | MODE_UPASS | MODE_ALTCHAN | MODE_NOFLOOD)) {
@@ -1796,7 +1822,7 @@ 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... */
@@ -1912,7 +1938,7 @@ 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 */
@@ -2562,7 +2588,7 @@ mode_parse_altchan(struct ParseState *state, ulong64 *flag_p)
         return;
       if(!(member = find_member_link(chptr, state->sptr)))
         return;
-      if(!IsChanOp(member)) {
+      if(!IsChanOpOrHalfOp(member)) {
         send_notoper(state);
         return;
       }
@@ -3268,13 +3294,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);
                        }
                }
@@ -3469,7 +3495,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
@@ -3484,6 +3510,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 {
@@ -3632,12 +3662,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 */
@@ -3652,21 +3682,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.");
                                        }
                                }
@@ -3740,6 +3770,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',
@@ -3868,7 +3899,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);