print "Sending ". length ($input) ." bytes...\n";
- syswrite $sock, "$cfg{'command'} RSPAMC/1.0 $CRLF";
+ syswrite $sock, "$cfg{'command'} RSPAMC/1.1 $CRLF";
if ($cfg{'deliver_to'}) {
syswrite $sock, "Deliver-To: " . $cfg{'deliver_to'} . $CRLF;
}
/* 1 worker by default */
#define DEFAULT_WORKERS_NUM 1
+#define DEFAULT_SCORE 10.0
+#define DEFAULT_REJECT_SCORE 999.0
+
#define yyerror parse_err
#define yywarn parse_warn
metric return METRIC;
name return NAME;
required_score return REQUIRED_SCORE;
+reject_score return REJECT_SCORE;
function return FUNCTION;
cache_file return CACHE_FILE;
control return CONTROL;
%token MEMCACHED WORKER TYPE MODULES MODULE_PATH
%token MODULE_OPT PARAM VARIABLE
%token FILTERS FACTORS METRIC NAME CACHE_FILE
-%token REQUIRED_SCORE FUNCTION FRACT COMPOSITES CONTROL PASSWORD
+%token REQUIRED_SCORE REJECT_SCORE FUNCTION FRACT COMPOSITES CONTROL PASSWORD
%token LOGGING LOG_TYPE LOG_TYPE_CONSOLE LOG_TYPE_SYSLOG LOG_TYPE_FILE
%token LOG_LEVEL LOG_LEVEL_DEBUG LOG_LEVEL_INFO LOG_LEVEL_WARNING LOG_LEVEL_ERROR LOG_FACILITY LOG_FILENAME
%token STATFILE ALIAS PATTERN WEIGHT STATFILE_POOL_SIZE SIZE TOKENIZER CLASSIFIER
| metricname
| metricfunction
| metricscore
+ | metricrjscore
| metricclassifier
| metriccache
;
}
;
+metricrjscore:
+ REJECT_SCORE EQSIGN NUMBER {
+ if (cur_metric == NULL) {
+ cur_metric = memory_pool_alloc0 (cfg->cfg_pool, sizeof (struct metric));
+ }
+ cur_metric->reject_score = $3;
+ }
+ | REQUIRED_SCORE EQSIGN FRACT {
+ if (cur_metric == NULL) {
+ cur_metric = memory_pool_alloc0 (cfg->cfg_pool, sizeof (struct metric));
+ }
+ cur_metric->reject_score = $3;
+ }
+ ;
+
metricclassifier:
CLASSIFIER EQSIGN QUOTEDSTRING {
if (cur_metric == NULL) {
def_metric->func_name = "factors";
def_metric->func = factor_consolidation_func;
def_metric->required_score = DEFAULT_SCORE;
+ def_metric->reject_score = DEFAULT_REJECT_SCORE;
def_metric->classifier = get_classifier ("winnow");
cfg->metrics_list = g_list_prepend (cfg->metrics_list, def_metric);
g_hash_table_insert (cfg->metrics, DEFAULT_METRIC, def_metric);
check_metric_is_spam (struct worker_task *task, struct metric *metric)
{
struct metric_result *res;
- double ms;
+ double ms, rs;
res = g_hash_table_lookup (task->results, metric->name);
if (res) {
metric_process_callback_forced (metric->name, res, task);
- if (!check_metric_settings (task, metric, &ms)) {
+ if (!check_metric_settings (task, metric, &ms, &rs)) {
ms = metric->required_score;
}
return res->score >= ms;
char header_name[128], outbuf[1000];
GList *symbols = NULL, *cur;
struct metric_result *metric_res = (struct metric_result *)metric_value;
- double ms;
+ double ms, rs;
snprintf (header_name, sizeof (header_name), "X-Spam-%s", metric_res->metric->name);
- if (!check_metric_settings (task, metric_res->metric, &ms)) {
+ if (!check_metric_settings (task, metric_res->metric, &ms, &rs)) {
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);
+ r += snprintf (outbuf + r, sizeof (outbuf) - r, "yes; %.2f/%.2f/%.2f; ", metric_res->score, ms, rs);
}
else {
- r += snprintf (outbuf + r, sizeof (outbuf) - r, "no; %.2f/%.2f; ", metric_res->score, ms);
+ r += snprintf (outbuf + r, sizeof (outbuf) - r, "no; %.2f/%.2f/%.2f; ", metric_res->score, ms, rs);
}
symbols = g_hash_table_get_keys (metric_res->symbols);
char *func_name; /**< name of consolidation function */
metric_cons_func func; /**< c consolidation function */
double required_score; /**< required score for this metric */
+ double reject_score; /**< reject score for this metric */
struct classifier *classifier; /**< classifier that is used for metric */
struct symbols_cache *cache; /**< symbols cache for metric */
char *cache_filename; /**< filename of cache file */
} state; /**< current session state */
size_t content_length; /**< length of user's input */
enum rspamd_protocol proto; /**< protocol (rspamc or spamc) */
+ const char *proto_ver; /**< protocol version */
enum rspamd_command cmd; /**< command */
struct custom_command *custom_cmd; /**< custom command if any */
int sock; /**< socket descriptor */
if (strncasecmp (line->begin, RSPAMC_GREETING, sizeof (RSPAMC_GREETING) - 1) == 0) {
task->proto = RSPAMC_PROTO;
+ task->proto_ver = RSPAMC_PROTO_1_0;
+ if (*(line->begin + sizeof (RSPAMC_GREETING) - 1) == '/') {
+ /* Extract protocol version */
+ token = line->begin + sizeof (RSPAMC_GREETING);
+ if (strncmp (token, RSPAMC_PROTO_1_1, sizeof (RSPAMC_PROTO_1_1) - 1) == 0) {
+ task->proto_ver = RSPAMC_PROTO_1_1;
+ }
+ }
}
else if (strncasecmp (line->begin, SPAMC_GREETING, sizeof (SPAMC_GREETING) - 1) == 0) {
task->proto = SPAMC_PROTO;
+ task->proto_ver = RSPAMC_PROTO_1_1;
}
else {
return -1;
}
+
task->state = READ_HEADER;
+
return 0;
}
struct metric_result *metric_res = (struct metric_result *)metric_value;
struct metric *m;
int is_spam = 0;
- double ms;
+ double ms = 0, rs = 0;
if (metric_name == NULL || metric_value == NULL) {
m = g_hash_table_lookup (task->cfg->metrics, "default");
- if (!check_metric_settings (task, m, &ms)) {
+ if (!check_metric_settings (task, m, &ms, &rs)) {
ms = m->required_score;
+ rs = m->reject_score;
}
if (task->proto == SPAMC_PROTO) {
- r = snprintf (outbuf, sizeof (outbuf), "Spam: False ; 0 / %.2f" CRLF, m != NULL ? ms : 0);
+ r = snprintf (outbuf, sizeof (outbuf), "Spam: False ; 0 / %.2f" CRLF, ms);
}
else {
- r = snprintf (outbuf, sizeof (outbuf), "Metric: default; False; 0 / %.2f" CRLF, m != NULL ? ms : 0);
+ if (strcmp (task->proto_ver, RSPAMC_PROTO_1_1) == 0) {
+ r = snprintf (outbuf, sizeof (outbuf), "Metric: default; False; 0 / %.2f / %.2f" CRLF, ms, rs);
+ }
+ else {
+ r = snprintf (outbuf, sizeof (outbuf), "Metric: default; False; 0 / %.2f" CRLF, ms);
+ }
}
- cd->log_offset += snprintf (cd->log_buf + cd->log_offset, cd->log_size - cd->log_offset, "(%s: F: [0/%.2f] [", "default", m != NULL ? ms : 0);
+ cd->log_offset += snprintf (cd->log_buf + cd->log_offset, cd->log_size - cd->log_offset, "(%s: F: [0/%.2f/%.2f] [", "default", ms, rs);
}
else {
- if (!check_metric_settings (task, metric_res->metric, &ms)) {
+ if (!check_metric_settings (task, metric_res->metric, &ms, &rs)) {
ms = metric_res->metric->required_score;
+ rs = metric_res->metric->reject_score;
}
if (metric_res->score >= ms) {
is_spam = 1;
r = snprintf (outbuf, sizeof (outbuf), "Spam: %s ; %.2f / %.2f" CRLF, (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, ms);
+ if (strcmp (task->proto_ver, RSPAMC_PROTO_1_1) == 0) {
+ r = snprintf (outbuf, sizeof (outbuf), "Metric: %s; %s; %.2f / %.2f / %.2f" CRLF,
+ (char *)metric_name, (is_spam) ? "True" : "False", metric_res->score, ms, rs);
+ }
+ else {
+ r = snprintf (outbuf, sizeof (outbuf), "Metric: %s; %s; %.2f / %.2f" CRLF,
+ (char *)metric_name, (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, ms);
+ cd->log_offset += 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);
}
if (task->cmd == CMD_PROCESS) {
#ifndef GMIME24
struct metric_result *metric_res;
struct metric_callback_data cd;
- r = snprintf (outbuf, sizeof (outbuf), "%s 0 %s" CRLF, (task->proto == SPAMC_PROTO) ? SPAMD_REPLY_BANNER : RSPAMD_REPLY_BANNER, "OK");
+ r = snprintf (outbuf, sizeof (outbuf), "%s/%s 0 %s" CRLF, (task->proto == SPAMC_PROTO) ? SPAMD_REPLY_BANNER : RSPAMD_REPLY_BANNER,
+ task->proto_ver, "OK");
rspamd_dispatcher_write (task->dispatcher, outbuf, r, TRUE, FALSE);
cd.task = task;
struct metric_result *metric_res;
struct metric_callback_data cd;
- r = snprintf (outbuf, sizeof (outbuf), "%s 0 %s" CRLF "Content-Length: %zd" CRLF CRLF, (task->proto == SPAMC_PROTO) ? SPAMD_REPLY_BANNER : RSPAMD_REPLY_BANNER, "OK", task->msg->len);
+ r = snprintf (outbuf, sizeof (outbuf), "%s/%s 0 %s" CRLF "Content-Length: %zd" CRLF CRLF,
+ (task->proto == SPAMC_PROTO) ? SPAMD_REPLY_BANNER : RSPAMD_REPLY_BANNER,
+ task->proto_ver, "OK", task->msg->len);
cd.task = task;
cd.log_buf = logbuf;
if (task->error_code != 0) {
/* Write error message and error code to reply */
if (task->proto == SPAMC_PROTO) {
- r = snprintf (outbuf, sizeof (outbuf), "%s %d %s" CRLF CRLF, SPAMD_REPLY_BANNER, task->error_code, SPAMD_ERROR);
+ r = snprintf (outbuf, sizeof (outbuf), "%s/%s %d %s" CRLF CRLF,
+ SPAMD_REPLY_BANNER, task->proto_ver, task->error_code, SPAMD_ERROR);
msg_debug ("write_reply: writing error: %s", outbuf);
}
else {
- r = snprintf (outbuf, sizeof (outbuf), "%s %d %s" CRLF "%s: %s" CRLF CRLF, RSPAMD_REPLY_BANNER, task->error_code, SPAMD_ERROR, ERROR_HEADER, task->last_error);
+ r = snprintf (outbuf, sizeof (outbuf), "%s/%s %d %s" CRLF "%s: %s" CRLF CRLF,
+ RSPAMD_REPLY_BANNER, task->proto_ver, task->error_code, SPAMD_ERROR, ERROR_HEADER, task->last_error);
msg_debug ("write_reply: writing error: %s", outbuf);
}
/* Write to bufferevent error message */
return write_process_reply (task);
break;
case CMD_SKIP:
- r = snprintf (outbuf, sizeof (outbuf), "%s 0 %s" CRLF, (task->proto == SPAMC_PROTO) ? SPAMD_REPLY_BANNER : RSPAMD_REPLY_BANNER, SPAMD_OK);
+ r = snprintf (outbuf, sizeof (outbuf), "%s/%s 0 %s" CRLF,
+ (task->proto == SPAMC_PROTO) ? SPAMD_REPLY_BANNER : RSPAMD_REPLY_BANNER, task->proto_ver, SPAMD_OK);
rspamd_dispatcher_write (task->dispatcher, outbuf, r, FALSE, FALSE);
break;
case CMD_PING:
- r = snprintf (outbuf, sizeof (outbuf), "%s 0 PONG" CRLF, (task->proto == SPAMC_PROTO) ? SPAMD_REPLY_BANNER : RSPAMD_REPLY_BANNER);
+ r = snprintf (outbuf, sizeof (outbuf), "%s/%s 0 PONG" CRLF,
+ (task->proto == SPAMC_PROTO) ? SPAMD_REPLY_BANNER : RSPAMD_REPLY_BANNER, task->proto_ver);
rspamd_dispatcher_write (task->dispatcher, outbuf, r, FALSE, FALSE);
break;
case CMD_OTHER:
#define RSPAMD_PROTOCOL_ERROR 3
#define RSPAMD_LENGTH_ERROR 4
+#define RSPAMC_PROTO_1_0 "1.0"
+#define RSPAMC_PROTO_1_1 "1.1"
+
/*
* Reply messages
*/
-#define RSPAMD_REPLY_BANNER "RSPAMD/1.0"
-#define SPAMD_REPLY_BANNER "SPAMD/1.1"
+#define RSPAMD_REPLY_BANNER "RSPAMD"
+#define SPAMD_REPLY_BANNER "SPAMD"
#define SPAMD_OK "EX_OK"
/* XXX: try to convert rspamd errors to spamd errors */
#define SPAMD_ERROR "EX_ERROR"
if (s->metric_scores) {
g_hash_table_destroy (s->metric_scores);
}
+ if (s->reject_scores) {
+ g_hash_table_destroy (s->reject_scores);
+ }
g_free (s);
}
for (i = 0; i < nelts; i++) {
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->factors = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
cur_settings->statfile_alias = NULL;
cur_settings->want_spam = FALSE;
json_it = json_object_iter_next (cur_nm, json_it);
}
}
+ /* Rejects object */
+ cur_nm = json_object_get (cur_elt, "rejects");
+ if (cur_nm != NULL && json_is_object (cur_nm)) {
+ json_it = json_object_iter (cur_nm);
+ while (json_it) {
+ it_val = json_object_iter_value (json_it);
+ 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->reject_scores, g_strdup (json_object_iter_key (json_it)),
+ score);
+ }
+ json_it = json_object_iter_next(cur_nm, json_it);
+ }
+ }
/* Want spam */
cur_nm = json_object_get (cur_elt, "want_spam");
if (cur_nm != NULL) {
}
gboolean
-check_metric_settings (struct worker_task * task, struct metric * metric, double *score)
+check_metric_settings (struct worker_task * task, struct metric * metric, double *score, double *rscore)
{
struct rspamd_settings *us, *ds;
- double *sc;
+ double *sc, *rs;
+
+ *rscore = DEFAULT_REJECT_SCORE;
if (check_setting (task, &us, &ds)) {
if (us != NULL) {
/* First search in user's settings */
+ if ((rs = g_hash_table_lookup (us->reject_scores, metric->name)) != NULL) {
+ *rscore = *rs;
+ }
if ((sc = g_hash_table_lookup (us->metric_scores, metric->name)) != NULL) {
*score = *sc;
return TRUE;
}
/* Now check in domain settings */
+ if (ds && ((rs = g_hash_table_lookup (ds->reject_scores, metric->name)) != NULL)) {
+ *rscore = *rs;
+ }
if (ds && (sc = g_hash_table_lookup (ds->metric_scores, metric->name)) != NULL) {
*score = *sc;
return TRUE;
}
}
else if (ds != NULL) {
+ if ((rs = g_hash_table_lookup (ds->reject_scores, metric->name)) != NULL) {
+ *rscore = *rs;
+ }
if ((sc = g_hash_table_lookup (ds->metric_scores, metric->name)) != NULL) {
*score = *sc;
return TRUE;
#include "main.h"
struct rspamd_settings {
- GHashTable *metric_scores; /**< hash table of metric scores for this setting */
+ 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 *factors; /**< hash table of new factors for this setting */
char *statfile_alias; /**< alias for statfile used */
gboolean want_spam; /**< if true disable rspamd checks */
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_metric_settings (struct worker_task *task, struct metric *metric, double *score, double *rscore);
gboolean check_factor_settings (struct worker_task *task, const char *symbol, double *factor);
gboolean check_want_spam (struct worker_task *task);