From 7a09c641f8caaefa697013540166f0a7f3857321 Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Wed, 20 Oct 2010 22:26:34 +0400 Subject: [PATCH] * Add ability to recursive scan over multipart's parts for functions: - content_type_is_type - content_type_is_subtype - content_type_compare_param - content_type_has_param * Add ability to specify any of actions in user's settings, not only reject. --- src/cfg_xml.c | 26 +--- src/expressions.c | 327 ++++++++++++++++++++++++++++++++++------------ src/filter.c | 74 +++++++++++ src/filter.h | 4 + src/protocol.c | 59 +-------- src/protocol.h | 3 - src/settings.c | 93 ++++++++++++- src/settings.h | 2 + 8 files changed, 416 insertions(+), 172 deletions(-) diff --git a/src/cfg_xml.c b/src/cfg_xml.c index 5e0d69a4c..2e762ba1e 100644 --- a/src/cfg_xml.c +++ b/src/cfg_xml.c @@ -690,28 +690,6 @@ worker_handle_bind (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GH return TRUE; } -static inline gboolean -check_action (const gchar *data, gint *result) -{ - if (g_ascii_strncasecmp (data, "reject", sizeof ("reject") - 1) == 0) { - *result = METRIC_ACTION_REJECT; - } - else if (g_ascii_strncasecmp (data, "greylist", sizeof ("greylist") - 1) == 0) { - *result = METRIC_ACTION_GREYLIST; - } - else if (g_ascii_strncasecmp (data, "add_header", sizeof ("add_header") - 1) == 0) { - *result = METRIC_ACTION_ADD_HEADER; - } - else if (g_ascii_strncasecmp (data, "rewrite_subject", sizeof ("rewrite_subject") - 1) == 0) { - *result = METRIC_ACTION_REWRITE_SUBJECT; - } - else { - msg_err ("unknown action for metric: %s", data); - return FALSE; - } - return TRUE; -} - gboolean handle_metric_action (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, gint offset) { @@ -722,14 +700,14 @@ handle_metric_action (struct config_file *cfg, struct rspamd_xml_userdata *ctx, /* First of all check whether we have data with weight (reject:50 for example) */ if ((p = strchr (data, ':')) == NULL) { - if (check_action (data, &res)) { + if (check_action_str (data, &res)) { metric->action = res; return TRUE; } return FALSE; } else { - if (!check_action (data, &res)) { + if (!check_action_str (data, &res)) { return FALSE; } else { diff --git a/src/expressions.c b/src/expressions.c index 9d17a146c..c4fa738b5 100644 --- a/src/expressions.c +++ b/src/expressions.c @@ -1003,10 +1003,13 @@ rspamd_content_type_compare_param (struct worker_task * task, GList * args, void gchar *param_name, *param_pattern; const gchar *param_data; struct rspamd_regexp *re; - struct expression_argument *arg; + struct expression_argument *arg, *arg1; GMimeObject *part; - const GMimeContentType *ct; + GMimeContentType *ct; gint r; + gboolean recursive = FALSE, result = FALSE; + GList *cur; + struct mime_part *cur_part; if (args == NULL) { msg_warn ("no parameters to function"); @@ -1022,45 +1025,83 @@ rspamd_content_type_compare_param (struct worker_task * task, GList * args, void arg = get_function_arg (args->data, task, TRUE); param_pattern = arg->data; + part = g_mime_message_get_mime_part (task->message); if (part) { - ct = g_mime_object_get_content_type (part); + ct = (GMimeContentType *)g_mime_object_get_content_type (part); + if (args->next) { + args = g_list_next (args); + arg1 = get_function_arg (args->data, task, TRUE); + if (g_ascii_strncasecmp (arg1->data, "true", sizeof ("true") - 1) == 0) { + recursive = TRUE; + } + } + else { + /* + * If user did not specify argument, let's assume that he wants + * recursive search if mime part is multipart/mixed + */ + if (g_mime_content_type_is_type (ct, "multipart", "*")) { + recursive = TRUE; + } + } + + if (recursive) { + cur = task->parts; + } + #ifndef GMIME24 g_object_unref (part); #endif + for (;;) { + if ((param_data = g_mime_content_type_get_parameter ((GMimeContentType *)ct, param_name)) == NULL) { + result = FALSE; + } + if (*param_pattern == '/') { + /* This is regexp, so compile and create g_regexp object */ + if ((re = re_cache_check (param_pattern, task->cfg->cfg_pool)) == NULL) { + re = parse_regexp (task->cfg->cfg_pool, param_pattern, task->cfg->raw_mode); + if (re == NULL) { + msg_warn ("cannot compile regexp for function"); + return FALSE; + } + re_cache_add (param_pattern, re, task->cfg->cfg_pool); + } + if ((r = task_cache_check (task, re)) == -1) { + if (g_regex_match (re->regexp, param_data, 0, NULL) == TRUE) { + task_cache_add (task, re, 1); + return TRUE; + } + task_cache_add (task, re, 0); + } + else { - if ((param_data = g_mime_content_type_get_parameter ((GMimeContentType *)ct, param_name)) == NULL) { - return FALSE; - } - if (*param_pattern == '/') { - /* This is regexp, so compile and create g_regexp object */ - if ((re = re_cache_check (param_pattern, task->cfg->cfg_pool)) == NULL) { - re = parse_regexp (task->cfg->cfg_pool, param_pattern, task->cfg->raw_mode); - if (re == NULL) { - msg_warn ("cannot compile regexp for function"); - return FALSE; } - re_cache_add (param_pattern, re, task->cfg->cfg_pool); } - if ((r = task_cache_check (task, re)) == -1) { - if (g_regex_match (re->regexp, param_data, 0, NULL) == TRUE) { - task_cache_add (task, re, 1); + else { + /* Just do strcasecmp */ + if (g_ascii_strcasecmp (param_data, param_pattern) == 0) { return TRUE; } - task_cache_add (task, re, 0); } - else { - return r == 1; + /* Get next part */ + if (! recursive) { + return result; } - } - else { - /* Just do strcasecmp */ - if (g_ascii_strcasecmp (param_data, param_pattern) == 0) { - return TRUE; + else if (cur != NULL) { + cur_part = cur->data; + if (cur_part->type != NULL) { + ct = cur_part->type; + } + cur = g_list_next (cur); + } + else { + /* All is done */ + return result; } } - } + } return FALSE; } @@ -1070,9 +1111,12 @@ rspamd_content_type_has_param (struct worker_task * task, GList * args, void *un { gchar *param_name; const gchar *param_data; - struct expression_argument *arg; + struct expression_argument *arg, *arg1; GMimeObject *part; - const GMimeContentType *ct; + GMimeContentType *ct; + gboolean recursive = FALSE, result = FALSE; + GList *cur; + struct mime_part *cur_part; if (args == NULL) { msg_warn ("no parameters to function"); @@ -1080,18 +1124,55 @@ rspamd_content_type_has_param (struct worker_task * task, GList * args, void *un } arg = get_function_arg (args->data, task, TRUE); param_name = arg->data; + part = g_mime_message_get_mime_part (task->message); if (part) { - ct = g_mime_object_get_content_type (part); + ct = (GMimeContentType *)g_mime_object_get_content_type (part); + if (args->next) { + args = g_list_next (args); + arg1 = get_function_arg (args->data, task, TRUE); + if (g_ascii_strncasecmp (arg1->data, "true", sizeof ("true") - 1) == 0) { + recursive = TRUE; + } + } + else { + /* + * If user did not specify argument, let's assume that he wants + * recursive search if mime part is multipart/mixed + */ + if (g_mime_content_type_is_type (ct, "multipart", "*")) { + recursive = TRUE; + } + } + + if (recursive) { + cur = task->parts; + } + #ifndef GMIME24 g_object_unref (part); #endif - - debug_task ("checking %s param", param_name); - param_data = g_mime_content_type_get_parameter ((GMimeContentType *)ct, param_name); - if (param_data == NULL) { - return FALSE; + for (;;) { + if ((param_data = g_mime_content_type_get_parameter ((GMimeContentType *)ct, param_name)) != NULL) { + return TRUE; + } + /* Get next part */ + if (! recursive) { + return result; + } + else if (cur != NULL) { + cur_part = cur->data; + if (cur_part->type != NULL) { + ct = cur_part->type; + } + cur = g_list_next (cur); + } + else { + /* All is done */ + return result; + } } + } return TRUE; @@ -1100,58 +1181,95 @@ rspamd_content_type_has_param (struct worker_task * task, GList * args, void *un gboolean rspamd_content_type_is_subtype (struct worker_task *task, GList * args, void *unused) { - gchar *param_pattern; + gchar *param_pattern; struct rspamd_regexp *re; - struct expression_argument *arg; + struct expression_argument *arg, *arg1; GMimeObject *part; GMimeContentType *ct; gint r; + gboolean recursive = FALSE, result = FALSE; + GList *cur; + struct mime_part *cur_part; if (args == NULL) { msg_warn ("no parameters to function"); return FALSE; } - arg = get_function_arg (args->data, task, TRUE); param_pattern = arg->data; + part = g_mime_message_get_mime_part (task->message); if (part) { ct = (GMimeContentType *)g_mime_object_get_content_type (part); + if (args->next) { + args = g_list_next (args); + arg1 = get_function_arg (args->data, task, TRUE); + if (g_ascii_strncasecmp (arg1->data, "true", sizeof ("true") - 1) == 0) { + recursive = TRUE; + } + } + else { + /* + * If user did not specify argument, let's assume that he wants + * recursive search if mime part is multipart/mixed + */ + if (g_mime_content_type_is_type (ct, "multipart", "*")) { + recursive = TRUE; + } + } + + if (recursive) { + cur = task->parts; + } + #ifndef GMIME24 g_object_unref (part); #endif + for (;;) { + if (*param_pattern == '/') { + /* This is regexp, so compile and create g_regexp object */ + if ((re = re_cache_check (param_pattern, task->cfg->cfg_pool)) == NULL) { + re = parse_regexp (task->cfg->cfg_pool, param_pattern, task->cfg->raw_mode); + if (re == NULL) { + msg_warn ("cannot compile regexp for function"); + return FALSE; + } + re_cache_add (param_pattern, re, task->cfg->cfg_pool); + } + if ((r = task_cache_check (task, re)) == -1) { + if (g_regex_match (re->regexp, ct->subtype, 0, NULL) == TRUE) { + task_cache_add (task, re, 1); + return TRUE; + } + task_cache_add (task, re, 0); + } + else { - if (ct == NULL ) { - return FALSE; - } - - if (*param_pattern == '/') { - /* This is regexp, so compile and create g_regexp object */ - if ((re = re_cache_check (param_pattern, task->cfg->cfg_pool)) == NULL) { - re = parse_regexp (task->cfg->cfg_pool, param_pattern, task->cfg->raw_mode); - if (re == NULL) { - msg_warn ("cannot compile regexp for function"); - return FALSE; } - re_cache_add (param_pattern, re, task->cfg->cfg_pool); } - if ((r = task_cache_check (task, re)) == -1) { - if (g_regex_match (re->regexp, ct->subtype, 0, NULL) == TRUE) { - task_cache_add (task, re, 1); + else { + /* Just do strcasecmp */ + if (g_ascii_strcasecmp (ct->subtype, param_pattern) == 0) { return TRUE; } - task_cache_add (task, re, 0); } - else { - return r == 1; + /* Get next part */ + if (! recursive) { + return result; } - } - else { - /* Just do strcasecmp */ - if (ct->subtype && g_ascii_strcasecmp (ct->subtype, param_pattern) == 0) { - return TRUE; + else if (cur != NULL) { + cur_part = cur->data; + if (cur_part->type != NULL) { + ct = cur_part->type; + } + cur = g_list_next (cur); + } + else { + /* All is done */ + return result; } } + } return FALSE; @@ -1160,59 +1278,96 @@ rspamd_content_type_is_subtype (struct worker_task *task, GList * args, void *un gboolean rspamd_content_type_is_type (struct worker_task * task, GList * args, void *unused) { - gchar *param_pattern; + gchar *param_pattern; struct rspamd_regexp *re; + struct expression_argument *arg, *arg1; GMimeObject *part; GMimeContentType *ct; - struct expression_argument *arg; gint r; + gboolean recursive = FALSE, result = FALSE; + GList *cur; + struct mime_part *cur_part; if (args == NULL) { msg_warn ("no parameters to function"); return FALSE; } - arg = get_function_arg (args->data, task, TRUE); param_pattern = arg->data; + part = g_mime_message_get_mime_part (task->message); if (part) { ct = (GMimeContentType *)g_mime_object_get_content_type (part); + if (args->next) { + args = g_list_next (args); + arg1 = get_function_arg (args->data, task, TRUE); + if (g_ascii_strncasecmp (arg1->data, "true", sizeof ("true") - 1) == 0) { + recursive = TRUE; + } + } + else { + /* + * If user did not specify argument, let's assume that he wants + * recursive search if mime part is multipart/mixed + */ + if (g_mime_content_type_is_type (ct, "multipart", "*")) { + recursive = TRUE; + } + } + + if (recursive) { + cur = task->parts; + } + #ifndef GMIME24 g_object_unref (part); #endif + for (;;) { + if (*param_pattern == '/') { + /* This is regexp, so compile and create g_regexp object */ + if ((re = re_cache_check (param_pattern, task->cfg->cfg_pool)) == NULL) { + re = parse_regexp (task->cfg->cfg_pool, param_pattern, task->cfg->raw_mode); + if (re == NULL) { + msg_warn ("cannot compile regexp for function"); + return FALSE; + } + re_cache_add (param_pattern, re, task->cfg->cfg_pool); + } + if ((r = task_cache_check (task, re)) == -1) { + if (g_regex_match (re->regexp, ct->type, 0, NULL) == TRUE) { + task_cache_add (task, re, 1); + return TRUE; + } + task_cache_add (task, re, 0); + } + else { - if (ct == NULL) { - return FALSE; - } - - if (*param_pattern == '/') { - /* This is regexp, so compile and create g_regexp object */ - if ((re = re_cache_check (param_pattern, task->cfg->cfg_pool)) == NULL) { - re = parse_regexp (task->cfg->cfg_pool, param_pattern, task->cfg->raw_mode); - if (re == NULL) { - msg_warn ("cannot compile regexp for function"); - return FALSE; } - re_cache_add (param_pattern, re, task->cfg->cfg_pool); } - if ((r = task_cache_check (task, re)) == -1) { - if (g_regex_match (re->regexp, ct->type, 0, NULL) == TRUE) { - task_cache_add (task, re, 1); + else { + /* Just do strcasecmp */ + if (g_ascii_strcasecmp (ct->type, param_pattern) == 0) { return TRUE; } - task_cache_add (task, re, 0); } - else { - return r == 1; + /* Get next part */ + if (! recursive) { + return result; } - } - else { - /* Just do strcasecmp */ - if (ct->type && g_ascii_strcasecmp (ct->type, param_pattern) == 0) { - return TRUE; + else if (cur != NULL) { + cur_part = cur->data; + if (cur_part->type != NULL) { + ct = cur_part->type; + } + cur = g_list_next (cur); + } + else { + /* All is done */ + return result; } } + } return FALSE; diff --git a/src/filter.c b/src/filter.c index 53beb762e..9cc6e542f 100644 --- a/src/filter.c +++ b/src/filter.c @@ -612,6 +612,80 @@ insert_headers (struct worker_task *task) g_hash_table_foreach (task->results, insert_metric_header, task); } +gboolean +check_action_str (const gchar *data, gint *result) +{ + if (g_ascii_strncasecmp (data, "reject", sizeof ("reject") - 1) == 0) { + *result = METRIC_ACTION_REJECT; + } + else if (g_ascii_strncasecmp (data, "greylist", sizeof ("greylist") - 1) == 0) { + *result = METRIC_ACTION_GREYLIST; + } + else if (g_ascii_strncasecmp (data, "add_header", sizeof ("add_header") - 1) == 0) { + *result = METRIC_ACTION_ADD_HEADER; + } + else if (g_ascii_strncasecmp (data, "rewrite_subject", sizeof ("rewrite_subject") - 1) == 0) { + *result = METRIC_ACTION_REWRITE_SUBJECT; + } + else { + msg_err ("unknown action for metric: %s", data); + return FALSE; + } + return TRUE; +} + +const gchar * +str_action_metric (enum rspamd_metric_action action) +{ + switch (action) { + case METRIC_ACTION_REJECT: + return "reject"; + case METRIC_ACTION_SOFT_REJECT: + return "soft reject"; + case METRIC_ACTION_REWRITE_SUBJECT: + return "rewrite subject"; + case METRIC_ACTION_ADD_HEADER: + return "add header"; + case METRIC_ACTION_GREYLIST: + return "greylist"; + case METRIC_ACTION_NOACTION: + return "no action"; + } + + return "unknown action"; +} + +gint +check_metric_action (double score, double required_score, struct metric *metric) +{ + GList *cur; + struct metric_action *action, *selected_action = NULL; + + if (score >= required_score) { + return metric->action; + } + else if (metric->actions == NULL) { + return METRIC_ACTION_NOACTION; + } + else { + cur = metric->actions; + while (cur) { + action = cur->data; + if (score >= action->score) { + selected_action = action; + } + cur = g_list_next (cur); + } + if (selected_action) { + return selected_action->action; + } + else { + return METRIC_ACTION_NOACTION; + } + } +} + + /* * vi:ts=4 */ diff --git a/src/filter.h b/src/filter.h index bc8d98de5..2540d95e9 100644 --- a/src/filter.h +++ b/src/filter.h @@ -111,4 +111,8 @@ void make_composites (struct worker_task *task); */ double factor_consolidation_func (struct worker_task *task, const gchar *metric_name, const gchar *unused); +gboolean check_action_str (const gchar *data, gint *result); +const gchar *str_action_metric (enum rspamd_metric_action action); +gint check_metric_action (double score, double required_score, struct metric *metric); + #endif diff --git a/src/protocol.c b/src/protocol.c index 6806c0ea3..6ef5e5d84 100644 --- a/src/protocol.c +++ b/src/protocol.c @@ -620,57 +620,6 @@ show_metric_symbols (struct metric_result *metric_res, struct metric_callback_da return TRUE; } -const gchar * -str_action_metric (enum rspamd_metric_action action) -{ - switch (action) { - case METRIC_ACTION_REJECT: - return "reject"; - case METRIC_ACTION_SOFT_REJECT: - return "soft reject"; - case METRIC_ACTION_REWRITE_SUBJECT: - return "rewrite subject"; - case METRIC_ACTION_ADD_HEADER: - return "add header"; - case METRIC_ACTION_GREYLIST: - return "greylist"; - case METRIC_ACTION_NOACTION: - return "no action"; - } - - return "unknown action"; -} - -gint -check_metric_action (double score, double required_score, struct metric *metric) -{ - GList *cur; - struct metric_action *action, *selected_action = NULL; - - if (score >= required_score) { - return metric->action; - } - else if (metric->actions == NULL) { - return METRIC_ACTION_NOACTION; - } - else { - cur = metric->actions; - while (cur) { - action = cur->data; - if (score >= action->score) { - selected_action = action; - } - cur = g_list_next (cur); - } - if (selected_action) { - return selected_action->action; - } - else { - return METRIC_ACTION_NOACTION; - } - } -} - static void show_metric_result (gpointer metric_name, gpointer metric_value, void *user_data) { @@ -682,7 +631,7 @@ show_metric_result (gpointer metric_name, gpointer metric_value, void *user_data struct metric *m; gint is_spam = 0; double ms = 0, rs = 0; - enum rspamd_metric_action action; + enum rspamd_metric_action action = METRIC_ACTION_NOACTION; if (! cd->alive) { return; @@ -730,10 +679,14 @@ show_metric_result (gpointer metric_name, gpointer metric_value, void *user_data ms = metric_res->metric->required_score; rs = metric_res->metric->reject_score; } + if (! check_metric_action_settings (task, metric_res->metric, metric_res->score, &action)) { + action = check_metric_action (metric_res->score, ms, metric_res->metric); + } + if (metric_res->score >= ms) { is_spam = 1; } - action = check_metric_action (metric_res->score, ms, metric_res->metric); + if (task->proto == SPAMC_PROTO) { r = rspamd_snprintf (outbuf, sizeof (outbuf), "Spam: %s ; %.2f / %.2f" CRLF, (is_spam) ? "True" : "False", metric_res->score, ms); } diff --git a/src/protocol.h b/src/protocol.h index e40f6db85..05e25c45a 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -77,7 +77,4 @@ gboolean write_reply (struct worker_task *task) G_GNUC_WARN_UNUSED_RESULT; */ void register_protocol_command (const gchar *name, protocol_reply_func func); -const gchar *str_action_metric (enum rspamd_metric_action action); -gint check_metric_action (double score, double required_score, struct metric *metric); - #endif diff --git a/src/settings.c b/src/settings.c index e2998984d..edfc5a21d 100644 --- a/src/settings.c +++ b/src/settings.c @@ -27,6 +27,7 @@ #include "map.h" #include "main.h" #include "settings.h" +#include "filter.h" #include "json/jansson.h" struct json_buf { @@ -36,6 +37,19 @@ struct json_buf { size_t buflen; }; +static void +settings_actions_free (gpointer data) +{ + GList *cur = data; + + while (cur) { + g_free (cur->data); + cur = g_list_next (cur); + } + + g_list_free ((GList *)data); +} + static void settings_free (gpointer data) { @@ -104,10 +118,12 @@ void json_fin_cb (memory_pool_t * pool, struct map_cb_data *data) { struct json_buf *jb; - gint nelts, i, n, a; - json_t *js, *cur_elt, *cur_nm, *it_val; + gint nelts, i, n, j; + json_t *js, *cur_elt, *cur_nm, *it_val, *act_it, *act_value; json_error_t je; + struct metric_action *new_act; struct rspamd_settings *cur_settings; + GList *cur_act; gchar *cur_name; void *json_it; double *score; @@ -156,6 +172,7 @@ json_fin_cb (memory_pool_t * pool, struct map_cb_data *data) cur_settings = g_malloc (sizeof (struct rspamd_settings)); cur_settings->metric_scores = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); cur_settings->reject_scores = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + cur_settings->metric_actions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, settings_actions_free); cur_settings->factors = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); cur_settings->whitelist = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); cur_settings->statfile_alias = NULL; @@ -201,7 +218,29 @@ json_fin_cb (memory_pool_t * pool, struct map_cb_data *data) if (it_val && json_is_number (it_val)) { score = g_malloc (sizeof (double)); *score = json_number_value (it_val); - g_hash_table_insert (cur_settings->metric_scores, g_strdup (json_object_iter_key (json_it)), score); + g_hash_table_insert (cur_settings->metric_scores, + g_strdup (json_object_iter_key (json_it)), score); + } + else if (it_val && json_is_object (it_val)) { + /* Assume this as actions hash */ + cur_act = NULL; + act_it = json_object_iter (it_val); + while (act_it) { + act_value = json_object_iter_value (act_it); + if (it_val && json_is_number (act_value)) { + if (check_action_str (json_object_iter_key (act_it), &j)) { + new_act = g_malloc (sizeof (struct metric_action)); + new_act->action = j; + new_act->score = json_number_value (act_value); + cur_act = g_list_prepend (cur_act, new_act); + } + } + act_it = json_object_iter_next (it_val, act_it); + } + if (cur_act != NULL) { + g_hash_table_insert (cur_settings->metric_actions, + g_strdup (json_object_iter_key (json_it)), cur_act); + } } json_it = json_object_iter_next (cur_nm, json_it); } @@ -225,10 +264,11 @@ json_fin_cb (memory_pool_t * pool, struct map_cb_data *data) cur_nm = json_object_get (cur_elt, "whitelist"); if (cur_nm != NULL && json_is_array (cur_nm)) { n = json_array_size(cur_nm); - for(a = 0; a < n; a++) { - it_val = json_array_get(cur_nm, a); + for(j = 0; j < n; j++) { + it_val = json_array_get(cur_nm, j); if (it_val && json_is_string (it_val)) { - g_hash_table_insert (cur_settings->whitelist, g_strdup (json_string_value (it_val)), g_strdup (json_string_value (it_val))); + g_hash_table_insert (cur_settings->whitelist, + g_strdup (json_string_value (it_val)), g_strdup (json_string_value (it_val))); } } @@ -390,6 +430,47 @@ check_metric_settings (struct worker_task * task, struct metric * metric, double return FALSE; } +gboolean +check_metric_action_settings (struct worker_task *task, struct metric *metric, double score, enum rspamd_metric_action *result) +{ + struct rspamd_settings *us = NULL, *ds = NULL; + struct metric_action *act; + GList *cur; + enum rspamd_metric_action res = METRIC_ACTION_NOACTION; + + if (check_setting (task, &us, &ds)) { + if (us != NULL) { + if ((cur = g_hash_table_lookup (us->metric_actions, metric->name)) != NULL) { + while (cur) { + act = cur->data; + if (score >= act->score) { + res = act->action; + } + cur = g_list_next (cur); + } + } + } + else if (ds != NULL) { + if ((cur = g_hash_table_lookup (ds->metric_actions, metric->name)) != NULL) { + while (cur) { + act = cur->data; + if (score >= act->score) { + res = act->action; + } + cur = g_list_next (cur); + } + } + } + } + + if (res != METRIC_ACTION_NOACTION && result != NULL) { + *result = res; + return TRUE; + } + + return FALSE; +} + gboolean check_factor_settings (struct worker_task * task, const gchar *symbol, double *factor) { diff --git a/src/settings.h b/src/settings.h index a1d712a87..43b70c2f0 100644 --- a/src/settings.h +++ b/src/settings.h @@ -7,6 +7,7 @@ struct rspamd_settings { GHashTable *metric_scores; /**< hash table of metric require scores for this setting */ GHashTable *reject_scores; /**< hash table of metric reject scores for this setting */ + GHashTable *metric_actions; /**< hash table of metric actions for this setting */ GHashTable *factors; /**< hash table of new factors for this setting */ GHashTable *whitelist; /**< hash table of whitelist for this setting */ gchar *statfile_alias; /**< alias for statfile used */ @@ -17,6 +18,7 @@ struct rspamd_settings { gboolean read_settings (const gchar *path, struct config_file *cfg, GHashTable *table); void init_settings (struct config_file *cfg); gboolean check_metric_settings (struct worker_task *task, struct metric *metric, double *score, double *rscore); +gboolean check_metric_action_settings (struct worker_task *task, struct metric *metric, double score, enum rspamd_metric_action *result); gboolean check_factor_settings (struct worker_task *task, const gchar *symbol, double *factor); gboolean check_want_spam (struct worker_task *task); -- 2.39.5