X-Git-Url: https://jfr.im/git/solanum.git/blobdiff_plain/4016731b1cf38951bc7566e7bc3ca8ebb8f282b4..6b6a579925a75bda0039b7ae99fda6b24776d8a0:/src/chmode.c diff --git a/src/chmode.c b/src/chmode.c index 6598eacf..e131e453 100644 --- a/src/chmode.c +++ b/src/chmode.c @@ -2,9 +2,9 @@ * charybdis: A slightly useful ircd. * chmode.c: channel mode management * - * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center - * Copyright (C) 1996-2002 Hybrid Development Team - * Copyright (C) 2002-2005 ircd-ratbox development team + * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center + * Copyright (C) 1996-2002 Hybrid Development Team + * Copyright (C) 2002-2005 ircd-ratbox development team * Copyright (C) 2005-2006 charybdis development team * * This program is free software; you can redistribute it and/or modify @@ -31,8 +31,7 @@ #include "common.h" #include "hash.h" #include "hook.h" -#include "irc_string.h" -#include "sprintf_irc.h" +#include "match.h" #include "ircd.h" #include "numeric.h" #include "s_serv.h" /* captab */ @@ -42,6 +41,8 @@ #include "s_conf.h" /* ConfigFileEntry, ConfigChannel */ #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 */ @@ -56,35 +57,193 @@ #define SM_ERR_NOPRIVS 0x00000400 #define SM_ERR_RPL_Q 0x00000800 #define SM_ERR_RPL_F 0x00001000 +#define SM_ERR_MLOCK 0x00002000 -void set_channel_mode(struct Client *, struct Client *, - struct Channel *, struct membership *, int, const char **); - -int add_id(struct Client *source_p, struct Channel *chptr, - const char *banid, rb_dlink_list * list, long mode_type); +#define MAXMODES_SIMPLE 46 /* a-zA-Z except bqeIov */ static struct ChModeChange mode_changes[BUFSIZE]; static int mode_count; static int mode_limit; +static int mode_limit_simple; static int mask_pos; +static int removed_mask_pos; + +char cflagsbuf[256]; +char cflagsmyinfo[256]; + +int chmode_flags[256]; + +extern int h_get_channel_access; + +/* OPTIMIZE ME! -- dwr */ +void +construct_cflags_strings(void) +{ + int i; + char *ptr = cflagsbuf; + char *ptr2 = cflagsmyinfo; + + *ptr = '\0'; + *ptr2 = '\0'; + + for(i = 0; i < 256; i++) + { + if( !(chmode_table[i].set_func == chm_ban) && + !(chmode_table[i].set_func == chm_forward) && + !(chmode_table[i].set_func == chm_throttle) && + !(chmode_table[i].set_func == chm_key) && + !(chmode_table[i].set_func == chm_limit) && + !(chmode_table[i].set_func == chm_op) && + !(chmode_table[i].set_func == chm_voice)) + { + chmode_flags[i] = chmode_table[i].mode_type; + } + else + { + chmode_flags[i] = 0; + } + + switch (chmode_flags[i]) + { + case MODE_FREETARGET: + case MODE_DISFORWARD: + if(ConfigChannel.use_forward) + *ptr++ = (char) i; + break; + default: + if(chmode_flags[i] != 0) + { + *ptr++ = (char) i; + } + } + + /* Should we leave orphaned check here? -- dwr */ + if(!(chmode_table[i].set_func == chm_nosuch) && !(chmode_table[i].set_func == chm_orphaned)) + { + *ptr2++ = (char) i; + } + } + + *ptr++ = '\0'; + *ptr2++ = '\0'; +} + +/* + * find_umode_slot + * + * inputs - NONE + * outputs - an available cflag bitmask or + * 0 if no cflags are available + * side effects - NONE + */ +static unsigned int +find_cflag_slot(void) +{ + unsigned int all_cflags = 0, my_cflag = 0, i; + + for (i = 0; i < 256; i++) + all_cflags |= chmode_flags[i]; + + for (my_cflag = 1; my_cflag && (all_cflags & my_cflag); + my_cflag <<= 1); + + return my_cflag; +} + +unsigned int +cflag_add(char c_, ChannelModeFunc function) +{ + int c = (unsigned char)c_; + + if (chmode_table[c].set_func != chm_nosuch && + chmode_table[c].set_func != chm_orphaned) + return 0; + + if (chmode_table[c].set_func == chm_nosuch) + chmode_table[c].mode_type = find_cflag_slot(); + if (chmode_table[c].mode_type == 0) + return 0; + chmode_table[c].set_func = function; + construct_cflags_strings(); + return chmode_table[c].mode_type; +} + +void +cflag_orphan(char c_) +{ + int c = (unsigned char)c_; + + s_assert(chmode_flags[c] != 0); + chmode_table[c].set_func = chm_orphaned; + construct_cflags_strings(); +} int get_channel_access(struct Client *source_p, struct membership *msptr) { - if(!MyClient(source_p) || is_chanop(msptr)) + hook_data_channel_approval moduledata; + + if(!MyClient(source_p)) return CHFL_CHANOP; - return CHFL_PEON; + if (msptr == NULL) + return CHFL_PEON; + + moduledata.client = source_p; + moduledata.chptr = msptr->chptr; + moduledata.msptr = msptr; + moduledata.target = NULL; + moduledata.approved = is_chanop(msptr) ? CHFL_CHANOP : CHFL_PEON; + + call_hook(h_get_channel_access, &moduledata); + + 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 + * inputs - client, channel, id to add, type, forward * outputs - 0 on failure, 1 on success * side effects - given id is added to the appropriate list */ int -add_id(struct Client *source_p, struct Channel *chptr, const char *banid, +add_id(struct Client *source_p, struct Channel *chptr, const char *banid, const char *forward, rb_dlink_list * list, long mode_type) { struct Ban *actualBan; @@ -126,9 +285,9 @@ add_id(struct Client *source_p, struct Channel *chptr, const char *banid, if(IsPerson(source_p)) rb_sprintf(who, "%s!%s@%s", source_p->name, source_p->username, source_p->host); else - strlcpy(who, source_p->name, sizeof(who)); + rb_strlcpy(who, source_p->name, sizeof(who)); - actualBan = allocate_ban(realban, who); + actualBan = allocate_ban(realban, who, forward); actualBan->when = rb_current_time(); rb_dlinkAdd(actualBan, &actualBan->node, list); @@ -143,17 +302,17 @@ add_id(struct Client *source_p, struct Channel *chptr, const char *banid, /* del_id() * * inputs - channel, id to remove, type - * outputs - 0 on failure, 1 on success - * side effects - given id is removed from the appropriate list + * outputs - pointer to ban that was removed, if any + * side effects - given id is removed from the appropriate list and returned */ -int +struct Ban * del_id(struct Channel *chptr, const char *banid, rb_dlink_list * list, long mode_type) { rb_dlink_node *ptr; struct Ban *banptr; if(EmptyString(banid)) - return 0; + return NULL; RB_DLINK_FOREACH(ptr, list->head) { @@ -162,17 +321,16 @@ del_id(struct Channel *chptr, const char *banid, rb_dlink_list * list, long mode if(irccmp(banid, banptr->banstr) == 0) { rb_dlinkDelete(&banptr->node, list); - free_ban(banptr); /* invalidate the can_send() cache */ if(mode_type == CHFL_BAN || mode_type == CHFL_QUIET || mode_type == CHFL_EXCEPTION) chptr->bants++; - return 1; + return banptr; } } - return 0; + return NULL; } /* check_string() @@ -216,26 +374,30 @@ pretty_mask(const char *idmask) { static char mask_buf[BUFSIZE]; int old_mask_pos; - char *nick, *user, *host; - char splat[] = "*"; + const char *nick, *user, *host, *forward = NULL; char *t, *at, *ex; - char ne = 0, ue = 0, he = 0; /* save values at nick[NICKLEN], et all */ + int nl, ul, hl, fl; char *mask; + size_t masklen; mask = LOCAL_COPY(idmask); mask = check_string(mask); collapse(mask); + masklen = strlen(mask); - nick = user = host = splat; + nick = user = host = "*"; + nl = ul = hl = 1; + fl = 0; - if((size_t) BUFSIZE - mask_pos < strlen(mask) + 5) + if((size_t) BUFSIZE - mask_pos < masklen + 5) return NULL; old_mask_pos = mask_pos; if (*mask == '$') { - mask_pos += rb_sprintf(mask_buf + mask_pos, "%s", mask) + 1; + memcpy(mask_buf + mask_pos, mask, masklen + 1); + mask_pos += masklen + 1; t = mask_buf + old_mask_pos + 1; if (*t == '!') *t = '~'; @@ -246,80 +408,124 @@ pretty_mask(const char *idmask) } at = ex = NULL; - if((t = strchr(mask, '@')) != NULL) + if((t = memchr(mask, '@', masklen)) != NULL) { at = t; - *t++ = '\0'; + t++; if(*t != '\0') - host = t; + host = t, hl = strlen(t); - if((t = strchr(mask, '!')) != NULL) + if((t = memchr(mask, '!', at - mask)) != NULL) { ex = t; - *t++ = '\0'; - if(*t != '\0') - user = t; - if(*mask != '\0') - nick = mask; + t++; + if(at != t) + user = t, ul = at - t; + if(ex != mask) + nick = mask, nl = ex - mask; } else { - if(*mask != '\0') - user = mask; + if(at != mask) + user = mask, ul = at - mask; + } + + if((t = memchr(host, '!', hl)) != NULL || + (t = memchr(host, '$', hl)) != NULL) + { + t++; + if (host + hl != t) + forward = t, fl = host + hl - t; + hl = t - 1 - host; } } - else if((t = strchr(mask, '!')) != NULL) + else if((t = memchr(mask, '!', masklen)) != NULL) { ex = t; - *t++ = '\0'; - if(*mask != '\0') - nick = mask; + t++; + if(ex != mask) + nick = mask, nl = ex - mask; if(*t != '\0') - user = t; + user = t, ul = strlen(t); } - else if(strchr(mask, '.') != NULL || strchr(mask, ':') != NULL) + else if(memchr(mask, '.', masklen) != NULL || + memchr(mask, ':', masklen) != NULL) { - if(*mask != '\0') - host = mask; + host = mask, hl = masklen; } else { - if(*mask != '\0') - nick = mask; + if(masklen > 0) + nick = mask, nl = masklen; } /* truncate values to max lengths */ - if(strlen(nick) > NICKLEN - 1) + if(nl > NICKLEN - 1) + nl = NICKLEN - 1; + if(ul > USERLEN) + ul = USERLEN; + if(hl > HOSTLEN) + hl = HOSTLEN; + if(fl > CHANNELLEN) + fl = CHANNELLEN; + + memcpy(mask_buf + mask_pos, nick, nl), mask_pos += nl; + mask_buf[mask_pos++] = '!'; + memcpy(mask_buf + mask_pos, user, ul), mask_pos += ul; + mask_buf[mask_pos++] = '@'; + memcpy(mask_buf + mask_pos, host, hl), mask_pos += hl; + if (forward) { + mask_buf[mask_pos++] = '$'; + memcpy(mask_buf + mask_pos, forward, fl), mask_pos += fl; + } + mask_buf[mask_pos++] = '\0'; + + return mask_buf + old_mask_pos; +} + +/* check_forward() + * + * input - client, channel to set mode on, target channel name + * output - true if forwarding should be allowed + * side effects - numeric sent if not allowed + */ +static int +check_forward(struct Client *source_p, struct Channel *chptr, + const char *forward) +{ + struct Channel *targptr; + struct membership *msptr; + + if(!check_channel_name(forward) || + (MyClient(source_p) && (strlen(forward) > LOC_CHANNELLEN || hash_find_resv(forward)))) { - ne = nick[NICKLEN - 1]; - nick[NICKLEN - 1] = '\0'; + sendto_one_numeric(source_p, ERR_BADCHANNAME, form_str(ERR_BADCHANNAME), forward); + return 0; } - if(strlen(user) > USERLEN) + /* don't forward to inconsistent target -- jilles */ + if(chptr->chname[0] == '#' && forward[0] == '&') { - ue = user[USERLEN]; - user[USERLEN] = '\0'; + sendto_one_numeric(source_p, ERR_BADCHANNAME, + form_str(ERR_BADCHANNAME), forward); + return 0; } - if(strlen(host) > HOSTLEN) + if(MyClient(source_p) && (targptr = find_channel(forward)) == NULL) { - he = host[HOSTLEN]; - host[HOSTLEN] = '\0'; + sendto_one_numeric(source_p, ERR_NOSUCHCHANNEL, + form_str(ERR_NOSUCHCHANNEL), forward); + return 0; } - - mask_pos += rb_sprintf(mask_buf + mask_pos, "%s!%s@%s", nick, user, host) + 1; - - /* restore mask, since we may need to use it again later */ - if(at) - *at = '@'; - if(ex) - *ex = '!'; - if(ne) - nick[NICKLEN - 1] = ne; - if(ue) - user[USERLEN] = ue; - if(he) - host[HOSTLEN] = he; - - return mask_buf + old_mask_pos; + if(MyClient(source_p) && !(targptr->mode.mode & MODE_FREETARGET)) + { + if((msptr = find_channel_membership(targptr, source_p)) == NULL || + get_channel_access(source_p, msptr) != CHFL_CHANOP) + { + sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), + me.name, source_p->name, targptr->chname); + return 0; + } + } + return 1; } /* fix_key() @@ -388,17 +594,10 @@ chm_simple(struct Client *source_p, struct Channel *chptr, 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; - } - /* +ntspmaikl == 9 + MAXMODEPARAMS (4 * +o) */ - if(MyClient(source_p) && (++mode_limit > (9 + MAXMODEPARAMS))) + if(MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE)) return; /* setting + */ @@ -406,15 +605,13 @@ chm_simple(struct Client *source_p, struct Channel *chptr, { /* if +f is disabled, ignore an attempt to set +QF locally */ if(!ConfigChannel.use_forward && MyClient(source_p) && - (c == 'Q' || c == 'F')) + (c == 'Q' || c == 'F')) return; chptr->mode.mode |= mode_type; mode_changes[mode_count].letter = c; mode_changes[mode_count].dir = MODE_ADD; - mode_changes[mode_count].caps = 0; - mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].id = NULL; mode_changes[mode_count].mems = ALL_MEMBERS; mode_changes[mode_count++].arg = NULL; @@ -425,8 +622,36 @@ chm_simple(struct Client *source_p, struct Channel *chptr, mode_changes[mode_count].letter = c; mode_changes[mode_count].dir = MODE_DEL; - mode_changes[mode_count].caps = 0; - mode_changes[mode_count].nocaps = 0; + mode_changes[mode_count].mems = ALL_MEMBERS; + mode_changes[mode_count].id = NULL; + mode_changes[mode_count++].arg = NULL; + } +} + +void +chm_orphaned(struct Client *source_p, struct Channel *chptr, + int alevel, int parc, int *parn, + const char **parv, int *errors, int dir, char c, long mode_type) +{ + if(MyClient(source_p)) + return; + + if((dir == MODE_ADD) && !(chptr->mode.mode & mode_type)) + { + chptr->mode.mode |= mode_type; + + mode_changes[mode_count].letter = c; + mode_changes[mode_count].dir = MODE_ADD; + mode_changes[mode_count].id = NULL; + mode_changes[mode_count].mems = ALL_MEMBERS; + mode_changes[mode_count++].arg = NULL; + } + else if((dir == MODE_DEL) && (chptr->mode.mode & mode_type)) + { + chptr->mode.mode &= ~mode_type; + + mode_changes[mode_count].letter = c; + mode_changes[mode_count].dir = MODE_DEL; mode_changes[mode_count].mems = ALL_MEMBERS; mode_changes[mode_count].id = NULL; mode_changes[mode_count++].arg = NULL; @@ -454,6 +679,12 @@ chm_staff(struct Client *source_p, struct Channel *chptr, return; } + if(!allow_mode_change(source_p, chptr, CHFL_CHANOP, errors, c)) + return; + + if(MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE)) + return; + /* setting + */ if((dir == MODE_ADD) && !(chptr->mode.mode & mode_type)) { @@ -461,8 +692,6 @@ chm_staff(struct Client *source_p, struct Channel *chptr, mode_changes[mode_count].letter = c; mode_changes[mode_count].dir = MODE_ADD; - mode_changes[mode_count].caps = 0; - mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].id = NULL; mode_changes[mode_count].mems = ALL_MEMBERS; mode_changes[mode_count++].arg = NULL; @@ -473,8 +702,6 @@ chm_staff(struct Client *source_p, struct Channel *chptr, mode_changes[mode_count].letter = c; mode_changes[mode_count].dir = MODE_DEL; - mode_changes[mode_count].caps = 0; - mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = ALL_MEMBERS; mode_changes[mode_count].id = NULL; mode_changes[mode_count++].arg = NULL; @@ -486,15 +713,14 @@ chm_ban(struct Client *source_p, struct Channel *chptr, int alevel, int parc, int *parn, const char **parv, int *errors, int dir, char c, long mode_type) { - const char *mask; - const char *raw_mask; + const char *mask, *raw_mask; + char *forward; rb_dlink_list *list; rb_dlink_node *ptr; struct Ban *banptr; int errorval; - int rpl_list; - int rpl_endlist; - int caps; + const char *rpl_list_p; + const char *rpl_endlist_p; int mems; switch (mode_type) @@ -502,10 +728,9 @@ chm_ban(struct Client *source_p, struct Channel *chptr, 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; case CHFL_EXCEPTION: @@ -516,9 +741,8 @@ chm_ban(struct Client *source_p, struct Channel *chptr, list = &chptr->exceptlist; errorval = SM_ERR_RPL_E; - rpl_list = RPL_EXCEPTLIST; - rpl_endlist = RPL_ENDOFEXCEPTLIST; - caps = CAP_EX; + rpl_list_p = form_str(RPL_EXCEPTLIST); + rpl_endlist_p = form_str(RPL_ENDOFEXCEPTLIST); if(ConfigChannel.use_except || (dir == MODE_DEL)) mems = ONLY_CHANOPS; @@ -534,9 +758,8 @@ chm_ban(struct Client *source_p, struct Channel *chptr, list = &chptr->invexlist; errorval = SM_ERR_RPL_I; - rpl_list = RPL_INVITELIST; - rpl_endlist = RPL_ENDOFINVITELIST; - caps = CAP_IE; + rpl_list_p = form_str(RPL_INVITELIST); + rpl_endlist_p = form_str(RPL_ENDOFINVITELIST); if(ConfigChannel.use_invex || (dir == MODE_DEL)) mems = ONLY_CHANOPS; @@ -547,10 +770,9 @@ chm_ban(struct Client *source_p, struct Channel *chptr, case CHFL_QUIET: list = &chptr->quietlist; errorval = SM_ERR_RPL_Q; - rpl_list = RPL_BANLIST; - rpl_endlist = RPL_ENDOFBANLIST; + rpl_list_p = form_str(RPL_QUIETLIST); + rpl_endlist_p = form_str(RPL_ENDOFQUIETLIST); mems = ALL_MEMBERS; - caps = 0; break; default: @@ -566,6 +788,7 @@ chm_ban(struct Client *source_p, struct Channel *chptr, *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) { @@ -578,26 +801,24 @@ chm_ban(struct Client *source_p, struct Channel *chptr, RB_DLINK_FOREACH(ptr, list->head) { + char buf[BANLEN]; banptr = ptr->data; - sendto_one(source_p, form_str(rpl_list), + if(banptr->forward) + rb_snprintf(buf, sizeof(buf), "%s$%s", banptr->banstr, banptr->forward); + else + rb_strlcpy(buf, banptr->banstr, sizeof(buf)); + + sendto_one(source_p, rpl_list_p, me.name, source_p->name, chptr->chname, - banptr->banstr, banptr->who, banptr->when); + buf, banptr->who, banptr->when); } - if (mode_type == CHFL_QUIET) - sendto_one(source_p, ":%s %d %s %s :End of Channel Quiet List", me.name, rpl_endlist, source_p->name, chptr->chname); - else - 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; @@ -624,7 +845,23 @@ chm_ban(struct Client *source_p, struct Channel *chptr, * 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 + * it delimits a forward channel. + */ + if ((forward = strchr(mask+1, '$')) != NULL) + { + *forward++ = '\0'; + if (*forward == '\0') + forward = NULL; + } /* if we're adding a NEW id */ if(dir == MODE_ADD) @@ -632,40 +869,85 @@ chm_ban(struct Client *source_p, struct Channel *chptr, 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 + * local clients. Accept any forward channel from servers. + */ + if(forward != NULL && MyClient(source_p)) + { + /* 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 * let remote servers set duplicate bans */ - if(!add_id(source_p, chptr, mask, list, mode_type)) + if(!add_id(source_p, chptr, mask, forward, list, mode_type)) return; + if(forward) + forward[-1]= '$'; + mode_changes[mode_count].letter = c; mode_changes[mode_count].dir = MODE_ADD; - mode_changes[mode_count].caps = caps; - mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = mems; mode_changes[mode_count].id = NULL; mode_changes[mode_count++].arg = mask; } else if(dir == MODE_DEL) { - if(del_id(chptr, mask, list, mode_type) == 0) + struct Ban *removed; + static char buf[BANLEN * MAXMODEPARAMS]; + int old_removed_mask_pos = removed_mask_pos; + if((removed = del_id(chptr, mask, list, mode_type)) == NULL) { /* mask isn't a valid ban, check raw_mask */ - if(del_id(chptr, raw_mask, list, mode_type)) + if((removed = del_id(chptr, raw_mask, list, mode_type)) != NULL) mask = raw_mask; } + if(removed && 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 + old_removed_mask_pos, mask, sizeof(buf)) + 1; + if(removed) + { + free_ban(removed); + removed = NULL; + } + mode_changes[mode_count].letter = c; mode_changes[mode_count].dir = MODE_DEL; - mode_changes[mode_count].caps = caps; - mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = mems; mode_changes[mode_count].id = NULL; - mode_changes[mode_count++].arg = mask; + mode_changes[mode_count++].arg = buf + old_removed_mask_pos; } } @@ -678,14 +960,8 @@ chm_op(struct Client *source_p, struct Channel *chptr, 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; @@ -721,17 +997,14 @@ chm_op(struct Client *source_p, struct Channel *chptr, if(dir == MODE_ADD) { - if(targ_p == source_p) + if(targ_p == source_p && mstptr->flags & CHFL_CHANOP) return; mode_changes[mode_count].letter = c; mode_changes[mode_count].dir = MODE_ADD; - mode_changes[mode_count].caps = 0; - mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = ALL_MEMBERS; mode_changes[mode_count].id = targ_p->id; - mode_changes[mode_count].arg = targ_p->name; - mode_changes[mode_count++].client = targ_p; + mode_changes[mode_count++].arg = targ_p->name; mstptr->flags |= CHFL_CHANOP; } @@ -746,12 +1019,9 @@ chm_op(struct Client *source_p, struct Channel *chptr, mode_changes[mode_count].letter = c; mode_changes[mode_count].dir = MODE_DEL; - mode_changes[mode_count].caps = 0; - mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = ALL_MEMBERS; mode_changes[mode_count].id = targ_p->id; - mode_changes[mode_count].arg = targ_p->name; - mode_changes[mode_count++].client = targ_p; + mode_changes[mode_count++].arg = targ_p->name; mstptr->flags &= ~CHFL_CHANOP; } @@ -766,14 +1036,8 @@ chm_voice(struct Client *source_p, struct Channel *chptr, 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; @@ -811,12 +1075,9 @@ chm_voice(struct Client *source_p, struct Channel *chptr, { mode_changes[mode_count].letter = c; mode_changes[mode_count].dir = MODE_ADD; - mode_changes[mode_count].caps = 0; - mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = ALL_MEMBERS; mode_changes[mode_count].id = targ_p->id; - mode_changes[mode_count].arg = targ_p->name; - mode_changes[mode_count++].client = targ_p; + mode_changes[mode_count++].arg = targ_p->name; mstptr->flags |= CHFL_VOICE; } @@ -824,12 +1085,9 @@ chm_voice(struct Client *source_p, struct Channel *chptr, { mode_changes[mode_count].letter = 'v'; mode_changes[mode_count].dir = MODE_DEL; - mode_changes[mode_count].caps = 0; - mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = ALL_MEMBERS; mode_changes[mode_count].id = targ_p->id; - mode_changes[mode_count].arg = targ_p->name; - mode_changes[mode_count++].client = targ_p; + mode_changes[mode_count++].arg = targ_p->name; mstptr->flags &= ~CHFL_VOICE; } @@ -844,18 +1102,15 @@ chm_limit(struct Client *source_p, struct Channel *chptr, 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; + if(MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE)) + return; + if((dir == MODE_ADD) && parc > *parn) { lstr = parv[(*parn)]; @@ -868,8 +1123,6 @@ chm_limit(struct Client *source_p, struct Channel *chptr, mode_changes[mode_count].letter = c; mode_changes[mode_count].dir = MODE_ADD; - mode_changes[mode_count].caps = 0; - mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = ALL_MEMBERS; mode_changes[mode_count].id = NULL; mode_changes[mode_count++].arg = limitstr; @@ -885,8 +1138,6 @@ chm_limit(struct Client *source_p, struct Channel *chptr, mode_changes[mode_count].letter = c; mode_changes[mode_count].dir = MODE_DEL; - mode_changes[mode_count].caps = 0; - mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = ALL_MEMBERS; mode_changes[mode_count].id = NULL; mode_changes[mode_count++].arg = NULL; @@ -900,18 +1151,15 @@ chm_throttle(struct Client *source_p, struct Channel *chptr, { 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; + if(MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE)) + return; + if((dir == MODE_ADD) && parc > *parn) { sscanf(parv[(*parn)], "%d:%d", &joins, ×lice); @@ -921,8 +1169,6 @@ chm_throttle(struct Client *source_p, struct Channel *chptr, mode_changes[mode_count].letter = c; mode_changes[mode_count].dir = MODE_ADD; - mode_changes[mode_count].caps = 0; - mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = ALL_MEMBERS; mode_changes[mode_count].id = NULL; mode_changes[mode_count++].arg = parv[(*parn)]; @@ -944,8 +1190,6 @@ chm_throttle(struct Client *source_p, struct Channel *chptr, mode_changes[mode_count].letter = c; mode_changes[mode_count].dir = MODE_DEL; - mode_changes[mode_count].caps = 0; - mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = ALL_MEMBERS; mode_changes[mode_count].id = NULL; mode_changes[mode_count++].arg = NULL; @@ -957,8 +1201,6 @@ chm_forward(struct Client *source_p, struct Channel *chptr, int alevel, int parc, int *parn, const char **parv, int *errors, int dir, char c, long mode_type) { - struct Channel *targptr = NULL; - struct membership *msptr; const char *forward; /* if +f is disabled, ignore local attempts to set it */ @@ -980,14 +1222,8 @@ chm_forward(struct Client *source_p, struct Channel *chptr, } #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)) { @@ -998,6 +1234,9 @@ chm_forward(struct Client *source_p, struct Channel *chptr, } #endif + if(MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE)) + return; + if(dir == MODE_ADD && parc > *parn) { forward = parv[(*parn)]; @@ -1005,43 +1244,16 @@ chm_forward(struct Client *source_p, struct Channel *chptr, if(EmptyString(forward)) return; - if(!check_channel_name(forward) || - (MyClient(source_p) && (strlen(forward) > LOC_CHANNELLEN || hash_find_resv(forward)))) - { - sendto_one_numeric(source_p, ERR_BADCHANNAME, form_str(ERR_BADCHANNAME), forward); - return; - } - /* don't forward to inconsistent target -- jilles */ - if(chptr->chname[0] == '#' && forward[0] == '&') - { - sendto_one_numeric(source_p, ERR_BADCHANNAME, - form_str(ERR_BADCHANNAME), forward); - return; - } - if(MyClient(source_p) && (targptr = find_channel(forward)) == NULL) - { - sendto_one_numeric(source_p, ERR_NOSUCHCHANNEL, - form_str(ERR_NOSUCHCHANNEL), forward); + + if(!check_forward(source_p, chptr, forward)) return; - } - if(MyClient(source_p) && !(targptr->mode.mode & MODE_FREETARGET)) - { - if((msptr = find_channel_membership(targptr, source_p)) == NULL || - get_channel_access(source_p, msptr) != CHFL_CHANOP) - { - sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), - me.name, source_p->name, targptr->chname); - return; - } - } - strlcpy(chptr->mode.forward, forward, sizeof(chptr->mode.forward)); + rb_strlcpy(chptr->mode.forward, forward, sizeof(chptr->mode.forward)); mode_changes[mode_count].letter = c; mode_changes[mode_count].dir = MODE_ADD; - mode_changes[mode_count].caps = 0; - mode_changes[mode_count].nocaps = 0; - mode_changes[mode_count].mems = ConfigChannel.use_forward ? ALL_MEMBERS : ONLY_SERVERS; + mode_changes[mode_count].mems = + ConfigChannel.use_forward ? ALL_MEMBERS : ONLY_SERVERS; mode_changes[mode_count].id = NULL; mode_changes[mode_count++].arg = forward; } @@ -1054,8 +1266,6 @@ chm_forward(struct Client *source_p, struct Channel *chptr, mode_changes[mode_count].letter = c; mode_changes[mode_count].dir = MODE_DEL; - mode_changes[mode_count].caps = 0; - mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = ALL_MEMBERS; mode_changes[mode_count].id = NULL; mode_changes[mode_count++].arg = NULL; @@ -1069,18 +1279,15 @@ chm_key(struct Client *source_p, struct Channel *chptr, { 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; + if(MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE)) + return; + if((dir == MODE_ADD) && parc > *parn) { key = LOCAL_COPY(parv[(*parn)]); @@ -1095,12 +1302,10 @@ chm_key(struct Client *source_p, struct Channel *chptr, return; s_assert(key[0] != ' '); - strlcpy(chptr->mode.key, key, sizeof(chptr->mode.key)); + rb_strlcpy(chptr->mode.key, key, sizeof(chptr->mode.key)); mode_changes[mode_count].letter = c; mode_changes[mode_count].dir = MODE_ADD; - mode_changes[mode_count].caps = 0; - mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = ALL_MEMBERS; mode_changes[mode_count].id = NULL; mode_changes[mode_count++].arg = chptr->mode.key; @@ -1130,49 +1335,12 @@ chm_key(struct Client *source_p, struct Channel *chptr, mode_changes[mode_count].letter = c; mode_changes[mode_count].dir = MODE_DEL; - mode_changes[mode_count].caps = 0; - mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = ALL_MEMBERS; mode_changes[mode_count].id = NULL; mode_changes[mode_count++].arg = "*"; } } -void -chm_regonly(struct Client *source_p, struct Channel *chptr, - 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; - return; - } - - if(dir == MODE_QUERY) - return; - - if(((dir == MODE_ADD) && (chptr->mode.mode & MODE_REGONLY)) || - ((dir == MODE_DEL) && !(chptr->mode.mode & MODE_REGONLY))) - return; - - if(dir == MODE_ADD) - chptr->mode.mode |= MODE_REGONLY; - else - chptr->mode.mode &= ~MODE_REGONLY; - - mode_changes[mode_count].letter = c; - mode_changes[mode_count].dir = dir; - mode_changes[mode_count].caps = CAP_SERVICE; - mode_changes[mode_count].nocaps = 0; - mode_changes[mode_count].mems = ALL_MEMBERS; - mode_changes[mode_count].id = NULL; - mode_changes[mode_count++].arg = NULL; -} - /* *INDENT-OFF* */ struct ChannelMode chmode_table[256] = { @@ -1244,7 +1412,7 @@ struct ChannelMode chmode_table[256] = {chm_nosuch, 0 }, /* @ */ {chm_nosuch, 0 }, /* A */ {chm_nosuch, 0 }, /* B */ - {chm_nosuch, 0 }, /* C */ + {chm_nosuch, 0 }, /* C */ {chm_nosuch, 0 }, /* D */ {chm_nosuch, 0 }, /* E */ {chm_simple, MODE_FREETARGET }, /* F */ @@ -1276,7 +1444,7 @@ struct ChannelMode chmode_table[256] = {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 */ @@ -1291,7 +1459,7 @@ struct ChannelMode chmode_table[256] = {chm_op, 0 }, /* o */ {chm_simple, MODE_PRIVATE }, /* p */ {chm_ban, CHFL_QUIET }, /* q */ - {chm_regonly, 0 }, /* r */ + {chm_simple, MODE_REGONLY }, /* r */ {chm_simple, MODE_SECRET }, /* s */ {chm_simple, MODE_TOPICLIMIT }, /* t */ {chm_nosuch, 0 }, /* u */ @@ -1449,7 +1617,7 @@ struct ChannelMode chmode_table[256] = /* set_channel_mode() * * inputs - client, source, channel, membership pointer, params - * output - + * output - * side effects - channel modes/memberships are changed, MODE is issued * * Extensively modified to be hotpluggable, 03/09/06 -- nenolod @@ -1473,8 +1641,10 @@ set_channel_mode(struct Client *client_p, struct Client *source_p, struct Client *fakesource_p; mask_pos = 0; + removed_mask_pos = 0; mode_count = 0; mode_limit = 0; + mode_limit_simple = 0; alevel = get_channel_access(source_p, msptr); @@ -1595,3 +1765,24 @@ set_channel_mode(struct Client *client_p, struct Client *source_p, if(MyClient(source_p) || rb_dlink_list_length(&serv_list) > 1) send_cap_mode_changes(client_p, source_p, chptr, mode_changes, mode_count); } + +/* set_channel_mlock() + * + * inputs - client, source, channel, params + * output - + * side effects - channel mlock is changed / MLOCK is propagated + */ +void +set_channel_mlock(struct Client *client_p, struct Client *source_p, + struct Channel *chptr, const char *newmlock, int propagate) +{ + rb_free(chptr->mode_lock); + chptr->mode_lock = newmlock ? rb_strdup(newmlock) : NULL; + + if (propagate) + { + sendto_server(client_p, NULL, CAP_TS6 | CAP_MLOCK, NOCAPS, ":%s MLOCK %ld %s :%s", + source_p->id, (long) chptr->channelts, chptr->chname, + chptr->mode_lock ? chptr->mode_lock : ""); + } +}