]> source.dussan.org Git - rspamd.git/commitdiff
Add metrics handler for rcl.
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Wed, 11 Sep 2013 16:20:03 +0000 (17:20 +0100)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Wed, 11 Sep 2013 16:20:03 +0000 (17:20 +0100)
src/cfg_rcl.c

index f5d5bab2e1a3a47bfe985832e5bc60d03039bf11..b6a08657aa4f5beece2021c70d485c43b258b38c 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "cfg_rcl.h"
 #include "main.h"
+#include "settings.h"
 
 /*
  * Common section handlers
@@ -186,6 +187,192 @@ rspamd_rcl_options_handler (struct config_file *cfg, rspamd_cl_object_t *obj,
        return rspamd_rcl_section_parse_defaults (section, cfg, obj, cfg, err);
 }
 
+static gint
+rspamd_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);
+}
+
+/**
+ * Insert a symbol to the metric
+ * @param cfg
+ * @param metric
+ * @param obj symbol rcl object (either float value or an object)
+ * @param err
+ * @return
+ */
+static gboolean
+rspamd_rcl_insert_symbol (struct config_file *cfg, struct metric *metric,
+               rspamd_cl_object_t *obj, GError **err)
+{
+       const gchar *group = "ungrouped", *description = NULL;
+       gdouble symbol_score, *score_ptr;
+       rspamd_cl_object_t *val;
+       struct symbols_group *sym_group;
+       struct symbol_def *sym_def;
+       GList *metric_list, *group_list;
+
+       /*
+        * We allow two type of definitions:
+        * symbol = weight
+        * or
+        * symbol {
+        *      weight = ...;
+        *      description = ...;
+        *      group = ...;
+        * }
+        */
+       if (rspamd_cl_obj_todouble_safe (obj, &symbol_score)) {
+               description = NULL;
+       }
+       else if (obj->type == RSPAMD_CL_OBJECT) {
+               HASH_FIND_STR (obj, "score", val);
+               if (val == NULL || !rspamd_cl_obj_todouble_safe (val, &symbol_score)) {
+                       g_set_error (err, CFG_RCL_ERROR, EINVAL, "invalid symbol score: %s", obj->key);
+                       return FALSE;
+               }
+               HASH_FIND_STR (obj, "description", val);
+               if (val != NULL) {
+                       description = rspamd_cl_obj_tostring (val);
+               }
+               HASH_FIND_STR (obj, "group", val);
+               if (val != NULL) {
+                       rspamd_cl_obj_tostring_safe (val, &group);
+               }
+       }
+       else {
+               g_set_error (err, CFG_RCL_ERROR, EINVAL, "invalid symbol type: %s", obj->key);
+               return FALSE;
+       }
+
+       sym_def = memory_pool_alloc (cfg->cfg_pool, sizeof (struct symbol_def));
+       score_ptr = memory_pool_alloc (cfg->cfg_pool, sizeof (gdouble));
+
+       *score_ptr = symbol_score;
+       sym_def->weight_ptr = score_ptr;
+       sym_def->name = memory_pool_strdup (cfg->cfg_pool, obj->key);
+       sym_def->description = (gchar *)description;
+
+       g_hash_table_insert (metric->symbols, sym_def->name, score_ptr);
+
+       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, sym_def->name, metric_list);
+       }
+       else {
+               /* Slow but keep start element of list in safe */
+               if (!g_list_find (metric_list, metric)) {
+                       metric_list = g_list_append (metric_list, metric);
+               }
+       }
+
+       /* Search for symbol group */
+       group_list = g_list_find_custom (cfg->symbols_groups, group, rspamd_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;
+}
+
+static gboolean
+rspamd_rcl_metric_handler (struct config_file *cfg, rspamd_cl_object_t *obj,
+               gpointer ud, struct rspamd_rcl_section *section, GError **err)
+{
+       rspamd_cl_object_t *val, *lobj, *cur, *tmp;
+       const gchar *metric_name, *subject_name;
+       struct metric *metric;
+       struct metric_action *action;
+       gdouble action_score, grow_factor;
+       gint action_value;
+       gboolean new = TRUE;
+
+       lobj = obj->value.ov;
+
+       HASH_FIND_STR (lobj, "name", val);
+       if (val == NULL || !rspamd_cl_obj_tostring_safe (val, &metric_name)) {
+               metric_name = DEFAULT_METRIC;
+       }
+
+       metric = g_hash_table_lookup (cfg->metrics, metric_name);
+       if (metric == NULL) {
+               metric = check_metric_conf (cfg, metric);
+       }
+       else {
+               new = FALSE;
+       }
+
+       /* Handle actions */
+       HASH_FIND_STR (lobj, "actions", val);
+       if (val != NULL) {
+               if (val->type != RSPAMD_CL_OBJECT) {
+                       g_set_error (err, CFG_RCL_ERROR, EINVAL, "actions must be an object");
+                       return FALSE;
+               }
+               HASH_ITER (hh, val, cur, tmp) {
+                       if (!check_action_str (cur->key, &action_value) ||
+                                       !rspamd_cl_obj_todouble_safe (cur, &action_score)) {
+                               g_set_error (err, CFG_RCL_ERROR, EINVAL, "invalid action definition: %s", cur->key);
+                               return FALSE;
+                       }
+                       action = memory_pool_alloc (cfg->cfg_pool, sizeof (struct metric_action));
+                       action->action = action_value;
+                       action->score = action_score;
+                       metric->actions = g_list_prepend (metric->actions, action);
+               }
+       }
+       else if (new) {
+               g_set_error (err, CFG_RCL_ERROR, EINVAL, "metric %s has no actions", metric_name);
+               return FALSE;
+       }
+
+       /* Handle symbols */
+       HASH_FIND_STR (lobj, "symbols", val);
+       if (val != NULL) {
+               if (val->type == RSPAMD_CL_ARRAY) {
+                       val = val->value.ov;
+               }
+               if (val->type != RSPAMD_CL_OBJECT) {
+                       g_set_error (err, CFG_RCL_ERROR, EINVAL, "symbols must be an object");
+                       return FALSE;
+               }
+               HASH_ITER (hh, val, cur, tmp) {
+                       if (!rspamd_rcl_insert_symbol (cfg, metric, cur, err)) {
+                               return FALSE;
+                       }
+               }
+       }
+
+       HASH_FIND_STR (lobj, "grow_factor", val);
+       if (val && rspamd_cl_obj_todouble_safe (val, &grow_factor)) {
+               metric->grow_factor = grow_factor;
+       }
+
+       HASH_FIND_STR (lobj, "subject", val);
+       if (val && rspamd_cl_obj_tostring_safe (val, &subject_name)) {
+               metric->subject = (gchar *)subject_name;
+       }
+
+       /* Insert the resulting metric */
+       g_hash_table_insert (cfg->metrics, metric->name, metric);
+       cfg->metrics_list = g_list_prepend (cfg->metrics_list, metric);
+
+       return TRUE;
+}
+
 /**
  * Fake handler to parse default options only, uses struct cfg_file as pointer
  * for default handlers
@@ -317,6 +504,11 @@ rspamd_rcl_config_init (void)
        rspamd_rcl_add_default_handler (sub, "use_mlock", rspamd_rcl_parse_struct_boolean,
                        G_STRUCT_OFFSET (struct config_file, mlock_statfile_pool), 0);
 
+       /**
+        * Metric section
+        */
+       sub = rspamd_rcl_add_section (new, "metric", rspamd_rcl_metric_handler, RSPAMD_CL_OBJECT,
+                       FALSE, TRUE);
 
        return new;
 }