]> source.dussan.org Git - rspamd.git/commitdiff
* New syntax parser that should make syntax check of XML configs
authorVsevolod Stakhov <vsevolod@rambler-co.ru>
Sat, 27 Mar 2010 02:12:35 +0000 (05:12 +0300)
committerVsevolod Stakhov <vsevolod@rambler-co.ru>
Sat, 27 Mar 2010 02:12:35 +0000 (05:12 +0300)
* 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
src/cfg_utils.c
src/cfg_xml.c
src/cfg_xml.h

index 74ce91911917a806ea49ef920b7ee7a813aa37d0..18291077dbba61d192740a30e5c1d494a8cf21f3 100644 (file)
@@ -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 */
 /* 
index 534d44eaea1c95b7cdd3a20946e65982a3e996d6..d2f75e11c21004112215289e4ae42c14da28d511 100644 (file)
 #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;
index 2308fdba4a6efa18a0d98c2316a3f733d0c87d59..509f29a46afb3baf4f88b72db0a92e9db9a0fd80 100644 (file)
 #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;
index 3e1a4bcd45dfb8ec4c4c32063d28b943cc68b284..5bac52f861d350389b67e596aaaa07c6c9c084d9 100644 (file)
@@ -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 <foo bar="baz"> */
 void rspamd_xml_start_element (GMarkupParseContext     *context,
                                                                const gchar         *element_name,