Browse Source

* 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
tags/0.3.0
Vsevolod Stakhov 14 years ago
parent
commit
7dbdebbca0
4 changed files with 460 additions and 144 deletions
  1. 39
    39
      src/cfg_file.h
  2. 38
    38
      src/cfg_utils.c
  3. 376
    66
      src/cfg_xml.c
  4. 7
    1
      src/cfg_xml.h

+ 39
- 39
src/cfg_file.h View 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 */
/*

+ 38
- 38
src/cfg_utils.c View File

@@ -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;

+ 376
- 66
src/cfg_xml.c View File

@@ -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;

+ 7
- 1
src/cfg_xml.h View 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,

Loading…
Cancel
Save