Rework channel mode handling

Incoming MODE processing is split into a parsing step and an execution
step, instead of a mode's effector function being involved in its own
parsing. Modes can no longer use custom logic to control their parsing,
and instead supply a combination of CHM_* flags to the parser. As a
result, we know before we try to effect any mode changes what all of
them will be.

The reauthorize hack for override is no longer necessary. A side effect
of its introduction was that `MODE #foo b x!y@z` no longer worked; in
removing it we restore that behaviour.

We gain the ability to reject various invalid inputs that:
- mutate or query unknown modes
- supply excess mode arguments
- query modes that can't be queried

In each case, whether we *should* reject it is an open question; for now
I'm rejecting the first one.
This commit is contained in:
Ed Kellett 2020-11-05 16:31:57 +00:00
parent b5c8d52d82
commit 04952c32ad
6 changed files with 210 additions and 242 deletions

View file

@ -15,41 +15,33 @@ static const char chm_operonly_compat[] =
static int _modinit(void); static int _modinit(void);
static void _moddeinit(void); static void _moddeinit(void);
static void chm_operonly(struct Client *source_p, struct Channel *chptr, static void chm_operonly(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type);
const char **parv, int *errors, int dir, char c, long mode_type);
DECLARE_MODULE_AV2(chm_operonly_compat, _modinit, _moddeinit, NULL, NULL, NULL, NULL, NULL, chm_operonly_compat); DECLARE_MODULE_AV2(chm_operonly_compat, _modinit, _moddeinit, NULL, NULL, NULL, NULL, NULL, chm_operonly_compat);
static int static int
_modinit(void) _modinit(void)
{ {
chmode_table['O'].set_func = chm_operonly; chmode_table['O'] = (struct ChannelMode){chm_operonly, 0, 0};
chmode_table['O'].mode_type = 0;
return 0; return 0;
} }
static void static void
_moddeinit(void) _moddeinit(void)
{ {
chmode_table['O'].set_func = chm_nosuch; chmode_table['O'] = (struct ChannelMode){chm_nosuch, 0, 0};
chmode_table['O'].mode_type = 0;
} }
static void static void
chm_operonly(struct Client *source_p, struct Channel *chptr, chm_operonly(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
const char **parv, int *errors, int dir, char c, long mode_type)
{ {
int newparn = 0;
const char *newparv[] = { "$o" };
if (MyClient(source_p)) { if (MyClient(source_p)) {
chm_simple(source_p, chptr, alevel, parc, parn, parv, chm_simple(source_p, chptr, alevel, NULL,
errors, dir, 'i', MODE_INVITEONLY); errors, dir, 'i', MODE_INVITEONLY);
chm_ban(source_p, chptr, alevel, 1, &newparn, newparv, chm_ban(source_p, chptr, alevel, "$o",
errors, dir, 'I', CHFL_INVEX); errors, dir, 'I', CHFL_INVEX);
} else } else
chm_nosuch(source_p, chptr, alevel, parc, parn, parv, chm_nosuch(source_p, chptr, alevel, NULL,
errors, dir, c, mode_type); errors, dir, c, mode_type);
} }

View file

@ -16,39 +16,31 @@ static const char chm_quietunreg_compat_desc[] =
static int _modinit(void); static int _modinit(void);
static void _moddeinit(void); static void _moddeinit(void);
static void chm_quietunreg(struct Client *source_p, struct Channel *chptr, static void chm_quietunreg(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type);
const char **parv, int *errors, int dir, char c, long mode_type);
DECLARE_MODULE_AV2(chm_quietunreg_compat, _modinit, _moddeinit, NULL, NULL, NULL, NULL, NULL, chm_quietunreg_compat_desc); DECLARE_MODULE_AV2(chm_quietunreg_compat, _modinit, _moddeinit, NULL, NULL, NULL, NULL, NULL, chm_quietunreg_compat_desc);
static int static int
_modinit(void) _modinit(void)
{ {
chmode_table['R'].set_func = chm_quietunreg; chmode_table['R'] = (struct ChannelMode){ chm_quietunreg, 0, 0 };
chmode_table['R'].mode_type = 0;
return 0; return 0;
} }
static void static void
_moddeinit(void) _moddeinit(void)
{ {
chmode_table['R'].set_func = chm_nosuch; chmode_table['R'] = (struct ChannelMode){ chm_nosuch, 0, 0 };
chmode_table['R'].mode_type = 0;
} }
static void static void
chm_quietunreg(struct Client *source_p, struct Channel *chptr, chm_quietunreg(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
const char **parv, int *errors, int dir, char c, long mode_type)
{ {
int newparn = 0;
const char *newparv[] = { "$~a" };
if (MyClient(source_p)) if (MyClient(source_p))
chm_ban(source_p, chptr, alevel, 1, &newparn, newparv, chm_ban(source_p, chptr, alevel, "$~a",
errors, dir, 'q', CHFL_QUIET); errors, dir, 'q', CHFL_QUIET);
else else
chm_nosuch(source_p, chptr, alevel, parc, parn, parv, chm_nosuch(source_p, chptr, alevel, NULL,
errors, dir, c, mode_type); errors, dir, c, mode_type);
} }

View file

@ -15,39 +15,31 @@ static const char chm_sslonly_compat_desc[] =
static int _modinit(void); static int _modinit(void);
static void _moddeinit(void); static void _moddeinit(void);
static void chm_sslonly(struct Client *source_p, struct Channel *chptr, static void chm_sslonly(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type);
const char **parv, int *errors, int dir, char c, long mode_type);
DECLARE_MODULE_AV2(chm_sslonly_compat, _modinit, _moddeinit, NULL, NULL, NULL, NULL, NULL, chm_sslonly_compat_desc); DECLARE_MODULE_AV2(chm_sslonly_compat, _modinit, _moddeinit, NULL, NULL, NULL, NULL, NULL, chm_sslonly_compat_desc);
static int static int
_modinit(void) _modinit(void)
{ {
chmode_table['S'].set_func = chm_sslonly; chmode_table['S'] = (struct ChannelMode){ chm_sslonly, 0, 0 };
chmode_table['S'].mode_type = 0;
return 0; return 0;
} }
static void static void
_moddeinit(void) _moddeinit(void)
{ {
chmode_table['S'].set_func = chm_nosuch; chmode_table['S'] = (struct ChannelMode){ chm_nosuch, 0, 0 };
chmode_table['S'].mode_type = 0;
} }
static void static void
chm_sslonly(struct Client *source_p, struct Channel *chptr, chm_sslonly(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
const char **parv, int *errors, int dir, char c, long mode_type)
{ {
int newparn = 0;
const char *newparv[] = { "$~z" };
if (MyClient(source_p)) if (MyClient(source_p))
chm_ban(source_p, chptr, alevel, 1, &newparn, newparv, chm_ban(source_p, chptr, alevel, "$~z",
errors, dir, 'b', CHFL_BAN); errors, dir, 'b', CHFL_BAN);
else else
chm_nosuch(source_p, chptr, alevel, parc, parn, parv, chm_nosuch(source_p, chptr, alevel, NULL,
errors, dir, c, mode_type); errors, dir, c, mode_type);
} }

View file

@ -122,13 +122,23 @@ struct ChModeChange
}; };
typedef void (*ChannelModeFunc)(struct Client *source_p, struct Channel *chptr, typedef void (*ChannelModeFunc)(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type);
const char **parv, int *errors, int dir, char c, long mode_type);
enum chm_flags
{
CHM_CAN_QUERY = 1 << 0,
CHM_OPS_QUERY = 1 << 1,
CHM_ARG_SET = 1 << 2,
CHM_ARG_DEL = 1 << 3,
CHM_ARGS = CHM_ARG_SET | CHM_ARG_DEL,
CHM_QUERYABLE = CHM_ARGS | CHM_CAN_QUERY,
};
struct ChannelMode struct ChannelMode
{ {
ChannelModeFunc set_func; ChannelModeFunc set_func;
long mode_type; long mode_type;
enum chm_flags flags;
}; };
typedef int (*ExtbanFunc)(const char *data, struct Client *client_p, typedef int (*ExtbanFunc)(const char *data, struct Client *client_p,

View file

@ -35,41 +35,29 @@
extern int chmode_flags[256]; extern int chmode_flags[256];
extern void chm_nosuch(struct Client *source_p, struct Channel *chptr, extern void chm_nosuch(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type);
const char **parv, int *errors, int dir, char c, long mode_type);
extern void chm_orphaned(struct Client *source_p, struct Channel *chptr, extern void chm_orphaned(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type);
const char **parv, int *errors, int dir, char c, long mode_type);
extern void chm_simple(struct Client *source_p, struct Channel *chptr, extern void chm_simple(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type);
const char **parv, int *errors, int dir, char c, long mode_type);
extern void chm_ban(struct Client *source_p, struct Channel *chptr, extern void chm_ban(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type);
const char **parv, int *errors, int dir, char c, long mode_type);
extern void chm_hidden(struct Client *source_p, struct Channel *chptr, extern void chm_hidden(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type);
const char **parv, int *errors, int dir, char c, long mode_type);
extern void chm_staff(struct Client *source_p, struct Channel *chptr, extern void chm_staff(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type);
const char **parv, int *errors, int dir, char c, long mode_type);
extern void chm_forward(struct Client *source_p, struct Channel *chptr, extern void chm_forward(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type);
const char **parv, int *errors, int dir, char c, long mode_type);
extern void chm_throttle(struct Client *source_p, struct Channel *chptr, extern void chm_throttle(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type);
const char **parv, int *errors, int dir, char c, long mode_type);
extern void chm_key(struct Client *source_p, struct Channel *chptr, extern void chm_key(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type);
const char **parv, int *errors, int dir, char c, long mode_type);
extern void chm_limit(struct Client *source_p, struct Channel *chptr, extern void chm_limit(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type);
const char **parv, int *errors, int dir, char c, long mode_type);
extern void chm_op(struct Client *source_p, struct Channel *chptr, extern void chm_op(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type);
const char **parv, int *errors, int dir, char c, long mode_type);
extern void chm_voice(struct Client *source_p, struct Channel *chptr, extern void chm_voice(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type);
const char **parv, int *errors, int dir, char c, long mode_type);
extern unsigned int cflag_add(char c, ChannelModeFunc function); extern unsigned int cflag_add(char c, ChannelModeFunc function);
extern void cflag_orphan(char c); extern void cflag_orphan(char c);

View file

@ -41,6 +41,7 @@
#include "chmode.h" #include "chmode.h"
#include "s_assert.h" #include "s_assert.h"
#include "parse.h" #include "parse.h"
#include "msgbuf.h"
/* bitmasks for error returns, so we send once per call */ /* bitmasks for error returns, so we send once per call */
#define SM_ERR_NOTS 0x00000001 /* No TS on channel */ #define SM_ERR_NOTS 0x00000001 /* No TS on channel */
@ -580,8 +581,7 @@ fix_key_remote(char *arg)
*/ */
void void
chm_nosuch(struct Client *source_p, struct Channel *chptr, chm_nosuch(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
const char **parv, int *errors, int dir, char c, long mode_type)
{ {
if(*errors & SM_ERR_UNKNOWN) if(*errors & SM_ERR_UNKNOWN)
return; return;
@ -591,8 +591,7 @@ chm_nosuch(struct Client *source_p, struct Channel *chptr,
void void
chm_simple(struct Client *source_p, struct Channel *chptr, chm_simple(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
const char **parv, int *errors, int dir, char c, long mode_type)
{ {
if(!allow_mode_change(source_p, chptr, alevel, errors, c)) if(!allow_mode_change(source_p, chptr, alevel, errors, c))
return; return;
@ -630,8 +629,7 @@ chm_simple(struct Client *source_p, struct Channel *chptr,
void void
chm_orphaned(struct Client *source_p, struct Channel *chptr, chm_orphaned(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
const char **parv, int *errors, int dir, char c, long mode_type)
{ {
if(MyClient(source_p)) if(MyClient(source_p))
return; return;
@ -660,8 +658,7 @@ chm_orphaned(struct Client *source_p, struct Channel *chptr,
void void
chm_hidden(struct Client *source_p, struct Channel *chptr, chm_hidden(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
const char **parv, int *errors, int dir, char c, long mode_type)
{ {
if(MyClient(source_p) && !IsOperGeneral(source_p)) if(MyClient(source_p) && !IsOperGeneral(source_p))
{ {
@ -707,8 +704,7 @@ chm_hidden(struct Client *source_p, struct Channel *chptr,
void void
chm_staff(struct Client *source_p, struct Channel *chptr, chm_staff(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
const char **parv, int *errors, int dir, char c, long mode_type)
{ {
if(MyClient(source_p) && !IsOper(source_p)) if(MyClient(source_p) && !IsOper(source_p))
{ {
@ -754,10 +750,9 @@ chm_staff(struct Client *source_p, struct Channel *chptr,
void void
chm_ban(struct Client *source_p, struct Channel *chptr, chm_ban(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
const char **parv, int *errors, int dir, char c, long mode_type)
{ {
const char *mask, *raw_mask; const char *mask;
char *forward; char *forward;
rb_dlink_list *list; rb_dlink_list *list;
rb_dlink_node *ptr; rb_dlink_node *ptr;
@ -779,8 +774,7 @@ chm_ban(struct Client *source_p, struct Channel *chptr,
case CHFL_EXCEPTION: case CHFL_EXCEPTION:
/* if +e is disabled, allow all but +e locally */ /* if +e is disabled, allow all but +e locally */
if(!ConfigChannel.use_except && MyClient(source_p) && if (!ConfigChannel.use_except && MyClient(source_p) && dir == MODE_ADD)
((dir == MODE_ADD) && (parc > *parn)))
return; return;
list = &chptr->exceptlist; list = &chptr->exceptlist;
@ -796,8 +790,7 @@ chm_ban(struct Client *source_p, struct Channel *chptr,
case CHFL_INVEX: case CHFL_INVEX:
/* if +I is disabled, allow all but +I locally */ /* if +I is disabled, allow all but +I locally */
if(!ConfigChannel.use_invex && MyClient(source_p) && if (!ConfigChannel.use_invex && MyClient(source_p) && dir == MODE_ADD)
(dir == MODE_ADD) && (parc > *parn))
return; return;
list = &chptr->invexlist; list = &chptr->invexlist;
@ -824,7 +817,7 @@ chm_ban(struct Client *source_p, struct Channel *chptr,
return; return;
} }
if(dir == 0 || parc <= *parn) if (dir == MODE_QUERY)
{ {
if((*errors & errorval) != 0) if((*errors & errorval) != 0)
return; return;
@ -859,29 +852,26 @@ chm_ban(struct Client *source_p, struct Channel *chptr,
return; return;
} }
if(!allow_mode_change(source_p, chptr, alevel, errors, c)) if (!allow_mode_change(source_p, chptr, alevel, errors, c))
return; return;
if(MyClient(source_p) && (++mode_limit > MAXMODEPARAMS)) if (MyClient(source_p) && (++mode_limit > MAXMODEPARAMS))
return; return;
raw_mask = parv[(*parn)];
(*parn)++;
/* empty ban, or starts with ':' which messes up s2s, ignore it */ /* empty ban, or starts with ':' which messes up s2s, ignore it */
if(EmptyString(raw_mask) || *raw_mask == ':') if (EmptyString(arg) || *arg == ':')
return; return;
if(!MyClient(source_p)) if (!MyClient(source_p))
{ {
if(strchr(raw_mask, ' ')) if (strchr(arg, ' '))
return; return;
mask = raw_mask; mask = arg;
} }
else else
mask = pretty_mask(raw_mask); mask = pretty_mask(arg);
/* we'd have problems parsing this, hyb6 does it too /* we'd have problems parsing this, hyb6 does it too
* also make sure it will always fit on a line with channel * also make sure it will always fit on a line with channel
@ -891,7 +881,7 @@ chm_ban(struct Client *source_p, struct Channel *chptr,
{ {
sendto_one_numeric(source_p, ERR_INVALIDBAN, sendto_one_numeric(source_p, ERR_INVALIDBAN,
form_str(ERR_INVALIDBAN), form_str(ERR_INVALIDBAN),
chptr->chname, c, raw_mask); chptr->chname, c, arg);
return; return;
} }
@ -907,7 +897,7 @@ chm_ban(struct Client *source_p, struct Channel *chptr,
} }
/* if we're adding a NEW id */ /* if we're adding a NEW id */
if(dir == MODE_ADD) if (dir == MODE_ADD)
{ {
if (*mask == '$' && MyClient(source_p)) if (*mask == '$' && MyClient(source_p))
{ {
@ -915,7 +905,7 @@ chm_ban(struct Client *source_p, struct Channel *chptr,
{ {
sendto_one_numeric(source_p, ERR_INVALIDBAN, sendto_one_numeric(source_p, ERR_INVALIDBAN,
form_str(ERR_INVALIDBAN), form_str(ERR_INVALIDBAN),
chptr->chname, c, raw_mask); chptr->chname, c, arg);
return; return;
} }
} }
@ -923,28 +913,28 @@ chm_ban(struct Client *source_p, struct Channel *chptr,
/* For compatibility, only check the forward channel from /* For compatibility, only check the forward channel from
* local clients. Accept any forward channel from servers. * local clients. Accept any forward channel from servers.
*/ */
if(forward != NULL && MyClient(source_p)) if (forward != NULL && MyClient(source_p))
{ {
/* For simplicity and future flexibility, do not /* For simplicity and future flexibility, do not
* allow '$' in forwarding targets. * allow '$' in forwarding targets.
*/ */
if(!ConfigChannel.use_forward || if (!ConfigChannel.use_forward ||
strchr(forward, '$') != NULL) strchr(forward, '$') != NULL)
{ {
sendto_one_numeric(source_p, ERR_INVALIDBAN, sendto_one_numeric(source_p, ERR_INVALIDBAN,
form_str(ERR_INVALIDBAN), form_str(ERR_INVALIDBAN),
chptr->chname, c, raw_mask); chptr->chname, c, arg);
return; return;
} }
/* check_forward() sends its own error message */ /* check_forward() sends its own error message */
if(!check_forward(source_p, chptr, forward)) if (!check_forward(source_p, chptr, forward))
return; return;
/* Forwards only make sense for bans. */ /* Forwards only make sense for bans. */
if(mode_type != CHFL_BAN) if (mode_type != CHFL_BAN)
{ {
sendto_one_numeric(source_p, ERR_INVALIDBAN, sendto_one_numeric(source_p, ERR_INVALIDBAN,
form_str(ERR_INVALIDBAN), form_str(ERR_INVALIDBAN),
chptr->chname, c, raw_mask); chptr->chname, c, arg);
return; return;
} }
} }
@ -952,10 +942,10 @@ chm_ban(struct Client *source_p, struct Channel *chptr,
/* dont allow local clients to overflow the banlist, dont /* dont allow local clients to overflow the banlist, dont
* let remote servers set duplicate bans * let remote servers set duplicate bans
*/ */
if(!add_id(source_p, chptr, mask, forward, list, mode_type)) if (!add_id(source_p, chptr, mask, forward, list, mode_type))
return; return;
if(forward) if (forward)
forward[-1]= '$'; forward[-1]= '$';
mode_changes[mode_count].letter = c; mode_changes[mode_count].letter = c;
@ -964,23 +954,23 @@ chm_ban(struct Client *source_p, struct Channel *chptr,
mode_changes[mode_count].id = NULL; mode_changes[mode_count].id = NULL;
mode_changes[mode_count++].arg = mask; mode_changes[mode_count++].arg = mask;
} }
else if(dir == MODE_DEL) else if (dir == MODE_DEL)
{ {
struct Ban *removed; struct Ban *removed;
static char buf[BANLEN * MAXMODEPARAMS]; static char buf[BANLEN * MAXMODEPARAMS];
int old_removed_mask_pos = removed_mask_pos; int old_removed_mask_pos = removed_mask_pos;
if((removed = del_id(chptr, mask, list, mode_type)) == NULL) if ((removed = del_id(chptr, mask, list, mode_type)) == NULL)
{ {
/* mask isn't a valid ban, check raw_mask */ /* mask isn't a valid ban, check arg */
if((removed = del_id(chptr, raw_mask, list, mode_type)) != NULL) if ((removed = del_id(chptr, arg, list, mode_type)) != NULL)
mask = raw_mask; mask = arg;
} }
if(removed && removed->forward) if (removed && removed->forward)
removed_mask_pos += snprintf(buf + old_removed_mask_pos, sizeof(buf), "%s$%s", removed->banstr, removed->forward) + 1; removed_mask_pos += snprintf(buf + old_removed_mask_pos, sizeof(buf), "%s$%s", removed->banstr, removed->forward) + 1;
else else
removed_mask_pos += rb_strlcpy(buf + old_removed_mask_pos, mask, sizeof(buf)) + 1; removed_mask_pos += rb_strlcpy(buf + old_removed_mask_pos, mask, sizeof(buf)) + 1;
if(removed) if (removed)
{ {
free_ban(removed); free_ban(removed);
removed = NULL; removed = NULL;
@ -996,30 +986,22 @@ chm_ban(struct Client *source_p, struct Channel *chptr,
void void
chm_op(struct Client *source_p, struct Channel *chptr, chm_op(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
const char **parv, int *errors, int dir, char c, long mode_type)
{ {
struct membership *mstptr; struct membership *mstptr;
const char *opnick;
struct Client *targ_p; struct Client *targ_p;
if(!allow_mode_change(source_p, chptr, alevel, errors, c)) if(!allow_mode_change(source_p, chptr, alevel, errors, c))
return; return;
if((dir == MODE_QUERY) || (parc <= *parn))
return;
opnick = parv[(*parn)];
(*parn)++;
/* empty nick */ /* empty nick */
if(EmptyString(opnick)) if(EmptyString(arg))
{ {
sendto_one_numeric(source_p, ERR_NOSUCHNICK, form_str(ERR_NOSUCHNICK), "*"); sendto_one_numeric(source_p, ERR_NOSUCHNICK, form_str(ERR_NOSUCHNICK), "*");
return; return;
} }
if((targ_p = find_chasing(source_p, opnick, NULL)) == NULL) if((targ_p = find_chasing(source_p, arg, NULL)) == NULL)
{ {
return; return;
} }
@ -1030,7 +1012,7 @@ chm_op(struct Client *source_p, struct Channel *chptr,
{ {
if(!(*errors & SM_ERR_NOTONCHANNEL) && MyClient(source_p)) if(!(*errors & SM_ERR_NOTONCHANNEL) && MyClient(source_p))
sendto_one_numeric(source_p, ERR_USERNOTINCHANNEL, sendto_one_numeric(source_p, ERR_USERNOTINCHANNEL,
form_str(ERR_USERNOTINCHANNEL), opnick, chptr->chname); form_str(ERR_USERNOTINCHANNEL), arg, chptr->chname);
*errors |= SM_ERR_NOTONCHANNEL; *errors |= SM_ERR_NOTONCHANNEL;
return; return;
} }
@ -1072,30 +1054,22 @@ chm_op(struct Client *source_p, struct Channel *chptr,
void void
chm_voice(struct Client *source_p, struct Channel *chptr, chm_voice(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
const char **parv, int *errors, int dir, char c, long mode_type)
{ {
struct membership *mstptr; struct membership *mstptr;
const char *opnick;
struct Client *targ_p; struct Client *targ_p;
if(!allow_mode_change(source_p, chptr, alevel, errors, c)) if(!allow_mode_change(source_p, chptr, alevel, errors, c))
return; return;
if((dir == MODE_QUERY) || parc <= *parn)
return;
opnick = parv[(*parn)];
(*parn)++;
/* empty nick */ /* empty nick */
if(EmptyString(opnick)) if(EmptyString(arg))
{ {
sendto_one_numeric(source_p, ERR_NOSUCHNICK, form_str(ERR_NOSUCHNICK), "*"); sendto_one_numeric(source_p, ERR_NOSUCHNICK, form_str(ERR_NOSUCHNICK), "*");
return; return;
} }
if((targ_p = find_chasing(source_p, opnick, NULL)) == NULL) if((targ_p = find_chasing(source_p, arg, NULL)) == NULL)
{ {
return; return;
} }
@ -1106,7 +1080,7 @@ chm_voice(struct Client *source_p, struct Channel *chptr,
{ {
if(!(*errors & SM_ERR_NOTONCHANNEL) && MyClient(source_p)) if(!(*errors & SM_ERR_NOTONCHANNEL) && MyClient(source_p))
sendto_one_numeric(source_p, ERR_USERNOTINCHANNEL, sendto_one_numeric(source_p, ERR_USERNOTINCHANNEL,
form_str(ERR_USERNOTINCHANNEL), opnick, chptr->chname); form_str(ERR_USERNOTINCHANNEL), arg, chptr->chname);
*errors |= SM_ERR_NOTONCHANNEL; *errors |= SM_ERR_NOTONCHANNEL;
return; return;
} }
@ -1138,28 +1112,20 @@ chm_voice(struct Client *source_p, struct Channel *chptr,
void void
chm_limit(struct Client *source_p, struct Channel *chptr, chm_limit(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
const char **parv, int *errors, int dir, char c, long mode_type)
{ {
const char *lstr;
static char limitstr[30]; static char limitstr[30];
int limit; int limit;
if(!allow_mode_change(source_p, chptr, alevel, errors, c)) if (!allow_mode_change(source_p, chptr, alevel, errors, c))
return; return;
if(dir == MODE_QUERY) if (MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE))
return; return;
if(MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE)) if (dir == MODE_ADD)
return;
if((dir == MODE_ADD) && parc > *parn)
{ {
lstr = parv[(*parn)]; if (EmptyString(arg) || (limit = atoi(arg)) <= 0)
(*parn)++;
if(EmptyString(lstr) || (limit = atoi(lstr)) <= 0)
return; return;
sprintf(limitstr, "%d", limit); sprintf(limitstr, "%d", limit);
@ -1172,7 +1138,7 @@ chm_limit(struct Client *source_p, struct Channel *chptr,
chptr->mode.limit = limit; chptr->mode.limit = limit;
} }
else if(dir == MODE_DEL) else if (dir == MODE_DEL)
{ {
if(!chptr->mode.limit) if(!chptr->mode.limit)
return; return;
@ -1189,23 +1155,19 @@ chm_limit(struct Client *source_p, struct Channel *chptr,
void void
chm_throttle(struct Client *source_p, struct Channel *chptr, chm_throttle(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
const char **parv, int *errors, int dir, char c, long mode_type)
{ {
int joins = 0, timeslice = 0; int joins = 0, timeslice = 0;
if(!allow_mode_change(source_p, chptr, alevel, errors, c)) if (!allow_mode_change(source_p, chptr, alevel, errors, c))
return; return;
if(dir == MODE_QUERY) if (MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE))
return; return;
if(MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE)) if (dir == MODE_ADD)
return;
if((dir == MODE_ADD) && parc > *parn)
{ {
if (sscanf(parv[(*parn)], "%d:%d", &joins, &timeslice) < 2) if (sscanf(arg, "%d:%d", &joins, &timeslice) < 2)
return; return;
if(joins <= 0 || timeslice <= 0) if(joins <= 0 || timeslice <= 0)
@ -1215,9 +1177,7 @@ chm_throttle(struct Client *source_p, struct Channel *chptr,
mode_changes[mode_count].dir = MODE_ADD; mode_changes[mode_count].dir = MODE_ADD;
mode_changes[mode_count].mems = ALL_MEMBERS; mode_changes[mode_count].mems = ALL_MEMBERS;
mode_changes[mode_count].id = NULL; mode_changes[mode_count].id = NULL;
mode_changes[mode_count++].arg = parv[(*parn)]; mode_changes[mode_count++].arg = arg;
(*parn)++;
chptr->mode.join_num = joins; chptr->mode.join_num = joins;
chptr->mode.join_time = timeslice; chptr->mode.join_time = timeslice;
@ -1242,17 +1202,13 @@ chm_throttle(struct Client *source_p, struct Channel *chptr,
void void
chm_forward(struct Client *source_p, struct Channel *chptr, chm_forward(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
const char **parv, int *errors, int dir, char c, long mode_type)
{ {
const char *forward;
/* if +f is disabled, ignore local attempts to set it */ /* if +f is disabled, ignore local attempts to set it */
if(!ConfigChannel.use_forward && MyClient(source_p) && if (!ConfigChannel.use_forward && MyClient(source_p) && dir == MODE_ADD)
(dir == MODE_ADD) && (parc > *parn))
return; return;
if(dir == MODE_QUERY || (dir == MODE_ADD && parc <= *parn)) if (dir == MODE_QUERY)
{ {
if (!(*errors & SM_ERR_RPL_F)) if (!(*errors & SM_ERR_RPL_F))
{ {
@ -1266,10 +1222,10 @@ chm_forward(struct Client *source_p, struct Channel *chptr,
} }
#ifndef FORWARD_OPERONLY #ifndef FORWARD_OPERONLY
if(!allow_mode_change(source_p, chptr, alevel, errors, c)) if (!allow_mode_change(source_p, chptr, alevel, errors, c))
return; return;
#else #else
if(!IsOperGeneral(source_p) && !IsServer(source_p)) if (!IsOperGeneral(source_p) && !IsServer(source_p))
{ {
if(!(*errors & SM_ERR_NOPRIVS)) if(!(*errors & SM_ERR_NOPRIVS))
sendto_one_numeric(source_p, ERR_NOPRIVILEGES, form_str(ERR_NOPRIVILEGES)); sendto_one_numeric(source_p, ERR_NOPRIVILEGES, form_str(ERR_NOPRIVILEGES));
@ -1278,28 +1234,25 @@ chm_forward(struct Client *source_p, struct Channel *chptr,
} }
#endif #endif
if(MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE)) if (MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE))
return; return;
if(dir == MODE_ADD && parc > *parn) if (dir == MODE_ADD)
{ {
forward = parv[(*parn)]; if(EmptyString(arg))
(*parn)++;
if(EmptyString(forward))
return; return;
if(!check_forward(source_p, chptr, forward)) if(!check_forward(source_p, chptr, arg))
return; return;
rb_strlcpy(chptr->mode.forward, forward, sizeof(chptr->mode.forward)); rb_strlcpy(chptr->mode.forward, arg, sizeof(chptr->mode.forward));
mode_changes[mode_count].letter = c; mode_changes[mode_count].letter = c;
mode_changes[mode_count].dir = MODE_ADD; mode_changes[mode_count].dir = MODE_ADD;
mode_changes[mode_count].mems = mode_changes[mode_count].mems =
ConfigChannel.use_forward ? ALL_MEMBERS : ONLY_SERVERS; ConfigChannel.use_forward ? ALL_MEMBERS : ONLY_SERVERS;
mode_changes[mode_count].id = NULL; mode_changes[mode_count].id = NULL;
mode_changes[mode_count++].arg = forward; mode_changes[mode_count++].arg = arg;
} }
else if(dir == MODE_DEL) else if(dir == MODE_DEL)
{ {
@ -1318,24 +1271,19 @@ chm_forward(struct Client *source_p, struct Channel *chptr,
void void
chm_key(struct Client *source_p, struct Channel *chptr, chm_key(struct Client *source_p, struct Channel *chptr,
int alevel, int parc, int *parn, int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
const char **parv, int *errors, int dir, char c, long mode_type)
{ {
char *key; char *key;
if(!allow_mode_change(source_p, chptr, alevel, errors, c)) if (!allow_mode_change(source_p, chptr, alevel, errors, c))
return; return;
if(dir == MODE_QUERY) if (MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE))
return; return;
if(MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE)) if (dir == MODE_ADD)
return;
if((dir == MODE_ADD) && parc > *parn)
{ {
key = LOCAL_COPY(parv[(*parn)]); key = LOCAL_COPY(arg);
(*parn)++;
if(MyClient(source_p)) if(MyClient(source_p))
fix_key(key); fix_key(key);
@ -1359,9 +1307,6 @@ chm_key(struct Client *source_p, struct Channel *chptr,
static char splat[] = "*"; static char splat[] = "*";
int i; int i;
if(parc > *parn)
(*parn)++;
if(!(*chptr->mode.key)) if(!(*chptr->mode.key))
return; return;
@ -1388,29 +1333,29 @@ chm_key(struct Client *source_p, struct Channel *chptr,
/* *INDENT-OFF* */ /* *INDENT-OFF* */
struct ChannelMode chmode_table[256] = struct ChannelMode chmode_table[256] =
{ {
['F'] = {chm_simple, MODE_FREETARGET }, ['F'] = {chm_simple, MODE_FREETARGET, 0 },
['I'] = {chm_ban, CHFL_INVEX }, ['I'] = {chm_ban, CHFL_INVEX, CHM_QUERYABLE | CHM_OPS_QUERY },
['L'] = {chm_staff, MODE_EXLIMIT }, ['L'] = {chm_staff, MODE_EXLIMIT, 0 },
['P'] = {chm_staff, MODE_PERMANENT }, ['P'] = {chm_staff, MODE_PERMANENT, 0 },
['Q'] = {chm_simple, MODE_DISFORWARD }, ['Q'] = {chm_simple, MODE_DISFORWARD, 0 },
['b'] = {chm_ban, CHFL_BAN }, ['b'] = {chm_ban, CHFL_BAN, CHM_QUERYABLE },
['e'] = {chm_ban, CHFL_EXCEPTION }, ['e'] = {chm_ban, CHFL_EXCEPTION, CHM_QUERYABLE | CHM_OPS_QUERY },
['f'] = {chm_forward, 0 }, ['f'] = {chm_forward, 0, CHM_ARG_SET | CHM_CAN_QUERY }, /* weird because it's nonstandard and violates isupport */
['g'] = {chm_simple, MODE_FREEINVITE }, ['g'] = {chm_simple, MODE_FREEINVITE, 0 },
['i'] = {chm_simple, MODE_INVITEONLY }, ['i'] = {chm_simple, MODE_INVITEONLY, 0 },
['j'] = {chm_throttle, 0 }, ['j'] = {chm_throttle, 0, CHM_ARG_SET },
['k'] = {chm_key, 0 }, ['k'] = {chm_key, 0, CHM_QUERYABLE },
['l'] = {chm_limit, 0 }, ['l'] = {chm_limit, 0, CHM_ARG_SET },
['m'] = {chm_simple, MODE_MODERATED }, ['m'] = {chm_simple, MODE_MODERATED, 0 },
['n'] = {chm_simple, MODE_NOPRIVMSGS }, ['n'] = {chm_simple, MODE_NOPRIVMSGS, 0 },
['o'] = {chm_op, 0 }, ['o'] = {chm_op, 0, CHM_ARGS },
['p'] = {chm_simple, MODE_PRIVATE }, ['p'] = {chm_simple, MODE_PRIVATE, 0 },
['q'] = {chm_ban, CHFL_QUIET }, ['q'] = {chm_ban, CHFL_QUIET, CHM_QUERYABLE },
['r'] = {chm_simple, MODE_REGONLY }, ['r'] = {chm_simple, MODE_REGONLY, 0 },
['s'] = {chm_simple, MODE_SECRET }, ['s'] = {chm_simple, MODE_SECRET, 0 },
['t'] = {chm_simple, MODE_TOPICLIMIT }, ['t'] = {chm_simple, MODE_TOPICLIMIT, 0 },
['v'] = {chm_voice, 0 }, ['v'] = {chm_voice, 0, CHM_ARGS },
['z'] = {chm_simple, MODE_OPMODERATE }, ['z'] = {chm_simple, MODE_OPMODERATE, 0 },
}; };
/* *INDENT-ON* */ /* *INDENT-ON* */
@ -1433,14 +1378,14 @@ set_channel_mode(struct Client *client_p, struct Client *source_p,
char *pbuf; char *pbuf;
int cur_len, mlen, paralen, paracount, arglen, len; int cur_len, mlen, paralen, paracount, arglen, len;
int i, j, flags; int i, j, flags;
int dir = MODE_QUERY; int dir = MODE_ADD;
int access_dir = MODE_QUERY;
int parn = 1; int parn = 1;
int errors = 0; int errors = 0;
int alevel; int alevel;
const char *ml = parv[0]; const char *ml = parv[0];
char c; char c;
struct Client *fakesource_p; struct Client *fakesource_p;
int reauthorized = 0; /* if we change from MODE_QUERY to MODE_ADD/MODE_DEL, then reauth once, ugly but it works */
int flags_list[3] = { ALL_MEMBERS, ONLY_CHANOPS, ONLY_OPERS }; int flags_list[3] = { ALL_MEMBERS, ONLY_CHANOPS, ONLY_OPERS };
mask_pos = 0; mask_pos = 0;
@ -1455,42 +1400,91 @@ set_channel_mode(struct Client *client_p, struct Client *source_p,
else else
fakesource_p = source_p; fakesource_p = source_p;
alevel = get_channel_access(source_p, chptr, msptr, dir, reconstruct_parv(parc, parv)); struct modeset {
const struct ChannelMode *cm;
const char *arg;
int dir;
char mode;
};
for(; (c = *ml) != 0; ml++) static struct modeset modesets[MAXPARA];
struct modeset *ms = modesets, *mend;
for (ml = parv[0]; *ml != 0; ml++)
{ {
c = *ml;
switch (c) switch (c)
{ {
ChannelModeFunc set_func;
case '+': case '+':
dir = MODE_ADD; dir = MODE_ADD;
if (!reauthorized)
{
alevel = get_channel_access(source_p, chptr, msptr, dir, reconstruct_parv(parc, parv));
reauthorized = 1;
}
break; break;
case '-': case '-':
dir = MODE_DEL; dir = MODE_DEL;
if (!reauthorized)
{
alevel = get_channel_access(source_p, chptr, msptr, dir, reconstruct_parv(parc, parv));
reauthorized = 1;
}
break; break;
case '=': case '=':
dir = MODE_QUERY; dir = MODE_QUERY;
break; break;
default: default:
set_func = chmode_table[(unsigned char) c].set_func; {
if (set_func == NULL) int effective_dir = dir;
set_func = chm_nosuch; const struct ChannelMode *cm = &chmode_table[(unsigned char) c];
set_func(fakesource_p, chptr, alevel, bool use_arg = dir == MODE_ADD ? cm->flags & CHM_ARG_SET :
parc, &parn, parv, dir == MODE_DEL ? cm->flags & CHM_ARG_DEL :
&errors, dir, c, false;
chmode_table[(unsigned char) c].mode_type); if (cm->set_func == NULL || cm->set_func == chm_nosuch)
break; {
sendto_one(source_p, form_str(ERR_UNKNOWNMODE), me.name, source_p->name, c);
return;
}
if (use_arg && parn >= parc)
{
if (!(cm->flags & CHM_CAN_QUERY))
{
sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), me.name, source_p->name, "MODE");
return;
}
effective_dir = MODE_QUERY;
use_arg = false;
}
if (effective_dir == MODE_QUERY && !(cm->flags & CHM_CAN_QUERY))
{
/* XXX this currently replicates traditional behaviour and just
* does nothing for a query on a mode with no query. would it be
* good to send an error here?
*/
}
if (effective_dir != MODE_QUERY && access_dir == MODE_QUERY)
access_dir = effective_dir;
ms->cm = cm;
ms->dir = effective_dir;
if (use_arg)
ms->arg = parv[parn++];
else
ms->arg = NULL;
ms->mode = c;
ms++;
} }
}
}
if (parn < parc)
{
/* XXX we could reject excess params here */
}
mend = ms;
alevel = get_channel_access(source_p, chptr, msptr, access_dir, reconstruct_parv(parc, parv));
for (ms = modesets; ms < mend; ms++)
{
ChannelModeFunc set_func = ms->cm->set_func;
if (set_func == NULL)
set_func = chm_nosuch;
set_func(fakesource_p, chptr, alevel, ms->arg, &errors, ms->dir, ms->mode, ms->cm->mode_type);
} }
/* bail out if we have nothing to do... */ /* bail out if we have nothing to do... */