#include "cfg_rcl.h"
#include "main.h"
+#include "settings.h"
/*
* Common section handlers
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
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;
}