diff --git a/include/h.h b/include/h.h index 348192c6d..08be4df9d 100644 --- a/include/h.h +++ b/include/h.h @@ -534,7 +534,7 @@ extern void init_CommandHash(void); /* CRULE */ extern struct CRuleNode* crule_parse(const char*); extern void crule_free(struct CRuleNode**); -extern int crule_eval(struct CRuleNode* rule); +extern int crule_eval(crule_context *context, CRuleNode *rule); extern int crule_test(const char *rule); extern const char *crule_errstring(int errcode); @@ -818,10 +818,11 @@ extern MODVAR TKL *(*tkl_add_banexception)(int type, const char *usermask, const time_t expire_at, time_t set_at, int soft, const char *bantypes, int flags); extern MODVAR TKL *(*tkl_add_nameban)(int type, const char *name, int hold, const char *reason, const char *setby, time_t expire_at, time_t set_at, int flags); -extern MODVAR TKL *(*tkl_add_spamfilter)(int type, unsigned short target, BanAction *action, Match *match, const char *setby, - time_t expire_at, time_t set_at, - time_t spamf_tkl_duration, const char *spamf_tkl_reason, - int flags); +extern MODVAR TKL *(*tkl_add_spamfilter)(int type, unsigned short target, BanAction *action, + Match *match, const char *rule, const char *setby, + time_t expire_at, time_t set_at, + time_t spamf_tkl_duration, const char *spamf_tkl_reason, + int flags); extern MODVAR TKL *(*find_tkl_serverban)(int type, const char *usermask, const char *hostmask, int softban); extern MODVAR TKL *(*find_tkl_banexception)(int type, const char *usermask, const char *hostmask, int softban); extern MODVAR TKL *(*find_tkl_nameban)(int type, const char *name, int hold); diff --git a/include/struct.h b/include/struct.h index d5ed9f0f8..0aeb76c10 100644 --- a/include/struct.h +++ b/include/struct.h @@ -855,6 +855,7 @@ struct LoopStruct { typedef enum { MATCH_SIMPLE=1, /**< Simple pattern with * and ? */ MATCH_PCRE_REGEX=2, /**< PCRE2 Perl-like regex (new) */ + MATCH_NONE=3, /**< No matching at all (rule-based) */ } MatchType; /** Match struct, which allows various matching styles, see MATCH_* */ @@ -1063,6 +1064,37 @@ struct Secret { SecretCache *cache; }; +/* CRULE stuff */ + +#define CRULE_ALL 0 +#define CRULE_AUTO 1 + +/* some constants and shared data types */ +#define CR_MAXARGLEN 80 /**< Maximum arg length (must be > HOSTLEN) */ +#define CR_MAXARGS 3 /**< Maximum number of args for a rule */ + +/* context when running a crule */ +typedef struct crule_context crule_context; +struct crule_context +{ + Client *client; +}; + +/** Evaluation function for a connection rule. */ +typedef int (*crule_funcptr) (crule_context *context, int, void **); + +/** CRULE - Node in a connection rule tree. */ +struct CRuleNode { + crule_funcptr funcptr; /**< Evaluation function for this node. */ + int numargs; /**< Number of arguments. */ + void *arg[CR_MAXARGS]; /**< Array of arguments. For operators, each arg + is a tree element; for functions, each arg is + a string. */ + int func_test_type; /* for >, < and == */ + int func_test_value; /* integer value to compare against */ +}; +typedef struct CRuleNode CRuleNode; +typedef struct CRuleNode* CRuleNodePtr; /* tkl: * TKL_KILL|TKL_GLOBAL = Global K-Line (GLINE) @@ -1176,6 +1208,8 @@ struct Spamfilter { unsigned short target; BanAction *action; /**< Ban action */ Match *match; /**< Spamfilter matcher */ + CRuleNode *rule; /**< parsed crule */ + char *prettyrule; /**< human printable version */ char *tkl_reason; /**< Reason to use for bans placed by this spamfilter, escaped by unreal_encodespace(). */ time_t tkl_duration; /**< Duration of bans placed by this spamfilter */ }; @@ -1529,32 +1563,6 @@ struct AuthConfig { #define crypt DES_crypt #endif -/* CRULE stuff */ - -#define CRULE_ALL 0 -#define CRULE_AUTO 1 - -/* some constants and shared data types */ -#define CR_MAXARGLEN 80 /**< Maximum arg length (must be > HOSTLEN) */ -#define CR_MAXARGS 3 /**< Maximum number of args for a rule */ - -/** Evaluation function for a connection rule. */ -typedef int (*crule_funcptr) (int, void **); - -/** CRULE - Node in a connection rule tree. */ -struct CRuleNode { - crule_funcptr funcptr; /**< Evaluation function for this node. */ - int numargs; /**< Number of arguments. */ - void *arg[CR_MAXARGS]; /**< Array of arguments. For operators, each arg - is a tree element; for functions, each arg is - a string. */ - int func_test_type; /* for >, < and == */ - int func_test_value; /* integer value to compare against */ -}; -typedef struct CRuleNode CRuleNode; -typedef struct CRuleNode* CRuleNodePtr; - - /* * conf2 stuff -stskeeps */ diff --git a/src/api-efunctions.c b/src/api-efunctions.c index cee0a698f..d15535d2b 100644 --- a/src/api-efunctions.c +++ b/src/api-efunctions.c @@ -55,10 +55,11 @@ TKL *(*tkl_add_serverban)(int type, const char *usermask, const char *hostmask, time_t expire_at, time_t set_at, int soft, int flags); TKL *(*tkl_add_nameban)(int type, const char *name, int hold, const char *reason, const char *setby, time_t expire_at, time_t set_at, int flags); -TKL *(*tkl_add_spamfilter)(int type, unsigned short target, BanAction *action, Match *match, const char *setby, - time_t expire_at, time_t set_at, - time_t spamf_tkl_duration, const char *spamf_tkl_reason, - int flags); +TKL *(*tkl_add_spamfilter)(int type, unsigned short target, BanAction *action, + Match *match, const char *rule, const char *setby, + time_t expire_at, time_t set_at, + time_t spamf_tkl_duration, const char *spamf_tkl_reason, + int flags); TKL *(*tkl_add_banexception)(int type, const char *usermask, const char *hostmask, SecurityGroup *match, const char *reason, const char *set_by, time_t expire_at, time_t set_at, int soft, const char *bantypes, int flags); diff --git a/src/crule.c b/src/crule.c index ea379a8b0..dd262c73f 100644 --- a/src/crule.c +++ b/src/crule.c @@ -132,12 +132,15 @@ enum crule_errcode { */ /* rule function prototypes - local! */ -static int crule_connected(int, void **); -static int crule_directcon(int, void **); -static int crule_via(int, void **); -static int crule_directop(int, void **); -static int crule__andor(int, void **); -static int crule__not(int, void **); +static int crule_connected(crule_context *, int, void **); +static int crule_directcon(crule_context *, int, void **); +static int crule_via(crule_context *, int, void **); +static int crule_directop(crule_context *, int, void **); +static int crule__andor(crule_context *, int, void **); +static int crule__not(crule_context *, int, void **); +// newstyle +static int crule_online_time(crule_context *, int, void **); +static int crule_reputation(crule_context *, int, void **); /* parsing function prototypes - local! */ static int crule_gettoken(crule_token *next_tokp, const char** str); @@ -185,13 +188,15 @@ struct crule_funclistent { struct crule_funclistent crule_funclist[] = { /* maximum function name length is 14 chars */ {"connected", 1, crule_connected}, + {"online_time", 0, crule_online_time}, + {"reputation", 0, crule_reputation}, {"directcon", 1, crule_directcon}, {"via", 2, crule_via}, {"directop", 0, crule_directop}, {"", 0, NULL} /* this must be here to mark end of list */ }; -static int crule_connected(int numargs, void *crulearg[]) +static int crule_connected(crule_context *context, int numargs, void *crulearg[]) { #if !defined(CR_DEBUG) && !defined(CR_CHKCONF) Client *client; @@ -208,7 +213,7 @@ static int crule_connected(int numargs, void *crulearg[]) #endif } -static int crule_directcon(int numargs, void *crulearg[]) +static int crule_directcon(crule_context *context, int numargs, void *crulearg[]) { #if !defined(CR_DEBUG) && !defined(CR_CHKCONF) Client *client; @@ -227,7 +232,7 @@ static int crule_directcon(int numargs, void *crulearg[]) #endif } -static int crule_via(int numargs, void *crulearg[]) +static int crule_via(crule_context *context, int numargs, void *crulearg[]) { #if !defined(CR_DEBUG) && !defined(CR_CHKCONF) Client *client; @@ -246,7 +251,7 @@ static int crule_via(int numargs, void *crulearg[]) #endif } -static int crule_directop(int numargs, void *crulearg[]) +static int crule_directop(crule_context *context, int numargs, void *crulearg[]) { #if !defined(CR_DEBUG) && !defined(CR_CHKCONF) Client *client; @@ -264,13 +269,27 @@ static int crule_directop(int numargs, void *crulearg[]) #endif } +static int crule_online_time(crule_context *context, int numargs, void *crulearg[]) +{ + if (context && context->client) + return get_connected_time(context->client); + return 0; +} + +static int crule_reputation(crule_context *context, int numargs, void *crulearg[]) +{ + if (context && context->client) + return GetReputation(context->client); + return 0; +} + /** Evaluate a connection rule. * @param[in] rule Rule to evalute. * @return Non-zero if the rule allows the connection, zero otherwise. */ -int crule_eval(struct CRuleNode* rule) +int crule_eval(crule_context *context, struct CRuleNode* rule) { - int ret = rule->funcptr(rule->numargs, rule->arg); + int ret = rule->funcptr(context, rule->numargs, rule->arg); switch (rule->func_test_type) { case CR_EQUAL: @@ -300,15 +319,15 @@ int crule_eval(struct CRuleNode* rule) * @param[in] crulearg Argument array. * @return Non-zero if the condition is true, zero if not. */ -static int crule__andor(int numargs, void *crulearg[]) +static int crule__andor(crule_context *context, int numargs, void *crulearg[]) { int result1; - result1 = crule_eval(crulearg[0]); + result1 = crule_eval(context, crulearg[0]); if (crulearg[2]) /* or */ - return (result1 || crule_eval(crulearg[1])); + return (result1 || crule_eval(context, crulearg[1])); else - return (result1 && crule_eval(crulearg[1])); + return (result1 && crule_eval(context, crulearg[1])); } /** Logically invert the result of crulearg[0]. @@ -316,9 +335,9 @@ static int crule__andor(int numargs, void *crulearg[]) * @param[in] crulearg Argument array. * @return Non-zero if the condition is true, zero if not. */ -static int crule__not(int numargs, void *crulearg[]) +static int crule__not(crule_context *context, int numargs, void *crulearg[]) { - return (!crule_eval(crulearg[0])); + return (!crule_eval(context, crulearg[0])); } /** Scan an input token from \a ruleptr. @@ -388,7 +407,7 @@ static int crule_gettoken(crule_token *next_tokp, const char** ruleptr) break; default: if ((isalnum(*(--(*ruleptr)))) || (**ruleptr == '*') || - (**ruleptr == '?') || (**ruleptr == '.') || (**ruleptr == '-')) + (**ruleptr == '?') || (**ruleptr == '.') || (**ruleptr == '-') || (**ruleptr == '_')) *next_tokp = CR_WORD; else return (CR_UNKNWTOK); @@ -411,7 +430,8 @@ static void crule_getword(char* word, int* wordlenp, size_t maxlen, const char** while ((size_t)(word_ptr - word) < maxlen && (isalnum(**ruleptr) || **ruleptr == '*' || **ruleptr == '?' - || **ruleptr == '.' || **ruleptr == '-')) + || **ruleptr == '.' || **ruleptr == '-' + || **ruleptr == '_')) *word_ptr++ = *(*ruleptr)++; *word_ptr = '\0'; *wordlenp = word_ptr - word; diff --git a/src/json.c b/src/json.c index 79e9b307c..97d6bbf4a 100644 --- a/src/json.c +++ b/src/json.c @@ -569,8 +569,13 @@ void json_expand_tkl(json_t *root, const char *key, TKL *tkl, int detail) } else if (TKLIsSpamfilter(tkl)) { - json_object_set_new(j, "name", json_string_unreal(tkl->ptr.spamfilter->match->str)); - json_object_set_new(j, "match_type", json_string_unreal(unreal_match_method_valtostr(tkl->ptr.spamfilter->match->type))); + if (tkl->ptr.spamfilter->match->str) + { + json_object_set_new(j, "name", json_string_unreal(tkl->ptr.spamfilter->match->str)); + json_object_set_new(j, "match_type", json_string_unreal(unreal_match_method_valtostr(tkl->ptr.spamfilter->match->type))); + } + if (tkl->ptr.spamfilter->prettyrule) + json_object_set_new(j, "rule", json_string_unreal(tkl->ptr.spamfilter->prettyrule)); json_object_set_new(j, "ban_action", json_string_unreal(ban_actions_to_string(tkl->ptr.spamfilter->action))); json_object_set_new(j, "ban_duration", json_integer(tkl->ptr.spamfilter->tkl_duration)); json_object_set_new(j, "ban_duration_string", json_string_unreal(pretty_time_val_r(buf, sizeof(buf), tkl->ptr.spamfilter->tkl_duration))); diff --git a/src/match.c b/src/match.c index 7f54ee13f..6fcb7fde6 100644 --- a/src/match.c +++ b/src/match.c @@ -428,6 +428,9 @@ Match *unreal_create_match(MatchType type, const char *str, char **error) } pcre2_jit_compile(m->ext.pcre2_expr, PCRE2_JIT_COMPLETE); return m; + } else if (m->type == MATCH_NONE) + { + /* Nothing to do */ } else { /* Unknown type, how did that happen ? */ @@ -463,6 +466,9 @@ int unreal_match(Match *m, const char *str) return 0; /* NO MATCH */ } + if (m->type == MATCH_NONE) + return 1; + return 0; } @@ -472,6 +478,8 @@ int unreal_match_method_strtoval(const char *str) return MATCH_PCRE_REGEX; if (!strcmp(str, "simple") || !strcmp(str, "glob")) return MATCH_SIMPLE; + if (!strcmp(str, "none")) + return MATCH_NONE; return 0; } @@ -481,7 +489,9 @@ char *unreal_match_method_valtostr(int val) return "regex"; if (val == MATCH_SIMPLE) return "simple"; - + if (val == MATCH_NONE) + return "none"; + return "unknown"; } diff --git a/src/misc.c b/src/misc.c index ca5609d01..0c82ed319 100644 --- a/src/misc.c +++ b/src/misc.c @@ -971,7 +971,7 @@ char *spamfilter_target_inttostring(int v) if (v & e->value) *p++ = e->character; *p = '\0'; - return buf; + return *buf ? buf : "-"; } /** Replace underscores back to the space character. diff --git a/src/modules/rpc/spamfilter.c b/src/modules/rpc/spamfilter.c index 132827c15..b78e49012 100644 --- a/src/modules/rpc/spamfilter.c +++ b/src/modules/rpc/spamfilter.c @@ -247,7 +247,7 @@ RPC_CALL_FUNC(rpc_spamfilter_add) return; } - tkl = tkl_add_spamfilter(type, targets, banact_value_to_struct(action), m, set_by, 0, TStime(), + tkl = tkl_add_spamfilter(type, targets, banact_value_to_struct(action), m, NULL, set_by, 0, TStime(), ban_duration, reason, 0); if (!tkl) diff --git a/src/modules/server.c b/src/modules/server.c index 95edcc6f4..8230ee19c 100644 --- a/src/modules/server.c +++ b/src/modules/server.c @@ -2204,13 +2204,14 @@ int _is_services_but_not_ulined(Client *client) const char *_check_deny_link(ConfigItem_link *link, int auto_connect) { ConfigItem_deny_link *d; + crule_context context; for (d = conf_deny_link; d; d = d->next) { if ((auto_connect == 0) && (d->flag.type == CRULE_AUTO)) continue; if (unreal_mask_match_string(link->servername, d->mask) && - crule_eval(d->rule)) + crule_eval(NULL, d->rule)) { return d->reason; } diff --git a/src/modules/tkl.c b/src/modules/tkl.c index 68e4c9b5d..f942b8c88 100644 --- a/src/modules/tkl.c +++ b/src/modules/tkl.c @@ -68,10 +68,11 @@ TKL *_tkl_add_banexception(int type, char *usermask, char *hostmask, SecurityGro time_t expire_at, time_t set_at, int soft, char *bantypes, int flags); TKL *_tkl_add_nameban(int type, char *name, int hold, char *reason, char *set_by, time_t expire_at, time_t set_at, int flags); -TKL *_tkl_add_spamfilter(int type, unsigned short target, BanAction *action, Match *match, char *set_by, - time_t expire_at, time_t set_at, - time_t spamf_tkl_duration, char *spamf_tkl_reason, - int flags); +TKL *_tkl_add_spamfilter(int type, unsigned short target, BanAction *action, Match *match, + const char *rule, const char *set_by, + time_t expire_at, time_t set_at, + time_t spamf_tkl_duration, const char *spamf_tkl_reason, + int flags); void _sendnotice_tkl_del(char *removed_by, TKL *tkl); void _sendnotice_tkl_add(TKL *tkl); void _free_tkl(TKL *tkl); @@ -269,7 +270,7 @@ int tkl_config_test_spamfilter(ConfigFile *cf, ConfigEntry *ce, int type, int *e ConfigEntry *cep, *cepp; int errors = 0; char *match = NULL, *reason = NULL; - char has_target = 0, has_match = 0, has_action = 0, has_reason = 0, has_bantime = 0, has_match_type = 0; + char has_target = 0, has_match = 0, has_rule = 0, has_action = 0, has_reason = 0, has_bantime = 0, has_match_type = 0; int match_type = 0; /* We are only interested in spamfilter { } blocks */ @@ -357,6 +358,25 @@ int tkl_config_test_spamfilter(ConfigFile *cf, ConfigEntry *ce, int type, int *e has_match = 1; match = cep->value; } + else if (!strcmp(cep->name, "rule")) + { + int val; + if (has_rule) + { + config_warn_duplicate(cep->file->filename, + cep->line_number, "spamfilter::rule"); + continue; + } + has_rule = 1; + if ((val = crule_test(cep->value))) + { + config_error("%s:%i: spamfilter::rule contains an invalid expression: %s", + cep->file->filename, + cep->line_number, + crule_errstring(val)); + errors++; + } + } else if (!strcmp(cep->name, "ban-time")) { if (has_bantime) @@ -425,13 +445,19 @@ int tkl_config_test_spamfilter(ConfigFile *cf, ConfigEntry *ce, int type, int *e } } - if (!has_match) + if (!has_match && !has_rule) { config_error_missing(ce->file->filename, ce->line_number, - "spamfilter::match"); + "spamfilter::match or spamfilter::rule"); errors++; } - if (!has_target) + if (!has_match && has_match_type && has_rule) + { + config_error("%s:%i: spamfilter::match-type makes no sense if you only have a spamfilter::rule. Remove the match-type.", + ce->file->filename, ce->line_number); + errors++; + } + if (!has_target && match) { config_error_missing(ce->file->filename, ce->line_number, "spamfilter::target"); @@ -450,7 +476,7 @@ int tkl_config_test_spamfilter(ConfigFile *cf, ConfigEntry *ce, int type, int *e ce->file->filename, ce->line_number); errors++; } - if (!has_match_type) + if (!has_match_type && match) { config_error_missing(ce->file->filename, ce->line_number, "spamfilter::match-type"); @@ -474,13 +500,14 @@ int tkl_config_run_spamfilter(ConfigFile *cf, ConfigEntry *ce, int type) { ConfigEntry *cep; ConfigEntry *cepp; - char *word = NULL; + char *match = NULL; + char *rule = NULL; time_t bantime = tempiConf.spamfilter_ban_time; char *banreason = tempiConf.spamfilter_ban_reason; BanAction *action; int target = 0; int match_type = 0; - Match *m; + Match *m = NULL; /* We are only interested in spamfilter { } blocks */ if ((type != CONFIG_MAIN) || strcmp(ce->name, "spamfilter")) @@ -490,7 +517,11 @@ int tkl_config_run_spamfilter(ConfigFile *cf, ConfigEntry *ce, int type) { if (!strcmp(cep->name, "match")) { - word = cep->value; + match = cep->value; + } + else if (!strcmp(cep->name, "rule")) + { + rule = cep->value; } else if (!strcmp(cep->name, "target")) { @@ -520,11 +551,16 @@ int tkl_config_run_spamfilter(ConfigFile *cf, ConfigEntry *ce, int type) } } - m = unreal_create_match(match_type, word, NULL); + if (!match && rule) + match_type = MATCH_NONE; + + if (match) + m = unreal_create_match(match_type, match, NULL); tkl_add_spamfilter(TKL_SPAMF, target, action, m, + rule, "-config-", 0, TStime(), @@ -2585,9 +2621,10 @@ TKL *tkl_find_head(char type, char *hostmask, TKL *def) * @returns The TKL entry, or NULL in case of a problem, * such as a regex failing to compile, memory problem, .. */ -TKL *_tkl_add_spamfilter(int type, unsigned short target, BanAction *action, Match *match, char *set_by, +TKL *_tkl_add_spamfilter(int type, unsigned short target, BanAction *action, + Match *match, const char *rule, const char *set_by, time_t expire_at, time_t set_at, - time_t tkl_duration, char *tkl_reason, + time_t tkl_duration, const char *tkl_reason, int flags) { TKL *tkl; @@ -2597,14 +2634,26 @@ TKL *_tkl_add_spamfilter(int type, unsigned short target, BanAction *action, Mat abort(); tkl = safe_alloc(sizeof(TKL)); - /* First the common fields */ tkl->type = type; tkl->flags = flags; tkl->set_at = set_at; safe_strdup(tkl->set_by, set_by); tkl->expire_at = expire_at; - /* Then the spamfilter fields */ tkl->ptr.spamfilter = safe_alloc(sizeof(Spamfilter)); + if (rule) + { + tkl->ptr.spamfilter->rule = crule_parse(rule); + safe_strdup(tkl->ptr.spamfilter->prettyrule, rule); + } + if (rule && !match) + { + /* When no spamfilter::match, name it $RULE:xxx */ + char buf[512]; + snprintf(buf, sizeof(buf), "$RULE:%s", rule); + match = safe_alloc(sizeof(Match)); + match->type = MATCH_NONE; + safe_strdup(match->str, buf); + } tkl->ptr.spamfilter->target = target; tkl->ptr.spamfilter->action = action; tkl->ptr.spamfilter->match = match; @@ -2826,6 +2875,9 @@ void _free_tkl(TKL *tkl) safe_free(tkl->ptr.spamfilter->tkl_reason); if (tkl->ptr.spamfilter->match) unreal_delete_match(tkl->ptr.spamfilter->match); + if (tkl->ptr.spamfilter->rule) + crule_free(&tkl->ptr.spamfilter->rule); + safe_free(tkl->ptr.spamfilter->prettyrule); safe_free(tkl->ptr.spamfilter); } else if (TKLIsBanException(tkl) && tkl->ptr.banexception) @@ -4344,7 +4396,7 @@ CMD_FUNC(cmd_tkl_add) log_data_string("spamfilter_regex_error", err)); return; } - tkl = tkl_add_spamfilter(type, target, banact_value_to_struct(action), m, set_by, expire_at, set_at, + tkl = tkl_add_spamfilter(type, target, banact_value_to_struct(action), m, NULL, set_by, expire_at, set_at, tkl_duration, tkl_reason, 0); } } else @@ -4931,40 +4983,58 @@ int _match_spamfilter(Client *client, const char *str_in, int target, const char if (IsLoggedIn(client) && only_soft_actions(tkl->ptr.spamfilter->action)) continue; -#ifdef SPAMFILTER_DETECTSLOW - memset(&rnow, 0, sizeof(rnow)); - memset(&rprev, 0, sizeof(rnow)); - - getrusage(RUSAGE_SELF, &rprev); -#endif - - ret = unreal_match(tkl->ptr.spamfilter->match, str); - -#ifdef SPAMFILTER_DETECTSLOW - getrusage(RUSAGE_SELF, &rnow); - - ms_past = ((rnow.ru_utime.tv_sec - rprev.ru_utime.tv_sec) * 1000) + - ((rnow.ru_utime.tv_usec - rprev.ru_utime.tv_usec) / 1000); - - if ((SPAMFILTER_DETECTSLOW_FATAL > 0) && (ms_past > SPAMFILTER_DETECTSLOW_FATAL)) + /* Run any pre 'rule' if there is any */ + if (tkl->ptr.spamfilter->rule) { - unreal_log(ULOG_ERROR, "tkl", "SPAMFILTER_SLOW_FATAL", NULL, - "[Spamfilter] WARNING: Too slow spamfilter detected (took $msec_time msec to execute) " - "-- spamfilter will be \002REMOVED!\002: $tkl", - log_data_tkl("tkl", tkl), - log_data_integer("msec_time", ms_past)); - tkl_del_line(tkl); - return 0; /* Act as if it didn't match, even if it did.. it's gone now anyway.. */ - } else - if ((SPAMFILTER_DETECTSLOW_WARN > 0) && (ms_past > SPAMFILTER_DETECTSLOW_WARN)) - { - unreal_log(ULOG_WARNING, "tkl", "SPAMFILTER_SLOW_WARN", NULL, - "[Spamfilter] WARNING: Slow spamfilter detected (took $msec_time msec to execute): $tkl", - log_data_tkl("tkl", tkl), - log_data_integer("msec_time", ms_past)); + crule_context context; + memset(&context, 0, sizeof(context)); + context.client = client; + if (!crule_eval(&context, tkl->ptr.spamfilter->rule)) + continue; } + + if (tkl->ptr.spamfilter->match && (tkl->ptr.spamfilter->match->type != MATCH_NONE)) + { + // TODO: wait, why are we running slow spamfilter detection for simple (non-regex) too ? +#ifdef SPAMFILTER_DETECTSLOW + memset(&rnow, 0, sizeof(rnow)); + memset(&rprev, 0, sizeof(rnow)); + + getrusage(RUSAGE_SELF, &rprev); #endif + ret = unreal_match(tkl->ptr.spamfilter->match, str); + +#ifdef SPAMFILTER_DETECTSLOW + getrusage(RUSAGE_SELF, &rnow); + + ms_past = ((rnow.ru_utime.tv_sec - rprev.ru_utime.tv_sec) * 1000) + + ((rnow.ru_utime.tv_usec - rprev.ru_utime.tv_usec) / 1000); + + if ((SPAMFILTER_DETECTSLOW_FATAL > 0) && (ms_past > SPAMFILTER_DETECTSLOW_FATAL)) + { + unreal_log(ULOG_ERROR, "tkl", "SPAMFILTER_SLOW_FATAL", NULL, + "[Spamfilter] WARNING: Too slow spamfilter detected (took $msec_time msec to execute) " + "-- spamfilter will be \002REMOVED!\002: $tkl", + log_data_tkl("tkl", tkl), + log_data_integer("msec_time", ms_past)); + tkl_del_line(tkl); + return 0; /* Act as if it didn't match, even if it did.. it's gone now anyway.. */ + } else + if ((SPAMFILTER_DETECTSLOW_WARN > 0) && (ms_past > SPAMFILTER_DETECTSLOW_WARN)) + { + unreal_log(ULOG_WARNING, "tkl", "SPAMFILTER_SLOW_WARN", NULL, + "[Spamfilter] WARNING: Slow spamfilter detected (took $msec_time msec to execute): $tkl", + log_data_tkl("tkl", tkl), + log_data_integer("msec_time", ms_past)); + } +#endif + } else { + /* There is no ::match but there was a ::rule, and that is enough for a match.. */ + if (tkl->ptr.spamfilter->rule) + ret = 1; + } + if (ret) { /* We have a match! But.. perhaps it's on the exceptions list? */ diff --git a/src/modules/tkldb.c b/src/modules/tkldb.c index 14155b22b..0cd9b7254 100644 --- a/src/modules/tkldb.c +++ b/src/modules/tkldb.c @@ -723,6 +723,7 @@ int read_tkldb(void) tkl_add_spamfilter(tkl->type, tkl->ptr.spamfilter->target, tkl->ptr.spamfilter->action, tkl->ptr.spamfilter->match, + NULL, tkl->set_by, tkl->expire_at, tkl->set_at, tkl->ptr.spamfilter->tkl_duration, tkl->ptr.spamfilter->tkl_reason,