From: Vsevolod Stakhov Date: Tue, 8 Sep 2009 16:13:29 +0000 (+0400) Subject: * Make settings working X-Git-Tag: 0.2.7~21 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=c6d4570aa00c93e6d142aaa8cd38a3c9ee29eb8f;p=rspamd.git * Make settings working --- diff --git a/rspamc.pl.in b/rspamc.pl.in index af2ea7a7e..7e435bf36 100755 --- a/rspamc.pl.in +++ b/rspamc.pl.in @@ -19,18 +19,20 @@ my %cfg = ( 'password' => '', 'control' => 0, 'statfile' => '', + 'deliver_to'=> '', ); $main::VERSION = '@RSPAMD_VERSION@'; sub HELP_MESSAGE { print < STRING %type VARIABLE @@ -97,6 +99,7 @@ command : | raw_mode | profile_file | view + | settings ; tempdir : @@ -984,6 +987,37 @@ viewsymbols: } } ; + +settings: + SETTINGS OBRACE settingsbody EBRACE + ; + +settingsbody: + | settingscmd SEMICOLON + | settingsbody settingscmd SEMICOLON + ; + +settingscmd: + | usersettings + | domainsettings + ; + +usersettings: + USER_SETTINGS EQSIGN QUOTEDSTRING { + if (!read_settings ($3, cfg, cfg->user_settings)) { + yyerror ("yyparse: cannot read settings %s", $3); + YYERROR; + } + } + ; +domainsettings: + DOMAIN_SETTINGS EQSIGN QUOTEDSTRING { + if (!read_settings ($3, cfg, cfg->domain_settings)) { + yyerror ("yyparse: cannot read settings %s", $3); + YYERROR; + } + } + ; %% /* * vi:ts=4 diff --git a/src/cfg_utils.c b/src/cfg_utils.c index 21092356f..7d06e662c 100644 --- a/src/cfg_utils.c +++ b/src/cfg_utils.c @@ -29,6 +29,7 @@ #include "cfg_file.h" #include "main.h" #include "filter.h" +#include "settings.h" #include "classifiers/classifiers.h" #define DEFAULT_SCORE 10.0 @@ -187,6 +188,7 @@ init_defaults (struct config_file *cfg) cfg->composite_symbols = g_hash_table_new (g_str_hash, g_str_equal); cfg->statfiles = g_hash_table_new (g_str_hash, g_str_equal); cfg->cfg_params = g_hash_table_new (g_str_hash, g_str_equal); + init_settings (cfg); } diff --git a/src/filter.c b/src/filter.c index f35ed1dd6..34e487192 100644 --- a/src/filter.c +++ b/src/filter.c @@ -34,6 +34,7 @@ #include "cfg_file.h" #include "util.h" #include "expressions.h" +#include "settings.h" #include "classifiers/classifiers.h" #include "tokenizers/tokenizers.h" @@ -121,18 +122,23 @@ struct consolidation_callback_data { static void consolidation_callback (gpointer key, gpointer value, gpointer arg) { - double *factor; + double *factor, fs; struct symbol *s = (struct symbol *)value; struct consolidation_callback_data *data = (struct consolidation_callback_data *)arg; - - factor = g_hash_table_lookup (data->task->worker->srv->cfg->factors, key); - if (factor == NULL) { - msg_debug ("consolidation_callback: got %.2f score for metric %s, factor: 1", s->score, (char *)key); - data->score += s->score; + + if (check_factor_settings (data->task, key, &fs)) { + data->score += fs * s->score; } else { - data->score += *factor * s->score; - msg_debug ("consolidation_callback: got %.2f score for metric %s, factor: %.2f", s->score, (char *)key, *factor); + factor = g_hash_table_lookup (data->task->worker->srv->cfg->factors, key); + if (factor == NULL) { + msg_debug ("consolidation_callback: got %.2f score for metric %s, factor: 1", s->score, (char *)key); + data->score += s->score; + } + else { + data->score += *factor * s->score; + msg_debug ("consolidation_callback: got %.2f score for metric %s, factor: %.2f", s->score, (char *)key, *factor); + } } } @@ -229,11 +235,15 @@ static gboolean check_metric_is_spam (struct worker_task *task, struct metric *metric) { struct metric_result *res; + double ms; res = g_hash_table_lookup (task->results, metric->name); if (res) { metric_process_callback_forced (metric->name, res, task); - return res->score >= metric->required_score; + if (!check_metric_settings (task, metric, &ms)) { + ms = metric->required_score; + } + return res->score >= ms; } return FALSE; @@ -281,6 +291,11 @@ process_filters (struct worker_task *task) task->save.saved = 0; return continue_process_filters (task); } + /* Check want spam setting */ + if (check_want_spam (task)) { + msg_info ("process_filters: disable check for message id <%s>, user wants spam", task->message_id); + return 1; + } /* Process metrics symbols */ cur = task->worker->srv->cfg->metrics_list; @@ -600,14 +615,18 @@ insert_metric_header (gpointer metric_name, gpointer metric_value, gpointer data char header_name[128], outbuf[1000]; GList *symbols = NULL, *cur; struct metric_result *metric_res = (struct metric_result *)metric_value; + double ms; snprintf (header_name, sizeof (header_name), "X-Spam-%s", metric_res->metric->name); - if (metric_res->score >= metric_res->metric->required_score) { - r += snprintf (outbuf + r, sizeof (outbuf) - r, "yes; %.2f/%.2f; ", metric_res->score, metric_res->metric->required_score); + if (!check_metric_settings (task, metric_res->metric, &ms)) { + ms = metric_res->metric->required_score; + } + if (metric_res->score >= ms) { + r += snprintf (outbuf + r, sizeof (outbuf) - r, "yes; %.2f/%.2f; ", metric_res->score, ms); } else { - r += snprintf (outbuf + r, sizeof (outbuf) - r, "no; %.2f/%.2f; ", metric_res->score, metric_res->metric->required_score); + r += snprintf (outbuf + r, sizeof (outbuf) - r, "no; %.2f/%.2f; ", metric_res->score, ms); } symbols = g_hash_table_get_keys (metric_res->symbols); diff --git a/src/main.h b/src/main.h index ef9a29537..bfc78e888 100644 --- a/src/main.h +++ b/src/main.h @@ -180,6 +180,8 @@ struct worker_task { GList *rcpt; /**< recipients list */ unsigned int nrcpt; /**< number of recipients */ struct in_addr from_addr; /**< client addr in numeric form */ + char *deliver_to; /**< address to deliver */ + char *user; /**< user to deliver */ f_str_t *msg; /**< message buffer */ rspamd_io_dispatcher_t *dispatcher; /**< IO dispatcher object */ memcached_ctx_t *memc_ctx; /**< memcached context associated with task */ diff --git a/src/protocol.c b/src/protocol.c index 5374487df..176160381 100644 --- a/src/protocol.c +++ b/src/protocol.c @@ -26,6 +26,7 @@ #include "main.h" #include "util.h" #include "cfg_file.h" +#include "settings.h" /* Max line size as it is defined in rfc2822 */ #define OUTBUFSIZ 1000 @@ -81,6 +82,7 @@ #define QUEUE_ID_HEADER "Queue-ID" #define ERROR_HEADER "Error" #define USER_HEADER "User" +#define DELIVER_TO_HEADER "Deliver-To" static GList *custom_commands = NULL; @@ -255,6 +257,18 @@ parse_header (struct worker_task *task, f_str_t *line) return -1; } break; + case 'd': + case 'D': + /* Deliver-To */ + if (strncasecmp (headern, DELIVER_TO_HEADER, sizeof (DELIVER_TO_HEADER) - 1) == 0) { + task->deliver_to = memory_pool_fstrdup (task->task_pool, line); + msg_debug ("parse_header: read deliver-to header, value: %s", task->deliver_to); + } + else { + msg_info ("parse_header: wrong header: %s", headern); + return -1; + } + break; case 'h': case 'H': /* helo */ @@ -337,6 +351,7 @@ parse_header (struct worker_task *task, f_str_t *line) case 'U': if (strncasecmp (headern, USER_HEADER, sizeof (USER_HEADER) - 1) == 0) { /* XXX: use this header somehow */ + task->user = memory_pool_fstrdup (task->task_pool, line); } else { return -1; @@ -495,36 +510,43 @@ show_metric_result (gpointer metric_name, gpointer metric_value, void *user_data struct metric_result *metric_res = (struct metric_result *)metric_value; struct metric *m; int is_spam = 0; - + double ms; + if (metric_name == NULL || metric_value == NULL) { m = g_hash_table_lookup (task->cfg->metrics, "default"); + if (!check_metric_settings (task, m, &ms)) { + ms = m->required_score; + } if (task->proto == SPAMC_PROTO) { r = snprintf (outbuf, sizeof (outbuf), "Spam: False ; 0 / %.2f" CRLF, - m != NULL ? m->required_score : 0); + m != NULL ? ms : 0); } else { r = snprintf (outbuf, sizeof (outbuf), "Metric: default; False; 0 / %.2f" CRLF, - m != NULL ? m->required_score : 0); + m != NULL ? ms : 0); } cd->log_offset += snprintf (cd->log_buf + cd->log_offset, cd->log_size - cd->log_offset, - "(%s: F: [0/%.2f] [", "default", m != NULL ? m->required_score : 0); + "(%s: F: [0/%.2f] [", "default", m != NULL ? ms : 0); } else { - if (metric_res->score >= metric_res->metric->required_score) { + if (!check_metric_settings (task, metric_res->metric, &ms)) { + ms = metric_res->metric->required_score; + } + if (metric_res->score >= ms) { is_spam = 1; } if (task->proto == SPAMC_PROTO) { r = snprintf (outbuf, sizeof (outbuf), "Spam: %s ; %.2f / %.2f" CRLF, - (is_spam) ? "True" : "False", metric_res->score, metric_res->metric->required_score); + (is_spam) ? "True" : "False", metric_res->score, ms); } else { r = snprintf (outbuf, sizeof (outbuf), "Metric: %s; %s; %.2f / %.2f" CRLF, (char *)metric_name, - (is_spam) ? "True" : "False", metric_res->score, metric_res->metric->required_score); + (is_spam) ? "True" : "False", metric_res->score, ms); } cd->log_offset += snprintf (cd->log_buf + cd->log_offset, cd->log_size - cd->log_offset, "(%s: %s: [%.2f/%.2f] [", (char *)metric_name, is_spam ? "T" : "F", - metric_res->score, metric_res->metric->required_score); + metric_res->score, ms); } if (task->cmd == CMD_PROCESS) { #ifndef GMIME24 diff --git a/src/settings.c b/src/settings.c index f05f59b45..75ba04c76 100644 --- a/src/settings.c +++ b/src/settings.c @@ -73,9 +73,9 @@ json_read_cb (memory_pool_t *pool, u_char *chunk, size_t len, struct map_cb_data if (jb->buf == NULL) { /* Allocate memory for buffer */ - jb->pos = jb->buf; jb->buflen = len * 2; jb->buf = g_malloc (jb->buflen); + jb->pos = jb->buf; } off = jb->pos - jb->buf; @@ -87,7 +87,7 @@ json_read_cb (memory_pool_t *pool, u_char *chunk, size_t len, struct map_cb_data jb->pos = jb->buf + off; } - memcpy (chunk, jb->pos, len); + memcpy (jb->pos, chunk, len); jb->pos += len; /* Say not to copy any part of this buffer */ @@ -115,10 +115,21 @@ json_fin_cb (memory_pool_t *pool, struct map_cb_data *data) if (jb->buf) { g_free (jb->buf); } - g_free (jb->buf); + g_free (jb); } /* Now parse json */ + if (data->cur_data) { + jb = data->cur_data; + } + else { + msg_err ("json_fin_cb: no data read"); + return; + } + if (jb->buf == NULL) { + msg_err ("json_fin_cb: no data read"); + return; + } /* NULL terminate current buf */ *jb->pos = '\0'; @@ -183,7 +194,7 @@ 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->factors, g_strdup (json_object_iter_key (json_it)), + g_hash_table_insert (cur_settings->metric_scores, g_strdup (json_object_iter_key (json_it)), score); } json_it = json_object_iter_next(cur_nm, json_it); @@ -204,12 +215,15 @@ json_fin_cb (memory_pool_t *pool, struct map_cb_data *data) gboolean read_settings (const char *path, struct config_file *cfg, GHashTable *table) { - struct json_buf *jb = g_malloc (sizeof (struct json_buf)); + struct json_buf *jb = g_malloc (sizeof (struct json_buf)), **pjb; + + pjb = g_malloc (sizeof (struct json_buf *)); jb->table = table; jb->buf = NULL; + *pjb = jb; - if (!add_map (path, json_read_cb, json_fin_cb, (void **)&jb)) { + if (!add_map (path, json_read_cb, json_fin_cb, (void **)pjb)) { msg_err ("read_settings: cannot add map %s", path); return FALSE; } @@ -224,6 +238,141 @@ init_settings (struct config_file *cfg) cfg->user_settings = g_hash_table_new_full (rspamd_strcase_hash, rspamd_strcase_equal, g_free, settings_free); } +static gboolean +check_setting (struct worker_task *task, struct rspamd_settings **user_settings, struct rspamd_settings **domain_settings) +{ + char *field = NULL, *domain = NULL; + + if (task->deliver_to != NULL) { + /* First try to use deliver-to field */ + field = task->deliver_to; + } + else if (task->user != NULL) { + /* Then user field */ + field = task->user; + } + else if (task->rcpt != NULL) { + /* Then first recipient */ + field = task->rcpt->data; + } + else { + return FALSE; + } + + domain = strchr (field, '@'); + if (domain == NULL) { + /* First try to search in first recipient */ + if (task->rcpt) { + domain = strchr (task->rcpt->data, '@'); + } + } + if (domain != NULL) { + domain ++; + } + + /* First try to search per-user settings */ + if (field != NULL) { + *user_settings = g_hash_table_lookup (task->cfg->user_settings, field); + } + if (domain != NULL) { + *domain_settings = g_hash_table_lookup (task->cfg->domain_settings, domain); + } + + if (*domain_settings != NULL || *user_settings != NULL) { + return TRUE; + } + + return FALSE; +} + +gboolean +check_metric_settings (struct worker_task *task, struct metric *metric, double *score) +{ + struct rspamd_settings *us, *ds; + double *sc; + + if (check_setting (task, &us, &ds)) { + if (us != NULL) { + /* First search in user's settings */ + if ((sc = g_hash_table_lookup (us->metric_scores, metric->name)) != NULL) { + *score = *sc; + return TRUE; + } + /* Now check in domain settings */ + if (ds && (sc = g_hash_table_lookup (ds->metric_scores, metric->name)) != NULL) { + *score = *sc; + return TRUE; + } + } + else if (ds != NULL) { + if ((sc = g_hash_table_lookup (ds->metric_scores, metric->name)) != NULL) { + *score = *sc; + return TRUE; + } + } + } + + return FALSE; +} + +gboolean +check_factor_settings (struct worker_task *task, const char *symbol, double *factor) +{ + struct rspamd_settings *us, *ds; + double *fc; + + if (check_setting (task, &us, &ds)) { + if (us != NULL) { + /* First search in user's settings */ + if ((fc = g_hash_table_lookup (us->factors, symbol)) != NULL) { + *factor = *fc; + return TRUE; + } + /* Now check in domain settings */ + if (ds && (fc = g_hash_table_lookup (ds->factors, symbol)) != NULL) { + *factor = *fc; + return TRUE; + } + } + else if (ds != NULL) { + if ((fc = g_hash_table_lookup (ds->factors, symbol)) != NULL) { + *factor = *fc; + return TRUE; + } + } + } + + return FALSE; + +} + + +gboolean +check_want_spam (struct worker_task *task) +{ + struct rspamd_settings *us, *ds; + + if (check_setting (task, &us, &ds)) { + if (us != NULL) { + /* First search in user's settings */ + if (us->want_spam) { + return TRUE; + } + /* Now check in domain settings */ + if (ds && ds->want_spam) { + return TRUE; + } + } + else if (ds != NULL) { + if (ds->want_spam) { + return TRUE; + } + } + } + + return FALSE; +} + /* * vi:ts=4 */ diff --git a/src/settings.h b/src/settings.h index 8d2214ae1..696bd9d76 100644 --- a/src/settings.h +++ b/src/settings.h @@ -12,6 +12,10 @@ struct rspamd_settings { }; -int read_settings (const char *path, struct config_file *cfg, GHashTable *table); +gboolean read_settings (const char *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); +gboolean check_factor_settings (struct worker_task *task, const char *symbol, double *factor); +gboolean check_want_spam (struct worker_task *task); #endif