From 2778ec22a2ff4c81c7adcae657d39db08749851d Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Mon, 14 Aug 2023 14:05:14 +0100 Subject: [Rework] Move rcl logic to C++ --- src/libserver/cfg_rcl.c | 4296 ----------------------------------------------- 1 file changed, 4296 deletions(-) delete mode 100644 src/libserver/cfg_rcl.c (limited to 'src/libserver/cfg_rcl.c') diff --git a/src/libserver/cfg_rcl.c b/src/libserver/cfg_rcl.c deleted file mode 100644 index 8267a4bc8..000000000 --- a/src/libserver/cfg_rcl.c +++ /dev/null @@ -1,4296 +0,0 @@ -/*- - * Copyright 2016 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 "cfg_rcl.h" -#include "rspamd.h" -#include "cfg_file_private.h" -#include "utlist.h" -#include "cfg_file.h" -#include "lua/lua_common.h" -#include "expression.h" -#include "src/libserver/composites/composites.h" -#include "libserver/worker_util.h" -#include "unix-std.h" -#include "cryptobox.h" -#include "libutil/multipattern.h" -#include "libmime/email_addr.h" -#include "libmime/lang_detection.h" - -#ifdef HAVE_SYSLOG_H -#include -#endif - -#include - -struct rspamd_rcl_default_handler_data { - struct rspamd_rcl_struct_parser pd; - gchar *key; - rspamd_rcl_default_handler_t handler; - UT_hash_handle hh; -}; - -struct rspamd_rcl_section { - const gchar *name; /**< name of section */ - const gchar *key_attr; - const gchar *default_key; - rspamd_rcl_handler_t handler; /**< handler of section attributes */ - enum ucl_type type; /**< type of attribute */ - gboolean required; /**< whether this param is required */ - gboolean strict_type; /**< whether we need strict type */ - UT_hash_handle hh; /** hash handle */ - struct rspamd_rcl_section *subsections; /**< hash table of subsections */ - struct rspamd_rcl_default_handler_data *default_parser; /**< generic parsing fields */ - rspamd_rcl_section_fin_t fin; /** called at the end of section parsing */ - gpointer fin_ud; - ucl_object_t *doc_ref; /**< reference to the section's documentation */ -}; - -struct rspamd_worker_param_key { - const gchar *name; - gpointer ptr; -}; - -struct rspamd_worker_param_parser { - rspamd_rcl_default_handler_t handler; /**< handler function */ - struct rspamd_rcl_struct_parser parser; /**< parser attributes */ - - struct rspamd_worker_param_key key; -}; - -struct rspamd_worker_cfg_parser { - GHashTable *parsers; /**< parsers hash */ - gint type; /**< workers quark */ - gboolean (*def_obj_parser)(ucl_object_t *obj, gpointer ud); /**< - default object parser */ - gpointer def_ud; -}; - -static gboolean rspamd_rcl_process_section(struct rspamd_config *cfg, - struct rspamd_rcl_section *sec, - gpointer ptr, const ucl_object_t *obj, rspamd_mempool_t *pool, - GError **err); - -/* - * Common section handlers - */ -static gboolean -rspamd_rcl_logging_handler(rspamd_mempool_t *pool, const ucl_object_t *obj, - const gchar *key, gpointer ud, struct rspamd_rcl_section *section, - GError **err) -{ - const ucl_object_t *val; - const gchar *facility = NULL, *log_type = NULL, *log_level = NULL; - struct rspamd_config *cfg = ud; - - val = ucl_object_lookup(obj, "type"); - if (val != NULL && ucl_object_tostring_safe(val, &log_type)) { - if (g_ascii_strcasecmp(log_type, "file") == 0) { - /* Need to get filename */ - val = ucl_object_lookup(obj, "filename"); - if (val == NULL || val->type != UCL_STRING) { - g_set_error(err, - CFG_RCL_ERROR, - ENOENT, - "filename attribute must be specified for file logging type"); - return FALSE; - } - cfg->log_type = RSPAMD_LOG_FILE; - cfg->log_file = rspamd_mempool_strdup(cfg->cfg_pool, - ucl_object_tostring(val)); - } - else if (g_ascii_strcasecmp(log_type, "syslog") == 0) { - /* Need to get facility */ -#ifdef HAVE_SYSLOG_H - cfg->log_facility = LOG_DAEMON; - cfg->log_type = RSPAMD_LOG_SYSLOG; - val = ucl_object_lookup(obj, "facility"); - if (val != NULL && ucl_object_tostring_safe(val, &facility)) { - if (g_ascii_strcasecmp(facility, "LOG_AUTH") == 0 || - g_ascii_strcasecmp(facility, "auth") == 0) { - cfg->log_facility = LOG_AUTH; - } - else if (g_ascii_strcasecmp(facility, "LOG_CRON") == 0 || - g_ascii_strcasecmp(facility, "cron") == 0) { - cfg->log_facility = LOG_CRON; - } - else if (g_ascii_strcasecmp(facility, "LOG_DAEMON") == 0 || - g_ascii_strcasecmp(facility, "daemon") == 0) { - cfg->log_facility = LOG_DAEMON; - } - else if (g_ascii_strcasecmp(facility, "LOG_MAIL") == 0 || - g_ascii_strcasecmp(facility, "mail") == 0) { - cfg->log_facility = LOG_MAIL; - } - else if (g_ascii_strcasecmp(facility, "LOG_USER") == 0 || - g_ascii_strcasecmp(facility, "user") == 0) { - cfg->log_facility = LOG_USER; - } - else if (g_ascii_strcasecmp(facility, "LOG_LOCAL0") == 0 || - g_ascii_strcasecmp(facility, "local0") == 0) { - cfg->log_facility = LOG_LOCAL0; - } - else if (g_ascii_strcasecmp(facility, "LOG_LOCAL1") == 0 || - g_ascii_strcasecmp(facility, "local1") == 0) { - cfg->log_facility = LOG_LOCAL1; - } - else if (g_ascii_strcasecmp(facility, "LOG_LOCAL2") == 0 || - g_ascii_strcasecmp(facility, "local2") == 0) { - cfg->log_facility = LOG_LOCAL2; - } - else if (g_ascii_strcasecmp(facility, "LOG_LOCAL3") == 0 || - g_ascii_strcasecmp(facility, "local3") == 0) { - cfg->log_facility = LOG_LOCAL3; - } - else if (g_ascii_strcasecmp(facility, "LOG_LOCAL4") == 0 || - g_ascii_strcasecmp(facility, "local4") == 0) { - cfg->log_facility = LOG_LOCAL4; - } - else if (g_ascii_strcasecmp(facility, "LOG_LOCAL5") == 0 || - g_ascii_strcasecmp(facility, "local5") == 0) { - cfg->log_facility = LOG_LOCAL5; - } - else if (g_ascii_strcasecmp(facility, "LOG_LOCAL6") == 0 || - g_ascii_strcasecmp(facility, "local6") == 0) { - cfg->log_facility = LOG_LOCAL6; - } - else if (g_ascii_strcasecmp(facility, "LOG_LOCAL7") == 0 || - g_ascii_strcasecmp(facility, "local7") == 0) { - cfg->log_facility = LOG_LOCAL7; - } - else { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "invalid log facility: %s", - facility); - return FALSE; - } - } -#endif - } - else if (g_ascii_strcasecmp(log_type, - "stderr") == 0 || - g_ascii_strcasecmp(log_type, "console") == 0) { - cfg->log_type = RSPAMD_LOG_CONSOLE; - } - else { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "invalid log type: %s", - log_type); - return FALSE; - } - } - else { - /* No type specified */ - msg_warn_config( - "logging type is not specified correctly, log output to the console"); - } - - /* Handle log level */ - val = ucl_object_lookup(obj, "level"); - if (val != NULL && ucl_object_tostring_safe(val, &log_level)) { - if (g_ascii_strcasecmp(log_level, "error") == 0) { - cfg->log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL; - } - else if (g_ascii_strcasecmp(log_level, "warning") == 0) { - cfg->log_level = G_LOG_LEVEL_WARNING; - } - else if (g_ascii_strcasecmp(log_level, "info") == 0) { - cfg->log_level = G_LOG_LEVEL_INFO | G_LOG_LEVEL_MESSAGE; - } - else if (g_ascii_strcasecmp(log_level, "message") == 0 || - g_ascii_strcasecmp(log_level, "notice") == 0) { - cfg->log_level = G_LOG_LEVEL_MESSAGE; - } - else if (g_ascii_strcasecmp(log_level, "silent") == 0) { - cfg->log_level = G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO; - cfg->log_silent_workers = TRUE; - } - else if (g_ascii_strcasecmp(log_level, "debug") == 0) { - cfg->log_level = G_LOG_LEVEL_DEBUG; - } - else { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "invalid log level: %s", - log_level); - return FALSE; - } - } - - /* Handle flags */ - val = ucl_object_lookup_any(obj, "color", "log_color", NULL); - if (val && ucl_object_toboolean(val)) { - cfg->log_flags |= RSPAMD_LOG_FLAG_COLOR; - } - - val = ucl_object_lookup_any(obj, "severity", "log_severity", NULL); - if (val && ucl_object_toboolean(val)) { - cfg->log_flags |= RSPAMD_LOG_FLAG_SEVERITY; - } - - val = ucl_object_lookup_any(obj, "systemd", "log_systemd", NULL); - if (val && ucl_object_toboolean(val)) { - cfg->log_flags |= RSPAMD_LOG_FLAG_SYSTEMD; - } - - val = ucl_object_lookup(obj, "log_re_cache"); - if (val && ucl_object_toboolean(val)) { - cfg->log_flags |= RSPAMD_LOG_FLAG_RE_CACHE; - } - - val = ucl_object_lookup_any(obj, "usec", "log_usec", NULL); - if (val && ucl_object_toboolean(val)) { - cfg->log_flags |= RSPAMD_LOG_FLAG_USEC; - } - - return rspamd_rcl_section_parse_defaults(cfg, section, cfg->cfg_pool, obj, - cfg, err); -} - -static gboolean -rspamd_rcl_options_handler(rspamd_mempool_t *pool, const ucl_object_t *obj, - const gchar *key, gpointer ud, - struct rspamd_rcl_section *section, GError **err) -{ - const ucl_object_t *dns, *upstream, *neighbours; - struct rspamd_config *cfg = ud; - struct rspamd_rcl_section *dns_section, *upstream_section, *neighbours_section; - - HASH_FIND_STR(section->subsections, "dns", dns_section); - - dns = ucl_object_lookup(obj, "dns"); - if (dns_section != NULL && dns != NULL) { - if (!rspamd_rcl_section_parse_defaults(cfg, - dns_section, cfg->cfg_pool, dns, - cfg, err)) { - return FALSE; - } - } - - HASH_FIND_STR(section->subsections, "upstream", upstream_section); - - upstream = ucl_object_lookup_any(obj, "upstream", "upstreams", NULL); - if (upstream_section != NULL && upstream != NULL) { - if (!rspamd_rcl_section_parse_defaults(cfg, - upstream_section, cfg->cfg_pool, - upstream, cfg, err)) { - return FALSE; - } - } - - HASH_FIND_STR(section->subsections, "neighbours", neighbours_section); - - neighbours = ucl_object_lookup(obj, "neighbours"); - if (neighbours_section != NULL && neighbours != NULL) { - const ucl_object_t *cur; - - LL_FOREACH(neighbours, cur) - { - if (!rspamd_rcl_process_section(cfg, neighbours_section, cfg, cur, - pool, err)) { - return FALSE; - } - } - } - - if (rspamd_rcl_section_parse_defaults(cfg, - section, cfg->cfg_pool, obj, - cfg, err)) { - /* We need to init this early */ - rspamd_multipattern_library_init(cfg->hs_cache_dir); - - return TRUE; - } - - return FALSE; -} - -struct rspamd_rcl_symbol_data { - struct rspamd_symbols_group *gr; - struct rspamd_config *cfg; -}; - -static gboolean -rspamd_rcl_group_handler(rspamd_mempool_t *pool, const ucl_object_t *obj, - const gchar *key, gpointer ud, - struct rspamd_rcl_section *section, GError **err) -{ - struct rspamd_config *cfg = ud; - struct rspamd_symbols_group *gr; - const ucl_object_t *val, *elt; - struct rspamd_rcl_section *subsection; - struct rspamd_rcl_symbol_data sd; - const gchar *description = NULL; - - g_assert(key != NULL); - - gr = g_hash_table_lookup(cfg->groups, key); - - if (gr == NULL) { - gr = rspamd_config_new_group(cfg, key); - } - - if (!rspamd_rcl_section_parse_defaults(cfg, section, pool, obj, - gr, err)) { - return FALSE; - } - - if ((elt = ucl_object_lookup(obj, "one_shot")) != NULL) { - if (ucl_object_type(elt) != UCL_BOOLEAN) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "one_shot attribute is not boolean for symbol: '%s'", - key); - - return FALSE; - } - if (ucl_object_toboolean(elt)) { - gr->flags |= RSPAMD_SYMBOL_GROUP_ONE_SHOT; - } - } - - if ((elt = ucl_object_lookup(obj, "disabled")) != NULL) { - if (ucl_object_type(elt) != UCL_BOOLEAN) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "disabled attribute is not boolean for symbol: '%s'", - key); - - return FALSE; - } - if (ucl_object_toboolean(elt)) { - gr->flags |= RSPAMD_SYMBOL_GROUP_DISABLED; - } - } - - if ((elt = ucl_object_lookup(obj, "enabled")) != NULL) { - if (ucl_object_type(elt) != UCL_BOOLEAN) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "enabled attribute is not boolean for symbol: '%s'", - key); - - return FALSE; - } - if (!ucl_object_toboolean(elt)) { - gr->flags |= RSPAMD_SYMBOL_GROUP_DISABLED; - } - } - - if ((elt = ucl_object_lookup(obj, "public")) != NULL) { - if (ucl_object_type(elt) != UCL_BOOLEAN) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "public attribute is not boolean for symbol: '%s'", - key); - - return FALSE; - } - if (ucl_object_toboolean(elt)) { - gr->flags |= RSPAMD_SYMBOL_GROUP_PUBLIC; - } - } - - if ((elt = ucl_object_lookup(obj, "private")) != NULL) { - if (ucl_object_type(elt) != UCL_BOOLEAN) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "private attribute is not boolean for symbol: '%s'", - key); - - return FALSE; - } - if (!ucl_object_toboolean(elt)) { - gr->flags |= RSPAMD_SYMBOL_GROUP_PUBLIC; - } - } - - elt = ucl_object_lookup(obj, "description"); - if (elt) { - description = ucl_object_tostring(elt); - - gr->description = rspamd_mempool_strdup(cfg->cfg_pool, - description); - } - - sd.gr = gr; - sd.cfg = cfg; - - /* Handle symbols */ - val = ucl_object_lookup(obj, "symbols"); - if (val != NULL && ucl_object_type(val) == UCL_OBJECT) { - HASH_FIND_STR(section->subsections, "symbols", subsection); - g_assert(subsection != NULL); - if (!rspamd_rcl_process_section(cfg, subsection, &sd, val, - pool, err)) { - - return FALSE; - } - } - - return TRUE; -} - -static gboolean -rspamd_rcl_symbol_handler(rspamd_mempool_t *pool, const ucl_object_t *obj, - const gchar *key, gpointer ud, - struct rspamd_rcl_section *section, GError **err) -{ - struct rspamd_rcl_symbol_data *sd = ud; - struct rspamd_config *cfg; - const ucl_object_t *elt; - const gchar *description = NULL; - gdouble score = NAN; - guint priority = 1, flags = 0; - gint nshots = 0; - - g_assert(key != NULL); - cfg = sd->cfg; - - if ((elt = ucl_object_lookup(obj, "one_shot")) != NULL) { - if (ucl_object_type(elt) != UCL_BOOLEAN) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "one_shot attribute is not boolean for symbol: '%s'", - key); - - return FALSE; - } - if (ucl_object_toboolean(elt)) { - nshots = 1; - } - } - - if ((elt = ucl_object_lookup(obj, "any_shot")) != NULL) { - if (ucl_object_type(elt) != UCL_BOOLEAN) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "any_shot attribute is not boolean for symbol: '%s'", - key); - - return FALSE; - } - if (ucl_object_toboolean(elt)) { - nshots = -1; - } - } - - if ((elt = ucl_object_lookup(obj, "one_param")) != NULL) { - if (ucl_object_type(elt) != UCL_BOOLEAN) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "one_param attribute is not boolean for symbol: '%s'", - key); - - return FALSE; - } - - if (ucl_object_toboolean(elt)) { - flags |= RSPAMD_SYMBOL_FLAG_ONEPARAM; - } - } - - if ((elt = ucl_object_lookup(obj, "ignore")) != NULL) { - if (ucl_object_type(elt) != UCL_BOOLEAN) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "ignore attribute is not boolean for symbol: '%s'", - key); - - return FALSE; - } - - if (ucl_object_toboolean(elt)) { - flags |= RSPAMD_SYMBOL_FLAG_IGNORE_METRIC; - } - } - - if ((elt = ucl_object_lookup(obj, "enabled")) != NULL) { - if (ucl_object_type(elt) != UCL_BOOLEAN) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "enabled attribute is not boolean for symbol: '%s'", - key); - - return FALSE; - } - - if (!ucl_object_toboolean(elt)) { - flags |= RSPAMD_SYMBOL_FLAG_DISABLED; - } - } - - if ((elt = ucl_object_lookup(obj, "nshots")) != NULL) { - if (ucl_object_type(elt) != UCL_FLOAT && ucl_object_type(elt) != UCL_INT) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "nshots attribute is not numeric for symbol: '%s'", - key); - - return FALSE; - } - - nshots = ucl_object_toint(elt); - } - - elt = ucl_object_lookup_any(obj, "score", "weight", NULL); - if (elt) { - if (ucl_object_type(elt) != UCL_FLOAT && ucl_object_type(elt) != UCL_INT) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "score attribute is not numeric for symbol: '%s'", - key); - - return FALSE; - } - - score = ucl_object_todouble(elt); - } - - elt = ucl_object_lookup(obj, "priority"); - if (elt) { - if (ucl_object_type(elt) != UCL_FLOAT && ucl_object_type(elt) != UCL_INT) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "priority attribute is not numeric for symbol: '%s'", - key); - - return FALSE; - } - - priority = ucl_object_toint(elt); - } - else { - priority = ucl_object_get_priority(obj) + 1; - } - - elt = ucl_object_lookup(obj, "description"); - if (elt) { - description = ucl_object_tostring(elt); - } - - if (sd->gr) { - rspamd_config_add_symbol(cfg, key, score, - description, sd->gr->name, flags, priority, nshots); - } - else { - rspamd_config_add_symbol(cfg, key, score, - description, NULL, flags, priority, nshots); - } - - 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); - } - - return TRUE; -} - -static gboolean -rspamd_rcl_actions_handler(rspamd_mempool_t *pool, const ucl_object_t *obj, - const gchar *key, gpointer ud, - struct rspamd_rcl_section *section, GError **err) -{ - struct rspamd_config *cfg = ud; - const ucl_object_t *cur; - ucl_object_iter_t it; - - it = ucl_object_iterate_new(obj); - - while ((cur = ucl_object_iterate_safe(it, true)) != NULL) { - gint type = ucl_object_type(cur); - - if (type == UCL_NULL) { - rspamd_config_maybe_disable_action(cfg, ucl_object_key(cur), - ucl_object_get_priority(cur)); - } - else if (type == UCL_OBJECT || type == UCL_FLOAT || type == UCL_INT) { - /* Exceptions */ - struct rspamd_rcl_default_handler_data *sec_cur, *sec_tmp; - gboolean default_elt = FALSE; - - HASH_ITER(hh, section->default_parser, sec_cur, sec_tmp) - { - if (strcmp(ucl_object_key(cur), sec_cur->key) == 0) { - default_elt = TRUE; - } - } - - if (default_elt) { - continue; - } - - /* Something non-default */ - if (!rspamd_config_set_action_score(cfg, - ucl_object_key(cur), - cur)) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "invalid action definition for: '%s'", - ucl_object_key(cur)); - ucl_object_iterate_free(it); - - return FALSE; - } - } - } - - ucl_object_iterate_free(it); - - return rspamd_rcl_section_parse_defaults(cfg, section, pool, obj, cfg, err); -} - -static gboolean -rspamd_rcl_worker_handler(rspamd_mempool_t *pool, const ucl_object_t *obj, - const gchar *key, gpointer ud, - struct rspamd_rcl_section *section, GError **err) -{ - const ucl_object_t *val, *cur, *cur_obj; - ucl_object_t *robj; - ucl_object_iter_t it = NULL; - const gchar *worker_type, *worker_bind; - struct rspamd_config *cfg = ud; - GQuark qtype; - struct rspamd_worker_conf *wrk; - struct rspamd_worker_cfg_parser *wparser; - struct rspamd_worker_param_parser *whandler; - struct rspamd_worker_param_key srch; - - g_assert(key != NULL); - worker_type = key; - - qtype = g_quark_try_string(worker_type); - if (qtype != 0) { - wrk = rspamd_config_new_worker(cfg, NULL); - wrk->options = ucl_object_copy(obj); - wrk->worker = rspamd_get_worker_by_type(cfg, qtype); - - if (wrk->worker == NULL) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "unknown worker type: %s", - worker_type); - return FALSE; - } - - wrk->type = qtype; - - if (wrk->worker->worker_init_func) { - wrk->ctx = wrk->worker->worker_init_func(cfg); - } - } - else { - msg_err_config("unknown worker type: %s", worker_type); - return TRUE; - } - - val = ucl_object_lookup_any(obj, "bind_socket", "listen", "bind", NULL); - /* This name is more logical */ - if (val != NULL) { - it = ucl_object_iterate_new(val); - - while ((cur = ucl_object_iterate_safe(it, true)) != NULL) { - if (!ucl_object_tostring_safe(cur, &worker_bind)) { - continue; - } - if (!rspamd_parse_bind_line(cfg, wrk, worker_bind)) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "cannot parse bind line: %s", - worker_bind); - ucl_object_iterate_free(it); - return FALSE; - } - } - - ucl_object_iterate_free(it); - } - - if (!rspamd_rcl_section_parse_defaults(cfg, section, cfg->cfg_pool, obj, - wrk, err)) { - return FALSE; - } - - /* Parse other attributes */ - wparser = g_hash_table_lookup(cfg->wrk_parsers, &qtype); - - if (wparser != NULL && obj->type == UCL_OBJECT) { - it = ucl_object_iterate_new(obj); - - while ((cur = ucl_object_iterate_full(it, UCL_ITERATE_EXPLICIT)) != NULL) { - srch.name = ucl_object_key(cur); - srch.ptr = wrk->ctx; /* XXX: is it valid? Update! no, it is not valid, omfg... */ - whandler = g_hash_table_lookup(wparser->parsers, &srch); - - if (whandler != NULL) { - - LL_FOREACH(cur, cur_obj) - { - if (!whandler->handler(cfg->cfg_pool, - cur_obj, - &whandler->parser, - section, - err)) { - - ucl_object_iterate_free(it); - return FALSE; - } - - if (!(whandler->parser.flags & RSPAMD_CL_FLAG_MULTIPLE)) { - break; - } - } - } - } - - ucl_object_iterate_free(it); - - if (wparser->def_obj_parser != NULL) { - robj = ucl_object_ref(obj); - - if (!wparser->def_obj_parser(robj, wparser->def_ud)) { - ucl_object_unref(robj); - - return FALSE; - } - - ucl_object_unref(robj); - } - } - - cfg->workers = g_list_prepend(cfg->workers, wrk); - - return TRUE; -} - -static gboolean -rspamd_rcl_lua_handler(rspamd_mempool_t *pool, const ucl_object_t *obj, - const gchar *key, gpointer ud, - struct rspamd_rcl_section *section, GError **err) -{ - struct rspamd_config *cfg = ud; - const gchar *lua_src = rspamd_mempool_strdup(pool, - ucl_object_tostring(obj)); - gchar *cur_dir, *lua_dir, *lua_file; - lua_State *L = cfg->lua_state; - gint err_idx; - - lua_dir = g_path_get_dirname(lua_src); - lua_file = g_path_get_basename(lua_src); - - if (lua_dir && lua_file) { - cur_dir = g_malloc(PATH_MAX); - if (getcwd(cur_dir, PATH_MAX) != NULL && chdir(lua_dir) != -1) { - /* Push traceback function */ - lua_pushcfunction(L, &rspamd_lua_traceback); - err_idx = lua_gettop(L); - - /* Load file */ - if (luaL_loadfile(L, lua_file) != 0) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "cannot load lua file %s: %s", - lua_src, - lua_tostring(L, -1)); - if (chdir(cur_dir) == -1) { - msg_err_config("cannot chdir to %s: %s", cur_dir, - strerror(errno)); - } - g_free(cur_dir); - g_free(lua_dir); - g_free(lua_file); - return FALSE; - } - - /* Now do it */ - if (lua_pcall(L, 0, 0, err_idx) != 0) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "cannot init lua file %s: %s", - lua_src, - lua_tostring(L, -1)); - lua_settop(L, 0); - - if (chdir(cur_dir) == -1) { - msg_err_config("cannot chdir to %s: %s", cur_dir, - strerror(errno)); - } - - g_free(cur_dir); - g_free(lua_file); - g_free(lua_dir); - - return FALSE; - } - - lua_pop(L, 1); - } - else { - g_set_error(err, CFG_RCL_ERROR, ENOENT, "cannot chdir to %s: %s", - lua_dir, strerror(errno)); - if (chdir(cur_dir) == -1) { - msg_err_config("cannot chdir to %s: %s", cur_dir, strerror(errno)); - } - g_free(cur_dir); - g_free(lua_dir); - g_free(lua_file); - return FALSE; - } - if (chdir(cur_dir) == -1) { - msg_err_config("cannot chdir to %s: %s", cur_dir, strerror(errno)); - } - g_free(cur_dir); - g_free(lua_dir); - g_free(lua_file); - } - else { - g_free(lua_dir); - g_free(lua_file); - g_set_error(err, CFG_RCL_ERROR, ENOENT, "cannot find to %s: %s", - lua_src, strerror(errno)); - return FALSE; - } - - return TRUE; -} - -gboolean -rspamd_rcl_add_lua_plugins_path(struct rspamd_config *cfg, - const gchar *path, - gboolean main_path, - GHashTable *modules_seen, - GError **err) -{ - struct stat st; - struct script_module *cur_mod, *seen_mod; - GPtrArray *paths; - gchar *fname, *ext_pos; - guint i; - - if (stat(path, &st) == -1) { - - if (errno != ENOENT || main_path) { - g_set_error(err, - CFG_RCL_ERROR, - errno, - "cannot stat path %s, %s", - path, - strerror(errno)); - return FALSE; - } - else { - msg_debug_config("optional plugins path %s is absent, skip it", path); - - return TRUE; - } - } - - /* Handle directory */ - if (S_ISDIR(st.st_mode)) { - paths = rspamd_glob_path(path, "*.lua", TRUE, err); - - if (!paths) { - return FALSE; - } - - PTR_ARRAY_FOREACH(paths, i, fname) - { - cur_mod = - rspamd_mempool_alloc(cfg->cfg_pool, - sizeof(struct script_module)); - cur_mod->path = rspamd_mempool_strdup(cfg->cfg_pool, fname); - cur_mod->name = g_path_get_basename(cur_mod->path); - rspamd_mempool_add_destructor(cfg->cfg_pool, g_free, - cur_mod->name); - ext_pos = strstr(cur_mod->name, ".lua"); - - if (ext_pos != NULL) { - *ext_pos = '\0'; - } - - if (modules_seen) { - seen_mod = g_hash_table_lookup(modules_seen, cur_mod->name); - - if (seen_mod != NULL) { - msg_info_config("already seen module %s at %s, skip %s", - cur_mod->name, seen_mod->path, cur_mod->path); - continue; - } - } - - if (cfg->script_modules == NULL) { - cfg->script_modules = g_list_append(cfg->script_modules, - cur_mod); - rspamd_mempool_add_destructor(cfg->cfg_pool, - (rspamd_mempool_destruct_t) g_list_free, - cfg->script_modules); - } - else { - cfg->script_modules = g_list_append(cfg->script_modules, - cur_mod); - } - - if (modules_seen) { - g_hash_table_insert(modules_seen, cur_mod->name, cur_mod); - } - } - - g_ptr_array_free(paths, TRUE); - } - else { - /* Handle single file */ - cur_mod = - rspamd_mempool_alloc(cfg->cfg_pool, sizeof(struct script_module)); - cur_mod->path = rspamd_mempool_strdup(cfg->cfg_pool, path); - cur_mod->name = g_path_get_basename(cur_mod->path); - rspamd_mempool_add_destructor(cfg->cfg_pool, g_free, - cur_mod->name); - ext_pos = strstr(cur_mod->name, ".lua"); - - if (ext_pos != NULL) { - *ext_pos = '\0'; - } - - if (modules_seen) { - seen_mod = g_hash_table_lookup(modules_seen, cur_mod->name); - - if (seen_mod != NULL) { - msg_info_config("already seen module %s at %s, skip %s", - cur_mod->name, seen_mod->path, cur_mod->path); - - return TRUE; - } - } - - if (cfg->script_modules == NULL) { - cfg->script_modules = g_list_append(cfg->script_modules, - cur_mod); - rspamd_mempool_add_destructor(cfg->cfg_pool, - (rspamd_mempool_destruct_t) g_list_free, - cfg->script_modules); - } - else { - cfg->script_modules = g_list_append(cfg->script_modules, - cur_mod); - } - - if (modules_seen) { - g_hash_table_insert(modules_seen, cur_mod->name, cur_mod); - } - } - - return TRUE; -} - -static gboolean -rspamd_rcl_modules_handler(rspamd_mempool_t *pool, const ucl_object_t *obj, - const gchar *key, gpointer ud, - struct rspamd_rcl_section *section, GError **err) -{ - const ucl_object_t *val, *cur; - struct rspamd_config *cfg = ud; - const gchar *data; - - if (obj->type == UCL_OBJECT) { - GHashTable *mods_seen = g_hash_table_new(rspamd_strcase_hash, - rspamd_strcase_equal); - val = ucl_object_lookup(obj, "path"); - - if (val) { - LL_FOREACH(val, cur) - { - if (ucl_object_tostring_safe(cur, &data)) { - if (!rspamd_rcl_add_lua_plugins_path(cfg, - rspamd_mempool_strdup(cfg->cfg_pool, data), - TRUE, - mods_seen, - err)) { - g_hash_table_unref(mods_seen); - - return FALSE; - } - } - } - } - else { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "path attribute is missing"); - g_hash_table_unref(mods_seen); - - return FALSE; - } - - val = ucl_object_lookup(obj, "fallback_path"); - - if (val) { - LL_FOREACH(val, cur) - { - if (ucl_object_tostring_safe(cur, &data)) { - if (!rspamd_rcl_add_lua_plugins_path(cfg, - rspamd_mempool_strdup(cfg->cfg_pool, data), - FALSE, - mods_seen, - err)) { - g_hash_table_unref(mods_seen); - - return FALSE; - } - } - } - } - - val = ucl_object_lookup(obj, "try_path"); - - if (val) { - LL_FOREACH(val, cur) - { - if (ucl_object_tostring_safe(cur, &data)) { - if (!rspamd_rcl_add_lua_plugins_path(cfg, - rspamd_mempool_strdup(cfg->cfg_pool, data), - FALSE, - mods_seen, - err)) { - g_hash_table_unref(mods_seen); - - return FALSE; - } - } - } - } - - g_hash_table_unref(mods_seen); - } - else if (ucl_object_tostring_safe(obj, &data)) { - if (!rspamd_rcl_add_lua_plugins_path(cfg, - rspamd_mempool_strdup(cfg->cfg_pool, data), TRUE, NULL, err)) { - return FALSE; - } - } - else { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "module parameter has wrong type (must be an object or a string)"); - return FALSE; - } - - return TRUE; -} - -struct statfile_parser_data { - struct rspamd_config *cfg; - struct rspamd_classifier_config *ccf; -}; - -static gboolean -rspamd_rcl_statfile_handler(rspamd_mempool_t *pool, const ucl_object_t *obj, - const gchar *key, gpointer ud, - struct rspamd_rcl_section *section, GError **err) -{ - struct statfile_parser_data *stud = ud; - struct rspamd_classifier_config *ccf; - struct rspamd_config *cfg; - const ucl_object_t *val; - struct rspamd_statfile_config *st; - GList *labels; - - g_assert(key != NULL); - - cfg = stud->cfg; - ccf = stud->ccf; - - st = rspamd_config_new_statfile(cfg, NULL); - st->symbol = rspamd_mempool_strdup(cfg->cfg_pool, key); - - if (rspamd_rcl_section_parse_defaults(cfg, section, pool, obj, st, err)) { - ccf->statfiles = rspamd_mempool_glist_prepend(pool, ccf->statfiles, st); - - if (st->label != NULL) { - labels = g_hash_table_lookup(ccf->labels, st->label); - if (labels != NULL) { - labels = g_list_append(labels, st); - } - else { - g_hash_table_insert(ccf->labels, st->label, - g_list_prepend(NULL, st)); - } - } - - if (st->symbol != NULL) { - g_hash_table_insert(cfg->classifiers_symbols, st->symbol, st); - } - else { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "statfile must have a symbol defined"); - return FALSE; - } - - st->opts = (ucl_object_t *) obj; - st->clcf = ccf; - - val = ucl_object_lookup(obj, "spam"); - if (val == NULL) { - msg_info_config( - "statfile %s has no explicit 'spam' setting, trying to guess by symbol", - st->symbol); - if (rspamd_substring_search_caseless(st->symbol, - strlen(st->symbol), "spam", 4) != -1) { - st->is_spam = TRUE; - } - else if (rspamd_substring_search_caseless(st->symbol, - strlen(st->symbol), "ham", 3) != -1) { - st->is_spam = FALSE; - } - else { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "cannot guess spam setting from %s", - st->symbol); - return FALSE; - } - msg_info_config("guessed that statfile with symbol %s is %s", - st->symbol, - st->is_spam ? "spam" : "ham"); - } - return TRUE; - } - - return FALSE; -} - -static gboolean -rspamd_rcl_classifier_handler(rspamd_mempool_t *pool, - const ucl_object_t *obj, - const gchar *key, - gpointer ud, - struct rspamd_rcl_section *section, - GError **err) -{ - const ucl_object_t *val, *cur; - ucl_object_iter_t it = NULL; - struct rspamd_config *cfg = ud; - struct statfile_parser_data stud; - const gchar *st_key; - struct rspamd_classifier_config *ccf; - gboolean res = TRUE; - struct rspamd_rcl_section *stat_section; - struct rspamd_tokenizer_config *tkcf = NULL; - lua_State *L = cfg->lua_state; - - g_assert(key != NULL); - ccf = rspamd_config_new_classifier(cfg, NULL); - - ccf->classifier = rspamd_mempool_strdup(cfg->cfg_pool, key); - - if (rspamd_rcl_section_parse_defaults(cfg, section, cfg->cfg_pool, obj, - ccf, err)) { - - HASH_FIND_STR(section->subsections, "statfile", stat_section); - - if (ccf->classifier == NULL) { - ccf->classifier = "bayes"; - } - - if (ccf->name == NULL) { - ccf->name = ccf->classifier; - } - - it = ucl_object_iterate_new(obj); - - while ((val = ucl_object_iterate_safe(it, true)) != NULL && res) { - st_key = ucl_object_key(val); - - if (st_key != NULL) { - if (g_ascii_strcasecmp(st_key, "statfile") == 0) { - LL_FOREACH(val, cur) - { - stud.cfg = cfg; - stud.ccf = ccf; - res = rspamd_rcl_process_section(cfg, stat_section, &stud, - cur, cfg->cfg_pool, err); - - if (!res) { - ucl_object_iterate_free(it); - - return FALSE; - } - } - } - else if (g_ascii_strcasecmp(st_key, "tokenizer") == 0) { - tkcf = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(*tkcf)); - - if (ucl_object_type(val) == UCL_STRING) { - tkcf->name = ucl_object_tostring(val); - } - else if (ucl_object_type(val) == UCL_OBJECT) { - cur = ucl_object_lookup(val, "name"); - if (cur != NULL) { - tkcf->name = ucl_object_tostring(cur); - tkcf->opts = val; - } - else { - cur = ucl_object_lookup(val, "type"); - if (cur != NULL) { - tkcf->name = ucl_object_tostring(cur); - tkcf->opts = val; - } - } - } - } - } - } - - ucl_object_iterate_free(it); - } - else { - msg_err_config("fatal configuration error, cannot parse statfile definition"); - } - - if (tkcf == NULL) { - tkcf = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(*tkcf)); - tkcf->name = NULL; - } - - ccf->tokenizer = tkcf; - - /* Handle lua conditions */ - val = ucl_object_lookup_any(obj, "learn_condition", NULL); - - if (val) { - LL_FOREACH(val, cur) - { - if (ucl_object_type(cur) == UCL_STRING) { - const gchar *lua_script; - gsize slen; - gint ref_idx; - - lua_script = ucl_object_tolstring(cur, &slen); - ref_idx = rspamd_lua_function_ref_from_str(L, - lua_script, slen, "learn_condition", err); - - if (ref_idx == LUA_NOREF) { - return FALSE; - } - - rspamd_lua_add_ref_dtor(L, cfg->cfg_pool, ref_idx); - ccf->learn_conditions = rspamd_mempool_glist_append( - cfg->cfg_pool, - ccf->learn_conditions, - GINT_TO_POINTER(ref_idx)); - } - } - } - - val = ucl_object_lookup_any(obj, "classify_condition", NULL); - - if (val) { - LL_FOREACH(val, cur) - { - if (ucl_object_type(cur) == UCL_STRING) { - const gchar *lua_script; - gsize slen; - gint ref_idx; - - lua_script = ucl_object_tolstring(cur, &slen); - ref_idx = rspamd_lua_function_ref_from_str(L, - lua_script, slen, "classify_condition", err); - - if (ref_idx == LUA_NOREF) { - return FALSE; - } - - rspamd_lua_add_ref_dtor(L, cfg->cfg_pool, ref_idx); - ccf->classify_conditions = rspamd_mempool_glist_append( - cfg->cfg_pool, - ccf->classify_conditions, - GINT_TO_POINTER(ref_idx)); - } - } - } - - ccf->opts = (ucl_object_t *) obj; - cfg->classifiers = g_list_prepend(cfg->classifiers, ccf); - - return res; -} - -static gboolean -rspamd_rcl_composite_handler(rspamd_mempool_t *pool, - const ucl_object_t *obj, - const gchar *key, - gpointer ud, - struct rspamd_rcl_section *section, - GError **err) -{ - struct rspamd_config *cfg = ud; - void *composite; - const gchar *composite_name; - - g_assert(key != NULL); - - composite_name = key; - - const ucl_object_t *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 ((composite = rspamd_composites_manager_add_from_ucl(cfg->composites_manager, - composite_name, obj)) != NULL) { - rspamd_symcache_add_symbol(cfg->cache, composite_name, 0, - NULL, composite, SYMBOL_TYPE_COMPOSITE, -1); - } - - return composite != NULL; -} - -static gboolean -rspamd_rcl_composites_handler(rspamd_mempool_t *pool, - const ucl_object_t *obj, - const gchar *key, - gpointer ud, - struct rspamd_rcl_section *section, - GError **err) -{ - ucl_object_iter_t it = NULL; - const ucl_object_t *cur; - gboolean success = TRUE; - - it = ucl_object_iterate_new(obj); - - while ((cur = ucl_object_iterate_safe(it, true))) { - success = rspamd_rcl_composite_handler(pool, cur, - ucl_object_key(cur), ud, section, err); - if (!success) { - break; - } - } - - ucl_object_iterate_free(it); - - return success; -} - -static gboolean -rspamd_rcl_neighbours_handler(rspamd_mempool_t *pool, - const ucl_object_t *obj, - const gchar *key, - gpointer ud, - struct rspamd_rcl_section *section, - GError **err) -{ - struct rspamd_config *cfg = ud; - const ucl_object_t *hostval, *pathval; - ucl_object_t *neigh; - gboolean has_port = FALSE, has_proto = FALSE; - GString *urlstr; - const gchar *p; - - if (key == NULL) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "missing name for neighbour"); - return FALSE; - } - - hostval = ucl_object_lookup(obj, "host"); - - if (hostval == NULL || ucl_object_type(hostval) != UCL_STRING) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "missing host for neighbour: %s", ucl_object_key(obj)); - return FALSE; - } - - neigh = ucl_object_typed_new(UCL_OBJECT); - ucl_object_insert_key(neigh, ucl_object_copy(hostval), "host", 0, false); - - if ((p = strrchr(ucl_object_tostring(hostval), ':')) != NULL) { - if (g_ascii_isdigit(p[1])) { - has_port = TRUE; - } - } - - if (strstr(ucl_object_tostring(hostval), "://") != NULL) { - has_proto = TRUE; - } - - /* Now make url */ - urlstr = g_string_sized_new(63); - pathval = ucl_object_lookup(obj, "path"); - - if (!has_proto) { - g_string_append_len(urlstr, "http://", sizeof("http://") - 1); - } - - g_string_append(urlstr, ucl_object_tostring(hostval)); - - if (!has_port) { - g_string_append(urlstr, ":11334"); - } - - if (pathval == NULL) { - g_string_append(urlstr, "/"); - } - else { - g_string_append(urlstr, ucl_object_tostring(pathval)); - } - - ucl_object_insert_key(neigh, - ucl_object_fromlstring(urlstr->str, urlstr->len), - "url", 0, false); - g_string_free(urlstr, TRUE); - ucl_object_insert_key(cfg->neighbours, neigh, key, 0, true); - - return TRUE; -} - - -struct rspamd_rcl_section * -rspamd_rcl_add_section(struct rspamd_rcl_section **top, - const gchar *name, const gchar *key_attr, rspamd_rcl_handler_t handler, - enum ucl_type type, gboolean required, gboolean strict_type) -{ - struct rspamd_rcl_section *new; - ucl_object_t *parent_doc; - - new = g_malloc0(sizeof(struct rspamd_rcl_section)); - new->name = name; - new->key_attr = key_attr; - new->handler = handler; - new->type = type; - new->strict_type = strict_type; - - if (*top == NULL) { - parent_doc = NULL; - new->doc_ref = NULL; - } - else { - parent_doc = (*top)->doc_ref; - new->doc_ref = ucl_object_ref(rspamd_rcl_add_doc_obj(parent_doc, - NULL, - name, - type, - NULL, - 0, - NULL, - 0)); - } - - HASH_ADD_KEYPTR(hh, *top, new->name, strlen(new->name), new); - return new; -} - -struct rspamd_rcl_section * -rspamd_rcl_add_section_doc(struct rspamd_rcl_section **top, - const gchar *name, const gchar *key_attr, rspamd_rcl_handler_t handler, - enum ucl_type type, gboolean required, gboolean strict_type, - ucl_object_t *doc_target, - const gchar *doc_string) -{ - struct rspamd_rcl_section *new_section; - - new_section = g_malloc0(sizeof(struct rspamd_rcl_section)); - new_section->name = name; - new_section->key_attr = key_attr; - new_section->handler = handler; - new_section->type = type; - new_section->strict_type = strict_type; - - new_section->doc_ref = ucl_object_ref(rspamd_rcl_add_doc_obj(doc_target, - doc_string, - name, - type, - NULL, - 0, - NULL, - 0)); - - HASH_ADD_KEYPTR(hh, *top, new_section->name, strlen(new_section->name), new_section); - return new_section; -} - -struct rspamd_rcl_default_handler_data * -rspamd_rcl_add_default_handler(struct rspamd_rcl_section *section, - const gchar *name, - rspamd_rcl_default_handler_t handler, - goffset offset, - gint flags, - const gchar *doc_string) -{ - struct rspamd_rcl_default_handler_data *nhandler; - - nhandler = g_malloc0(sizeof(struct rspamd_rcl_default_handler_data)); - nhandler->key = g_strdup(name); - nhandler->handler = handler; - nhandler->pd.offset = offset; - nhandler->pd.flags = flags; - - if (section->doc_ref != NULL) { - rspamd_rcl_add_doc_obj(section->doc_ref, - doc_string, - name, - UCL_NULL, - handler, - flags, - NULL, - 0); - } - - HASH_ADD_KEYPTR(hh, section->default_parser, nhandler->key, strlen(nhandler->key), nhandler); - return nhandler; -} - -struct rspamd_rcl_section * -rspamd_rcl_config_init(struct rspamd_config *cfg, GHashTable *skip_sections) -{ - struct rspamd_rcl_section *new = NULL, *sub, *ssub; - - /* - * Important notice: - * the order of parsing is equal to order of this initialization, therefore - * it is possible to init some portions of config prior to others - */ - - /** - * Logging section - */ - if (!(skip_sections && g_hash_table_lookup(skip_sections, "logging"))) { - sub = rspamd_rcl_add_section_doc(&new, - "logging", NULL, - rspamd_rcl_logging_handler, - UCL_OBJECT, - FALSE, - TRUE, - cfg->doc_strings, - "Configure rspamd logging"); - /* Default handlers */ - rspamd_rcl_add_default_handler(sub, - "log_buffer", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, log_buf_size), - RSPAMD_CL_FLAG_INT_32, - "Size of log buffer in bytes (for file logging)"); - rspamd_rcl_add_default_handler(sub, - "log_urls", - rspamd_rcl_parse_struct_boolean, - G_STRUCT_OFFSET(struct rspamd_config, log_urls), - 0, - "Write each URL found in a message to the log file"); - rspamd_rcl_add_default_handler(sub, - "debug_ip", - rspamd_rcl_parse_struct_ucl, - G_STRUCT_OFFSET(struct rspamd_config, debug_ip_map), - 0, - "Enable debugging log for the specified IP addresses"); - rspamd_rcl_add_default_handler(sub, - "debug_modules", - rspamd_rcl_parse_struct_string_list, - G_STRUCT_OFFSET(struct rspamd_config, debug_modules), - RSPAMD_CL_FLAG_STRING_LIST_HASH, - "Enable debugging for the specified modules"); - rspamd_rcl_add_default_handler(sub, - "log_format", - rspamd_rcl_parse_struct_string, - G_STRUCT_OFFSET(struct rspamd_config, log_format_str), - 0, - "Specify format string for the task logging output " - "(https://rspamd.com/doc/configuration/logging.html " - "for details)"); - rspamd_rcl_add_default_handler(sub, - "encryption_key", - rspamd_rcl_parse_struct_pubkey, - G_STRUCT_OFFSET(struct rspamd_config, log_encryption_key), - 0, - "Encrypt sensitive information in logs using this pubkey"); - rspamd_rcl_add_default_handler(sub, - "error_elts", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, log_error_elts), - RSPAMD_CL_FLAG_UINT, - "Size of circular buffer for last errors (10 by default)"); - rspamd_rcl_add_default_handler(sub, - "error_maxlen", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, log_error_elt_maxlen), - RSPAMD_CL_FLAG_UINT, - "Size of each element in error log buffer (1000 by default)"); - - /* Documentation only options, handled in log_handler to map flags */ - rspamd_rcl_add_doc_by_path(cfg, - "logging", - "Enable colored output (for console logging)", - "log_color", - UCL_BOOLEAN, - NULL, - 0, - NULL, - 0); - rspamd_rcl_add_doc_by_path(cfg, - "logging", - "Enable severity logging output (e.g. [error] or [warning])", - "log_severity", - UCL_BOOLEAN, - NULL, - 0, - NULL, - 0); - rspamd_rcl_add_doc_by_path(cfg, - "logging", - "Enable systemd compatible logging", - "systemd", - UCL_BOOLEAN, - NULL, - 0, - NULL, - 0); - rspamd_rcl_add_doc_by_path(cfg, - "logging", - "Write statistics of regexp processing to log (useful for hyperscan)", - "log_re_cache", - UCL_BOOLEAN, - NULL, - 0, - NULL, - 0); - rspamd_rcl_add_doc_by_path(cfg, - "logging", - "Use microseconds resolution for timestamps", - "log_usec", - UCL_BOOLEAN, - NULL, - 0, - NULL, - 0); - } - if (!(skip_sections && g_hash_table_lookup(skip_sections, "options"))) { - /** - * Options section - */ - sub = rspamd_rcl_add_section_doc(&new, - "options", NULL, - rspamd_rcl_options_handler, - UCL_OBJECT, - FALSE, - TRUE, - cfg->doc_strings, - "Global rspamd options"); - rspamd_rcl_add_default_handler(sub, - "cache_file", - rspamd_rcl_parse_struct_string, - G_STRUCT_OFFSET(struct rspamd_config, cache_filename), - RSPAMD_CL_FLAG_STRING_PATH, - "Path to the cache file"); - rspamd_rcl_add_default_handler(sub, - "cache_reload", - rspamd_rcl_parse_struct_time, - G_STRUCT_OFFSET(struct rspamd_config, cache_reload_time), - RSPAMD_CL_FLAG_TIME_FLOAT, - "How often cache reload should be performed"); - /* Old DNS configuration */ - rspamd_rcl_add_default_handler(sub, - "dns_nameserver", - rspamd_rcl_parse_struct_ucl, - G_STRUCT_OFFSET(struct rspamd_config, nameservers), - 0, - "Legacy option for DNS servers used"); - rspamd_rcl_add_default_handler(sub, - "dns_timeout", - rspamd_rcl_parse_struct_time, - G_STRUCT_OFFSET(struct rspamd_config, dns_timeout), - RSPAMD_CL_FLAG_TIME_FLOAT, - "Legacy option for DNS request timeout"); - rspamd_rcl_add_default_handler(sub, - "dns_retransmits", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, dns_retransmits), - RSPAMD_CL_FLAG_INT_32, - "Legacy option for DNS retransmits count"); - rspamd_rcl_add_default_handler(sub, - "dns_sockets", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, dns_io_per_server), - RSPAMD_CL_FLAG_INT_32, - "Legacy option for DNS sockets per server count"); - rspamd_rcl_add_default_handler(sub, - "dns_max_requests", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, dns_max_requests), - RSPAMD_CL_FLAG_INT_32, - "Maximum DNS requests per task (default: 64)"); - rspamd_rcl_add_default_handler(sub, - "control_socket", - rspamd_rcl_parse_struct_string, - G_STRUCT_OFFSET(struct rspamd_config, control_socket_path), - 0, - "Path to the control socket"); - rspamd_rcl_add_default_handler(sub, - "explicit_modules", - rspamd_rcl_parse_struct_string_list, - G_STRUCT_OFFSET(struct rspamd_config, explicit_modules), - RSPAMD_CL_FLAG_STRING_LIST_HASH, - "Always load these modules even if they are not configured explicitly"); - rspamd_rcl_add_default_handler(sub, - "allow_raw_input", - rspamd_rcl_parse_struct_boolean, - G_STRUCT_OFFSET(struct rspamd_config, allow_raw_input), - 0, - "Allow non MIME input for rspamd"); - rspamd_rcl_add_default_handler(sub, - "one_shot", - rspamd_rcl_parse_struct_boolean, - G_STRUCT_OFFSET(struct rspamd_config, one_shot_mode), - 0, - "Add all symbols only once per message"); - rspamd_rcl_add_default_handler(sub, - "check_attachements", - rspamd_rcl_parse_struct_boolean, - G_STRUCT_OFFSET(struct rspamd_config, check_text_attachements), - 0, - "Treat text attachments as normal text parts"); - rspamd_rcl_add_default_handler(sub, - "tempdir", - rspamd_rcl_parse_struct_string, - G_STRUCT_OFFSET(struct rspamd_config, temp_dir), - RSPAMD_CL_FLAG_STRING_PATH, - "Directory for temporary files"); - rspamd_rcl_add_default_handler(sub, - "pidfile", - rspamd_rcl_parse_struct_string, - G_STRUCT_OFFSET(struct rspamd_config, pid_file), - RSPAMD_CL_FLAG_STRING_PATH, - "Path to the pid file"); - rspamd_rcl_add_default_handler(sub, - "filters", - rspamd_rcl_parse_struct_string_list, - G_STRUCT_OFFSET(struct rspamd_config, filters), - 0, - "List of internal filters enabled"); - rspamd_rcl_add_default_handler(sub, - "map_watch_interval", - rspamd_rcl_parse_struct_time, - G_STRUCT_OFFSET(struct rspamd_config, map_timeout), - RSPAMD_CL_FLAG_TIME_FLOAT, - "Interval for checking maps"); - rspamd_rcl_add_default_handler(sub, - "map_file_watch_multiplier", - rspamd_rcl_parse_struct_double, - G_STRUCT_OFFSET(struct rspamd_config, map_file_watch_multiplier), - 0, - "Multiplier for map watch interval when map is file"); - rspamd_rcl_add_default_handler(sub, - "maps_cache_dir", - rspamd_rcl_parse_struct_string, - G_STRUCT_OFFSET(struct rspamd_config, maps_cache_dir), - 0, - "Directory to save maps cached data (default: $DBDIR)"); - rspamd_rcl_add_default_handler(sub, - "monitoring_watch_interval", - rspamd_rcl_parse_struct_time, - G_STRUCT_OFFSET(struct rspamd_config, monitored_interval), - RSPAMD_CL_FLAG_TIME_FLOAT, - "Interval for checking monitored instances"); - rspamd_rcl_add_default_handler(sub, - "disable_monitoring", - rspamd_rcl_parse_struct_boolean, - G_STRUCT_OFFSET(struct rspamd_config, disable_monitored), - 0, - "Disable monitoring completely"); - rspamd_rcl_add_default_handler(sub, - "fips_mode", - rspamd_rcl_parse_struct_boolean, - G_STRUCT_OFFSET(struct rspamd_config, fips_mode), - 0, - "Enable FIPS 140-2 mode in OpenSSL"); - rspamd_rcl_add_default_handler(sub, - "dynamic_conf", - rspamd_rcl_parse_struct_string, - G_STRUCT_OFFSET(struct rspamd_config, dynamic_conf), - 0, - "Path to the dynamic configuration"); - rspamd_rcl_add_default_handler(sub, - "rrd", - rspamd_rcl_parse_struct_string, - G_STRUCT_OFFSET(struct rspamd_config, rrd_file), - RSPAMD_CL_FLAG_STRING_PATH, - "Path to RRD file"); - rspamd_rcl_add_default_handler(sub, - "stats_file", - rspamd_rcl_parse_struct_string, - G_STRUCT_OFFSET(struct rspamd_config, stats_file), - RSPAMD_CL_FLAG_STRING_PATH, - "Path to stats file"); - rspamd_rcl_add_default_handler(sub, - "history_file", - rspamd_rcl_parse_struct_string, - G_STRUCT_OFFSET(struct rspamd_config, history_file), - RSPAMD_CL_FLAG_STRING_PATH, - "Path to history file"); - rspamd_rcl_add_default_handler(sub, - "check_all_filters", - rspamd_rcl_parse_struct_boolean, - G_STRUCT_OFFSET(struct rspamd_config, check_all_filters), - 0, - "Always check all filters"); - rspamd_rcl_add_default_handler(sub, - "public_groups_only", - rspamd_rcl_parse_struct_boolean, - G_STRUCT_OFFSET(struct rspamd_config, public_groups_only), - 0, - "Output merely public groups everywhere"); - rspamd_rcl_add_default_handler(sub, - "enable_test_patterns", - rspamd_rcl_parse_struct_boolean, - G_STRUCT_OFFSET(struct rspamd_config, enable_test_patterns), - 0, - "Enable test GTUBE like patterns (not for production!)"); - rspamd_rcl_add_default_handler(sub, - "enable_css_parser", - rspamd_rcl_parse_struct_boolean, - G_STRUCT_OFFSET(struct rspamd_config, enable_css_parser), - 0, - "Enable CSS parser (experimental)"); - rspamd_rcl_add_default_handler(sub, - "enable_experimental", - rspamd_rcl_parse_struct_boolean, - G_STRUCT_OFFSET(struct rspamd_config, enable_experimental), - 0, - "Enable experimental plugins"); - rspamd_rcl_add_default_handler(sub, - "disable_pcre_jit", - rspamd_rcl_parse_struct_boolean, - G_STRUCT_OFFSET(struct rspamd_config, disable_pcre_jit), - 0, - "Disable PCRE JIT"); - rspamd_rcl_add_default_handler(sub, - "min_word_len", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, min_word_len), - RSPAMD_CL_FLAG_UINT, - "Minimum length of the word to be considered in statistics/fuzzy"); - rspamd_rcl_add_default_handler(sub, - "max_word_len", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, max_word_len), - RSPAMD_CL_FLAG_UINT, - "Maximum length of the word to be considered in statistics/fuzzy"); - rspamd_rcl_add_default_handler(sub, - "max_html_len", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, max_word_len), - RSPAMD_CL_FLAG_INT_SIZE, - "Maximum length of the html part to be parsed"); - rspamd_rcl_add_default_handler(sub, - "words_decay", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, words_decay), - RSPAMD_CL_FLAG_UINT, - "Start skipping words at this amount"); - rspamd_rcl_add_default_handler(sub, - "url_tld", - rspamd_rcl_parse_struct_string, - G_STRUCT_OFFSET(struct rspamd_config, tld_file), - RSPAMD_CL_FLAG_STRING_PATH, - "Path to the TLD file for urls detector"); - rspamd_rcl_add_default_handler(sub, - "tld", - rspamd_rcl_parse_struct_string, - G_STRUCT_OFFSET(struct rspamd_config, tld_file), - RSPAMD_CL_FLAG_STRING_PATH, - "Path to the TLD file for urls detector"); - rspamd_rcl_add_default_handler(sub, - "hs_cache_dir", - rspamd_rcl_parse_struct_string, - G_STRUCT_OFFSET(struct rspamd_config, hs_cache_dir), - RSPAMD_CL_FLAG_STRING_PATH, - "Path directory where rspamd would save hyperscan cache"); - rspamd_rcl_add_default_handler(sub, - "history_rows", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, history_rows), - RSPAMD_CL_FLAG_UINT, - "Number of records in the history file"); - rspamd_rcl_add_default_handler(sub, - "disable_hyperscan", - rspamd_rcl_parse_struct_boolean, - G_STRUCT_OFFSET(struct rspamd_config, disable_hyperscan), - 0, - "Disable hyperscan optimizations for regular expressions"); - rspamd_rcl_add_default_handler(sub, - "vectorized_hyperscan", - rspamd_rcl_parse_struct_boolean, - G_STRUCT_OFFSET(struct rspamd_config, vectorized_hyperscan), - 0, - "Use hyperscan in vectorized mode (obsoleted, do not use)"); - rspamd_rcl_add_default_handler(sub, - "cores_dir", - rspamd_rcl_parse_struct_string, - G_STRUCT_OFFSET(struct rspamd_config, cores_dir), - RSPAMD_CL_FLAG_STRING_PATH, - "Path to the directory where rspamd core files are intended to be dumped"); - rspamd_rcl_add_default_handler(sub, - "max_cores_size", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, max_cores_size), - RSPAMD_CL_FLAG_INT_SIZE, - "Limit of joint size of all files in `cores_dir`"); - rspamd_rcl_add_default_handler(sub, - "max_cores_count", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, max_cores_count), - RSPAMD_CL_FLAG_INT_SIZE, - "Limit of files count in `cores_dir`"); - rspamd_rcl_add_default_handler(sub, - "local_addrs", - rspamd_rcl_parse_struct_ucl, - G_STRUCT_OFFSET(struct rspamd_config, local_addrs), - 0, - "Use the specified addresses as local ones"); - rspamd_rcl_add_default_handler(sub, - "local_networks", - rspamd_rcl_parse_struct_ucl, - G_STRUCT_OFFSET(struct rspamd_config, local_addrs), - 0, - "Use the specified addresses as local ones (alias for `local_addrs`)"); - rspamd_rcl_add_default_handler(sub, - "trusted_keys", - rspamd_rcl_parse_struct_string_list, - G_STRUCT_OFFSET(struct rspamd_config, trusted_keys), - RSPAMD_CL_FLAG_STRING_LIST_HASH, - "List of trusted public keys used for signatures in base32 encoding"); - rspamd_rcl_add_default_handler(sub, - "enable_shutdown_workaround", - rspamd_rcl_parse_struct_boolean, - G_STRUCT_OFFSET(struct rspamd_config, enable_shutdown_workaround), - 0, - "Enable workaround for legacy clients"); - rspamd_rcl_add_default_handler(sub, - "ignore_received", - rspamd_rcl_parse_struct_boolean, - G_STRUCT_OFFSET(struct rspamd_config, ignore_received), - 0, - "Ignore data from the first received header"); - rspamd_rcl_add_default_handler(sub, - "ssl_ca_path", - rspamd_rcl_parse_struct_string, - G_STRUCT_OFFSET(struct rspamd_config, ssl_ca_path), - RSPAMD_CL_FLAG_STRING_PATH, - "Path to ssl CA file"); - rspamd_rcl_add_default_handler(sub, - "ssl_ciphers", - rspamd_rcl_parse_struct_string, - G_STRUCT_OFFSET(struct rspamd_config, ssl_ciphers), - 0, - "List of ssl ciphers (e.g. HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4)"); - rspamd_rcl_add_default_handler(sub, - "max_message", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, max_message), - RSPAMD_CL_FLAG_INT_SIZE, - "Maximum size of the message to be scanned (50Mb by default)"); - rspamd_rcl_add_default_handler(sub, - "max_pic", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, max_pic_size), - RSPAMD_CL_FLAG_INT_SIZE, - "Maximum size of the picture to be normalized (1Mb by default)"); - rspamd_rcl_add_default_handler(sub, - "images_cache", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, max_pic_size), - RSPAMD_CL_FLAG_INT_SIZE, - "Size of DCT data cache for images (256 elements by default)"); - rspamd_rcl_add_default_handler(sub, - "zstd_input_dictionary", - rspamd_rcl_parse_struct_string, - G_STRUCT_OFFSET(struct rspamd_config, zstd_input_dictionary), - RSPAMD_CL_FLAG_STRING_PATH, - "Dictionary for zstd inbound protocol compression"); - rspamd_rcl_add_default_handler(sub, - "zstd_output_dictionary", - rspamd_rcl_parse_struct_string, - G_STRUCT_OFFSET(struct rspamd_config, zstd_output_dictionary), - RSPAMD_CL_FLAG_STRING_PATH, - "Dictionary for outbound zstd compression"); - rspamd_rcl_add_default_handler(sub, - "compat_messages", - rspamd_rcl_parse_struct_boolean, - G_STRUCT_OFFSET(struct rspamd_config, compat_messages), - 0, - "Use pre 1.4 style of messages in the protocol"); - rspamd_rcl_add_default_handler(sub, - "max_shots", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, default_max_shots), - 0, - "Maximum number of hits per a single symbol (default: 100)"); - rspamd_rcl_add_default_handler(sub, - "sessions_cache", - rspamd_rcl_parse_struct_boolean, - G_STRUCT_OFFSET(struct rspamd_config, enable_sessions_cache), - 0, - "Enable sessions cache to debug dangling sessions"); - rspamd_rcl_add_default_handler(sub, - "max_sessions_cache", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, max_sessions_cache), - 0, - "Maximum number of sessions in cache before warning (default: 100)"); - rspamd_rcl_add_default_handler(sub, - "task_timeout", - rspamd_rcl_parse_struct_time, - G_STRUCT_OFFSET(struct rspamd_config, task_timeout), - RSPAMD_CL_FLAG_TIME_FLOAT, - "Maximum time for checking a message"); - rspamd_rcl_add_default_handler(sub, - "soft_reject_on_timeout", - rspamd_rcl_parse_struct_boolean, - G_STRUCT_OFFSET(struct rspamd_config, soft_reject_on_timeout), - 0, - "Emit soft reject if task timeout takes place"); - rspamd_rcl_add_default_handler(sub, - "check_timeout", - rspamd_rcl_parse_struct_time, - G_STRUCT_OFFSET(struct rspamd_config, task_timeout), - RSPAMD_CL_FLAG_TIME_FLOAT, - "Maximum time for checking a message (alias for task_timeout)"); - rspamd_rcl_add_default_handler(sub, - "lua_gc_step", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, lua_gc_step), - RSPAMD_CL_FLAG_UINT, - "Lua garbage-collector step (default: 200)"); - rspamd_rcl_add_default_handler(sub, - "lua_gc_pause", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, lua_gc_pause), - RSPAMD_CL_FLAG_UINT, - "Lua garbage-collector pause (default: 200)"); - rspamd_rcl_add_default_handler(sub, - "full_gc_iters", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, full_gc_iters), - RSPAMD_CL_FLAG_UINT, - "Task scanned before memory gc is performed (default: 0 - disabled)"); - rspamd_rcl_add_default_handler(sub, - "heartbeat_interval", - rspamd_rcl_parse_struct_time, - G_STRUCT_OFFSET(struct rspamd_config, heartbeat_interval), - RSPAMD_CL_FLAG_TIME_FLOAT, - "Time between workers heartbeats"); - rspamd_rcl_add_default_handler(sub, - "heartbeats_loss_max", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, heartbeats_loss_max), - RSPAMD_CL_FLAG_INT_32, - "Maximum count of heartbeats to be lost before trying to " - "terminate a worker (default: 0 - disabled)"); - rspamd_rcl_add_default_handler(sub, - "max_lua_urls", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, max_lua_urls), - RSPAMD_CL_FLAG_INT_32, - "Maximum count of URLs to pass to Lua to avoid DoS (default: 1024)"); - rspamd_rcl_add_default_handler(sub, - "max_urls", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, max_urls), - RSPAMD_CL_FLAG_INT_32, - "Maximum count of URLs to process to avoid DoS (default: 10240)"); - rspamd_rcl_add_default_handler(sub, - "max_recipients", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, max_recipients), - RSPAMD_CL_FLAG_INT_32, - "Maximum count of recipients to process to avoid DoS (default: 1024)"); - rspamd_rcl_add_default_handler(sub, - "max_blas_threads", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, max_blas_threads), - RSPAMD_CL_FLAG_INT_32, - "Maximum number of Blas threads for learning neural networks (default: 1)"); - rspamd_rcl_add_default_handler(sub, - "max_opts_len", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, max_opts_len), - RSPAMD_CL_FLAG_INT_32, - "Maximum size of all options for a single symbol (default: 4096)"); - rspamd_rcl_add_default_handler(sub, - "events_backend", - rspamd_rcl_parse_struct_string, - G_STRUCT_OFFSET(struct rspamd_config, events_backend), - 0, - "Events backend to use: kqueue, epoll, select, poll or auto (default: auto)"); - - /* Neighbours configuration */ - rspamd_rcl_add_section_doc(&sub->subsections, "neighbours", "name", - rspamd_rcl_neighbours_handler, - UCL_OBJECT, FALSE, TRUE, - cfg->doc_strings, - "List of members of Rspamd cluster"); - - /* New DNS configuration */ - ssub = rspamd_rcl_add_section_doc(&sub->subsections, "dns", NULL, NULL, - UCL_OBJECT, FALSE, TRUE, - cfg->doc_strings, - "Options for DNS resolver"); - rspamd_rcl_add_default_handler(ssub, - "nameserver", - rspamd_rcl_parse_struct_ucl, - G_STRUCT_OFFSET(struct rspamd_config, nameservers), - 0, - "List of DNS servers"); - rspamd_rcl_add_default_handler(ssub, - "server", - rspamd_rcl_parse_struct_ucl, - G_STRUCT_OFFSET(struct rspamd_config, nameservers), - 0, - "List of DNS servers"); - rspamd_rcl_add_default_handler(ssub, - "timeout", - rspamd_rcl_parse_struct_time, - G_STRUCT_OFFSET(struct rspamd_config, dns_timeout), - RSPAMD_CL_FLAG_TIME_FLOAT, - "DNS request timeout"); - rspamd_rcl_add_default_handler(ssub, - "retransmits", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, dns_retransmits), - RSPAMD_CL_FLAG_INT_32, - "DNS request retransmits"); - rspamd_rcl_add_default_handler(ssub, - "sockets", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, dns_io_per_server), - RSPAMD_CL_FLAG_INT_32, - "Number of sockets per DNS server"); - rspamd_rcl_add_default_handler(ssub, - "connections", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, dns_io_per_server), - RSPAMD_CL_FLAG_INT_32, - "Number of sockets per DNS server"); - rspamd_rcl_add_default_handler(ssub, - "enable_dnssec", - rspamd_rcl_parse_struct_boolean, - G_STRUCT_OFFSET(struct rspamd_config, enable_dnssec), - 0, - "Enable DNSSEC support in Rspamd"); - - - /* New upstreams configuration */ - ssub = rspamd_rcl_add_section_doc(&sub->subsections, "upstream", NULL, NULL, - UCL_OBJECT, FALSE, TRUE, - cfg->doc_strings, - "Upstreams configuration parameters"); - rspamd_rcl_add_default_handler(ssub, - "max_errors", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_config, upstream_max_errors), - RSPAMD_CL_FLAG_UINT, - "Maximum number of errors during `error_time` to consider upstream down"); - rspamd_rcl_add_default_handler(ssub, - "error_time", - rspamd_rcl_parse_struct_time, - G_STRUCT_OFFSET(struct rspamd_config, upstream_error_time), - RSPAMD_CL_FLAG_TIME_FLOAT, - "Time frame to check errors"); - rspamd_rcl_add_default_handler(ssub, - "revive_time", - rspamd_rcl_parse_struct_time, - G_STRUCT_OFFSET(struct rspamd_config, upstream_revive_time), - RSPAMD_CL_FLAG_TIME_FLOAT, - "Time before attempting to recover upstream after an error"); - rspamd_rcl_add_default_handler(ssub, - "lazy_resolve_time", - rspamd_rcl_parse_struct_time, - G_STRUCT_OFFSET(struct rspamd_config, upstream_lazy_resolve_time), - RSPAMD_CL_FLAG_TIME_FLOAT, - "Time to resolve upstreams addresses in lazy mode"); - } - - if (!(skip_sections && g_hash_table_lookup(skip_sections, "actions"))) { - /** - * Symbols and actions sections - */ - sub = rspamd_rcl_add_section_doc(&new, - "actions", NULL, - rspamd_rcl_actions_handler, - UCL_OBJECT, - FALSE, - TRUE, - cfg->doc_strings, - "Actions configuration"); - rspamd_rcl_add_default_handler(sub, - "unknown_weight", - rspamd_rcl_parse_struct_double, - G_STRUCT_OFFSET(struct rspamd_config, unknown_weight), - 0, - "Accept unknown symbols with the specified weight"); - rspamd_rcl_add_default_handler(sub, - "grow_factor", - rspamd_rcl_parse_struct_double, - G_STRUCT_OFFSET(struct rspamd_config, grow_factor), - 0, - "Multiply the subsequent symbols by this number " - "(does not affect symbols with score less or " - "equal to zero)"); - rspamd_rcl_add_default_handler(sub, - "subject", - rspamd_rcl_parse_struct_string, - G_STRUCT_OFFSET(struct rspamd_config, subject), - 0, - "Rewrite subject with this value"); - } - - if (!(skip_sections && g_hash_table_lookup(skip_sections, "group"))) { - sub = rspamd_rcl_add_section_doc(&new, - "group", "name", - rspamd_rcl_group_handler, - UCL_OBJECT, - FALSE, - TRUE, - cfg->doc_strings, - "Symbol groups configuration"); - ssub = rspamd_rcl_add_section_doc(&sub->subsections, "symbols", "name", - rspamd_rcl_symbol_handler, - UCL_OBJECT, FALSE, TRUE, - cfg->doc_strings, - "Symbols configuration"); - - /* Group part */ - rspamd_rcl_add_default_handler(sub, - "max_score", - rspamd_rcl_parse_struct_double, - G_STRUCT_OFFSET(struct rspamd_symbols_group, max_score), - 0, - "Maximum score that could be reached by this symbols group"); - } - - if (!(skip_sections && g_hash_table_lookup(skip_sections, "worker"))) { - /** - * Worker section - */ - sub = rspamd_rcl_add_section_doc(&new, - "worker", "type", - rspamd_rcl_worker_handler, - UCL_OBJECT, - FALSE, - TRUE, - cfg->doc_strings, - "Workers common options"); - rspamd_rcl_add_default_handler(sub, - "count", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_worker_conf, count), - RSPAMD_CL_FLAG_INT_16, - "Number of workers to spawn"); - rspamd_rcl_add_default_handler(sub, - "max_files", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_worker_conf, rlimit_nofile), - RSPAMD_CL_FLAG_INT_64, - "Maximum number of opened files per worker"); - rspamd_rcl_add_default_handler(sub, - "max_core", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_worker_conf, rlimit_maxcore), - RSPAMD_CL_FLAG_INT_64, - "Max size of core file in bytes"); - rspamd_rcl_add_default_handler(sub, - "enabled", - rspamd_rcl_parse_struct_boolean, - G_STRUCT_OFFSET(struct rspamd_worker_conf, enabled), - 0, - "Enable or disable a worker (true by default)"); - } - - if (!(skip_sections && g_hash_table_lookup(skip_sections, "modules"))) { - /** - * Modules handler - */ - sub = rspamd_rcl_add_section_doc(&new, - "modules", NULL, - rspamd_rcl_modules_handler, - UCL_OBJECT, - FALSE, - FALSE, - cfg->doc_strings, - "Lua plugins to load"); - } - - if (!(skip_sections && g_hash_table_lookup(skip_sections, "classifier"))) { - /** - * Classifiers handler - */ - sub = rspamd_rcl_add_section_doc(&new, - "classifier", "type", - rspamd_rcl_classifier_handler, - UCL_OBJECT, - FALSE, - TRUE, - cfg->doc_strings, - "CLassifier options"); - /* Default classifier is 'bayes' for now */ - sub->default_key = "bayes"; - - rspamd_rcl_add_default_handler(sub, - "min_tokens", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_classifier_config, min_tokens), - RSPAMD_CL_FLAG_INT_32, - "Minimum count of tokens (words) to be considered for statistics"); - rspamd_rcl_add_default_handler(sub, - "min_token_hits", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_classifier_config, min_token_hits), - RSPAMD_CL_FLAG_UINT, - "Minimum number of hits for a token to be considered"); - rspamd_rcl_add_default_handler(sub, - "min_prob_strength", - rspamd_rcl_parse_struct_double, - G_STRUCT_OFFSET(struct rspamd_classifier_config, min_token_hits), - 0, - "Use only tokens with probability in [0.5 - MPS, 0.5 + MPS]"); - rspamd_rcl_add_default_handler(sub, - "max_tokens", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_classifier_config, max_tokens), - RSPAMD_CL_FLAG_INT_32, - "Maximum count of tokens (words) to be considered for statistics"); - rspamd_rcl_add_default_handler(sub, - "min_learns", - rspamd_rcl_parse_struct_integer, - G_STRUCT_OFFSET(struct rspamd_classifier_config, min_learns), - RSPAMD_CL_FLAG_UINT, - "Minimum number of learns for each statfile to use this classifier"); - rspamd_rcl_add_default_handler(sub, - "backend", - rspamd_rcl_parse_struct_string, - G_STRUCT_OFFSET(struct rspamd_classifier_config, backend), - 0, - "Statfiles engine"); - rspamd_rcl_add_default_handler(sub, - "name", - rspamd_rcl_parse_struct_string, - G_STRUCT_OFFSET(struct rspamd_classifier_config, name), - 0, - "Name of classifier"); - - /* - * Statfile defaults - */ - ssub = rspamd_rcl_add_section_doc(&sub->subsections, - "statfile", "symbol", - rspamd_rcl_statfile_handler, - UCL_OBJECT, - TRUE, - TRUE, - sub->doc_ref, - "Statfiles options"); - rspamd_rcl_add_default_handler(ssub, - "label", - rspamd_rcl_parse_struct_string, - G_STRUCT_OFFSET(struct rspamd_statfile_config, label), - 0, - "Statfile unique label"); - rspamd_rcl_add_default_handler(ssub, - "spam", - rspamd_rcl_parse_struct_boolean, - G_STRUCT_OFFSET(struct rspamd_statfile_config, is_spam), - 0, - "Sets if this statfile contains spam samples"); - } - - if (!(skip_sections && g_hash_table_lookup(skip_sections, "composite"))) { - /** - * Composites handlers - */ - sub = rspamd_rcl_add_section_doc(&new, - "composite", "name", - rspamd_rcl_composite_handler, - UCL_OBJECT, - FALSE, - TRUE, - cfg->doc_strings, - "Rspamd composite symbols"); - sub = rspamd_rcl_add_section_doc(&new, - "composites", NULL, - rspamd_rcl_composites_handler, - UCL_OBJECT, - FALSE, - TRUE, - cfg->doc_strings, - "Rspamd composite symbols"); - } - - if (!(skip_sections && g_hash_table_lookup(skip_sections, "lua"))) { - /** - * Lua handler - */ - sub = rspamd_rcl_add_section_doc(&new, - "lua", NULL, - rspamd_rcl_lua_handler, - UCL_STRING, - FALSE, - TRUE, - cfg->doc_strings, - "Lua files to load"); - } - - return new; -} - -struct rspamd_rcl_section * -rspamd_rcl_config_get_section(struct rspamd_rcl_section *top, - const char *path) -{ - struct rspamd_rcl_section *cur, *found = NULL; - char **path_components; - gint ncomponents, i; - - - if (path == NULL) { - return top; - } - - path_components = g_strsplit_set(path, "/", -1); - ncomponents = g_strv_length(path_components); - - cur = top; - for (i = 0; i < ncomponents; i++) { - if (cur == NULL) { - g_strfreev(path_components); - return NULL; - } - HASH_FIND_STR(cur, path_components[i], found); - if (found == NULL) { - g_strfreev(path_components); - return NULL; - } - cur = found; - } - - g_strfreev(path_components); - return found; -} - -static gboolean -rspamd_rcl_process_section(struct rspamd_config *cfg, - struct rspamd_rcl_section *sec, - gpointer ptr, const ucl_object_t *obj, rspamd_mempool_t *pool, - GError **err) -{ - ucl_object_iter_t it; - const ucl_object_t *cur; - gboolean is_nested = TRUE; - const gchar *key = NULL; - - g_assert(obj != NULL); - g_assert(sec->handler != NULL); - - if (sec->key_attr != NULL) { - it = ucl_object_iterate_new(obj); - - while ((cur = ucl_object_iterate_full(it, UCL_ITERATE_EXPLICIT)) != NULL) { - if (ucl_object_type(cur) != UCL_OBJECT) { - is_nested = FALSE; - break; - } - } - - ucl_object_iterate_free(it); - } - else { - is_nested = FALSE; - } - - if (is_nested) { - /* Just reiterate on all subobjects */ - it = ucl_object_iterate_new(obj); - - while ((cur = ucl_object_iterate_full(it, UCL_ITERATE_EXPLICIT)) != NULL) { - if (!sec->handler(pool, cur, ucl_object_key(cur), ptr, sec, err)) { - ucl_object_iterate_free(it); - - return FALSE; - } - } - - ucl_object_iterate_free(it); - - return TRUE; - } - else { - if (sec->key_attr != NULL) { - /* First of all search for required attribute and use it as a key */ - cur = ucl_object_lookup(obj, sec->key_attr); - - if (cur == NULL) { - if (sec->default_key == NULL) { - g_set_error(err, CFG_RCL_ERROR, EINVAL, "required attribute " - "'%s' is missing for section '%s', current key: %s", - sec->key_attr, - sec->name, - ucl_object_emit(obj, UCL_EMIT_CONFIG)); - - return FALSE; - } - else { - msg_info("using default key '%s' for mandatory field '%s' " - "for section '%s'", - sec->default_key, sec->key_attr, - sec->name); - key = sec->default_key; - } - } - else if (ucl_object_type(cur) != UCL_STRING) { - g_set_error(err, CFG_RCL_ERROR, EINVAL, "required attribute %s" - " is not a string for section %s", - sec->key_attr, sec->name); - - return FALSE; - } - else { - key = ucl_object_tostring(cur); - } - } - } - - return sec->handler(pool, obj, key, ptr, sec, err); -} - -gboolean -rspamd_rcl_parse(struct rspamd_rcl_section *top, - struct rspamd_config *cfg, - gpointer ptr, rspamd_mempool_t *pool, - const ucl_object_t *obj, GError **err) -{ - const ucl_object_t *found, *cur_obj; - struct rspamd_rcl_section *cur, *tmp, *found_sec; - - if (obj->type != UCL_OBJECT) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "top configuration must be an object"); - return FALSE; - } - - /* Iterate over known sections and ignore unknown ones */ - HASH_ITER(hh, top, cur, tmp) - { - if (strcmp(cur->name, "*") == 0) { - /* Default section handler */ - LL_FOREACH(obj, cur_obj) - { - HASH_FIND_STR(top, ucl_object_key(cur_obj), found_sec); - - if (found_sec == NULL) { - if (cur->handler != NULL) { - if (!rspamd_rcl_process_section(cfg, cur, ptr, cur_obj, - pool, err)) { - return FALSE; - } - } - else { - rspamd_rcl_section_parse_defaults(cfg, - cur, - pool, - cur_obj, - ptr, - err); - } - } - } - } - else { - found = ucl_object_lookup(obj, cur->name); - if (found == NULL) { - if (cur->required) { - g_set_error(err, CFG_RCL_ERROR, ENOENT, - "required section %s is missing", cur->name); - return FALSE; - } - } - else { - /* Check type */ - if (cur->strict_type) { - if (cur->type != found->type) { - g_set_error(err, CFG_RCL_ERROR, EINVAL, - "object in section %s has invalid type", cur->name); - return FALSE; - } - } - - LL_FOREACH(found, cur_obj) - { - if (cur->handler != NULL) { - if (!rspamd_rcl_process_section(cfg, cur, ptr, cur_obj, - pool, err)) { - return FALSE; - } - } - else { - rspamd_rcl_section_parse_defaults(cfg, cur, - pool, - cur_obj, - ptr, - err); - } - } - } - } - if (cur->fin) { - cur->fin(pool, cur->fin_ud); - } - } - - return TRUE; -} - -gboolean -rspamd_rcl_section_parse_defaults(struct rspamd_config *cfg, - struct rspamd_rcl_section *section, - rspamd_mempool_t *pool, const ucl_object_t *obj, gpointer ptr, - GError **err) -{ - const ucl_object_t *found, *cur_obj; - struct rspamd_rcl_default_handler_data *cur, *tmp; - - if (obj->type != UCL_OBJECT) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "default configuration must be an object for section %s " - "(actual type is %s)", - section->name, ucl_object_type_to_string(obj->type)); - return FALSE; - } - - HASH_ITER(hh, section->default_parser, cur, tmp) - { - found = ucl_object_lookup(obj, cur->key); - if (found != NULL) { - cur->pd.user_struct = ptr; - cur->pd.cfg = cfg; - - LL_FOREACH(found, cur_obj) - { - if (!cur->handler(pool, cur_obj, &cur->pd, section, err)) { - return FALSE; - } - - if (!(cur->pd.flags & RSPAMD_CL_FLAG_MULTIPLE)) { - break; - } - } - } - } - - return TRUE; -} - -gboolean -rspamd_rcl_parse_struct_string(rspamd_mempool_t *pool, - const ucl_object_t *obj, - gpointer ud, - struct rspamd_rcl_section *section, - GError **err) -{ - struct rspamd_rcl_struct_parser *pd = ud; - gchar **target; - const gsize num_str_len = 32; - - target = (gchar **) (((gchar *) pd->user_struct) + pd->offset); - switch (obj->type) { - case UCL_STRING: - *target = - rspamd_mempool_strdup(pool, ucl_copy_value_trash(obj)); - break; - case UCL_INT: - *target = rspamd_mempool_alloc(pool, num_str_len); - rspamd_snprintf(*target, num_str_len, "%L", obj->value.iv); - break; - case UCL_FLOAT: - *target = rspamd_mempool_alloc(pool, num_str_len); - rspamd_snprintf(*target, num_str_len, "%f", obj->value.dv); - break; - case UCL_BOOLEAN: - *target = rspamd_mempool_alloc(pool, num_str_len); - rspamd_snprintf(*target, num_str_len, "%s", - ((gboolean) obj->value.iv) ? "true" : "false"); - break; - case UCL_NULL: - /* String is enforced to be null */ - *target = NULL; - break; - default: - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "cannot convert %s to string in option %s", - ucl_object_type_to_string(ucl_object_type(obj)), - ucl_object_key(obj)); - return FALSE; - } - - return TRUE; -} - -gboolean -rspamd_rcl_parse_struct_integer(rspamd_mempool_t *pool, - const ucl_object_t *obj, - gpointer ud, - struct rspamd_rcl_section *section, - GError **err) -{ - struct rspamd_rcl_struct_parser *pd = ud; - union { - gint *ip; - gint32 *i32p; - gint16 *i16p; - gint64 *i64p; - guint *up; - gsize *sp; - } target; - int64_t val; - - if (pd->flags == RSPAMD_CL_FLAG_INT_32) { - target.i32p = (gint32 *) (((gchar *) pd->user_struct) + pd->offset); - if (!ucl_object_toint_safe(obj, &val)) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "cannot convert %s to integer in option %s", - ucl_object_type_to_string(ucl_object_type(obj)), - ucl_object_key(obj)); - return FALSE; - } - *target.i32p = val; - } - else if (pd->flags == RSPAMD_CL_FLAG_INT_64) { - target.i64p = (gint64 *) (((gchar *) pd->user_struct) + pd->offset); - if (!ucl_object_toint_safe(obj, &val)) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "cannot convert %s to integer in option %s", - ucl_object_type_to_string(ucl_object_type(obj)), - ucl_object_key(obj)); - return FALSE; - } - *target.i64p = val; - } - else if (pd->flags == RSPAMD_CL_FLAG_INT_SIZE) { - target.sp = (gsize *) (((gchar *) pd->user_struct) + pd->offset); - if (!ucl_object_toint_safe(obj, &val)) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "cannot convert %s to integer in option %s", - ucl_object_type_to_string(ucl_object_type(obj)), - ucl_object_key(obj)); - return FALSE; - } - *target.sp = val; - } - else if (pd->flags == RSPAMD_CL_FLAG_INT_16) { - target.i16p = (gint16 *) (((gchar *) pd->user_struct) + pd->offset); - if (!ucl_object_toint_safe(obj, &val)) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "cannot convert %s to integer in option %s", - ucl_object_type_to_string(ucl_object_type(obj)), - ucl_object_key(obj)); - return FALSE; - } - *target.i16p = val; - } - else if (pd->flags == RSPAMD_CL_FLAG_UINT) { - target.up = (guint *) (((gchar *) pd->user_struct) + pd->offset); - if (!ucl_object_toint_safe(obj, &val)) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "cannot convert %s to integer in option %s", - ucl_object_type_to_string(ucl_object_type(obj)), - ucl_object_key(obj)); - return FALSE; - } - *target.up = val; - } - else { - target.ip = (gint *) (((gchar *) pd->user_struct) + pd->offset); - if (!ucl_object_toint_safe(obj, &val)) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "cannot convert %s to integer in option %s", - ucl_object_type_to_string(ucl_object_type(obj)), - ucl_object_key(obj)); - return FALSE; - } - *target.ip = val; - } - - return TRUE; -} - -gboolean -rspamd_rcl_parse_struct_double(rspamd_mempool_t *pool, - const ucl_object_t *obj, - gpointer ud, - struct rspamd_rcl_section *section, - GError **err) -{ - struct rspamd_rcl_struct_parser *pd = ud; - gdouble *target; - - target = (gdouble *) (((gchar *) pd->user_struct) + pd->offset); - - if (!ucl_object_todouble_safe(obj, target)) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "cannot convert %s to double in option %s", - ucl_object_type_to_string(ucl_object_type(obj)), - ucl_object_key(obj)); - return FALSE; - } - - return TRUE; -} - -gboolean -rspamd_rcl_parse_struct_time(rspamd_mempool_t *pool, - const ucl_object_t *obj, - gpointer ud, - struct rspamd_rcl_section *section, - GError **err) -{ - struct rspamd_rcl_struct_parser *pd = ud; - union { - gint *psec; - guint32 *pu32; - gdouble *pdv; - struct timeval *ptv; - struct timespec *pts; - } target; - gdouble val; - - if (!ucl_object_todouble_safe(obj, &val)) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "cannot convert %s to double in option %s", - ucl_object_type_to_string(ucl_object_type(obj)), - ucl_object_key(obj)); - return FALSE; - } - - if (pd->flags == RSPAMD_CL_FLAG_TIME_TIMEVAL) { - target.ptv = - (struct timeval *) (((gchar *) pd->user_struct) + pd->offset); - target.ptv->tv_sec = (glong) val; - target.ptv->tv_usec = (val - (glong) val) * 1000000; - } - else if (pd->flags == RSPAMD_CL_FLAG_TIME_TIMESPEC) { - target.pts = - (struct timespec *) (((gchar *) pd->user_struct) + pd->offset); - target.pts->tv_sec = (glong) val; - target.pts->tv_nsec = (val - (glong) val) * 1000000000000LL; - } - else if (pd->flags == RSPAMD_CL_FLAG_TIME_FLOAT) { - target.pdv = (double *) (((gchar *) pd->user_struct) + pd->offset); - *target.pdv = val; - } - else if (pd->flags == RSPAMD_CL_FLAG_TIME_INTEGER) { - target.psec = (gint *) (((gchar *) pd->user_struct) + pd->offset); - *target.psec = val * 1000; - } - else if (pd->flags == RSPAMD_CL_FLAG_TIME_UINT_32) { - target.pu32 = (guint32 *) (((gchar *) pd->user_struct) + pd->offset); - *target.pu32 = val * 1000; - } - else { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "cannot convert %s to time in option %s", - ucl_object_type_to_string(ucl_object_type(obj)), - ucl_object_key(obj)); - return FALSE; - } - - return TRUE; -} - -gboolean -rspamd_rcl_parse_struct_keypair(rspamd_mempool_t *pool, - const ucl_object_t *obj, - gpointer ud, - struct rspamd_rcl_section *section, - GError **err) -{ - struct rspamd_rcl_struct_parser *pd = ud; - struct rspamd_cryptobox_keypair **target, *kp; - - target = (struct rspamd_cryptobox_keypair **) (((gchar *) pd->user_struct) + - pd->offset); - if (obj->type == UCL_OBJECT) { - kp = rspamd_keypair_from_ucl(obj); - - if (kp != NULL) { - rspamd_mempool_add_destructor(pool, - (rspamd_mempool_destruct_t) rspamd_keypair_unref, kp); - *target = kp; - } - else { - gchar *dump = ucl_object_emit(obj, UCL_EMIT_JSON_COMPACT); - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "cannot load the keypair specified: %s; section: %s; value: %s", - ucl_object_key(obj), section->name, dump); - free(dump); - - return FALSE; - } - } - else { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "no sane pubkey or privkey found in the keypair: %s", - ucl_object_key(obj)); - return FALSE; - } - - return TRUE; -} - -gboolean -rspamd_rcl_parse_struct_pubkey(rspamd_mempool_t *pool, - const ucl_object_t *obj, - gpointer ud, - struct rspamd_rcl_section *section, - GError **err) -{ - struct rspamd_rcl_struct_parser *pd = ud; - struct rspamd_cryptobox_pubkey **target, *pk; - gsize len; - const gchar *str; - gint keypair_type = RSPAMD_KEYPAIR_KEX, - keypair_mode = RSPAMD_CRYPTOBOX_MODE_25519; - - if (pd->flags & RSPAMD_CL_FLAG_SIGNKEY) { - keypair_type = RSPAMD_KEYPAIR_SIGN; - } - if (pd->flags & RSPAMD_CL_FLAG_NISTKEY) { - keypair_mode = RSPAMD_CRYPTOBOX_MODE_NIST; - } - - target = (struct rspamd_cryptobox_pubkey **) (((gchar *) pd->user_struct) + - pd->offset); - if (obj->type == UCL_STRING) { - str = ucl_object_tolstring(obj, &len); - pk = rspamd_pubkey_from_base32(str, len, keypair_type, - keypair_mode); - - if (pk != NULL) { - *target = pk; - } - else { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "cannot load the pubkey specified: %s", - ucl_object_key(obj)); - return FALSE; - } - } - else { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "no sane pubkey found in the element: %s", - ucl_object_key(obj)); - return FALSE; - } - - rspamd_mempool_add_destructor(pool, - (rspamd_mempool_destruct_t) rspamd_pubkey_unref, pk); - - return TRUE; -} - -static void -rspamd_rcl_insert_string_list_item(gpointer *target, rspamd_mempool_t *pool, - const gchar *src, gboolean is_hash) -{ - union { - GHashTable *hv; - GList *lv; - gpointer p; - } d; - gchar *val; - - d.p = *target; - - if (is_hash) { - if (d.hv == NULL) { - d.hv = g_hash_table_new(rspamd_str_hash, rspamd_str_equal); - rspamd_mempool_add_destructor(pool, - (rspamd_mempool_destruct_t) g_hash_table_unref, d.hv); - } - - val = rspamd_mempool_strdup(pool, src); - g_hash_table_insert(d.hv, val, val); - } - else { - val = rspamd_mempool_strdup(pool, src); - d.lv = g_list_prepend(d.lv, val); - } - - *target = d.p; -} - -gboolean -rspamd_rcl_parse_struct_string_list(rspamd_mempool_t *pool, - const ucl_object_t *obj, - gpointer ud, - struct rspamd_rcl_section *section, - GError **err) -{ - struct rspamd_rcl_struct_parser *pd = ud; - gpointer *target; - gchar *val, **strvec, **cvec; - const ucl_object_t *cur; - const gsize num_str_len = 32; - ucl_object_iter_t iter = NULL; - gboolean is_hash, need_destructor = TRUE; - - - is_hash = pd->flags & RSPAMD_CL_FLAG_STRING_LIST_HASH; - target = (gpointer *) (((gchar *) pd->user_struct) + pd->offset); - - if (!is_hash && *target != NULL) { - need_destructor = FALSE; - } - - iter = ucl_object_iterate_new(obj); - - while ((cur = ucl_object_iterate_safe(iter, true)) != NULL) { - switch (cur->type) { - case UCL_STRING: - strvec = g_strsplit_set(ucl_object_tostring(cur), ",", -1); - cvec = strvec; - - while (*cvec) { - rspamd_rcl_insert_string_list_item(target, pool, *cvec, is_hash); - cvec++; - } - - g_strfreev(strvec); - /* Go to the next object */ - continue; - case UCL_INT: - val = rspamd_mempool_alloc(pool, num_str_len); - rspamd_snprintf(val, num_str_len, "%L", cur->value.iv); - break; - case UCL_FLOAT: - val = rspamd_mempool_alloc(pool, num_str_len); - rspamd_snprintf(val, num_str_len, "%f", cur->value.dv); - break; - case UCL_BOOLEAN: - val = rspamd_mempool_alloc(pool, num_str_len); - rspamd_snprintf(val, num_str_len, "%s", - ((gboolean) cur->value.iv) ? "true" : "false"); - break; - default: - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "cannot convert %s to a string list in option %s", - ucl_object_type_to_string(ucl_object_type(obj)), - ucl_object_key(obj)); - ucl_object_iterate_free(iter); - - return FALSE; - } - - rspamd_rcl_insert_string_list_item(target, pool, val, is_hash); - } - - ucl_object_iterate_free(iter); - -#if 0 - /* WTF: why don't we allow empty list here?? */ - if (*target == NULL) { - g_set_error (err, - CFG_RCL_ERROR, - EINVAL, - "non-empty array of strings is expected: %s, " - "got: %s, of length: %d", - ucl_object_key (obj), ucl_object_type_to_string (obj->type), - obj->len); - return FALSE; - } -#endif - - if (!is_hash && *target != NULL) { - *target = g_list_reverse(*target); - - if (need_destructor) { - rspamd_mempool_add_destructor(pool, - (rspamd_mempool_destruct_t) g_list_free, - *target); - } - } - - return TRUE; -} - -gboolean -rspamd_rcl_parse_struct_ucl(rspamd_mempool_t *pool, - const ucl_object_t *obj, - gpointer ud, - struct rspamd_rcl_section *section, - GError **err) -{ - struct rspamd_rcl_struct_parser *pd = ud; - const ucl_object_t **target; - - target = (const ucl_object_t **) (((gchar *) pd->user_struct) + pd->offset); - - *target = obj; - - return TRUE; -} - - -gboolean -rspamd_rcl_parse_struct_boolean(rspamd_mempool_t *pool, - const ucl_object_t *obj, - gpointer ud, - struct rspamd_rcl_section *section, - GError **err) -{ - struct rspamd_rcl_struct_parser *pd = ud; - gboolean *target; - - target = (gboolean *) (((gchar *) pd->user_struct) + pd->offset); - - if (obj->type == UCL_BOOLEAN) { - *target = obj->value.iv; - } - else if (obj->type == UCL_INT) { - *target = obj->value.iv; - } - else { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "cannot convert %s to boolean in option %s", - ucl_object_type_to_string(ucl_object_type(obj)), - ucl_object_key(obj)); - return FALSE; - } - - if (pd->flags & RSPAMD_CL_FLAG_BOOLEAN_INVERSE) { - *target = !*target; - } - - return TRUE; -} - -gboolean -rspamd_rcl_parse_struct_addr(rspamd_mempool_t *pool, - const ucl_object_t *obj, - gpointer ud, - struct rspamd_rcl_section *section, - GError **err) -{ - struct rspamd_rcl_struct_parser *pd = ud; - rspamd_inet_addr_t **target; - const gchar *val; - gsize size; - - target = (rspamd_inet_addr_t **) (((gchar *) pd->user_struct) + pd->offset); - - if (ucl_object_type(obj) == UCL_STRING) { - val = ucl_object_tolstring(obj, &size); - - if (!rspamd_parse_inet_address(target, val, size, - RSPAMD_INET_ADDRESS_PARSE_DEFAULT)) { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "cannot parse inet address: %s", val); - return FALSE; - } - } - else { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "cannot convert %s to inet address in option %s", - ucl_object_type_to_string(ucl_object_type(obj)), - ucl_object_key(obj)); - return FALSE; - } - - return TRUE; -} - -gboolean -rspamd_rcl_parse_struct_mime_addr(rspamd_mempool_t *pool, - const ucl_object_t *obj, - gpointer ud, - struct rspamd_rcl_section *section, - GError **err) -{ - struct rspamd_rcl_struct_parser *pd = ud; - GPtrArray **target, *tmp_addr = NULL; - const gchar *val; - ucl_object_iter_t it; - const ucl_object_t *cur; - - target = (GPtrArray **) (((gchar *) pd->user_struct) + pd->offset); - it = ucl_object_iterate_new(obj); - - while ((cur = ucl_object_iterate_safe(it, true)) != NULL) { - if (ucl_object_type(cur) == UCL_STRING) { - val = ucl_object_tostring(obj); - tmp_addr = rspamd_email_address_from_mime(pool, val, - strlen(val), tmp_addr, -1); - } - else { - g_set_error(err, - CFG_RCL_ERROR, - EINVAL, - "cannot get inet address from ucl object in %s", - ucl_object_key(obj)); - ucl_object_iterate_free(it); - - return FALSE; - } - } - - ucl_object_iterate_free(it); - *target = tmp_addr; - - return TRUE; -} - -static guint -rspamd_worker_param_key_hash(gconstpointer p) -{ - const struct rspamd_worker_param_key *k = p; - rspamd_cryptobox_fast_hash_state_t st; - - rspamd_cryptobox_fast_hash_init(&st, rspamd_hash_seed()); - rspamd_cryptobox_fast_hash_update(&st, k->name, strlen(k->name)); - rspamd_cryptobox_fast_hash_update(&st, &k->ptr, sizeof(gpointer)); - - return rspamd_cryptobox_fast_hash_final(&st); -} - -static gboolean -rspamd_worker_param_key_equal(gconstpointer p1, gconstpointer p2) -{ - const struct rspamd_worker_param_key *k1 = p1, *k2 = p2; - - if (k1->ptr == k2->ptr) { - return strcmp(k1->name, k2->name) == 0; - } - - return FALSE; -} - -void rspamd_rcl_register_worker_option(struct rspamd_config *cfg, - GQuark type, - const gchar *name, - rspamd_rcl_default_handler_t handler, - gpointer target, - glong offset, - gint flags, - const gchar *doc_string) -{ - struct rspamd_worker_param_parser *nhandler; - struct rspamd_worker_cfg_parser *nparser; - struct rspamd_worker_param_key srch; - const ucl_object_t *doc_workers, *doc_target; - ucl_object_t *doc_obj; - - nparser = g_hash_table_lookup(cfg->wrk_parsers, &type); - - if (nparser == NULL) { - rspamd_rcl_register_worker_parser(cfg, type, NULL, NULL); - nparser = g_hash_table_lookup(cfg->wrk_parsers, &type); - - g_assert(nparser != NULL); - } - - srch.name = name; - srch.ptr = target; - - nhandler = g_hash_table_lookup(nparser->parsers, &srch); - if (nhandler != NULL) { - msg_warn_config( - "handler for parameter %s is already registered for worker type %s", - name, - g_quark_to_string(type)); - return; - } - - nhandler = - rspamd_mempool_alloc0(cfg->cfg_pool, - sizeof(struct rspamd_worker_param_parser)); - nhandler->key.name = name; - nhandler->key.ptr = target; - nhandler->parser.flags = flags; - nhandler->parser.offset = offset; - nhandler->parser.user_struct = target; - nhandler->handler = handler; - - g_hash_table_insert(nparser->parsers, &nhandler->key, nhandler); - - doc_workers = ucl_object_lookup(cfg->doc_strings, "workers"); - - if (doc_workers == NULL) { - doc_obj = ucl_object_typed_new(UCL_OBJECT); - ucl_object_insert_key(cfg->doc_strings, doc_obj, "workers", 0, false); - doc_workers = doc_obj; - } - - doc_target = ucl_object_lookup(doc_workers, g_quark_to_string(type)); - - if (doc_target == NULL) { - doc_obj = ucl_object_typed_new(UCL_OBJECT); - ucl_object_insert_key((ucl_object_t *) doc_workers, doc_obj, - g_quark_to_string(type), 0, true); - doc_target = doc_obj; - } - - rspamd_rcl_add_doc_obj((ucl_object_t *) doc_target, - doc_string, - name, - UCL_NULL, - handler, - flags, - NULL, - 0); -} - - -void rspamd_rcl_register_worker_parser(struct rspamd_config *cfg, gint type, - gboolean (*func)(ucl_object_t *, gpointer), gpointer ud) -{ - struct rspamd_worker_cfg_parser *nparser; - - nparser = g_hash_table_lookup(cfg->wrk_parsers, &type); - - if (nparser == NULL) { - /* Allocate new parser for this worker */ - nparser = - rspamd_mempool_alloc0(cfg->cfg_pool, - sizeof(struct rspamd_worker_cfg_parser)); - nparser->type = type; - nparser->parsers = g_hash_table_new(rspamd_worker_param_key_hash, - rspamd_worker_param_key_equal); - rspamd_mempool_add_destructor(cfg->cfg_pool, - (rspamd_mempool_destruct_t) g_hash_table_unref, nparser->parsers); - - g_hash_table_insert(cfg->wrk_parsers, &nparser->type, nparser); - } - - nparser->def_obj_parser = func; - nparser->def_ud = ud; -} - -/* Checksum functions */ -static int -rspamd_rcl_emitter_append_c(unsigned char c, size_t nchars, void *ud) -{ - rspamd_cryptobox_hash_state_t *hs = ud; - guint64 d[2]; - - d[0] = nchars; - d[1] = c; - - rspamd_cryptobox_hash_update(hs, (const guchar *) d, sizeof(d)); - - return 0; -} - -static int -rspamd_rcl_emitter_append_len(unsigned const char *str, size_t len, void *ud) -{ - rspamd_cryptobox_hash_state_t *hs = ud; - - rspamd_cryptobox_hash_update(hs, str, len); - - return 0; -} -static int -rspamd_rcl_emitter_append_int(int64_t elt, void *ud) -{ - rspamd_cryptobox_hash_state_t *hs = ud; - - rspamd_cryptobox_hash_update(hs, (const guchar *) &elt, sizeof(elt)); - - return 0; -} - -static int -rspamd_rcl_emitter_append_double(double elt, void *ud) -{ - rspamd_cryptobox_hash_state_t *hs = ud; - - rspamd_cryptobox_hash_update(hs, (const guchar *) &elt, sizeof(elt)); - - return 0; -} - -void rspamd_rcl_section_free(gpointer p) -{ - struct rspamd_rcl_section *top = p, *cur, *tmp; - struct rspamd_rcl_default_handler_data *dh, *dhtmp; - - HASH_ITER(hh, top, cur, tmp) - { - HASH_DEL(top, cur); - - if (cur->subsections) { - rspamd_rcl_section_free(cur->subsections); - } - - HASH_ITER(hh, cur->default_parser, dh, dhtmp) - { - HASH_DEL(cur->default_parser, dh); - g_free(dh->key); - g_free(dh); - } - - ucl_object_unref(cur->doc_ref); - g_free(cur); - } -} - -/** - * Calls for an external lua function to apply potential config transformations - * if needed. This function can change the cfg->rcl_obj. - * - * Example of transformation function: - * - * function(obj) - * if obj.something == 'foo' then - * obj.something = "bla" - * return true, obj - * end - * - * return false, nil - * end - * - * If function returns 'false' then rcl_obj is not touched. Otherwise, - * it is changed, then rcl_obj is imported from lua. Old config is dereferenced. - * @param cfg - */ -void rspamd_rcl_maybe_apply_lua_transform(struct rspamd_config *cfg) -{ - lua_State *L = cfg->lua_state; - gint err_idx, ret; - gchar str[PATH_MAX]; - static const char *transform_script = "lua_cfg_transform"; - - g_assert(L != NULL); - - rspamd_snprintf(str, sizeof(str), "return require \"%s\"", - transform_script); - - if (luaL_dostring(L, str) != 0) { - msg_warn_config("cannot execute lua script %s: %s", - str, lua_tostring(L, -1)); - return; - } - else { -#if LUA_VERSION_NUM >= 504 - lua_settop(L, -2); -#endif - if (lua_type(L, -1) != LUA_TFUNCTION) { - msg_warn_config("lua script must return " - "function and not %s", - lua_typename(L, lua_type(L, -1))); - - return; - } - } - - lua_pushcfunction(L, &rspamd_lua_traceback); - err_idx = lua_gettop(L); - - /* Push function */ - lua_pushvalue(L, -2); - - /* Push the existing config */ - ucl_object_push_lua(L, cfg->rcl_obj, true); - - if ((ret = lua_pcall(L, 1, 2, err_idx)) != 0) { - msg_err("call to rspamadm lua script failed (%d): %s", ret, - lua_tostring(L, -1)); - lua_settop(L, 0); - - return; - } - - if (lua_toboolean(L, -2) && lua_type(L, -1) == LUA_TTABLE) { - ucl_object_t *old_cfg = cfg->rcl_obj; - - msg_info_config("configuration has been transformed in Lua"); - cfg->rcl_obj = ucl_object_lua_import(L, -1); - ucl_object_unref(old_cfg); - } - - /* error function */ - lua_settop(L, 0); -} - -static bool -rspamd_rcl_decrypt_handler(struct ucl_parser *parser, - const unsigned char *source, size_t source_len, - unsigned char **destination, size_t *dest_len, - void *user_data) -{ - GError *err = NULL; - struct rspamd_cryptobox_keypair *kp = (struct rspamd_cryptobox_keypair *) user_data; - - if (!rspamd_keypair_decrypt(kp, source, source_len, - destination, dest_len, &err)) { - msg_err("cannot decrypt file: %e", err); - g_error_free(err); - - return false; - } - - return true; -} - -static bool -rspamd_rcl_jinja_handler(struct ucl_parser *parser, - const unsigned char *source, size_t source_len, - unsigned char **destination, size_t *dest_len, - void *user_data) -{ - struct rspamd_config *cfg = (struct rspamd_config *) user_data; - lua_State *L = cfg->lua_state; - gint err_idx; - - lua_pushcfunction(L, &rspamd_lua_traceback); - err_idx = lua_gettop(L); - - /* Obtain function */ - if (!rspamd_lua_require_function(L, "lua_util", "jinja_template")) { - msg_err_config("cannot require lua_util.jinja_template"); - lua_settop(L, err_idx - 1); - - return false; - } - - lua_pushlstring(L, source, source_len); - lua_getglobal(L, "rspamd_env"); - lua_pushboolean(L, false); - - if (lua_pcall(L, 3, 1, err_idx) != 0) { - msg_err_config("cannot call lua jinja_template script: %s", - lua_tostring(L, -1)); - lua_settop(L, err_idx - 1); - - return false; - } - - if (lua_type(L, -1) == LUA_TSTRING) { - const char *ndata; - gsize nsize; - - ndata = lua_tolstring(L, -1, &nsize); - *destination = UCL_ALLOC(nsize); - memcpy(*destination, ndata, nsize); - *dest_len = nsize; - } - else { - msg_err_config("invalid return type when templating jinja %s", - lua_typename(L, lua_type(L, -1))); - lua_settop(L, err_idx - 1); - - return false; - } - - lua_settop(L, err_idx - 1); - - return true; -} - -static void -rspamd_rcl_decrypt_free(unsigned char *data, size_t len, void *user_data) -{ - g_free(data); -} - -void rspamd_config_calculate_cksum(struct rspamd_config *cfg) -{ - rspamd_cryptobox_hash_state_t hs; - unsigned char cksumbuf[rspamd_cryptobox_HASHBYTES]; - struct ucl_emitter_functions f; - - /* Calculate checksum */ - rspamd_cryptobox_hash_init(&hs, NULL, 0); - f.ucl_emitter_append_character = rspamd_rcl_emitter_append_c; - f.ucl_emitter_append_double = rspamd_rcl_emitter_append_double; - f.ucl_emitter_append_int = rspamd_rcl_emitter_append_int; - f.ucl_emitter_append_len = rspamd_rcl_emitter_append_len; - f.ucl_emitter_free_func = NULL; - f.ud = &hs; - ucl_object_emit_full(cfg->rcl_obj, UCL_EMIT_MSGPACK, - &f, cfg->config_comments); - rspamd_cryptobox_hash_final(&hs, cksumbuf); - cfg->checksum = rspamd_encode_base32(cksumbuf, sizeof(cksumbuf), RSPAMD_BASE32_DEFAULT); - /* Also change the tag of cfg pool to be equal to the checksum */ - rspamd_strlcpy(cfg->cfg_pool->tag.uid, cfg->checksum, - MIN(sizeof(cfg->cfg_pool->tag.uid), strlen(cfg->checksum))); -} - -gboolean -rspamd_config_parse_ucl(struct rspamd_config *cfg, - const gchar *filename, - GHashTable *vars, - ucl_include_trace_func_t inc_trace, - void *trace_data, - gboolean skip_jinja, - GError **err) -{ - struct stat st; - gint fd; - struct ucl_parser *parser; - gchar keypair_path[PATH_MAX]; - struct rspamd_cryptobox_keypair *decrypt_keypair = NULL; - gchar *data; - - if ((fd = open(filename, O_RDONLY)) == -1) { - g_set_error(err, cfg_rcl_error_quark(), errno, - "cannot open %s: %s", filename, strerror(errno)); - return FALSE; - } - if (fstat(fd, &st) == -1) { - g_set_error(err, cfg_rcl_error_quark(), errno, - "cannot stat %s: %s", filename, strerror(errno)); - close(fd); - - return FALSE; - } - /* Now mmap this file to simplify reading process */ - if ((data = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) { - g_set_error(err, cfg_rcl_error_quark(), errno, - "cannot mmap %s: %s", filename, strerror(errno)); - close(fd); - - return FALSE; - } - - close(fd); - - /* Try to load keyfile if available */ - rspamd_snprintf(keypair_path, sizeof(keypair_path), "%s.key", - filename); - if ((fd = open(keypair_path, O_RDONLY)) != -1) { - struct ucl_parser *kp_parser; - - kp_parser = ucl_parser_new(0); - - if (ucl_parser_add_fd(kp_parser, fd)) { - ucl_object_t *kp_obj; - - kp_obj = ucl_parser_get_object(kp_parser); - - g_assert(kp_obj != NULL); - decrypt_keypair = rspamd_keypair_from_ucl(kp_obj); - - if (decrypt_keypair == NULL) { - msg_err_config_forced("cannot load keypair from %s: invalid keypair", - keypair_path); - } - else { - /* Add decryption support to UCL */ - rspamd_mempool_add_destructor(cfg->cfg_pool, - (rspamd_mempool_destruct_t) rspamd_keypair_unref, - decrypt_keypair); - } - - ucl_object_unref(kp_obj); - } - else { - msg_err_config_forced("cannot load keypair from %s: %s", - keypair_path, ucl_parser_get_error(kp_parser)); - } - - ucl_parser_free(kp_parser); - close(fd); - } - - parser = ucl_parser_new(UCL_PARSER_SAVE_COMMENTS); - rspamd_ucl_add_conf_variables(parser, vars); - rspamd_ucl_add_conf_macros(parser, cfg); - ucl_parser_set_filevars(parser, filename, true); - - if (inc_trace) { - ucl_parser_set_include_tracer(parser, inc_trace, trace_data); - } - - if (decrypt_keypair) { - struct ucl_parser_special_handler *decrypt_handler; - - decrypt_handler = rspamd_mempool_alloc0(cfg->cfg_pool, - sizeof(*decrypt_handler)); - decrypt_handler->user_data = decrypt_keypair; - decrypt_handler->magic = encrypted_magic; - decrypt_handler->magic_len = sizeof(encrypted_magic); - decrypt_handler->handler = rspamd_rcl_decrypt_handler; - decrypt_handler->free_function = rspamd_rcl_decrypt_free; - - ucl_parser_add_special_handler(parser, decrypt_handler); - } - - if (!skip_jinja) { - struct ucl_parser_special_handler *jinja_handler; - - jinja_handler = rspamd_mempool_alloc0(cfg->cfg_pool, - sizeof(*jinja_handler)); - jinja_handler->user_data = cfg; - jinja_handler->flags = UCL_SPECIAL_HANDLER_PREPROCESS_ALL; - jinja_handler->handler = rspamd_rcl_jinja_handler; - - ucl_parser_add_special_handler(parser, jinja_handler); - } - - if (!ucl_parser_add_chunk(parser, data, st.st_size)) { - g_set_error(err, cfg_rcl_error_quark(), errno, - "ucl parser error: %s", ucl_parser_get_error(parser)); - ucl_parser_free(parser); - munmap(data, st.st_size); - - return FALSE; - } - - munmap(data, st.st_size); - cfg->rcl_obj = ucl_parser_get_object(parser); - cfg->config_comments = ucl_object_ref(ucl_parser_get_comments(parser)); - ucl_parser_free(parser); - - return TRUE; -} - -gboolean -rspamd_config_read(struct rspamd_config *cfg, - const gchar *filename, - rspamd_rcl_section_fin_t logger_fin, - gpointer logger_ud, - GHashTable *vars, - gboolean skip_jinja, - gchar **lua_env) -{ - GError *err = NULL; - struct rspamd_rcl_section *top, *logger_section; - const ucl_object_t *logger_obj; - - rspamd_lua_set_path(cfg->lua_state, NULL, vars); - - if (!rspamd_lua_set_env(cfg->lua_state, vars, lua_env, &err)) { - msg_err_config_forced("failed to set up environment: %e", err); - g_error_free(err); - - return FALSE; - } - - if (!rspamd_config_parse_ucl(cfg, filename, vars, NULL, NULL, skip_jinja, &err)) { - msg_err_config_forced("failed to load config: %e", err); - g_error_free(err); - - return FALSE; - } - - top = rspamd_rcl_config_init(cfg, NULL); - /* Add new paths if defined in options */ - rspamd_lua_set_path(cfg->lua_state, cfg->rcl_obj, vars); - rspamd_lua_set_globals(cfg, cfg->lua_state); - rspamd_mempool_add_destructor(cfg->cfg_pool, rspamd_rcl_section_free, top); - err = NULL; - - if (logger_fin != NULL) { - HASH_FIND_STR(top, "logging", logger_section); - - if (logger_section != NULL) { - logger_obj = ucl_object_lookup_any(cfg->rcl_obj, "logging", - "logger", NULL); - - if (logger_obj == NULL) { - logger_fin(cfg->cfg_pool, logger_ud); - } - else { - if (!rspamd_rcl_process_section(cfg, logger_section, cfg, - logger_obj, cfg->cfg_pool, &err)) { - msg_err_config_forced("cannot init logger: %e", err); - g_error_free(err); - - return FALSE; - } - else { - logger_fin(cfg->cfg_pool, logger_ud); - } - - /* Init lua logging */ - lua_State *L = cfg->lua_state; - gint err_idx; - struct rspamd_config **pcfg; - - lua_pushcfunction(L, &rspamd_lua_traceback); - err_idx = lua_gettop(L); - - /* Obtain function */ - if (!rspamd_lua_require_function(L, "lua_util", - "init_debug_logging")) { - msg_err_config("cannot require lua_util.init_debug_logging"); - lua_settop(L, err_idx - 1); - - return FALSE; - } - - pcfg = lua_newuserdata(L, sizeof(*pcfg)); - *pcfg = cfg; - rspamd_lua_setclass(L, "rspamd{config}", -1); - - if (lua_pcall(L, 1, 0, err_idx) != 0) { - msg_err_config("cannot call lua init_debug_logging script: %s", - lua_tostring(L, -1)); - lua_settop(L, err_idx - 1); - - return FALSE; - } - - lua_settop(L, err_idx - 1); - } - - HASH_DEL(top, logger_section); - } - } - - /* Transform config if needed */ - rspamd_rcl_maybe_apply_lua_transform(cfg); - rspamd_config_calculate_cksum(cfg); - - if (!rspamd_rcl_parse(top, cfg, cfg, cfg->cfg_pool, cfg->rcl_obj, &err)) { - msg_err_config("rcl parse error: %e", err); - - if (err) { - g_error_free(err); - } - - return FALSE; - } - - cfg->lang_det = rspamd_language_detector_init(cfg); - rspamd_mempool_add_destructor(cfg->cfg_pool, - (rspamd_mempool_destruct_t) rspamd_language_detector_unref, - cfg->lang_det); - - return TRUE; -} - -static void -rspamd_rcl_doc_obj_from_handler(ucl_object_t *doc_obj, - rspamd_rcl_default_handler_t handler, - gint flags) -{ - gboolean has_example = FALSE, has_type = FALSE; - const gchar *type = NULL; - - if (ucl_object_lookup(doc_obj, "example") != NULL) { - has_example = TRUE; - } - - if (ucl_object_lookup(doc_obj, "type") != NULL) { - has_type = TRUE; - } - - if (handler == rspamd_rcl_parse_struct_string) { - if (!has_type) { - ucl_object_insert_key(doc_obj, ucl_object_fromstring("string"), - "type", 0, false); - } - } - else if (handler == rspamd_rcl_parse_struct_integer) { - type = "int"; - - if (flags & RSPAMD_CL_FLAG_INT_16) { - type = "int16"; - } - else if (flags & RSPAMD_CL_FLAG_INT_32) { - type = "int32"; - } - else if (flags & RSPAMD_CL_FLAG_INT_64) { - type = "int64"; - } - else if (flags & RSPAMD_CL_FLAG_INT_SIZE) { - type = "size"; - } - else if (flags & RSPAMD_CL_FLAG_UINT) { - type = "uint"; - } - - if (!has_type) { - ucl_object_insert_key(doc_obj, ucl_object_fromstring(type), - "type", 0, false); - } - } - else if (handler == rspamd_rcl_parse_struct_double) { - if (!has_type) { - ucl_object_insert_key(doc_obj, ucl_object_fromstring("double"), - "type", 0, false); - } - } - else if (handler == rspamd_rcl_parse_struct_time) { - type = "time"; - - if (!has_type) { - ucl_object_insert_key(doc_obj, ucl_object_fromstring(type), - "type", 0, false); - } - } - else if (handler == rspamd_rcl_parse_struct_string_list) { - if (!has_type) { - ucl_object_insert_key(doc_obj, ucl_object_fromstring("string list"), - "type", 0, false); - } - if (!has_example) { - ucl_object_insert_key(doc_obj, - ucl_object_fromstring_common("param = \"str1, str2, str3\" OR " - "param = [\"str1\", \"str2\", \"str3\"]", - 0, 0), - "example", - 0, - false); - } - } - else if (handler == rspamd_rcl_parse_struct_boolean) { - if (!has_type) { - ucl_object_insert_key(doc_obj, - ucl_object_fromstring("bool"), - "type", - 0, - false); - } - } - else if (handler == rspamd_rcl_parse_struct_keypair) { - if (!has_type) { - ucl_object_insert_key(doc_obj, - ucl_object_fromstring("keypair"), - "type", - 0, - false); - } - if (!has_example) { - ucl_object_insert_key(doc_obj, - ucl_object_fromstring("keypair { " - "pubkey = ;" - " privkey = ; " - "}"), - "example", - 0, - false); - } - } - else if (handler == rspamd_rcl_parse_struct_addr) { - if (!has_type) { - ucl_object_insert_key(doc_obj, - ucl_object_fromstring("socket address"), - "type", - 0, - false); - } - } - else if (handler == rspamd_rcl_parse_struct_mime_addr) { - if (!has_type) { - ucl_object_insert_key(doc_obj, - ucl_object_fromstring("email address"), - "type", - 0, - false); - } - } -} - -ucl_object_t * -rspamd_rcl_add_doc_obj(ucl_object_t *doc_target, - const char *doc_string, - const char *doc_name, - ucl_type_t type, - rspamd_rcl_default_handler_t handler, - gint flags, - const char *default_value, - gboolean required) -{ - ucl_object_t *doc_obj; - - if (doc_target == NULL || doc_name == NULL) { - return NULL; - } - - doc_obj = ucl_object_typed_new(UCL_OBJECT); - - /* Insert doc string itself */ - if (doc_string) { - ucl_object_insert_key(doc_obj, - ucl_object_fromstring_common(doc_string, 0, 0), - "data", 0, false); - } - else { - ucl_object_insert_key(doc_obj, ucl_object_fromstring("undocumented"), - "data", 0, false); - } - - if (type != UCL_NULL) { - ucl_object_insert_key(doc_obj, - ucl_object_fromstring(ucl_object_type_to_string(type)), - "type", 0, false); - } - - rspamd_rcl_doc_obj_from_handler(doc_obj, handler, flags); - - ucl_object_insert_key(doc_obj, - ucl_object_frombool(required), - "required", 0, false); - - if (default_value) { - ucl_object_insert_key(doc_obj, - ucl_object_fromstring_common(default_value, 0, 0), - "default", 0, false); - } - - ucl_object_insert_key(doc_target, doc_obj, doc_name, 0, true); - - return doc_obj; -} - -ucl_object_t * -rspamd_rcl_add_doc_by_path(struct rspamd_config *cfg, - const gchar *doc_path, - const char *doc_string, - const char *doc_name, - ucl_type_t type, - rspamd_rcl_default_handler_t handler, - gint flags, - const char *default_value, - gboolean required) -{ - const ucl_object_t *found, *cur; - ucl_object_t *obj; - gchar **path_components, **comp; - - if (doc_path == NULL) { - /* Assume top object */ - return rspamd_rcl_add_doc_obj(cfg->doc_strings, - doc_string, - doc_name, - type, - handler, - flags, - default_value, - required); - } - else { - found = ucl_object_lookup_path(cfg->doc_strings, doc_path); - - if (found != NULL) { - return rspamd_rcl_add_doc_obj((ucl_object_t *) found, - doc_string, - doc_name, - type, - handler, - flags, - default_value, - required); - } - - /* Otherwise we need to insert all components of the path */ - path_components = g_strsplit_set(doc_path, ".", -1); - cur = cfg->doc_strings; - - for (comp = path_components; *comp != NULL; comp++) { - if (ucl_object_type(cur) != UCL_OBJECT) { - msg_err_config("Bad path while lookup for '%s' at %s", - doc_path, *comp); - g_strfreev(path_components); - - return NULL; - } - - found = ucl_object_lookup(cur, *comp); - - if (found == NULL) { - obj = ucl_object_typed_new(UCL_OBJECT); - ucl_object_insert_key((ucl_object_t *) cur, - obj, - *comp, - 0, - true); - cur = obj; - } - else { - cur = found; - } - } - - g_strfreev(path_components); - } - - return rspamd_rcl_add_doc_obj(ucl_object_ref(cur), - doc_string, - doc_name, - type, - handler, - flags, - default_value, - required); -} - -static void -rspamd_rcl_add_doc_from_comments(struct rspamd_config *cfg, - ucl_object_t *top_doc, const ucl_object_t *obj, - const ucl_object_t *comments, gboolean is_top) -{ - ucl_object_iter_t it = NULL; - const ucl_object_t *cur, *cmt; - ucl_object_t *cur_doc; - - if (ucl_object_type(obj) == UCL_OBJECT) { - while ((cur = ucl_object_iterate(obj, &it, true)) != NULL) { - cur_doc = NULL; - - if ((cmt = ucl_comments_find(comments, cur)) != NULL) { - cur_doc = rspamd_rcl_add_doc_obj(top_doc, - ucl_object_tostring(cmt), ucl_object_key(cur), - ucl_object_type(cur), NULL, 0, NULL, FALSE); - } - - if (ucl_object_type(cur) == UCL_OBJECT) { - if (cur_doc) { - rspamd_rcl_add_doc_from_comments(cfg, cur_doc, cur, - comments, - FALSE); - } - else { - rspamd_rcl_add_doc_from_comments(cfg, top_doc, cur, - comments, - FALSE); - } - } - } - } - else if (!is_top) { - if ((cmt = ucl_comments_find(comments, obj)) != NULL) { - rspamd_rcl_add_doc_obj(top_doc, - ucl_object_tostring(cmt), ucl_object_key(obj), - ucl_object_type(obj), NULL, 0, NULL, FALSE); - } - } -} - -ucl_object_t * -rspamd_rcl_add_doc_by_example(struct rspamd_config *cfg, - const gchar *root_path, - const gchar *doc_string, - const gchar *doc_name, - const gchar *example_data, gsize example_len) -{ - struct ucl_parser *parser; - ucl_object_t *top, *top_doc; - const ucl_object_t *comments; - - parser = ucl_parser_new(UCL_PARSER_NO_FILEVARS | UCL_PARSER_SAVE_COMMENTS); - - if (!ucl_parser_add_chunk(parser, example_data, example_len)) { - msg_err_config("cannot parse example: %s", - ucl_parser_get_error(parser)); - ucl_parser_free(parser); - - return NULL; - } - - top = ucl_parser_get_object(parser); - comments = ucl_parser_get_comments(parser); - - /* Add top object */ - top_doc = rspamd_rcl_add_doc_by_path(cfg, root_path, doc_string, - doc_name, ucl_object_type(top), NULL, 0, NULL, FALSE); - ucl_object_insert_key(top_doc, - ucl_object_fromstring_common(example_data, example_len, 0), - "example", 0, false); - - rspamd_rcl_add_doc_from_comments(cfg, top_doc, top, comments, TRUE); - - return top_doc; -} -- cgit v1.2.3