summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/controller.c9
-rw-r--r--src/dynamic_cfg.c20
-rw-r--r--src/dynamic_cfg.h2
-rw-r--r--src/filter.c6
-rw-r--r--src/json/jansson.h1
-rw-r--r--src/json/load.c58
-rw-r--r--src/protocol.c28
-rw-r--r--src/webui.c214
8 files changed, 312 insertions, 26 deletions
diff --git a/src/controller.c b/src/controller.c
index 20a733693..f633f5b01 100644
--- a/src/controller.c
+++ b/src/controller.c
@@ -532,6 +532,7 @@ process_dynamic_conf_command (gchar **cmd_args, struct controller_session *sessi
gchar *arg, *metric, *name, *err_str;
gdouble value;
gboolean res;
+ guint real_act;
if (cfg->dynamic_conf == NULL) {
if (!session->restful) {
@@ -589,7 +590,13 @@ process_dynamic_conf_command (gchar **cmd_args, struct controller_session *sessi
}
if (is_action) {
- res = add_dynamic_action (cfg, metric, name, value);
+ if (!check_action_str (name, &real_act)) {
+ msg_info ("invalid action string: %s", name);
+ res = FALSE;
+ }
+ else {
+ res = add_dynamic_action (cfg, metric, real_act, value);
+ }
}
else {
res = add_dynamic_symbol (cfg, metric, name, value);
diff --git a/src/dynamic_cfg.c b/src/dynamic_cfg.c
index 7b9596c6c..3979e8ddc 100644
--- a/src/dynamic_cfg.c
+++ b/src/dynamic_cfg.c
@@ -565,23 +565,17 @@ add_dynamic_symbol (struct config_file *cfg, const gchar *metric_name, const gch
* @return
*/
gboolean
-add_dynamic_action (struct config_file *cfg, const gchar *metric_name, const gchar *action, gdouble value)
+add_dynamic_action (struct config_file *cfg, const gchar *metric_name, guint action, gdouble value)
{
GList *cur;
struct dynamic_cfg_metric *metric = NULL;
struct dynamic_cfg_action *act = NULL;
- gint real_act;
if (cfg->dynamic_conf == NULL) {
msg_info ("dynamic conf is disabled");
return FALSE;
}
- if (!check_action_str (action, &real_act)) {
- msg_info ("invalid action string: %s", action);
- return FALSE;
- }
-
cur = cfg->current_dynamic_conf;
while (cur) {
metric = cur->data;
@@ -597,9 +591,9 @@ add_dynamic_action (struct config_file *cfg, const gchar *metric_name, const gch
cur = metric->actions;
while (cur) {
act = cur->data;
- if ((gint)act->action == real_act) {
+ if (act->action == action) {
act->value = value;
- msg_debug ("change value of action %s to %.2f", action, value);
+ msg_debug ("change value of action %d to %.2f", action, value);
break;
}
act = NULL;
@@ -608,22 +602,22 @@ add_dynamic_action (struct config_file *cfg, const gchar *metric_name, const gch
if (act == NULL) {
/* Action not found, insert it */
act = g_slice_alloc (sizeof (struct dynamic_cfg_action));
- act->action = real_act;
+ act->action = action;
act->value = value;
metric->actions = g_list_prepend (metric->actions, act);
- msg_debug ("create action %s in metric %s", action, metric_name);
+ msg_debug ("create action %d in metric %s", action, metric_name);
}
}
else {
/* Metric not found, create it */
metric = g_slice_alloc0 (sizeof (struct dynamic_cfg_metric));
act = g_slice_alloc (sizeof (struct dynamic_cfg_action));
- act->action = real_act;
+ act->action = action;
act->value = value;
metric->actions = g_list_prepend (metric->actions, act);
metric->name = g_strdup (metric_name);
cfg->current_dynamic_conf = g_list_prepend (cfg->current_dynamic_conf, metric);
- msg_debug ("create metric %s for action %s", metric_name, action);
+ msg_debug ("create metric %s for action %d", metric_name, action);
}
apply_dynamic_conf (cfg->current_dynamic_conf, cfg);
diff --git a/src/dynamic_cfg.h b/src/dynamic_cfg.h
index c791a3c38..b65d7aa9a 100644
--- a/src/dynamic_cfg.h
+++ b/src/dynamic_cfg.h
@@ -60,7 +60,7 @@ gboolean add_dynamic_symbol (struct config_file *cfg, const gchar *metric, const
* @param value value of symbol
* @return
*/
-gboolean add_dynamic_action (struct config_file *cfg, const gchar *metric, const gchar *action, gdouble value);
+gboolean add_dynamic_action (struct config_file *cfg, const gchar *metric, guint action, gdouble value);
#endif /* DYNAMIC_CFG_H_ */
diff --git a/src/filter.c b/src/filter.c
index bbe3b4678..44fc16a07 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -830,11 +830,11 @@ str_action_metric (enum rspamd_metric_action action)
case METRIC_ACTION_REJECT:
return "reject";
case METRIC_ACTION_SOFT_REJECT:
- return "soft reject";
+ return "soft_reject";
case METRIC_ACTION_REWRITE_SUBJECT:
- return "rewrite subject";
+ return "rewrite_subject";
case METRIC_ACTION_ADD_HEADER:
- return "add header";
+ return "add_header";
case METRIC_ACTION_GREYLIST:
return "greylist";
case METRIC_ACTION_NOACTION:
diff --git a/src/json/jansson.h b/src/json/jansson.h
index 0d859f384..a66fc363f 100644
--- a/src/json/jansson.h
+++ b/src/json/jansson.h
@@ -120,6 +120,7 @@ typedef struct {
json_t *json_loads(const char *input, json_error_t *error);
json_t *json_loadf(FILE *input, json_error_t *error);
json_t *json_load_file(const char *path, json_error_t *error);
+json_t *json_load_evbuffer(struct evbuffer *evb, json_error_t *error);
#define JSON_INDENT(n) (n & 0xFF)
diff --git a/src/json/load.c b/src/json/load.c
index 07d0b9f8b..0f0968e9e 100644
--- a/src/json/load.c
+++ b/src/json/load.c
@@ -856,3 +856,61 @@ json_load_file (const char *path, json_error_t * error)
fclose (fp);
return result;
}
+
+
+typedef struct evbuffer_data_s {
+ const char *data;
+ gsize len;
+ guint pos;
+} evbuffer_data_t;
+
+
+static int
+evbuffer_get (void *data)
+{
+ evbuffer_data_t *stream = (evbuffer_data_t *) data;
+
+ if (stream->pos >= stream->len) {
+ return EOF;
+ }
+
+ return *(stream->data + stream->pos++);
+}
+
+static int
+evbuffer_eof (void *data)
+{
+ evbuffer_data_t *stream = (evbuffer_data_t *) data;
+
+ return stream->pos >= stream->len;
+}
+
+json_t *
+json_load_evbuffer (struct evbuffer *evb, json_error_t *error)
+{
+ evbuffer_data_t stream_data;
+ lex_t lex;
+ json_t *result;
+
+ stream_data.data = EVBUFFER_DATA (evb);
+ stream_data.pos = 0;
+ stream_data.len = EVBUFFER_LENGTH (evb);
+
+ if (lex_init (&lex, evbuffer_get, evbuffer_eof, (void *)&stream_data))
+ return NULL;
+
+ result = parse_json (&lex, error);
+ if (!result)
+ goto out;
+
+ lex_scan (&lex, error);
+ if (lex.token != TOKEN_EOF) {
+ error_set (error, &lex, "end of file expected");
+ json_decref (result);
+ result = NULL;
+ }
+
+ out:
+ lex_close (&lex);
+ return result;
+}
diff --git a/src/protocol.c b/src/protocol.c
index a303b4285..ab9836e85 100644
--- a/src/protocol.c
+++ b/src/protocol.c
@@ -96,6 +96,28 @@
static GList *custom_commands = NULL;
+/* XXX: remove this legacy sometimes */
+static const gchar *
+str_action_metric_spamc (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";
+}
+
/* For default metric, dirty hack, but much faster than hash lookup */
static double default_score, default_required_score;
@@ -1040,12 +1062,12 @@ print_metric_data_rspamc (struct worker_task *task, gchar *outbuf, gsize size,
if (task->pre_result.action == METRIC_ACTION_NOACTION) {
r += rspamd_snprintf (outbuf + r, size - r,
- "Action: %s" CRLF, str_action_metric (
+ "Action: %s" CRLF, str_action_metric_spamc (
METRIC_ACTION_NOACTION));
}
else {
r += rspamd_snprintf (outbuf + r, size - r,
- "Action: %s" CRLF, str_action_metric (
+ "Action: %s" CRLF, str_action_metric_spamc (
task->pre_result.action));
if (task->pre_result.str != NULL) {
r += rspamd_snprintf (outbuf + r, size - r,
@@ -1086,7 +1108,7 @@ print_metric_data_rspamc (struct worker_task *task, gchar *outbuf, gsize size,
metric_res->score, ms);
}
r += rspamd_snprintf (outbuf + r, size - r,
- "Action: %s" CRLF, str_action_metric (action));
+ "Action: %s" CRLF, str_action_metric_spamc (action));
}
if (action == METRIC_ACTION_REWRITE_SUBJECT && metric_res->metric->subject != NULL) {
r += rspamd_snprintf (outbuf + r, size - r,
diff --git a/src/webui.c b/src/webui.c
index e77aeaa95..7e3963b64 100644
--- a/src/webui.c
+++ b/src/webui.c
@@ -36,6 +36,7 @@
#include "classifiers/classifiers.h"
#include "dynamic_cfg.h"
#include "rrd.h"
+#include "json/jansson.h"
#include <evhttp.h>
#if (_EVENT_NUMERIC_VERSION > 0x02010000) && defined(HAVE_OPENSSL)
@@ -453,6 +454,42 @@ http_prepare_learn (struct evhttp_request *req, struct rspamd_webui_worker_ctx *
return cbdata;
}
+/*
+ * Set metric action
+ */
+static gboolean
+http_set_metric_action (struct config_file *cfg,
+ json_t *jv, struct metric *metric, enum rspamd_metric_action act)
+{
+ gdouble actval;
+ GList *cur;
+ struct metric_action *act_found;
+
+ if (!json_is_number (jv)) {
+ msg_err ("json element data error");
+ return FALSE;
+ }
+ actval = json_number_value (jv);
+ if (metric->action == act && metric->required_score != actval) {
+ return add_dynamic_action (cfg, DEFAULT_METRIC, act, actval);
+ }
+
+ /* Try to search in all metrics */
+ /* XXX: clarify this code, currently it looks like a crap */
+ cur = metric->actions;
+ while (cur) {
+ act_found = cur->data;
+ if (act_found->action == act) {
+ if (act_found->score != actval) {
+ return add_dynamic_action (cfg, DEFAULT_METRIC, act, actval);
+ }
+ }
+ cur = g_list_next (cur);
+ }
+
+ return TRUE;
+}
+
/* Command handlers */
/*
@@ -1138,7 +1175,7 @@ http_handle_learn_ham (struct evhttp_request *req, gpointer arg)
* Save actions command handler:
* request: /saveactions
* headers: Password
- * input: json data
+ * input: json array [<spam>,<probable spam>,<greylist>]
* reply: json {"success":true} or {"error":"error message"}
*/
static void
@@ -1146,6 +1183,10 @@ http_handle_save_actions (struct evhttp_request *req, gpointer arg)
{
struct rspamd_webui_worker_ctx *ctx = arg;
struct evbuffer *evb;
+ struct metric *metric;
+ json_t *json, *jv;
+ json_error_t je;
+
evb = evbuffer_new ();
if (!evb) {
@@ -1154,7 +1195,72 @@ http_handle_save_actions (struct evhttp_request *req, gpointer arg)
return;
}
- /* XXX: add real saving */
+ metric = g_hash_table_lookup (ctx->cfg->metrics, DEFAULT_METRIC);
+ if (metric == NULL) {
+ msg_err ("cannot find default metric");
+ evbuffer_free (evb);
+ evhttp_send_reply (req, HTTP_INTERNAL, "500 default metric not defined", NULL);
+ return;
+ }
+
+ /* Now check for dynamic config */
+ if (!ctx->cfg->dynamic_conf) {
+ msg_err ("dynamic conf has not been defined");
+ evbuffer_free (evb);
+ evhttp_send_reply (req, HTTP_INTERNAL, "503 dynamic config not found, cannot save", NULL);
+ return;
+ }
+
+ /* Try to load json */
+ json = json_load_evbuffer (req->input_buffer, &je);
+ if (json == NULL) {
+ msg_err ("json load error: %s", je.text);
+ evbuffer_free (evb);
+ evhttp_send_reply (req, HTTP_INTERNAL, "504 json parse error", NULL);
+ return;
+ }
+
+ if (!json_is_array (json) || json_array_size (json) != 3) {
+ msg_err ("json data error");
+ evbuffer_free (evb);
+ json_delete (json);
+ evhttp_send_reply (req, HTTP_INTERNAL, "505 invalid json data", NULL);
+ return;
+ }
+
+ /* Now try to check what we have */
+
+ /* Spam element */
+ jv = json_array_get (json, 0);
+ if (!http_set_metric_action (ctx->cfg, jv, metric, METRIC_ACTION_REJECT)) {
+ msg_err ("json data error");
+ evbuffer_free (evb);
+ json_delete (json);
+ evhttp_send_reply (req, HTTP_INTERNAL, "506 cannot set action's value", NULL);
+ return;
+ }
+
+ /* Probable spam */
+ jv = json_array_get (json, 1);
+ if (!http_set_metric_action (ctx->cfg, jv, metric, METRIC_ACTION_ADD_HEADER)) {
+ msg_err ("json data error");
+ evbuffer_free (evb);
+ json_delete (json);
+ evhttp_send_reply (req, HTTP_INTERNAL, "506 cannot set action's value", NULL);
+ return;
+ }
+
+ /* Greylist */
+ jv = json_array_get (json, 2);
+ if (!http_set_metric_action (ctx->cfg, jv, metric, METRIC_ACTION_GREYLIST)) {
+ msg_err ("json data error");
+ evbuffer_free (evb);
+ json_delete (json);
+ evhttp_send_reply (req, HTTP_INTERNAL, "506 cannot set action's value", NULL);
+ return;
+ }
+
+ dump_dynamic_config (ctx->cfg);
evbuffer_add_printf (evb, "{\"success\":true}" CRLF);
evhttp_add_header (req->output_headers, "Connection", "close");
@@ -1175,6 +1281,12 @@ http_handle_save_symbols (struct evhttp_request *req, gpointer arg)
{
struct rspamd_webui_worker_ctx *ctx = arg;
struct evbuffer *evb;
+ struct metric *metric;
+ struct symbol *sym;
+ json_t *json, *jv, *jname, *jvalue;
+ json_error_t je;
+ guint i, len;
+ gdouble val;
evb = evbuffer_new ();
if (!evb) {
@@ -1183,7 +1295,69 @@ http_handle_save_symbols (struct evhttp_request *req, gpointer arg)
return;
}
- /* XXX: add real saving */
+ metric = g_hash_table_lookup (ctx->cfg->metrics, DEFAULT_METRIC);
+ if (metric == NULL) {
+ msg_err ("cannot find default metric");
+ evbuffer_free (evb);
+ evhttp_send_reply (req, HTTP_INTERNAL, "500 default metric not defined", NULL);
+ return;
+ }
+
+ /* Now check for dynamic config */
+ if (!ctx->cfg->dynamic_conf) {
+ msg_err ("dynamic conf has not been defined");
+ evbuffer_free (evb);
+ evhttp_send_reply (req, HTTP_INTERNAL, "503 dynamic config not found, cannot save", NULL);
+ return;
+ }
+
+ /* Try to load json */
+ json = json_load_evbuffer (req->input_buffer, &je);
+ if (json == NULL) {
+ msg_err ("json load error: %s", je.text);
+ evbuffer_free (evb);
+ evhttp_send_reply (req, HTTP_INTERNAL, "504 json parse error", NULL);
+ return;
+ }
+
+ if (!json_is_array (json) || (len = json_array_size (json)) == 0) {
+ msg_err ("json data error");
+ evbuffer_free (evb);
+ json_delete (json);
+ evhttp_send_reply (req, HTTP_INTERNAL, "505 invalid json data", NULL);
+ return;
+ }
+
+ /* Iterate over all elements */
+ for (i = 0; i < len; i ++) {
+ jv = json_array_get (json, i);
+ if (!json_is_object (jv)) {
+ msg_err ("json array data error");
+ evbuffer_free (evb);
+ json_delete (json);
+ evhttp_send_reply (req, HTTP_INTERNAL, "505 invalid json data", NULL);
+ return;
+ }
+ jname = json_object_get (jv, "name");
+ jvalue = json_object_get (jv, "value");
+ if (!json_is_string (jname) || !json_is_number (jvalue)) {
+ msg_err ("json object data error");
+ evbuffer_free (evb);
+ json_delete (json);
+ evhttp_send_reply (req, HTTP_INTERNAL, "505 invalid json data", NULL);
+ return;
+ }
+ val = json_number_value (jvalue);
+ sym = g_hash_table_lookup (metric->symbols, json_string_value (jname));
+ if (sym && fabs (sym->score - val) > 0.01) {
+ if (!add_dynamic_symbol (ctx->cfg, DEFAULT_METRIC, sym->name, val)) {
+ evbuffer_free (evb);
+ json_delete (json);
+ evhttp_send_reply (req, HTTP_INTERNAL, "506 cannot write symbol's value", NULL);
+ return;
+ }
+ }
+ }
evbuffer_add_printf (evb, "{\"success\":true}" CRLF);
evhttp_add_header (req->output_headers, "Connection", "close");
@@ -1210,6 +1384,7 @@ http_handle_save_map (struct evhttp_request *req, gpointer arg)
gchar *errstr;
guint32 id;
gboolean found = FALSE;
+ gint fd;
if (!http_check_password (ctx, req)) {
@@ -1252,13 +1427,42 @@ http_handle_save_map (struct evhttp_request *req, gpointer arg)
}
if (!found) {
- msg_info ("map not found");
+ msg_info ("map not found: %d", id);
evbuffer_free (evb);
evhttp_send_reply (req, HTTP_NOTFOUND, "404 map not found", NULL);
return;
}
- /* XXX: add real saving */
+ if (g_atomic_int_get (map->locked)) {
+ msg_info ("map locked: %s", map->uri);
+ evbuffer_free (evb);
+ evhttp_send_reply (req, HTTP_NOTFOUND, "404 map is locked", NULL);
+ return;
+ }
+
+ /* Set lock */
+ g_atomic_int_set (map->locked, 1);
+ fd = open (map->uri, O_WRONLY | O_TRUNC);
+ if (fd == -1) {
+ g_atomic_int_set (map->locked, 0);
+ msg_info ("map %s open error: %s", map->uri, strerror (errno));
+ evbuffer_free (evb);
+ evhttp_send_reply (req, HTTP_NOTFOUND, "404 map open error", NULL);
+ return;
+ }
+
+ if (evbuffer_write (req->input_buffer, fd) == -1) {
+ close (fd);
+ g_atomic_int_set (map->locked, 0);
+ msg_info ("map %s open error: %s", map->uri, strerror (errno));
+ evbuffer_free (evb);
+ evhttp_send_reply (req, HTTP_INTERNAL, "500 map open error", NULL);
+ return;
+ }
+
+ /* Close and unlock */
+ close (fd);
+ g_atomic_int_set (map->locked, 0);
evbuffer_add_printf (evb, "{\"success\":true}" CRLF);
evhttp_add_header (req->output_headers, "Connection", "close");