Browse Source

Add saving of actions, symbols and maps.

tags/0.5.4
Vsevolod Stakhov 11 years ago
parent
commit
823c263b9d
8 changed files with 312 additions and 26 deletions
  1. 8
    1
      src/controller.c
  2. 7
    13
      src/dynamic_cfg.c
  3. 1
    1
      src/dynamic_cfg.h
  4. 3
    3
      src/filter.c
  5. 1
    0
      src/json/jansson.h
  6. 58
    0
      src/json/load.c
  7. 25
    3
      src/protocol.c
  8. 209
    5
      src/webui.c

+ 8
- 1
src/controller.c View File

@@ -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);

+ 7
- 13
src/dynamic_cfg.c View File

@@ -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);

+ 1
- 1
src/dynamic_cfg.h View File

@@ -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_ */

+ 3
- 3
src/filter.c View File

@@ -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:

+ 1
- 0
src/json/jansson.h View File

@@ -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)


+ 58
- 0
src/json/load.c View File

@@ -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;
}

+ 25
- 3
src/protocol.c View File

@@ -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,

+ 209
- 5
src/webui.c View File

@@ -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");

Loading…
Cancel
Save