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. */
* @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];
/* 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:
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
}
}
+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.
member->user = who;
member->channel = chptr;
member->status = flags;
+ member->flood = NULL;
SetOpLevel(member, oplevel);
member->next_member = chptr->members;
--(cli_user(member->user))->joined;
+ member->flood = NULL;
+
member->next_member = membershipFreeList;
membershipFreeList = member;
*/
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);
* 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;
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;
}
}
*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);
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)
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)
+ 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
- *
- * @param cptr Client to send the exceptionlist to.
- * @param chptr Channel whose exceptionlist to send.
- */
-static void send_exception_list(struct Client* cptr, struct Channel* chptr)
-{
- struct Ban* lp;
-
- 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);
- }
-
- send_reply(cptr, RPL_ENDOFEXCEPTIONLIST, chptr->chname);
-}
-
/** Get a channel block, creating if necessary.
* Get Channel block for chname (and allocate a new channel
* block, if it didn't exists before).
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',
0x0, 0x0
};
static ulong64 local_flags[] = {
bufptr[(*bufptr_i)++] = MB_TYPE(mbuf, i) & MODE_CHANOP ? 'o' : '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)) {
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))
{
case MODE_APASS:
mode_char = 'A';
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;
default:
mode_char = 'b';
break;
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))
build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0, ' ');
/* deal with keys... */
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 */
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))
build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0, ' ');
/*
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_AUDITORIUM);
+ MODE_QUARANTINE | MODE_AUDITORIUM | MODE_SSLCHAN);
if (!(mode & ~(MODE_ADD | MODE_DEL))) /* don't add empty modes... */
return;
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',
0x0, 0x0
};
ulong64 add;
int i, bufpos = 0, len;
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);
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);
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);
}
}
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';
#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 */
struct ParseState {
struct ModeBuf *mbuf;
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(!IsChanOp(member)) {
send_notoper(state);
return;
+ }
}
if (!(state->flags & MODE_PARSE_WIPEOUT) &&
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_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] == '@') {
+ if(tmp[0] == '+') flags |= FLFL_VOICE;
+ 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 <<= 3;
+ 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;
+ }
}
}
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 (MyUser(state->sptr) && !(state->done & DONE_BANLIST)) {
+ send_ban_list(state->sptr, state->chptr);
+ state->done |= DONE_BANLIST;
}
return;
}
} 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 | MODE_BAN, bandup, 1);
if (state->flags & MODE_PARSE_SET) { /* Ok, make it take effect */
if (prevban) /* clip it out of the list... */
char *bandup;
/* 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 | MODE_BAN, bandup, 1);
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->flags = ban->flags & BAN_IPMASK;
newban->next = state->chptr->banlist; /* and link it in */
state->chptr->banlist = newban;
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) {
state->cli_change[i].client,
state->cli_change[i].oplevel);
- if(MyUser(member->user) && (member->channel->mode.mode & MODE_AUDITORIUM) && (state->cli_change[i].flag & MODE_CHANOP)) {
- do_names(member->user, member->channel, NAMES_ALL|NAMES_EON|((member->status & MODE_CHANOP) ? 0 : NAMES_OPS));
+ 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)) {
+ //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(!IsChanOp(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(!IsChanOp(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++) */
}
/* 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];
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_ADD, '+',
MODE_DEL, '-',
0x0, 0x0
case 'l': /* deal with limits */
mode_parse_limit(&state, flag_p);
break;
- case 'u': /* deal with not oped members */
- audit_chan_users(&state, flag_p);
- mode_parse_mode(&state, flag_p);
- break;
case 'a': /* deal with limits */
mode_parse_access(&state, flag_p);
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))
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 */
mode_parse_ban(&state, flag_p);
break;
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);
SetUserParting(member);
/* Send notification to channel */
- if((chan->mode.mode & MODE_AUDITORIUM) && !(flags & CHFL_VOICED_OR_OPPED)) {
- //do noting here
+ 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) ?
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
*/
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 = 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);
}
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 & 0x00000007); //0000 0000 0000 0000 0000 0000 0000 0111 = 0x00000007 >> 0
+ unsigned int count = (chptr->mode.noflood_value & 0x00001ff8) >> 3; //0000 0000 0000 0000 0001 1111 1111 1000 = 0x00001ff8 >> 3
+ int time = (chptr->mode.noflood_value & 0x07ffe000) >> 13; //0000 0111 1111 1111 1110 0000 0000 0000 = 0x07ffe000 >> 13
+ 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_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;
+}
+