commit fae4ed9: [Rework] Add composites manager concept

Vsevolod Stakhov vsevolod at highsecure.ru
Thu Jul 15 19:07:07 UTC 2021


Author: Vsevolod Stakhov
Date: 2021-07-15 19:44:01 +0100
URL: https://github.com/rspamd/rspamd/commit/fae4ed9fce781764540e636fec1e8397f1f9f532

[Rework] Add composites manager concept

---
 src/libserver/CMakeLists.txt                     |   1 +
 src/libserver/cfg_file.h                         |   2 +-
 src/libserver/cfg_rcl.c                          | 116 +--------------
 src/libserver/cfg_utils.c                        |   6 +-
 src/libserver/composites/composites.cxx          |  69 ++-------
 src/libserver/composites/composites.h            |  26 +++-
 src/libserver/composites/composites_internal.hxx | 113 +++++++++++++++
 src/libserver/composites/composites_manager.cxx  | 171 +++++++++++++++++++++++
 src/libutil/regexp.c                             |   2 +-
 9 files changed, 326 insertions(+), 180 deletions(-)

diff --git a/src/libserver/CMakeLists.txt b/src/libserver/CMakeLists.txt
index 75fad36ac..1dc8d9006 100644
--- a/src/libserver/CMakeLists.txt
+++ b/src/libserver/CMakeLists.txt
@@ -4,6 +4,7 @@ SET(LIBRSPAMDSERVERSRC
 				${CMAKE_CURRENT_SOURCE_DIR}/cfg_utils.c
 				${CMAKE_CURRENT_SOURCE_DIR}/cfg_rcl.c
 				${CMAKE_CURRENT_SOURCE_DIR}/composites/composites.cxx
+				${CMAKE_CURRENT_SOURCE_DIR}/composites/composites_manager.cxx
 				${CMAKE_CURRENT_SOURCE_DIR}/dkim.c
 				${CMAKE_CURRENT_SOURCE_DIR}/dns.c
 				${CMAKE_CURRENT_SOURCE_DIR}/dynamic_cfg.c
diff --git a/src/libserver/cfg_file.h b/src/libserver/cfg_file.h
index 5525030b0..6ee407332 100644
--- a/src/libserver/cfg_file.h
+++ b/src/libserver/cfg_file.h
@@ -419,7 +419,7 @@ struct rspamd_config {
 	ucl_object_t *config_comments;                  /**< comments saved from the config						*/
 	ucl_object_t *doc_strings;                      /**< documentation strings for config options			*/
 	GPtrArray *c_modules;                           /**< list of C modules			*/
-	GHashTable *composite_symbols;                 /**< hash of composite symbols indexed by its name		*/
+	void *composites_manager;                       /**< hash of composite symbols indexed by its name		*/
 	GList *classifiers;                             /**< list of all classifiers defined                    */
 	GList *statfiles;                               /**< list of all statfiles in config file order         */
 	GHashTable *classifiers_symbols;                /**< hashtable indexed by symbol name of classifiers    */
diff --git a/src/libserver/cfg_rcl.c b/src/libserver/cfg_rcl.c
index 68b94abfe..a8f3c57a6 100644
--- a/src/libserver/cfg_rcl.c
+++ b/src/libserver/cfg_rcl.c
@@ -1374,128 +1374,20 @@ rspamd_rcl_composite_handler (rspamd_mempool_t *pool,
 	struct rspamd_rcl_section *section,
 	GError **err)
 {
-	const ucl_object_t *val, *elt;
-	struct rspamd_expression *expr;
 	struct rspamd_config *cfg = ud;
-	struct rspamd_composite *composite;
-	const gchar *composite_name, *composite_expression, *group,
-		*description;
-	gdouble score;
-	gboolean new = TRUE;
+	void *composite;
+	const gchar *composite_name;
 
 	g_assert (key != NULL);
 
 	composite_name = key;
 
-	val = ucl_object_lookup (obj, "enabled");
-	if (val != NULL && !ucl_object_toboolean (val)) {
-		msg_info_config ("composite %s is disabled", composite_name);
-		return TRUE;
-	}
-
-	if (g_hash_table_lookup (cfg->composite_symbols, composite_name) != NULL) {
-		msg_warn_config ("composite %s is redefined", composite_name);
-		new = FALSE;
-	}
-
-	val = ucl_object_lookup (obj, "expression");
-	if (val == NULL || !ucl_object_tostring_safe (val, &composite_expression)) {
-		g_set_error (err,
-			CFG_RCL_ERROR,
-			EINVAL,
-			"composite must have an expression defined");
-		return FALSE;
-	}
-
-	if (!rspamd_parse_expression (composite_expression, 0, &composite_expr_subr,
-				NULL, cfg->cfg_pool, err, &expr)) {
-		if (err && *err) {
-			msg_err_config ("cannot parse composite expression for %s: %e",
-				composite_name, *err);
-		}
-		else {
-			msg_err_config ("cannot parse composite expression for %s: unknown error",
-				composite_name);
-		}
-
-		return FALSE;
-	}
-
-	composite =
-		rspamd_mempool_alloc0 (cfg->cfg_pool, sizeof (struct rspamd_composite));
-	composite->expr = expr;
-	composite->id = g_hash_table_size (cfg->composite_symbols);
-	composite->str_expr = composite_expression;
-	composite->sym = composite_name;
-
-	val = ucl_object_lookup (obj, "score");
-	if (val != NULL && ucl_object_todouble_safe (val, &score)) {
-		/* Also set score in the metric */
-
-		val = ucl_object_lookup (obj, "group");
-		if (val != NULL) {
-			group = ucl_object_tostring (val);
-		}
-		else {
-			group = "composite";
-		}
-
-		val = ucl_object_lookup (obj, "description");
-		if (val != NULL) {
-			description = ucl_object_tostring (val);
-		}
-		else {
-			description = composite_expression;
-		}
-
-		rspamd_config_add_symbol (cfg, composite_name, score,
-				description, group,
-				0,
-				ucl_object_get_priority (obj), /* No +1 as it is default... */
-				1);
-
-		elt = ucl_object_lookup (obj, "groups");
-
-		if (elt) {
-			ucl_object_iter_t gr_it;
-			const ucl_object_t *cur_gr;
-
-			gr_it = ucl_object_iterate_new (elt);
-
-			while ((cur_gr = ucl_object_iterate_safe (gr_it, true)) != NULL) {
-				rspamd_config_add_symbol_group (cfg, key,
-						ucl_object_tostring (cur_gr));
-			}
-
-			ucl_object_iterate_free (gr_it);
-		}
-	}
-
-	val = ucl_object_lookup (obj, "policy");
-
-	if (val) {
-		composite->policy = rspamd_composite_policy_from_str (
-				ucl_object_tostring (val));
-
-		if (composite->policy == RSPAMD_COMPOSITE_POLICY_UNKNOWN) {
-			g_set_error (err,
-					CFG_RCL_ERROR,
-					EINVAL,
-					"composite %s has incorrect policy", composite_name);
-			return FALSE;
-		}
-	}
-
-	g_hash_table_insert (cfg->composite_symbols,
-			(gpointer)composite_name,
-			composite);
-
-	if (new) {
+	if ((composite = rspamd_composites_manager_add_from_ucl(cfg->composites_manager, obj)) != NULL) {
 		rspamd_symcache_add_symbol (cfg->cache, composite_name, 0,
 				NULL, composite, SYMBOL_TYPE_COMPOSITE, -1);
 	}
 
-	return TRUE;
+	return composite != NULL;
 }
 
 static gboolean
diff --git a/src/libserver/cfg_utils.c b/src/libserver/cfg_utils.c
index d846153b8..d71a3956e 100644
--- a/src/libserver/cfg_utils.c
+++ b/src/libserver/cfg_utils.c
@@ -57,6 +57,7 @@
 #include <sys/resource.h>
 #endif
 #include <math.h>
+#include "libserver/composites/composites.h"
 
 #include "blas-config.h"
 
@@ -200,8 +201,7 @@ rspamd_config_new (enum rspamd_config_init_flags flags)
 
 
 	rspamd_config_init_metric (cfg);
-	cfg->composite_symbols =
-		g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
+	cfg->composites_manager = rspamd_composites_manager_create(cfg);
 	cfg->classifiers_symbols = g_hash_table_new (rspamd_str_hash,
 			rspamd_str_equal);
 	cfg->cfg_params = g_hash_table_new (rspamd_str_hash, rspamd_str_equal);
@@ -323,8 +323,6 @@ rspamd_config_free (struct rspamd_config *cfg)
 	ucl_object_unref (cfg->config_comments);
 	ucl_object_unref (cfg->doc_strings);
 	ucl_object_unref (cfg->neighbours);
-	g_hash_table_remove_all (cfg->composite_symbols);
-	g_hash_table_unref (cfg->composite_symbols);
 	g_hash_table_remove_all (cfg->cfg_params);
 	g_hash_table_unref (cfg->cfg_params);
 	g_hash_table_unref (cfg->classifiers_symbols);
diff --git a/src/libserver/composites/composites.cxx b/src/libserver/composites/composites.cxx
index 56ca554b3..777329f4e 100644
--- a/src/libserver/composites/composites.cxx
+++ b/src/libserver/composites/composites.cxx
@@ -26,6 +26,8 @@
 #include <variant>
 #include "contrib/robin-hood/robin_hood.h"
 
+#include "composites_internal.hxx"
+
 #define msg_err_composites(...) rspamd_default_log_function (G_LOG_LEVEL_CRITICAL, \
         "composites", task->task_pool->tag.uid, \
         G_STRFUNC, \
@@ -55,7 +57,6 @@ static gdouble rspamd_composite_expr_process(void *ud, rspamd_expression_atom_t
 static gint rspamd_composite_expr_priority(rspamd_expression_atom_t *atom);
 static void rspamd_composite_expr_destroy(rspamd_expression_atom_t *atom);
 static void composites_foreach_callback(gpointer key, gpointer value, void *data);
-}
 
 const struct rspamd_atom_subr composite_expr_subr = {
 		.parse = rspamd::composites::rspamd_composite_expr_parse,
@@ -63,30 +64,12 @@ const struct rspamd_atom_subr composite_expr_subr = {
 		.priority = rspamd::composites::rspamd_composite_expr_priority,
 		.destroy = rspamd::composites::rspamd_composite_expr_destroy
 };
+}
 
 namespace rspamd::composites {
 
 static constexpr const double epsilon = 0.00001;
 
-enum class rspamd_composite_policy {
-	RSPAMD_COMPOSITE_POLICY_REMOVE_ALL = 0,
-	RSPAMD_COMPOSITE_POLICY_REMOVE_SYMBOL,
-	RSPAMD_COMPOSITE_POLICY_REMOVE_WEIGHT,
-	RSPAMD_COMPOSITE_POLICY_LEAVE,
-	RSPAMD_COMPOSITE_POLICY_UNKNOWN
-};
-
-/**
- * Static composites structure
- */
-struct rspamd_composite {
-	std::string str_expr;
-	std::string sym;
-	struct rspamd_expression *expr;
-	gint id;
-	rspamd_composite_policy policy;
-};
-
 struct symbol_remove_data {
 	const char *sym;
 	struct rspamd_composite *comp;
@@ -104,7 +87,7 @@ struct composites_data {
 
 	explicit composites_data(struct rspamd_task *task, struct rspamd_scan_result *mres) :
 			task(task), composite(nullptr), metric_res(mres) {
-		checked.resize(g_hash_table_size(task->cfg->composite_symbols) * 2);
+		checked.resize(rspamd_composites_manager_nelts(task->cfg->composites_manager) * 2);
 	}
 };
 
@@ -164,7 +147,7 @@ enum class rspamd_composite_atom_type {
 struct rspamd_composite_atom {
 	std::string symbol;
 	rspamd_composite_atom_type comp_type = rspamd_composite_atom_type::ATOM_UNKNOWN;
-	struct rspamd_composite *ncomp; /* underlying composite */
+	const struct rspamd_composite *ncomp; /* underlying composite */
 	std::vector<rspamd_composite_option_match> opts;
 };
 
@@ -516,7 +499,7 @@ process_symbol_removal(rspamd_expression_atom_t *atom,
 
 static auto
 process_single_symbol(struct composites_data *cd,
-					  const gchar *sym,
+					  std::string_view sym,
 					  struct rspamd_symbol_result **pms,
 					  struct rspamd_composite_atom *atom) -> double
 {
@@ -524,16 +507,14 @@ process_single_symbol(struct composites_data *cd,
 	gdouble rc = 0;
 	struct rspamd_task *task = cd->task;
 
-	if ((ms = rspamd_task_find_symbol_result(cd->task, sym, cd->metric_res)) == nullptr) {
+	if ((ms = rspamd_task_find_symbol_result(cd->task, sym.data(), cd->metric_res)) == nullptr) {
 		msg_debug_composites ("not found symbol %s in composite %s", sym,
 				cd->composite->sym.c_str());
 
 		if (G_UNLIKELY(atom->comp_type == rspamd_composite_atom_type::ATOM_UNKNOWN)) {
-			struct rspamd_composite *ncomp;
+			const struct rspamd_composite *ncomp;
 
-			if ((ncomp =
-						 g_hash_table_lookup(cd->task->cfg->composite_symbols,
-								 sym)) != NULL) {
+			if ((ncomp = COMPOSITE_MANAGER_FROM_PTR(task->cfg->composites_manager)->find(sym)) != NULL) {
 				atom->comp_type = rspamd_composite_atom_type::ATOM_COMPOSITE;
 				atom->ncomp = ncomp;
 			}
@@ -558,7 +539,7 @@ process_single_symbol(struct composites_data *cd,
 				cd->composite = saved;
 				cd->checked[cd->composite->id * 2] = false;
 
-				ms = rspamd_task_find_symbol_result(cd->task, sym,
+				ms = rspamd_task_find_symbol_result(cd->task, sym.data(),
 						cd->metric_res);
 			}
 			else {
@@ -566,7 +547,7 @@ process_single_symbol(struct composites_data *cd,
 				 * XXX: in case of cyclic references this would return 0
 				 */
 				if (cd->checked[atom->ncomp->id * 2 + 1]) {
-					ms = rspamd_task_find_symbol_result(cd->task, sym,
+					ms = rspamd_task_find_symbol_result(cd->task, sym.data(),
 							cd->metric_res);
 				}
 			}
@@ -667,7 +648,7 @@ rspamd_composite_expr_process(void *ud, rspamd_expression_atom_t *atom) -> doubl
 
 				if (cond(sdef->score)) {
 					rc = process_single_symbol(cd,
-							sdef->name,
+							std::string_view(sdef->name),
 							&ms,
 							comp_atom);
 
@@ -700,7 +681,7 @@ rspamd_composite_expr_process(void *ud, rspamd_expression_atom_t *atom) -> doubl
 			rc = group_process_functor([](auto sc) { return sc < 0.; }, 3);
 		}
 		else {
-			rc = process_single_symbol(cd, sym.data(), &ms, comp_atom);
+			rc = process_single_symbol(cd, sym, &ms, comp_atom);
 
 			if (rc) {
 				process_symbol_removal(atom,
@@ -711,7 +692,7 @@ rspamd_composite_expr_process(void *ud, rspamd_expression_atom_t *atom) -> doubl
 		}
 	}
 	else {
-		rc = process_single_symbol(cd, sym.data(), &ms, comp_atom);
+		rc = process_single_symbol(cd, sym, &ms, comp_atom);
 
 		if (fabs(rc) > epsilon) {
 			process_symbol_removal(atom,
@@ -923,25 +904,3 @@ rspamd_composites_process_task (struct rspamd_task *task)
 	}
 }
 
-
-enum rspamd_composite_policy
-rspamd_composite_policy_from_str (const gchar *string)
-{
-	enum rspamd_composite_policy ret = RSPAMD_COMPOSITE_POLICY_UNKNOWN;
-
-	if (strcmp (string, "remove") == 0 || strcmp (string, "remove_all") == 0 ||
-			strcmp (string, "default") == 0) {
-		ret = RSPAMD_COMPOSITE_POLICY_REMOVE_ALL;
-	}
-	else if (strcmp (string, "remove_symbol") == 0) {
-		ret = RSPAMD_COMPOSITE_POLICY_REMOVE_SYMBOL;
-	}
-	else if (strcmp (string, "remove_weight") == 0) {
-		ret = RSPAMD_COMPOSITE_POLICY_REMOVE_WEIGHT;
-	}
-	else if (strcmp (string, "leave") == 0 || strcmp (string, "remove_none") == 0) {
-		ret = RSPAMD_COMPOSITE_POLICY_LEAVE;
-	}
-
-	return ret;
-}
diff --git a/src/libserver/composites/composites.h b/src/libserver/composites/composites.h
index d39863b88..2db020e74 100644
--- a/src/libserver/composites/composites.h
+++ b/src/libserver/composites/composites.h
@@ -17,25 +17,37 @@
 #define SRC_LIBSERVER_COMPOSITES_H_
 
 #include "config.h"
+#include "contrib/libucl/ucl.h"
 
 #ifdef  __cplusplus
 extern "C" {
 #endif
 
 struct rspamd_task;
-
-/**
- * Subr for composite expressions
- */
-extern const struct rspamd_atom_subr composite_expr_subr;
+struct rspamd_config;
 
 /**
  * Process all results and form composite metrics from existent metrics as it is defined in config
  * @param task worker's task that present message from user
  */
-void rspamd_composites_process_task (struct rspamd_task *task);
+void rspamd_composites_process_task(struct rspamd_task *task);
 
-enum rspamd_composite_policy rspamd_composite_policy_from_str (const gchar *string);
+/**
+ * Creates a composites manager
+ * @param cfg
+ * @return
+ */
+void* rspamd_composites_manager_create(struct rspamd_config *cfg);
+/**
+ * Returns number of elements in a composite manager
+ * @return
+ */
+gsize rspamd_composites_manager_nelts(void *);
+/**
+ * Adds a composite from config
+ * @return
+ */
+void* rspamd_composites_manager_add_from_ucl(void *, const ucl_object_t *);
 
 #ifdef  __cplusplus
 }
diff --git a/src/libserver/composites/composites_internal.hxx b/src/libserver/composites/composites_internal.hxx
new file mode 100644
index 000000000..d06f592db
--- /dev/null
+++ b/src/libserver/composites/composites_internal.hxx
@@ -0,0 +1,113 @@
+/*-
+ * Copyright 2021 Vsevolod Stakhov
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RSPAMD_COMPOSITES_INTERNAL_HXX
+#define RSPAMD_COMPOSITES_INTERNAL_HXX
+#pragma once
+
+#include <string>
+#include "libutil/expression.h"
+#include "libserver/cfg_file.h"
+
+namespace rspamd::composites {
+
+/**
+ * Subr for composite expressions
+ */
+extern const struct rspamd_atom_subr composite_expr_subr;
+
+enum class rspamd_composite_policy {
+	RSPAMD_COMPOSITE_POLICY_REMOVE_ALL = 0,
+	RSPAMD_COMPOSITE_POLICY_REMOVE_SYMBOL,
+	RSPAMD_COMPOSITE_POLICY_REMOVE_WEIGHT,
+	RSPAMD_COMPOSITE_POLICY_LEAVE,
+	RSPAMD_COMPOSITE_POLICY_UNKNOWN
+};
+
+/**
+ * Static composites structure
+ */
+struct rspamd_composite {
+	std::string str_expr;
+	std::string sym;
+	struct rspamd_expression *expr;
+	gint id;
+	rspamd_composite_policy policy;
+};
+
+#define COMPOSITE_MANAGER_FROM_PTR(ptr) (reinterpret_cast<rspamd::composites::composites_manager *>(ptr))
+
+class composites_manager {
+public:
+	composites_manager(struct rspamd_config *_cfg) : cfg(_cfg) {
+		rspamd_mempool_add_destructor(_cfg->cfg_pool, composites_manager_dtor, this);
+	}
+
+	auto size(void) const -> std::size_t {
+		return composites.size();
+	}
+
+	auto find(std::string_view name) const -> const rspamd_composite * {
+		auto found = composites.find(std::string(name));
+
+		if (found != composites.end()) {
+			return found->second.get();
+		}
+
+		return nullptr;
+	}
+
+	auto add_composite(std::string_view, const ucl_object_t *) -> rspamd_composite *;
+private:
+	~composites_manager() = default;
+	static void composites_manager_dtor(void *ptr) {
+		delete COMPOSITE_MANAGER_FROM_PTR(ptr);
+	}
+
+	/* Enable lookup by string view */
+	struct smart_str_equal {
+		using is_transparent = void;
+		auto operator()(const std::string &a, const std::string &b) const {
+			return a == b;
+		}
+		auto operator()(const std::string_view &a, const std::string &b) const {
+			return a == b;
+		}
+		auto operator()(const std::string &a, const std::string_view &b) const {
+			return a == b;
+		}
+	};
+
+	struct smart_str_hash {
+		using is_transparent = void;
+		auto operator()(const std::string &a) const {
+			return robin_hood::hash<std::string>()(a);
+		}
+		auto operator()(const std::string_view &a) const {
+			return robin_hood::hash<std::string_view>()(a);
+		}
+	};
+
+	robin_hood::unordered_flat_map<std::string,
+			std::shared_ptr<rspamd_composite>, smart_str_hash, smart_str_equal> composites;
+	/* Store all composites here, even if we have duplicates */
+	std::vector<std::shared_ptr<rspamd_composite>> all_composites;
+	struct rspamd_config *cfg;
+};
+
+}
+
+#endif //RSPAMD_COMPOSITES_INTERNAL_HXX
diff --git a/src/libserver/composites/composites_manager.cxx b/src/libserver/composites/composites_manager.cxx
new file mode 100644
index 000000000..0cffcdcf3
--- /dev/null
+++ b/src/libserver/composites/composites_manager.cxx
@@ -0,0 +1,171 @@
+/*-
+ * Copyright 2021 Vsevolod Stakhov
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+#include <vector>
+#include "contrib/robin-hood/robin_hood.h"
+
+#include "composites.h"
+#include "composites_internal.hxx"
+#include "libserver/cfg_file.h"
+#include "libserver/logger.h"
+
+namespace rspamd::composites {
+
+static auto
+composite_policy_from_str(const std::string_view &inp) -> enum rspamd_composite_policy
+{
+	const static robin_hood::unordered_flat_map<std::string_view,
+			enum rspamd_composite_policy> names{
+			{"remove", rspamd_composite_policy::RSPAMD_COMPOSITE_POLICY_REMOVE_ALL},
+			{"remove_all", rspamd_composite_policy::RSPAMD_COMPOSITE_POLICY_REMOVE_ALL},
+			{"default", rspamd_composite_policy::RSPAMD_COMPOSITE_POLICY_REMOVE_ALL},
+			{"remove_symbol", rspamd_composite_policy::RSPAMD_COMPOSITE_POLICY_REMOVE_SYMBOL},
+			{"remove_weight", rspamd_composite_policy::RSPAMD_COMPOSITE_POLICY_REMOVE_WEIGHT},
+			{"leave", rspamd_composite_policy::RSPAMD_COMPOSITE_POLICY_LEAVE},
+			{"remove_none", rspamd_composite_policy::RSPAMD_COMPOSITE_POLICY_LEAVE},
+	};
+
+	auto found = names.find(inp);
+	if (found != names.end()) {
+		return found->second;
+	}
+
+	return rspamd_composite_policy::RSPAMD_COMPOSITE_POLICY_UNKNOWN;
+}
+
+auto
+composites_manager::add_composite(std::string_view composite_name, const ucl_object_t *obj) -> rspamd_composite *
+{
+
+	const auto *val = ucl_object_lookup (obj, "enabled");
+	if (val != nullptr && !ucl_object_toboolean (val)) {
+		msg_info_config ("composite %s is disabled", composite_name.data());
+		return nullptr;
+	}
+
+	if (composites.contains(composite_name)) {
+		msg_warn_config ("composite %s is redefined", composite_name.data());
+	}
+
+	const char *composite_expression = nullptr;
+	val = ucl_object_lookup (obj, "expression");
+
+	if (val == NULL || !ucl_object_tostring_safe (val, &composite_expression)) {
+		msg_err_config ("composite must have an expression defined in %s",
+				composite_name.data());
+		return nullptr;
+	}
+
+	GError *err = nullptr;
+	rspamd_expression *expr = nullptr;
+
+	if (!rspamd_parse_expression(composite_expression, 0, &composite_expr_subr,
+			NULL, cfg->cfg_pool, &err, &expr)) {
+		msg_err_config ("cannot parse composite expression for %s: %e",
+				composite_name.data(), err);
+
+		if (err) {
+			g_error_free(err);
+		}
+
+		return nullptr;
+	}
+
+	auto &composite = all_composites.emplace_back(std::make_shared<rspamd_composite>());
+	composite->expr = expr;
+	composite->id = all_composites.size();
+	composite->str_expr = composite_expression;
+	composite->sym = composite_name;
+
+	double score;
+	val = ucl_object_lookup (obj, "score");
+	if (val != nullptr && ucl_object_todouble_safe (val, &score)) {
+		/* Also set score in the metric */
+
+		const auto *group = "composite";
+		val = ucl_object_lookup (obj, "group");
+		if (val != nullptr) {
+			group = ucl_object_tostring (val);
+		}
+
+		const auto *description = composite_expression;
+		val = ucl_object_lookup (obj, "description");
+		if (val != nullptr) {
+			description = ucl_object_tostring (val);
+		}
+		else {
+			description = composite_expression;
+		}
+
+		rspamd_config_add_symbol(cfg, composite_name.data(), score,
+				description, group,
+				0,
+				ucl_object_get_priority (obj), /* No +1 as it is default... */
+				1);
+
+		const auto *elt = ucl_object_lookup (obj, "groups");
+		if (elt) {
+			const ucl_object_t *cur_gr;
+			auto *gr_it = ucl_object_iterate_new (elt);
+
+			while ((cur_gr = ucl_object_iterate_safe(gr_it, true)) != nullptr) {
+				rspamd_config_add_symbol_group(cfg, composite_name.data(),
+						ucl_object_tostring(cur_gr));
+			}
+
+			ucl_object_iterate_free(gr_it);
+		}
+	}
+
+	val = ucl_object_lookup(obj, "policy");
+	if (val) {
+		composite->policy = composite_policy_from_str(ucl_object_tostring(val));
+
+		if (composite->policy == rspamd_composite_policy::RSPAMD_COMPOSITE_POLICY_UNKNOWN) {
+			msg_err_config("composite %s has incorrect policy", composite_name.data());
+			return nullptr;
+		}
+	}
+
+	composites[std::string(composite_name)] = composite;
*** OUTPUT TRUNCATED, 40 LINES SKIPPED ***


More information about the Commits mailing list