#include "s_newconf.h"
#include "logger.h"
#include "chmode.h"
+#include "s_assert.h"
/* bitmasks for error returns, so we send once per call */
#define SM_ERR_NOTS 0x00000001 /* No TS on channel */
return moduledata.approved;
}
+/* allow_mode_change()
+ *
+ * Checks if mlock and chanops permit a mode change.
+ *
+ * inputs - client, channel, access level, errors pointer, mode char
+ * outputs - 0 on failure, 1 on success
+ * side effects - error message sent on failure
+ */
+static int
+allow_mode_change(struct Client *source_p, struct Channel *chptr, int alevel,
+ int *errors, char c)
+{
+ /* If this mode char is locked, don't allow local users to change it. */
+ if (MyClient(source_p) && chptr->mode_lock && strchr(chptr->mode_lock, c))
+ {
+ if (!(*errors & SM_ERR_MLOCK))
+ sendto_one_numeric(source_p,
+ ERR_MLOCKRESTRICTED,
+ form_str(ERR_MLOCKRESTRICTED),
+ chptr->chname,
+ c,
+ chptr->mode_lock);
+ *errors |= SM_ERR_MLOCK;
+ return 0;
+ }
+ if(alevel != CHFL_CHANOP)
+ {
+ if(!(*errors & SM_ERR_NOOPS))
+ sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED),
+ me.name, source_p->name, chptr->chname);
+ *errors |= SM_ERR_NOOPS;
+ return 0;
+ }
+ return 1;
+}
+
/* add_id()
*
* inputs - client, channel, id to add, type, forward
int alevel, int parc, int *parn,
const char **parv, int *errors, int dir, char c, long mode_type)
{
- if(alevel != CHFL_CHANOP)
- {
- if(!(*errors & SM_ERR_NOOPS))
- sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED),
- me.name, source_p->name, chptr->chname);
- *errors |= SM_ERR_NOOPS;
+ if(!allow_mode_change(source_p, chptr, alevel, errors, c))
return;
- }
if(MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE))
return;
rb_dlink_node *ptr;
struct Ban *banptr;
int errorval;
- int rpl_list;
- int rpl_endlist;
+ const char *rpl_list_p;
+ const char *rpl_endlist_p;
int caps;
int mems;
case CHFL_BAN:
list = &chptr->banlist;
errorval = SM_ERR_RPL_B;
- rpl_list = RPL_BANLIST;
- rpl_endlist = RPL_ENDOFBANLIST;
+ rpl_list_p = form_str(RPL_BANLIST);
+ rpl_endlist_p = form_str(RPL_ENDOFBANLIST);
mems = ALL_MEMBERS;
caps = 0;
break;
list = &chptr->exceptlist;
errorval = SM_ERR_RPL_E;
- rpl_list = RPL_EXCEPTLIST;
- rpl_endlist = RPL_ENDOFEXCEPTLIST;
+ rpl_list_p = form_str(RPL_EXCEPTLIST);
+ rpl_endlist_p = form_str(RPL_ENDOFEXCEPTLIST);
caps = CAP_EX;
if(ConfigChannel.use_except || (dir == MODE_DEL))
list = &chptr->invexlist;
errorval = SM_ERR_RPL_I;
- rpl_list = RPL_INVITELIST;
- rpl_endlist = RPL_ENDOFINVITELIST;
+ rpl_list_p = form_str(RPL_INVITELIST);
+ rpl_endlist_p = form_str(RPL_ENDOFINVITELIST);
caps = CAP_IE;
if(ConfigChannel.use_invex || (dir == MODE_DEL))
case CHFL_QUIET:
list = &chptr->quietlist;
errorval = SM_ERR_RPL_Q;
- rpl_list = RPL_QUIETLIST;
- rpl_endlist = RPL_ENDOFQUIETLIST;
+ rpl_list_p = form_str(RPL_QUIETLIST);
+ rpl_endlist_p = form_str(RPL_ENDOFQUIETLIST);
mems = ALL_MEMBERS;
caps = 0;
break;
*errors |= errorval;
/* non-ops cant see +eI lists.. */
+ /* note that this is still permitted if +e/+I are mlocked. */
if(alevel != CHFL_CHANOP && mode_type != CHFL_BAN &&
mode_type != CHFL_QUIET)
{
else
rb_strlcpy(buf, banptr->banstr, sizeof(buf));
- sendto_one(source_p, form_str(rpl_list),
+ sendto_one(source_p, rpl_list_p,
me.name, source_p->name, chptr->chname,
buf, banptr->who, banptr->when);
}
- sendto_one(source_p, form_str(rpl_endlist), me.name, source_p->name, chptr->chname);
+ sendto_one(source_p, rpl_endlist_p, me.name, source_p->name, chptr->chname);
return;
}
- if(alevel != CHFL_CHANOP)
- {
- if(!(*errors & SM_ERR_NOOPS))
- sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED),
- me.name, source_p->name, chptr->chname);
- *errors |= SM_ERR_NOOPS;
+ if(!allow_mode_change(source_p, chptr, alevel, errors, c))
return;
- }
+
if(MyClient(source_p) && (++mode_limit > MAXMODEPARAMS))
return;
* name etc.
*/
if(strlen(mask) > IRCD_MIN(BANLEN, MODEBUFLEN - 5))
+ {
+ sendto_one_numeric(source_p, ERR_INVALIDBAN,
+ form_str(ERR_INVALIDBAN),
+ chptr->chname, c, raw_mask);
return;
+ }
/* Look for a $ after the first character.
* As the first character, it marks an extban; afterwards
if (*mask == '$' && MyClient(source_p))
{
if (!valid_extban(mask, source_p, chptr, mode_type))
- /* XXX perhaps return an error message here */
+ {
+ sendto_one_numeric(source_p, ERR_INVALIDBAN,
+ form_str(ERR_INVALIDBAN),
+ chptr->chname, c, raw_mask);
return;
+ }
}
/* For compatibility, only check the forward channel from
*/
if(forward != NULL && MyClient(source_p))
{
- if(!ConfigChannel.use_forward)
- forward = NULL;
- else if(!check_forward(source_p, chptr, forward))
+ /* For simplicity and future flexibility, do not
+ * allow '$' in forwarding targets.
+ */
+ if(!ConfigChannel.use_forward ||
+ strchr(forward, '$') != NULL)
+ {
+ sendto_one_numeric(source_p, ERR_INVALIDBAN,
+ form_str(ERR_INVALIDBAN),
+ chptr->chname, c, raw_mask);
return;
+ }
+ /* check_forward() sends its own error message */
+ if(!check_forward(source_p, chptr, forward))
+ return;
+ /* Forwards only make sense for bans. */
+ if(mode_type != CHFL_BAN)
+ {
+ sendto_one_numeric(source_p, ERR_INVALIDBAN,
+ form_str(ERR_INVALIDBAN),
+ chptr->chname, c, raw_mask);
+ return;
+ }
}
/* dont allow local clients to overflow the banlist, dont
}
if(removed && removed->forward)
- removed_mask_pos += rb_snprintf(buf, sizeof(buf), "%s$%s", removed->banstr, removed->forward);
+ removed_mask_pos += rb_snprintf(buf + old_removed_mask_pos, sizeof(buf), "%s$%s", removed->banstr, removed->forward) + 1;
else
- removed_mask_pos += rb_strlcpy(buf, mask, sizeof(buf));
+ removed_mask_pos += rb_strlcpy(buf + old_removed_mask_pos, mask, sizeof(buf)) + 1;
if(removed)
{
free_ban(removed);
const char *opnick;
struct Client *targ_p;
- if(alevel != CHFL_CHANOP)
- {
- if(!(*errors & SM_ERR_NOOPS))
- sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED),
- me.name, source_p->name, chptr->chname);
- *errors |= SM_ERR_NOOPS;
+ if(!allow_mode_change(source_p, chptr, alevel, errors, c))
return;
- }
if((dir == MODE_QUERY) || (parc <= *parn))
return;
const char *opnick;
struct Client *targ_p;
- if(alevel != CHFL_CHANOP)
- {
- if(!(*errors & SM_ERR_NOOPS))
- sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED),
- me.name, source_p->name, chptr->chname);
- *errors |= SM_ERR_NOOPS;
+ if(!allow_mode_change(source_p, chptr, alevel, errors, c))
return;
- }
if((dir == MODE_QUERY) || parc <= *parn)
return;
static char limitstr[30];
int limit;
- if(alevel != CHFL_CHANOP)
- {
- if(!(*errors & SM_ERR_NOOPS))
- sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED),
- me.name, source_p->name, chptr->chname);
- *errors |= SM_ERR_NOOPS;
+ if(!allow_mode_change(source_p, chptr, alevel, errors, c))
return;
- }
if(dir == MODE_QUERY)
return;
{
int joins = 0, timeslice = 0;
- if(alevel != CHFL_CHANOP)
- {
- if(!(*errors & SM_ERR_NOOPS))
- sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED),
- me.name, source_p->name, chptr->chname);
- *errors |= SM_ERR_NOOPS;
+ if(!allow_mode_change(source_p, chptr, alevel, errors, c))
return;
- }
if(dir == MODE_QUERY)
return;
}
#ifndef FORWARD_OPERONLY
- if(alevel != CHFL_CHANOP)
- {
- if(!(*errors & SM_ERR_NOOPS))
- sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED),
- me.name, source_p->name, chptr->chname);
- *errors |= SM_ERR_NOOPS;
+ if(!allow_mode_change(source_p, chptr, alevel, errors, c))
return;
- }
#else
if(!IsOper(source_p) && !IsServer(source_p))
{
{
char *key;
- if(alevel != CHFL_CHANOP)
- {
- if(!(*errors & SM_ERR_NOOPS))
- sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED),
- me.name, source_p->name, chptr->chname);
- *errors |= SM_ERR_NOOPS;
+ if(!allow_mode_change(source_p, chptr, alevel, errors, c))
return;
- }
if(dir == MODE_QUERY)
return;
{chm_nosuch, 0 }, /* @ */
{chm_nosuch, 0 }, /* A */
{chm_nosuch, 0 }, /* B */
- {chm_simple, MODE_NOCTCP }, /* C */
+ {chm_nosuch, 0 }, /* C */
{chm_nosuch, 0 }, /* D */
{chm_nosuch, 0 }, /* E */
{chm_simple, MODE_FREETARGET }, /* F */
{chm_nosuch, 0 },
{chm_nosuch, 0 }, /* a */
{chm_ban, CHFL_BAN }, /* b */
- {chm_simple, MODE_NOCOLOR }, /* c */
+ {chm_nosuch, 0 }, /* c */
{chm_nosuch, 0 }, /* d */
{chm_ban, CHFL_EXCEPTION }, /* e */
{chm_forward, 0 }, /* f */
dir = MODE_QUERY;
break;
default:
- /* If this mode char is locked, don't allow local users to change it. */
- if (MyClient(source_p) && chptr->mode_lock && strchr(chptr->mode_lock, c))
- {
- if (!(errors & SM_ERR_MLOCK))
- sendto_one_numeric(source_p,
- ERR_MLOCKRESTRICTED,
- form_str(ERR_MLOCKRESTRICTED),
- chptr->chname,
- c,
- chptr->mode_lock);
- errors |= SM_ERR_MLOCK;
- continue;
- }
chmode_table[(unsigned char) c].set_func(fakesource_p, chptr, alevel,
parc, &parn, parv,
&errors, dir, c,