summaryrefslogtreecommitdiffstats
path: root/src/cfg_xml.c
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@rambler-co.ru>2010-03-27 05:12:35 +0300
committerVsevolod Stakhov <vsevolod@rambler-co.ru>2010-03-27 05:12:35 +0300
commit7dbdebbca0dace0f7c1f1a82f38a99ca489b0180 (patch)
treefb5c948313e0466bda328db7b99324d017c7e0a7 /src/cfg_xml.c
parent2f2642851746b0985f67e8dde58e2458eae07cca (diff)
downloadrspamd-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.c442
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;