X-Git-Url: https://jfr.im/git/irc/rqf/shadowircd.git/blobdiff_plain/af81d5a0b09446188fd6f9c292b51519f2c1cedd..82f8e812f5f3df9a23b58dff2024f83237280b9d:/src/chmode.c diff --git a/src/chmode.c b/src/chmode.c index 4f50f82..7f78db9 100644 --- a/src/chmode.c +++ b/src/chmode.c @@ -26,14 +26,12 @@ */ #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 +40,8 @@ #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" /* bitmasks for error returns, so we send once per call */ #define SM_ERR_NOTS 0x00000001 /* No TS on channel */ @@ -61,22 +57,132 @@ #define SM_ERR_RPL_Q 0x00000800 #define SM_ERR_RPL_F 0x00001000 -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; -int +char cflagsbuf[256]; +char cflagsmyinfo[256]; + +int chmode_flags[256]; + +/* OPTIMIZE ME! -- dwr */ +void +construct_noparam_modes(void) +{ + int i; + char *ptr = cflagsbuf; + char *ptr2 = cflagsmyinfo; + static int prev_chmode_flags[256]; + + *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_owner) && + !(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; + } + + if (prev_chmode_flags[i] != 0 && prev_chmode_flags[i] != chmode_flags[i]) + { + if (chmode_flags[i] == 0) + { + chmode_table[i].set_func = chm_orphaned; + sendto_realops_snomask(SNO_DEBUG, L_ALL, "Cmode +%c is now orphaned", i); + } + else + { + sendto_realops_snomask(SNO_DEBUG, L_ALL, "Orphaned cmode +%c is picked up by module", i); + } + chmode_flags[i] = prev_chmode_flags[i]; + } + else + prev_chmode_flags[i] = chmode_flags[i]; + + 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) + { + *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 + */ +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; +} + +static int get_channel_access(struct Client *source_p, struct membership *msptr) { - if(!MyClient(source_p) || is_chanop(msptr)) + if(!MyClient(source_p) || is_owner(msptr)) + return CHFL_OWNER; + else if(is_chanop(msptr)) return CHFL_CHANOP; + else if(is_halfop(msptr)) + return CHFL_HALFOP; return CHFL_PEON; } @@ -108,7 +214,7 @@ add_id(struct Client *source_p, struct Channel *chptr, const char *banid, return 0; } - DLINK_FOREACH(ptr, list->head) + RB_DLINK_FOREACH(ptr, list->head) { actualBan = ptr->data; if(mask_match(actualBan->banstr, realban)) @@ -118,7 +224,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,10 +236,10 @@ 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(); rb_dlinkAdd(actualBan, &actualBan->node, list); @@ -159,7 +265,7 @@ del_id(struct Channel *chptr, const char *banid, rb_dlink_list * list, long mode if(EmptyString(banid)) return 0; - DLINK_FOREACH(ptr, list->head) + RB_DLINK_FOREACH(ptr, list->head) { banptr = ptr->data; @@ -281,7 +387,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,7 +498,7 @@ 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(alevel != CHFL_CHANOP && alevel != CHFL_OWNER && alevel != CHFL_HALFOP) { if(!(*errors & SM_ERR_NOOPS)) sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), @@ -401,8 +507,7 @@ chm_simple(struct Client *source_p, struct Channel *chptr, 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 + */ @@ -437,6 +542,40 @@ chm_simple(struct Client *source_p, struct Channel *chptr, } } +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].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_staff(struct Client *source_p, struct Channel *chptr, int alevel, int parc, int *parn, @@ -458,6 +597,9 @@ chm_staff(struct Client *source_p, struct Channel *chptr, return; } + if(MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE)) + return; + /* setting + */ if((dir == MODE_ADD) && !(chptr->mode.mode & mode_type)) { @@ -570,7 +712,7 @@ 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_OWNER && alevel != CHFL_HALFOP && mode_type != CHFL_BAN && mode_type != CHFL_QUIET) { if(!(*errors & SM_ERR_NOOPS)) @@ -580,7 +722,7 @@ chm_ban(struct Client *source_p, struct Channel *chptr, return; } - DLINK_FOREACH(ptr, list->head) + RB_DLINK_FOREACH(ptr, list->head) { banptr = ptr->data; sendto_one(source_p, form_str(rpl_list), @@ -594,7 +736,7 @@ chm_ban(struct Client *source_p, struct Channel *chptr, return; } - if(alevel != CHFL_CHANOP) + if(alevel != CHFL_CHANOP && alevel != CHFL_OWNER && alevel != CHFL_HALFOP) { if(!(*errors & SM_ERR_NOOPS)) sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), @@ -673,6 +815,103 @@ chm_ban(struct Client *source_p, struct Channel *chptr, } } +void +chm_owner(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 *ownernick; + struct Client *targ_p; + + if(!ConfigChannel.use_owner) + { + 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_OWNER) + { + 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; + + ownernick = parv[(*parn)]; + (*parn)++; + + /* empty nick */ + if(EmptyString(ownernick)) + { + sendto_one_numeric(source_p, ERR_NOSUCHNICK, form_str(ERR_NOSUCHNICK), "*"); + return; + } + + if((targ_p = find_chasing(source_p, ownernick, 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), ownernick, chptr->chname); + *errors |= SM_ERR_NOTONCHANNEL; + return; + } + + if(MyClient(source_p) && (++mode_limit > MAXMODEPARAMS)) + return; + + if(dir == MODE_ADD) + { + if(targ_p == source_p) + 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; + + mstptr->flags |= CHFL_OWNER; + } + 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++].client = targ_p; + + mstptr->flags &= ~CHFL_OWNER; + } +} + void chm_op(struct Client *source_p, struct Channel *chptr, int alevel, int parc, int *parn, @@ -682,7 +921,7 @@ chm_op(struct Client *source_p, struct Channel *chptr, const char *opnick; struct Client *targ_p; - if(alevel != CHFL_CHANOP) + if(alevel != CHFL_CHANOP && alevel != CHFL_OWNER) { if(!(*errors & SM_ERR_NOOPS)) sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), @@ -761,6 +1000,103 @@ chm_op(struct Client *source_p, struct Channel *chptr, } } +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; + + 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_OWNER && 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((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) + 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; + + 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++].client = targ_p; + + mstptr->flags &= ~CHFL_HALFOP; + } +} + void chm_voice(struct Client *source_p, struct Channel *chptr, int alevel, int parc, int *parn, @@ -770,7 +1106,7 @@ chm_voice(struct Client *source_p, struct Channel *chptr, const char *opnick; struct Client *targ_p; - if(alevel != CHFL_CHANOP) + if(alevel != CHFL_CHANOP && alevel != CHFL_OWNER && alevel != CHFL_HALFOP) { if(!(*errors & SM_ERR_NOOPS)) sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), @@ -848,7 +1184,7 @@ chm_limit(struct Client *source_p, struct Channel *chptr, static char limitstr[30]; int limit; - if(alevel != CHFL_CHANOP) + if(alevel != CHFL_CHANOP && alevel != CHFL_OWNER && alevel != CHFL_HALFOP) { if(!(*errors & SM_ERR_NOOPS)) sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), @@ -860,6 +1196,9 @@ chm_limit(struct Client *source_p, struct Channel *chptr, if(dir == MODE_QUERY) return; + if(MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE)) + return; + if((dir == MODE_ADD) && parc > *parn) { lstr = parv[(*parn)]; @@ -904,7 +1243,7 @@ chm_throttle(struct Client *source_p, struct Channel *chptr, { int joins = 0, timeslice = 0; - if(alevel != CHFL_CHANOP) + if(alevel != CHFL_CHANOP && alevel != CHFL_OWNER && alevel != CHFL_HALFOP) { if(!(*errors & SM_ERR_NOOPS)) sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), @@ -916,6 +1255,9 @@ chm_throttle(struct Client *source_p, struct Channel *chptr, 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); @@ -984,7 +1326,7 @@ chm_forward(struct Client *source_p, struct Channel *chptr, } #ifndef FORWARD_OPERONLY - if(alevel != CHFL_CHANOP) + if(alevel != CHFL_CHANOP && alevel != CHFL_OWNER && alevel != CHFL_HALFOP) { if(!(*errors & SM_ERR_NOOPS)) sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), @@ -1002,6 +1344,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,7 +1376,7 @@ 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); @@ -1039,7 +1384,7 @@ chm_forward(struct Client *source_p, struct Channel *chptr, } } - 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; @@ -1073,7 +1418,7 @@ chm_key(struct Client *source_p, struct Channel *chptr, { char *key; - if(alevel != CHFL_CHANOP) + if(alevel != CHFL_CHANOP && alevel != CHFL_OWNER && alevel != CHFL_HALFOP) { if(!(*errors & SM_ERR_NOOPS)) sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), @@ -1085,6 +1430,9 @@ chm_key(struct Client *source_p, struct Channel *chptr, 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 +1447,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; @@ -1147,7 +1495,7 @@ 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(alevel != CHFL_CHANOP && alevel != CHFL_OWNER && alevel != CHFL_HALFOP) { if(!(*errors & SM_ERR_NOOPS)) sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED), @@ -1159,14 +1507,17 @@ chm_regonly(struct Client *source_p, struct Channel *chptr, 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; @@ -1248,24 +1599,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_simple, MODE_NOREJOIN }, /* J */ {chm_nosuch, 0 }, /* K */ {chm_staff, MODE_EXLIMIT }, /* L */ {chm_nosuch, 0 }, /* M */ - {chm_nosuch, 0 }, /* N */ + {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 +1629,14 @@ struct ChannelMode chmode_table[256] = {chm_nosuch, 0 }, {chm_nosuch, 0 }, {chm_nosuch, 0 }, - {chm_nosuch, 0 }, /* a */ + {chm_owner, 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 +1646,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 */ @@ -1479,6 +1830,7 @@ set_channel_mode(struct Client *client_p, struct Client *source_p, mask_pos = 0; mode_count = 0; mode_limit = 0; + mode_limit_simple = 0; alevel = get_channel_access(source_p, msptr);