* @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;
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;
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);
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;
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,
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,
#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)
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 */
/*
* 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
{
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) {
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");
{
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) {
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");
gchar *errstr;
guint32 id;
gboolean found = FALSE;
+ gint fd;
if (!http_check_password (ctx, req)) {
}
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");