You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

lua_expression.c 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. /*-
  2. * Copyright 2016 Vsevolod Stakhov
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "lua_common.h"
  17. #include "expression.h"
  18. /***
  19. * @module rspamd_expression
  20. * This module can be used to implement different logic expressions in lua using
  21. * rspamd AST optimizer. There are some examples in individual methods definitions to help understanding of this module.
  22. */
  23. /***
  24. * @function rspamd_expression.create(line, {parse_func, [process_func]}, pool)
  25. * Create expression from the line using atom parsing routines and the specified memory pool
  26. * @param {string} line expression line
  27. * @param {table} atom_functions parse_atom function and optional process_atom function
  28. * @param {rspamd_mempool} memory pool to use for this function
  29. * @return {expr, err} expression object and error message of `expr` is nil
  30. * @example
  31. require "fun" ()
  32. local rspamd_expression = require "rspamd_expression"
  33. local rspamd_mempool = require "rspamd_mempool"
  34. local function parse_func(str)
  35. -- extract token till the first space character
  36. local token = table.concat(totable(take_while(function(s) return s ~= ' ' end, iter(str))))
  37. -- Return token name
  38. return token
  39. end
  40. local function process_func(token)
  41. -- Do something using token and task
  42. end
  43. local pool = rspamd_mempool.create()
  44. local expr,err = rspamd_expression.create('A & B | !C', {parse_func, process_func}, pool)
  45. -- Expression is destroyed when the corresponding pool is destroyed
  46. pool:destroy()
  47. */
  48. LUA_FUNCTION_DEF (expr, create);
  49. /***
  50. * @method rspamd_expression:to_string()
  51. * Converts rspamd expression to string
  52. * @return {string} string representation of rspamd expression
  53. */
  54. LUA_FUNCTION_DEF (expr, to_string);
  55. /***
  56. * @method rspamd_expression:process([callback, ]input[, flags])
  57. * Executes the expression and pass input to process atom callbacks
  58. * @param {function} callback if not specified on process, then callback must be here
  59. * @param {any} input input data for processing callbacks
  60. * @return {number} result of the expression evaluation
  61. */
  62. LUA_FUNCTION_DEF (expr, process);
  63. /***
  64. * @method rspamd_expression:process_traced([callback, ]input[, flags])
  65. * Executes the expression and pass input to process atom callbacks. This function also saves the full trace
  66. * @param {function} callback if not specified on process, then callback must be here
  67. * @param {any} input input data for processing callbacks
  68. * @return {number, table of matched atoms} result of the expression evaluation
  69. */
  70. LUA_FUNCTION_DEF (expr, process_traced);
  71. /***
  72. * @method rspamd_expression:atoms()
  73. * Extract all atoms from the expression as table of strings
  74. * @return {table/strings} list of all atoms in the expression
  75. */
  76. LUA_FUNCTION_DEF (expr, atoms);
  77. static const struct luaL_reg exprlib_m[] = {
  78. LUA_INTERFACE_DEF (expr, to_string),
  79. LUA_INTERFACE_DEF (expr, atoms),
  80. LUA_INTERFACE_DEF (expr, process),
  81. LUA_INTERFACE_DEF (expr, process_traced),
  82. {"__tostring", lua_expr_to_string},
  83. {NULL, NULL}
  84. };
  85. static const struct luaL_reg exprlib_f[] = {
  86. LUA_INTERFACE_DEF (expr, create),
  87. {NULL, NULL}
  88. };
  89. static rspamd_expression_atom_t * lua_atom_parse (const gchar *line, gsize len,
  90. rspamd_mempool_t *pool, gpointer ud, GError **err);
  91. static gdouble lua_atom_process (gpointer runtime_ud, rspamd_expression_atom_t *atom);
  92. static const struct rspamd_atom_subr lua_atom_subr = {
  93. .parse = lua_atom_parse,
  94. .process = lua_atom_process,
  95. .priority = NULL,
  96. .destroy = NULL
  97. };
  98. struct lua_expression {
  99. struct rspamd_expression *expr;
  100. gint parse_idx;
  101. gint process_idx;
  102. lua_State *L;
  103. rspamd_mempool_t *pool;
  104. };
  105. static GQuark
  106. lua_expr_quark (void)
  107. {
  108. return g_quark_from_static_string ("lua-expression");
  109. }
  110. struct lua_expression *
  111. rspamd_lua_expression (lua_State * L, gint pos)
  112. {
  113. void *ud = rspamd_lua_check_udata (L, pos, "rspamd{expr}");
  114. luaL_argcheck (L, ud != NULL, pos, "'expr' expected");
  115. return ud ? *((struct lua_expression **)ud) : NULL;
  116. }
  117. static void
  118. lua_expr_dtor (gpointer p)
  119. {
  120. struct lua_expression *e = (struct lua_expression *)p;
  121. if (e->parse_idx != -1) {
  122. luaL_unref (e->L, LUA_REGISTRYINDEX, e->parse_idx);
  123. }
  124. if (e->process_idx != -1) {
  125. luaL_unref (e->L, LUA_REGISTRYINDEX, e->process_idx);
  126. }
  127. }
  128. static rspamd_expression_atom_t *
  129. lua_atom_parse (const gchar *line, gsize len,
  130. rspamd_mempool_t *pool, gpointer ud, GError **err)
  131. {
  132. struct lua_expression *e = (struct lua_expression *)ud;
  133. rspamd_expression_atom_t *atom;
  134. gsize rlen;
  135. const gchar *tok;
  136. lua_rawgeti (e->L, LUA_REGISTRYINDEX, e->parse_idx);
  137. lua_pushlstring (e->L, line, len);
  138. if (lua_pcall (e->L, 1, 1, 0) != 0) {
  139. msg_info ("callback call failed: %s", lua_tostring (e->L, -1));
  140. lua_pop (e->L, 1);
  141. return NULL;
  142. }
  143. if (lua_type (e->L, -1) != LUA_TSTRING) {
  144. g_set_error (err, lua_expr_quark(), 500, "cannot parse lua atom");
  145. lua_pop (e->L, 1);
  146. return NULL;
  147. }
  148. tok = lua_tolstring (e->L, -1, &rlen);
  149. atom = rspamd_mempool_alloc0 (e->pool, sizeof (*atom));
  150. atom->str = rspamd_mempool_strdup (e->pool, tok);
  151. atom->len = rlen;
  152. atom->data = ud;
  153. lua_pop (e->L, 1);
  154. return atom;
  155. }
  156. struct lua_atom_process_data {
  157. lua_State *L;
  158. struct lua_expression *e;
  159. gint process_cb_pos;
  160. gint stack_item;
  161. };
  162. static gdouble
  163. lua_atom_process (gpointer runtime_ud, rspamd_expression_atom_t *atom)
  164. {
  165. struct lua_atom_process_data *pd = (struct lua_atom_process_data *)runtime_ud;
  166. gdouble ret = 0;
  167. guint nargs;
  168. gint err_idx;
  169. if (pd->stack_item != -1) {
  170. nargs = 2;
  171. }
  172. else {
  173. nargs = 1;
  174. }
  175. lua_pushcfunction (pd->L, &rspamd_lua_traceback);
  176. err_idx = lua_gettop (pd->L);
  177. /* Function position */
  178. lua_pushvalue (pd->L, pd->process_cb_pos);
  179. /* Atom name */
  180. lua_pushlstring (pd->L, atom->str, atom->len);
  181. /* If we have data passed */
  182. if (pd->stack_item != -1) {
  183. lua_pushvalue (pd->L, pd->stack_item);
  184. }
  185. if (lua_pcall (pd->L, nargs, 1, err_idx) != 0) {
  186. msg_info ("expression process callback failed: %s", lua_tostring (pd->L, -1));
  187. }
  188. else {
  189. ret = lua_tonumber (pd->L, -1);
  190. }
  191. lua_settop (pd->L, err_idx - 1);
  192. return ret;
  193. }
  194. static gint
  195. lua_expr_process (lua_State *L)
  196. {
  197. LUA_TRACE_POINT;
  198. struct lua_expression *e = rspamd_lua_expression (L, 1);
  199. struct lua_atom_process_data pd;
  200. gdouble res;
  201. gint flags = 0, old_top;
  202. pd.L = L;
  203. pd.e = e;
  204. old_top = lua_gettop (L);
  205. if (e->process_idx == -1) {
  206. if (!lua_isfunction (L, 2)) {
  207. return luaL_error (L, "expression process is called with no callback function");
  208. }
  209. pd.process_cb_pos = 2;
  210. if (lua_type (L, 3) != LUA_TNONE && lua_type (L, 3) != LUA_TNIL) {
  211. pd.stack_item = 3;
  212. }
  213. else {
  214. pd.stack_item = -1;
  215. }
  216. if (lua_isnumber (L, 4)) {
  217. flags = lua_tointeger (L, 4);
  218. }
  219. }
  220. else {
  221. lua_rawgeti (L, LUA_REGISTRYINDEX, e->process_idx);
  222. pd.process_cb_pos = lua_gettop (L);
  223. if (lua_type (L, 2) != LUA_TNONE && lua_type (L, 2) != LUA_TNIL) {
  224. pd.stack_item = 2;
  225. }
  226. else {
  227. pd.stack_item = -1;
  228. }
  229. if (lua_isnumber (L, 3)) {
  230. flags = lua_tointeger (L, 3);
  231. }
  232. }
  233. res = rspamd_process_expression (e->expr, flags, &pd);
  234. lua_settop (L, old_top);
  235. lua_pushnumber (L, res);
  236. return 1;
  237. }
  238. static gint
  239. lua_expr_process_traced (lua_State *L)
  240. {
  241. LUA_TRACE_POINT;
  242. struct lua_expression *e = rspamd_lua_expression (L, 1);
  243. struct lua_atom_process_data pd;
  244. gdouble res;
  245. gint flags = 0, old_top;
  246. GPtrArray *trace;
  247. pd.L = L;
  248. pd.e = e;
  249. old_top = lua_gettop (L);
  250. if (e->process_idx == -1) {
  251. if (!lua_isfunction (L, 2)) {
  252. return luaL_error (L, "expression process is called with no callback function");
  253. }
  254. pd.process_cb_pos = 2;
  255. pd.stack_item = 3;
  256. if (lua_isnumber (L, 4)) {
  257. flags = lua_tointeger (L, 4);
  258. }
  259. }
  260. else {
  261. lua_rawgeti (L, LUA_REGISTRYINDEX, e->process_idx);
  262. pd.process_cb_pos = lua_gettop (L);
  263. pd.stack_item = 2;
  264. if (lua_isnumber (L, 3)) {
  265. flags = lua_tointeger (L, 3);
  266. }
  267. }
  268. res = rspamd_process_expression_track (e->expr, flags, &pd, &trace);
  269. lua_settop (L, old_top);
  270. lua_pushnumber (L, res);
  271. lua_createtable (L, trace->len, 0);
  272. for (guint i = 0; i < trace->len; i ++) {
  273. struct rspamd_expression_atom_s *atom = g_ptr_array_index (trace, i);
  274. lua_pushlstring (L, atom->str, atom->len);
  275. lua_rawseti (L, -2, i + 1);
  276. }
  277. g_ptr_array_free (trace, TRUE);
  278. return 2;
  279. }
  280. static gint
  281. lua_expr_create (lua_State *L)
  282. {
  283. LUA_TRACE_POINT;
  284. struct lua_expression *e, **pe;
  285. const char *line;
  286. gsize len;
  287. gboolean no_process = FALSE;
  288. GError *err = NULL;
  289. rspamd_mempool_t *pool;
  290. /* Check sanity of the arguments */
  291. if (lua_type (L, 1) != LUA_TSTRING ||
  292. (lua_type (L, 2) != LUA_TTABLE && lua_type (L, 2) != LUA_TFUNCTION) ||
  293. rspamd_lua_check_mempool (L, 3) == NULL) {
  294. lua_pushnil (L);
  295. lua_pushstring (L, "bad arguments");
  296. }
  297. else {
  298. line = lua_tolstring (L, 1, &len);
  299. pool = rspamd_lua_check_mempool (L, 3);
  300. e = rspamd_mempool_alloc (pool, sizeof (*e));
  301. e->L = L;
  302. e->pool = pool;
  303. /* Check callbacks */
  304. if (lua_istable (L, 2)) {
  305. lua_pushvalue (L, 2);
  306. lua_pushnumber (L, 1);
  307. lua_gettable (L, -2);
  308. if (lua_type (L, -1) != LUA_TFUNCTION) {
  309. lua_pop (L, 1);
  310. lua_pushnil (L);
  311. lua_pushstring (L, "bad parse callback");
  312. return 2;
  313. }
  314. lua_pop (L, 1);
  315. lua_pushnumber (L, 2);
  316. lua_gettable (L, -2);
  317. if (lua_type (L, -1) != LUA_TFUNCTION) {
  318. if (lua_type (L, -1) != LUA_TNIL && lua_type (L, -1) != LUA_TNONE) {
  319. lua_pop (L, 1);
  320. lua_pushnil (L);
  321. lua_pushstring (L, "bad process callback");
  322. return 2;
  323. }
  324. else {
  325. no_process = TRUE;
  326. }
  327. }
  328. lua_pop (L, 1);
  329. /* Table is still on the top of stack */
  330. lua_pushnumber (L, 1);
  331. lua_gettable (L, -2);
  332. e->parse_idx = luaL_ref (L, LUA_REGISTRYINDEX);
  333. if (!no_process) {
  334. lua_pushnumber (L, 2);
  335. lua_gettable (L, -2);
  336. e->process_idx = luaL_ref (L, LUA_REGISTRYINDEX);
  337. }
  338. else {
  339. e->process_idx = -1;
  340. }
  341. lua_pop (L, 1); /* Table */
  342. }
  343. else {
  344. /* Process function is just a function, not a table */
  345. lua_pushvalue (L, 2);
  346. e->parse_idx = luaL_ref (L, LUA_REGISTRYINDEX);
  347. e->process_idx = -1;
  348. }
  349. if (!rspamd_parse_expression (line, len, &lua_atom_subr, e, pool, &err,
  350. &e->expr)) {
  351. lua_pushnil (L);
  352. lua_pushstring (L, err->message);
  353. g_error_free (err);
  354. lua_expr_dtor (e);
  355. return 2;
  356. }
  357. rspamd_mempool_add_destructor (pool, lua_expr_dtor, e);
  358. pe = lua_newuserdata (L, sizeof (struct lua_expression *));
  359. rspamd_lua_setclass (L, "rspamd{expr}", -1);
  360. *pe = e;
  361. lua_pushnil (L);
  362. }
  363. return 2;
  364. }
  365. static gint
  366. lua_expr_to_string (lua_State *L)
  367. {
  368. LUA_TRACE_POINT;
  369. struct lua_expression *e = rspamd_lua_expression (L, 1);
  370. GString *str;
  371. if (e != NULL && e->expr != NULL) {
  372. str = rspamd_expression_tostring (e->expr);
  373. if (str) {
  374. lua_pushlstring (L, str->str, str->len);
  375. g_string_free (str, TRUE);
  376. }
  377. else {
  378. lua_pushnil (L);
  379. }
  380. }
  381. else {
  382. lua_pushnil (L);
  383. }
  384. return 1;
  385. }
  386. struct lua_expr_atoms_cbdata {
  387. lua_State *L;
  388. gint idx;
  389. };
  390. static void
  391. lua_exr_atom_cb (const rspamd_ftok_t *tok, gpointer ud)
  392. {
  393. struct lua_expr_atoms_cbdata *cbdata = ud;
  394. lua_pushlstring (cbdata->L, tok->begin, tok->len);
  395. lua_rawseti (cbdata->L, -2, cbdata->idx ++);
  396. }
  397. static gint
  398. lua_expr_atoms (lua_State *L)
  399. {
  400. LUA_TRACE_POINT;
  401. struct lua_expression *e = rspamd_lua_expression (L, 1);
  402. struct lua_expr_atoms_cbdata cbdata;
  403. if (e != NULL && e->expr != NULL) {
  404. lua_newtable (L);
  405. cbdata.L = L;
  406. cbdata.idx = 1;
  407. rspamd_expression_atom_foreach (e->expr, lua_exr_atom_cb, &cbdata);
  408. }
  409. else {
  410. lua_pushnil (L);
  411. }
  412. return 1;
  413. }
  414. static gint
  415. lua_load_expression (lua_State * L)
  416. {
  417. lua_newtable (L);
  418. luaL_register (L, NULL, exprlib_f);
  419. return 1;
  420. }
  421. void
  422. luaopen_expression (lua_State * L)
  423. {
  424. rspamd_lua_new_class (L, "rspamd{expr}", exprlib_m);
  425. lua_pop (L, 1);
  426. rspamd_lua_add_preload (L, "rspamd_expression", lua_load_expression);
  427. }