X-Git-Url: https://jfr.im/git/irc/rqf/shadowircd.git/blobdiff_plain/38e6acddad741b38a475fe3d10dfffb3c6d6c639..ab72b3e800c5848fa2376dc982c49b0ffa6079ed:/src/chmode.c diff --git a/src/chmode.c b/src/chmode.c index 9bf11c3..6ee15eb 100644 --- a/src/chmode.c +++ b/src/chmode.c @@ -22,18 +22,15 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * - * $Id: chmode.c 3580 2007-11-07 23:45:14Z jilles $ */ #include "stdinc.h" -#include "tools.h" #include "channel.h" #include "client.h" #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,10 +39,10 @@ #include "whowas.h" #include "s_conf.h" /* ConfigFileEntry, ConfigChannel */ #include "s_newconf.h" -#include "event.h" -#include "memory.h" -#include "balloc.h" -#include "s_log.h" +#include "logger.h" +#include "chmode.h" +#include "irc_dictionary.h" + /* bitmasks for error returns, so we send once per call */ #define SM_ERR_NOTS 0x00000001 /* No TS on channel */ @@ -60,23 +57,165 @@ #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, 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 no_override_deop; -int +char cflagsbuf[256]; +char cflagsmyinfo[256]; +char cflagsparaminfo[256]; + +int chmode_flags[256]; + +/* 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_admin) && + !(chmode_table[i].set_func == chm_op) && + !(chmode_table[i].set_func == chm_halfop) && + !(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_EXLIMIT: + case MODE_DISFORWARD: + if(ConfigChannel.use_forward) + { + *ptr++ = (char) i; + } + + break; + case MODE_REGONLY: + if(rb_dlink_list_length(&service_list)) + { + *ptr++ = (char) i; + } + + break; + default: + if(chmode_flags[i] != 0 && !(chmode_table[i].set_func == chm_orphaned)) + { + *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) && + !(chmode_table[i].set_func == chm_admin && !ConfigChannel.use_admin) && + !(chmode_table[i].set_func == chm_halfop && !ConfigChannel.use_halfop)) + { + *ptr2++ = (char) i; + } + } + + *ptr++ = '\0'; + *ptr2++ = '\0'; +} + +void +construct_cflag_param_string(void) +{ + + *cflagsparaminfo = '\0'; + rb_snprintf(cflagsparaminfo, sizeof cflagsparaminfo, "%sb%s%s%s%sklov%s%s", + ConfigChannel.use_admin ? "a" : "", + ConfigChannel.use_except ? "e" : "", + ConfigChannel.use_forward ? "f" : "", + ConfigChannel.use_halfop ? "h" : "", + strcasecmp(ConfigChannel.disabledmodes, "j") ? "" : "j", + strcasecmp(ConfigChannel.disabledmodes, "q") ? "" : "q", + ConfigChannel.use_invex ? "I" : ""); +} + +/* + * 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(); +} + +static int get_channel_access(struct Client *source_p, struct membership *msptr) { - if(!MyClient(source_p) || is_chanop(msptr)) + if(!MyClient(source_p) || is_admin(msptr)) + return CHFL_ADMIN; + else if(is_chanop(msptr)) return CHFL_CHANOP; + else if(is_halfop(msptr)) + return CHFL_HALFOP; return CHFL_PEON; } @@ -89,26 +228,26 @@ get_channel_access(struct Client *source_p, struct membership *msptr) */ int add_id(struct Client *source_p, struct Channel *chptr, const char *banid, - dlink_list * list, long mode_type) + rb_dlink_list * list, long mode_type) { struct Ban *actualBan; static char who[USERHOST_REPLYLEN]; char *realban = LOCAL_COPY(banid); - dlink_node *ptr; + rb_dlink_node *ptr; /* dont let local clients overflow the banlist, or set redundant * bans */ if(MyClient(source_p)) { - if((dlink_list_length(&chptr->banlist) + dlink_list_length(&chptr->exceptlist) + dlink_list_length(&chptr->invexlist) + dlink_list_length(&chptr->quietlist)) >= (chptr->mode.mode & MODE_EXLIMIT ? ConfigChannel.max_bans_large : ConfigChannel.max_bans)) + if((rb_dlink_list_length(&chptr->banlist) + rb_dlink_list_length(&chptr->exceptlist) + rb_dlink_list_length(&chptr->invexlist) + rb_dlink_list_length(&chptr->quietlist)) >= (chptr->mode.mode & MODE_EXLIMIT ? ConfigChannel.max_bans_large : ConfigChannel.max_bans)) { sendto_one(source_p, form_str(ERR_BANLISTFULL), me.name, source_p->name, chptr->chname, realban); return 0; } - DLINK_FOREACH(ptr, list->head) + RB_DLINK_FOREACH(ptr, list->head) { actualBan = ptr->data; if(mask_match(actualBan->banstr, realban)) @@ -118,7 +257,7 @@ add_id(struct Client *source_p, struct Channel *chptr, const char *banid, /* dont let remotes set duplicates */ else { - DLINK_FOREACH(ptr, list->head) + RB_DLINK_FOREACH(ptr, list->head) { actualBan = ptr->data; if(!irccmp(actualBan->banstr, realban)) @@ -130,12 +269,12 @@ 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->when = CurrentTime; + actualBan->when = rb_current_time(); - dlinkAdd(actualBan, &actualBan->node, list); + rb_dlinkAdd(actualBan, &actualBan->node, list); /* invalidate the can_send() cache */ if(mode_type == CHFL_BAN || mode_type == CHFL_QUIET || mode_type == CHFL_EXCEPTION) @@ -151,21 +290,21 @@ add_id(struct Client *source_p, struct Channel *chptr, const char *banid, * side effects - given id is removed from the appropriate list */ int -del_id(struct Channel *chptr, const char *banid, dlink_list * list, long mode_type) +del_id(struct Channel *chptr, const char *banid, rb_dlink_list * list, long mode_type) { - dlink_node *ptr; + rb_dlink_node *ptr; struct Ban *banptr; if(EmptyString(banid)) return 0; - DLINK_FOREACH(ptr, list->head) + RB_DLINK_FOREACH(ptr, list->head) { banptr = ptr->data; if(irccmp(banid, banptr->banstr) == 0) { - dlinkDelete(&banptr->node, list); + rb_dlinkDelete(&banptr->node, list); free_ban(banptr); /* invalidate the can_send() cache */ @@ -281,7 +420,7 @@ pretty_mask(const char *idmask) if(*t != '\0') user = t; } - else if(strchr(mask, '.') != NULL || strchr(mask, ':') != NULL) + else if(strchr(mask, '.') != NULL || strchr(mask, ':') != NULL || strchr(mask, '/') != NULL) { if(*mask != '\0') host = mask; @@ -392,17 +531,26 @@ 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) + + int override = 0; + struct Metadata *md; + struct DictionaryIter iter; + + if(alevel != CHFL_CHANOP && alevel != CHFL_ADMIN && alevel != CHFL_HALFOP) { - 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 (IsOverride(source_p)) + override = 1; + else + { + 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; + } } - /* +ntspmaikl == 9 + MAXMODEPARAMS (4 * +o) */ - if(MyClient(source_p) && (++mode_limit > (9 + MAXMODEPARAMS))) + if(MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE)) return; /* setting + */ @@ -421,10 +569,23 @@ chm_simple(struct Client *source_p, struct Channel *chptr, mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].id = NULL; mode_changes[mode_count].mems = ALL_MEMBERS; + mode_changes[mode_count].override = override; mode_changes[mode_count++].arg = NULL; } else if((dir == MODE_DEL) && (chptr->mode.mode & mode_type)) { + /* cleanup metadata when the related mode is removed */ + if(c == 'J') + { + DICTIONARY_FOREACH(md, &iter, chptr->metadata) + { + if(!strcmp(md->value, "KICKNOREJOIN")) + channel_metadata_delete(chptr, md->name, 0); + } + } + if(c == 'K') + channel_metadata_delete(chptr, "NOREPEAT", 0); + chptr->mode.mode &= ~mode_type; mode_changes[mode_count].letter = c; @@ -433,15 +594,135 @@ chm_simple(struct Client *source_p, struct Channel *chptr, mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = ALL_MEMBERS; mode_changes[mode_count].id = NULL; + mode_changes[mode_count].override = override; 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)) + { + sendto_one_numeric(source_p, 469, "Mode %c is disabled.", c); + 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].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; + } + 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].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_hidden(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(!IsOper(source_p) && !IsServer(source_p)) + { + if(!(*errors & SM_ERR_NOPRIVS)) + sendto_one_numeric(source_p, ERR_NOPRIVILEGES, form_str(ERR_NOPRIVILEGES)); + *errors |= SM_ERR_NOPRIVS; + return; + } + if(MyClient(source_p) && !IsOperAdmin(source_p)) + { + if(!(*errors & SM_ERR_NOPRIVS)) + sendto_one(source_p, form_str(ERR_NOPRIVS), me.name, + source_p->name, "cmodes"); + *errors |= SM_ERR_NOPRIVS; + return; + } + + if(MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE)) + return; + + + /* setting + */ + 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].caps = 0; + mode_changes[mode_count].nocaps = 0; + mode_changes[mode_count].id = NULL; + mode_changes[mode_count].mems = ONLY_OPERS; + mode_changes[mode_count].override = 0; + mode_changes[mode_count++].arg = NULL; + + /* A little ugly */ + sendto_wallops_flags(UMODE_WALLOP, &me, + "+%c set on [%s] by %s!%s@%s", + c, chptr->chname, source_p->name, source_p->username, source_p->host); + ilog(L_MAIN, "+%c set on [%s] by %s", + c, chptr->chname, get_oper_name(source_p)); + + if(*chptr->chname != '&') + sendto_server(NULL, NULL, NOCAPS, NOCAPS, + ":%s WALLOPS :+%c set on [%s] by %s!%s@%s", + me.name, c, chptr->chname, source_p->name, source_p->username, + source_p->host); + } + 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].caps = 0; + mode_changes[mode_count].nocaps = 0; + mode_changes[mode_count].mems = ONLY_OPERS; + mode_changes[mode_count].id = NULL; + mode_changes[mode_count].override = 0; + mode_changes[mode_count++].arg = NULL; + + /* A little ugly */ + sendto_wallops_flags(UMODE_WALLOP, &me, + "+%c unset from [%s] by %s!%s@%s", + c, chptr->chname, source_p->name, source_p->username, source_p->host); + ilog(L_MAIN, "+%c unset from [%s] by %s", + c, chptr->chname, get_oper_name(source_p)); + + if(*chptr->chname != '&') + sendto_server(NULL, NULL, NOCAPS, NOCAPS, + ":%s WALLOPS :+%c unset from [%s] by %s!%s@%s", + me.name, c, chptr->chname, source_p->name, source_p->username, + source_p->host); + } +} + void chm_staff(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) { + int override = 0; + if(!IsOper(source_p) && !IsServer(source_p)) { if(!(*errors & SM_ERR_NOPRIVS)) @@ -458,6 +739,23 @@ chm_staff(struct Client *source_p, struct Channel *chptr, return; } + if(alevel != CHFL_CHANOP && alevel != CHFL_ADMIN && alevel != CHFL_HALFOP) + { + if (IsOverride(source_p)) + override = 1; + else + { + 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(MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE)) + return; + /* setting + */ if((dir == MODE_ADD) && !(chptr->mode.mode & mode_type)) { @@ -469,6 +767,7 @@ chm_staff(struct Client *source_p, struct Channel *chptr, mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].id = NULL; mode_changes[mode_count].mems = ALL_MEMBERS; + mode_changes[mode_count].override = override; mode_changes[mode_count++].arg = NULL; } else if((dir == MODE_DEL) && (chptr->mode.mode & mode_type)) @@ -480,6 +779,7 @@ chm_staff(struct Client *source_p, struct Channel *chptr, mode_changes[mode_count].caps = 0; mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = ALL_MEMBERS; + mode_changes[mode_count].override = override; mode_changes[mode_count].id = NULL; mode_changes[mode_count++].arg = NULL; } @@ -492,14 +792,15 @@ chm_ban(struct Client *source_p, struct Channel *chptr, { const char *mask; const char *raw_mask; - dlink_list *list; - dlink_node *ptr; + rb_dlink_list *list; + rb_dlink_node *ptr; struct Ban *banptr; int errorval; int rpl_list; int rpl_endlist; int caps; int mems; + int override = 0; switch (mode_type) { @@ -551,8 +852,8 @@ 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 = RPL_QUIETLIST; + rpl_endlist = RPL_ENDOFQUIETLIST; mems = ALL_MEMBERS; caps = 0; break; @@ -570,37 +871,53 @@ chm_ban(struct Client *source_p, struct Channel *chptr, *errors |= errorval; /* non-ops cant see +eI lists.. */ - if(alevel != CHFL_CHANOP && mode_type != CHFL_BAN && + if(alevel != CHFL_CHANOP && alevel != CHFL_ADMIN && alevel != CHFL_HALFOP && mode_type != CHFL_BAN && mode_type != CHFL_QUIET) { - 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(IsOverride(source_p)) + { + sendto_wallops_flags(UMODE_WALLOP, &me, + "%s is overriding modes on %s: (%s list)", + get_oper_name(source_p), chptr->chname, mode_type == CHFL_INVEX ? "invex" : "exempt"); + sendto_server(NULL, chptr, NOCAPS, NOCAPS, + ":%s WALLOPS :%s is overriding modes on %s: (%s list)", + me.name, get_oper_name(source_p), chptr->chname, mode_type == CHFL_INVEX ? "invex" : "exempt"); + } + else + { + + 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; + } } - DLINK_FOREACH(ptr, list->head) + RB_DLINK_FOREACH(ptr, list->head) { banptr = ptr->data; sendto_one(source_p, form_str(rpl_list), me.name, source_p->name, chptr->chname, banptr->banstr, 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, form_str(rpl_endlist), me.name, source_p->name, chptr->chname); return; } - if(alevel != CHFL_CHANOP) + if(alevel != CHFL_CHANOP && alevel != CHFL_ADMIN && alevel != CHFL_HALFOP) { - 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(IsOverride(source_p)) + override = 1; + else + { + + 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(MyClient(source_p) && (++mode_limit > MAXMODEPARAMS)) @@ -652,6 +969,7 @@ chm_ban(struct Client *source_p, struct Channel *chptr, mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = mems; mode_changes[mode_count].id = NULL; + mode_changes[mode_count].override = override; mode_changes[mode_count++].arg = mask; } else if(dir == MODE_DEL) @@ -669,10 +987,123 @@ chm_ban(struct Client *source_p, struct Channel *chptr, mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = mems; mode_changes[mode_count].id = NULL; + mode_changes[mode_count].override = override; mode_changes[mode_count++].arg = mask; } } +void +chm_admin(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 membership *mstptr; + const char *adminnick; + struct Client *targ_p; + int override = 0; + + if(!ConfigChannel.use_admin) + { + if(*errors & SM_ERR_UNKNOWN) + return; + *errors |= SM_ERR_UNKNOWN; + sendto_one(source_p, form_str(ERR_UNKNOWNMODE), me.name, source_p->name, c); + return; + } + + if(alevel != CHFL_ADMIN) + { + if(IsOverride(source_p)) + override = 1; + else + { + + if(!(*errors & SM_ERR_NOOPS)) + sendto_one(source_p, ":%s 482 %s %s :You're not a channel administrator", me.name, source_p->name, chptr->chname); + *errors |= SM_ERR_NOOPS; + return; + } + } + + if((dir == MODE_QUERY) || (parc <= *parn)) + return; + + adminnick = parv[(*parn)]; + (*parn)++; + + /* empty nick */ + if(EmptyString(adminnick)) + { + sendto_one_numeric(source_p, ERR_NOSUCHNICK, form_str(ERR_NOSUCHNICK), "*"); + return; + } + + if((targ_p = find_chasing(source_p, adminnick, NULL)) == NULL) + { + return; + } + + mstptr = find_channel_membership(chptr, targ_p); + + if(mstptr == NULL) + { + if(!(*errors & SM_ERR_NOTONCHANNEL) && MyClient(source_p)) + sendto_one_numeric(source_p, ERR_USERNOTINCHANNEL, + form_str(ERR_USERNOTINCHANNEL), adminnick, chptr->chname); + *errors |= SM_ERR_NOTONCHANNEL; + return; + } + + if(MyClient(source_p) && (++mode_limit > MAXMODEPARAMS)) + return; + + if(dir == MODE_ADD) + { + if(targ_p == source_p) + { + no_override_deop = 1; + /* Don't reject modes from remote. It desyncs, and this is perfectly + * legitimate from a remote override oper. + if(!override) + 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].override = override; + mode_changes[mode_count++].client = targ_p; + + mstptr->flags |= CHFL_ADMIN; + } + else + { + if(MyClient(source_p) && IsService(targ_p)) + { + sendto_one(source_p, form_str(ERR_ISCHANSERVICE), + me.name, source_p->name, targ_p->name, chptr->chname); + return; + } + + 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].override = override; + mode_changes[mode_count++].client = targ_p; + + mstptr->flags &= ~CHFL_ADMIN; + } +} + void chm_op(struct Client *source_p, struct Channel *chptr, int alevel, int parc, int *parn, @@ -681,14 +1112,21 @@ chm_op(struct Client *source_p, struct Channel *chptr, struct membership *mstptr; const char *opnick; struct Client *targ_p; + int override = 0; - if(alevel != CHFL_CHANOP) + if(alevel != CHFL_CHANOP && alevel != CHFL_ADMIN) { - 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(IsOverride(source_p)) + override = 1; + else + { + + 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) || (parc <= *parn)) @@ -726,7 +1164,14 @@ chm_op(struct Client *source_p, struct Channel *chptr, if(dir == MODE_ADD) { if(targ_p == source_p) - return; + { + no_override_deop = 1; + /* Don't reject modes from remote. It desyncs, and this is perfectly + * legitimate from a remote override oper. + if(!override) + return; + */ + } mode_changes[mode_count].letter = c; mode_changes[mode_count].dir = MODE_ADD; @@ -735,6 +1180,7 @@ chm_op(struct Client *source_p, struct Channel *chptr, 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].override = override; mode_changes[mode_count++].client = targ_p; mstptr->flags |= CHFL_CHANOP; @@ -755,12 +1201,126 @@ chm_op(struct Client *source_p, struct Channel *chptr, 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].override = override; mode_changes[mode_count++].client = targ_p; mstptr->flags &= ~CHFL_CHANOP; } } +void +chm_halfop(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 membership *mstptr; + const char *halfopnick; + struct Client *targ_p; + int override = 0; + + if(!ConfigChannel.use_halfop) + { + if(*errors & SM_ERR_UNKNOWN) + return; + *errors |= SM_ERR_UNKNOWN; + sendto_one(source_p, form_str(ERR_UNKNOWNMODE), me.name, source_p->name, c); + return; + } + + if(alevel != CHFL_CHANOP && alevel != CHFL_ADMIN) + { + if(IsOverride(source_p)) + override = 1; + else + { + + 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) || (parc <= *parn)) + return; + + halfopnick = parv[(*parn)]; + (*parn)++; + + /* empty nick */ + if(EmptyString(halfopnick)) + { + sendto_one_numeric(source_p, ERR_NOSUCHNICK, form_str(ERR_NOSUCHNICK), "*"); + return; + } + + if((targ_p = find_chasing(source_p, halfopnick, NULL)) == NULL) + { + return; + } + + mstptr = find_channel_membership(chptr, targ_p); + + if(mstptr == NULL) + { + if(!(*errors & SM_ERR_NOTONCHANNEL) && MyClient(source_p)) + sendto_one_numeric(source_p, ERR_USERNOTINCHANNEL, + form_str(ERR_USERNOTINCHANNEL), halfopnick, chptr->chname); + *errors |= SM_ERR_NOTONCHANNEL; + return; + } + + if(MyClient(source_p) && (++mode_limit > MAXMODEPARAMS)) + return; + + if(dir == MODE_ADD) + { + if(targ_p == source_p) + { + no_override_deop = 1; + /* Don't reject modes from remote. It desyncs, and this is perfectly + * legitimate from a remote override oper. + if(!override) + 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].override = override; + mode_changes[mode_count++].client = targ_p; + + mstptr->flags |= CHFL_HALFOP; + } + else + { + if(MyClient(source_p) && IsService(targ_p)) + { + sendto_one(source_p, form_str(ERR_ISCHANSERVICE), + me.name, source_p->name, targ_p->name, chptr->chname); + return; + } + + 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].override = override; + mode_changes[mode_count++].client = targ_p; + + mstptr->flags &= ~CHFL_HALFOP; + } +} + void chm_voice(struct Client *source_p, struct Channel *chptr, int alevel, int parc, int *parn, @@ -769,14 +1329,21 @@ chm_voice(struct Client *source_p, struct Channel *chptr, struct membership *mstptr; const char *opnick; struct Client *targ_p; + int override = 0; - if(alevel != CHFL_CHANOP) + if(alevel != CHFL_CHANOP && alevel != CHFL_ADMIN && alevel != CHFL_HALFOP) { - 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(IsOverride(source_p)) + override = 1; + else + { + + 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) || parc <= *parn) @@ -820,6 +1387,7 @@ chm_voice(struct Client *source_p, struct Channel *chptr, 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].override = override; mode_changes[mode_count++].client = targ_p; mstptr->flags |= CHFL_VOICE; @@ -833,6 +1401,7 @@ chm_voice(struct Client *source_p, struct Channel *chptr, 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].override = override; mode_changes[mode_count++].client = targ_p; mstptr->flags &= ~CHFL_VOICE; @@ -847,19 +1416,28 @@ chm_limit(struct Client *source_p, struct Channel *chptr, const char *lstr; static char limitstr[30]; int limit; + int override = 0; - if(alevel != CHFL_CHANOP) + if(alevel != CHFL_CHANOP && alevel != CHFL_ADMIN && alevel != CHFL_HALFOP) { - 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(IsOverride(source_p)) + override = 1; + else + { + 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(MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE)) + return; + if((dir == MODE_ADD) && parc > *parn) { lstr = parv[(*parn)]; @@ -876,6 +1454,7 @@ chm_limit(struct Client *source_p, struct Channel *chptr, mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = ALL_MEMBERS; mode_changes[mode_count].id = NULL; + mode_changes[mode_count].override = override; mode_changes[mode_count++].arg = limitstr; chptr->mode.limit = limit; @@ -893,6 +1472,7 @@ chm_limit(struct Client *source_p, struct Channel *chptr, mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = ALL_MEMBERS; mode_changes[mode_count].id = NULL; + mode_changes[mode_count].override = override; mode_changes[mode_count++].arg = NULL; } } @@ -903,19 +1483,29 @@ chm_throttle(struct Client *source_p, struct Channel *chptr, const char **parv, int *errors, int dir, char c, long mode_type) { int joins = 0, timeslice = 0; + int override = 0; - if(alevel != CHFL_CHANOP) + if(alevel != CHFL_CHANOP && alevel != CHFL_ADMIN && alevel != CHFL_HALFOP) { - 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(IsOverride(source_p)) + override = 1; + else + { + + 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(MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE)) + return; + if((dir == MODE_ADD) && parc > *parn) { sscanf(parv[(*parn)], "%d:%d", &joins, ×lice); @@ -929,6 +1519,7 @@ chm_throttle(struct Client *source_p, struct Channel *chptr, mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = ALL_MEMBERS; mode_changes[mode_count].id = NULL; + mode_changes[mode_count].override = override; mode_changes[mode_count++].arg = parv[(*parn)]; (*parn)++; @@ -952,6 +1543,7 @@ chm_throttle(struct Client *source_p, struct Channel *chptr, mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = ALL_MEMBERS; mode_changes[mode_count].id = NULL; + mode_changes[mode_count].override = override; mode_changes[mode_count++].arg = NULL; } } @@ -964,6 +1556,7 @@ chm_forward(struct Client *source_p, struct Channel *chptr, struct Channel *targptr = NULL; struct membership *msptr; const char *forward; + int override = 0; /* if +f is disabled, ignore local attempts to set it */ if(!ConfigChannel.use_forward && MyClient(source_p) && @@ -984,13 +1577,18 @@ chm_forward(struct Client *source_p, struct Channel *chptr, } #ifndef FORWARD_OPERONLY - if(alevel != CHFL_CHANOP) + if(alevel != CHFL_CHANOP && alevel != CHFL_ADMIN && alevel != CHFL_HALFOP) { - 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(IsOverride(source_p)) + override = 1; + else + { + 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; + } } #else if(!IsOper(source_p) && !IsServer(source_p)) @@ -1002,6 +1600,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)]; @@ -1031,15 +1632,20 @@ chm_forward(struct Client *source_p, struct Channel *chptr, 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) + !is_any_op(msptr)) { - sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), - me.name, source_p->name, targptr->chname); - return; + if(IsOverride(source_p)) + override = 1; + else + { + 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; @@ -1047,6 +1653,7 @@ chm_forward(struct Client *source_p, struct Channel *chptr, mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = ConfigChannel.use_forward ? ALL_MEMBERS : ONLY_SERVERS; mode_changes[mode_count].id = NULL; + mode_changes[mode_count].override = override; mode_changes[mode_count++].arg = forward; } else if(dir == MODE_DEL) @@ -1062,6 +1669,7 @@ chm_forward(struct Client *source_p, struct Channel *chptr, mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = ALL_MEMBERS; mode_changes[mode_count].id = NULL; + mode_changes[mode_count].override = override; mode_changes[mode_count++].arg = NULL; } } @@ -1072,19 +1680,28 @@ chm_key(struct Client *source_p, struct Channel *chptr, const char **parv, int *errors, int dir, char c, long mode_type) { char *key; + int override = 0; - if(alevel != CHFL_CHANOP) + if(alevel != CHFL_CHANOP && alevel != CHFL_ADMIN && alevel != CHFL_HALFOP) { - 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(IsOverride(source_p)) + override = 1; + else + { + 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(MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE)) + return; + if((dir == MODE_ADD) && parc > *parn) { key = LOCAL_COPY(parv[(*parn)]); @@ -1099,7 +1716,7 @@ 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; @@ -1107,6 +1724,7 @@ chm_key(struct Client *source_p, struct Channel *chptr, mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = ALL_MEMBERS; mode_changes[mode_count].id = NULL; + mode_changes[mode_count].override = override; mode_changes[mode_count++].arg = chptr->mode.key; } else if(dir == MODE_DEL) @@ -1138,6 +1756,7 @@ chm_key(struct Client *source_p, struct Channel *chptr, mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = ALL_MEMBERS; mode_changes[mode_count].id = NULL; + mode_changes[mode_count].override = override; mode_changes[mode_count++].arg = "*"; } } @@ -1147,26 +1766,36 @@ 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) + int override = 0; + + if(alevel != CHFL_CHANOP && alevel != CHFL_ADMIN && alevel != CHFL_HALFOP) { - 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(IsOverride(source_p)) + override = 1; + else + { + 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))) + if(((dir == MODE_ADD) && (chptr->mode.mode & mode_type)) || + ((dir == MODE_DEL) && !(chptr->mode.mode & mode_type))) + return; + + if(MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE)) return; if(dir == MODE_ADD) - chptr->mode.mode |= MODE_REGONLY; + chptr->mode.mode |= mode_type; else - chptr->mode.mode &= ~MODE_REGONLY; + chptr->mode.mode &= ~mode_type; mode_changes[mode_count].letter = c; mode_changes[mode_count].dir = dir; @@ -1174,6 +1803,7 @@ chm_regonly(struct Client *source_p, struct Channel *chptr, mode_changes[mode_count].nocaps = 0; mode_changes[mode_count].mems = ALL_MEMBERS; mode_changes[mode_count].id = NULL; + mode_changes[mode_count].override = override; mode_changes[mode_count++].arg = NULL; } @@ -1248,24 +1878,24 @@ struct ChannelMode chmode_table[256] = {chm_nosuch, 0 }, /* @ */ {chm_nosuch, 0 }, /* A */ {chm_nosuch, 0 }, /* B */ - {chm_nosuch, 0 }, /* C */ - {chm_nosuch, 0 }, /* D */ - {chm_nosuch, 0 }, /* E */ + {chm_simple, MODE_NOCTCP }, /* C */ + {chm_simple, MODE_NOACTION }, /* D */ + {chm_simple, MODE_NOKICK }, /* E */ {chm_simple, MODE_FREETARGET }, /* F */ - {chm_nosuch, 0 }, /* G */ + {chm_simple, MODE_NOCAPS }, /* G */ {chm_nosuch, 0 }, /* H */ {chm_ban, CHFL_INVEX }, /* I */ - {chm_nosuch, 0 }, /* J */ - {chm_nosuch, 0 }, /* K */ + {chm_simple, MODE_NOREJOIN }, /* J */ + {chm_simple, MODE_NOREPEAT }, /* K */ {chm_staff, MODE_EXLIMIT }, /* L */ - {chm_nosuch, 0 }, /* M */ - {chm_nosuch, 0 }, /* N */ + {chm_hidden, MODE_NOOPERKICK }, /* M */ + {chm_simple, MODE_NONICK }, /* N */ {chm_nosuch, 0 }, /* O */ {chm_staff, MODE_PERMANENT }, /* P */ {chm_simple, MODE_DISFORWARD }, /* Q */ {chm_nosuch, 0 }, /* R */ {chm_nosuch, 0 }, /* S */ - {chm_nosuch, 0 }, /* T */ + {chm_simple, MODE_NONOTICE }, /* T */ {chm_nosuch, 0 }, /* U */ {chm_nosuch, 0 }, /* V */ {chm_nosuch, 0 }, /* W */ @@ -1278,14 +1908,14 @@ struct ChannelMode chmode_table[256] = {chm_nosuch, 0 }, {chm_nosuch, 0 }, {chm_nosuch, 0 }, - {chm_nosuch, 0 }, /* a */ + {chm_admin, 0 }, /* a */ {chm_ban, CHFL_BAN }, /* b */ {chm_simple, MODE_NOCOLOR }, /* c */ {chm_nosuch, 0 }, /* d */ {chm_ban, CHFL_EXCEPTION }, /* e */ {chm_forward, 0 }, /* f */ {chm_simple, MODE_FREEINVITE }, /* g */ - {chm_nosuch, 0 }, /* h */ + {chm_halfop, 0 }, /* h */ {chm_simple, MODE_INVITEONLY }, /* i */ {chm_throttle, 0 }, /* j */ {chm_key, 0 }, /* k */ @@ -1295,7 +1925,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_regonly, MODE_REGONLY }, /* r */ {chm_simple, MODE_SECRET }, /* s */ {chm_simple, MODE_TOPICLIMIT }, /* t */ {chm_nosuch, 0 }, /* u */ @@ -1462,12 +2092,13 @@ void set_channel_mode(struct Client *client_p, struct Client *source_p, struct Channel *chptr, struct membership *msptr, int parc, const char *parv[]) { + static char cmdbuf[BUFSIZE]; static char modebuf[BUFSIZE]; static char parabuf[BUFSIZE]; char *mbuf; char *pbuf; int cur_len, mlen, paralen, paracount, arglen, len; - int i, j, flags; + int i, j, flags, override; int dir = MODE_ADD; int parn = 1; int errors = 0; @@ -1475,13 +2106,17 @@ set_channel_mode(struct Client *client_p, struct Client *source_p, const char *ml = parv[0]; char c; struct Client *fakesource_p; + int flags_list[3] = { ALL_MEMBERS, ONLY_CHANOPS, ONLY_OPERS }; mask_pos = 0; mode_count = 0; mode_limit = 0; + mode_limit_simple = 0; alevel = get_channel_access(source_p, msptr); + no_override_deop = 0; + /* Hide connecting server on netburst -- jilles */ if (ConfigServerHide.flatten_links && IsServer(source_p) && !has_id(source_p) && !HasSentEob(source_p)) fakesource_p = &me; @@ -1502,6 +2137,19 @@ set_channel_mode(struct Client *client_p, struct Client *source_p, 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, @@ -1515,87 +2163,158 @@ set_channel_mode(struct Client *client_p, struct Client *source_p, return; if(IsServer(source_p)) - mlen = rb_sprintf(modebuf, ":%s MODE %s ", fakesource_p->name, chptr->chname); + rb_sprintf(cmdbuf, ":%s MODE %s ", fakesource_p->name, chptr->chname); else - mlen = rb_sprintf(modebuf, ":%s!%s@%s MODE %s ", + rb_sprintf(cmdbuf, ":%s!%s@%s MODE %s ", source_p->name, source_p->username, source_p->host, chptr->chname); - for(j = 0, flags = ALL_MEMBERS; j < 2; j++, flags = ONLY_CHANOPS) + mlen = 0; + + for (override = 0; override < (IsOverride(source_p) && alevel != CHFL_CHANOP ? 2 : 1); ++override) { - cur_len = mlen; - mbuf = modebuf + mlen; - pbuf = parabuf; - parabuf[0] = '\0'; - paracount = paralen = 0; - dir = MODE_QUERY; + int was_on_chan = 0; - for(i = 0; i < mode_count; i++) + if(override) { - if(mode_changes[i].letter == 0 || mode_changes[i].mems != flags) - continue; - - if(mode_changes[i].arg != NULL) + if(msptr) { - arglen = strlen(mode_changes[i].arg); - - if(arglen > MODEBUFLEN - 5) - continue; + was_on_chan = 1; + msptr->flags |= CHFL_CHANOP; } else - arglen = 0; - - /* if we're creeping over MAXMODEPARAMSSERV, or over - * bufsize (4 == +/-,modechar,two spaces) send now. - */ - if(mode_changes[i].arg != NULL && - ((paracount == MAXMODEPARAMSSERV) || - ((cur_len + paralen + arglen + 4) > (BUFSIZE - 3)))) + add_user_to_channel(chptr, source_p, CHFL_CHANOP); + } + + for(j = 0, flags = flags_list[0]; j < 3; j++, flags = flags_list[j]) + { + cur_len = mlen; + mbuf = modebuf + mlen; + pbuf = parabuf; + parabuf[0] = '\0'; + paracount = paralen = 0; + dir = MODE_QUERY; + + for(i = 0; i < mode_count; i++) { - *mbuf = '\0'; + if(mode_changes[i].letter == 0 || mode_changes[i].mems != flags) + continue; - if(cur_len > mlen) - sendto_channel_local(flags, chptr, "%s %s", modebuf, - parabuf); - else + if(mode_changes[i].override != override) continue; - paracount = paralen = 0; - cur_len = mlen; - mbuf = modebuf + mlen; - pbuf = parabuf; - parabuf[0] = '\0'; - dir = MODE_QUERY; - } + if(mode_changes[i].arg != NULL) + { + arglen = strlen(mode_changes[i].arg); - if(dir != mode_changes[i].dir) - { - *mbuf++ = (mode_changes[i].dir == MODE_ADD) ? '+' : '-'; + if(arglen > MODEBUFLEN - 5) + continue; + } + else + arglen = 0; + + /* if we're creeping over MAXMODEPARAMSSERV, or over + * bufsize (4 == +/-,modechar,two spaces) send now. + */ + if(mode_changes[i].arg != NULL && + ((paracount == MAXMODEPARAMSSERV) || + ((cur_len + paralen + arglen + 4) > (BUFSIZE - 3)))) + { + *mbuf = '\0'; + + if(cur_len > mlen) + { + sendto_channel_local(flags, chptr, "%s%s %s", + cmdbuf, modebuf, parabuf); + if(override) + { + sendto_wallops_flags(UMODE_WALLOP, &me, + "%s is overriding modes on %s: %s %s", + get_oper_name(source_p), chptr->chname, modebuf, parabuf); + sendto_server(NULL, chptr, NOCAPS, NOCAPS, + ":%s WALLOPS :%s is overriding modes on %s: %s %s", + me.name, get_oper_name(source_p), chptr->chname, modebuf, parabuf); + } + } + else + continue; + + paracount = paralen = 0; + cur_len = mlen; + mbuf = modebuf + mlen; + pbuf = parabuf; + parabuf[0] = '\0'; + dir = MODE_QUERY; + } + + if(dir != mode_changes[i].dir) + { + *mbuf++ = (mode_changes[i].dir == MODE_ADD) ? '+' : '-'; + cur_len++; + dir = mode_changes[i].dir; + } + + *mbuf++ = mode_changes[i].letter; cur_len++; - dir = mode_changes[i].dir; + + if(mode_changes[i].arg != NULL) + { + paracount++; + len = rb_sprintf(pbuf, "%s ", mode_changes[i].arg); + pbuf += len; + paralen += len; + } } - *mbuf++ = mode_changes[i].letter; - cur_len++; + if(paralen && parabuf[paralen - 1] == ' ') + parabuf[paralen - 1] = '\0'; - if(mode_changes[i].arg != NULL) + *mbuf = '\0'; + if(cur_len > mlen) { - paracount++; - len = rb_sprintf(pbuf, "%s ", mode_changes[i].arg); - pbuf += len; - paralen += len; + sendto_channel_local(flags, chptr, "%s%s %s", cmdbuf, modebuf, parabuf); + if(override) + { + sendto_wallops_flags(UMODE_WALLOP, &me, + "%s is overriding modes on %s: %s %s", + get_oper_name(source_p), chptr->chname, modebuf, parabuf); + sendto_server(NULL, chptr, NOCAPS, NOCAPS, + ":%s WALLOPS :%s is overriding modes on %s: %s %s", + me.name, get_oper_name(source_p), chptr->chname, modebuf, parabuf); + } } } - - if(paralen && parabuf[paralen - 1] == ' ') - parabuf[paralen - 1] = '\0'; - - *mbuf = '\0'; - if(cur_len > mlen) - sendto_channel_local(flags, chptr, "%s %s", modebuf, parabuf); + if(override) + { + if(!was_on_chan) + remove_user_from_channel(find_channel_membership(chptr, source_p)); + else if (!no_override_deop) + msptr->flags &= ~CHFL_CHANOP; + } } /* only propagate modes originating locally, or if we're hubbing */ - if(MyClient(source_p) || dlink_list_length(&serv_list) > 1) + 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 : ""); + } +}