summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@rambler-co.ru>2010-12-15 22:10:42 +0300
committerVsevolod Stakhov <vsevolod@rambler-co.ru>2010-12-15 22:10:42 +0300
commit0bb8eff9b54d2bba798012137bd3785c3277c401 (patch)
tree4cb1e38d7ecc28ae1326706dab0629ad924178b2
parentea03e94d0a79c2f0f35e05bd6c9d44bf995a3e5b (diff)
downloadrspamd-0bb8eff9b54d2bba798012137bd3785c3277c401.tar.gz
rspamd-0bb8eff9b54d2bba798012137bd3785c3277c401.zip
* Implement ability to add conditional rules to rspamd.xml
-rw-r--r--CMakeLists.txt1
-rw-r--r--config.h.in3
-rw-r--r--src/cfg_utils.c6
-rw-r--r--src/cfg_xml.c122
-rw-r--r--src/cfg_xml.h17
-rw-r--r--src/lua/lua_cfg_file.c66
-rw-r--r--src/lua/lua_common.h1
7 files changed, 170 insertions, 46 deletions
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 <sys/utsname.h>
+#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 */