#include "config.h"
#include "main.h"
+#include "map.h"
+#include "filter.h"
#include "dynamic_cfg.h"
+#include "json/jansson.h"
-struct dynamic_cfg {
+struct dynamic_cfg_symbol {
+ gchar *name;
+ gdouble value;
+};
+
+struct dynamic_cfg_action {
+ enum rspamd_metric_action action;
+ gdouble value;
+};
+struct dynamic_cfg_metric {
+ GList *symbols;
+ GList *actions;
+ gchar *name;
};
struct config_json_buf {
gchar *pos;
size_t buflen;
struct config_file *cfg;
+ GList *config_metrics;
};
+/**
+ * Free dynamic configuration
+ * @param conf_metrics
+ */
+static void
+dynamic_cfg_free (GList *conf_metrics)
+{
+ GList *cur, *cur_elt;
+ struct dynamic_cfg_metric *metric;
+ struct dynamic_cfg_symbol *sym;
+ struct dynamic_cfg_action *act;
+
+ if (conf_metrics) {
+ cur = conf_metrics;
+ while (cur) {
+ metric = cur->data;
+ if (metric->symbols) {
+ cur_elt = metric->symbols;
+ while (cur_elt) {
+ sym = cur_elt->data;
+ g_free (sym->name);
+ g_slice_free1 (sizeof (struct dynamic_cfg_symbol), sym);
+ cur_elt = g_list_next (cur_elt);
+ }
+ g_list_free (metric->symbols);
+ }
+
+ if (metric->actions) {
+ cur_elt = metric->actions;
+ while (cur_elt) {
+ act = cur_elt->data;
+ g_slice_free1 (sizeof (struct dynamic_cfg_symbol), act);
+ cur_elt = g_list_next (cur_elt);
+ }
+ g_list_free (metric->actions);
+ }
+ g_slice_free1 (sizeof (struct dynamic_cfg_metric), metric);
+ cur = g_list_next (cur);
+ }
+ g_list_free (conf_metrics);
+ }
+}
+/**
+ * Apply configuration to the specified configuration
+ * @param conf_metrics
+ * @param cfg
+ */
+static void
+apply_dynamic_conf (GList *conf_metrics, struct config_file *cfg)
+{
+ GList *cur, *cur_elt, *tmp;
+ struct dynamic_cfg_metric *metric;
+ struct dynamic_cfg_symbol *sym;
+ struct dynamic_cfg_action *act;
+ struct metric *real_metric;
+ struct metric_action *real_act;
+ gdouble *w;
+
+ cur = conf_metrics;
+ while (cur) {
+ metric = cur->data;
+ if ((real_metric = g_hash_table_lookup (cfg->metrics, metric->name)) != NULL) {
+ cur_elt = metric->symbols;
+ while (cur_elt) {
+ sym = cur_elt->data;
+ if ((w = g_hash_table_lookup (real_metric->symbols, sym->name)) != NULL) {
+ *w = sym->value;
+ }
+ else {
+ msg_info ("symbol %s is not found in the main configuration", sym->name);
+ }
+ cur_elt = g_list_next (cur_elt);
+ }
+
+ cur_elt = metric->actions;
+ while (cur_elt) {
+ act = cur_elt->data;
+ tmp = real_metric->actions;
+ while (tmp) {
+ real_act = tmp->data;
+ if (real_act->action == act->action) {
+ real_act->score = act->value;
+ }
+ tmp = g_list_next (tmp);
+ }
+ cur_elt = g_list_next (cur_elt);
+ }
+ }
+ cur = g_list_next (cur);
+ }
+}
+
/* Callbacks for reading json dynamic rules */
gchar *
json_config_read_cb (memory_pool_t * pool, gchar * chunk, gint len, struct map_cb_data *data)
{
- struct regexp_json_buf *jb;
+ struct config_json_buf *jb;
gint free, off;
if (data->cur_data == NULL) {
- jb = g_malloc (sizeof (struct regexp_json_buf));
- jb->cfg = ((struct regexp_json_buf *)data->prev_data)->cfg;
+ jb = g_malloc (sizeof (struct config_json_buf));
+ jb->cfg = ((struct config_json_buf *)data->prev_data)->cfg;
jb->buf = NULL;
jb->pos = NULL;
data->cur_data = jb;
json_config_fin_cb (memory_pool_t * pool, struct map_cb_data *data)
{
struct config_json_buf *jb;
- guint nelts, i, j;
+ guint nelts, i, j, selts;
+ gint test_act;
json_t *js, *cur_elt, *cur_nm, *it_val;
json_error_t je;
+ struct dynamic_cfg_metric *cur_metric;
+ struct dynamic_cfg_symbol *cur_symbol;
+ struct dynamic_cfg_action *cur_action;
if (data->prev_data) {
jb = data->prev_data;
msg_err ("loaded json is not an array");
return;
}
+
+ dynamic_cfg_free (jb->config_metrics);
+ jb->config_metrics = NULL;
+
+ /* Parse configuration */
+ nelts = json_array_size (js);
+ for (i = 0; i < nelts; i++) {
+ cur_elt = json_array_get (js, i);
+ if (!cur_elt || !json_is_object (cur_elt)) {
+ msg_err ("loaded json array element is not an object");
+ continue;
+ }
+
+ cur_nm = json_object_get (cur_elt, "name");
+ if (!cur_nm || !json_is_string (cur_nm)) {
+ msg_err ("loaded json array element has no 'name' attribute");
+ continue;
+ }
+ cur_metric = g_slice_alloc0 (sizeof (struct dynamic_cfg_metric));
+ cur_metric->name = g_strdup (json_string_value (cur_nm));
+ cur_nm = json_object_get (cur_elt, "symbols");
+ /* Parse symbols */
+ if (cur_nm && json_is_array (cur_nm)) {
+ selts = json_array_size (cur_nm);
+ for (j = 0; j < selts; j ++) {
+ it_val = json_array_get (cur_nm, j);
+ if (it_val && json_is_object (it_val)) {
+ if (json_object_get (it_val, "name") && json_object_get (it_val, "value")) {
+ cur_symbol = g_slice_alloc0 (sizeof (struct dynamic_cfg_symbol));
+ cur_symbol->name = g_strdup (json_string_value (json_object_get (it_val, "name")));
+ cur_symbol->value = json_number_value (json_object_get (it_val, "value"));
+ /* Insert symbol */
+ cur_metric->symbols = g_list_prepend (cur_metric->symbols, cur_symbol);
+ }
+ }
+ }
+ }
+ cur_nm = json_object_get (cur_elt, "actions");
+ /* Parse actions */
+ if (cur_nm && json_is_array (cur_nm)) {
+ selts = json_array_size (cur_nm);
+ for (j = 0; j < selts; j ++) {
+ it_val = json_array_get (cur_nm, j);
+ if (it_val && json_is_object (it_val)) {
+ if (json_object_get (it_val, "name") && json_object_get (it_val, "value")) {
+
+ cur_action = g_slice_alloc0 (sizeof (struct dynamic_cfg_action));
+ if (!check_action_str (json_string_value (json_object_get (it_val, "name")), &test_act)) {
+ msg_err ("unknown action: %s", json_string_value (json_object_get (it_val, "name")));
+ g_slice_free1 (sizeof (struct dynamic_cfg_action), cur_action);
+ continue;
+ }
+ cur_action->action = test_act;
+ cur_action->value = json_number_value (json_object_get (it_val, "value"));
+ /* Insert action */
+ cur_metric->actions = g_list_prepend (cur_metric->actions, cur_action);
+ }
+ }
+ }
+ }
+ jb->config_metrics = g_list_prepend (jb->config_metrics, cur_metric);
+ }
+ /*
+ * Note about thread safety: we are updating values that are gdoubles so it is not atomic in general case
+ * but on the other hand all that data is used only in the main thread, so why it is *likely* safe
+ * to do this task in this way without explicit lock.
+ */
+ apply_dynamic_conf (jb->config_metrics, jb->cfg);
+
+ json_decref (js);
}
/**
void
init_dynamic_config (struct config_file *cfg)
{
- gint fd;
struct stat st;
struct config_json_buf *jb, **pjb;
}
/* Now try to add map with json data */
- jb = g_malloc (sizeof (struct regexp_json_buf));
- pjb = g_malloc (sizeof (struct regexp_json_buf *));
+ jb = g_malloc (sizeof (struct config_json_buf));
+ pjb = g_malloc (sizeof (struct config_json_buf *));
jb->buf = NULL;
jb->cfg = cfg;
*pjb = jb;
- if (!add_map (cfg->dynamic_conf, json_config_read_cb, json_config_fin_cb, (void **)pjb)) {
+ if (!add_map (cfg, cfg->dynamic_conf, json_config_read_cb, json_config_fin_cb, (void **)pjb)) {
msg_err ("cannot add map for configuration %s", cfg->dynamic_conf);
}
}