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(feature_bool(FEAT_CHMODE_A_NOSET) && !(state->flags & MODE_PARSE_FORCE)) /* mode can'T be set. */
+ return;
+
if (!(state->flags & MODE_PARSE_WIPEOUT) &&
(!t_access || t_access == state->chptr->mode.access))
return;
return;
if(!(member = find_member_link(chptr, state->sptr)))
return;
- if(!IsChanOp(member)) {
+ if(!IsChanOpOrHalfOp(member)) {
send_notoper(state);
return;
}
else t_str++; //simply ignore if it's not an opmode
tmp++;
}
- if(tmp[0] == '+' || tmp[0] == '@') {
+ if(tmp[0] == '+' || tmp[0] == '%' || tmp[0] == '@') {
if(tmp[0] == '+') flags |= FLFL_VOICE;
+ if(tmp[0] == '%') flags |= FLFL_HALFOP;
if(tmp[0] == '@') flags |= FLFL_CHANOP;
tmp++;
}
unsigned int noflood_value = time;
noflood_value <<= 10;
noflood_value |= count;
- noflood_value <<= 3;
+ noflood_value <<= 4;
noflood_value |= flags;
state->chptr->mode.noflood_value = noflood_value;
} else {
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) && !(state->flags & MODE_PARSE_FORCE)) {
+ 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);
int p_pos = 0;
int is_visible = 1, is_ccode = 0, i = 0, j = 0;
char codes[5];
- for(i = 0; p != '\n'; p = stripped_message[++i]) {
+ for(i = 0; p != '\n' && p != 0; p = stripped_message[++i]) {
if(p == 3) {
j = 0;
is_ccode = 1;
struct Membership *member = find_member_link(chptr, cptr);
if(!member) return 0; //TODO: we've no check for -n channels implemented, yet
//check if this user is really affected by +f
- unsigned int flags = (chptr->mode.noflood_value & 0x00000007); //0000 0000 0000 0000 0000 0000 0000 0111 = 0x00000007 >> 0
- unsigned int count = (chptr->mode.noflood_value & 0x00001ff8) >> 3; //0000 0000 0000 0000 0001 1111 1111 1000 = 0x00001ff8 >> 3
- int time = (chptr->mode.noflood_value & 0x07ffe000) >> 13; //0000 0111 1111 1111 1110 0000 0000 0000 = 0x07ffe000 >> 13
+ unsigned int flags = (chptr->mode.noflood_value & 0x0000000f); //0000 0000 0000 0000 0000 0000 0000 1111 = 0x0000000f >> 0
+ unsigned int count = (chptr->mode.noflood_value & 0x00002ff0) >> 4; //0000 0000 0000 0000 0011 1111 1111 0000 = 0x00002ff0 >> 4
+ int time = (chptr->mode.noflood_value & 0x0fffc000) >> 14; //0000 1111 1111 1111 1100 0000 0000 0000 = 0x0fffc000 >> 14
if(count == 0 || time == 0) return 0;
if(!(flags & FLFL_NOFLOOD) && HasPriv(cptr, PRIV_FLOOD))
return 0;
if(!(flags & FLFL_CHANOP) && (member->status & CHFL_CHANOP))
return 0;
- if(!(flags & (FLFL_CHANOP | FLFL_VOICE)) && (member->status & CHFL_VOICE))
+ if(!(flags & (FLFL_CHANOP | FLFL_HALFOP)) && (member->status & CHFL_HALFOP))
+ return 0;
+ if(!(flags & (FLFL_CHANOP | FLFL_HALFOP | FLFL_VOICE)) && (member->status & CHFL_VOICE))
return 0;
int floodcount = 0;
struct MemberFlood *floodnode, *prev_floodnode;