From a85a38e69dfd512d501643fc280ee081f85706e0 Mon Sep 17 00:00:00 2001 From: Bram Matthys Date: Tue, 25 May 2021 20:25:31 +0200 Subject: [PATCH] Add the ability to SPAMFILTER message-tags. The new target type is called 'T' and we match against "name=value" of each message tag (or just "name" if it is without value). Example: SPAMFILTER ADD -simple T kill 0 this_is_a_test +typing=active (No this is not a suggestion :D) This probably won't be used much at all, but it is good to have the option available in case there is some massive problem, especially since more message tags may pop up sooner or later. Caveat: this is actually a bit slow as we may have to check multiple message tags for a single line. If there are zero message-tag spamfilters then we will automatically short-circuit and save all this CPU, which will be the most common case. --- doc/conf/help/help.conf | 3 +- include/h.h | 3 +- include/modules.h | 1 + include/struct.h | 13 ++++--- src/aliases.c | 16 ++++---- src/api-efunctions.c | 4 +- src/ircd.c | 2 +- src/misc.c | 17 +++++---- src/modules/away.c | 2 +- src/modules/dccdeny.c | 2 +- src/modules/message.c | 19 ++++++++-- src/modules/nick.c | 4 +- src/modules/part.c | 2 +- src/modules/quit.c | 2 +- src/modules/setname.c | 2 +- src/modules/tkl.c | 83 ++++++++++++++++++++++++++++++++++++++--- src/modules/topic.c | 2 +- src/parse.c | 6 +++ 18 files changed, 140 insertions(+), 43 deletions(-) diff --git a/doc/conf/help/help.conf b/doc/conf/help/help.conf index 3d66cc687..1056d0c47 100644 --- a/doc/conf/help/help.conf +++ b/doc/conf/help/help.conf @@ -1405,7 +1405,8 @@ help Spamfilter { " [type] specifies the target type, you can specify multiple targets:"; " 'c' channel msg, 'p' private msg, 'n' private notice,"; " 'N' channel notice, 'P' part msg, 'q' quit msg, 'd' dcc,"; - " 'a' away, 't' topic, 'u' user (nick!user@host:realname ban)"; + " 'a' away, 't' topic, 'T' message-tag (eg: +typing=active),", + " 'u' user (nick!user@host:realname ban)"; " [action] specifies the action to be taken (only 1 action can be specified):"; " 'kill', 'tempshun' (only shun current session), 'shun',"; " 'kline', 'gline', 'zline', 'gzline', 'block' (blocks the msg),"; diff --git a/include/h.h b/include/h.h index 83cfbb404..3fdc50eb2 100644 --- a/include/h.h +++ b/include/h.h @@ -723,7 +723,8 @@ extern MODVAR void (*tkl_stats)(Client *cptr, int type, char *para, int *cnt); extern MODVAR void (*tkl_sync)(Client *client); extern MODVAR void (*cmd_tkl)(Client *client, MessageTag *recv_mtags, int parc, char *parv[]); extern MODVAR int (*place_host_ban)(Client *client, BanAction action, char *reason, long duration); -extern MODVAR int (*match_spamfilter)(Client *client, char *str_in, int type, char *target, int flags, TKL **rettk); +extern MODVAR int (*match_spamfilter)(Client *client, char *str_in, int type, char *cmd, char *target, int flags, TKL **rettk); +extern MODVAR int (*match_spamfilter_mtags)(Client *client, MessageTag *mtags, char *cmd); extern MODVAR int (*join_viruschan)(Client *client, TKL *tk, int type); extern MODVAR unsigned char *(*StripColors)(unsigned char *text); extern MODVAR const char *(*StripControlCodes)(unsigned char *text); diff --git a/include/modules.h b/include/modules.h index 1a7b151ed..d887ccbfa 100644 --- a/include/modules.h +++ b/include/modules.h @@ -2260,6 +2260,7 @@ enum EfunctionType { EFUNC_CMD_TKL, EFUNC_PLACE_HOST_BAN, EFUNC_DOSPAMFILTER, + EFUNC_MATCH_SPAMFILTER_MTAGS, EFUNC_DOSPAMFILTER_VIRUSCHAN, EFUNC_FIND_TKLINE_MATCH_ZAP_EX, EFUNC_SEND_LIST, diff --git a/include/struct.h b/include/struct.h index 306cbb544..a1ece52d1 100644 --- a/include/struct.h +++ b/include/struct.h @@ -992,12 +992,13 @@ struct Secret { #define SPAMF_USERMSG 0x0002 /* p */ #define SPAMF_USERNOTICE 0x0004 /* n */ #define SPAMF_CHANNOTICE 0x0008 /* N */ -#define SPAMF_PART 0x0010 /* P */ -#define SPAMF_QUIT 0x0020 /* q */ -#define SPAMF_DCC 0x0040 /* d */ -#define SPAMF_USER 0x0080 /* u */ -#define SPAMF_AWAY 0x0100 /* a */ -#define SPAMF_TOPIC 0x0200 /* t */ +#define SPAMF_PART 0x0010 /* P */ +#define SPAMF_QUIT 0x0020 /* q */ +#define SPAMF_DCC 0x0040 /* d */ +#define SPAMF_USER 0x0080 /* u */ +#define SPAMF_AWAY 0x0100 /* a */ +#define SPAMF_TOPIC 0x0200 /* t */ +#define SPAMF_MTAG 0x0400 /* m */ /* Other flags only for function calls: */ #define SPAMFLAG_NOWARN 0x0001 diff --git a/src/aliases.c b/src/aliases.c index 860e95ba5..22f299b7d 100644 --- a/src/aliases.c +++ b/src/aliases.c @@ -66,7 +66,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char * { if (SERVICES_NAME && (acptr = find_person(alias->nick, NULL))) { - if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_USERMSG, alias->nick, 0, NULL)) + if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_USERMSG, cmd, alias->nick, 0, NULL)) return; sendto_one(acptr, NULL, ":%s PRIVMSG %s@%s :%s", client->name, alias->nick, SERVICES_NAME, parv[1]); @@ -78,7 +78,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char * { if (STATS_SERVER && (acptr = find_person(alias->nick, NULL))) { - if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_USERMSG, alias->nick, 0, NULL)) + if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_USERMSG, cmd, alias->nick, 0, NULL)) return; sendto_one(acptr, NULL, ":%s PRIVMSG %s@%s :%s", client->name, alias->nick, STATS_SERVER, parv[1]); @@ -90,7 +90,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char * { if ((acptr = find_person(alias->nick, NULL))) { - if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_USERMSG, alias->nick, 0, NULL)) + if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_USERMSG, cmd, alias->nick, 0, NULL)) return; if (MyUser(acptr)) sendto_one(acptr, NULL, ":%s!%s@%s PRIVMSG %s :%s", client->name, @@ -112,7 +112,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char * char *errmsg = NULL; if (can_send_to_channel(client, channel, &msg, &errmsg, 0)) { - if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_CHANMSG, channel->chname, 0, NULL)) + if (alias->spamfilter && match_spamfilter(client, parv[1], SPAMF_CHANMSG, cmd, channel->chname, 0, NULL)) return; new_message(client, NULL, &mtags); sendto_channel(channel, client, client->direction, @@ -203,7 +203,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char * { if (SERVICES_NAME && (acptr = find_person(format->nick, NULL))) { - if (alias->spamfilter && match_spamfilter(client, output, SPAMF_USERMSG, format->nick, 0, NULL)) + if (alias->spamfilter && match_spamfilter(client, output, SPAMF_USERMSG, cmd, format->nick, 0, NULL)) return; sendto_one(acptr, NULL, ":%s PRIVMSG %s@%s :%s", client->name, format->nick, SERVICES_NAME, output); @@ -214,7 +214,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char * { if (STATS_SERVER && (acptr = find_person(format->nick, NULL))) { - if (alias->spamfilter && match_spamfilter(client, output, SPAMF_USERMSG, format->nick, 0, NULL)) + if (alias->spamfilter && match_spamfilter(client, output, SPAMF_USERMSG, cmd, format->nick, 0, NULL)) return; sendto_one(acptr, NULL, ":%s PRIVMSG %s@%s :%s", client->name, format->nick, STATS_SERVER, output); @@ -225,7 +225,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char * { if ((acptr = find_person(format->nick, NULL))) { - if (alias->spamfilter && match_spamfilter(client, output, SPAMF_USERMSG, format->nick, 0, NULL)) + if (alias->spamfilter && match_spamfilter(client, output, SPAMF_USERMSG, cmd, format->nick, 0, NULL)) return; if (MyUser(acptr)) sendto_one(acptr, NULL, ":%s!%s@%s PRIVMSG %s :%s", client->name, @@ -247,7 +247,7 @@ void cmd_alias(Client *client, MessageTag *mtags, int parc, char *parv[], char * char *errmsg = NULL; if (!can_send_to_channel(client, channel, &msg, &errmsg, 0)) { - if (alias->spamfilter && match_spamfilter(client, output, SPAMF_CHANMSG, channel->chname, 0, NULL)) + if (alias->spamfilter && match_spamfilter(client, output, SPAMF_CHANMSG, cmd, channel->chname, 0, NULL)) return; new_message(client, NULL, &mtags); sendto_channel(channel, client, client->direction, diff --git a/src/api-efunctions.c b/src/api-efunctions.c index a739fb3f7..9ede41c11 100644 --- a/src/api-efunctions.c +++ b/src/api-efunctions.c @@ -67,7 +67,8 @@ void (*tkl_stats)(Client *client, int type, char *para, int *cnt); void (*tkl_sync)(Client *client); void (*cmd_tkl)(Client *client, MessageTag *mtags, int parc, char *parv[]); int (*place_host_ban)(Client *client, BanAction action, char *reason, long duration); -int (*match_spamfilter)(Client *client, char *str_in, int type, char *target, int flags, TKL **rettk); +int (*match_spamfilter)(Client *client, char *str_in, int type, char *cmd, char *target, int flags, TKL **rettk); +int (*match_spamfilter_mtags)(Client *client, MessageTag *mtags, char *cmd); int (*join_viruschan)(Client *client, TKL *tk, int type); unsigned char *(*StripColors)(unsigned char *text); const char *(*StripControlCodes)(unsigned char *text); @@ -307,6 +308,7 @@ void efunctions_init(void) efunc_init_function(EFUNC_CMD_TKL, cmd_tkl, NULL); efunc_init_function(EFUNC_PLACE_HOST_BAN, place_host_ban, NULL); efunc_init_function(EFUNC_DOSPAMFILTER, match_spamfilter, NULL); + efunc_init_function(EFUNC_MATCH_SPAMFILTER_MTAGS, match_spamfilter_mtags, NULL); efunc_init_function(EFUNC_DOSPAMFILTER_VIRUSCHAN, join_viruschan, NULL); efunc_init_function(EFUNC_STRIPCOLORS, StripColors, NULL); efunc_init_function(EFUNC_STRIPCONTROLCODES, StripControlCodes, NULL); diff --git a/src/ircd.c b/src/ircd.c index 32f307e9b..ed1e0d42b 100644 --- a/src/ircd.c +++ b/src/ircd.c @@ -370,7 +370,7 @@ int match_tkls(Client *client) if (loop.do_bancheck_spamf_away && IsUser(client) && client->user->away != NULL && - match_spamfilter(client, client->user->away, SPAMF_AWAY, NULL, SPAMFLAG_NOWARN, NULL)) + match_spamfilter(client, client->user->away, SPAMF_AWAY, "AWAY", NULL, SPAMFLAG_NOWARN, NULL)) { return 1; } diff --git a/src/misc.c b/src/misc.c index e5292f0e1..a7c1c7fb3 100644 --- a/src/misc.c +++ b/src/misc.c @@ -81,16 +81,17 @@ typedef struct { } SpamfilterTargetTable; SpamfilterTargetTable spamfiltertargettable[] = { - { SPAMF_CHANMSG, 'c', "channel", "PRIVMSG" }, - { SPAMF_USERMSG, 'p', "private", "PRIVMSG" }, + { SPAMF_CHANMSG, 'c', "channel", "PRIVMSG" }, + { SPAMF_USERMSG, 'p', "private", "PRIVMSG" }, { SPAMF_USERNOTICE, 'n', "private-notice", "NOTICE" }, { SPAMF_CHANNOTICE, 'N', "channel-notice", "NOTICE" }, - { SPAMF_PART, 'P', "part", "PART" }, - { SPAMF_QUIT, 'q', "quit", "QUIT" }, - { SPAMF_DCC, 'd', "dcc", "PRIVMSG" }, - { SPAMF_USER, 'u', "user", "NICK" }, - { SPAMF_AWAY, 'a', "away", "AWAY" }, - { SPAMF_TOPIC, 't', "topic", "TOPIC" }, + { SPAMF_PART, 'P', "part", "PART" }, + { SPAMF_QUIT, 'q', "quit", "QUIT" }, + { SPAMF_DCC, 'd', "dcc", "PRIVMSG" }, + { SPAMF_USER, 'u', "user", "NICK" }, + { SPAMF_AWAY, 'a', "away", "AWAY" }, + { SPAMF_TOPIC, 't', "topic", "TOPIC" }, + { SPAMF_MTAG, 'T', "message-tag", "message-tag" }, { 0, 0, 0, 0 } }; diff --git a/src/modules/away.c b/src/modules/away.c index 0f6cc18e4..3a741f505 100644 --- a/src/modules/away.c +++ b/src/modules/away.c @@ -86,7 +86,7 @@ CMD_FUNC(cmd_away) } /* Check spamfilters */ - if (MyUser(client) && match_spamfilter(client, new_reason, SPAMF_AWAY, NULL, 0, NULL)) + if (MyUser(client) && match_spamfilter(client, new_reason, SPAMF_AWAY, "AWAY", NULL, 0, NULL)) return; /* Check set::anti-flood::away-flood */ diff --git a/src/modules/dccdeny.c b/src/modules/dccdeny.c index 9aaf23e52..d27a9f7cd 100644 --- a/src/modules/dccdeny.c +++ b/src/modules/dccdeny.c @@ -639,7 +639,7 @@ static int can_dcc(Client *client, char *target, Client *targetcli, char *filena return 0; } - if (match_spamfilter(client, filename, SPAMF_DCC, target, 0, NULL)) + if (match_spamfilter(client, filename, SPAMF_DCC, "PRIVMSG", target, 0, NULL)) return 0; if ((fl = dcc_isforbidden(client, filename))) diff --git a/src/modules/message.c b/src/modules/message.c index 78e0c309d..e9c9c1994 100644 --- a/src/modules/message.c +++ b/src/modules/message.c @@ -117,8 +117,14 @@ int can_send_to_user(Client *client, Client *target, char **msgtext, char **errm } // Possible FIXME: make match_spamfilter also use errmsg, or via a wrapper? or use same numeric? - if (MyUser(client) && match_spamfilter(client, *msgtext, (sendtype == SEND_TYPE_NOTICE ? SPAMF_USERNOTICE : SPAMF_USERMSG), target->name, 0, NULL)) - return 0; + if (MyUser(client)) + { + int spamtype = (sendtype == SEND_TYPE_NOTICE ? SPAMF_USERNOTICE : SPAMF_USERMSG); + char *cmd = sendtype_to_cmd(sendtype); + + if (match_spamfilter(client, *msgtext, spamtype, cmd, target->name, 0, NULL)) + return 0; + } n = HOOK_CONTINUE; for (h = Hooks[HOOKTYPE_CAN_SEND_TO_USER]; h; h = h->next) @@ -379,8 +385,13 @@ void cmd_message(Client *client, MessageTag *recv_mtags, int parc, char *parv[], if ((*parv[2] == '\001') && strncmp(&parv[2][1], "ACTION ", 7)) sendflags |= SKIP_CTCP; - if (MyUser(client) && match_spamfilter(client, text, (sendtype == SEND_TYPE_NOTICE ? SPAMF_CHANNOTICE : SPAMF_CHANMSG), channel->chname, 0, NULL)) - return; + if (MyUser(client)) + { + int spamtype = (sendtype == SEND_TYPE_NOTICE ? SPAMF_USERNOTICE : SPAMF_USERMSG); + + if (match_spamfilter(client, text, spamtype, cmd, channel->chname, 0, NULL)) + return; + } new_message(client, recv_mtags, &mtags); diff --git a/src/modules/nick.c b/src/modules/nick.c index 680a8bf1b..f6364e239 100644 --- a/src/modules/nick.c +++ b/src/modules/nick.c @@ -298,7 +298,7 @@ CMD_FUNC(cmd_nick_local) { /* Local client changing nick: check spamfilter */ spamfilter_build_user_string(spamfilter_user, nick, client); - if (match_spamfilter(client, spamfilter_user, SPAMF_USER, NULL, 0, NULL)) + if (match_spamfilter(client, spamfilter_user, SPAMF_USER, "NICK", NULL, 0, NULL)) return; } @@ -883,7 +883,7 @@ int _register_user(Client *client, char *nick, char *username, char *umode, char find_shun(client); spamfilter_build_user_string(spamfilter_user, client->name, client); - if (match_spamfilter(client, spamfilter_user, SPAMF_USER, NULL, 0, &savetkl)) + if (match_spamfilter(client, spamfilter_user, SPAMF_USER, NULL, NULL, 0, &savetkl)) { if (savetkl && ((savetkl->ptr.spamfilter->action == BAN_ACT_VIRUSCHAN) || (savetkl->ptr.spamfilter->action == BAN_ACT_SOFT_VIRUSCHAN))) diff --git a/src/modules/part.c b/src/modules/part.c index efc86591f..937087a7e 100644 --- a/src/modules/part.c +++ b/src/modules/part.c @@ -89,7 +89,7 @@ CMD_FUNC(cmd_part) } if (commentx) { - if (match_spamfilter(client, commentx, SPAMF_PART, parv[1], 0, NULL)) + if (match_spamfilter(client, commentx, SPAMF_PART, "PART", parv[1], 0, NULL)) commentx = NULL; if (IsDead(client)) return; diff --git a/src/modules/quit.c b/src/modules/quit.c index 7af7a5250..8c59821a1 100644 --- a/src/modules/quit.c +++ b/src/modules/quit.c @@ -82,7 +82,7 @@ CMD_FUNC(cmd_quit) return; } - if (match_spamfilter(client, comment, SPAMF_QUIT, NULL, 0, NULL)) + if (match_spamfilter(client, comment, SPAMF_QUIT, "QUIT", NULL, 0, NULL)) { comment = client->name; if (IsDead(client)) diff --git a/src/modules/setname.c b/src/modules/setname.c index eb75a6be8..85ab9c3e5 100644 --- a/src/modules/setname.c +++ b/src/modules/setname.c @@ -89,7 +89,7 @@ CMD_FUNC(cmd_setname) /* set the new name before we check, but don't send to servers unless it is ok */ strcpy(client->info, parv[1]); spamfilter_build_user_string(spamfilter_user, client->name, client); - if (match_spamfilter(client, spamfilter_user, SPAMF_USER, NULL, 0, NULL)) + if (match_spamfilter(client, spamfilter_user, SPAMF_USER, "SETNAME", NULL, 0, NULL)) { /* Was rejected by spamfilter, restore the realname */ strcpy(client->info, tmpinfo); diff --git a/src/modules/tkl.c b/src/modules/tkl.c index 94ee581a4..ff1cc93c0 100644 --- a/src/modules/tkl.c +++ b/src/modules/tkl.c @@ -82,7 +82,9 @@ void _tkl_stats(Client *client, int type, char *para, int *cnt); void _tkl_sync(Client *client); CMD_FUNC(_cmd_tkl); int _place_host_ban(Client *client, BanAction action, char *reason, long duration); -int _match_spamfilter(Client *client, char *str_in, int type, char *target, int flags, TKL **rettk); +int _match_spamfilter(Client *client, char *str_in, int type, char *cmd, char *target, int flags, TKL **rettk); +int _match_spamfilter_mtags(Client *client, MessageTag *mtags, char *cmd); +int check_mtag_spamfilters_present(void); int _join_viruschan(Client *client, TKL *tk, int type); void _spamfilter_build_user_string(char *buf, char *nick, Client *client); int _match_user(char *rmask, Client *client, int options); @@ -146,6 +148,7 @@ TKLTypeTable tkl_types[] = { #define ALL_VALID_EXCEPTION_TYPES "kline, gline, zline, gzline, spamfilter, shun, qline, blacklist, connect-flood, handshake-data-flood, antirandom, antimixedutf8, ban-version" int max_stats_matches = 1000; +int mtag_spamfilters_present = 0; /**< Are any spamfilters with type SPAMF_MTAG present? */ MOD_TEST() { @@ -179,6 +182,7 @@ MOD_TEST() EfunctionAddVoid(modinfo->handle, EFUNC_CMD_TKL, _cmd_tkl); EfunctionAdd(modinfo->handle, EFUNC_PLACE_HOST_BAN, _place_host_ban); EfunctionAdd(modinfo->handle, EFUNC_DOSPAMFILTER, _match_spamfilter); + EfunctionAdd(modinfo->handle, EFUNC_MATCH_SPAMFILTER_MTAGS, _match_spamfilter_mtags); EfunctionAdd(modinfo->handle, EFUNC_DOSPAMFILTER_VIRUSCHAN, _join_viruschan); EfunctionAddVoid(modinfo->handle, EFUNC_SPAMFILTER_BUILD_USER_STRING, _spamfilter_build_user_string); EfunctionAdd(modinfo->handle, EFUNC_MATCH_USER, _match_user); @@ -213,6 +217,7 @@ MOD_INIT() MOD_LOAD() { + check_mtag_spamfilters_present(); EventAdd(modinfo->handle, "tklexpire", tkl_check_expire, NULL, 5000, 0); return MOD_SUCCESS; } @@ -2383,6 +2388,9 @@ TKL *_tkl_add_spamfilter(int type, unsigned short target, BanAction action, Matc index = tkl_hash(tkl_typetochar(type)); AddListItem(tkl, tklines[index]); + if (target & SPAMF_MTAG) + mtag_spamfilters_present = 1; + return tkl; } @@ -2653,6 +2661,7 @@ void _tkl_del_line(TKL *tkl) /* Finally, free the entry */ free_tkl(tkl); + check_mtag_spamfilters_present(); } /** Add some default ban exceptions - for localhost */ @@ -3130,7 +3139,7 @@ int _find_spamfilter_user(Client *client, int flags) return 0; spamfilter_build_user_string(spamfilter_user, client->name, client); - return match_spamfilter(client, spamfilter_user, SPAMF_USER, NULL, flags, NULL); + return match_spamfilter(client, spamfilter_user, SPAMF_USER, NULL, NULL, flags, NULL); } /** Check a spamfilter against all local users and print a message. @@ -4670,6 +4679,7 @@ int _join_viruschan(Client *client, TKL *tkl, int type) /** match_spamfilter: executes the spamfilter on the input string. * @param str The text (eg msg text, notice text, part text, quit text, etc * @param target The spamfilter target (SPAMF_*) + * @param cmd The command (eg: "PRIVMSG") * @param destination The destination as a text string (eg: "somenick", can be NULL.. eg for away) * @param flags Any flags (SPAMFLAG_*) * @param rettkl Pointer to an aTKLline struct, _used for special circumstances only_ @@ -4677,7 +4687,7 @@ int _join_viruschan(Client *client, TKL *tkl, int type) * 1 if spamfilter matched and it should be blocked (or client exited), 0 if not matched. * In case of 1, be sure to check IsDead(client).. */ -int _match_spamfilter(Client *client, char *str_in, int target, char *destination, int flags, TKL **rettkl) +int _match_spamfilter(Client *client, char *str_in, int target, char *cmd, char *destination, int flags, TKL **rettkl) { TKL *tkl; TKL *winner_tkl = NULL; @@ -4692,6 +4702,9 @@ int _match_spamfilter(Client *client, char *str_in, int target, char *destinatio if (rettkl) *rettkl = NULL; /* initialize to NULL */ + if (!cmd) + cmd = cmdname_by_spamftarget(target); + if (target == SPAMF_USER) str = str_in; else @@ -4771,7 +4784,7 @@ int _match_spamfilter(Client *client, char *str_in, int target, char *destinatio ircsnprintf(buf, sizeof(buf), "[Spamfilter] %s!%s@%s matches filter '%s': [%s%s: '%s'] [%s]", client->name, client->user->username, client->user->realhost, tkl->ptr.spamfilter->match->str, - cmdname_by_spamftarget(target), destinationbuf, str, + cmd, destinationbuf, str, unreal_decodespace(tkl->ptr.spamfilter->tkl_reason)); sendto_snomask_global(SNO_SPAMF, "%s", buf); @@ -4823,6 +4836,12 @@ int _match_spamfilter(Client *client, char *str_in, int target, char *destinatio me.name, client->name, destination, reason); break; } + case SPAMF_MTAG: + { + sendnumericfmt(client, ERR_CANNOTDOCOMMAND, "%s :Command blocked: %s", + cmd, reason); + break; + } case SPAMF_DCC: { char errmsg[512]; @@ -4853,7 +4872,7 @@ int _match_spamfilter(Client *client, char *str_in, int target, char *destinatio if ((tkl->ptr.spamfilter->action == BAN_ACT_WARN) || (tkl->ptr.spamfilter->action == BAN_ACT_SOFT_WARN)) { if ((target != SPAMF_USER) && (target != SPAMF_QUIT)) - sendnumeric(client, RPL_SPAMCMDFWD, cmdname_by_spamftarget(target), reason); + sendnumeric(client, RPL_SPAMCMDFWD, cmd, reason); return 0; } else if ((tkl->ptr.spamfilter->action == BAN_ACT_DCCBLOCK) || (tkl->ptr.spamfilter->action == BAN_ACT_SOFT_DCCBLOCK)) @@ -4891,6 +4910,60 @@ int _match_spamfilter(Client *client, char *str_in, int target, char *destinatio return 0; /* NOTREACHED */ } +/** Check message-tag spamfilters. + * @param client The client + * @param mtags Message tags sent by client + * @param cmd Command to be executed (can be NULL) + * @retval Return 1 to stop processing the command (ignore it) or 0 to allow/continue as normal + */ +int _match_spamfilter_mtags(Client *client, MessageTag *mtags, char *cmd) +{ + MessageTag *m; + char buf[4096]; + char *str; + + /* This is a shortcut: if there are no spamfilters present + * on message tags then we can return immediately. + * Saves a lot of CPU and it is quite likely too! + */ + if (mtag_spamfilters_present == 0) + return 0; + + for (m = mtags; m; m = m->next) + { + if (m->value) + { + snprintf(buf, sizeof(buf), "%s=%s", m->name, m->value); + str = buf; + } else { + str = m->name; + } + if (match_spamfilter(client, str, SPAMF_MTAG, cmd, NULL, 0, NULL)) + return 1; + } + return 0; +} + +/** Updates 'mtag_spamfilters_present' based on if any spamfilters + * are present with the SPAMF_MTAG target. + */ +int check_mtag_spamfilters_present(void) +{ + TKL *tkl; + + for (tkl = tklines[tkl_hash('F')]; tkl; tkl = tkl->next) + { + if (tkl->ptr.spamfilter->target & SPAMF_MTAG) + { + mtag_spamfilters_present = 1; + return 1; + } + } + + mtag_spamfilters_present = 0; + return 0; +} + /** CIDR function to compare the first 'mask' bits. * @author Taken from atheme * @returns 1 if equal, 0 if not. diff --git a/src/modules/topic.c b/src/modules/topic.c index 2aa170834..309de5d6d 100644 --- a/src/modules/topic.c +++ b/src/modules/topic.c @@ -242,7 +242,7 @@ CMD_FUNC(cmd_topic) Hook *tmphook; int n; - if (match_spamfilter(client, topic, SPAMF_TOPIC, channel->chname, 0, NULL)) + if (match_spamfilter(client, topic, SPAMF_TOPIC, "TOPIC", channel->chname, 0, NULL)) return; for (tmphook = Hooks[HOOKTYPE_PRE_LOCAL_TOPIC]; tmphook; tmphook = tmphook->next) { diff --git a/src/parse.c b/src/parse.c index 717ce1294..696695870 100644 --- a/src/parse.c +++ b/src/parse.c @@ -485,6 +485,11 @@ static void parse2(Client *cptr, Client **fromptr, MessageTag *mtags, char *ch) } } para[++i] = NULL; + + /* Check if one of the message tags are rejected by spamfilter */ + if (MyConnect(from) && !IsServer(from) && match_spamfilter_mtags(from, mtags, cmptr ? cmptr->cmd : NULL)) + return; + if (cmptr == NULL) { do_numeric(numeric, from, mtags, i, para); @@ -494,6 +499,7 @@ static void parse2(Client *cptr, Client **fromptr, MessageTag *mtags, char *ch) if (IsUser(cptr) && (cmptr->flags & CMD_RESETIDLE)) cptr->local->last = TStime(); + /* Now ready to execute the command */ #ifndef DEBUGMODE if (cmptr->flags & CMD_ALIAS) {