From b8402b9ef585580454491088f9581a1c3a8ca8ef Mon Sep 17 00:00:00 2001 From: pk910 Date: Sun, 18 Dec 2011 12:57:14 +0100 Subject: [PATCH] added MODE_HALFOP (+h, %) --- include/channel.h | 93 +++++++++++++++++++++++------------------ include/ircd_features.h | 2 + include/supported.h | 2 +- ircd/channel.c | 82 +++++++++++++++++++++++++----------- ircd/ircd_features.c | 1 + ircd/m_burst.c | 16 ++++++- ircd/m_clearmode.c | 9 +++- ircd/m_invite.c | 2 +- ircd/m_kick.c | 6 ++- ircd/m_mode.c | 4 +- ircd/m_names.c | 4 +- ircd/m_topic.c | 2 +- ircd/m_whois.c | 2 + ircd/whocmds.c | 4 ++ 14 files changed, 155 insertions(+), 74 deletions(-) diff --git a/include/channel.h b/include/channel.h index cad2de0..63cb19c 100644 --- a/include/channel.h +++ b/include/channel.h @@ -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); /* diff --git a/include/ircd_features.h b/include/ircd_features.h index cb14044..1b26017 100644 --- a/include/ircd_features.h +++ b/include/ircd_features.h @@ -179,6 +179,8 @@ enum Feature { FEAT_CHMODE_A_TARGET, FEAT_CHMODE_F_ENABLE, + FEAT_HALFOP, + FEAT_LAST_F }; diff --git a/include/supported.h b/include/supported.h index 31b044d..5bbd5be 100644 --- a/include/supported.h +++ b/include/supported.h @@ -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) diff --git a/ircd/channel.c b/ircd/channel.c index e22935c..7e9bb09 100644 --- a/ircd/channel.c +++ b/ircd/channel.c @@ -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); diff --git a/ircd/ircd_features.c b/ircd/ircd_features.c index 7a67361..8cc5037 100644 --- a/ircd/ircd_features.c +++ b/ircd/ircd_features.c @@ -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 diff --git a/ircd/m_burst.c b/ircd/m_burst.c index 15d7034..71fac54 100644 --- a/ircd/m_burst.c +++ b/ircd/m_burst.c @@ -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; } } diff --git a/ircd/m_clearmode.c b/ircd/m_clearmode.c index 4d0f179..2695574 100644 --- a/ircd/m_clearmode.c +++ b/ircd/m_clearmode.c @@ -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) { diff --git a/ircd/m_invite.c b/ircd/m_invite.c index 9308007..2e9cc05 100644 --- a/ircd/m_invite.c +++ b/ircd/m_invite.c @@ -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; } diff --git a/ircd/m_kick.c b/ircd/m_kick.c index f2a67d5..447329a 100644 --- a/ircd/m_kick.c +++ b/ircd/m_kick.c @@ -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. diff --git a/ircd/m_mode.c b/ircd/m_mode.c index 794cc04..b469041 100644 --- a/ircd/m_mode.c +++ b/ircd/m_mode.c @@ -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 */ diff --git a/ircd/m_names.c b/ircd/m_names.c index 26666a6..9e67b32 100644 --- a/ircd/m_names.c +++ b/ircd/m_names.c @@ -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)); diff --git a/ircd/m_topic.c b/ircd/m_topic.c index 37ae26f..d967e04 100644 --- a/ircd/m_topic.c +++ b/ircd/m_topic.c @@ -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); diff --git a/ircd/m_whois.c b/ircd/m_whois.c index 01ee78e..6891006 100644 --- a/ircd/m_whois.c +++ b/ircd/m_whois.c @@ -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)) diff --git a/ircd/whocmds.c b/ircd/whocmds.c index 584cebb..e100c19 100644 --- a/ircd/whocmds.c +++ b/ircd/whocmds.c @@ -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)) -- 2.20.1