From: Vsevolod Stakhov Date: Wed, 15 Dec 2010 19:10:42 +0000 (+0300) Subject: * Implement ability to add conditional rules to rspamd.xml X-Git-Tag: 0.3.7~101 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=0bb8eff9b54d2bba798012137bd3785c3277c401;p=rspamd.git * Implement ability to add conditional rules to rspamd.xml --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 7945ae059..4125b0249 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -354,6 +354,7 @@ CHECK_INCLUDE_FILES(sys/wait.h HAVE_SYS_WAIT_H) CHECK_INCLUDE_FILES(sys/param.h HAVE_SYS_PARAM_H) CHECK_INCLUDE_FILES(sys/cdefs.h HAVE_SYS_CDEFS_H) CHECK_INCLUDE_FILES(sys/file.h HAVE_SYS_FILE_H) +CHECK_INCLUDE_FILES(sys/utsname.h HAVE_SYS_UTSNAME_H) CHECK_INCLUDE_FILES(sys/resource.h HAVE_SYS_RESOURCE_H) CHECK_INCLUDE_FILES(netinet/in.h HAVE_NETINET_IN_H) CHECK_INCLUDE_FILES(arpa/inet.h HAVE_ARPA_INET_H) diff --git a/config.h.in b/config.h.in index ae9afee83..bb8e80a28 100644 --- a/config.h.in +++ b/config.h.in @@ -67,7 +67,8 @@ #cmakedefine HAVE_SYS_PARAM_H 1 #cmakedefine HAVE_SYS_FILE_H 1 #cmakedefine HAVE_SYS_RESOURCE_H 1 -#cmakedefine HAVE_SYS_TIMEB_H 1 +#cmakedefine HAVE_SYS_TIMEB_H 1 +#cmakedefine HAVE_SYS_UTSNAME_H 1 #cmakedefine HAVE_PIDFILE 1 diff --git a/src/cfg_utils.c b/src/cfg_utils.c index b98746fe9..334283d22 100644 --- a/src/cfg_utils.c +++ b/src/cfg_utils.c @@ -930,10 +930,16 @@ read_xml_config (struct config_file *cfg, const gchar *filename) /* Prepare xml parser */ ud.cfg = cfg; ud.state = XML_READ_START; + ud.if_stack = g_queue_new (); ctx = g_markup_parse_context_new (&xml_parser, G_MARKUP_TREAT_CDATA_AS_TEXT | G_MARKUP_PREFIX_ERROR_POSITION, &ud, NULL); res = g_markup_parse_context_parse (ctx, data, st.st_size, &err); + if (g_queue_get_length (ud.if_stack) != 0) { + msg_err ("unexpected nesting for if arguments"); + res = FALSE; + } + munmap (data, st.st_size); return res; diff --git a/src/cfg_xml.c b/src/cfg_xml.c index 37e8dea14..7fd096334 100644 --- a/src/cfg_xml.c +++ b/src/cfg_xml.c @@ -449,6 +449,42 @@ xml_error_quark (void) return g_quark_from_static_string ("xml-error-quark"); } +static inline const gchar * +xml_state_to_string (struct rspamd_xml_userdata *ud) +{ + switch (ud->state) { + case XML_READ_START: + return "read start tag"; + case XML_READ_PARAM: + return "read param"; + case XML_READ_MODULE: + return "read module section"; + case XML_READ_MODULES: + return "read modules section"; + case XML_READ_CLASSIFIER: + return "read classifier section"; + case XML_READ_STATFILE: + return "read statfile section"; + case XML_READ_METRIC: + return "read metric section"; + case XML_READ_WORKER: + return "read worker section"; + case XML_READ_VIEW: + return "read view section"; + case XML_READ_LOGGING: + return "read logging section"; + case XML_READ_VALUE: + return "read value"; + case XML_SKIP_ELEMENTS: + return "skip if block"; + case XML_ERROR: + return "error occured"; + case XML_END: + return "read final tag"; + } + /* Unreached */ + return "unknown state"; +} static inline gboolean extract_attr (const gchar *attr, const gchar **attribute_names, const gchar **attribute_values, gchar **res) @@ -1333,8 +1369,23 @@ rspamd_xml_start_element (GMarkupParseContext *context, const gchar *element_nam { struct rspamd_xml_userdata *ud = user_data; struct classifier_config *ccf; - gchar *res; - + gchar *res, *condition; + + if (g_ascii_strcasecmp (element_name, "if") == 0) { + /* Push current state to queue */ + g_queue_push_head (ud->if_stack, GSIZE_TO_POINTER ((gsize)ud->state)); + /* Now get attributes */ + ud->cur_attrs = process_attrs (ud->cfg, attribute_names, attribute_values); + if ((condition = g_hash_table_lookup (ud->cur_attrs, "condition")) == NULL) { + msg_err ("unknown condition attribute for if tag"); + *error = g_error_new (xml_error_quark (), XML_PARAM_MISSING, "param 'condition' is required for tag 'if'"); + ud->state = XML_ERROR; + } + if (! lua_check_condition (ud->cfg, condition)) { + ud->state = XML_SKIP_ELEMENTS; + } + return; + } switch (ud->state) { case XML_READ_START: if (g_ascii_strcasecmp (element_name, "rspamd") != 0) { @@ -1432,6 +1483,9 @@ rspamd_xml_start_element (GMarkupParseContext *context, const gchar *element_nam ud->cur_attrs = process_attrs (ud->cfg, attribute_names, attribute_values); } break; + case XML_SKIP_ELEMENTS: + /* Do nothing */ + return; case XML_READ_MODULE: case XML_READ_METRIC: case XML_READ_MODULES: @@ -1444,7 +1498,10 @@ rspamd_xml_start_element (GMarkupParseContext *context, const gchar *element_nam 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); + if (*error == NULL) { + *error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "element %s is unexpected in this state %s", + element_name, xml_state_to_string (ud)); + } break; } } @@ -1471,8 +1528,25 @@ rspamd_xml_end_element (GMarkupParseContext *context, const gchar *element_name, struct metric *m; struct classifier_config *ccf; struct statfile *st; - gboolean res; + gboolean res; + gpointer tptr; + if (g_ascii_strcasecmp (element_name, "if") == 0) { + tptr = g_queue_pop_head (ud->if_stack); + + if (tptr == NULL) { + *error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "element %s is umatched", element_name); + ud->state = XML_ERROR; + } + /* Restore state */ + if (ud->state == XML_SKIP_ELEMENTS) { + ud->state = GPOINTER_TO_SIZE (tptr); + } + /* Skip processing */ + + return; + } + switch (ud->state) { case XML_READ_MODULE: CHECK_TAG ("module", FALSE); @@ -1562,6 +1636,8 @@ rspamd_xml_end_element (GMarkupParseContext *context, const gchar *element_name, ud->state = XML_ERROR; } break; + case XML_SKIP_ELEMENTS: + return; default: ud->state = XML_ERROR; break; @@ -1643,6 +1719,9 @@ rspamd_xml_text (GMarkupParseContext *context, const gchar *text, gsize text_len ud->state = XML_ERROR; } break; + case XML_SKIP_ELEMENTS: + /* Do nothing */ + return; default: ud->state = XML_ERROR; break; @@ -1650,41 +1729,6 @@ rspamd_xml_text (GMarkupParseContext *context, const gchar *text, gsize text_len } -static inline const gchar * -xml_state_to_string (struct rspamd_xml_userdata *ud) -{ - switch (ud->state) { - case XML_READ_START: - return "read start tag"; - case XML_READ_PARAM: - return "read param"; - case XML_READ_MODULE: - return "read module section"; - case XML_READ_MODULES: - return "read modules section"; - case XML_READ_CLASSIFIER: - return "read classifier section"; - case XML_READ_STATFILE: - return "read statfile section"; - case XML_READ_METRIC: - return "read metric section"; - case XML_READ_WORKER: - return "read worker section"; - case XML_READ_VIEW: - return "read view section"; - case XML_READ_LOGGING: - return "read logging section"; - case XML_READ_VALUE: - return "read value"; - case XML_ERROR: - return "error occured"; - case XML_END: - return "read final tag"; - } - /* Unreached */ - return "unknown state"; -} - void rspamd_xml_error (GMarkupParseContext *context, GError *error, gpointer user_data) { diff --git a/src/cfg_xml.h b/src/cfg_xml.h index db3dd9e8f..2abdf650b 100644 --- a/src/cfg_xml.h +++ b/src/cfg_xml.h @@ -24,6 +24,7 @@ enum xml_read_state { XML_READ_VIEW, XML_READ_LOGGING, XML_READ_VALUE, + XML_SKIP_ELEMENTS, XML_ERROR, XML_END }; @@ -39,13 +40,17 @@ enum module_opt_type { MODULE_OPT_TYPE_ANY }; +/** + * Structure that is used for semantic resolution of configuration + */ struct rspamd_xml_userdata { - enum xml_read_state state; - struct config_file *cfg; - gchar section_name[MAX_NAME]; - gpointer section_pointer; - gpointer parent_pointer; - GHashTable *cur_attrs; + enum xml_read_state state; /*< state of parser */ + struct config_file *cfg; /*< configuration object */ + gchar section_name[MAX_NAME]; /*< current section */ + gpointer section_pointer; /*< pointer to object related with section */ + gpointer parent_pointer; /*< parent's section object */ + GHashTable *cur_attrs; /*< attributes of current tag */ + GQueue *if_stack; /*< stack of if elements */ }; /* Text is NULL terminated here */ diff --git a/src/lua/lua_cfg_file.c b/src/lua/lua_cfg_file.c index b67466d7b..a7c48aedf 100644 --- a/src/lua/lua_cfg_file.c +++ b/src/lua/lua_cfg_file.c @@ -23,6 +23,9 @@ */ #include "lua_common.h" +#ifdef HAVE_SYS_UTSNAME_H +#include +#endif /* * This is implementation of lua routines to handle config file params @@ -295,3 +298,66 @@ lua_handle_param (struct worker_task *task, gchar *mname, gchar *optname, enum l *res = NULL; return FALSE; } + +#define FAKE_RES_VAR "rspamd_res" +gboolean +lua_check_condition (struct config_file *cfg, const gchar *condition) +{ + lua_State *L = cfg->lua_state; + gchar *hostbuf, *condbuf; + gsize hostlen; + gboolean res; +#ifdef HAVE_SYS_UTSNAME_H + struct utsname uts; +#endif + + /* Set some globals for condition */ + /* XXX: think what other variables can be useful */ + hostlen = sysconf (_SC_HOST_NAME_MAX) + 1; + hostbuf = alloca (hostlen); + gethostname (hostbuf, hostlen); + hostbuf[hostlen - 1] = '\0'; + + /* Hostname */ + lua_pushstring (L, hostbuf); + lua_setglobal (L, "hostname"); + /* Config file name */ + lua_pushstring (L, cfg->cfg_name); + lua_setglobal (L, "cfg_name"); + /* Check for uname */ +#ifdef HAVE_SYS_UTSNAME_H + uname (&uts); + lua_pushstring (L, uts.sysname); + lua_setglobal (L, "osname"); + lua_pushstring (L, uts.release); + lua_setglobal (L, "osrelease"); +#else + lua_pushstring (L, "unknown"); + lua_setglobal (L, "osname"); + lua_pushstring (L, ""); + lua_setglobal (L, "osrelease"); +#endif + /* Make fake string */ + hostlen = sizeof (FAKE_RES_VAR "=") + strlen (condition); + condbuf = g_malloc (hostlen); + rspamd_strlcpy (condbuf, FAKE_RES_VAR "=", sizeof (FAKE_RES_VAR "=")); + g_strlcat (condbuf, condition, hostlen); + /* Evaluate condition */ + if (luaL_dostring (L, condbuf) != 0) { + msg_err ("eval of '%s' failed: '%s'", condition, lua_tostring (L, -1)); + g_free (condbuf); + return FALSE; + } + /* Get global variable res to get result */ + lua_getglobal (L, FAKE_RES_VAR); + if (! lua_isboolean (L, -1)) { + msg_err ("bad string evaluated: %s, type: %s", condbuf, lua_typename (L, lua_type (L, -1))); + g_free (condbuf); + return FALSE; + } + + res = lua_toboolean (L, -1); + g_free (condbuf); + + return res; +} diff --git a/src/lua/lua_common.h b/src/lua/lua_common.h index 6c51368fb..823056316 100644 --- a/src/lua/lua_common.h +++ b/src/lua/lua_common.h @@ -55,6 +55,7 @@ void lua_post_load_config (struct config_file *cfg); void lua_process_element (struct config_file *cfg, const gchar *name, struct module_opt *opt, gint idx); gboolean lua_handle_param (struct worker_task *task, gchar *mname, gchar *optname, enum lua_var_type expected_type, gpointer *res); +gboolean lua_check_condition (struct config_file *cfg, const gchar *condition); #endif /* WITH_LUA */