From 6af303bd3ab37702ad4a6d50b74e95d2a2808e85 Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Sun, 6 Jan 2013 20:33:31 +0400 Subject: [PATCH] Add /maps and /getmap commands to webui. Identify maps by id. Initialize secure random numbers using openssl. Add description to maps. --- src/cfg_utils.c | 6 ++ src/cfg_xml.c | 4 +- src/dynamic_cfg.c | 2 +- src/logger.c | 3 +- src/lua/lua_config.c | 15 ++-- src/main.c | 20 +++++ src/map.c | 24 +++--- src/map.h | 10 ++- src/plugins/dkim_check.c | 4 +- src/plugins/fuzzy_check.c | 2 +- src/plugins/regexp.c | 2 +- src/plugins/spf.c | 2 +- src/plugins/surbl.c | 6 +- src/settings.c | 4 +- src/settings.h | 2 +- src/view.c | 10 +-- src/webui.c | 157 ++++++++++++++++++++++++++++++++++++++ 17 files changed, 235 insertions(+), 38 deletions(-) diff --git a/src/cfg_utils.c b/src/cfg_utils.c index 7b1910324..a0191b103 100644 --- a/src/cfg_utils.c +++ b/src/cfg_utils.c @@ -494,11 +494,17 @@ parse_flag (const gchar *str) if (g_ascii_strncasecmp (str, "no", len) == 0) { return 0; } + else if (g_ascii_strncasecmp (str, "on", len) == 0) { + return 1; + } break; case 3: if (g_ascii_strncasecmp (str, "yes", len) == 0) { return 1; } + else if (g_ascii_strncasecmp (str, "off", len) == 0) { + return 0; + } break; case 4: if (g_ascii_strncasecmp (str, "true", len) == 0) { diff --git a/src/cfg_xml.c b/src/cfg_xml.c index 6ae3ce7c3..f81492d77 100644 --- a/src/cfg_xml.c +++ b/src/cfg_xml.c @@ -1458,7 +1458,7 @@ handle_view_symbols (struct config_file *cfg, struct rspamd_xml_userdata *ctx, G gboolean handle_user_settings (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, gint offset) { - if (!read_settings (data, cfg, cfg->user_settings)) { + if (!read_settings (data, "Users' settings", cfg, cfg->user_settings)) { msg_err ("cannot read settings %s", data); return FALSE; } @@ -1469,7 +1469,7 @@ handle_user_settings (struct config_file *cfg, struct rspamd_xml_userdata *ctx, gboolean handle_domain_settings (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, gint offset) { - if (!read_settings (data, cfg, cfg->domain_settings)) { + if (!read_settings (data, "Domains' settings", cfg, cfg->domain_settings)) { msg_err ("cannot read settings %s", data); return FALSE; } diff --git a/src/dynamic_cfg.c b/src/dynamic_cfg.c index 279b1e0a9..7b9596c6c 100644 --- a/src/dynamic_cfg.c +++ b/src/dynamic_cfg.c @@ -334,7 +334,7 @@ init_dynamic_config (struct config_file *cfg) jb->buf = NULL; jb->cfg = cfg; *pjb = jb; - if (!add_map (cfg, cfg->dynamic_conf, json_config_read_cb, json_config_fin_cb, (void **)pjb)) { + if (!add_map (cfg, cfg->dynamic_conf, "Dynamic configuration map", json_config_read_cb, json_config_fin_cb, (void **)pjb)) { msg_err ("cannot add map for configuration %s", cfg->dynamic_conf); } } diff --git a/src/logger.c b/src/logger.c index ea47b98b9..88636d2e3 100644 --- a/src/logger.c +++ b/src/logger.c @@ -308,7 +308,8 @@ rspamd_set_logger (enum rspamd_log_type type, GQuark ptype, struct rspamd_main * radix_tree_free (rspamd->logger->debug_ip); } rspamd->logger->debug_ip = radix_tree_create (); - if (!add_map (rspamd->cfg, rspamd->cfg->debug_ip_map, read_radix_list, fin_radix_list, (void **)&rspamd->logger->debug_ip)) { + if (!add_map (rspamd->cfg, rspamd->cfg->debug_ip_map, "IP addresses for which debug logs are enabled", + read_radix_list, fin_radix_list, (void **)&rspamd->logger->debug_ip)) { /* Try to parse it as list */ strvec = g_strsplit_set (rspamd->cfg->debug_ip_map, ",; ", 0); num = g_strv_length (strvec); diff --git a/src/lua/lua_config.c b/src/lua/lua_config.c index ba9dfab37..b84879df0 100644 --- a/src/lua/lua_config.c +++ b/src/lua/lua_config.c @@ -560,14 +560,15 @@ static gint lua_config_add_radix_map (lua_State *L) { struct config_file *cfg = lua_check_config (L); - const gchar *map_line; + const gchar *map_line, *description; radix_tree_t **r, ***ud; if (cfg) { map_line = luaL_checkstring (L, 2); + description = lua_tostring (L, 3); r = memory_pool_alloc (cfg->cfg_pool, sizeof (radix_tree_t *)); *r = radix_tree_create (); - if (!add_map (cfg, map_line, read_radix_list, fin_radix_list, (void **)r)) { + if (!add_map (cfg, map_line, description, read_radix_list, fin_radix_list, (void **)r)) { msg_warn ("invalid radix map %s", map_line); radix_tree_free (*r); lua_pushnil (L); @@ -589,14 +590,15 @@ static gint lua_config_add_hash_map (lua_State *L) { struct config_file *cfg = lua_check_config (L); - const gchar *map_line; + const gchar *map_line, *description; GHashTable **r, ***ud; if (cfg) { map_line = luaL_checkstring (L, 2); + description = lua_tostring (L, 3); r = memory_pool_alloc (cfg->cfg_pool, sizeof (GHashTable *)); *r = g_hash_table_new (rspamd_strcase_hash, rspamd_strcase_equal); - if (!add_map (cfg, map_line, read_host_list, fin_host_list, (void **)r)) { + if (!add_map (cfg, map_line, description, read_host_list, fin_host_list, (void **)r)) { msg_warn ("invalid hash map %s", map_line); g_hash_table_destroy (*r); lua_pushnil (L); @@ -619,14 +621,15 @@ static gint lua_config_add_kv_map (lua_State *L) { struct config_file *cfg = lua_check_config (L); - const gchar *map_line; + const gchar *map_line, *description; GHashTable **r, ***ud; if (cfg) { map_line = luaL_checkstring (L, 2); + description = lua_tostring (L, 3); r = memory_pool_alloc (cfg->cfg_pool, sizeof (GHashTable *)); *r = g_hash_table_new (rspamd_strcase_hash, rspamd_strcase_equal); - if (!add_map (cfg, map_line, read_kv_list, fin_kv_list, (void **)r)) { + if (!add_map (cfg, map_line, description, read_kv_list, fin_kv_list, (void **)r)) { msg_warn ("invalid hash map %s", map_line); g_hash_table_destroy (*r); lua_pushnil (L); diff --git a/src/main.c b/src/main.c index f85fe7ade..94001298d 100644 --- a/src/main.c +++ b/src/main.c @@ -34,6 +34,10 @@ #include "cfg_xml.h" #include "symbols_cache.h" #include "lua/lua_common.h" +#ifdef HAVE_OPENSSL +#include +#include +#endif /* 2 seconds to fork new process in place of dead one */ #define SOFT_FORK_TIME 2 @@ -861,6 +865,10 @@ main (gint argc, gchar **argv, gchar **env) GList *l; worker_t **pworker; GQuark type; +#ifdef HAVE_OPENSSL + gchar rand_bytes[sizeof (guint32)]; + guint32 rand_seed; +#endif #ifdef HAVE_SA_SIGINFO signals_info = g_queue_new (); @@ -912,6 +920,18 @@ main (gint argc, gchar **argv, gchar **env) setlocale (LC_TIME, "C"); #endif + /* Init random generator */ +#ifdef HAVE_OPENSSL + if (RAND_bytes (rand_bytes, sizeof (rand_bytes)) != 1) { + msg_err ("cannot seed random generator using openssl: %s, using time", ERR_error_string (ERR_get_error (), NULL)); + g_random_set_seed (time (NULL)); + } + else { + memcpy (&rand_seed, rand_bytes, sizeof (guint32)); + g_random_set_seed (rand_seed); + } +#endif + /* First set logger to console logger */ rspamd_set_logger (RSPAMD_LOG_CONSOLE, type, rspamd_main); (void)open_log (rspamd_main->logger); diff --git a/src/map.c b/src/map.c index 1ee6e7de4..09b4df8df 100644 --- a/src/map.c +++ b/src/map.c @@ -915,7 +915,7 @@ start_map_watch (struct config_file *cfg, struct event_base *ev_base) while (cur) { map = cur->data; map->ev_base = ev_base; - if (map->protocol == PROTO_FILE) { + if (map->protocol == MAP_PROTO_FILE) { evtimer_set (&map->ev, file_callback, map); event_base_set (map->ev_base, &map->ev); /* Read initial data */ @@ -929,7 +929,7 @@ start_map_watch (struct config_file *cfg, struct event_base *ev_base) map->tv.tv_usec = 0; evtimer_add (&map->ev, &map->tv); } - else if (map->protocol == PROTO_HTTP) { + else if (map->protocol == MAP_PROTO_HTTP) { evtimer_set (&map->ev, http_callback, map); event_base_set (map->ev_base, &map->ev); /* Read initial data */ @@ -959,19 +959,19 @@ check_map_proto (const gchar *map_line, gint *res, const gchar **pos) { if (g_ascii_strncasecmp (map_line, "http://", sizeof ("http://") - 1) == 0) { if (res && pos) { - *res = PROTO_HTTP; + *res = MAP_PROTO_HTTP; *pos = map_line + sizeof ("http://") - 1; } } else if (g_ascii_strncasecmp (map_line, "file://", sizeof ("file://") - 1) == 0) { if (res && pos) { - *res = PROTO_FILE; + *res = MAP_PROTO_FILE; *pos = map_line + sizeof ("file://") - 1; } } else if (*map_line == '/') { /* Trivial file case */ - *res = PROTO_FILE; + *res = MAP_PROTO_FILE; *pos = map_line; } else { @@ -983,11 +983,12 @@ check_map_proto (const gchar *map_line, gint *res, const gchar **pos) } gboolean -add_map (struct config_file *cfg, const gchar *map_line, map_cb_t read_callback, map_fin_cb_t fin_callback, void **user_data) +add_map (struct config_file *cfg, const gchar *map_line, const gchar *description, + map_cb_t read_callback, map_fin_cb_t fin_callback, void **user_data) { struct rspamd_map *new_map; enum fetch_proto proto; - const gchar *def, *p, *hostend; + const gchar *def, *p, *hostend; struct file_map_data *fdata; struct http_map_data *hdata; gchar portbuf[6]; @@ -1008,9 +1009,14 @@ add_map (struct config_file *cfg, const gchar *map_line, map_cb_t read_callback, new_map->user_data = user_data; new_map->protocol = proto; new_map->cfg = cfg; + new_map->uri = memory_pool_strdup (cfg->cfg_pool, proto == MAP_PROTO_FILE ? def : map_line); + new_map->id = g_random_int (); + if (description != NULL) { + new_map->description = memory_pool_strdup (cfg->cfg_pool, description); + } /* Now check for each proto separately */ - if (proto == PROTO_FILE) { + if (proto == MAP_PROTO_FILE) { fdata = memory_pool_alloc0 (cfg->map_pool, sizeof (struct file_map_data)); if (access (def, R_OK) == -1) { if (errno != ENOENT) { @@ -1028,7 +1034,7 @@ add_map (struct config_file *cfg, const gchar *map_line, map_cb_t read_callback, fdata->filename = memory_pool_strdup (cfg->map_pool, def); new_map->map_data = fdata; } - else if (proto == PROTO_HTTP) { + else if (proto == MAP_PROTO_HTTP) { hdata = memory_pool_alloc0 (cfg->map_pool, sizeof (struct http_map_data)); /* Try to search port */ if ((p = strchr (def, ':')) != NULL) { diff --git a/src/map.h b/src/map.h index 69fe5f87c..5005786dc 100644 --- a/src/map.h +++ b/src/map.h @@ -12,8 +12,8 @@ */ enum fetch_proto { - PROTO_FILE, - PROTO_HTTP, + MAP_PROTO_FILE, + MAP_PROTO_HTTP, }; /** @@ -70,6 +70,9 @@ struct rspamd_map { struct timeval tv; struct event_base *ev_base; void *map_data; + gchar *uri; + gchar *description; + guint32 id; }; /** @@ -79,7 +82,8 @@ gboolean check_map_proto (const gchar *map_line, gint *res, const gchar **pos); /** * Add map from line */ -gboolean add_map (struct config_file *cfg, const gchar *map_line, map_cb_t read_callback, map_fin_cb_t fin_callback, void **user_data); +gboolean add_map (struct config_file *cfg, const gchar *map_line, const gchar *description, + map_cb_t read_callback, map_fin_cb_t fin_callback, void **user_data); /** * Start watching of maps by adding events to libevent event loop diff --git a/src/plugins/dkim_check.c b/src/plugins/dkim_check.c index 7bb32db55..a1a022665 100644 --- a/src/plugins/dkim_check.c +++ b/src/plugins/dkim_check.c @@ -158,12 +158,12 @@ dkim_module_config (struct config_file *cfg) dkim_module_ctx->time_jitter = DEFAULT_TIME_JITTER; } if ((value = get_module_opt (cfg, "dkim", "whitelist")) != NULL) { - if (! add_map (cfg, value, read_radix_list, fin_radix_list, (void **)&dkim_module_ctx->whitelist_ip)) { + if (! add_map (cfg, value, "DKIM whitelist", read_radix_list, fin_radix_list, (void **)&dkim_module_ctx->whitelist_ip)) { msg_warn ("cannot load whitelist from %s", value); } } if ((value = get_module_opt (cfg, "dkim", "domains")) != NULL) { - if (! add_map (cfg, value, read_kv_list, fin_kv_list, (void **)&dkim_module_ctx->dkim_domains)) { + if (! add_map (cfg, value, "DKIM domains", read_kv_list, fin_kv_list, (void **)&dkim_module_ctx->dkim_domains)) { msg_warn ("cannot load dkim domains list from %s", value); } else { diff --git a/src/plugins/fuzzy_check.c b/src/plugins/fuzzy_check.c index 2f61ac1b3..984163858 100644 --- a/src/plugins/fuzzy_check.c +++ b/src/plugins/fuzzy_check.c @@ -419,7 +419,7 @@ fuzzy_check_module_config (struct config_file *cfg) if ((value = get_module_opt (cfg, "fuzzy_check", "whitelist")) != NULL) { fuzzy_module_ctx->whitelist = radix_tree_create (); - if (!add_map (cfg, value, read_radix_list, fin_radix_list, (void **)&fuzzy_module_ctx->whitelist)) { + if (!add_map (cfg, value, "Fuzzy whitelist", read_radix_list, fin_radix_list, (void **)&fuzzy_module_ctx->whitelist)) { msg_err ("cannot add whitelist '%s'", value); } } diff --git a/src/plugins/regexp.c b/src/plugins/regexp.c index ebb4ff648..9dce07772 100644 --- a/src/plugins/regexp.c +++ b/src/plugins/regexp.c @@ -611,7 +611,7 @@ regexp_module_config (struct config_file *cfg) jb->buf = NULL; jb->cfg = cfg; *pjb = jb; - if (!add_map (cfg, value, json_regexp_read_cb, json_regexp_fin_cb, (void **)pjb)) { + if (!add_map (cfg, value, "Dynamic regexp rules", json_regexp_read_cb, json_regexp_fin_cb, (void **)pjb)) { msg_err ("cannot add map %s", value); } } diff --git a/src/plugins/spf.c b/src/plugins/spf.c index d10498699..273243c13 100644 --- a/src/plugins/spf.c +++ b/src/plugins/spf.c @@ -138,7 +138,7 @@ spf_module_config (struct config_file *cfg) cache_expire = DEFAULT_CACHE_MAXAGE; } if ((value = get_module_opt (cfg, "spf", "whitelist")) != NULL) { - if (! add_map (cfg, value, read_radix_list, fin_radix_list, (void **)&spf_module_ctx->whitelist_ip)) { + if (! add_map (cfg, value, "SPF whitelist", read_radix_list, fin_radix_list, (void **)&spf_module_ctx->whitelist_ip)) { msg_warn ("cannot load whitelist from %s", value); } } diff --git a/src/plugins/surbl.c b/src/plugins/surbl.c index ba2df2dd8..57bd257c7 100644 --- a/src/plugins/surbl.c +++ b/src/plugins/surbl.c @@ -363,7 +363,7 @@ surbl_module_config (struct config_file *cfg) surbl_module_ctx->read_timeout = DEFAULT_REDIRECTOR_READ_TIMEOUT; } if ((value = get_module_opt (cfg, "surbl", "redirector_hosts_map")) != NULL) { - add_map (cfg, value, read_redirectors_list, fin_redirectors_list, (void **)&surbl_module_ctx->redirector_hosts); + add_map (cfg, value, "SURBL redirectors list", read_redirectors_list, fin_redirectors_list, (void **)&surbl_module_ctx->redirector_hosts); } else { surbl_module_ctx->read_timeout = DEFAULT_REDIRECTOR_READ_TIMEOUT; @@ -375,12 +375,12 @@ surbl_module_config (struct config_file *cfg) surbl_module_ctx->max_urls = DEFAULT_SURBL_MAX_URLS; } if ((value = get_module_opt (cfg, "surbl", "exceptions")) != NULL) { - if (add_map (cfg, value, read_exceptions_list, fin_exceptions_list, (void **)&surbl_module_ctx->exceptions)) { + if (add_map (cfg, value, "SURBL exceptions list", read_exceptions_list, fin_exceptions_list, (void **)&surbl_module_ctx->exceptions)) { surbl_module_ctx->tld2_file = memory_pool_strdup (surbl_module_ctx->surbl_pool, value + sizeof ("file://") - 1); } } if ((value = get_module_opt (cfg, "surbl", "whitelist")) != NULL) { - if (add_map (cfg, value, read_host_list, fin_host_list, (void **)&surbl_module_ctx->whitelist)) { + if (add_map (cfg, value, "SURBL whitelist", read_host_list, fin_host_list, (void **)&surbl_module_ctx->whitelist)) { surbl_module_ctx->whitelist_file = memory_pool_strdup (surbl_module_ctx->surbl_pool, value + sizeof ("file://") - 1); } } diff --git a/src/settings.c b/src/settings.c index 0d856bf3f..d7e96222a 100644 --- a/src/settings.c +++ b/src/settings.c @@ -348,7 +348,7 @@ json_fin_cb (memory_pool_t * pool, struct map_cb_data *data) } gboolean -read_settings (const gchar *path, struct config_file *cfg, GHashTable * table) +read_settings (const gchar *path, const gchar *description, struct config_file *cfg, GHashTable * table) { struct json_buf *jb = g_malloc (sizeof (struct json_buf)), **pjb; @@ -358,7 +358,7 @@ read_settings (const gchar *path, struct config_file *cfg, GHashTable * table) jb->buf = NULL; *pjb = jb; - if (!add_map (cfg, path, json_read_cb, json_fin_cb, (void **)pjb)) { + if (!add_map (cfg, path, description, json_read_cb, json_fin_cb, (void **)pjb)) { msg_err ("cannot add map %s", path); return FALSE; } diff --git a/src/settings.h b/src/settings.h index fb8f8681e..7f89dd4b9 100644 --- a/src/settings.h +++ b/src/settings.h @@ -20,7 +20,7 @@ struct rspamd_settings { /* * Read settings from specified path */ -gboolean read_settings (const gchar *path, struct config_file *cfg, GHashTable *table); +gboolean read_settings (const gchar *path, const gchar *description, struct config_file *cfg, GHashTable *table); /* * Init configuration structures for settings diff --git a/src/view.c b/src/view.c index b0d16dd5b..a59f3eb0b 100644 --- a/src/view.c +++ b/src/view.c @@ -55,7 +55,7 @@ add_view_from (struct rspamd_view * view, gchar *line) { struct rspamd_regexp *re = NULL; - if (add_map (view->cfg, line, read_host_list, fin_host_list, (void **)&view->from_hash)) { + if (add_map (view->cfg, line, "SMTP From view", read_host_list, fin_host_list, (void **)&view->from_hash)) { return TRUE; } else if ((re = parse_regexp (view->pool, line, TRUE)) != NULL) { @@ -71,7 +71,7 @@ add_view_rcpt (struct rspamd_view * view, gchar *line) { struct rspamd_regexp *re = NULL; - if (add_map (view->cfg, line, read_host_list, fin_host_list, (void **)&view->rcpt_hash)) { + if (add_map (view->cfg, line, "Recipients view", read_host_list, fin_host_list, (void **)&view->rcpt_hash)) { return TRUE; } else if ((re = parse_regexp (view->pool, line, TRUE)) != NULL) { @@ -88,7 +88,7 @@ add_view_symbols (struct rspamd_view * view, gchar *line) struct rspamd_regexp *re = NULL; GList *symbols; - if (add_map (view->cfg, line, read_host_list, fin_host_list, (void **)&view->symbols_hash)) { + if (add_map (view->cfg, line, "Symbols view", read_host_list, fin_host_list, (void **)&view->symbols_hash)) { return TRUE; } else if ((re = parse_regexp (view->pool, line, TRUE)) != NULL) { @@ -112,7 +112,7 @@ add_view_symbols (struct rspamd_view * view, gchar *line) gboolean add_view_ip (struct rspamd_view * view, gchar *line) { - if (add_map (view->cfg, line, read_radix_list, fin_radix_list, (void **)&view->ip_tree)) { + if (add_map (view->cfg, line, "IP view", read_radix_list, fin_radix_list, (void **)&view->ip_tree)) { return TRUE; } @@ -122,7 +122,7 @@ add_view_ip (struct rspamd_view * view, gchar *line) gboolean add_view_client_ip (struct rspamd_view * view, gchar *line) { - if (add_map (view->cfg, line, read_radix_list, fin_radix_list, (void **)&view->client_ip_tree)) { + if (add_map (view->cfg, line, "Client IP view", read_radix_list, fin_radix_list, (void **)&view->client_ip_tree)) { return TRUE; } diff --git a/src/webui.c b/src/webui.c index 05c64fc1d..326cd7639 100644 --- a/src/webui.c +++ b/src/webui.c @@ -58,6 +58,8 @@ /* HTTP paths */ #define PATH_AUTH "/login" #define PATH_SYMBOLS "/symbols" +#define PATH_MAPS "/maps" +#define PATH_GET_MAP "/getmap" gpointer init_webui_worker (void); void start_webui_worker (struct rspamd_worker *worker); @@ -253,6 +255,7 @@ http_handle_auth (struct evhttp_request *req, gpointer arg) evb = evbuffer_new (); if (!evb) { msg_err ("cannot allocate evbuffer for reply"); + evhttp_send_reply (req, HTTP_INTERNAL, "500 insufficient memory", NULL); return; } @@ -320,6 +323,7 @@ http_handle_symbols (struct evhttp_request *req, gpointer arg) evb = evbuffer_new (); if (!evb) { msg_err ("cannot allocate evbuffer for reply"); + evhttp_send_reply (req, HTTP_INTERNAL, "500", NULL); return; } @@ -363,6 +367,157 @@ http_handle_symbols (struct evhttp_request *req, gpointer arg) evbuffer_free (evb); } +/* + * Maps command handler: + * request: /maps + * headers: Password + * reply: json [ + * { + * "map": "name", + * "description": "description", + * "editable": true + * }, + * {...} + * ] + */ +static void +http_handle_maps (struct evhttp_request *req, gpointer arg) +{ + struct rspamd_webui_worker_ctx *ctx = arg; + struct evbuffer *evb; + GList *cur; + struct rspamd_map *map; + gboolean editable; + + evb = evbuffer_new (); + if (!evb) { + msg_err ("cannot allocate evbuffer for reply"); + evhttp_send_reply (req, HTTP_INTERNAL, "500 insufficient memory", NULL); + return; + } + + /* Trailer */ + evbuffer_add (evb, "[", 1); + + /* Iterate over all maps */ + cur = ctx->cfg->maps; + while (cur) { + map = cur->data; + if (map->protocol == MAP_PROTO_FILE && map->description != NULL) { + if (access (map->uri, R_OK) == 0) { + editable = access (map->uri, W_OK) == 0; + evbuffer_add_printf (evb, "{\"map\":%u,\"description\":\"%s\",\"editable\":%s%s", + map->id, map->description, editable ? "true" : "false", + g_list_next (cur) ? "}," : "}"); + } + } + cur = g_list_next (cur); + } + + evbuffer_add (evb, "]" CRLF, 3); + 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); +} + +/* + * Get map command handler: + * request: /getmap + * headers: Password, Map + * reply: plain-text + */ +static void +http_handle_get_map (struct evhttp_request *req, gpointer arg) +{ + struct rspamd_webui_worker_ctx *ctx = arg; + struct evbuffer *evb; + GList *cur; + struct rspamd_map *map; + const gchar *idstr; + gchar *errstr; + struct stat st; + gint fd; + guint32 id; + gboolean found = FALSE; + + evb = evbuffer_new (); + if (!evb) { + msg_err ("cannot allocate evbuffer for reply"); + evhttp_send_reply (req, HTTP_INTERNAL, "500 insufficient memory", NULL); + return; + } + + idstr = evhttp_find_header (req->input_headers, "Map"); + + if (idstr == NULL) { + msg_info ("absent map id"); + evbuffer_free (evb); + evhttp_send_reply (req, HTTP_INTERNAL, "500 map open error", NULL); + return; + } + + id = strtoul (idstr, &errstr, 10); + if (*errstr != '\0') { + msg_info ("invalid map id"); + evbuffer_free (evb); + evhttp_send_reply (req, HTTP_INTERNAL, "500 map open error", NULL); + return; + } + + /* Now let's be sure that we have map defined in configuration */ + cur = ctx->cfg->maps; + while (cur) { + map = cur->data; + if (map->id == id && map->protocol == MAP_PROTO_FILE) { + found = TRUE; + break; + } + cur = g_list_next (cur); + } + + if (!found) { + msg_info ("map not found"); + evbuffer_free (evb); + evhttp_send_reply (req, HTTP_NOTFOUND, "404 map not found", NULL); + return; + } + + if (stat (map->uri, &st) == -1 || (fd = open (map->uri, O_RDONLY)) == -1) { + msg_err ("cannot open map %s: %s", map->uri, strerror (errno)); + evbuffer_free (evb); + evhttp_send_reply (req, HTTP_INTERNAL, "500 map open error", NULL); + return; + } + /* Set buffer size */ + if (evbuffer_expand (evb, st.st_size) != 0) { + msg_err ("cannot allocate buffer for map %s: %s", map->uri, strerror (errno)); + evbuffer_free (evb); + evhttp_send_reply (req, HTTP_INTERNAL, "500 insufficient memory", NULL); + close (fd); + return; + } + + /* Read the whole buffer */ + if (evbuffer_read (evb, fd, st.st_size) == -1) { + msg_err ("cannot read map %s: %s", map->uri, strerror (errno)); + evbuffer_free (evb); + evhttp_send_reply (req, HTTP_INTERNAL, "500 map read error", NULL); + close (fd); + return; + } + + evhttp_add_header (req->output_headers, "Connection", "close"); + http_calculate_content_length (evb, req); + + close (fd); + + evhttp_send_reply (req, HTTP_OK, "OK", evb); + evbuffer_free (evb); +} + + gpointer init_webui_worker (void) { @@ -438,6 +593,8 @@ start_webui_worker (struct rspamd_worker *worker) /* Add callbacks for different methods */ evhttp_set_cb (ctx->http, PATH_AUTH, http_handle_auth, ctx); evhttp_set_cb (ctx->http, PATH_SYMBOLS, http_handle_symbols, ctx); + evhttp_set_cb (ctx->http, PATH_MAPS, http_handle_maps, ctx); + evhttp_set_cb (ctx->http, PATH_GET_MAP, http_handle_get_map, ctx); ctx->resolver = dns_resolver_init (ctx->ev_base, worker->srv->cfg); -- 2.39.5