From 7dbdebbca0dace0f7c1f1a82f38a99ca489b0180 Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Sat, 27 Mar 2010 05:12:35 +0300 Subject: [PATCH] * New syntax parser that should make syntax check of XML configs * Rework of config structure types (use glib types) * TODO: - implement the whole syntax of rspamd in XML - implement custom handlers that modules can install - write missing handlers --- src/cfg_file.h | 78 ++++----- src/cfg_utils.c | 76 ++++----- src/cfg_xml.c | 442 ++++++++++++++++++++++++++++++++++++++++-------- src/cfg_xml.h | 8 +- 4 files changed, 460 insertions(+), 144 deletions(-) diff --git a/src/cfg_file.h b/src/cfg_file.h index 74ce91911..18291077d 100644 --- a/src/cfg_file.h +++ b/src/cfg_file.h @@ -77,10 +77,10 @@ enum rspamd_log_type { */ struct rspamd_regexp { enum rspamd_regexp_type type; /**< regexp type */ - char *regexp_text; /**< regexp text representation */ + gchar *regexp_text; /**< regexp text representation */ GRegex *regexp; /**< glib regexp structure */ GRegex *raw_regexp; /**< glib regexp structure for raw matching */ - char *header; /**< header name for header regexps */ + gchar *header; /**< header name for header regexps */ }; /** @@ -98,16 +98,16 @@ struct memcached_server { * script module list item */ struct script_module { - char *name; /**< name of module */ - char *path; /**< path to module */ + gchar *name; /**< name of module */ + gchar *path; /**< path to module */ }; /** * Module option */ struct module_opt { - char *param; /**< parameter name */ - char *value; /**< paramater value */ + gchar *param; /**< parameter name */ + gchar *value; /**< paramater value */ }; /** @@ -123,7 +123,7 @@ struct statfile_section { * Statfile autolearn parameters */ struct statfile_autolearn_params { - const char *metric; /**< metric name for autolearn triggering */ + const gchar *metric; /**< metric name for autolearn triggering */ double threshold_min; /**< threshold mark */ double threshold_max; /**< threshold mark */ GList *symbols; /**< list of symbols */ @@ -154,9 +154,9 @@ typedef double (*statfile_normalize_func)(double score, void *params); * Statfile config definition */ struct statfile { - char *symbol; /**< symbol of statfile */ - char *path; /**< filesystem pattern (with %r or %f) */ - size_t size; /**< size of statfile */ + gchar *symbol; /**< symbol of statfile */ + gchar *path; /**< filesystem pattern (with %r or %f) */ + gsize size; /**< size of statfile */ GList *sections; /**< list of sections in statfile */ struct statfile_autolearn_params *autolearn; /**< autolearn params */ struct statfile_binlog_params *binlog; /**< binlog params */ @@ -169,7 +169,7 @@ struct statfile { */ struct classifier_config { GList *statfiles; /**< statfiles list */ - char *metric; /**< metric of this classifier */ + gchar *metric; /**< metric of this classifier */ struct classifier *classifier; /**< classifier interface */ struct tokenizer *tokenizer; /**< tokenizer used for classifier */ GHashTable *opts; /**< other options */ @@ -196,7 +196,7 @@ struct config_scalar { */ struct worker_conf { int type; /**< worker type */ - char *bind_host; /**< bind line */ + gchar *bind_host; /**< bind line */ struct in_addr bind_addr; /**< bind address in case of TCP socket */ uint16_t bind_port; /**< bind port in case of TCP socket */ uint16_t bind_family; /**< bind type (AF_UNIX or AF_INET) */ @@ -213,14 +213,14 @@ struct worker_conf { * Structure that stores all config data */ struct config_file { - char *rspamd_user; /**< user to run as */ - char *rspamd_group; /**< group to run as */ + gchar *rspamd_user; /**< user to run as */ + gchar *rspamd_group; /**< group to run as */ memory_pool_t *cfg_pool; /**< memory pool for config */ - char *cfg_name; /**< name of config file */ - char *pid_file; /**< name of pid file */ - char *temp_dir; /**< dir for temp files */ + gchar *cfg_name; /**< name of config file */ + gchar *pid_file; /**< name of pid file */ + gchar *temp_dir; /**< dir for temp files */ #ifdef WITH_GPERF_TOOLS - char *profile_path; + gchar *profile_path; #endif gboolean no_fork; /**< if 1 do not call daemon() */ @@ -230,16 +230,16 @@ struct config_file { enum rspamd_log_type log_type; /**< log type */ int log_facility; /**< log facility in case of syslog */ int log_level; /**< log level trigger */ - char *log_file; /**< path to logfile in case of file logging */ + gchar *log_file; /**< path to logfile in case of file logging */ gboolean log_buffered; /**< whether logging is buffered */ uint32_t log_buf_size; /**< length of log buffer */ - char *debug_ip_map; /**< turn on debugging for specified ip addresses */ + gchar *debug_ip_map; /**< turn on debugging for specified ip addresses */ gboolean log_urls; /**< whether we should log URLs */ - size_t max_statfile_size; /**< maximum size for statfile */ + gsize max_statfile_size; /**< maximum size for statfile */ struct memcached_server memcached_servers[MAX_MEMCACHED_SERVERS]; /**< memcached servers */ - size_t memcached_servers_num; /**< number of memcached servers */ + gsize memcached_servers_num; /**< number of memcached servers */ memc_proto_t memcached_protocol; /**< memcached protocol */ unsigned int memcached_error_time; /**< memcached error time (see upstream documentation) */ unsigned int memcached_dead_time; /**< memcached dead time */ @@ -247,18 +247,18 @@ struct config_file { unsigned int memcached_connect_timeout; /**< connection timeout */ gboolean delivery_enable; /**< is delivery agent is enabled */ - char *deliver_host; /**< host for mail deliviring */ + gchar *deliver_host; /**< host for mail deliviring */ struct in_addr deliver_addr; /**< its address */ uint16_t deliver_port; /**< port for deliviring */ uint16_t deliver_family; /**< socket family for delivirnig */ - char *deliver_agent_path; /**< deliver to pipe instead of socket */ + gchar *deliver_agent_path; /**< deliver to pipe instead of socket */ gboolean deliver_lmtp; /**< use LMTP instead of SMTP */ GList *script_modules; /**< linked list of script modules to load */ GList *filters; /**< linked list of all filters */ GList *workers; /**< linked list of all workers params */ - char *filters_str; /**< string of filters */ + gchar *filters_str; /**< string of filters */ GHashTable* modules_opts; /**< hash for module options indexed by module name */ GHashTable* variables; /**< hash of $variables defined in config, indexed by variable name */ GHashTable* metrics; /**< hash of metrics indexed by metric name */ @@ -284,7 +284,7 @@ struct config_file { * @param str line that describes server's credits * @return 1 if line was successfully parsed and 0 in case of error */ -int add_memcached_server (struct config_file *cf, char *str); +int add_memcached_server (struct config_file *cf, gchar *str); /** * Parse host:port line @@ -292,7 +292,7 @@ int add_memcached_server (struct config_file *cf, char *str); * @param port port * @return TRUE if string was parsed */ -gboolean parse_host_port (const char *str, struct in_addr *ina, uint16_t *port); +gboolean parse_host_port (const gchar *str, struct in_addr *ina, uint16_t *port); /** * Parse bind credits @@ -301,7 +301,7 @@ gboolean parse_host_port (const char *str, struct in_addr *ina, uint16_t *port); * @param type type of credits * @return 1 if line was successfully parsed and 0 in case of error */ -int parse_bind_line (struct config_file *cfg, struct worker_conf *cf, char *str); +int parse_bind_line (struct config_file *cfg, struct worker_conf *cf, gchar *str); /** * Init default values @@ -322,28 +322,28 @@ void free_config (struct config_file *cfg); * @param opt_name name of option to get * @return module value or NULL if option does not defined */ -char* get_module_opt (struct config_file *cfg, char *module_name, char *opt_name); +gchar* get_module_opt (struct config_file *cfg, gchar *module_name, gchar *opt_name); /** * Parse limit * @param limit string representation of limit (eg. 1M) * @return numeric value of limit */ -size_t parse_limit (const char *limit); +gsize parse_limit (const gchar *limit); /** * Parse seconds * @param t string representation of seconds (eg. 1D) * @return numeric value of string */ -unsigned int parse_seconds (const char *t); +unsigned int parse_seconds (const gchar *t); /** * Parse flag * @param str string representation of flag (eg. 'on') * @return numeric value of flag (0 or 1) */ -char parse_flag (const char *str); +gchar parse_flag (const gchar *str); /** * Substitutes variable in specified string, may be recursive (eg. ${var1${var2}}) @@ -353,7 +353,7 @@ char parse_flag (const char *str); * @param recursive whether do recursive scanning * @return new string with substituted variables (uses cfg memory pool for allocating) */ -char* substitute_variable (struct config_file *cfg, char *name, char *str, u_char recursive); +gchar* substitute_variable (struct config_file *cfg, gchar *name, gchar *str, guchar recursive); /** * Do post load actions for config @@ -366,19 +366,19 @@ void post_load_config (struct config_file *cfg); * Replace all \" with a single " in given string * @param line input string */ -void unescape_quotes (char *line); +void unescape_quotes (gchar *line); -GList* parse_comma_list (memory_pool_t *pool, char *line); +GList* parse_comma_list (memory_pool_t *pool, gchar *line); struct classifier_config* check_classifier_cfg (struct config_file *cfg, struct classifier_config *c); struct worker_conf* check_worker_conf (struct config_file *cfg, struct worker_conf *c); -gboolean parse_normalizer (struct config_file *cfg, struct statfile *st, const char *line); -gboolean read_xml_config (struct config_file *cfg, const char *filename); +gboolean parse_normalizer (struct config_file *cfg, struct statfile *st, const gchar *line); +gboolean read_xml_config (struct config_file *cfg, const gchar *filename); int yylex (void); int yyparse (void); void yyrestart (FILE *); -void parse_err (const char *fmt, ...); -void parse_warn (const char *fmt, ...); +void parse_err (const gchar *fmt, ...); +void parse_warn (const gchar *fmt, ...); #endif /* ifdef CFG_FILE_H */ /* diff --git a/src/cfg_utils.c b/src/cfg_utils.c index 534d44eae..d2f75e11c 100644 --- a/src/cfg_utils.c +++ b/src/cfg_utils.c @@ -41,10 +41,10 @@ #define DEFAULT_RLIMIT_MAXCORE 0 extern int yylineno; -extern char *yytext; +extern gchar *yytext; int -add_memcached_server (struct config_file *cf, char *str) +add_memcached_server (struct config_file *cf, gchar *str) { struct memcached_server *mc; uint16_t port; @@ -71,9 +71,9 @@ add_memcached_server (struct config_file *cf, char *str) } gboolean -parse_host_port (const char *str, struct in_addr *ina, uint16_t *port) +parse_host_port (const gchar *str, struct in_addr *ina, uint16_t *port) { - char **tokens, *err_str; + gchar **tokens, *err_str; struct hostent *hent; unsigned int port_parsed, saved_errno = errno; @@ -128,9 +128,9 @@ err: } int -parse_bind_line (struct config_file *cfg, struct worker_conf *cf, char *str) +parse_bind_line (struct config_file *cfg, struct worker_conf *cf, gchar *str) { - char **host; + gchar **host; int16_t *family, *port; struct in_addr *addr; @@ -148,7 +148,7 @@ parse_bind_line (struct config_file *cfg, struct worker_conf *cf, char *str) /* Try to check path of bind credit */ struct stat st; int fd; - char *copy = memory_pool_strdup (cfg->cfg_pool, str); + gchar *copy = memory_pool_strdup (cfg->cfg_pool, str); if (stat (copy, &st) == -1) { if (errno == ENOENT) { if ((fd = open (str, O_RDWR | O_TRUNC | O_CREAT, S_IWUSR | S_IRUSR)) == -1) { @@ -235,8 +235,8 @@ free_config (struct config_file *cfg) memory_pool_delete (cfg->cfg_pool); } -char * -get_module_opt (struct config_file *cfg, char *module_name, char *opt_name) +gchar * +get_module_opt (struct config_file *cfg, gchar *module_name, gchar *opt_name) { GList *cur_opt; struct module_opt *cur; @@ -257,11 +257,11 @@ get_module_opt (struct config_file *cfg, char *module_name, char *opt_name) return NULL; } -size_t -parse_limit (const char *limit) +gsize +parse_limit (const gchar *limit) { - size_t result = 0; - char *err_str; + gsize result = 0; + gchar *err_str; if (!limit || *limit == '\0') return 0; @@ -287,10 +287,10 @@ parse_limit (const char *limit) } unsigned int -parse_seconds (const char *t) +parse_seconds (const gchar *t) { unsigned int result = 0; - char *err_str; + gchar *err_str; if (!t || *t == '\0') return 0; @@ -322,8 +322,8 @@ parse_seconds (const char *t) return result; } -char -parse_flag (const char *str) +gchar +parse_flag (const gchar *str) { if (!str || !*str) return -1; @@ -351,11 +351,11 @@ parse_flag (const char *str) * Try to substitute all variables in given string * Return: newly allocated string with substituted variables (original string may be freed if variables are found) */ -char * -substitute_variable (struct config_file *cfg, char *name, char *str, u_char recursive) +gchar * +substitute_variable (struct config_file *cfg, gchar *name, gchar *str, guchar recursive) { - char *var, *new, *v_begin, *v_end, *p, t; - size_t len; + gchar *var, *new, *v_begin, *v_end, *p, t; + gsize len; gboolean changed = FALSE; if (str == NULL) { @@ -423,11 +423,11 @@ substitute_all_variables (gpointer key, gpointer value, gpointer data) struct config_file *cfg = (struct config_file *)data; /* Do recursive substitution */ - (void)substitute_variable (cfg, (char *)key, (char *)value, 1); + (void)substitute_variable (cfg, (gchar *)key, (gchar *)value, 1); } static void -parse_filters_str (struct config_file *cfg, const char *str) +parse_filters_str (struct config_file *cfg, const gchar *str) { gchar **strvec, **p; struct filter *cur; @@ -544,10 +544,10 @@ post_load_config (struct config_file *cfg) void -parse_err (const char *fmt, ...) +parse_err (const gchar *fmt, ...) { va_list aq; - char logbuf[BUFSIZ], readbuf[32]; + gchar logbuf[BUFSIZ], readbuf[32]; int r; va_start (aq, fmt); @@ -561,10 +561,10 @@ parse_err (const char *fmt, ...) } void -parse_warn (const char *fmt, ...) +parse_warn (const gchar *fmt, ...) { va_list aq; - char logbuf[BUFSIZ], readbuf[32]; + gchar logbuf[BUFSIZ], readbuf[32]; int r; va_start (aq, fmt); @@ -578,9 +578,9 @@ parse_warn (const char *fmt, ...) } void -unescape_quotes (char *line) +unescape_quotes (gchar *line) { - char *c = line, *t; + gchar *c = line, *t; while (*c) { if (*c == '\\' && *(c + 1) == '"') { @@ -595,10 +595,10 @@ unescape_quotes (char *line) } GList * -parse_comma_list (memory_pool_t * pool, char *line) +parse_comma_list (memory_pool_t * pool, gchar *line) { GList *res = NULL; - char *c, *p, *str; + gchar *c, *p, *str; c = line; p = c; @@ -682,10 +682,10 @@ internal_normalizer_func (double score, void *data) } static gboolean -parse_internal_normalizer (struct config_file *cfg, struct statfile *st, const char *line) +parse_internal_normalizer (struct config_file *cfg, struct statfile *st, const gchar *line) { double *max; - char *err; + gchar *err; /* Line contains maximum value for internal normalizer */ max = memory_pool_alloc (cfg->cfg_pool, sizeof (double)); @@ -705,9 +705,9 @@ parse_internal_normalizer (struct config_file *cfg, struct statfile *st, const c #ifdef WITH_LUA static gboolean -parse_lua_normalizer (struct config_file *cfg, struct statfile *st, const char *line) +parse_lua_normalizer (struct config_file *cfg, struct statfile *st, const gchar *line) { - char *code_begin; + gchar *code_begin; GList *params = NULL; int len; @@ -736,9 +736,9 @@ parse_lua_normalizer (struct config_file *cfg, struct statfile *st, const char * gboolean -parse_normalizer (struct config_file *cfg, struct statfile *st, const char *line) +parse_normalizer (struct config_file *cfg, struct statfile *st, const gchar *line) { - char *params_begin; + gchar *params_begin; params_begin = strchr (line, ':'); if (params_begin == NULL) { @@ -769,7 +769,7 @@ static GMarkupParser xml_parser = { }; gboolean -read_xml_config (struct config_file *cfg, const char *filename) +read_xml_config (struct config_file *cfg, const gchar *filename) { struct stat st; int fd; diff --git a/src/cfg_xml.c b/src/cfg_xml.c index 2308fdba4..509f29a46 100644 --- a/src/cfg_xml.c +++ b/src/cfg_xml.c @@ -29,6 +29,151 @@ #include "config.h" #include "cfg_xml.h" #include "logger.h" +#include "util.h" + +/* Maximum attributes for param */ +#define MAX_PARAM 64 + +#define NULL_ATTR \ +{ \ + NULL, \ + NULL, \ + 0, \ + NULL \ +} \ + +/* Basic xml parsing functions */ +gboolean xml_handle_string (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, const gchar *data, gpointer user_data, gpointer dest_struct, int offset); + +/* Numeric params */ +gboolean xml_handle_size (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, const gchar *data, gpointer user_data, gpointer dest_struct, int offset); +gboolean xml_handle_double (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, const gchar *data, gpointer user_data, gpointer dest_struct, int offset); +gboolean xml_handle_seconds (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, const gchar *data, gpointer user_data, gpointer dest_struct, int offset); +gboolean xml_handle_int (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, const gchar *data, gpointer user_data, gpointer dest_struct, int offset); +gboolean xml_handle_uint32 (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, const gchar *data, gpointer user_data, gpointer dest_struct, int offset); +gboolean xml_handle_uint16 (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, const gchar *data, gpointer user_data, gpointer dest_struct, int offset); + +/* Flags */ +gboolean xml_handle_boolean (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, const gchar *data, gpointer user_data, gpointer dest_struct, int offset); + +/* Specific params */ +gboolean worker_handle_param (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, const gchar *data, gpointer user_data, gpointer dest_struct, int offset); +gboolean handle_factor (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, const gchar *data, gpointer user_data, gpointer dest_struct, int offset); + + +enum xml_config_section { + XML_SECTION_MAIN, + XML_SECTION_LOGGING, + XML_SECTION_WORKER, + XML_SECTION_METRIC, + XML_SECTION_CLASSIFIER, + XML_SECTION_FACTORS, + XML_SECTION_MODULE, + XML_SECTION_MODULES, + XML_SECTION_VIEW, + XML_SECTION_SETTINGS +}; + +struct xml_config_param { + const char *name; + element_handler_func handler; + int offset; + gpointer user_data; +}; + +struct xml_parser_rule { + enum xml_config_section section; + struct xml_config_param params[MAX_PARAM]; + struct xml_config_param default_param; +}; + +/* Here we describes our basic grammar */ +static struct xml_parser_rule grammar[] = { + { XML_SECTION_MAIN, { + { + "pidfile", + xml_handle_string, + G_STRUCT_OFFSET (struct config_file, pid_file), + NULL + }, + { + "statfile_pool_size", + xml_handle_size, + G_STRUCT_OFFSET (struct config_file, max_statfile_size), + NULL + }, + { + "filters", + xml_handle_string, + G_STRUCT_OFFSET (struct config_file, filters_str), + NULL + }, + NULL_ATTR + }, + NULL_ATTR + }, + { XML_SECTION_LOGGING, { + NULL_ATTR + }, + NULL_ATTR + }, + { XML_SECTION_WORKER, { + NULL_ATTR + }, + { + NULL, + worker_handle_param, + 0, + NULL + }, + }, + { XML_SECTION_METRIC, { + NULL_ATTR + }, + NULL_ATTR + }, + { XML_SECTION_CLASSIFIER, { + NULL_ATTR + }, + NULL_ATTR + }, + { XML_SECTION_FACTORS, { + { + "grow_factor", + xml_handle_double, + G_STRUCT_OFFSET (struct config_file, grow_factor), + NULL + }, + NULL_ATTR + }, + { + NULL, + handle_factor, + 0, + NULL + } + }, + { XML_SECTION_MODULE, { + NULL_ATTR + }, + NULL_ATTR + }, + { XML_SECTION_MODULES, { + NULL_ATTR + }, + NULL_ATTR + }, + { XML_SECTION_VIEW, { + NULL_ATTR + }, + NULL_ATTR + }, + { XML_SECTION_SETTINGS, { + NULL_ATTR + }, + NULL_ATTR + } +}; GQuark xml_error_quark (void) @@ -68,6 +213,77 @@ xml_asciiz_string (memory_pool_t *pool, const gchar *text, gsize len) return val; } +/* Find among attributes required ones and form new array of pairs attribute-value */ +static GHashTable * +process_attrs (struct config_file *cfg, const gchar **attribute_names, const gchar **attribute_values) +{ + const gchar **attr, **value; + GHashTable *res; + + if (*attribute_names == NULL) { + /* No attributes required */ + return NULL; + } + + res = g_hash_table_new (rspamd_strcase_hash, rspamd_strcase_equal); + + attr = attribute_names; + value = attribute_values; + while (*attr) { + /* Copy attributes to pool */ + g_hash_table_insert (res, memory_pool_strdup (cfg->cfg_pool, *attr), memory_pool_strdup (cfg->cfg_pool, *value)); + attr ++; + value ++; + } + + memory_pool_add_destructor (cfg->cfg_pool, (pool_destruct_func)g_hash_table_destroy, res); + + return res; +} + +static gboolean +call_param_handler (struct rspamd_xml_userdata *ctx, const gchar *name, const gchar *value, gpointer dest_struct, enum xml_config_section section) +{ + struct xml_parser_rule *rule; + struct xml_config_param *param; + int i; + + /* First find required section */ + for (i = 0; i < G_N_ELEMENTS (grammar); i ++) { + rule = &grammar[i]; + if (rule->section == section) { + /* Now find attribute in section or call default handler */ + param = &rule->params[0]; + while (param && param->handler) { + if (param->name && g_ascii_strcasecmp (param->name, name) == 0) { + /* Call specified handler */ + return param->handler (ctx->cfg, ctx, ctx->cur_attrs, value, param->user_data, dest_struct, param->offset); + } + param ++; + } + if (rule->default_param.handler != NULL) { + param = &rule->default_param; + /* Call default handler */ + return param->handler (ctx->cfg, ctx, ctx->cur_attrs, value, param->user_data, dest_struct, param->offset); + } + } + } + + return FALSE; +} + +gboolean +worker_handle_param (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, const gchar *data, gpointer user_data, gpointer dest_struct, int offset) +{ + +} + +gboolean +handle_factor (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, const gchar *data, gpointer user_data, gpointer dest_struct, int offset) +{ + +} + static void xml_parse_module_opt (struct rspamd_xml_userdata *ud, const gchar *text, gsize len) { @@ -105,6 +321,129 @@ xml_parse_module_opt (struct rspamd_xml_userdata *ud, const gchar *text, gsize l } +/* Common handlers */ +gboolean +xml_handle_string (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, const gchar *data, gpointer user_data, gpointer dest_struct, int offset) +{ + /* Simply assign pointer to pointer */ + gchar **dest; + + dest = (char **)G_STRUCT_MEMBER_P (dest_struct, offset); + *dest = memory_pool_strdup (cfg->cfg_pool, data); + + return TRUE; +} + + +gboolean +xml_handle_size (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, const gchar *data, gpointer user_data, gpointer dest_struct, int offset) +{ + gsize *dest; + + dest = (gsize *)G_STRUCT_MEMBER_P (dest_struct, offset); + *dest = parse_limit (data); + + return TRUE; +} + +gboolean +xml_handle_seconds (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, const gchar *data, gpointer user_data, gpointer dest_struct, int offset) +{ + time_t *dest; + + dest = (time_t *)G_STRUCT_MEMBER_P (dest_struct, offset); + *dest = parse_seconds (data); + + return TRUE; +} + +gboolean +xml_handle_boolean (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, const gchar *data, gpointer user_data, gpointer dest_struct, int offset) +{ + gboolean *dest; + + dest = (gboolean *)G_STRUCT_MEMBER_P (dest_struct, offset); + *dest = parse_flag (data); + /* gchar -> gboolean */ + if (*dest == -1) { + msg_err ("bad boolean: %s", data); + return FALSE; + } + else if (*dest == 1) { + *dest = TRUE; + } + + return TRUE; +} + +gboolean +xml_handle_double (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, const gchar *data, gpointer user_data, gpointer dest_struct, int offset) +{ + double *dest; + char *err = NULL; + + dest = (double *)G_STRUCT_MEMBER_P (dest_struct, offset); + errno = 0; + *dest = strtod (data, &err); + if (errno != 0 || (err != NULL && *err != 0)) { + msg_err ("invalid number: %s, %s", data, strerror (errno)); + return FALSE; + } + + return TRUE; +} + +gboolean +xml_handle_int (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, const gchar *data, gpointer user_data, gpointer dest_struct, int offset) +{ + int *dest; + char *err = NULL; + + dest = (int *)G_STRUCT_MEMBER_P (dest_struct, offset); + errno = 0; + *dest = strtol (data, &err, 10); + if (errno != 0 || (err != NULL && *err != 0)) { + msg_err ("invalid number: %s, %s", data, strerror (errno)); + return FALSE; + } + + return TRUE; +} + +gboolean +xml_handle_uint32 (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, const gchar *data, gpointer user_data, gpointer dest_struct, int offset) +{ + uint32_t *dest; + char *err = NULL; + + dest = (uint32_t *)G_STRUCT_MEMBER_P (dest_struct, offset); + errno = 0; + *dest = strtoul (data, &err, 10); + if (errno != 0 || (err != NULL && *err != 0)) { + msg_err ("invalid number: %s, %s", data, strerror (errno)); + return FALSE; + } + + return TRUE; +} + +gboolean +xml_handle_uint16 (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, const gchar *data, gpointer user_data, gpointer dest_struct, int offset) +{ + uint16_t *dest; + char *err = NULL; + + dest = (uint16_t *)G_STRUCT_MEMBER_P (dest_struct, offset); + errno = 0; + *dest = strtoul (data, &err, 10); + if (errno != 0 || (err != NULL && *err != 0)) { + msg_err ("invalid number: %s, %s", data, strerror (errno)); + return FALSE; + } + + return TRUE; +} + void rspamd_xml_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer user_data, GError **error) @@ -128,7 +467,7 @@ rspamd_xml_start_element (GMarkupParseContext *context, const gchar *element_nam if (g_ascii_strcasecmp (element_name, "module") == 0) { /* Read module data */ if (extract_attr ("name", attribute_names, attribute_values, &res)) { - ud->section_name = g_strdup (res); + g_strlcpy (ud->section_name, res, sizeof (res)); ud->state = XML_READ_MODULE; } else { @@ -144,7 +483,7 @@ rspamd_xml_start_element (GMarkupParseContext *context, const gchar *element_nam } else if (g_ascii_strcasecmp (element_name, "metric") == 0) { if (extract_attr ("name", attribute_names, attribute_values, &res)) { - ud->section_name = g_strdup (res); + g_strlcpy (ud->section_name, res, sizeof (res)); ud->state = XML_READ_METRIC; } else { @@ -154,7 +493,7 @@ rspamd_xml_start_element (GMarkupParseContext *context, const gchar *element_nam } else if (g_ascii_strcasecmp (element_name, "classifier") == 0) { if (extract_attr ("type", attribute_names, attribute_values, &res)) { - ud->section_name = g_strdup (res); + g_strlcpy (ud->section_name, res, sizeof (res)); ud->state = XML_READ_CLASSIFIER; } else { @@ -163,20 +502,13 @@ rspamd_xml_start_element (GMarkupParseContext *context, const gchar *element_nam } } else if (g_ascii_strcasecmp (element_name, "worker") == 0) { - if (extract_attr ("type", attribute_names, attribute_values, &res)) { - ud->section_name = g_strdup (res); - ud->state = XML_READ_WORKER; - } - else { - *error = g_error_new (xml_error_quark (), XML_PARAM_MISSING, "param 'type' is required for tag 'worker'"); - ud->state = XML_ERROR; - } + ud->state = XML_READ_WORKER; } else { /* Other params */ if (g_ascii_strcasecmp (element_name, "variable") == 0) { if (extract_attr ("name", attribute_names, attribute_values, &res)) { - ud->section_name = g_strdup (res); + g_strlcpy (ud->section_name, res, sizeof (res)); ud->state = XML_READ_VARIABLE; } else { @@ -194,34 +526,13 @@ rspamd_xml_start_element (GMarkupParseContext *context, const gchar *element_nam } break; case XML_READ_MODULE: - if (g_ascii_strcasecmp (element_name, "param") == 0) { - if (extract_attr ("name", attribute_names, attribute_values, &res)) { - ud->other_data = g_strdup (res); - } - else { - *error = g_error_new (xml_error_quark (), XML_PARAM_MISSING, "param 'name' is required for tag 'param'"); - ud->state = XML_ERROR; - } - } - break; + case XML_READ_FACTORS: case XML_READ_CLASSIFIER: - break; case XML_READ_STATFILE: - break; - case XML_READ_FACTORS: - if (g_ascii_strcasecmp (element_name, "factor") == 0) { - if (extract_attr ("name", attribute_names, attribute_values, &res)) { - ud->other_data = g_strdup (res); - } - else { - *error = g_error_new (xml_error_quark (), XML_PARAM_MISSING, "param 'name' is required for tag 'factor'"); - ud->state = XML_ERROR; - } - } - break; case XML_READ_WORKER: - break; case XML_READ_LOGGING: + /* Save attributes */ + ud->cur_attrs = process_attrs (ud->cfg, attribute_names, attribute_values); break; default: *error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "element %s is unexpected in this state", element_name); @@ -234,9 +545,6 @@ do { \ if (g_ascii_strcasecmp (element_name, (x)) == 0) { \ ud->state = XML_READ_PARAM; \ res = TRUE; \ - if (!required) { \ - g_free (ud->section_name); \ - } \ } \ else { \ res = FALSE; \ @@ -310,55 +618,57 @@ rspamd_xml_text (GMarkupParseContext *context, const gchar *text, gsize text_len { struct rspamd_xml_userdata *ud = user_data; char *val; - double *tmp; + struct config_file *cfg = ud->cfg; + + val = xml_asciiz_string (cfg->cfg_pool, text, text_len); switch (ud->state) { case XML_READ_MODULE: - if (ud->other_data) { - /* Insert or replace module's option */ - xml_parse_module_opt (ud, text, text_len); - g_free (ud->other_data); + if (!call_param_handler (ud, ud->section_name, val, ud->other_data, XML_SECTION_MODULE)) { + *error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "cannot parse tag's '%s' data: %s", ud->section_name, val); + ud->state = XML_ERROR; } break; case XML_READ_CLASSIFIER: + if (!call_param_handler (ud, ud->section_name, val, ud->other_data, XML_SECTION_CLASSIFIER)) { + *error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "cannot parse tag's '%s' data: %s", ud->section_name, val); + ud->state = XML_ERROR; + } break; case XML_READ_STATFILE: break; case XML_READ_FACTORS: - if (ud->other_data) { - /* Assume that we have factor name in other_data */ - val = xml_asciiz_string (ud->cfg->cfg_pool, text, text_len); - tmp = memory_pool_alloc (ud->cfg->cfg_pool, sizeof (double)); - *tmp = strtod (val, NULL); - g_hash_table_insert (ud->cfg->factors, ud->other_data, tmp); - g_free (ud->other_data); + if (!call_param_handler (ud, ud->section_name, val, cfg, XML_SECTION_CLASSIFIER)) { + *error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "cannot parse tag's '%s' data: %s", ud->section_name, val); + ud->state = XML_ERROR; } break; case XML_READ_METRIC: + if (!call_param_handler (ud, ud->section_name, val, ud->other_data, XML_SECTION_METRIC)) { + *error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "cannot parse tag's '%s' data: %s", ud->section_name, val); + ud->state = XML_ERROR; + } break; case XML_READ_WORKER: - break; - case XML_READ_VARIABLE: - if (ud->other_data) { - /* Assume that we have factor name in other_data */ - val = xml_asciiz_string (ud->cfg->cfg_pool, text, text_len); - g_hash_table_insert (ud->cfg->variables, ud->other_data, val); - g_free (ud->other_data); + if (!call_param_handler (ud, ud->section_name, val, ud->other_data, XML_SECTION_WORKER)) { + *error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "cannot parse tag's '%s' data: %s", ud->section_name, val); + ud->state = XML_ERROR; } break; + case XML_READ_VARIABLE: case XML_READ_PIDFILE: - val = xml_asciiz_string (ud->cfg->cfg_pool, text, text_len); - ud->cfg->pid_file = val; - break; case XML_READ_STATFILE_POOL: - val = xml_asciiz_string (ud->cfg->cfg_pool, text, text_len); - ud->cfg->max_statfile_size = strtoull (val, NULL, 10); - break; case XML_READ_FILTERS: - val = xml_asciiz_string (ud->cfg->cfg_pool, text, text_len); - ud->cfg->filters_str = val; + if (!call_param_handler (ud, ud->section_name, val, cfg, XML_SECTION_MAIN)) { + *error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "cannot parse tag's '%s' data: %s", ud->section_name, val); + ud->state = XML_ERROR; + } break; case XML_READ_LOGGING: + if (!call_param_handler (ud, ud->section_name, val, cfg, XML_SECTION_LOGGING)) { + *error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "cannot parse tag's '%s' data: %s", ud->section_name, val); + ud->state = XML_ERROR; + } break; case XML_READ_PARAM: break; diff --git a/src/cfg_xml.h b/src/cfg_xml.h index 3e1a4bcd4..5bac52f86 100644 --- a/src/cfg_xml.h +++ b/src/cfg_xml.h @@ -4,6 +4,8 @@ #include "config.h" #include "cfg_file.h" +#define MAX_NAME 8192 + #define XML_START_MISSING 1 #define XML_PARAM_MISSING 2 #define XML_EXTRA_ELEMENT 3 @@ -30,10 +32,14 @@ enum xml_read_state { struct rspamd_xml_userdata { enum xml_read_state state; struct config_file *cfg; - gchar *section_name; + gchar section_name[MAX_NAME]; gpointer other_data; + GHashTable *cur_attrs; }; +/* Text is NULL terminated here */ +typedef gboolean (*element_handler_func) (struct config_file *cfg, struct rspamd_xml_userdata *ctx, GHashTable *attrs, const gchar *data, gpointer user_data, gpointer dest_struct, int offset); + /* Called for open tags */ void rspamd_xml_start_element (GMarkupParseContext *context, const gchar *element_name, -- 2.39.5