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);