added MODE_HALFOP (+h, %)
authorpk910 <philipp@zoelle1.de>
Sun, 18 Dec 2011 11:57:14 +0000 (12:57 +0100)
committerpk910 <philipp@zoelle1.de>
Sun, 18 Dec 2011 12:38:34 +0000 (13:38 +0100)
14 files changed:
include/channel.h
include/ircd_features.h
include/supported.h
ircd/channel.c
ircd/ircd_features.c
ircd/m_burst.c
ircd/m_clearmode.c
ircd/m_invite.c
ircd/m_kick.c
ircd/m_mode.c
ircd/m_names.c
ircd/m_topic.c
ircd/m_whois.c
ircd/whocmds.c

index cad2de035ef81d6db02b0d4f65fea3f69f5fb434..63cb19c8709daddeaafa14cf3de4827a9d0f8ef2 100644 (file)
@@ -66,10 +66,12 @@ typedef signed long long long64;
 #define ChannelExists(n)        (0 != FindChannel(n))
 
 #define CHFL_CHANOP             0x0001  /**< Channel operator */
-#define CHFL_VOICE              0x0002  /**< the power to speak */
-#define CHFL_DEOPPED            0x0004  /**< Is de-opped by a server */
-#define CHFL_SERVOPOK           0x0008  /**< Server op allowed */
-#define CHFL_ZOMBIE             0x0010  /**< Kicked from channel */
+#define CHFL_HALFOP             0x0002  /**< Channel operator */
+#define CHFL_VOICE              0x0004  /**< the power to speak */
+
+#define CHFL_DEOPPED            0x0010  /**< Is de-opped by a server */
+#define CHFL_SERVOPOK           0x0020  /**< Server op allowed */
+#define CHFL_ZOMBIE             0x0040  /**< Kicked from channel */
 #define CHFL_BURST_JOINED       0x0100  /**< Just joined by net.junction */
 #define CHFL_BANVALID           0x0800  /**< CHFL_BANNED bit is valid */
 #define CHFL_BANNED             0x1000  /**< Channel member is banned */
@@ -82,50 +84,55 @@ typedef signed long long long64;
                                        /**, In oob BURST, but was already 
                                         * joined and voiced 
                                         */
-#define CHFL_CHANNEL_MANAGER   0x10000 /**< Set when creating channel or using 
+#define CHFL_BURST_ALREADY_HALFOPPED   0x10000  
+                                       /**, In oob BURST, but was already 
+                                        * joined and voiced 
+                                        */
+#define CHFL_CHANNEL_MANAGER   0x40000 /**< Set when creating channel or using 
                                         * Apass 
                                         */
-#define CHFL_USER_PARTING       0x20000 /**< User is already parting that 
+#define CHFL_USER_PARTING       0x80000 /**< User is already parting that 
                                         * channel 
                                         */
-#define CHFL_DELAYED            0x40000 /**< User's join message is delayed */
-#define CHFL_INVISIBLE            0x80000 /**< User's join message is delayed */
+#define CHFL_DELAYED            0x100000 /**< User's join message is delayed */
+#define CHFL_INVISIBLE            0x200000 /**< User's join message is delayed */
 
-#define CHFL_OVERLAP         (CHFL_CHANOP | CHFL_VOICE)
+#define CHFL_OVERLAP         (CHFL_CHANOP | CHFL_HALFOP | CHFL_VOICE)
 #define CHFL_BANVALIDMASK    (CHFL_BANVALID | CHFL_BANNED)
-#define CHFL_VOICED_OR_OPPED (CHFL_CHANOP | CHFL_VOICE)
+#define CHFL_VOICED_OR_OPPED (CHFL_CHANOP | CHFL_HALFOP | CHFL_VOICE)
 
 /* Channel Visibility macros */
 
 #define MODE_NULL           0
 #define MODE_CHANOP         CHFL_CHANOP        /**< +o Chanop */
+#define MODE_HALFOP         CHFL_HALFOP /**< +h Halfop */
 #define MODE_VOICE          CHFL_VOICE /**< +v Voice */
-#define MODE_PRIVATE        0x00004            /**< +p Private */
-#define MODE_SECRET         0x00008            /**< +s Secret */
-#define MODE_MODERATED      0x00010            /**< +m Moderated */
-#define MODE_TOPICLIMIT     0x00020            /**< +t Topic Limited */
-#define MODE_INVITEONLY     0x00040            /**< +i Invite only */
-#define MODE_NOPRIVMSGS     0x00080            /**< +n No Private Messages */
-#define MODE_KEY            0x00100            /**< +k Keyed */
-#define MODE_BAN            0x00200            /**< +b Ban */
-#define MODE_LIMIT          0x00400            /**< +l Limit */
-#define MODE_REGONLY        0x00800            /**< Only +r users may join */
-#define MODE_DELJOINS       0x01000            /**< New join messages are delayed */
-#define MODE_REGISTERED     0x02000            /**< Channel marked as registered (for future semantic expansion) */
-#define MODE_PERSIST        0x04000            /**< +z persistant channel */
-#define MODE_NOCOLOUR       0x08000            /**< no ANSI color codes */
-#define MODE_NOCTCP         0x10000            /**< no channel CTCPs */
-#define MODE_SAVE           0x20000            /**< save this mode-with-arg 'til later */
-#define MODE_FREE           0x40000        /**< string needs to be passed to  MyFree() */
-#define MODE_BURSTADDED     0x80000            /**< channel was created by a BURST */
-#define MODE_UPASS         0x100000
-#define MODE_APASS         0x200000
-#define MODE_WASDELJOINS   0x400000            /**< Not DELJOINS, but some joins  pending */
-
-#define MODE_NOAMSGS      0x1000000            /**< No multi target messages */
-#define MODE_NONOTICE     0x2000000            /**< No channel notices */
-#define MODE_QUARANTINE   0x4000000            /**< No channel notices */
-#define MODE_ALTCHAN      0x8000000            /**< Alternative channel */
+
+#define MODE_PRIVATE           0x10            /**< +p Private */
+#define MODE_SECRET            0x20            /**< +s Secret */
+#define MODE_MODERATED         0x40            /**< +m Moderated */
+#define MODE_TOPICLIMIT        0x80            /**< +t Topic Limited */
+#define MODE_INVITEONLY       0x100            /**< +i Invite only */
+#define MODE_NOPRIVMSGS       0x200            /**< +n No Private Messages */
+#define MODE_KEY              0x400            /**< +k Keyed */
+#define MODE_BAN              0x800            /**< +b Ban */
+#define MODE_LIMIT           0x1000            /**< +l Limit */
+#define MODE_REGONLY         0x2000            /**< Only +r users may join */
+#define MODE_DELJOINS        0x4000            /**< New join messages are delayed */
+#define MODE_REGISTERED      0x8000            /**< Channel marked as registered (for future semantic expansion) */
+#define MODE_PERSIST        0x10000            /**< +z persistant channel */
+#define MODE_NOCOLOUR       0x20000            /**< no ANSI color codes */
+#define MODE_NOCTCP         0x40000            /**< no channel CTCPs */
+#define MODE_SAVE           0x80000            /**< save this mode-with-arg 'til later */
+#define MODE_FREE          0x100000        /**< string needs to be passed to  MyFree() */
+#define MODE_BURSTADDED    0x200000            /**< channel was created by a BURST */
+#define MODE_UPASS         0x400000
+#define MODE_APASS         0x800000
+#define MODE_WASDELJOINS  0x1000000            /**< Not DELJOINS, but some joins  pending */
+#define MODE_NOAMSGS      0x2000000            /**< No multi target messages */
+#define MODE_NONOTICE     0x4000000            /**< No channel notices */
+#define MODE_QUARANTINE   0x8000000            /**< No channel notices */
+#define MODE_ALTCHAN     0x10000000            /**< Alternative channel */
 #define MODE_DEL         0x20000000
 #define MODE_ADD         0x40000000
 #define MODE_ACCESS     0x100000000LLU /**< ChanServ access */
@@ -134,12 +141,12 @@ typedef signed long long long64;
 #define MODE_SSLCHAN    0x800000000LLU /**< +S SSL Channel */
 /** mode flags which take another parameter (With PARAmeterS)
  */
-#define MODE_WPARAS     (MODE_CHANOP|MODE_VOICE|MODE_BAN|MODE_KEY|MODE_LIMIT|MODE_APASS|MODE_UPASS|MODE_ALTCHAN|MODE_ACCESS|MODE_NOFLOOD)
+#define MODE_WPARAS     (MODE_CHANOP|MODE_HALFOP|MODE_VOICE|MODE_BAN|MODE_KEY|MODE_LIMIT|MODE_APASS|MODE_UPASS|MODE_ALTCHAN|MODE_ACCESS|MODE_NOFLOOD)
 
 /** Available Channel modes */
-#define infochanmodes feature_bool(FEAT_OPLEVELS) ? "AcCbiklmMnNopsStuUvrDRzQu" : "cCbiklmMnNopsStuvrDRzQu"
+#define infochanmodes feature_bool(FEAT_OPLEVELS) ? "AcCbhiklmMnNopsStuUvrDRzQu" : "cCbhiklmMnNopsStuvrDRzQu"
 /** Available Channel modes that take parameters */
-#define infochanmodeswithparams feature_bool(FEAT_OPLEVELS) ? "AbfkloUvFa" : "bfklovFa"
+#define infochanmodeswithparams feature_bool(FEAT_OPLEVELS) ? "AbfhkloUvFa" : "bfhklovFa"
 
 #define HoldChannel(x)          (!(x))
 /** name invisible */
@@ -213,8 +220,9 @@ struct Membership {
 };
 
 #define FLFL_CHANOP  0x01
-#define FLFL_VOICE   0x02
-#define FLFL_NOFLOOD  0x04
+#define FLFL_HALFOP   0x02
+#define FLFL_VOICE   0x04
+#define FLFL_NOFLOOD  0x08
 
 struct MemberFlood {
   struct MemberFlood* next_memberflood;
@@ -230,6 +238,8 @@ struct MemberFlood {
 #define IsBanValid(x)       ((x)->status & CHFL_BANVALID)
 #define IsChanOp(x)         ((x)->status & CHFL_CHANOP)
 #define OpLevel(x)          ((x)->oplevel)
+#define IsHalfOp(x)         ((x)->status & CHFL_HALFOP)
+#define IsChanOpOrHalfOp(x) ((x)->status & (CHFL_CHANOP | CHFL_HALFOP))
 #define HasVoice(x)         ((x)->status & CHFL_VOICE)
 #define IsServOpOk(x)       ((x)->status & CHFL_SERVOPOK)
 #define IsBurstJoined(x)    ((x)->status & CHFL_BURST_JOINED)
@@ -423,6 +433,7 @@ extern void remove_user_from_channel(struct Client *sptr, struct Channel *chptr)
 extern void remove_user_from_all_channels(struct Client* cptr);
 
 extern int is_chan_op(struct Client *cptr, struct Channel *chptr);
+extern int is_halfop(struct Client *cptr, struct Channel *chptr);
 extern int is_zombie(struct Client *cptr, struct Channel *chptr);
 extern int has_voice(struct Client *cptr, struct Channel *chptr);
 /*
index cb14044cbea274a19beb15b7fc46511a93fa89b4..1b260172eb3af0793b6268785febf303cff32395 100644 (file)
@@ -179,6 +179,8 @@ enum Feature {
   FEAT_CHMODE_A_TARGET,
   FEAT_CHMODE_F_ENABLE,
   
+  FEAT_HALFOP,
+  
   FEAT_LAST_F
 };
 
index 31b044d850ee67731f0ae74dc014386c2faf077b..5bbd5be1f3d30c491e9ee2ee49fa6978da345a79 100644 (file)
@@ -64,7 +64,7 @@
 
 #define FEATURESVALUES2 NICKLEN, TOPICLEN, AWAYLEN, TOPICLEN, \
                         feature_int(FEAT_CHANNELLEN), CHANNELLEN, \
-                        (feature_bool(FEAT_LOCAL_CHANNELS) ? "#&" : "#"), "(ov)@+", "@+", \
+                        (feature_bool(FEAT_LOCAL_CHANNELS) ? "#&" : "#"), "(ohv)@%+", "@%+", \
                         (feature_bool(FEAT_OPLEVELS) ? "b,AkU,alfF,cCimMnNprstuDdRz" : "b,k,alfF,cCimMnNprstuDdRz"), \
                         "rfc1459", feature_str(FEAT_NETWORK)
 
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);
index 7a673619404f59b962fa7b9b85d3ca73b1a92516..8cc5037756c9e14b934dfc30cc7b083305c00af2 100644 (file)
@@ -443,6 +443,7 @@ static struct FeatureDesc {
   F_B(CHMODE_A_ENABLE, 0, 0, 0),
   F_S(CHMODE_A_TARGET, FEAT_NULL, 0, 0),
   F_B(CHMODE_F_ENABLE, 0, 0, 0),
+  F_B(HALFOP, 0, 0, 0),
   
 #undef F_S
 #undef F_B
index 15d70344ab600d8c2e10eb9a9b104534036ff036..71fac547ad6faa1d596cb2de64f3b13f543ec2c4 100644 (file)
@@ -505,6 +505,14 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
                  current_mode = (current_mode & ~CHFL_DELAYED) | CHFL_VOICE;
                  oplevel = -1; /* subsequent digits are an absolute op-level value. */
                 }
+        else if (*ptr == 'h') { /* has halfop status */
+                 if (current_mode_needs_reset) {
+                    current_mode = base_mode;
+                   current_mode_needs_reset = 0;
+                 }
+                 current_mode = (current_mode & ~CHFL_DELAYED) | CHFL_HALFOP;
+                 oplevel = -1; /* subsequent digits are an absolute op-level value. */
+                }
         else if (*ptr == 'i') { /* has voice status */
                  if (current_mode_needs_reset) {
                     current_mode = base_mode;
@@ -579,6 +587,8 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
               Remember the current mode. */
            if (member->status & CHFL_CHANOP)
              member->status |= CHFL_BURST_ALREADY_OPPED;
+        if (member->status & CHFL_HALFOP)
+             member->status |= CHFL_BURST_ALREADY_HALFOPPED;
            if (member->status & CHFL_VOICE)
              member->status |= CHFL_BURST_ALREADY_VOICED;
            /* Synchronize with the burst. */
@@ -613,15 +623,19 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
       if (member->status & CHFL_BURST_JOINED) { /* joined during burst */
        if ((member->status & CHFL_CHANOP) && !(member->status & CHFL_BURST_ALREADY_OPPED))
          modebuf_mode_client(mbuf, MODE_ADD | CHFL_CHANOP, member->user, OpLevel(member));
+    if ((member->status & CHFL_HALFOP) && !(member->status & CHFL_BURST_ALREADY_HALFOPPED))
+         modebuf_mode_client(mbuf, MODE_ADD | CHFL_HALFOP, member->user, OpLevel(member));
        if ((member->status & CHFL_VOICE) && !(member->status & CHFL_BURST_ALREADY_VOICED))
          modebuf_mode_client(mbuf, MODE_ADD | CHFL_VOICE, member->user, OpLevel(member));
       } else if (parse_flags & MODE_PARSE_WIPEOUT) { /* wipeout old ops */
        if (member->status & CHFL_CHANOP)
          modebuf_mode_client(mbuf, MODE_DEL | CHFL_CHANOP, member->user, OpLevel(member));
+    if (member->status & CHFL_HALFOP)
+         modebuf_mode_client(mbuf, MODE_DEL | CHFL_HALFOP, member->user, OpLevel(member));
        if (member->status & CHFL_VOICE)
          modebuf_mode_client(mbuf, MODE_DEL | CHFL_VOICE, member->user, OpLevel(member));
        member->status = (member->status
-                          & ~(CHFL_CHANNEL_MANAGER | CHFL_CHANOP | CHFL_VOICE))
+                          & ~(CHFL_CHANNEL_MANAGER | CHFL_CHANOP | CHFL_HALFOP | CHFL_VOICE))
                         | CHFL_DEOPPED;
       }
     }
index 4d0f17924ad13922f88ee67d461fa07a7abf21fc..26955743a3b84318bcf8e7456452172cd8a813c3 100644 (file)
@@ -113,6 +113,7 @@ do_clearmode(struct Client *cptr, struct Client *sptr, struct Channel *chptr,
   static int flags[] = {
     MODE_CHANOP,       'o',
     MODE_VOICE,                'v',
+    MODE_HALFOP,    'h',
     MODE_PRIVATE,      'p',
     MODE_SECRET,       's',
     MODE_MODERATED,    'm',
@@ -193,7 +194,7 @@ do_clearmode(struct Client *cptr, struct Client *sptr, struct Channel *chptr,
   }
 
   /* Deal with users on the channel */
-  if (del_mode & (MODE_BAN | MODE_CHANOP | MODE_VOICE))
+  if (del_mode & (MODE_BAN | MODE_CHANOP | MODE_HALFOP | MODE_VOICE))
     for (member = chptr->members; member; member = member->next_member) {
       if (IsZombie(member)) /* we ignore zombies */
        continue;
@@ -206,6 +207,12 @@ do_clearmode(struct Client *cptr, struct Client *sptr, struct Channel *chptr,
        modebuf_mode_client(&mbuf, MODE_DEL | MODE_CHANOP, member->user, MAXOPLEVEL + 1);
        member->status &= ~CHFL_CHANOP;
       }
+      
+      /* Drop halfop */
+      if (IsHalfOp(member) && del_mode & MODE_HALFOP) {
+       modebuf_mode_client(&mbuf, MODE_DEL | MODE_HALFOP, member->user, MAXOPLEVEL + 1);
+       member->status &= ~CHFL_HALFOP;
+      }
 
       /* Drop voice */
       if (HasVoice(member) && del_mode & MODE_VOICE) {
index 9308007cd52b9703ea15e9ed2c0cc9afc9a5c960..2e9cc0570061bcfb82edee4e98fdedd9a23e8069 100644 (file)
@@ -159,7 +159,7 @@ int m_invite(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
     return 0;
   }
 
-  if (!is_chan_op(sptr, chptr)) {
+  if (!is_chan_op(sptr, chptr) && !is_halfop(sptr, chptr)) {
     send_reply(sptr, ERR_CHANOPRIVSNEEDED, chptr->chname);
     return 0;
   }
index f2a67d58754030540dc8b7edba39e5243c28cdbd..447329a90bf94a11b68a86d87df0be2b550856cf 100644 (file)
@@ -124,7 +124,7 @@ int m_kick(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
     return send_reply(sptr, ERR_NOSUCHCHANNEL, name);
 
   if (!(member2 = find_member_link(chptr, sptr)) || IsZombie(member2)
-      || (!IsChanOp(member2) && !(IsNetServ(sptr) && IsSecurityServ(sptr) && IsChannelService(sptr))))
+      || (!IsChanOpOrHalfOp(member2) && !(IsNetServ(sptr) && IsSecurityServ(sptr) && IsChannelService(sptr))))
     return send_reply(sptr, ERR_CHANOPRIVSNEEDED, name);
 
   if (!(who = find_chasing(sptr, parv[2], 0)))
@@ -141,6 +141,10 @@ int m_kick(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
   /* check if kicked user is actually on the channel */
   if (!(member = find_member_link(chptr, who)) || IsZombie(member) || (IsInvisibleJoin(member) && IsDelayedJoin(member)))
     return send_reply(sptr, ERR_USERNOTINCHANNEL, cli_name(who), chptr->chname);
+    
+  /* Don't allow oped users to be kicked by halfops */
+  if (IsChanOp(member) && !IsChanOp(member2) && who != sptr)
+    return send_reply(sptr, ERR_CHANOPRIVSNEEDED, name);
 
   /* Don't allow to kick member with a higher op-level,
    * or members with the same op-level unless both are MAXOPLEVEL.
index 794cc0404a3ae25500b4fd0733b6fa6f82c77f45..b4690418ac287ac1c64794a5c251a1d8a2f9bd10 100644 (file)
@@ -160,7 +160,7 @@ m_mode(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
     destruct_nonpers_channel(chptr);
     return 0;
   } else {
-       if (!member || !IsChanOp(member)) {
+       if (!member || !IsChanOpOrHalfOp(member)) {
       mode_parse(0, cptr, sptr, chptr, parc - 2, parv + 2,
                 (member ? MODE_PARSE_NOTOPER : MODE_PARSE_NOTMEMBER), member);
     return 0;
@@ -236,7 +236,7 @@ ms_mode(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
                NULL);
   } else {
     /* services don't cause hack messages */
-    if(!IsChannelService(sptr) && (!(member = find_member_link(chptr, sptr)) || !IsChanOp(member))) {
+    if(!IsChannelService(sptr) && (!(member = find_member_link(chptr, sptr)) || !IsChanOpOrHalfOp(member))) {
       modebuf_init(&mbuf, sptr, cptr, chptr,
                   (MODEBUF_DEST_SERVER |  /* Send mode to server */
                    MODEBUF_DEST_HACK2  |  /* Send a HACK(2) message */
index 26666a60931417e986b8ff2b6abff781be83a153..9e67b325a3191c175e33608992a06d599da6eb5b 100644 (file)
@@ -167,7 +167,7 @@ void do_names(struct Client* sptr, struct Channel* chptr, int filter)
        if (IsInvisibleJoin(member) && member->user != sptr)
                continue;
        
-       if (!IsChanOp(member) && !HasVoice(member) && member->user != sptr && (filter & NAMES_OPS))
+       if (!IsChanOpOrHalfOp(member) && !HasVoice(member) && member->user != sptr && (filter & NAMES_OPS))
                continue;
 
     if (needs_space)
@@ -177,6 +177,8 @@ void do_names(struct Client* sptr, struct Channel* chptr, int filter)
       buf[idx++] = '!';
     else if (IsChanOp(member))
       buf[idx++] = '@';
+    else if (IsHalfOp(member))
+      buf[idx++] = '%';
     else if (HasVoice(member))
       buf[idx++] = '+';
     strcpy(buf + idx, cli_name(c2ptr));
index 37ae26f41512dedf6dce634f4ca12a139d60f784..d967e049243fa862051941bc8d705fe0b5501a64 100644 (file)
@@ -142,7 +142,7 @@ int m_topic(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
                   chptr->topic_time);
       }
     }
-    else if ((chptr->mode.mode & MODE_TOPICLIMIT) && (!is_chan_op(sptr, chptr) && !(IsNetServ(sptr) && IsSecurityServ(sptr) && IsChannelService(sptr))))
+    else if ((chptr->mode.mode & MODE_TOPICLIMIT) && (!is_chan_op(sptr, chptr) && !is_halfop(sptr, chptr) && !(IsNetServ(sptr) && IsSecurityServ(sptr) && IsChannelService(sptr))))
       send_reply(sptr, ERR_CHANOPRIVSNEEDED, chptr->chname);
     else if (!client_can_send_to_channel(sptr, chptr, 1))
       send_reply(sptr, ERR_CANNOTSENDTOCHAN, chptr->chname);
index 01ee78e4371b862ec73c73294b09799590a7de0e..68910066bb0e451f7ac21bd6467fa367f411f71d 100644 (file)
@@ -181,6 +181,8 @@ static void do_whois(struct Client* sptr, struct Client *acptr, int parc)
          *(buf + len++) = '<';
        else if (IsChanOp(chan))
          *(buf + len++) = '@';
+       else if (IsHalfOp(chan))
+         *(buf + len++) = '%';
        else if (HasVoice(chan))
          *(buf + len++) = '+';
        else if (IsZombie(chan))
index 584cebbbb146f098c6576067a8dcc95e38ee684b..e100c1947842555c53d264b79f521a5a0ba50804 100644 (file)
@@ -178,6 +178,8 @@ void do_who(struct Client* sptr, struct Client* acptr, struct Channel* repchan,
        */
       if (IsChanOp(chan))
         *(p1++) = '@';
+      if (IsHalfOp(chan))
+        *(p1++) = '%';
       if (HasVoice(chan))
         *(p1++) = '+';
       if (IsZombie(chan))
@@ -188,6 +190,8 @@ void do_who(struct Client* sptr, struct Client* acptr, struct Channel* repchan,
     else {
       if (IsChanOp(chan))
         *(p1++) = '@';
+      else if (IsHalfOp(chan))
+        *(p1++) = '%';
       else if (HasVoice(chan))
         *(p1++) = '+';
       else if (IsZombie(chan))