<commit_before>/* Copyright (c) 2013, <NAME>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *       * Redistributions of source code must retain the above copyright
 *         notice, this list of conditions and the following disclaimer.
 *       * Redistributions in binary form must reproduce the above copyright
 *         notice, this list of conditions and the following disclaimer in the
 *         documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "cfg_rcl.h"
#include "main.h"

/*
 * Common section handlers
 */
static gboolean
rspamd_rcl_logging_handler (struct config_file *cfg, rspamd_cl_object_t *obj,
		gpointer ud, struct rspamd_rcl_section *section, GError **err)
{
	rspamd_cl_object_t *val;
	gchar *filepath;
	const gchar *facility, *log_type, *log_level;

	obj = obj->value.ov;

	HASH_FIND_STR (obj, "type", val);
	if (val != NULL && rspamd_cl_obj_tostring_safe (val, &log_type)) {
		if (g_ascii_strcasecmp (log_type, "file") == 0) {
			/* Need to get filename */
			HASH_FIND_STR (obj, "filename", val);
			if (val == NULL || val->type != RSPAMD_CL_STRING) {
				g_set_error (err, CFG_RCL_ERROR, ENOENT, "filename attribute must be specified for file logging type");
				return FALSE;
			}
			if ((filepath = realpath (rspamd_cl_obj_tostring (val), NULL)) == NULL ||
					access (filepath, W_OK) == -1) {
				g_set_error (err, CFG_RCL_ERROR, errno, "log file is inaccessible");
				return FALSE;
			}
			cfg->log_type = RSPAMD_LOG_FILE;
			cfg->log_file = memory_pool_strdup (cfg->cfg_pool, filepath);
		}
		else if (g_ascii_strcasecmp (log_type, "syslog") == 0) {
			/* Need to get facility */
			cfg->log_facility = LOG_DAEMON;
			cfg->log_type = RSPAMD_LOG_SYSLOG;
			HASH_FIND_STR (obj, "facility", val);
			if (val != NULL && rspamd_cl_obj_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;
				}
			}
		}
		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 ("logging type is not specified correctly, log output to the console");
	}

	/* Handle log level */
	HASH_FIND_STR (obj, "level", val);
	if (val != NULL && rspamd_cl_obj_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, "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;
		}
	}

	return rspamd_rcl_section_parse_defaults (section, cfg, obj, cfg, err);
}

/**
 * Fake handler to parse default options only, uses struct cfg_file as pointer
 * for default handlers
 */
static gboolean
rspamd_rcl_empty_handler (struct config_file *cfg, rspamd_cl_object_t *obj,
		gpointer ud, struct rspamd_rcl_section *section, GError **err)
{
	return rspamd_rcl_section_parse_defaults (section, cfg, obj, cfg, err);
}

/**
 * Add new section to the configuration
 * @param top top section
 * @param name the name of the section
 * @param handler handler function for all attributes
 * @param type type of object handled by a handler
 * @param required whether at least one of these sections is required
 * @param strict_type turn on strict check for types for this section
 * @return newly created structure
 */
static inline struct rspamd_rcl_section*
rspamd_rcl_add_section (struct rspamd_rcl_section *top,
		const gchar *name, rspamd_rcl_handler_t handler,
		enum rspamd_cl_type type, gboolean required, gboolean strict_type)
{
	struct rspamd_rcl_section *new;

	new = g_slice_alloc0 (sizeof (struct rspamd_rcl_section));
	new->name = name;
	new->handler = handler;
	new->type = type;
	new->strict_type = strict_type;

	HASH_ADD_KEYPTR (hh, top, new->name, strlen (new->name), new);
	return new;
}

/**
 * Add a default handler for a section
 * @param section section pointer
 * @param name name of param
 * @param handler handler of param
 * @param offset offset in a structure
 * @param flags flags for the parser
 * @return newly created structure
 */
static inline struct rspamd_rcl_default_handler_data *
rspamd_rcl_add_default_handler (struct rspamd_rcl_section *section, const gchar *name,
		rspamd_rcl_handler_t handler, gsize offset, gint flags)
{
	struct rspamd_rcl_default_handler_data *new;

	new = g_slice_alloc0 (sizeof (struct rspamd_rcl_default_handler_data));
	new->key = name;
	new->handler = handler;
	new->pd.offset = offset;
	new->pd.flags = flags;

	HASH_ADD_KEYPTR (hh, section->default_parser, new->key, strlen (new->key), new);
	return new;
}

struct rspamd_rcl_section*
rspamd_rcl_config_init (void)
{
	struct rspamd_rcl_section *new, *sub;

	new = g_slice_alloc0 (sizeof (struct rspamd_rcl_section));

	/* TODO: add all known rspamd sections here */
	/**
	 * Logging section
	 */
	sub = rspamd_rcl_add_section (new, "logging", rspamd_rcl_logging_handler, RSPAMD_CL_OBJECT,
			FALSE, TRUE);
	/* Default handlers */
	rspamd_rcl_add_default_handler (sub, "log_buffer", rspamd_rcl_parse_struct_integer,
			G_STRUCT_OFFSET (struct config_file, log_buf_size), 0);
	rspamd_rcl_add_default_handler (sub, "log_urls", rspamd_rcl_parse_struct_boolean,
			G_STRUCT_OFFSET (struct config_file, log_urls), 0);
	rspamd_rcl_add_default_handler (sub, "debug_ip", rspamd_rcl_parse_struct_string,
			G_STRUCT_OFFSET (struct config_file, debug_ip_map), 0);
	rspamd_rcl_add_default_handler (sub, "debug_symbols", rspamd_rcl_parse_struct_string_list,
			G_STRUCT_OFFSET (struct config_file, debug_symbols), 0);
	rspamd_rcl_add_default_handler (sub, "log_color", rspamd_rcl_parse_struct_boolean,
			G_STRUCT_OFFSET (struct config_file, log_color), 0);
	/**
	 * Options section
	 */
	sub = rspamd_rcl_add_section (new, "options", rspamd_rcl_empty_handler, RSPAMD_CL_OBJECT,
			FALSE, TRUE);
	rspamd_rcl_add_default_handler (sub, "statfile_pool_size", rspamd_rcl_parse_struct_integer,
			G_STRUCT_OFFSET (struct config_file, max_statfile_size), RSPAMD_CL_FLAG_INT_SIZE);
	rspamd_rcl_add_default_handler (sub, "cache_file", rspamd_rcl_parse_struct_string,
			G_STRUCT_OFFSET (struct config_file, cache_filename), 0);
	rspamd_rcl_add_default_handler (sub, "dns_timeout", rspamd_rcl_parse_struct_time,
			G_STRUCT_OFFSET (struct config_file, dns_timeout), RSPAMD_CL_FLAG_TIME_INTEGER);
	rspamd_rcl_add_default_handler (sub, "dns_retransmits", rspamd_rcl_parse_struct_integer,
			G_STRUCT_OFFSET (struct config_file, dns_retransmits), RSPAMD_CL_FLAG_INT_32);
	rspamd_rcl_add_default_handler (sub, "raw_mode", rspamd_rcl_parse_struct_boolean,
			G_STRUCT_OFFSET (struct config_file, raw_mode), 0);
	rspamd_rcl_add_default_handler (sub, "one_shot", rspamd_rcl_parse_struct_boolean,
			G_STRUCT_OFFSET (struct config_file, one_shot_mode), 0);
	rspamd_rcl_add_default_handler (sub, "check_attachements", rspamd_rcl_parse_struct_boolean,
			G_STRUCT_OFFSET (struct config_file, check_text_attachements), 0);
	rspamd_rcl_add_default_handler (sub, "tempdir", rspamd_rcl_parse_struct_string,
			G_STRUCT_OFFSET (struct config_file, temp_dir), 0);
	rspamd_rcl_add_default_handler (sub, "pidfile", rspamd_rcl_parse_struct_string,
			G_STRUCT_OFFSET (struct config_file, pid_file), 0);
	rspamd_rcl_add_default_handler (sub, "filters", rspamd_rcl_parse_struct_string,
			G_STRUCT_OFFSET (struct config_file, filters_str), 0);
	rspamd_rcl_add_default_handler (sub, "sync_interval", rspamd_rcl_parse_struct_time,
			G_STRUCT_OFFSET (struct config_file, statfile_sync_interval), RSPAMD_CL_FLAG_TIME_INTEGER);
	rspamd_rcl_add_default_handler (sub, "sync_timeout", rspamd_rcl_parse_struct_time,
			G_STRUCT_OFFSET (struct config_file, statfile_sync_timeout), RSPAMD_CL_FLAG_TIME_INTEGER);
	rspamd_rcl_add_default_handler (sub, "max_diff", rspamd_rcl_parse_struct_integer,
			G_STRUCT_OFFSET (struct config_file, max_diff), RSPAMD_CL_FLAG_INT_SIZE);
	rspamd_rcl_add_default_handler (sub, "map_watch_interval", rspamd_rcl_parse_struct_time,
			G_STRUCT_OFFSET (struct config_file, map_timeout), RSPAMD_CL_FLAG_TIME_FLOAT);
	rspamd_rcl_add_default_handler (sub, "dynamic_conf", rspamd_rcl_parse_struct_string,
			G_STRUCT_OFFSET (struct config_file, dynamic_conf), 0);
	rspamd_rcl_add_default_handler (sub, "rrd", rspamd_rcl_parse_struct_string,
			G_STRUCT_OFFSET (struct config_file, rrd_file), 0);
	rspamd_rcl_add_default_handler (sub, "history_file", rspamd_rcl_parse_struct_string,
			G_STRUCT_OFFSET (struct config_file, history_file), 0);
	rspamd_rcl_add_default_handler (sub, "use_mlock", rspamd_rcl_parse_struct_boolean,<commit_msg>Add a separate options handler.<commit_after>/* Copyright (c) 2013, <NAME>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *       * Redistributions of source code must retain the above copyright
 *         notice, this list of conditions and the following disclaimer.
 *       * Redistributions in binary form must reproduce the above copyright
 *         notice, this list of conditions and the following disclaimer in the
 *         documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "cfg_rcl.h"
#include "main.h"

/*
 * Common section handlers
 */
static gboolean
rspamd_rcl_logging_handler (struct config_file *cfg, rspamd_cl_object_t *obj,
		gpointer ud, struct rspamd_rcl_section *section, GError **err)
{
	rspamd_cl_object_t *val, *lobj;
	gchar *filepath;
	const gchar *facility, *log_type, *log_level;

	lobj = obj->value.ov;

	HASH_FIND_STR (lobj, "type", val);
	if (val != NULL && rspamd_cl_obj_tostring_safe (val, &log_type)) {
		if (g_ascii_strcasecmp (log_type, "file") == 0) {
			/* Need to get filename */
			HASH_FIND_STR (lobj, "filename", val);
			if (val == NULL || val->type != RSPAMD_CL_STRING) {
				g_set_error (err, CFG_RCL_ERROR, ENOENT, "filename attribute must be specified for file logging type");
				return FALSE;
			}
			if ((filepath = realpath (rspamd_cl_obj_tostring (val), NULL)) == NULL ||
					access (filepath, W_OK) == -1) {
				g_set_error (err, CFG_RCL_ERROR, errno, "log file is inaccessible");
				return FALSE;
			}
			cfg->log_type = RSPAMD_LOG_FILE;
			cfg->log_file = memory_pool_strdup (cfg->cfg_pool, filepath);
		}
		else if (g_ascii_strcasecmp (log_type, "syslog") == 0) {
			/* Need to get facility */
			cfg->log_facility = LOG_DAEMON;
			cfg->log_type = RSPAMD_LOG_SYSLOG;
			HASH_FIND_STR (lobj, "facility", val);
			if (val != NULL && rspamd_cl_obj_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;
				}
			}
		}
		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 ("logging type is not specified correctly, log output to the console");
	}

	/* Handle log level */
	HASH_FIND_STR (lobj, "level", val);
	if (val != NULL && rspamd_cl_obj_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, "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;
		}
	}

	return rspamd_rcl_section_parse_defaults (section, cfg, obj, cfg, err);
}

static gboolean
rspamd_rcl_options_handler (struct config_file *cfg, rspamd_cl_object_t *obj,
		gpointer ud, struct rspamd_rcl_section *section, GError **err)
{
	rspamd_cl_object_t *val, *lobj;
	const gchar *user_settings, *domain_settings;

	lobj = obj->value.ov;

	/* Handle user and domain settings */
	HASH_FIND_STR (lobj, "user_settings", val);
	if (val != NULL && rspamd_cl_obj_tostring_safe (val, &user_settings)) {
		if (!read_settings (user_settings, "Users' settings", cfg, cfg->user_settings)) {
			g_set_error (err, CFG_RCL_ERROR, EINVAL, "cannot read settings: %s", user_settings);
			return FALSE;
		}
		cfg->user_settings_str = memory_pool_strdup (cfg->cfg_pool, user_settings);
	}

	HASH_FIND_STR (lobj, "domain_settings", val);
	if (val != NULL && rspamd_cl_obj_tostring_safe (val, &domain_settings)) {
		if (!read_settings (domain_settings, "Domains settings", cfg, cfg->domain_settings)) {
			g_set_error (err, CFG_RCL_ERROR, EINVAL, "cannot read settings: %s", domain_settings);
			return FALSE;
		}
		cfg->domain_settings_str = memory_pool_strdup (cfg->cfg_pool, domain_settings);
	}

	return rspamd_rcl_section_parse_defaults (section, cfg, obj, cfg, err);
}

/**
 * Fake handler to parse default options only, uses struct cfg_file as pointer
 * for default handlers
 */
static gboolean
rspamd_rcl_empty_handler (struct config_file *cfg, rspamd_cl_object_t *obj,
		gpointer ud, struct rspamd_rcl_section *section, GError **err)
{
	return rspamd_rcl_section_parse_defaults (section, cfg, obj, cfg, err);
}

/**
 * Add new section to the configuration
 * @param top top section
 * @param name the name of the section
 * @param handler handler function for all attributes
 * @param type type of object handled by a handler
 * @param required whether at least one of these sections is required
 * @param strict_type turn on strict check for types for this section
 * @return newly created structure
 */
static inline struct rspamd_rcl_section*
rspamd_rcl_add_section (struct rspamd_rcl_section *top,
		const gchar *name, rspamd_rcl_handler_t handler,
		enum rspamd_cl_type type, gboolean required, gboolean strict_type)
{
	struct rspamd_rcl_section *new;

	new = g_slice_alloc0 (sizeof (struct rspamd_rcl_section));
	new->name = name;
	new->handler = handler;
	new->type = type;
	new->strict_type = strict_type;

	HASH_ADD_KEYPTR (hh, top, new->name, strlen (new->name), new);
	return new;
}

/**
 * Add a default handler for a section
 * @param section section pointer
 * @param name name of param
 * @param handler handler of param
 * @param offset offset in a structure
 * @param flags flags for the parser
 * @return newly created structure
 */
static inline struct rspamd_rcl_default_handler_data *
rspamd_rcl_add_default_handler (struct rspamd_rcl_section *section, const gchar *name,
		rspamd_rcl_handler_t handler, gsize offset, gint flags)
{
	struct rspamd_rcl_default_handler_data *new;

	new = g_slice_alloc0 (sizeof (struct rspamd_rcl_default_handler_data));
	new->key = name;
	new->handler = handler;
	new->pd.offset = offset;
	new->pd.flags = flags;

	HASH_ADD_KEYPTR (hh, section->default_parser, new->key, strlen (new->key), new);
	return new;
}

struct rspamd_rcl_section*
rspamd_rcl_config_init (void)
{
	struct rspamd_rcl_section *new, *sub;

	new = g_slice_alloc0 (sizeof (struct rspamd_rcl_section));

	/* TODO: add all known rspamd sections here */
	/**
	 * Logging section
	 */
	sub = rspamd_rcl_add_section (new, "logging", rspamd_rcl_logging_handler, RSPAMD_CL_OBJECT,
			FALSE, TRUE);
	/* Default handlers */
	rspamd_rcl_add_default_handler (sub, "log_buffer", rspamd_rcl_parse_struct_integer,
			G_STRUCT_OFFSET (struct config_file, log_buf_size), 0);
	rspamd_rcl_add_default_handler (sub, "log_urls", rspamd_rcl_parse_struct_boolean,
			G_STRUCT_OFFSET (struct config_file, log_urls), 0);
	rspamd_rcl_add_default_handler (sub, "debug_ip", rspamd_rcl_parse_struct_string,
			G_STRUCT_OFFSET (struct config_file, debug_ip_map), 0);
	rspamd_rcl_add_default_handler (sub, "debug_symbols", rspamd_rcl_parse_struct_string_list,
			G_STRUCT_OFFSET (struct config_file, debug_symbols), 0);
	rspamd_rcl_add_default_handler (sub, "log_color", rspamd_rcl_parse_struct_boolean,
			G_STRUCT_OFFSET (struct config_file, log_color), 0);
	/**
	 * Options section
	 */
	sub = rspamd_rcl_add_section (new, "options", rspamd_rcl_options_handler, RSPAMD_CL_OBJECT,
			FALSE, TRUE);
	rspamd_rcl_add_default_handler (sub, "statfile_pool_size", rspamd_rcl_parse_struct_integer,
			G_STRUCT_OFFSET (struct config_file, max_statfile_size), RSPAMD_CL_FLAG_INT_SIZE);
	rspamd_rcl_add_default_handler (sub, "cache_file", rspamd_rcl_parse_struct_string,
			G_STRUCT_OFFSET (struct config_file, cache_filename), 0);
	rspamd_rcl_add_default_handler (sub, "dns_nameserver", rspamd_rcl_parse_struct_string_list,
			G_STRUCT_OFFSET (struct config_file, nameservers), 0);
	rspamd_rcl_add_default_handler (sub, "dns_timeout", rspamd_rcl_parse_struct_time,
			G_STRUCT_OFFSET (struct config_file, dns_timeout), RSPAMD_CL_FLAG_TIME_INTEGER);
	rspamd_rcl_add_default_handler (sub, "dns_retransmits", rspamd_rcl_parse_struct_integer,
			G_STRUCT_OFFSET (struct config_file, dns_retransmits), RSPAMD_CL_FLAG_INT_32);
	rspamd_rcl_add_default_handler (sub, "raw_mode", rspamd_rcl_parse_struct_boolean,
			G_STRUCT_OFFSET (struct config_file, raw_mode), 0);
	rspamd_rcl_add_default_handler (sub, "one_shot", rspamd_rcl_parse_struct_boolean,
			G_STRUCT_OFFSET (struct config_file, one_shot_mode), 0);
	rspamd_rcl_add_default_handler (sub, "check_attachements", rspamd_rcl_parse_struct_boolean,
			G_STRUCT_OFFSET (struct config_file, check_text_attachements), 0);
	rspamd_rcl_add_default_handler (sub, "tempdir", rspamd_rcl_parse_struct_string,
			G_STRUCT_OFFSET (struct config_file, temp_dir), 0);
	rspamd_rcl_add_default_handler (sub, "pidfile", rspamd_rcl_parse_struct_string,
			G_STRUCT_OFFSET (struct config_file, pid_file), 0);
	rspamd_rcl_add_default_handler (sub, "filters", rspamd_rcl_parse_struct_string,
			G_STRUCT_OFFSET (struct config_file, filters_str), 0);
	rspamd_rcl_add_default_handler (sub, "sync_interval", rspamd_rcl_parse_struct_time,
			G_STRUCT_OFFSET (struct config_file, statfile_sync_interval), RSPAMD_CL_FLAG_TIME_INTEGER);
	rspamd_rcl_add_default_handler (sub, "sync_timeout", rspamd_rcl_parse_struct_time,
			G_STRUCT_OFFSET (struct config_file, statfile_sync_timeout), RSPAMD_CL_FLAG_TIME_INTEGER);
	rspamd_rcl_add_default_handler (sub, "max_diff", rspamd_rcl_parse_struct_integer,
			G_STRUCT_OFFSET (struct config_file, max_diff), RSPAMD_CL_FLAG_INT_SIZE);
	rspamd_rcl_add_default_handler (sub, "map_watch_interval", rspamd_rcl_parse_struct_time,
			G_STRUCT_OFFSET (struct config_file, map_timeout), RSPAMD_CL_FLAG_TIME_FLOAT);
	rspamd_rcl_add_default_handler (sub, "dynamic_conf", rspamd_rcl_parse_struct_string,
			G_STRUCT_OFFSET (struct config_file, dynamic_conf), 0);
	rspamd_rcl_add_default_handler (sub, "rrd", rspamd_rcl_parse_struct_string,
			G_STRUCT_OFFSET (struct config_file, rrd_file), 0);
	rspamd_rcl_add_default_handler (sub, "history_file", rspamd_rcl_parse_struct_string,
			G_STRUCT_OFFSET (struct config_file, history_file), 0);
	rspamd_rcl_add_default_handler (sub, "use_mlock", rspamd_rcl_parse_struct_boolean,
{'fraction_non_alphanumeric': 0.10401956530314387, 'fraction_numerical': 0.006587511340775512, 'mean_word_length': 5.052041059918835, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 88, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6385091', 'n_tokens_mistral': 10128, 'n_tokens_neox': 9200, 'n_words': 2203}