@@ -138,6 +138,23 @@ struct module_opt { | |||
enum lua_var_type lua_type; /**< type of lua variable */ | |||
}; | |||
/** | |||
* Symbol definition | |||
*/ | |||
struct symbol_def { | |||
gchar *name; | |||
gchar *description; | |||
gdouble weight; | |||
}; | |||
/** | |||
* Symbols group | |||
*/ | |||
struct symbols_group { | |||
gchar *name; | |||
GList *symbols; | |||
}; | |||
/** | |||
* Statfile section definition | |||
*/ | |||
@@ -308,6 +325,7 @@ struct config_file { | |||
GHashTable* modules_opts; /**< hash for module options indexed by module name */ | |||
GHashTable* variables; /**< hash of $variables defined in config, indexed by variable name */ | |||
GHashTable* metrics; /**< hash of metrics indexed by metric name */ | |||
GList* symbols_groups; /**< groups of symbols */ | |||
GList* metrics_list; /**< linked list of metrics */ | |||
GHashTable* metrics_symbols; /**< hash table of metrics indexed by symbol */ | |||
GHashTable* c_modules; /**< hash of c modules indexed by module name */ |
@@ -236,6 +236,9 @@ init_defaults (struct config_file *cfg) | |||
void | |||
free_config (struct config_file *cfg) | |||
{ | |||
GList *cur; | |||
struct symbols_group *gr; | |||
remove_all_maps (cfg); | |||
g_hash_table_remove_all (cfg->modules_opts); | |||
g_hash_table_unref (cfg->modules_opts); | |||
@@ -251,6 +254,19 @@ free_config (struct config_file *cfg) | |||
g_hash_table_unref (cfg->cfg_params); | |||
g_hash_table_destroy (cfg->metrics_symbols); | |||
g_hash_table_destroy (cfg->classifiers_symbols); | |||
/* Free symbols groups */ | |||
cur = cfg->symbols_groups; | |||
while (cur) { | |||
gr = cur->data; | |||
if (gr->symbols) { | |||
g_list_free (gr->symbols); | |||
} | |||
cur = g_list_next (cur); | |||
} | |||
if (cfg->symbols_groups) { | |||
g_list_free (cfg->symbols_groups); | |||
} | |||
if (cfg->checksum) { | |||
g_free (cfg->checksum); | |||
} |
@@ -1050,15 +1050,28 @@ handle_metric_action (struct config_file *cfg, struct rspamd_xml_userdata *ctx, | |||
return TRUE; | |||
} | |||
static gint | |||
symbols_group_find_func (gconstpointer a, gconstpointer b) | |||
{ | |||
const struct symbols_group *gr = a; | |||
const gchar *uv = b; | |||
return g_ascii_strcasecmp (gr->name, uv); | |||
} | |||
gboolean | |||
handle_metric_symbol (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, gchar *data, gpointer user_data, gpointer dest_struct, gint offset) | |||
{ | |||
gchar *strval, *err, *desc; | |||
gchar *strval, *err, *desc, *group; | |||
double *value; | |||
GList *metric_list; | |||
GList *metric_list, *group_list; | |||
struct metric *metric = ctx->section_pointer; | |||
struct symbols_group *sym_group; | |||
struct symbol_def *sym_def; | |||
sym_def = memory_pool_alloc (cfg->cfg_pool, sizeof (struct symbol_def)); | |||
value = memory_pool_alloc (cfg->cfg_pool, sizeof (double)); | |||
if (attrs == NULL || (strval = g_hash_table_lookup (attrs, "weight")) == NULL) { | |||
msg_info ("symbol tag should have \"weight\" attribute, assume weight 1.0"); | |||
*value = 1.0; | |||
@@ -1072,19 +1085,30 @@ handle_metric_symbol (struct config_file *cfg, struct rspamd_xml_userdata *ctx, | |||
} | |||
} | |||
sym_def->weight = *value; | |||
sym_def->name = memory_pool_strdup (cfg->cfg_pool, data); | |||
if (attrs != NULL) { | |||
desc = g_hash_table_lookup (attrs, "description"); | |||
if (desc) { | |||
g_hash_table_insert (metric->descriptions, data, memory_pool_strdup (cfg->cfg_pool, desc)); | |||
sym_def->description = memory_pool_strdup (cfg->cfg_pool, desc); | |||
g_hash_table_insert (metric->descriptions, data, sym_def->description); | |||
} | |||
else { | |||
sym_def->description = NULL; | |||
} | |||
group = g_hash_table_lookup (attrs, "group"); | |||
if (group == NULL) { | |||
group = "ungrouped"; | |||
} | |||
} | |||
g_hash_table_insert (metric->symbols, data, value); | |||
g_hash_table_insert (metric->symbols, sym_def->name, value); | |||
if ((metric_list = g_hash_table_lookup (cfg->metrics_symbols, data)) == NULL) { | |||
if ((metric_list = g_hash_table_lookup (cfg->metrics_symbols, sym_def->name)) == NULL) { | |||
metric_list = g_list_prepend (NULL, metric); | |||
memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func)g_list_free, metric_list); | |||
g_hash_table_insert (cfg->metrics_symbols, data, metric_list); | |||
g_hash_table_insert (cfg->metrics_symbols, sym_def->name, metric_list); | |||
} | |||
else { | |||
/* Slow but keep start element of list in safe */ | |||
@@ -1093,6 +1117,21 @@ handle_metric_symbol (struct config_file *cfg, struct rspamd_xml_userdata *ctx, | |||
} | |||
} | |||
/* Search for symbol group */ | |||
group_list = g_list_find_custom (cfg->symbols_groups, group, symbols_group_find_func); | |||
if (group_list == NULL) { | |||
/* Create new group */ | |||
sym_group = memory_pool_alloc (cfg->cfg_pool, sizeof (struct symbols_group)); | |||
sym_group->name = memory_pool_strdup (cfg->cfg_pool, group); | |||
sym_group->symbols = NULL; | |||
cfg->symbols_groups = g_list_prepend (cfg->symbols_groups, sym_group); | |||
} | |||
else { | |||
sym_group = group_list->data; | |||
} | |||
/* Insert symbol */ | |||
sym_group->symbols = g_list_prepend (sym_group->symbols, sym_def); | |||
return TRUE; | |||
} | |||
@@ -57,6 +57,7 @@ | |||
/* HTTP paths */ | |||
#define PATH_AUTH "/login" | |||
#define PATH_SYMBOLS "/symbols" | |||
gpointer init_webui_worker (void); | |||
void start_webui_worker (struct rspamd_worker *worker); | |||
@@ -218,6 +219,16 @@ webui_ssl_init (struct rspamd_webui_worker_ctx *ctx) | |||
} | |||
#endif | |||
/* Calculate and set content-length header */ | |||
static void | |||
http_calculate_content_length (struct evbuffer *evb, struct evhttp_request *req) | |||
{ | |||
gchar numbuf[64]; | |||
rspamd_snprintf (numbuf, sizeof (numbuf), "%z", evbuffer_get_length (evb)); | |||
evhttp_add_header(req->output_headers, "Content-Length", numbuf); | |||
} | |||
/* Command handlers */ | |||
/* | |||
@@ -266,15 +277,86 @@ http_handle_auth (struct evhttp_request *req, gpointer arg) | |||
hours = uptime / 3600; | |||
minutes = uptime / 60 - hours * 60; | |||
uptime -= hours * 3600 + minutes * 60; | |||
rspamd_snprintf (uptime_buf, sizeof (uptime_buf), "%d hour%s %d minute%s %d second%s", hours, hours > 1 ? "s" : " ", minutes, minutes > 1 ? "s" : " ", (gint)uptime, uptime > 1 ? "s" : " "); | |||
rspamd_snprintf (uptime_buf, sizeof (uptime_buf), "%d hour%s %d minute%s %d second%s", hours, hours != 1 ? "s" : " ", minutes, minutes != 1 ? "s" : " ", (gint)uptime, uptime != 1 ? "s" : " "); | |||
} | |||
evbuffer_add_printf (evb, "{\"auth\": \"%s\", \"version\": \"%s\", \"uptime\": \"%s\", \"error\": \"%s\"}" CRLF, | |||
auth, RVERSION, uptime_buf, error); | |||
evhttp_add_header(req->output_headers, "Connection", "close"); | |||
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); | |||
} | |||
/* | |||
* Symbols command handler: | |||
* request: /symbols | |||
* reply: json [{ | |||
* "name": "group_name", | |||
* "symbols": [ | |||
* { | |||
* "name": "name", | |||
* "weight": 0.1, | |||
* "description": "description of symbol" | |||
* }, | |||
* {...} | |||
* }, | |||
* {...}] | |||
*/ | |||
static void | |||
http_handle_symbols (struct evhttp_request *req, gpointer arg) | |||
{ | |||
struct rspamd_webui_worker_ctx *ctx = arg; | |||
struct evbuffer *evb; | |||
GList *cur_gr, *cur_sym; | |||
struct symbols_group *gr; | |||
struct symbol_def *sym; | |||
evb = evbuffer_new (); | |||
if (!evb) { | |||
msg_err ("cannot allocate evbuffer for reply"); | |||
return; | |||
} | |||
/* Trailer */ | |||
evbuffer_add (evb, "[", 1); | |||
/* Go throught all symbols groups */ | |||
cur_gr = ctx->cfg->symbols_groups; | |||
while (cur_gr) { | |||
gr = cur_gr->data; | |||
evbuffer_add_printf (evb, "{\"group\":\"%s\",\"rules\":[", gr->name); | |||
/* Iterate throught all symbols */ | |||
cur_sym = gr->symbols; | |||
while (cur_sym) { | |||
sym = cur_sym->data; | |||
if (sym->description) { | |||
evbuffer_add_printf (evb, "{\"symbol\":\"%s\",\"weight\":%.2f,\"description\":\"%s\"%s", sym->name, sym->weight, | |||
sym->description, g_list_next (cur_sym) ? "}," : "}"); | |||
} | |||
else { | |||
evbuffer_add_printf (evb, "{\"symbol\":\"%s\",\"weight\":%.2f%s", sym->name, sym->weight, | |||
g_list_next (cur_sym) ? "}," : "}"); | |||
} | |||
cur_sym = g_list_next (cur_sym); | |||
} | |||
if (g_list_next (cur_gr)) { | |||
evbuffer_add (evb, "]},", 3); | |||
} | |||
else { | |||
evbuffer_add (evb, "]},", 2); | |||
} | |||
cur_gr = g_list_next (cur_gr); | |||
} | |||
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); | |||
evhttp_send_reply (req, HTTP_OK, "OK", evb); | |||
evbuffer_free (evb); | |||
} | |||
gpointer | |||
@@ -351,6 +433,7 @@ 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); | |||
ctx->resolver = dns_resolver_init (ctx->ev_base, worker->srv->cfg); | |||