]> source.dussan.org Git - rspamd.git/commitdiff
* Implement ability to add conditional rules to rspamd.xml
authorVsevolod Stakhov <vsevolod@rambler-co.ru>
Wed, 15 Dec 2010 19:10:42 +0000 (22:10 +0300)
committerVsevolod Stakhov <vsevolod@rambler-co.ru>
Wed, 15 Dec 2010 19:10:42 +0000 (22:10 +0300)
CMakeLists.txt
config.h.in
src/cfg_utils.c
src/cfg_xml.c
src/cfg_xml.h
src/lua/lua_cfg_file.c
src/lua/lua_common.h

index 7945ae059a94020fb500a2f9471ca857d1ff6d35..4125b024983fcb2260989ebdc21c60f72f9a0aea 100644 (file)
@@ -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)
index ae9afee839405da61a849cd1a8f6679aad08bf72..bb8e80a285ebcfc766748e2671b8fed6288168ec 100644 (file)
@@ -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
 
index b98746fe91428d0fbe0bb970e597e926055d0f45..334283d22a16430c3f4a2b99e4bb3f56b4c9dd29 100644 (file)
@@ -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;
index 37e8dea146840bc9aeead071977a01177d663499..7fd0963341638fef629273ad607db6773cc994ee 100644 (file)
@@ -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)
 {
index db3dd9e8f2c304bbe9f2b5a693cec39019192419..2abdf650b52c5c5d08b5892569e3a8625691d2c1 100644 (file)
@@ -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 */
index b67466d7b82ea4c54b3c9a7973e406c0ac5315db..a7c48aedf27b44f11f91eb3fe0384ec05da5b5ae 100644 (file)
@@ -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;
+}
index 6c51368fbf9b00fc7969d7c30840be09488616d6..82305631692489fd6e2b3b3cb87ccaabed0d1622 100644 (file)
@@ -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 */