]> source.dussan.org Git - rspamd.git/commitdiff
Add LUA api for expressions.
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Tue, 17 Mar 2015 17:09:05 +0000 (17:09 +0000)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Tue, 17 Mar 2015 17:09:05 +0000 (17:09 +0000)
src/lua/CMakeLists.txt
src/lua/lua_common.c
src/lua/lua_common.h
src/lua/lua_expression.c [new file with mode: 0644]

index fbf2733d0d1033fd8053095e0e6311e7c77319c9..18e56508eeb2b135fd9483b061b1dbf0b0d9188f 100644 (file)
@@ -15,6 +15,7 @@ SET(LUASRC                      ${CMAKE_CURRENT_SOURCE_DIR}/lua_common.c
                                          ${CMAKE_CURRENT_SOURCE_DIR}/lua_buffer.c
                                          ${CMAKE_CURRENT_SOURCE_DIR}/lua_dns.c
                                          ${CMAKE_CURRENT_SOURCE_DIR}/lua_rsa.c
-                                         ${CMAKE_CURRENT_SOURCE_DIR}/lua_ip.c)
+                                         ${CMAKE_CURRENT_SOURCE_DIR}/lua_ip.c
+                                         ${CMAKE_CURRENT_SOURCE_DIR}/lua_expression.c)
 
 SET(RSPAMD_LUA ${LUASRC} PARENT_SCOPE)
\ No newline at end of file
index 123b81dad6af663260a0a9e212c63d315ecb8f16..63d34e4e6b32f09838fc23be246dea8bc7177601 100644 (file)
@@ -354,6 +354,7 @@ rspamd_lua_init (struct rspamd_config *cfg)
        luaopen_dns_resolver (L);
        luaopen_rsa (L);
        luaopen_ip (L);
+       luaopen_expression (L);
 
        rspamd_lua_add_preload (L, "ucl", luaopen_ucl);
 
index 1f3ea59225ed82fa431a334bbe21b834144de20d..ea0947c8a0c9fa13114ee16d89b5b4375aacdf05 100644 (file)
@@ -182,6 +182,7 @@ void luaopen_io_dispatcher (lua_State * L);
 void luaopen_dns_resolver (lua_State * L);
 void luaopen_rsa (lua_State * L);
 void luaopen_ip (lua_State * L);
+void luaopen_expression (lua_State * L);
 
 gint rspamd_lua_call_filter (const gchar *function, struct rspamd_task *task);
 gint rspamd_lua_call_chain_filter (const gchar *function,
diff --git a/src/lua/lua_expression.c b/src/lua/lua_expression.c
new file mode 100644 (file)
index 0000000..7c166cc
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 2015, Vsevolod Stakhov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *      * Redistributions of source code must retain the above copyright
+ *        notice, this list of conditions and the following disclaimer.
+ *      * Redistributions in binary form must reproduce the above copyright
+ *        notice, this list of conditions and the following disclaimer in the
+ *        documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "lua_common.h"
+#include "expression.h"
+
+/***
+ * @function rspamd_expression.create(line, {parse_func, process_func}, pool)
+ * Create expression from the line using atom parsing routines and the specified memory pool
+ * @param {string} line expression line
+ * @param {table} atom_functions parse_atom function and process_atom function
+ * @param {rspamd_mempool} memory pool to use for this function
+ * @return {expr, err} expression object and error message of `expr` is nil
+ * @example
+require "fun" ()
+local rspamd_expression = require "rspamd_expression"
+local rspamd_mempool = require "rspamd_mempool"
+
+local parse_func(str)
+       -- extract token till the first space character
+       local token = table.join('', take_while(function(s) return s <> ' ' end, str)
+       -- Return token name
+       return token
+end
+
+local process_func(token, task)
+       -- Do something using token and task
+end
+
+local pool = rspamd_mempool.create()
+local expr,err = rspamd_expression.create('A & B | !C', {parse_func, process_func}, pool)
+-- Expression is destroyed when the corresponding pool is destroyed
+pool:destroy()
+ */
+LUA_FUNCTION_DEF (expr, create);
+
+/***
+ * @method rspamd_expression:to_string()
+ * Converts rspamd expression to string
+ * @return {string} string representation of rspamd expression
+ */
+LUA_FUNCTION_DEF (expr, to_string);
+
+static const struct luaL_reg exprlib_m[] = {
+       LUA_INTERFACE_DEF (expr, to_string),
+       {NULL, NULL}
+};
+
+static const struct luaL_reg exprlib_f[] = {
+       LUA_INTERFACE_DEF (expr, create),
+       {NULL, NULL}
+};
+
+static rspamd_expression_atom_t * lua_atom_parse (const gchar *line, gsize len,
+                       rspamd_mempool_t *pool, gpointer ud, GError **err);
+static gint lua_atom_process (gpointer input, rspamd_expression_atom_t *atom);
+
+static const struct rspamd_atom_subr lua_atom_subr = {
+       .parse = lua_atom_parse,
+       .process = lua_atom_process,
+       .priority = NULL,
+       .destroy = NULL
+};
+
+struct lua_expression {
+       struct rspamd_expression *expr;
+       gint parse_idx;
+       gint process_idx;
+       lua_State *L;
+       rspamd_mempool_t *pool;
+};
+
+static GQuark
+lua_expr_quark (void)
+{
+       return g_quark_from_static_string ("lua-expression");
+}
+
+struct lua_expression *
+rspamd_lua_expression (lua_State * L, gint pos)
+{
+       void *ud = luaL_checkudata (L, pos, "rspamd{expr}");
+       luaL_argcheck (L, ud != NULL, pos, "'expr' expected");
+       return ud ? *((struct lua_expression **)ud) : NULL;
+}
+
+
+static rspamd_expression_atom_t *
+lua_atom_parse (const gchar *line, gsize len,
+                       rspamd_mempool_t *pool, gpointer ud, GError **err)
+{
+       struct lua_expression *e = (struct lua_expression *)ud;
+       rspamd_expression_atom_t *atom;
+       gsize rlen;
+       const gchar *tok;
+
+       lua_rawgeti (e->L, LUA_REGISTRYINDEX, e->parse_idx);
+       lua_pushlstring (e->L, line, len);
+
+       if (lua_pcall (e->L, 1, 1, 0) != 0) {
+               msg_info ("callback call failed: %s", lua_tostring (e->L, -1));
+       }
+
+       if (lua_type (e->L, -1) != LUA_TSTRING) {
+               g_set_error (err, lua_expr_quark(), 500, "cannot parse lua atom");
+               lua_pop (e->L, 1);
+               return NULL;
+       }
+
+       tok = lua_tolstring (e->L, -1, &rlen);
+       atom = rspamd_mempool_alloc0 (e->pool, sizeof (*atom));
+       atom->str = rspamd_mempool_strdup (e->pool, tok);
+       atom->len = rlen;
+       atom->data = ud;
+
+       lua_pop (e->L, 1);
+
+       return atom;
+}
+
+static gint
+lua_atom_process (gpointer input, rspamd_expression_atom_t *atom)
+{
+       struct lua_expression *e = (struct lua_expression *)atom->data;
+       gint ret;
+
+       lua_rawgeti (e->L, LUA_REGISTRYINDEX, e->process_idx);
+       lua_pushvalue (e->L, GPOINTER_TO_INT (input));
+
+       if (lua_pcall (e->L, 1, 1, 0) != 0) {
+               msg_info ("callback call failed: %s", lua_tostring (e->L, -1));
+       }
+
+       ret = lua_tonumber (e->L, -1);
+       lua_pop (e->L, 1);
+
+       return ret;
+}
+
+static gint
+lua_expr_create (lua_State *L)
+{
+       struct lua_expression *e, **pe;
+       const char *line;
+       gsize len;
+       GError *err = NULL;
+       rspamd_mempool_t *pool;
+
+       /* Check sanity of the arguments */
+       if (lua_type (L, 1) != LUA_TSTRING || lua_type (L, 2) != LUA_TTABLE ||
+                       rspamd_lua_check_mempool (L, 3) == NULL) {
+               msg_info ("bad arguments to lua_expr_create");
+               lua_pushnil (L);
+               lua_pushstring (L, "bad arguments");
+       }
+       else {
+               line = lua_tolstring (L, 1, &len);
+               pool = rspamd_lua_check_mempool (L, 3);
+
+               /* Check callbacks */
+               lua_pushvalue (L, 2);
+               lua_pushnumber (L, 1);
+               lua_gettable (L, -2);
+
+               if (lua_type (L, -1) != LUA_TFUNCTION) {
+                       lua_pop (L, 2);
+                       lua_pushnil (L);
+                       lua_pushstring (L, "bad parse callback");
+
+                       return 2;
+               }
+
+               lua_pop (L, 1);
+
+               lua_pushnumber (L, 1);
+               lua_gettable (L, -2);
+
+               if (lua_type (L, -1) != LUA_TFUNCTION) {
+                       lua_pop (L, 2);
+                       lua_pushnil (L);
+                       lua_pushstring (L, "bad process callback");
+
+                       return 2;
+               }
+
+               lua_pop (L, 1);
+
+               /* Table is still on the top of stack */
+
+               e = rspamd_mempool_alloc (pool, sizeof (*e));
+               e->L = L;
+               e->pool = pool;
+
+               lua_pushnumber (L, 1);
+               lua_gettable (L, -2);
+               e->parse_idx = luaL_ref (L, LUA_REGISTRYINDEX);
+               lua_pop (L, 1);
+               lua_pushnumber (L, 2);
+               lua_gettable (L, -2);
+               e->process_idx = luaL_ref (L, LUA_REGISTRYINDEX);
+               lua_pop (L, 2); /* Idx and table */
+
+               if (!rspamd_parse_expression (line, len, &lua_atom_subr, e, pool, &err,
+                               &e->expr)) {
+                       lua_pushnil (L);
+                       lua_pushstring (L, err->message);
+                       g_error_free (err);
+
+                       return 2;
+               }
+
+               pe = lua_newuserdata (L, sizeof (struct lua_expression *));
+               *pe = e;
+               lua_pushnil (L);
+       }
+
+       return 2;
+}
+
+static gint
+lua_expr_to_string (lua_State *L)
+{
+       struct lua_expression *e = rspamd_lua_expression (L, 1);
+       GString *str;
+
+       if (e != NULL && e->expr != NULL) {
+               str = rspamd_expression_tostring (e->expr);
+               if (str) {
+                       lua_pushlstring (L, str->str, str->len);
+                       g_string_free (str, TRUE);
+               }
+               else {
+                       lua_pushnil (L);
+               }
+       }
+       else {
+               lua_pushnil (L);
+       }
+
+       return 1;
+}
+
+static gint
+lua_load_expression (lua_State * L)
+{
+       lua_newtable (L);
+       luaL_register (L, NULL, exprlib_f);
+
+       return 1;
+}
+
+void
+luaopen_expression (lua_State * L)
+{
+       luaL_newmetatable (L, "rspamd{expr}");
+       lua_pushstring (L, "__index");
+       lua_pushvalue (L, -2);
+       lua_settable (L, -3);
+
+       lua_pushstring (L, "class");
+       lua_pushstring (L, "rspamd{expr}");
+       lua_rawset (L, -3);
+
+       luaL_register (L, NULL,            exprlib_m);
+       rspamd_lua_add_preload (L, "rspamd_expression", lua_load_expression);
+
+       lua_pop (L, 1);                      /* remove metatable from stack */
+}