diff options
author | Vsevolod Stakhov <vsevolod@rambler-co.ru> | 2010-03-27 05:12:35 +0300 |
---|---|---|
committer | Vsevolod Stakhov <vsevolod@rambler-co.ru> | 2010-03-27 05:12:35 +0300 |
commit | 7dbdebbca0dace0f7c1f1a82f38a99ca489b0180 (patch) | |
tree | fb5c948313e0466bda328db7b99324d017c7e0a7 /src/cfg_xml.c | |
parent | 2f2642851746b0985f67e8dde58e2458eae07cca (diff) | |
download | rspamd-7dbdebbca0dace0f7c1f1a82f38a99ca489b0180.tar.gz rspamd-7dbdebbca0dace0f7c1f1a82f38a99ca489b0180.zip |
* 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
Diffstat (limited to 'src/cfg_xml.c')
-rw-r--r-- | src/cfg_xml.c | 442 |
1 files changed, 376 insertions, 66 deletions
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; |