diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/controller.c | 9 | ||||
-rw-r--r-- | src/dynamic_cfg.c | 20 | ||||
-rw-r--r-- | src/dynamic_cfg.h | 2 | ||||
-rw-r--r-- | src/filter.c | 6 | ||||
-rw-r--r-- | src/json/jansson.h | 1 | ||||
-rw-r--r-- | src/json/load.c | 58 | ||||
-rw-r--r-- | src/protocol.c | 28 | ||||
-rw-r--r-- | src/webui.c | 214 |
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"); |