]> source.dussan.org Git - rspamd.git/commitdiff
Add /maps and /getmap commands to webui.
authorVsevolod Stakhov <vsevolod@rambler-co.ru>
Sun, 6 Jan 2013 16:33:31 +0000 (20:33 +0400)
committerVsevolod Stakhov <vsevolod@rambler-co.ru>
Sun, 6 Jan 2013 16:33:31 +0000 (20:33 +0400)
Identify maps by id.
Initialize secure random numbers using openssl.
Add description to maps.

17 files changed:
src/cfg_utils.c
src/cfg_xml.c
src/dynamic_cfg.c
src/logger.c
src/lua/lua_config.c
src/main.c
src/map.c
src/map.h
src/plugins/dkim_check.c
src/plugins/fuzzy_check.c
src/plugins/regexp.c
src/plugins/spf.c
src/plugins/surbl.c
src/settings.c
src/settings.h
src/view.c
src/webui.c

index 7b1910324771346a739933193bbf7d44dc21f529..a0191b103fe1f17483628bdab18c0b6a0c5f3e12 100644 (file)
@@ -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) {
index 6ae3ce7c378d12193e7914c7786c069447f44930..f81492d774647d4abcb6f39bda352cb03f4d552e 100644 (file)
@@ -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;
        }
index 279b1e0a99991eaf29f20274dd44f0f764df0437..7b9596c6c9e5a23f5fabfbeeec84e81874f7f4f6 100644 (file)
@@ -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);
        }
 }
index ea47b98b948953b8243e95fb459116658a64a6c3..88636d2e3c7b8bf3e0134dfdf30f1ca3f4bed851 100644 (file)
@@ -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);
index ba9dfab374e6cdf7821584687bf99a3d2d18da72..b84879df0c89139a80e3986b76cb4c800dbcd184 100644 (file)
@@ -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);
index f85fe7ade11e5d462cc3f93a9f52059530033882..94001298d25ab511bdec6d41c167c7354be31cbc 100644 (file)
 #include "cfg_xml.h"
 #include "symbols_cache.h"
 #include "lua/lua_common.h"
+#ifdef HAVE_OPENSSL
+#include <openssl/rand.h>
+#include <openssl/err.h>
+#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);
index 1ee6e7de4ca9754754f951396b503f5a7bdeaa5d..09b4df8df7d0a2ac391a97b30a2c9cc052937834 100644 (file)
--- 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) {
index 69fe5f87c863d53a4e3a9a1ab798b4fefaebbf85..5005786dca023bf8d1afec2ca67259b21c962e63 100644 (file)
--- 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
index 7bb32db55a1f6a4cd2060867f774e48fdac14206..a1a022665223ab67c49c0ee3f239f22896379dbe 100644 (file)
@@ -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 {
index 2f61ac1b336e8555ad92164ba6e365d7cd68ff4e..9841638584b2f510ef425d8fab0291403f298f63 100644 (file)
@@ -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);   
                }
        }
index ebb4ff64829f0095c1e4d7be0829288818e2de8b..9dce07772e52398767993997a0cb514f221adb1d 100644 (file)
@@ -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);
                }
        }
index d10498699aa1e137d893e34673dd6ab993b642c2..273243c138690ce72d535cca4d47b4434c1a476f 100644 (file)
@@ -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);
                }
        }
index ba2df2dd87a9b26680cd8de26ab5d3984158a1d7..57bd257c78ad475a5be593bded3cb1ad55c195f0 100644 (file)
@@ -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);
                }
        }
index 0d856bf3f7d9efb0267db62726c7a89dd20d2612..d7e96222a089759a880c3cbc573444faf2e86677 100644 (file)
@@ -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;
        }
index fb8f8681ea2d9603eecea3a20f5101ff862ad5b6..7f89dd4b90786d51e87f389a48111d004b82e4cb 100644 (file)
@@ -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
index b0d16dd5bad91c93e766b6a6ab3f89d3f98c7c89..a59f3eb0b1c0578ac59e7f926af9bdbd0fd97fb4 100644 (file)
@@ -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;
        }
 
index 05c64fc1d0d0d705765b9aff1bff9e13d4b30607..326cd763957e25c473ef30e50a7d614e95d54c61 100644 (file)
@@ -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);