#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 */
/**, 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 */
#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 */
};
#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;
#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)
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);
/*
FEAT_CHMODE_A_TARGET,
FEAT_CHMODE_F_ENABLE,
+ FEAT_HALFOP,
+
FEAT_LAST_F
};
#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)
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.
*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, "*");
*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, "*");
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;
* 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)
{
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 */
}
}
/* 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];
/* 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**)
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)
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)) {
}
/* 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... */
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 */
return;
if(!(member = find_member_link(chptr, state->sptr)))
return;
- if(!IsChanOp(member)) {
+ if(!IsChanOpOrHalfOp(member)) {
send_notoper(state);
return;
}
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);
}
}
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
} 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 {
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 */
} 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.");
}
}
{
static ulong64 chan_flags[] = {
MODE_CHANOP, 'o',
+ MODE_HALFOP, 'h',
MODE_VOICE, 'v',
MODE_PRIVATE, 'p',
MODE_SECRET, 's',
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);
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
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;
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. */
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;
}
}
static int flags[] = {
MODE_CHANOP, 'o',
MODE_VOICE, 'v',
+ MODE_HALFOP, 'h',
MODE_PRIVATE, 'p',
MODE_SECRET, 's',
MODE_MODERATED, 'm',
}
/* 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;
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) {
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;
}
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)))
/* 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.
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;
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 */
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)
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));
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);
*(buf + len++) = '<';
else if (IsChanOp(chan))
*(buf + len++) = '@';
+ else if (IsHalfOp(chan))
+ *(buf + len++) = '%';
else if (HasVoice(chan))
*(buf + len++) = '+';
else if (IsZombie(chan))
*/
if (IsChanOp(chan))
*(p1++) = '@';
+ if (IsHalfOp(chan))
+ *(p1++) = '%';
if (HasVoice(chan))
*(p1++) = '+';
if (IsZombie(chan))
else {
if (IsChanOp(chan))
*(p1++) = '@';
+ else if (IsHalfOp(chan))
+ *(p1++) = '%';
else if (HasVoice(chan))
*(p1++) = '+';
else if (IsZombie(chan))