From 205cb6049d86f74707949ac6a3b2de64afe2850a Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Thu, 15 Jul 2010 19:20:45 +0400 Subject: [PATCH] * Add ability to assign several actions to one metric * Report action in rspamc protocol * Mail::Rspamd::Client and rspamc can now understand Action header too * Write action to log as well --- perl/lib/Mail/Rspamd/Client.pm | 4 +++ rspamc.pl.in | 17 +++++++-- src/cfg_xml.c | 57 ++++++++++++++++++++++++------ src/filter.h | 37 ++++++++++++-------- src/protocol.c | 63 +++++++++++++++++++++++++++++++--- 5 files changed, 147 insertions(+), 31 deletions(-) diff --git a/perl/lib/Mail/Rspamd/Client.pm b/perl/lib/Mail/Rspamd/Client.pm index ca90b7421..d74484002 100644 --- a/perl/lib/Mail/Rspamd/Client.pm +++ b/perl/lib/Mail/Rspamd/Client.pm @@ -943,6 +943,7 @@ sub _do_rspamc_command { symbols => [], urls => [], messages => [], + action => 'reject', }; foreach my $line (@lines) { if ($line =~ /^Urls: (.+)$/) { @@ -979,6 +980,9 @@ sub _do_rspamc_command { my $symref = $metrics{$cur_metric}->{'messages'}; push(@$symref, $1); } + elsif ($line =~ /^Action: (.+)/ && $cur_metric) { + $metrics{$cur_metric}->{'action'} = $1; + } elsif ($line =~ /^${EOL}$/) { last; } diff --git a/rspamc.pl.in b/rspamc.pl.in index a71e061f1..b2f6162d8 100755 --- a/rspamc.pl.in +++ b/rspamc.pl.in @@ -178,9 +178,22 @@ sub print_rspamc_result { print "Symbols: "; $terminal->Tputs( 'me', 1, *STDOUT ); print join("; ", @{ $result->{symbols} }) . "\n"; - print "Urls: " . join(", ", @{ $result->{urls} }) . "\n"; + + $terminal->Tputs( 'md', 1, *STDOUT ); + print "Action: "; + $terminal->Tputs( 'me', 1, *STDOUT ); + print "$result->{action}\n"; + + $terminal->Tputs( 'md', 1, *STDOUT ); + print "Urls: "; + $terminal->Tputs( 'me', 1, *STDOUT ); + print join(", ", @{ $result->{urls} }) . "\n"; + foreach my $msg (@{ $result->{messages} }) { - print "Message: $msg\n"; + $terminal->Tputs( 'md', 1, *STDOUT ); + print "Message: "; + $terminal->Tputs( 'me', 1, *STDOUT ); + print "$msg\n"; } print "\n\n"; } diff --git a/src/cfg_xml.c b/src/cfg_xml.c index c88bf60db..534bfa961 100644 --- a/src/cfg_xml.c +++ b/src/cfg_xml.c @@ -672,19 +672,20 @@ worker_handle_bind (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GH 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, int offset) +G_INLINE_FUNC gboolean +check_action (const gchar *data, gint *result) { - struct metric *metric = ctx->section_pointer; - - if (g_ascii_strcasecmp (data, "reject") == 0) { - metric->action = METRIC_ACTION_REJECT; + if (g_ascii_strncasecmp (data, "reject", sizeof ("reject") - 1) == 0) { + *result = METRIC_ACTION_REJECT; } - else if (g_ascii_strcasecmp (data, "greylist") == 0) { - metric->action = METRIC_ACTION_GREYLIST; + 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_strcasecmp (data, "add_header") == 0) { - metric->action = 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); @@ -693,6 +694,42 @@ handle_metric_action (struct config_file *cfg, struct rspamd_xml_userdata *ctx, 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, int offset) +{ + struct metric *metric = ctx->section_pointer; + gchar *p, *errstr; + gint res; + struct metric_action *action; + + /* First of all check whether we have data with weight (reject:50 for example) */ + if ((p = strchr (data, ':')) == NULL) { + if (check_action (data, &res)) { + metric->action = res; + return TRUE; + } + return FALSE; + } + else { + if (!check_action (data, &res)) { + return FALSE; + } + else { + action = memory_pool_alloc (cfg->cfg_pool, sizeof (struct metric_action)); + action->action = res; + errno = 0; + action->score = strtod (p + 1, &errstr); + if (errno != 0 || (errstr != NULL && *errstr != '\0')) { + msg_err ("invalid double value: %s", data); + return FALSE; + } + metric->actions = g_list_prepend (metric->actions, action); + } + } + + return TRUE; +} + gboolean handle_metric_symbol (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, int offset) { diff --git a/src/filter.h b/src/filter.h index 24feaae9e..7996f16e6 100644 --- a/src/filter.h +++ b/src/filter.h @@ -33,24 +33,33 @@ struct symbol { GList *options; /**< list of symbol's options */ }; +enum rspamd_metric_action { + METRIC_ACTION_REJECT = 0, + METRIC_ACTION_SOFT_REJECT, + METRIC_ACTION_REWRITE_SUBJECT, + METRIC_ACTION_ADD_HEADER, + METRIC_ACTION_GREYLIST, + METRIC_ACTION_NOACTION +}; + +struct metric_action { + enum rspamd_metric_action action; + gdouble score; +}; + /** * Common definition of metric */ struct metric { - char *name; /**< name of metric */ - char *func_name; /**< name of consolidation function */ - metric_cons_func func; /**< c consolidation function */ - double grow_factor; /**< grow factor for metric */ - double required_score; /**< required score for this metric */ - double reject_score; /**< reject score for this metric */ - GHashTable *symbols; /**< weights of symbols in metric */ - enum { - METRIC_ACTION_REJECT = 0, - METRIC_ACTION_SOFT_REJECT, - METRIC_ACTION_REWRITE_SUBJECT, - METRIC_ACTION_ADD_HEADER, - METRIC_ACTION_GREYLIST - } action; /**< action to do by this metric */ + char *name; /**< name of metric */ + char *func_name; /**< name of consolidation function */ + metric_cons_func func; /**< c consolidation function */ + double grow_factor; /**< grow factor for metric */ + double required_score; /**< required score for this metric */ + double reject_score; /**< reject score for this metric */ + GHashTable *symbols; /**< weights of symbols in metric */ + enum rspamd_metric_action action; /**< action to do by this metric by default */ + GList *actions; /**< actions that can be performed by this metric */ }; /** diff --git a/src/protocol.c b/src/protocol.c index 1b349b49e..f4e95b40f 100644 --- a/src/protocol.c +++ b/src/protocol.c @@ -591,18 +591,69 @@ show_metric_symbols (struct metric_result *metric_res, struct metric_callback_da return TRUE; } +G_INLINE_FUNC const char * +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"; +} + +G_INLINE_FUNC 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) { struct metric_callback_data *cd = (struct metric_callback_data *)user_data; struct worker_task *task = cd->task; - int r; + int r = 0; char outbuf[OUTBUFSIZ]; struct metric_result *metric_res = (struct metric_result *)metric_value; struct metric *m; int is_spam = 0; double ms = 0, rs = 0; + enum rspamd_metric_action action; if (! cd->alive) { return; @@ -652,6 +703,7 @@ show_metric_result (gpointer metric_name, gpointer metric_value, void *user_data 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); } @@ -670,14 +722,15 @@ show_metric_result (gpointer metric_name, gpointer metric_value, void *user_data r = rspamd_snprintf (outbuf, sizeof (outbuf), "Metric: %s; %s; %.2f / %.2f" CRLF, (char *)metric_name, (is_spam) ? "True" : "False", metric_res->score, ms); } + r += rspamd_snprintf (outbuf + r, sizeof (outbuf) - r, "Action: %s" CRLF, str_action_metric(action)); } if (!task->is_skipped) { - cd->log_offset += rspamd_snprintf (cd->log_buf + cd->log_offset, cd->log_size - cd->log_offset, "(%s: %s: [%.2f/%.2f/%.2f] [", - (char *)metric_name, is_spam ? "T" : "F", metric_res->score, ms, rs); + cd->log_offset += rspamd_snprintf (cd->log_buf + cd->log_offset, cd->log_size - cd->log_offset, "(%s: %c (%s): [%.2f/%.2f/%.2f] [", + (char *)metric_name, is_spam ? 'T' : 'F', str_action_metric (action), metric_res->score, ms, rs); } else { - cd->log_offset += rspamd_snprintf (cd->log_buf + cd->log_offset, cd->log_size - cd->log_offset, "(%s: %s: [%.2f/%.2f/%.2f] [", - (char *)metric_name, "S", metric_res->score, ms, rs); + cd->log_offset += rspamd_snprintf (cd->log_buf + cd->log_offset, cd->log_size - cd->log_offset, "(%s: %c (%s): [%.2f/%.2f/%.2f] [", + (char *)metric_name, 'S', metric_res->score, ms, rs); } } -- 2.39.5