X-Git-Url: http://git.pk910.de/?p=ircu2.10.12-pk.git;a=blobdiff_plain;f=ircd%2Fchannel.c;h=02cdd96c52ad48fde34a1197cae6fad234022881;hp=01d25072c95275dc46bbafdbd9bdcc908244764f;hb=e894de73de8e7846934ecdf4d7cc3a14d5635ce6;hpb=c3727b053aa20cecf3984cc0c9c23b0140edf52c diff --git a/ircd/channel.c b/ircd/channel.c index 01d2507..02cdd96 100644 --- a/ircd/channel.c +++ b/ircd/channel.c @@ -68,6 +68,8 @@ static unsigned int membershipAllocCount; static struct Membership* membershipFreeList; /** Freelist for struct Ban*'s */ static struct Ban* free_bans; +/** Freelist for struct MemberFlood*'s */ +static struct MemberFlood* free_MemberFlood; /** Number of ban structures allocated. */ static size_t bans_alloc; /** Number of ban structures in use. */ @@ -372,7 +374,7 @@ struct Membership* find_channel_member(struct Client* cptr, struct Channel* chpt * @param[in] banlist The list of bans to test. * @return Pointer to a matching ban, or NULL if none exit. */ -struct Ban *find_ban(struct Client *cptr, struct Ban *banlist) +static struct Ban *find_ban_with_nick(struct Client *cptr, const char *nick, struct Ban *banlist) { char nu[NICKLEN + USERLEN + 2]; char tmphost[HOSTLEN + 1]; @@ -384,7 +386,7 @@ struct Ban *find_ban(struct Client *cptr, struct Ban *banlist) /* Build nick!user and alternate host names. */ ircd_snprintf(0, nu, sizeof(nu), "%s!%s", - cli_name(cptr), cli_user(cptr)->username); + (nick ? nick : cli_name(cptr)), cli_user(cptr)->username); ircd_ntoa_r(iphost, &cli_ip(cptr)); /* Check for all three possible hosts: @@ -437,6 +439,10 @@ struct Ban *find_ban(struct Client *cptr, struct Ban *banlist) return found; } +struct Ban *find_ban(struct Client *cptr, struct Ban *banlist) { + return find_ban_with_nick(cptr, NULL, banlist); +} + /** * This function returns true if the user is banned on the said channel. * This function will check the ban cache if applicable, otherwise will @@ -460,6 +466,13 @@ static int is_banned(struct Membership* member) } } +static int is_banned_with_nick(struct Membership* member, const char *nick) { + if (find_ban_with_nick(member->user, nick, member->channel->banlist)) + return 1; + else + return 0; +} + /** add a user to a channel. * adds a user to a channel by adding another link to the channels member * chain. @@ -489,6 +502,7 @@ void add_user_to_channel(struct Channel* chptr, struct Client* who, member->user = who; member->channel = chptr; member->status = flags; + member->flood = NULL; SetOpLevel(member, oplevel); member->next_member = chptr->members; @@ -549,6 +563,8 @@ static int remove_member_from_channel(struct Membership* member) --(cli_user(member->user))->joined; + member->flood = NULL; + member->next_member = membershipFreeList; membershipFreeList = member; @@ -640,6 +656,24 @@ int is_chan_op(struct Client *cptr, struct Channel *chptr) return 0; } +/** Check if this user is a legitimate halfop + * + * @param cptr Client to check + * @param chptr Channel to check + * + * @returns True if the user is a halfop (And not a zombie), False otherwise. + * @see \ref zombie + */ +int is_halfop(struct Client *cptr, struct Channel *chptr) +{ + struct Membership* member; + assert(chptr); + if ((member = find_member_link(chptr, cptr))) + return (!IsZombie(member) && IsHalfOp(member)); + + return 0; +} + /** Check if a user is a Zombie on a specific channel. * * @param cptr The client to check. @@ -778,7 +812,8 @@ int client_can_send_to_channel(struct Client *cptr, struct Channel *chptr, int r */ if (!member) { if ((chptr->mode.mode & (MODE_NOPRIVMSGS|MODE_MODERATED)) || - ((chptr->mode.mode & MODE_REGONLY) && !IsAccount(cptr))) + ((chptr->mode.mode & MODE_REGONLY) && !IsAccount(cptr)) || + ((chptr->mode.mode & MODE_SSLCHAN) && !IsSSL(cptr))) return 0; else return !find_ban(cptr, chptr->banlist); @@ -791,11 +826,12 @@ int client_can_send_to_channel(struct Client *cptr, struct Channel *chptr, int r * the name of the first channel banned on. * * @param cptr The client + * @param new_nick The new nick of the client * * @returns the name of the first channel banned on, or NULL if the user * can change nicks. */ -const char* find_no_nickchange_channel(struct Client* cptr) +const char* find_no_nickchange_channel(struct Client* cptr, const char *new_nick) { if (MyUser(cptr)) { struct Membership* member; @@ -805,7 +841,8 @@ const char* find_no_nickchange_channel(struct Client* cptr) continue; if ((member->channel->mode.mode & MODE_MODERATED) || (member->channel->mode.mode & MODE_REGONLY && !IsAccount(cptr)) - || is_banned(member)) + || (member->channel->mode.mode & MODE_SSLCHAN && !IsSSL(cptr)) + || is_banned(member) || (new_nick && is_banned_with_nick(member, new_nick))) return member->channel->chname; } } @@ -870,6 +907,10 @@ void channel_modes(struct Client *cptr, char *mbuf, char *pbuf, int buflen, *mbuf++ = 'M'; if (chptr->mode.mode & MODE_QUARANTINE) *mbuf++ = 'Q'; + if (chptr->mode.mode & MODE_AUDITORIUM) + *mbuf++ = 'u'; + if (chptr->mode.mode & MODE_SSLCHAN) + *mbuf++ = 'S'; if (chptr->mode.limit) { *mbuf++ = 'l'; ircd_snprintf(0, pbuf, buflen, "%u", chptr->mode.limit); @@ -889,11 +930,18 @@ void channel_modes(struct Client *cptr, char *mbuf, char *pbuf, int buflen, strcat(pbuf, chptr->mode.altchan); previous_parameter = 1; } + if (*chptr->mode.noflood) { + *mbuf++ = 'f'; + if (previous_parameter) + strcat(pbuf, " "); + strcat(pbuf, chptr->mode.noflood); + previous_parameter = 1; + } if (*chptr->mode.key) { *mbuf++ = 'k'; if (previous_parameter) strcat(pbuf, " "); - if (is_chan_op(cptr, chptr) || IsServer(cptr) || IsOper(cptr)) { + if ((member && IsChanOpOrHalfOp(member)) || IsServer(cptr) || IsOper(cptr)) { strcat(pbuf, chptr->mode.key); } else strcat(pbuf, "*"); @@ -913,7 +961,7 @@ void channel_modes(struct Client *cptr, char *mbuf, char *pbuf, int buflen, *mbuf++ = 'U'; if (previous_parameter) strcat(pbuf, " "); - if (IsServer(cptr) || (member && IsChanOp(member) && OpLevel(member) == 0) || IsOper(cptr)) { + if (IsServer(cptr) || (member && IsChanOpOrHalfOp(member) && OpLevel(member) == 0) || IsOper(cptr)) { strcat(pbuf, chptr->mode.upass); } else strcat(pbuf, "*"); @@ -949,8 +997,8 @@ int compare_member_oplevel(const void *mp1, const void *mp2) void send_channel_modes(struct Client *cptr, struct Channel *chptr) { /* The order in which modes are generated is now mandatory */ - static unsigned int current_flags[4] = - { 0, CHFL_VOICE, CHFL_CHANOP, CHFL_CHANOP | CHFL_VOICE }; + static unsigned int current_flags[8] = + { 0, CHFL_VOICE, CHFL_HALFOP, CHFL_VOICE | CHFL_HALFOP, CHFL_CHANOP, CHFL_CHANOP | CHFL_VOICE, CHFL_CHANOP | CHFL_HALFOP, CHFL_CHANOP | CHFL_VOICE | CHFL_HALFOP }; int first = 1; int full = 1; int flag_cnt = 0; @@ -1008,7 +1056,7 @@ void send_channel_modes(struct Client *cptr, struct Channel *chptr) * Then run 2 times over all opped members (which are ordered * by op-level) to also group voice and non-voice together. */ - for (first = 1; flag_cnt < 4; new_mode = 1, ++flag_cnt) + for (first = 1; flag_cnt < 8; new_mode = 1, ++flag_cnt) { while (member) { @@ -1063,6 +1111,8 @@ void send_channel_modes(struct Client *cptr, struct Channel *chptr) if (HasVoice(member)) /* flag_cnt == 1 or 3 */ tbuf[loc++] = 'v'; + if (IsHalfOp(member)) + tbuf[loc++] = 'h'; if (IsChanOp(member)) /* flag_cnt == 2 or 3 */ { /* append the absolute value of the oplevel */ @@ -1089,7 +1139,7 @@ void send_channel_modes(struct Client *cptr, struct Channel *chptr) } } /* Go to the next `member'. */ - if (flag_cnt < 2) + if (flag_cnt < 2 || !(current_flags[flag_cnt] & CHFL_CHANOP)) member = member->next_member; else member = opped_members[++opped_members_index]; @@ -1100,7 +1150,6 @@ void send_channel_modes(struct Client *cptr, struct Channel *chptr) /* Point `member' at the start of the list again. */ if (flag_cnt == 0) { - member = chptr->members; /* Now, after one loop, we know the number of ops and can * allocate the dynamic array with pointer to the ops. */ opped_members = (struct Membership**) @@ -1115,10 +1164,12 @@ void send_channel_modes(struct Client *cptr, struct Channel *chptr) if (flag_cnt == 1) qsort(opped_members, number_of_ops, sizeof(struct Membership*), compare_member_oplevel); - /* The third and fourth loop run only over the opped members. */ - member = opped_members[(opped_members_index = 0)]; } - + if(!(current_flags[flag_cnt+1] & CHFL_CHANOP)) { + member = chptr->members; + } else + member = opped_members[(opped_members_index = 0)]; + } /* loop over 0,+v,+o,+ov */ if (!full) @@ -1266,19 +1317,17 @@ static void send_ban_list(struct Client* cptr, struct Channel* chptr) assert(0 != cptr); assert(0 != chptr); - for (lp = chptr->banlist; lp; lp = lp->next) { - if(!(lp->flags & BAN_EXCEPTION)) - send_reply(cptr, RPL_BANLIST, chptr->chname, lp->banstr, - lp->who, lp->when); - } + for (lp = chptr->banlist; lp; lp = lp->next) + if ((lp->flags & BAN_EXCEPTION) != BAN_EXCEPTION) + send_reply(cptr, RPL_BANLIST, chptr->chname, lp->banstr, lp->who, lp->when); send_reply(cptr, RPL_ENDOFBANLIST, chptr->chname); } -/** send an exceptionlist to a client for a channel +/** send an exception list to a client for a channel * - * @param cptr Client to send the exceptionlist to. - * @param chptr Channel whose exceptionlist to send. + * @param cptr Client to send the banlist to. + * @param chptr Channel whose exception list to send. */ static void send_exception_list(struct Client* cptr, struct Channel* chptr) { @@ -1287,13 +1336,11 @@ static void send_exception_list(struct Client* cptr, struct Channel* chptr) assert(0 != cptr); assert(0 != chptr); - for (lp = chptr->banlist; lp; lp = lp->next) { - if(lp->flags & BAN_EXCEPTION) - send_reply(cptr, RPL_EXCEPTIONLIST, chptr->chname, lp->banstr, - lp->who, lp->when); - } + for (lp = chptr->banlist; lp; lp = lp->next) + if ((lp->flags & BAN_EXCEPTION) == BAN_EXCEPTION) + send_reply(cptr, RPL_EXCEPTLIST, chptr->chname, lp->banstr, lp->who, lp->when); - send_reply(cptr, RPL_ENDOFEXCEPTIONLIST, chptr->chname); + send_reply(cptr, RPL_ENDOFEXCEPTLIST, chptr->chname); } /** Get a channel block, creating if necessary. @@ -1577,7 +1624,7 @@ static int modebuf_flush_int(struct ModeBuf *mbuf, int all) { /* we only need the flags that don't take args right now */ - static int flags[] = { + static ulong64 flags[] = { /* MODE_CHANOP, 'o', */ /* MODE_VOICE, 'v', */ MODE_PRIVATE, 'p', @@ -1597,20 +1644,23 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all) MODE_PERSIST, 'z', MODE_NOCOLOUR, 'c', MODE_NOCTCP, 'C', -/* MODE_EXCEPTION, 'e', */ /* MODE_ALTCHAN, 'F', */ +/* MODE_NOFLOOD, 'f', */ MODE_ACCESS, 'a', MODE_NOAMSGS, 'M', MODE_NONOTICE, 'N', MODE_QUARANTINE, 'Q', + MODE_AUDITORIUM, 'u', + MODE_SSLCHAN, 'S', +/* MODE_BANEXCEPTION 'e', */ 0x0, 0x0 }; - static int local_flags[] = { + static ulong64 local_flags[] = { MODE_WASDELJOINS, 'd', 0x0, 0x0 }; int i; - int *flag_p; + ulong64 *flag_p; struct Client *app_source; /* where the MODE appears to come from */ @@ -1633,8 +1683,8 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all) char limitbuf[20],accessbuf[20]; /* convert limits to strings */ - unsigned int limitdel = MODE_LIMIT; - unsigned int accessdel = MODE_ACCESS; + ulong64 limitdel = MODE_LIMIT; + ulong64 accessdel = MODE_ACCESS; assert(0 != mbuf); @@ -1685,23 +1735,28 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all) bufptr_i = &rembuf_i; } - if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_VOICE)) { + if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_HALFOP | MODE_VOICE)) { tmp = strlen(cli_name(MB_CLIENT(mbuf, i))); if ((totalbuflen - IRCD_MAX(9, tmp)) <= 0) /* don't overflow buffer */ MB_TYPE(mbuf, i) |= MODE_SAVE; /* save for later */ else { - bufptr[(*bufptr_i)++] = MB_TYPE(mbuf, i) & MODE_CHANOP ? 'o' : 'v'; + if((MB_TYPE(mbuf, i) & MODE_CHANOP)) + bufptr[(*bufptr_i)++] = 'o'; + else if((MB_TYPE(mbuf, i) & MODE_HALFOP)) + bufptr[(*bufptr_i)++] = 'h'; + else + bufptr[(*bufptr_i)++] = 'v'; totalbuflen -= IRCD_MAX(9, tmp) + 1; } - } else if (MB_TYPE(mbuf, i) & (MODE_BAN | MODE_EXCEPTION | MODE_APASS | MODE_UPASS | MODE_ALTCHAN)) { + } else if (MB_TYPE(mbuf, i) & (MODE_BAN | MODE_APASS | MODE_UPASS | MODE_ALTCHAN | MODE_NOFLOOD | MODE_BANEXCEPTION)) { tmp = strlen(MB_STRING(mbuf, i)); if ((totalbuflen - tmp) <= 0) /* don't overflow buffer */ MB_TYPE(mbuf, i) |= MODE_SAVE; /* save for later */ else { char mode_char; - switch(MB_TYPE(mbuf, i) & (MODE_BAN | MODE_EXCEPTION | MODE_APASS | MODE_UPASS | MODE_ALTCHAN)) + switch(MB_TYPE(mbuf, i) & (MODE_BAN | MODE_APASS | MODE_UPASS | MODE_ALTCHAN | MODE_NOFLOOD | MODE_BANEXCEPTION)) { case MODE_APASS: mode_char = 'A'; @@ -1709,14 +1764,17 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all) case MODE_UPASS: mode_char = 'U'; break; - case MODE_EXCEPTION: - mode_char = 'e'; - break; case MODE_ALTCHAN: mode_char = 'F'; break; + case MODE_NOFLOOD: + mode_char = 'f'; + break; + case MODE_BAN: + mode_char = 'b'; + break; default: - mode_char = 'b'; + mode_char = 'e'; break; } bufptr[(*bufptr_i)++] = mode_char; @@ -1788,11 +1846,11 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all) } /* deal with clients... */ - if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_VOICE)) + if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_HALFOP | MODE_VOICE)) build_string(strptr, strptr_i, cli_name(MB_CLIENT(mbuf, i)), 0, ' '); /* deal with bans... */ - else if (MB_TYPE(mbuf, i) & (MODE_BAN | MODE_EXCEPTION)) + else if (MB_TYPE(mbuf, i) & (MODE_BAN | MODE_BANEXCEPTION)) build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0, ' '); /* deal with keys... */ @@ -1817,6 +1875,9 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all) else if (MB_TYPE(mbuf, i) & MODE_ALTCHAN) build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0, ' '); + else if ((MB_TYPE(mbuf, i) & (MODE_ADD | MODE_NOFLOOD)) == + (MODE_ADD | MODE_NOFLOOD)) + build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0, ' '); } /* send the messages off to their destination */ @@ -1901,11 +1962,11 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all) MB_OPLEVEL(mbuf, i)); /* deal with other modes that take clients */ - else if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_VOICE)) + else if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_HALFOP | MODE_VOICE)) build_string(strptr, strptr_i, NumNick(MB_CLIENT(mbuf, i)), ' '); /* deal with modes that take strings */ - else if (MB_TYPE(mbuf, i) & (MODE_KEY | MODE_BAN | MODE_EXCEPTION | MODE_APASS | MODE_UPASS | MODE_ALTCHAN)) + else if (MB_TYPE(mbuf, i) & (MODE_KEY | MODE_BAN | MODE_APASS | MODE_UPASS | MODE_ALTCHAN | MODE_NOFLOOD | MODE_BANEXCEPTION)) build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0, ' '); /* @@ -2032,7 +2093,7 @@ modebuf_init(struct ModeBuf *mbuf, struct Client *source, * @param mode MODE_ADD or MODE_DEL OR'd with MODE_PRIVATE etc. */ void -modebuf_mode(struct ModeBuf *mbuf, unsigned int mode) +modebuf_mode(struct ModeBuf *mbuf, ulong64 mode) { assert(0 != mbuf); assert(0 != (mode & (MODE_ADD | MODE_DEL))); @@ -2040,7 +2101,8 @@ modebuf_mode(struct ModeBuf *mbuf, unsigned int mode) mode &= (MODE_ADD | MODE_DEL | MODE_PRIVATE | MODE_SECRET | MODE_MODERATED | MODE_TOPICLIMIT | MODE_INVITEONLY | MODE_NOPRIVMSGS | MODE_REGONLY | MODE_DELJOINS | MODE_WASDELJOINS | MODE_REGISTERED | MODE_PERSIST | - MODE_NOCOLOUR | MODE_NOCTCP | MODE_NOAMSGS | MODE_NONOTICE | MODE_QUARANTINE); + MODE_NOCOLOUR | MODE_NOCTCP | MODE_NOAMSGS | MODE_NONOTICE | + MODE_QUARANTINE | MODE_AUDITORIUM | MODE_SSLCHAN); if (!(mode & ~(MODE_ADD | MODE_DEL))) /* don't add empty modes... */ return; @@ -2054,10 +2116,10 @@ modebuf_mode(struct ModeBuf *mbuf, unsigned int mode) } } -/** Append a mode that takes an int argument to the modebuf +/** Append a mode that takes an ulong64 argument to the modebuf * * This routine adds a mode to be added or deleted that takes a unsigned - * int parameter; mode may *only* be the relevant mode flag ORed with one + * ulong64 parameter; mode may *only* be the relevant mode flag ORed with one * of MODE_ADD or MODE_DEL * * @param mbuf The mode buffer to append to. @@ -2065,7 +2127,7 @@ modebuf_mode(struct ModeBuf *mbuf, unsigned int mode) * @param uint The argument to the mode. */ void -modebuf_mode_uint(struct ModeBuf *mbuf, unsigned int mode, unsigned int uint) +modebuf_mode_uint(struct ModeBuf *mbuf, ulong64 mode, unsigned int uint) { assert(0 != mbuf); assert(0 != (mode & (MODE_ADD | MODE_DEL))); @@ -2098,7 +2160,7 @@ modebuf_mode_uint(struct ModeBuf *mbuf, unsigned int mode, unsigned int uint) * @param free If the string should be free'd later. */ void -modebuf_mode_string(struct ModeBuf *mbuf, unsigned int mode, char *string, +modebuf_mode_string(struct ModeBuf *mbuf, ulong64 mode, char *string, int free) { assert(0 != mbuf); @@ -2124,7 +2186,7 @@ modebuf_mode_string(struct ModeBuf *mbuf, unsigned int mode, char *string, * @param oplevel The oplevel the user had or will have */ void -modebuf_mode_client(struct ModeBuf *mbuf, unsigned int mode, +modebuf_mode_client(struct ModeBuf *mbuf, ulong64 mode, struct Client *client, int oplevel) { assert(0 != mbuf); @@ -2192,7 +2254,7 @@ modebuf_flush(struct ModeBuf *mbuf) void modebuf_extract(struct ModeBuf *mbuf, char *buf) { - static int flags[] = { + static ulong64 flags[] = { /* MODE_CHANOP, 'o', */ /* MODE_VOICE, 'v', */ MODE_PRIVATE, 'p', @@ -2212,19 +2274,22 @@ modebuf_extract(struct ModeBuf *mbuf, char *buf) MODE_PERSIST, 'z', MODE_NOCOLOUR, 'c', MODE_NOCTCP, 'C', -/* MODE_EXCEPTION, 'e', */ MODE_NOAMSGS, 'M', MODE_NONOTICE, 'N', MODE_QUARANTINE, 'Q', MODE_ALTCHAN, 'F', MODE_ACCESS, 'a', + MODE_AUDITORIUM, 'u', + MODE_SSLCHAN, 'S', + MODE_NOFLOOD, 'f', +/* MODE_BANEXCEPTION, 'e', */ 0x0, 0x0 }; - unsigned int add; + ulong64 add; int i, bufpos = 0, len; - int *flag_p; + ulong64 *flag_p; char *key = 0, limitbuf[20], accessbuf[20]; - char *apass = 0, *upass = 0, *altchan = 0; + char *apass = 0, *upass = 0, *altchan = 0, *noflood = 0; assert(0 != mbuf); assert(0 != buf); @@ -2235,7 +2300,7 @@ modebuf_extract(struct ModeBuf *mbuf, char *buf) for (i = 0; i < mbuf->mb_count; i++) { /* find keys and limits */ if (MB_TYPE(mbuf, i) & MODE_ADD) { - add |= MB_TYPE(mbuf, i) & (MODE_KEY | MODE_LIMIT | MODE_APASS | MODE_UPASS | MODE_ALTCHAN | MODE_ACCESS); + add |= MB_TYPE(mbuf, i) & (MODE_KEY | MODE_LIMIT | MODE_APASS | MODE_UPASS | MODE_ALTCHAN | MODE_ACCESS | MODE_NOFLOOD); if (MB_TYPE(mbuf, i) & MODE_KEY) /* keep strings */ key = MB_STRING(mbuf, i); @@ -2249,6 +2314,8 @@ modebuf_extract(struct ModeBuf *mbuf, char *buf) apass = MB_STRING(mbuf, i); else if (MB_TYPE(mbuf, i) & MODE_ALTCHAN) altchan = MB_STRING(mbuf, i); + else if (MB_TYPE(mbuf, i) & MODE_NOFLOOD) + noflood = MB_STRING(mbuf, i); } } @@ -2274,6 +2341,8 @@ modebuf_extract(struct ModeBuf *mbuf, char *buf) build_string(buf, &bufpos, apass, 0, ' '); else if (buf[i] == 'F') build_string(buf, &bufpos, altchan, 0, ' '); + else if (buf[i] == 'f') + build_string(buf, &bufpos, noflood, 0, ' '); } buf[bufpos] = '\0'; @@ -2322,9 +2391,10 @@ mode_invite_clear(struct Channel *chan) #define DONE_KEY_DEL 0x80 /**< We've removed the key */ #define DONE_UPASS_DEL 0x100 /**< We've removed the user pass */ #define DONE_APASS_DEL 0x200 /**< We've removed the admin pass */ -#define DONE_EXCEPTIONLIST 0x400 /**< We've sent the exception list */ #define DONE_ALTCHAN 0x800 /**< We've set the altchan */ #define DONE_ACCESS 0x1000 /**< We've set the access */ +#define DONE_NOFLOOD 0x2000 /**< We've set the noflood options */ +#define DONE_EXCEPTLIST 0x4000 /**< We've sent the exception list */ struct ParseState { struct ModeBuf *mbuf; @@ -2334,17 +2404,17 @@ struct ParseState { struct Membership *member; int parc; char **parv; - unsigned int flags; - unsigned int dir; - unsigned int done; - unsigned int add; - unsigned int del; + ulong64 flags; + ulong64 dir; + ulong64 done; + ulong64 add; + ulong64 del; int args_used; int max_args; int numbans; struct Ban banlist[MAXPARA]; struct { - unsigned int flag; + ulong64 flag; unsigned short oplevel; struct Client *client; } cli_change[MAXPARA]; @@ -2375,7 +2445,7 @@ send_notoper(struct ParseState *state) * @param flag_p ? */ static void -mode_parse_limit(struct ParseState *state, int *flag_p) +mode_parse_limit(struct ParseState *state, ulong64 *flag_p) { unsigned int t_limit; @@ -2440,7 +2510,7 @@ mode_parse_limit(struct ParseState *state, int *flag_p) static void -mode_parse_access(struct ParseState *state, int *flag_p) +mode_parse_access(struct ParseState *state, ulong64 *flag_p) { unsigned int t_access; @@ -2469,6 +2539,9 @@ mode_parse_access(struct ParseState *state, int *flag_p) return; } + if(feature_bool(FEAT_CHMODE_A_NOSET) && !(state->flags & MODE_PARSE_FORCE)) /* mode can'T be set. */ + return; + if (!(state->flags & MODE_PARSE_WIPEOUT) && (!t_access || t_access == state->chptr->mode.access)) return; @@ -2513,7 +2586,7 @@ mode_parse_access(struct ParseState *state, int *flag_p) static void -mode_parse_altchan(struct ParseState *state, int *flag_p) +mode_parse_altchan(struct ParseState *state, ulong64 *flag_p) { char *t_str; @@ -2537,15 +2610,17 @@ mode_parse_altchan(struct ParseState *state, int *flag_p) if (!IsChannelName(t_str) || !strIsIrcCh(t_str) || strlen(t_str) > IRCD_MIN(CHANNELLEN, feature_int(FEAT_CHANNELLEN)) || t_str[0] == '&') /* only parse it if it's a valid channel name! */ return; - struct Channel *chptr; - struct Membership *member; - if (!(chptr = FindChannel(t_str))) + if(!(state->flags & MODE_PARSE_FORCE)) { + struct Channel *chptr; + struct Membership *member; + if (!(chptr = FindChannel(t_str))) return; - if(!(member = find_member_link(chptr, state->sptr))) + if(!(member = find_member_link(chptr, state->sptr))) return; - if(!IsChanOp(member)) { + if(!IsChanOpOrHalfOp(member)) { send_notoper(state); return; + } } if (!(state->flags & MODE_PARSE_WIPEOUT) && @@ -2589,15 +2664,140 @@ mode_parse_altchan(struct ParseState *state, int *flag_p) modebuf_mode_string(state->mbuf, state->dir | flag_p[0], t_str, 0); if (state->flags & MODE_PARSE_SET) { - if (state->dir == MODE_DEL) /* remove the old altchan */ + if (state->dir == MODE_DEL) { /* remove the old altchan */ *state->chptr->mode.altchan = '\0'; - else + state->chptr->mode.mode &= ~flag_p[0]; + } else { ircd_strncpy(state->chptr->mode.altchan, t_str, CHANNELLEN); + state->chptr->mode.mode |= flag_p[0]; + } } } static void -mode_parse_quarantine(struct ParseState *state, int *flag_p) +mode_parse_noflood(struct ParseState *state, ulong64 *flag_p) +{ + char *t_str; + char *tmp; + unsigned int count = 0, time = 0, flags = 0; + + if (state->dir == MODE_ADD) { /* convert arg only if adding noflood */ + if (MyUser(state->sptr) && state->max_args <= 0) /* too many args? */ + return; + + if (state->parc <= 0) { /* warn if not enough args */ + if (MyUser(state->sptr)) + need_more_params(state->sptr, "MODE +f"); + return; + } + + t_str = state->parv[state->args_used++]; /* grab arg */ + state->parc--; + state->max_args--; + + tmp = t_str; + + if(tmp[0] == '!') { + if(state->flags & MODE_PARSE_FORCE) flags |= FLFL_NOFLOOD; + else t_str++; //simply ignore if it's not an opmode + tmp++; + } + if(tmp[0] == '+' || tmp[0] == '%' || tmp[0] == '@') { + if(tmp[0] == '+') flags |= FLFL_VOICE; + if(tmp[0] == '%') flags |= FLFL_HALFOP; + if(tmp[0] == '@') flags |= FLFL_CHANOP; + tmp++; + } + char *p; + for(p = tmp; p[0]; p++) { + if(p[0] == ':') { + char tmpchar = p[0]; + p[0] = '\0'; + count = strtoul(tmp,0,10); + p[0] = tmpchar; + p++; + time = strtoul(p,0,10); + break; + } + } + if(count <= 0 || time <= 0 || count > 100 || time > 600) return; + + if (!(state->flags & MODE_PARSE_WIPEOUT) && + (!t_str || t_str == state->chptr->mode.noflood)) + return; + } else + t_str = state->chptr->mode.noflood; + + /* If they're not an oper, they can't change modes */ + if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) { + send_notoper(state); + return; + } + + /* Can't remove a noflood that's not there */ + if (state->dir == MODE_DEL && !*state->chptr->mode.noflood) + return; + + /* Skip if this is a burst and a lower noflood than this is set already */ + if ((state->flags & MODE_PARSE_BURST) && + *(state->chptr->mode.noflood)) + return; + + if (state->done & DONE_NOFLOOD) /* allow noflood to be set only once */ + return; + state->done |= DONE_NOFLOOD; + + if (!state->mbuf) + return; + + if (!(state->flags & MODE_PARSE_WIPEOUT) && state->dir == MODE_ADD && + !ircd_strcmp(state->chptr->mode.noflood, t_str)) + return; /* no change */ + + if (state->flags & MODE_PARSE_BOUNCE) { + if (*state->chptr->mode.noflood) /* reset old noflood */ + modebuf_mode_string(state->mbuf, MODE_DEL | flag_p[0], state->chptr->mode.noflood, 0); + else /* remove new bogus noflood */ + modebuf_mode_string(state->mbuf, MODE_ADD | flag_p[0], t_str, 0); + } else /* send new noflood */ + modebuf_mode_string(state->mbuf, state->dir | flag_p[0], t_str, 0); + + if (state->flags & MODE_PARSE_SET) { + if (state->dir == MODE_DEL) { /* remove the old noflood */ + *state->chptr->mode.noflood = '\0'; + state->chptr->mode.mode &= ~flag_p[0]; + } else { + ircd_strncpy(state->chptr->mode.noflood, t_str, CHANNELLEN); + state->chptr->mode.mode |= flag_p[0]; + } + } + + if (state->dir == MODE_ADD) { + unsigned int noflood_value = time; + noflood_value <<= 10; + noflood_value |= count; + noflood_value <<= 4; + noflood_value |= flags; + state->chptr->mode.noflood_value = noflood_value; + } else { + //removed the mode so free all flood objects + state->chptr->mode.noflood_value = 0; + struct Membership *member; + for(member = state->chptr->members; member; member = member->next_member) { + struct MemberFlood *floodnode; + for(floodnode = member->flood;floodnode ; floodnode = floodnode->next_memberflood) { + if(floodnode->next_memberflood == NULL) break; + } //simply walk to the end + if(!floodnode) continue; + floodnode->next_memberflood = free_MemberFlood; + free_MemberFlood = floodnode; + member->flood = NULL; + } + } +} + +static void +mode_parse_quarantine(struct ParseState *state, ulong64 *flag_p) { } @@ -2643,7 +2843,7 @@ is_clean_key(struct ParseState *state, char *s, char *command) * Helper function to convert keys */ static void -mode_parse_key(struct ParseState *state, int *flag_p) +mode_parse_key(struct ParseState *state, ulong64 *flag_p) { char *t_str; @@ -2730,7 +2930,7 @@ mode_parse_key(struct ParseState *state, int *flag_p) * Helper function to convert user passes */ static void -mode_parse_upass(struct ParseState *state, int *flag_p) +mode_parse_upass(struct ParseState *state, ulong64 *flag_p) { char *t_str; @@ -2849,7 +3049,7 @@ mode_parse_upass(struct ParseState *state, int *flag_p) * Helper function to convert admin passes */ static void -mode_parse_apass(struct ParseState *state, int *flag_p) +mode_parse_apass(struct ParseState *state, ulong64 *flag_p) { struct Membership *memb; char *t_str; @@ -3102,7 +3302,7 @@ int apply_ban(struct Ban **banlist, struct Ban *newban, int do_free) /* Removes MODE_WASDELJOINS in a channel. * Reveals all hidden users. */ -static void reveal_hidden_chan_users(struct ParseState *state, int *flag_p) { +static void reveal_hidden_chan_users(struct ParseState *state, ulong64 *flag_p) { struct Membership *member; /* If the channel is not +d, do nothing. */ @@ -3116,28 +3316,45 @@ static void reveal_hidden_chan_users(struct ParseState *state, int *flag_p) { } } +/* Handle MODE_AUDITORIUM changes + * set Delayed for all hidden users on MODE_DEL + * part all nonoped users on MODE_ADD + */ +static void audit_chan_users(struct ParseState *state, ulong64 *flag_p) { + struct Membership *member; + if (state->dir == MODE_ADD) { + for(member = state->chptr->members; member; member = member->next_member) { + if(!IsChanOpOrHalfOp(member) && !HasVoice(member)) { + sendcmdto_channel_butserv_butone(member->user, CMD_PART, member->channel, member->user, SKIP_OPS, "%H :%s", member->channel, "mode +u set."); + } + } + } else { + for(member = state->chptr->members; member; member = member->next_member) { + if(!IsChanOpOrHalfOp(member) && !HasVoice(member)) { + sendcmdto_channel_butserv_butone(member->user, CMD_JOIN, member->channel, member->user, SKIP_OPS, ":%H", member->channel); + } + } + } +} + + /* * Helper function to convert bans */ static void -mode_parse_ban(struct ParseState *state, int *flag_p) +mode_parse_ban(struct ParseState *state, ulong64 *flag_p) { char *t_str, *s; struct Ban *ban, *newban; if (state->parc <= 0) { /* Not enough args, send ban list */ if (MyUser(state->sptr)) { - if (*flag_p == MODE_EXCEPTION) { - if (!(state->done & DONE_EXCEPTIONLIST)) { - send_exception_list(state->sptr, state->chptr); - state->done |= DONE_EXCEPTIONLIST; - } - } - else { - if (!(state->done & DONE_BANLIST)) { - send_ban_list(state->sptr, state->chptr); - state->done |= DONE_BANLIST; - } + if (*flag_p == MODE_BANEXCEPTION && !(state->done & DONE_EXCEPTLIST)) { + send_exception_list(state->sptr, state->chptr); + state->done |= DONE_EXCEPTLIST; + } else if (*flag_p == MODE_BAN && !(state->done & DONE_BANLIST)) { + send_ban_list(state->sptr, state->chptr); + state->done |= DONE_BANLIST; } } return; @@ -3204,9 +3421,9 @@ mode_process_bans(struct ParseState *state) if ((ban->flags & (BAN_DEL | BAN_ADD)) == (BAN_DEL | BAN_ADD)) { if (prevban) - prevban->next = 0; /* Break the list; ban isn't a real ban */ + prevban->next = 0; /* Break the list; ban isn't a real ban */ else - state->chptr->banlist = 0; + state->chptr->banlist = 0; count--; len -= banlen; @@ -3215,61 +3432,57 @@ mode_process_bans(struct ParseState *state) } else if (ban->flags & BAN_DEL) { /* Deleted a ban? */ char *bandup; DupString(bandup, ban->banstr); - modebuf_mode_string(state->mbuf, MODE_DEL | ((ban->flags & BAN_EXCEPTION) ? MODE_EXCEPTION : MODE_BAN), - bandup, 1); + modebuf_mode_string(state->mbuf, MODE_DEL | ((ban->flags & BAN_EXCEPTION) ? MODE_BANEXCEPTION : MODE_BAN), bandup, 1); if (state->flags & MODE_PARSE_SET) { /* Ok, make it take effect */ - if (prevban) /* clip it out of the list... */ - prevban->next = ban->next; - else - state->chptr->banlist = ban->next; + if (prevban) /* clip it out of the list... */ + prevban->next = ban->next; + else + state->chptr->banlist = ban->next; - count--; - len -= banlen; + count--; + len -= banlen; free_ban(ban); - changed++; - continue; /* next ban; keep prevban like it is */ + changed++; + continue; /* next ban; keep prevban like it is */ } else - ban->flags &= BAN_IPMASK; /* unset other flags */ + ban->flags &= (BAN_IPMASK | BAN_EXCEPTION); /* unset other flags */ } else if (ban->flags & BAN_ADD) { /* adding a ban? */ if (prevban) - prevban->next = 0; /* Break the list; ban isn't a real ban */ + prevban->next = 0; /* Break the list; ban isn't a real ban */ else - state->chptr->banlist = 0; + state->chptr->banlist = 0; /* If we're supposed to ignore it, do so. */ - if (ban->flags & BAN_OVERLAPPED && - !(state->flags & MODE_PARSE_BOUNCE)) { - count--; - len -= banlen; + if (ban->flags & BAN_OVERLAPPED && !(state->flags & MODE_PARSE_BOUNCE)) { + count--; + len -= banlen; } else { - if (state->flags & MODE_PARSE_SET && MyUser(state->sptr) && - (len > (feature_int(FEAT_AVBANLEN) * feature_int(FEAT_MAXBANS)) || - count > feature_int(FEAT_MAXBANS))) { - send_reply(state->sptr, ERR_BANLISTFULL, state->chptr->chname, - ban->banstr); - count--; - len -= banlen; - } else { + if (state->flags & MODE_PARSE_SET && MyUser(state->sptr) && + (len > (feature_int(FEAT_AVBANLEN) * feature_int(FEAT_MAXBANS)) || + count > feature_int(FEAT_MAXBANS))) { + send_reply(state->sptr, ERR_BANLISTFULL, state->chptr->chname, ban->banstr); + count--; + len -= banlen; + } else { char *bandup; - /* add the ban to the buffer */ + /* add the ban to the buffer */ DupString(bandup, ban->banstr); - modebuf_mode_string(state->mbuf, MODE_ADD | ((ban->flags & BAN_EXCEPTION) ? MODE_EXCEPTION : MODE_BAN), - bandup, 1); + modebuf_mode_string(state->mbuf, MODE_ADD | ((ban->flags & BAN_EXCEPTION) ? MODE_BANEXCEPTION : MODE_BAN), bandup, 1); - if (state->flags & MODE_PARSE_SET) { /* create a new ban */ - newban = make_ban(ban->banstr); + if (state->flags & MODE_PARSE_SET) { /* create a new ban */ + newban = make_ban(ban->banstr); strcpy(newban->who, ban->who); - newban->when = ban->when; - newban->flags = ban->flags & (BAN_IPMASK | BAN_EXCEPTION); + newban->when = ban->when; + newban->flags = ban->flags & (BAN_IPMASK | BAN_EXCEPTION); - newban->next = state->chptr->banlist; /* and link it in */ - state->chptr->banlist = newban; + newban->next = state->chptr->banlist; /* and link it in */ + state->chptr->banlist = newban; - changed++; - } - } + changed++; + } + } } } @@ -3284,7 +3497,7 @@ mode_process_bans(struct ParseState *state) * Helper function to process client changes */ static void -mode_parse_client(struct ParseState *state, int *flag_p) +mode_parse_client(struct ParseState *state, ulong64 *flag_p) { char *t_str; char *colon; @@ -3315,7 +3528,7 @@ mode_parse_client(struct ParseState *state, int *flag_p) if (colon != NULL) { *colon++ = '\0'; req_oplevel = atoi(colon); - if (*flag_p == CHFL_VOICE || state->dir == MODE_DEL) { + if (*flag_p == CHFL_VOICE || *flag_p == CHFL_HALFOP || state->dir == MODE_DEL) { /* Ignore the colon and its argument. */ } else if (!(state->flags & MODE_PARSE_FORCE) && state->member @@ -3330,6 +3543,10 @@ mode_parse_client(struct ParseState *state, int *flag_p) } else if (req_oplevel <= MAXOPLEVEL) oplevel = req_oplevel; } + if(*flag_p == CHFL_CHANOP && state->member && !IsChanOp(state->member) && !(state->flags & MODE_PARSE_FORCE)) { + send_notoper(state); + return; + } /* find client we're manipulating */ acptr = find_chasing(state->sptr, t_str, NULL); } else { @@ -3469,25 +3686,56 @@ mode_process_clients(struct ParseState *state) else SetOpLevel(member, OpLevel(state->member) + 1); } - + + int user_visible = (member->status & CHFL_VOICED_OR_OPPED); + /* actually effect the change */ if (state->flags & MODE_PARSE_SET) { if (state->cli_change[i].flag & MODE_ADD) { if (IsDelayedJoin(member) && !IsZombie(member)) RevealDelayedJoin(member); member->status |= (state->cli_change[i].flag & - (MODE_CHANOP | MODE_VOICE)); + (MODE_CHANOP | MODE_HALFOP | MODE_VOICE)); if (state->cli_change[i].flag & MODE_CHANOP) ClearDeopped(member); } else member->status &= ~(state->cli_change[i].flag & - (MODE_CHANOP | MODE_VOICE)); + (MODE_CHANOP | MODE_HALFOP | MODE_VOICE)); } /* accumulate the change */ modebuf_mode_client(state->mbuf, state->cli_change[i].flag, state->cli_change[i].client, state->cli_change[i].oplevel); + + if((member->channel->mode.mode & MODE_AUDITORIUM)) { + //join or part the user + if((member->status & CHFL_VOICED_OR_OPPED) && !user_visible) { + sendcmdto_channel_butserv_butone(member->user, CMD_JOIN, member->channel, member->user, SKIP_OPS, ":%H", member->channel); + } else if(!(member->status & CHFL_VOICED_OR_OPPED) && user_visible) { + sendcmdto_channel_butserv_butone(member->user, CMD_PART, member->channel, member->user, SKIP_OPS, "%H :%s", member->channel, "user deoped/devoiced on a +u channel."); + } + if(MyUser(member->user) && (state->cli_change[i].flag & (MODE_CHANOP | MODE_HALFOP))) { + //do_names(member->user, member->channel, NAMES_ALL|NAMES_EON|((member->status & MODE_CHANOP) ? 0 : NAMES_OPS)); + //this is not working for all users :( so we have to send join/part events + struct Membership *member2; + if (state->cli_change[i].flag & MODE_ADD) { + //JOIN events + for(member2 = state->chptr->members; member2; member2 = member2->next_member) { + if(!IsChanOpOrHalfOp(member2) && !HasVoice(member2)) { + sendcmdto_one(member2->user, CMD_JOIN, member->user, ":%H", member->channel); + } + } + } else { + //PART events + for(member2 = state->chptr->members; member2; member2 = member2->next_member) { + if(!IsChanOpOrHalfOp(member2) && !HasVoice(member2) && member != member2) { + sendcmdto_one(member2->user, CMD_PART, member->user, "%H :%s", member->channel, "invisible user on +u channel."); + } + } + } + } + } } /* for (i = 0; state->cli_change[i].flags; i++) */ } @@ -3495,7 +3743,7 @@ mode_process_clients(struct ParseState *state) * Helper function to process the simple modes */ static void -mode_parse_mode(struct ParseState *state, int *flag_p) +mode_parse_mode(struct ParseState *state, ulong64 *flag_p) { /* If they're not an oper, they can't change modes */ if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) { @@ -3509,6 +3757,9 @@ mode_parse_mode(struct ParseState *state, int *flag_p) /* Local users are not permitted to change registration status */ if (flag_p[0] == MODE_REGISTERED && !(state->flags & MODE_PARSE_FORCE) && MyUser(state->sptr)) return; + + if(flag_p[0] == MODE_AUDITORIUM) + audit_chan_users(state, flag_p); if (state->dir == MODE_ADD) { state->add |= flag_p[0]; @@ -3550,8 +3801,9 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr, struct Channel *chptr, int parc, char *parv[], unsigned int flags, struct Membership* member) { - static int chan_flags[] = { + static ulong64 chan_flags[] = { MODE_CHANOP, 'o', + MODE_HALFOP, 'h', MODE_VOICE, 'v', MODE_PRIVATE, 'p', MODE_SECRET, 's', @@ -3571,19 +3823,22 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr, MODE_PERSIST, 'z', MODE_NOCOLOUR, 'c', MODE_NOCTCP, 'C', -/* MODE_EXCEPTION, 'e',*/ MODE_NOAMSGS, 'M', MODE_NONOTICE, 'N', MODE_QUARANTINE, 'Q', MODE_ALTCHAN, 'F', MODE_ACCESS, 'a', + MODE_AUDITORIUM, 'u', + MODE_SSLCHAN, 'S', + MODE_NOFLOOD, 'f', + MODE_BANEXCEPTION, 'e', MODE_ADD, '+', MODE_DEL, '-', 0x0, 0x0 }; int i; - int *flag_p; - unsigned int t_mode; + ulong64 *flag_p; + ulong64 t_mode; char *modestr; struct ParseState state; @@ -3647,6 +3902,9 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr, break; case 'F': mode_parse_altchan(&state, flag_p); + break; + case 'f': + mode_parse_noflood(&state, flag_p); break; case 'Q': if(IsNetServ(state.sptr) && IsSecurityServ(state.sptr)) @@ -3663,10 +3921,8 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr, if (IsServer(cptr) || feature_bool(FEAT_OPLEVELS)) mode_parse_upass(&state, flag_p); break; - -/* case 'e': - if(MyUser(state.sptr) && !(state.flags & MODE_PARSE_FORCE) && !feature_bool(FEAT_EXCEPT_ENABLE)) break;*/ case 'b': /* deal with bans */ + case 'e': /* and exceptions */ mode_parse_ban(&state, flag_p); break; @@ -3678,7 +3934,10 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr, case 'v': mode_parse_client(&state, flag_p); break; - + case 'h': + if (IsServer(cptr) || feature_bool(FEAT_HALFOP)) + mode_parse_client(&state, flag_p); + break; case 'z': /* remote clients are allowed to change +z */ if(!MyUser(state.sptr) || (state.flags & MODE_PARSE_FORCE)) mode_parse_mode(&state, flag_p); @@ -3794,6 +4053,9 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr, if (state.chptr->mode.altchan && !(state.done & DONE_ALTCHAN)) modebuf_mode_string(state.mbuf, MODE_DEL | MODE_ALTCHAN, state.chptr->mode.altchan, 0); + if (state.chptr->mode.noflood && !(state.done & DONE_NOFLOOD)) + modebuf_mode_string(state.mbuf, MODE_DEL | MODE_NOFLOOD, + state.chptr->mode.noflood, 0); if (*state.chptr->mode.key && !(state.done & DONE_KEY_DEL)) modebuf_mode_string(state.mbuf, MODE_DEL | MODE_KEY, state.chptr->mode.key, 0); @@ -3871,7 +4133,12 @@ joinbuf_join(struct JoinBuf *jbuf, struct Channel *chan, unsigned int flags) SetUserParting(member); /* Send notification to channel */ - if (!(flags & (CHFL_ZOMBIE | CHFL_DELAYED))) + if((chan->mode.mode & MODE_AUDITORIUM) && !(member->status & CHFL_VOICED_OR_OPPED)) { + //send part to ops only + sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_PART, chan, NULL, SKIP_NONOPS, "%H :%s", chan, jbuf->jb_comment); + if(MyUser(jbuf->jb_source)) + sendcmdto_one(jbuf->jb_source, CMD_PART, jbuf->jb_source, "%H :%s", chan, jbuf->jb_comment); + } else if (!(flags & (CHFL_ZOMBIE | CHFL_DELAYED))) sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_PART, chan, NULL, 0, (flags & CHFL_BANNED || !jbuf->jb_comment) ? ":%H" : "%H :%s", chan, jbuf->jb_comment); @@ -3909,8 +4176,14 @@ joinbuf_join(struct JoinBuf *jbuf, struct Channel *chan, unsigned int flags) "%H %Tu %i", chan, chan->creationtime, 0); } } - - if (!((chan->mode.mode & MODE_DELJOINS) && !(flags & CHFL_VOICED_OR_OPPED)) && !((flags & CHFL_INVISIBLE) && !(flags & CHFL_VOICED_OR_OPPED))) { + + if((chan->mode.mode & MODE_AUDITORIUM) && !(flags & CHFL_VOICED_OR_OPPED)) { + //we have to send this JOIN event to ops only... + sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_JOIN, chan, NULL, SKIP_NONOPS, "%H", chan); + if(MyUser(jbuf->jb_source)) + sendcmdto_one(jbuf->jb_source, CMD_JOIN, jbuf->jb_source, ":%H", chan); + } + else if (!((chan->mode.mode & MODE_DELJOINS) && !(flags & CHFL_VOICED_OR_OPPED)) && !((flags & CHFL_INVISIBLE) && !(flags & CHFL_VOICED_OR_OPPED))) { /* Send the notification to the channel */ sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_JOIN, chan, NULL, 0, "%H", chan); @@ -4023,6 +4296,16 @@ void CheckDelayedJoins(struct Channel *chan) } } +void CheckEnableDelayedJoins(struct Channel *chan) { + if (!(chan->mode.mode & MODE_WASDELJOINS) && find_delayed_joins(chan)) { + chan->mode.mode |= MODE_WASDELJOINS; + sendcmdto_channel_butserv_butone(&me, CMD_MODE, chan, NULL, 0, + "%H +d", chan); + sendcmdto_channel_servers_butone(&me, CMD_MODE, chan, NULL, 0, + "%H +d", chan); + } +} + /* checks whether a channel is nonpersistent with no users and deletes it * returns 1 if deleted, otherwise 0 */ @@ -4076,7 +4359,7 @@ void RevealDelayedJoinIfNeeded(struct Client *sptr, struct Channel *chptr) int ext_amsg_block(struct Client *cptr, struct Channel *chptr, const char *msg) { int amsg_time; - + /* First on every message we check whether the mechanism is enabled. * If it is enabled, we check: * - whether the channel has MODE_NOAMSGS @@ -4090,6 +4373,60 @@ int ext_amsg_block(struct Client *cptr, struct Channel *chptr, const char *msg) */ amsg_time = feature_int(FEAT_NOAMSG_TIME); if(amsg_time > 0) { + /* first of all strip the message (filter out invisible content) */ + char *stripped_message = MyMalloc(BUFSIZE + 1); + strcpy(stripped_message, msg); + char p = stripped_message[0]; + int p_pos = 0; + int is_visible = 1, is_ccode = 0, i = 0, j = 0; + char codes[5]; + for(i = 0; p != '\n' && p != 0; p = stripped_message[++i]) { + if(p == 3) { + j = 0; + is_ccode = 1; + } else if(is_ccode) { + if((p >= 48 && p <= 57) || p == 44) { + if(is_ccode == 1) { + if(p == 44) { + is_ccode = 2; + codes[j++] = 0; + j = 0; + } else + codes[j++] = p; + } else { + //compare + if(p != codes[j++]) { + is_ccode = 3; + } + } + } else { + //END of color code... + is_ccode = 0; + if(is_ccode != 1 && codes[j] != 0) is_ccode = 3; + if(is_ccode == 1) { + codes[j] = 0; + int k; + for(k = 0; k < j-1; k++) { + if(codes[k] != 48) { + is_visible = 1; + goto normalchar; + } + } + is_visible = 0; + } else if(is_ccode == 2) { + is_visible = 0; + } else if(is_ccode == 3) { + is_visible = 1; + goto normalchar; + } + } + } else { + normalchar: + if(is_visible) + stripped_message[p_pos++] = p; + } + } + stripped_message[p_pos++] = 0; /* Allocate a new buffer if there is none, yet. */ if(!cli_user(cptr)->lastmsg) { cli_user(cptr)->lastmsg = MyMalloc(BUFSIZE + 1); @@ -4097,17 +4434,77 @@ int ext_amsg_block(struct Client *cptr, struct Channel *chptr, const char *msg) } if((chptr->mode.mode & MODE_NOAMSGS) && ((cli_user(cptr)->lastmsg_time + amsg_time) >= CurrentTime) && - (strcmp(cli_user(cptr)->lastmsg, msg) == 0)) { + (strcmp(cli_user(cptr)->lastmsg, stripped_message) == 0)) { cli_user(cptr)->lastmsg_time = CurrentTime; cli_user(cptr)->lastmsg_num++; + MyFree(stripped_message); if(cli_user(cptr)->lastmsg_num >= feature_int(FEAT_NOAMSG_NUM)) return 1; else return 0; } /* Message did not match so update the data. */ cli_user(cptr)->lastmsg_time = CurrentTime; cli_user(cptr)->lastmsg_num = 0; - strcpy(cli_user(cptr)->lastmsg, msg); + strcpy(cli_user(cptr)->lastmsg, stripped_message); + MyFree(stripped_message); } return 0; } +/** Extended flood check. + * The channelmode MODE_NOFLOOD prevents users from flooding the channel. + * + * This function returns 0 if the message may pass and 1 if the message should + * be blocked. + * For this function to work properly it must be called on every PRIVMSG which + * is sent by any user. + * + * --pk910 2011/7/1 + */ +int ext_noflood_block(struct Client *cptr, struct Channel *chptr) { + if(!*chptr->mode.noflood) return 0; + struct Membership *member = find_member_link(chptr, cptr); + if(!member) return 0; //TODO: we've no check for -n channels implemented, yet + //check if this user is really affected by +f + unsigned int flags = (chptr->mode.noflood_value & 0x0000000f); //0000 0000 0000 0000 0000 0000 0000 1111 = 0x0000000f >> 0 + unsigned int count = (chptr->mode.noflood_value & 0x00002ff0) >> 4; //0000 0000 0000 0000 0011 1111 1111 0000 = 0x00002ff0 >> 4 + int time = (chptr->mode.noflood_value & 0x0fffc000) >> 14; //0000 1111 1111 1111 1100 0000 0000 0000 = 0x0fffc000 >> 14 + if(count == 0 || time == 0) return 0; + if(!(flags & FLFL_NOFLOOD) && HasPriv(cptr, PRIV_FLOOD)) + return 0; + if(!(flags & FLFL_CHANOP) && (member->status & CHFL_CHANOP)) + return 0; + if(!(flags & (FLFL_CHANOP | FLFL_HALFOP)) && (member->status & CHFL_HALFOP)) + return 0; + if(!(flags & (FLFL_CHANOP | FLFL_HALFOP | FLFL_VOICE)) && (member->status & CHFL_VOICE)) + return 0; + int floodcount = 0; + struct MemberFlood *floodnode, *prev_floodnode; + for (floodnode = member->flood; floodnode; floodnode = floodnode->next_memberflood) { + if(floodnode->time + time > CurrentTime) { + if(floodcount == 0 && floodnode != member->flood) { + //free all before + prev_floodnode->next_memberflood = free_MemberFlood; + free_MemberFlood = prev_floodnode; + member->flood = floodnode; + } + floodcount++; + } + prev_floodnode = floodnode; + } + Debug((DEBUG_INFO, "floodcount: %i", floodcount)); + if(floodcount >= count) return 1; //blocked! + //add a new floodnode :) + if(free_MemberFlood) { + floodnode = free_MemberFlood; + free_MemberFlood = floodnode->next_memberflood; + } else + floodnode = (struct MemberFlood*) MyMalloc(sizeof(struct MemberFlood)); + floodnode->time = CurrentTime; + floodnode->next_memberflood = NULL; + if(floodcount > 0) + prev_floodnode->next_memberflood = floodnode; + else + member->flood = floodnode; + return 0; +} +