aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--perl/lib/Mail/Rspamd/Client.pm4
-rwxr-xr-xrspamc.pl.in17
-rw-r--r--src/cfg_xml.c57
-rw-r--r--src/filter.h37
-rw-r--r--src/protocol.c63
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);
@@ -694,6 +695,42 @@ handle_metric_action (struct config_file *cfg, struct rspamd_xml_userdata *ctx,
}
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)
{
char *strval, *err;
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);
}
}