aboutsummaryrefslogtreecommitdiffstats
path: root/src/webui.c
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@rambler-co.ru>2013-01-08 20:08:17 +0400
committerVsevolod Stakhov <vsevolod@rambler-co.ru>2013-01-08 20:08:17 +0400
commit51189e8edb66a504bff39b10621171a6fbb4a869 (patch)
tree60e44c8a456d0b6f33205fcd9e060d5ee987dd65 /src/webui.c
parente43f85561bb422b8b8e3de1015876207388549cd (diff)
downloadrspamd-51189e8edb66a504bff39b10621171a6fbb4a869.tar.gz
rspamd-51189e8edb66a504bff39b10621171a6fbb4a869.zip
Add real learning via web interface.
Add sekeleton of save actions and symbols paths.
Diffstat (limited to 'src/webui.c')
-rw-r--r--src/webui.c276
1 files changed, 262 insertions, 14 deletions
diff --git a/src/webui.c b/src/webui.c
index 8f2c4216b..016c109c3 100644
--- a/src/webui.c
+++ b/src/webui.c
@@ -71,6 +71,8 @@
#define PATH_HISTORY "/history"
#define PATH_LEARN_SPAM "/learnspam"
#define PATH_LEARN_HAM "/learnham"
+#define PATH_SAVE_ACTIONS "/saveactions"
+#define PATH_SAVE_SYMBOLS "/savesymbols"
/* Graph colors */
#define COLOR_CLEAN "#58A458"
@@ -116,6 +118,8 @@ struct rspamd_webui_worker_ctx {
gchar *ssl_cert;
/* SSL private key */
gchar *ssl_key;
+ /* Worker */
+ struct rspamd_worker *worker;
};
static sig_atomic_t wanna_die = 0;
@@ -280,6 +284,177 @@ http_check_password (struct rspamd_webui_worker_ctx *ctx, struct evhttp_request
return TRUE;
}
+struct learn_callback_data {
+ struct worker_task *task;
+ struct evhttp_request *req;
+ struct classifier_config *classifier;
+ struct rspamd_webui_worker_ctx *ctx;
+ gboolean is_spam;
+ gboolean processed;
+};
+
+/*
+ * Called before destroying of task's session, here we can perform learning
+ */
+static void
+http_learn_task_free (gpointer arg)
+{
+ struct learn_callback_data *cbdata = arg;
+ GError *err = NULL;
+ struct evbuffer *evb;
+
+ if (cbdata->processed) {
+ evb = evbuffer_new ();
+ if (!evb) {
+ msg_err ("cannot allocate evbuffer for reply");
+ evhttp_send_reply (cbdata->req, HTTP_INTERNAL, "500 insufficient memory", NULL);
+ free_task_hard (cbdata->task);
+ return;
+ }
+
+ if (!learn_task_spam (cbdata->classifier, cbdata->task, cbdata->is_spam, &err)) {
+ evbuffer_add_printf (evb, "{\"error\":\"%s\"}" CRLF, err->message);
+ evhttp_add_header (cbdata->req->output_headers, "Connection", "close");
+ http_calculate_content_length (evb, cbdata->req);
+
+ evhttp_send_reply (cbdata->req, HTTP_INTERNAL + err->code, err->message, evb);
+ evbuffer_free (evb);
+ g_error_free (err);
+ free_task_hard (cbdata->task);
+ return;
+ }
+ /* Successful learn */
+ evbuffer_add_printf (evb, "{\"success\":true}" CRLF);
+ evhttp_add_header (cbdata->req->output_headers, "Connection", "close");
+ http_calculate_content_length (evb, cbdata->req);
+
+ evhttp_send_reply (cbdata->req, HTTP_OK, "OK", evb);
+ evbuffer_free (evb);
+ g_error_free (err);
+ free_task_hard (cbdata->task);
+ }
+ /* If task is not processed, just do nothing */
+ return;
+}
+
+/*
+ * Handler of session destroying
+ */
+static void
+http_learn_task_event_helper (int fd, short what, gpointer arg)
+{
+ struct learn_callback_data *cbdata = arg;
+
+ msg_info ("hui");
+
+ destroy_session (cbdata->task->s);
+}
+
+/*
+ * Called if all filters are processed, non-threaded and simple version
+ */
+static gboolean
+http_learn_task_fin (gpointer arg)
+{
+ struct learn_callback_data *cbdata = arg;
+ static struct timeval tv = {.tv_sec = 0, .tv_usec = 0 };
+
+ if (cbdata->task->state != WRITING_REPLY) {
+ cbdata->task->state = WRITE_REPLY;
+ }
+
+ /* Check if we have all events finished */
+ if (cbdata->task->state != WRITING_REPLY) {
+ if (cbdata->task->fin_callback) {
+ cbdata->task->fin_callback (cbdata->task->fin_arg);
+ }
+ else {
+ /*
+ * XXX: we cannot call this one directly as session mutex is locked, so we need to plan some event
+ * unfortunately
+ * destroy_session (cbdata->task->s);
+ */
+ event_base_once (cbdata->ctx->ev_base, -1, EV_TIMEOUT, http_learn_task_event_helper, cbdata, &tv);
+ }
+ }
+
+ return TRUE;
+}
+
+/*
+ * Called if session was restored inside fin callback
+ */
+static void
+http_learn_task_restore (gpointer arg)
+{
+ struct learn_callback_data *cbdata = arg;
+
+ /* Special state */
+ cbdata->task->state = WRITING_REPLY;
+}
+
+/* Prepare callback data for learn */
+static struct learn_callback_data*
+http_prepare_learn (struct evhttp_request *req, struct rspamd_webui_worker_ctx *ctx, struct evbuffer *in, gboolean is_spam, GError **err)
+{
+ struct worker_task *task;
+ struct learn_callback_data *cbdata;
+ struct classifier_config *cl;
+
+ /* Check for data */
+ if (EVBUFFER_LENGTH (in) == 0) {
+ g_set_error (err, g_quark_from_static_string ("webui"), 500, "no message for learning");
+ return NULL;
+ }
+ /* Check for classifier */
+ cl = find_classifier_conf (ctx->cfg, "bayes");
+ if (cl == NULL) {
+ g_set_error (err, g_quark_from_static_string ("webui"), 500, "classifier not found");
+ return NULL;
+ }
+ /* Prepare task */
+ task = construct_task (ctx->worker);
+ if (task == NULL) {
+ g_set_error (err, g_quark_from_static_string ("webui"), 500, "task cannot be created");
+ return NULL;
+ }
+
+ task->msg = memory_pool_alloc (task->task_pool, sizeof (f_str_t));
+ task->msg->begin = EVBUFFER_DATA (in);
+ task->msg->len = EVBUFFER_LENGTH (in);
+
+ task->resolver = ctx->resolver;
+ task->ev_base = ctx->ev_base;
+
+ cbdata = memory_pool_alloc (task->task_pool, sizeof (struct learn_callback_data));
+ cbdata->task = task;
+ cbdata->req = req;
+ cbdata->classifier = cl;
+ cbdata->ctx = ctx;
+ cbdata->is_spam = is_spam;
+ cbdata->processed = FALSE;
+
+ if (process_message (task) == -1) {
+ msg_warn ("processing of message failed");
+ g_set_error (err, g_quark_from_static_string ("webui"), 500, "message cannot be processed");
+ free_task_hard (task);
+ return NULL;
+ }
+
+ /* Set up async session */
+ task->s = new_async_session (task->task_pool, http_learn_task_fin, http_learn_task_restore, http_learn_task_free, cbdata);
+ if (process_filters (task) == -1) {
+ msg_warn ("filtering of message failed");
+ g_set_error (err, g_quark_from_static_string ("webui"), 500, "message cannot be filtered");
+ free_task_hard (task);
+ return NULL;
+ }
+
+ cbdata->processed = TRUE;
+
+ return cbdata;
+}
+
/* Command handlers */
/*
@@ -898,27 +1073,33 @@ http_handle_learn_spam (struct evhttp_request *req, gpointer arg)
{
struct rspamd_webui_worker_ctx *ctx = arg;
struct evbuffer *evb, *inb;
+ GError *err = NULL;
+ struct learn_callback_data *cbdata;
if (!http_check_password (ctx, req)) {
return;
}
inb = req->input_buffer;
- evb = evbuffer_new ();
- if (!evb) {
- msg_err ("cannot allocate evbuffer for reply");
- evhttp_send_reply (req, HTTP_INTERNAL, "500 insufficient memory", NULL);
- return;
- }
-
- /* XXX: Add real learning here */
- evbuffer_add_printf (evb, "{\"success\":true}" CRLF);
- evhttp_add_header (req->output_headers, "Connection", "close");
- http_calculate_content_length (evb, req);
+ cbdata = http_prepare_learn (req, ctx, inb, TRUE, &err);
+ if (cbdata == NULL) {
+ /* Handle error */
+ evb = evbuffer_new ();
+ if (!evb) {
+ msg_err ("cannot allocate evbuffer for reply");
+ evhttp_send_reply (req, HTTP_INTERNAL, "500 insufficient memory", NULL);
+ return;
+ }
+ evbuffer_add_printf (evb, "{\"error\":\"%s\"}" CRLF, err->message);
+ evhttp_add_header (req->output_headers, "Connection", "close");
+ http_calculate_content_length (evb, req);
- evhttp_send_reply (req, HTTP_OK, "OK", evb);
- evbuffer_free (evb);
+ evhttp_send_reply (req, err->code, err->message, evb);
+ evbuffer_free (evb);
+ g_error_free (err);
+ return;
+ }
}
/*
@@ -933,12 +1114,48 @@ http_handle_learn_ham (struct evhttp_request *req, gpointer arg)
{
struct rspamd_webui_worker_ctx *ctx = arg;
struct evbuffer *evb, *inb;
+ GError *err = NULL;
+ struct learn_callback_data *cbdata;
if (!http_check_password (ctx, req)) {
return;
}
inb = req->input_buffer;
+
+ cbdata = http_prepare_learn (req, ctx, inb, FALSE, &err);
+ if (cbdata == NULL) {
+ /* Handle error */
+ evb = evbuffer_new ();
+ if (!evb) {
+ msg_err ("cannot allocate evbuffer for reply");
+ evhttp_send_reply (req, HTTP_INTERNAL, "500 insufficient memory", NULL);
+ return;
+ }
+ evbuffer_add_printf (evb, "{\"error\":\"%s\"}" CRLF, err->message);
+ evhttp_add_header (req->output_headers, "Connection", "close");
+ http_calculate_content_length (evb, req);
+
+ evhttp_send_reply (req, err->code, err->message, evb);
+ evbuffer_free (evb);
+ g_error_free (err);
+ return;
+ }
+}
+
+/*
+ * Save actions command handler:
+ * request: /saveactions
+ * headers: Password
+ * input: json data
+ * reply: json {"success":true} or {"error":"error message"}
+ */
+static void
+http_handle_save_actions (struct evhttp_request *req, gpointer arg)
+{
+ struct rspamd_webui_worker_ctx *ctx = arg;
+ struct evbuffer *evb;
+
evb = evbuffer_new ();
if (!evb) {
msg_err ("cannot allocate evbuffer for reply");
@@ -946,12 +1163,40 @@ http_handle_learn_ham (struct evhttp_request *req, gpointer arg)
return;
}
- /* XXX: Add real learning here */
+ /* XXX: add real saving */
evbuffer_add_printf (evb, "{\"success\":true}" CRLF);
evhttp_add_header (req->output_headers, "Connection", "close");
http_calculate_content_length (evb, req);
+ evhttp_send_reply (req, HTTP_OK, "OK", evb);
+ evbuffer_free (evb);
+}
+
+/*
+ * Save symbols command handler:
+ * request: /savesymbols
+ * headers: Password
+ * input: json data
+ * reply: json {"success":true} or {"error":"error message"}
+ */
+static void
+http_handle_save_symbols (struct evhttp_request *req, gpointer arg)
+{
+ struct rspamd_webui_worker_ctx *ctx = arg;
+ struct evbuffer *evb;
+ evb = evbuffer_new ();
+ if (!evb) {
+ msg_err ("cannot allocate evbuffer for reply");
+ evhttp_send_reply (req, HTTP_INTERNAL, "500 insufficient memory", NULL);
+ return;
+ }
+
+ /* XXX: add real saving */
+
+ evbuffer_add_printf (evb, "{\"success\":true}" CRLF);
+ evhttp_add_header (req->output_headers, "Connection", "close");
+ http_calculate_content_length (evb, req);
evhttp_send_reply (req, HTTP_OK, "OK", evb);
evbuffer_free (evb);
}
@@ -1011,6 +1256,7 @@ start_webui_worker (struct rspamd_worker *worker)
signal_add (&worker->sig_ev_usr1, NULL);
ctx->start_time = time (NULL);
+ ctx->worker = worker;
/* Accept event */
ctx->http = evhttp_new (ctx->ev_base);
evhttp_accept_socket (ctx->http, worker->cf->listen_sock);
@@ -1039,6 +1285,8 @@ start_webui_worker (struct rspamd_worker *worker)
evhttp_set_cb (ctx->http, PATH_HISTORY, http_handle_history, ctx);
evhttp_set_cb (ctx->http, PATH_LEARN_SPAM, http_handle_learn_spam, ctx);
evhttp_set_cb (ctx->http, PATH_LEARN_HAM, http_handle_learn_ham, ctx);
+ evhttp_set_cb (ctx->http, PATH_SAVE_ACTIONS, http_handle_save_actions, ctx);
+ evhttp_set_cb (ctx->http, PATH_SAVE_SYMBOLS, http_handle_save_symbols, ctx);
ctx->resolver = dns_resolver_init (ctx->ev_base, worker->srv->cfg);