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_map.c 32KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421
  1. /*
  2. * Copyright 2024 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 "libserver/maps/map.h"
  18. #include "libserver/maps/map_helpers.h"
  19. #include "libserver/maps/map_private.h"
  20. #include "contrib/libucl/lua_ucl.h"
  21. /***
  22. * This module is used to manage rspamd maps and map like objects
  23. *
  24. * @module rspamd_map
  25. *
  26. * All maps could be obtained by function `rspamd_config:get_maps()`
  27. * Also see [`lua_maps` module description](lua_maps.html).
  28. *
  29. * **Important notice** maps cannot be queried outside of the worker context.
  30. * For example, you cannot add even a file map and query some keys from it during
  31. * some module initialisation, you need to add the appropriate event loop context
  32. * for a worker (e.g. you cannot use `get_key` outside of the symbols callbacks or
  33. * a worker `on_load` scripts).
  34. *
  35. @example
  36. local hash_map = rspamd_config:add_map{
  37. type = "hash",
  38. urls = ['file:///path/to/file'],
  39. description = 'sample map'
  40. }
  41. local function sample_symbol_cb(task)
  42. -- Check whether hash map contains from address of message
  43. if hash_map:get_key((task:get_from() or {})[1]) then
  44. -- key found
  45. end
  46. end
  47. rspamd_config:register_symbol{
  48. name = 'SAMPLE_SYMBOL',
  49. type = 'normal',
  50. score = 1.0,
  51. description = "A sample symbol",
  52. callback = sample_symbol_cb,
  53. }
  54. */
  55. /***
  56. * @method map:get_key(in)
  57. * Variable method for different types of maps:
  58. *
  59. * - For hash maps it returns boolean and accepts string
  60. * - For kv maps it returns string (or nil) and accepts string
  61. * - For radix maps it returns boolean and accepts IP address (as object, string or number)
  62. *
  63. * @param {vary} in input to check
  64. * @return {bool|string} if a value is found then this function returns string or `True` if not - then it returns `nil` or `False`
  65. */
  66. LUA_FUNCTION_DEF(map, get_key);
  67. /***
  68. * @method map:is_signed()
  69. * Returns `True` if a map is signed
  70. * @return {bool} signed value
  71. */
  72. LUA_FUNCTION_DEF(map, is_signed);
  73. /***
  74. * @method map:get_proto()
  75. * Returns protocol of map as string:
  76. *
  77. * - `http`: for HTTP map
  78. * - `file`: for file map
  79. * @return {string} string representation of the map protocol
  80. */
  81. LUA_FUNCTION_DEF(map, get_proto);
  82. /***
  83. * @method map:get_sign_key()
  84. * Returns pubkey used for signing as base32 string or nil
  85. * @return {string} base32 encoded string or nil
  86. */
  87. LUA_FUNCTION_DEF(map, get_sign_key);
  88. /***
  89. * @method map:set_sign_key(key)
  90. * Set trusted key for signatures for this map
  91. * @param {string} key base32 encoded string or nil
  92. */
  93. LUA_FUNCTION_DEF(map, set_sign_key);
  94. /***
  95. * @method map:set_callback(cb)
  96. * Set callback for a specified callback map.
  97. * @param {function} cb map callback function
  98. */
  99. LUA_FUNCTION_DEF(map, set_callback);
  100. /***
  101. * @method map:get_uri()
  102. * Get uri for a specified map
  103. * @return {string} map's URI
  104. */
  105. LUA_FUNCTION_DEF(map, get_uri);
  106. /***
  107. * @method map:get_stats(reset)
  108. * Get statistics for specific map. It returns table in form:
  109. * [key] => [nhits]
  110. * @param {boolean} reset reset stats if true
  111. * @return {table} map's stat
  112. */
  113. LUA_FUNCTION_DEF(map, get_stats);
  114. /***
  115. * @method map:foreach(callback, is_text)
  116. * Iterate over map elements and call callback for each element.
  117. * @param {function} callback callback function, that accepts two arguments: key and value, if it returns true then iteration is stopped
  118. * @param {boolean} is_text if true then callback accepts rspamd_text instead of Lua strings
  119. * @return {number} number of elements iterated
  120. */
  121. LUA_FUNCTION_DEF(map, foreach);
  122. /***
  123. * @method map:on_load(callback)
  124. * Sets a callback for a map that is called when map is loaded
  125. * @param {function} callback callback function, that accepts no arguments (pass maps in a closure if needed)
  126. */
  127. LUA_FUNCTION_DEF(map, on_load);
  128. /***
  129. * @method map:get_data_digest()
  130. * Get data digest for specific map
  131. * @return {string} 64 bit number represented as string (due to Lua limitations)
  132. */
  133. LUA_FUNCTION_DEF(map, get_data_digest);
  134. /***
  135. * @method map:get_nelts()
  136. * Get number of elements for specific map
  137. * @return {number} number of elements in the map
  138. */
  139. LUA_FUNCTION_DEF(map, get_nelts);
  140. static const struct luaL_reg maplib_m[] = {
  141. LUA_INTERFACE_DEF(map, get_key),
  142. LUA_INTERFACE_DEF(map, is_signed),
  143. LUA_INTERFACE_DEF(map, get_proto),
  144. LUA_INTERFACE_DEF(map, get_sign_key),
  145. LUA_INTERFACE_DEF(map, set_sign_key),
  146. LUA_INTERFACE_DEF(map, set_callback),
  147. LUA_INTERFACE_DEF(map, get_uri),
  148. LUA_INTERFACE_DEF(map, get_stats),
  149. LUA_INTERFACE_DEF(map, foreach),
  150. LUA_INTERFACE_DEF(map, on_load),
  151. LUA_INTERFACE_DEF(map, get_data_digest),
  152. LUA_INTERFACE_DEF(map, get_nelts),
  153. {"__tostring", rspamd_lua_class_tostring},
  154. {NULL, NULL}};
  155. struct lua_map_callback_data {
  156. lua_State *L;
  157. int ref;
  158. gboolean opaque;
  159. rspamd_fstring_t *data;
  160. struct rspamd_lua_map *lua_map;
  161. };
  162. struct rspamd_lua_map *
  163. lua_check_map(lua_State *L, int pos)
  164. {
  165. void *ud = rspamd_lua_check_udata(L, pos, rspamd_map_classname);
  166. luaL_argcheck(L, ud != NULL, pos, "'map' expected");
  167. return ud ? *((struct rspamd_lua_map **) ud) : NULL;
  168. }
  169. int lua_config_add_radix_map(lua_State *L)
  170. {
  171. LUA_TRACE_POINT;
  172. struct rspamd_config *cfg = lua_check_config(L, 1);
  173. const char *map_line, *description;
  174. struct rspamd_lua_map *map, **pmap;
  175. struct rspamd_map *m;
  176. if (cfg) {
  177. map_line = luaL_checkstring(L, 2);
  178. description = lua_tostring(L, 3);
  179. map = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(*map));
  180. map->data.radix = NULL;
  181. map->type = RSPAMD_LUA_MAP_RADIX;
  182. if ((m = rspamd_map_add(cfg, map_line, description,
  183. rspamd_radix_read,
  184. rspamd_radix_fin,
  185. rspamd_radix_dtor,
  186. (void **) &map->data.radix,
  187. NULL, RSPAMD_MAP_DEFAULT)) == NULL) {
  188. msg_warn_config("invalid radix map %s", map_line);
  189. lua_pushnil(L);
  190. return 1;
  191. }
  192. map->map = m;
  193. m->lua_map = map;
  194. pmap = lua_newuserdata(L, sizeof(void *));
  195. *pmap = map;
  196. rspamd_lua_setclass(L, rspamd_map_classname, -1);
  197. }
  198. else {
  199. return luaL_error(L, "invalid arguments");
  200. }
  201. return 1;
  202. }
  203. int lua_config_radix_from_config(lua_State *L)
  204. {
  205. LUA_TRACE_POINT;
  206. struct rspamd_config *cfg = lua_check_config(L, 1);
  207. const char *mname, *optname;
  208. const ucl_object_t *obj;
  209. struct rspamd_lua_map *map, **pmap;
  210. ucl_object_t *fake_obj;
  211. struct rspamd_map *m;
  212. if (!cfg) {
  213. return luaL_error(L, "invalid arguments");
  214. }
  215. mname = luaL_checkstring(L, 2);
  216. optname = luaL_checkstring(L, 3);
  217. if (mname && optname) {
  218. obj = rspamd_config_get_module_opt(cfg, mname, optname);
  219. if (obj) {
  220. map = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(*map));
  221. map->data.radix = NULL;
  222. map->type = RSPAMD_LUA_MAP_RADIX;
  223. fake_obj = ucl_object_typed_new(UCL_OBJECT);
  224. ucl_object_insert_key(fake_obj, ucl_object_ref(obj),
  225. "data", 0, false);
  226. ucl_object_insert_key(fake_obj, ucl_object_fromstring("static"),
  227. "url", 0, false);
  228. if ((m = rspamd_map_add_from_ucl(cfg, fake_obj, "static radix map",
  229. rspamd_radix_read,
  230. rspamd_radix_fin,
  231. rspamd_radix_dtor,
  232. (void **) &map->data.radix,
  233. NULL, RSPAMD_MAP_DEFAULT)) == NULL) {
  234. msg_err_config("invalid radix map static");
  235. lua_pushnil(L);
  236. ucl_object_unref(fake_obj);
  237. return 1;
  238. }
  239. ucl_object_unref(fake_obj);
  240. pmap = lua_newuserdata(L, sizeof(void *));
  241. map->map = m;
  242. m->lua_map = map;
  243. *pmap = map;
  244. rspamd_lua_setclass(L, rspamd_map_classname, -1);
  245. }
  246. else {
  247. msg_warn_config("Couldnt find config option [%s][%s]", mname,
  248. optname);
  249. lua_pushnil(L);
  250. }
  251. }
  252. else {
  253. return luaL_error(L, "invalid arguments");
  254. }
  255. return 1;
  256. }
  257. int lua_config_radix_from_ucl(lua_State *L)
  258. {
  259. LUA_TRACE_POINT;
  260. struct rspamd_config *cfg = lua_check_config(L, 1);
  261. ucl_object_t *obj;
  262. struct rspamd_lua_map *map, **pmap;
  263. ucl_object_t *fake_obj;
  264. struct rspamd_map *m;
  265. if (!cfg) {
  266. return luaL_error(L, "invalid arguments");
  267. }
  268. obj = ucl_object_lua_import(L, 2);
  269. if (obj) {
  270. map = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(*map));
  271. map->data.radix = NULL;
  272. map->type = RSPAMD_LUA_MAP_RADIX;
  273. fake_obj = ucl_object_typed_new(UCL_OBJECT);
  274. ucl_object_insert_key(fake_obj, ucl_object_ref(obj),
  275. "data", 0, false);
  276. ucl_object_insert_key(fake_obj, ucl_object_fromstring("static"),
  277. "url", 0, false);
  278. if ((m = rspamd_map_add_from_ucl(cfg, fake_obj, "static radix map",
  279. rspamd_radix_read,
  280. rspamd_radix_fin,
  281. rspamd_radix_dtor,
  282. (void **) &map->data.radix,
  283. NULL, RSPAMD_MAP_DEFAULT)) == NULL) {
  284. msg_err_config("invalid radix map static");
  285. lua_pushnil(L);
  286. ucl_object_unref(fake_obj);
  287. ucl_object_unref(obj);
  288. return 1;
  289. }
  290. ucl_object_unref(fake_obj);
  291. ucl_object_unref(obj);
  292. pmap = lua_newuserdata(L, sizeof(void *));
  293. map->map = m;
  294. m->lua_map = map;
  295. *pmap = map;
  296. rspamd_lua_setclass(L, rspamd_map_classname, -1);
  297. }
  298. else {
  299. return luaL_error(L, "invalid arguments");
  300. }
  301. return 1;
  302. }
  303. int lua_config_add_hash_map(lua_State *L)
  304. {
  305. LUA_TRACE_POINT;
  306. struct rspamd_config *cfg = lua_check_config(L, 1);
  307. const char *map_line, *description;
  308. struct rspamd_lua_map *map, **pmap;
  309. struct rspamd_map *m;
  310. if (cfg) {
  311. map_line = luaL_checkstring(L, 2);
  312. description = lua_tostring(L, 3);
  313. map = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(*map));
  314. map->data.hash = NULL;
  315. map->type = RSPAMD_LUA_MAP_SET;
  316. if ((m = rspamd_map_add(cfg, map_line, description,
  317. rspamd_kv_list_read,
  318. rspamd_kv_list_fin,
  319. rspamd_kv_list_dtor,
  320. (void **) &map->data.hash,
  321. NULL, RSPAMD_MAP_DEFAULT)) == NULL) {
  322. msg_warn_config("invalid set map %s", map_line);
  323. lua_pushnil(L);
  324. return 1;
  325. }
  326. map->map = m;
  327. m->lua_map = map;
  328. pmap = lua_newuserdata(L, sizeof(void *));
  329. *pmap = map;
  330. rspamd_lua_setclass(L, rspamd_map_classname, -1);
  331. }
  332. else {
  333. return luaL_error(L, "invalid arguments");
  334. }
  335. return 1;
  336. }
  337. int lua_config_add_kv_map(lua_State *L)
  338. {
  339. LUA_TRACE_POINT;
  340. struct rspamd_config *cfg = lua_check_config(L, 1);
  341. const char *map_line, *description;
  342. struct rspamd_lua_map *map, **pmap;
  343. struct rspamd_map *m;
  344. if (cfg) {
  345. map_line = luaL_checkstring(L, 2);
  346. description = lua_tostring(L, 3);
  347. map = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(*map));
  348. map->data.hash = NULL;
  349. map->type = RSPAMD_LUA_MAP_HASH;
  350. if ((m = rspamd_map_add(cfg, map_line, description,
  351. rspamd_kv_list_read,
  352. rspamd_kv_list_fin,
  353. rspamd_kv_list_dtor,
  354. (void **) &map->data.hash,
  355. NULL, RSPAMD_MAP_DEFAULT)) == NULL) {
  356. msg_warn_config("invalid hash map %s", map_line);
  357. lua_pushnil(L);
  358. return 1;
  359. }
  360. map->map = m;
  361. m->lua_map = map;
  362. pmap = lua_newuserdata(L, sizeof(void *));
  363. *pmap = map;
  364. rspamd_lua_setclass(L, rspamd_map_classname, -1);
  365. }
  366. else {
  367. return luaL_error(L, "invalid arguments");
  368. }
  369. return 1;
  370. }
  371. static char *
  372. lua_map_read(char *chunk, int len,
  373. struct map_cb_data *data,
  374. gboolean final)
  375. {
  376. struct lua_map_callback_data *cbdata, *old;
  377. if (data->cur_data == NULL) {
  378. old = (struct lua_map_callback_data *) data->prev_data;
  379. cbdata = old;
  380. cbdata->L = old->L;
  381. cbdata->ref = old->ref;
  382. cbdata->lua_map = old->lua_map;
  383. data->cur_data = cbdata;
  384. data->prev_data = NULL;
  385. }
  386. else {
  387. cbdata = (struct lua_map_callback_data *) data->cur_data;
  388. }
  389. if (cbdata->data == NULL) {
  390. cbdata->data = rspamd_fstring_new_init(chunk, len);
  391. }
  392. else {
  393. cbdata->data = rspamd_fstring_append(cbdata->data, chunk, len);
  394. }
  395. return NULL;
  396. }
  397. static void
  398. lua_map_fin(struct map_cb_data *data, void **target)
  399. {
  400. struct lua_map_callback_data *cbdata;
  401. struct rspamd_lua_map **pmap;
  402. struct rspamd_map *map;
  403. map = data->map;
  404. if (data->errored) {
  405. if (data->cur_data) {
  406. cbdata = (struct lua_map_callback_data *) data->cur_data;
  407. if (cbdata->ref != -1) {
  408. luaL_unref(cbdata->L, LUA_REGISTRYINDEX, cbdata->ref);
  409. }
  410. if (cbdata->data) {
  411. rspamd_fstring_free(cbdata->data);
  412. }
  413. data->cur_data = NULL;
  414. }
  415. }
  416. else {
  417. if (data->cur_data) {
  418. cbdata = (struct lua_map_callback_data *) data->cur_data;
  419. }
  420. else {
  421. msg_err_map("no data read for map");
  422. return;
  423. }
  424. if (cbdata->ref == -1) {
  425. msg_err_map("map has no callback set");
  426. }
  427. else if (cbdata->data != NULL && cbdata->data->len != 0) {
  428. lua_pushcfunction(cbdata->L, &rspamd_lua_traceback);
  429. int err_idx = lua_gettop(cbdata->L);
  430. lua_rawgeti(cbdata->L, LUA_REGISTRYINDEX, cbdata->ref);
  431. if (!cbdata->opaque) {
  432. lua_pushlstring(cbdata->L, cbdata->data->str, cbdata->data->len);
  433. }
  434. else {
  435. struct rspamd_lua_text *t;
  436. t = lua_newuserdata(cbdata->L, sizeof(*t));
  437. rspamd_lua_setclass(cbdata->L, rspamd_text_classname, -1);
  438. t->flags = 0;
  439. t->len = cbdata->data->len;
  440. t->start = cbdata->data->str;
  441. }
  442. pmap = lua_newuserdata(cbdata->L, sizeof(void *));
  443. *pmap = cbdata->lua_map;
  444. rspamd_lua_setclass(cbdata->L, rspamd_map_classname, -1);
  445. int ret = lua_pcall(cbdata->L, 2, 0, err_idx);
  446. if (ret != 0) {
  447. msg_info_map("call to %s failed (%d): %s", "map fin function",
  448. ret,
  449. lua_tostring(cbdata->L, -1));
  450. }
  451. lua_settop(cbdata->L, err_idx - 1);
  452. }
  453. cbdata->data = rspamd_fstring_assign(cbdata->data, "", 0);
  454. if (target) {
  455. *target = data->cur_data;
  456. }
  457. if (data->prev_data) {
  458. data->prev_data = NULL;
  459. }
  460. }
  461. }
  462. static void
  463. lua_map_dtor(struct map_cb_data *data)
  464. {
  465. struct lua_map_callback_data *cbdata;
  466. if (data->cur_data) {
  467. cbdata = (struct lua_map_callback_data *) data->cur_data;
  468. if (cbdata->ref != -1) {
  469. luaL_unref(cbdata->L, LUA_REGISTRYINDEX, cbdata->ref);
  470. }
  471. if (cbdata->data) {
  472. rspamd_fstring_free(cbdata->data);
  473. }
  474. }
  475. }
  476. int lua_config_add_map(lua_State *L)
  477. {
  478. LUA_TRACE_POINT;
  479. struct rspamd_config *cfg = lua_check_config(L, 1);
  480. const char *description = NULL;
  481. const char *type = NULL;
  482. ucl_object_t *map_obj = NULL;
  483. struct lua_map_callback_data *cbdata;
  484. struct rspamd_lua_map *map, **pmap;
  485. struct rspamd_map *m;
  486. gboolean opaque_data = FALSE;
  487. int cbidx = -1, ret;
  488. GError *err = NULL;
  489. if (cfg) {
  490. if (!rspamd_lua_parse_table_arguments(L, 2, &err,
  491. RSPAMD_LUA_PARSE_ARGUMENTS_DEFAULT,
  492. "*url=O;description=S;callback=F;type=S;opaque_data=B",
  493. &map_obj, &description, &cbidx, &type, &opaque_data)) {
  494. ret = luaL_error(L, "invalid table arguments: %s", err->message);
  495. g_error_free(err);
  496. if (map_obj) {
  497. ucl_object_unref(map_obj);
  498. }
  499. return ret;
  500. }
  501. g_assert(map_obj != NULL);
  502. if (type == NULL && cbidx != -1) {
  503. type = "callback";
  504. }
  505. else if (type == NULL) {
  506. return luaL_error(L, "invalid map type");
  507. }
  508. if (strcmp(type, "callback") == 0) {
  509. map = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(*map));
  510. map->type = RSPAMD_LUA_MAP_CALLBACK;
  511. map->data.cbdata = rspamd_mempool_alloc0(cfg->cfg_pool,
  512. sizeof(*map->data.cbdata));
  513. cbdata = map->data.cbdata;
  514. cbdata->L = L;
  515. cbdata->data = NULL;
  516. cbdata->lua_map = map;
  517. cbdata->ref = cbidx;
  518. cbdata->opaque = opaque_data;
  519. if ((m = rspamd_map_add_from_ucl(cfg, map_obj, description,
  520. lua_map_read,
  521. lua_map_fin,
  522. lua_map_dtor,
  523. (void **) &map->data.cbdata,
  524. NULL, RSPAMD_MAP_DEFAULT)) == NULL) {
  525. if (cbidx != -1) {
  526. luaL_unref(L, LUA_REGISTRYINDEX, cbidx);
  527. }
  528. if (map_obj) {
  529. ucl_object_unref(map_obj);
  530. }
  531. lua_pushnil(L);
  532. return 1;
  533. }
  534. m->lua_map = map;
  535. }
  536. else if (strcmp(type, "set") == 0) {
  537. map = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(*map));
  538. map->data.hash = NULL;
  539. map->type = RSPAMD_LUA_MAP_SET;
  540. if ((m = rspamd_map_add_from_ucl(cfg, map_obj, description,
  541. rspamd_kv_list_read,
  542. rspamd_kv_list_fin,
  543. rspamd_kv_list_dtor,
  544. (void **) &map->data.hash,
  545. NULL, RSPAMD_MAP_DEFAULT)) == NULL) {
  546. lua_pushnil(L);
  547. ucl_object_unref(map_obj);
  548. return 1;
  549. }
  550. m->lua_map = map;
  551. }
  552. else if (strcmp(type, "map") == 0 || strcmp(type, "hash") == 0) {
  553. map = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(*map));
  554. map->data.hash = NULL;
  555. map->type = RSPAMD_LUA_MAP_HASH;
  556. if ((m = rspamd_map_add_from_ucl(cfg, map_obj, description,
  557. rspamd_kv_list_read,
  558. rspamd_kv_list_fin,
  559. rspamd_kv_list_dtor,
  560. (void **) &map->data.hash,
  561. NULL, RSPAMD_MAP_DEFAULT)) == NULL) {
  562. lua_pushnil(L);
  563. ucl_object_unref(map_obj);
  564. return 1;
  565. }
  566. m->lua_map = map;
  567. }
  568. else if (strcmp(type, "radix") == 0) {
  569. map = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(*map));
  570. map->data.radix = NULL;
  571. map->type = RSPAMD_LUA_MAP_RADIX;
  572. if ((m = rspamd_map_add_from_ucl(cfg, map_obj, description,
  573. rspamd_radix_read,
  574. rspamd_radix_fin,
  575. rspamd_radix_dtor,
  576. (void **) &map->data.radix,
  577. NULL, RSPAMD_MAP_DEFAULT)) == NULL) {
  578. lua_pushnil(L);
  579. ucl_object_unref(map_obj);
  580. return 1;
  581. }
  582. m->lua_map = map;
  583. }
  584. else if (strcmp(type, "regexp") == 0) {
  585. map = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(*map));
  586. map->data.re_map = NULL;
  587. map->type = RSPAMD_LUA_MAP_REGEXP;
  588. if ((m = rspamd_map_add_from_ucl(cfg, map_obj, description,
  589. rspamd_regexp_list_read_single,
  590. rspamd_regexp_list_fin,
  591. rspamd_regexp_list_dtor,
  592. (void **) &map->data.re_map,
  593. NULL, RSPAMD_MAP_DEFAULT)) == NULL) {
  594. lua_pushnil(L);
  595. ucl_object_unref(map_obj);
  596. return 1;
  597. }
  598. m->lua_map = map;
  599. }
  600. else if (strcmp(type, "regexp_multi") == 0) {
  601. map = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(*map));
  602. map->data.re_map = NULL;
  603. map->type = RSPAMD_LUA_MAP_REGEXP_MULTIPLE;
  604. if ((m = rspamd_map_add_from_ucl(cfg, map_obj, description,
  605. rspamd_regexp_list_read_multiple,
  606. rspamd_regexp_list_fin,
  607. rspamd_regexp_list_dtor,
  608. (void **) &map->data.re_map,
  609. NULL, RSPAMD_MAP_DEFAULT)) == NULL) {
  610. lua_pushnil(L);
  611. ucl_object_unref(map_obj);
  612. return 1;
  613. }
  614. m->lua_map = map;
  615. }
  616. else if (strcmp(type, "glob") == 0) {
  617. map = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(*map));
  618. map->data.re_map = NULL;
  619. map->type = RSPAMD_LUA_MAP_REGEXP;
  620. if ((m = rspamd_map_add_from_ucl(cfg, map_obj, description,
  621. rspamd_glob_list_read_single,
  622. rspamd_regexp_list_fin,
  623. rspamd_regexp_list_dtor,
  624. (void **) &map->data.re_map,
  625. NULL, RSPAMD_MAP_DEFAULT)) == NULL) {
  626. lua_pushnil(L);
  627. ucl_object_unref(map_obj);
  628. return 1;
  629. }
  630. m->lua_map = map;
  631. }
  632. else if (strcmp(type, "glob_multi") == 0) {
  633. map = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(*map));
  634. map->data.re_map = NULL;
  635. map->type = RSPAMD_LUA_MAP_REGEXP_MULTIPLE;
  636. if ((m = rspamd_map_add_from_ucl(cfg, map_obj, description,
  637. rspamd_glob_list_read_multiple,
  638. rspamd_regexp_list_fin,
  639. rspamd_regexp_list_dtor,
  640. (void **) &map->data.re_map,
  641. NULL, RSPAMD_MAP_DEFAULT)) == NULL) {
  642. lua_pushnil(L);
  643. ucl_object_unref(map_obj);
  644. return 1;
  645. }
  646. m->lua_map = map;
  647. }
  648. else if (strcmp(type, "cdb") == 0) {
  649. map = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(*map));
  650. map->data.cdb_map = NULL;
  651. map->type = RSPAMD_LUA_MAP_CDB;
  652. if ((m = rspamd_map_add_from_ucl(cfg, map_obj, description,
  653. rspamd_cdb_list_read,
  654. rspamd_cdb_list_fin,
  655. rspamd_cdb_list_dtor,
  656. (void **) &map->data.cdb_map,
  657. NULL, RSPAMD_MAP_FILE_ONLY | RSPAMD_MAP_FILE_NO_READ)) == NULL) {
  658. lua_pushnil(L);
  659. ucl_object_unref(map_obj);
  660. return 1;
  661. }
  662. m->lua_map = map;
  663. }
  664. else {
  665. ret = luaL_error(L, "invalid arguments: unknown type '%s'", type);
  666. ucl_object_unref(map_obj);
  667. return ret;
  668. }
  669. map->map = m;
  670. pmap = lua_newuserdata(L, sizeof(void *));
  671. *pmap = map;
  672. rspamd_lua_setclass(L, rspamd_map_classname, -1);
  673. }
  674. else {
  675. return luaL_error(L, "invalid arguments");
  676. }
  677. ucl_object_unref(map_obj);
  678. return 1;
  679. }
  680. int lua_config_get_maps(lua_State *L)
  681. {
  682. LUA_TRACE_POINT;
  683. struct rspamd_config *cfg = lua_check_config(L, 1);
  684. struct rspamd_lua_map *map, **pmap;
  685. struct rspamd_map *m;
  686. int i = 1;
  687. GList *cur;
  688. if (cfg) {
  689. lua_newtable(L);
  690. cur = g_list_first(cfg->maps);
  691. while (cur) {
  692. m = cur->data;
  693. if (m->lua_map) {
  694. map = m->lua_map;
  695. }
  696. else {
  697. /* Implement heuristic */
  698. map = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(*map));
  699. if (m->read_callback == rspamd_radix_read) {
  700. map->type = RSPAMD_LUA_MAP_RADIX;
  701. map->data.radix = *m->user_data;
  702. }
  703. else if (m->read_callback == rspamd_kv_list_read) {
  704. map->type = RSPAMD_LUA_MAP_HASH;
  705. map->data.hash = *m->user_data;
  706. }
  707. else {
  708. map->type = RSPAMD_LUA_MAP_UNKNOWN;
  709. }
  710. map->map = m;
  711. m->lua_map = map;
  712. }
  713. pmap = lua_newuserdata(L, sizeof(*pmap));
  714. *pmap = map;
  715. rspamd_lua_setclass(L, rspamd_map_classname, -1);
  716. lua_rawseti(L, -2, i);
  717. cur = g_list_next(cur);
  718. i++;
  719. }
  720. }
  721. else {
  722. return luaL_error(L, "invalid arguments");
  723. }
  724. return 1;
  725. }
  726. static const char *
  727. lua_map_process_string_key(lua_State *L, int pos, gsize *len)
  728. {
  729. struct rspamd_lua_text *t;
  730. if (lua_type(L, pos) == LUA_TSTRING) {
  731. return lua_tolstring(L, pos, len);
  732. }
  733. else if (lua_type(L, pos) == LUA_TUSERDATA) {
  734. t = lua_check_text(L, pos);
  735. if (t) {
  736. *len = t->len;
  737. return t->start;
  738. }
  739. }
  740. return NULL;
  741. }
  742. /* Radix and hash table functions */
  743. static int
  744. lua_map_get_key(lua_State *L)
  745. {
  746. LUA_TRACE_POINT;
  747. struct rspamd_lua_map *map = lua_check_map(L, 1);
  748. struct rspamd_radix_map_helper *radix;
  749. struct rspamd_lua_ip *addr = NULL;
  750. const char *key, *value = NULL;
  751. gpointer ud;
  752. gsize len;
  753. uint32_t key_num = 0;
  754. gboolean ret = FALSE;
  755. if (map) {
  756. if (map->type == RSPAMD_LUA_MAP_RADIX) {
  757. radix = map->data.radix;
  758. if (lua_type(L, 2) == LUA_TSTRING) {
  759. const char *addr_str;
  760. addr_str = luaL_checklstring(L, 2, &len);
  761. addr = g_alloca(sizeof(*addr));
  762. addr->addr = g_alloca(rspamd_inet_address_storage_size());
  763. if (!rspamd_parse_inet_address_ip(addr_str, len, addr->addr)) {
  764. addr = NULL;
  765. }
  766. }
  767. else if (lua_type(L, 2) == LUA_TUSERDATA) {
  768. ud = rspamd_lua_check_udata(L, 2, rspamd_ip_classname);
  769. if (ud != NULL) {
  770. addr = *((struct rspamd_lua_ip **) ud);
  771. if (addr->addr == NULL) {
  772. addr = NULL;
  773. }
  774. }
  775. else {
  776. msg_err("invalid userdata type provided, rspamd{ip} expected");
  777. }
  778. }
  779. else if (lua_type(L, 2) == LUA_TNUMBER) {
  780. key_num = luaL_checkinteger(L, 2);
  781. key_num = htonl(key_num);
  782. }
  783. if (radix) {
  784. gconstpointer p = NULL;
  785. if (addr != NULL) {
  786. if ((p = rspamd_match_radix_map_addr(radix, addr->addr)) != NULL) {
  787. ret = TRUE;
  788. }
  789. else {
  790. p = 0;
  791. }
  792. }
  793. else if (key_num != 0) {
  794. if ((p = rspamd_match_radix_map(radix,
  795. (uint8_t *) &key_num, sizeof(key_num))) != NULL) {
  796. ret = TRUE;
  797. }
  798. else {
  799. p = 0;
  800. }
  801. }
  802. value = (const char *) p;
  803. }
  804. if (ret) {
  805. lua_pushstring(L, value);
  806. return 1;
  807. }
  808. }
  809. else if (map->type == RSPAMD_LUA_MAP_SET) {
  810. key = lua_map_process_string_key(L, 2, &len);
  811. if (key && map->data.hash) {
  812. ret = rspamd_match_hash_map(map->data.hash, key, len) != NULL;
  813. }
  814. }
  815. else if (map->type == RSPAMD_LUA_MAP_REGEXP) {
  816. key = lua_map_process_string_key(L, 2, &len);
  817. if (key && map->data.re_map) {
  818. value = rspamd_match_regexp_map_single(map->data.re_map, key,
  819. len);
  820. if (value) {
  821. lua_pushstring(L, value);
  822. return 1;
  823. }
  824. }
  825. }
  826. else if (map->type == RSPAMD_LUA_MAP_REGEXP_MULTIPLE) {
  827. GPtrArray *ar;
  828. unsigned int i;
  829. const char *val;
  830. key = lua_map_process_string_key(L, 2, &len);
  831. if (key && map->data.re_map) {
  832. ar = rspamd_match_regexp_map_all(map->data.re_map, key,
  833. len);
  834. if (ar) {
  835. lua_createtable(L, ar->len, 0);
  836. PTR_ARRAY_FOREACH(ar, i, val)
  837. {
  838. lua_pushstring(L, val);
  839. lua_rawseti(L, -2, i + 1);
  840. }
  841. g_ptr_array_free(ar, TRUE);
  842. return 1;
  843. }
  844. }
  845. }
  846. else if (map->type == RSPAMD_LUA_MAP_HASH) {
  847. /* key-value map */
  848. key = lua_map_process_string_key(L, 2, &len);
  849. if (key && map->data.hash) {
  850. value = rspamd_match_hash_map(map->data.hash, key, len);
  851. }
  852. if (value) {
  853. lua_pushstring(L, value);
  854. return 1;
  855. }
  856. }
  857. else if (map->type == RSPAMD_LUA_MAP_CDB) {
  858. /* cdb map */
  859. const rspamd_ftok_t *tok = NULL;
  860. key = lua_map_process_string_key(L, 2, &len);
  861. if (key && map->data.cdb_map) {
  862. tok = rspamd_match_cdb_map(map->data.cdb_map, key, len);
  863. }
  864. if (tok) {
  865. lua_pushlstring(L, tok->begin, tok->len);
  866. return 1;
  867. }
  868. }
  869. else {
  870. /* callback map or unknown type map */
  871. lua_pushnil(L);
  872. return 1;
  873. }
  874. }
  875. else {
  876. return luaL_error(L, "invalid arguments");
  877. }
  878. lua_pushboolean(L, ret);
  879. return 1;
  880. }
  881. static gboolean
  882. lua_map_traverse_cb(gconstpointer key,
  883. gconstpointer value, gsize hits, gpointer ud)
  884. {
  885. lua_State *L = (lua_State *) ud;
  886. lua_pushstring(L, key);
  887. lua_pushinteger(L, hits);
  888. lua_settable(L, -3);
  889. return TRUE;
  890. }
  891. static int
  892. lua_map_get_stats(lua_State *L)
  893. {
  894. LUA_TRACE_POINT;
  895. struct rspamd_lua_map *map = lua_check_map(L, 1);
  896. gboolean do_reset = FALSE;
  897. if (map != NULL) {
  898. if (lua_isboolean(L, 2)) {
  899. do_reset = lua_toboolean(L, 2);
  900. }
  901. lua_createtable(L, 0, map->map->nelts);
  902. if (map->map->traverse_function) {
  903. rspamd_map_traverse(map->map, lua_map_traverse_cb, L, do_reset);
  904. }
  905. }
  906. else {
  907. return luaL_error(L, "invalid arguments");
  908. }
  909. return 1;
  910. }
  911. struct lua_map_traverse_cbdata {
  912. lua_State *L;
  913. int cbref;
  914. gboolean use_text;
  915. };
  916. static gboolean
  917. lua_map_foreach_cb(gconstpointer key, gconstpointer value, gsize _hits, gpointer ud)
  918. {
  919. struct lua_map_traverse_cbdata *cbdata = ud;
  920. lua_State *L = cbdata->L;
  921. lua_pushvalue(L, cbdata->cbref);
  922. if (cbdata->use_text) {
  923. lua_new_text(L, key, strlen(key), 0);
  924. lua_new_text(L, value, strlen(value), 0);
  925. }
  926. else {
  927. lua_pushstring(L, key);
  928. lua_pushstring(L, value);
  929. }
  930. if (lua_pcall(L, 2, 1, 0) != 0) {
  931. msg_err("call to map foreach callback failed: %s", lua_tostring(L, -1));
  932. lua_pop(L, 1); /* error */
  933. return FALSE;
  934. }
  935. else {
  936. if (lua_isboolean(L, -1)) {
  937. lua_pop(L, 2);
  938. return lua_toboolean(L, -1);
  939. }
  940. lua_pop(L, 1); /* result */
  941. }
  942. return TRUE;
  943. }
  944. static int
  945. lua_map_foreach(lua_State *L)
  946. {
  947. LUA_TRACE_POINT;
  948. struct rspamd_lua_map *map = lua_check_map(L, 1);
  949. gboolean use_text = FALSE;
  950. if (map != NULL && lua_isfunction(L, 2)) {
  951. if (lua_isboolean(L, 3)) {
  952. use_text = lua_toboolean(L, 3);
  953. }
  954. struct lua_map_traverse_cbdata cbdata;
  955. cbdata.L = L;
  956. lua_pushvalue(L, 2); /* func */
  957. cbdata.cbref = lua_gettop(L);
  958. cbdata.use_text = use_text;
  959. if (map->map->traverse_function) {
  960. rspamd_map_traverse(map->map, lua_map_foreach_cb, &cbdata, FALSE);
  961. }
  962. /* Remove callback */
  963. lua_pop(L, 1);
  964. }
  965. else {
  966. return luaL_error(L, "invalid arguments");
  967. }
  968. return 1;
  969. }
  970. static int
  971. lua_map_get_data_digest(lua_State *L)
  972. {
  973. LUA_TRACE_POINT;
  974. struct rspamd_lua_map *map = lua_check_map(L, 1);
  975. char numbuf[64];
  976. if (map != NULL) {
  977. rspamd_snprintf(numbuf, sizeof(numbuf), "%uL", map->map->digest);
  978. lua_pushstring(L, numbuf);
  979. }
  980. else {
  981. return luaL_error(L, "invalid arguments");
  982. }
  983. return 1;
  984. }
  985. static int
  986. lua_map_get_nelts(lua_State *L)
  987. {
  988. LUA_TRACE_POINT;
  989. struct rspamd_lua_map *map = lua_check_map(L, 1);
  990. if (map != NULL) {
  991. lua_pushinteger(L, map->map->nelts);
  992. }
  993. else {
  994. return luaL_error(L, "invalid arguments");
  995. }
  996. return 1;
  997. }
  998. static int
  999. lua_map_is_signed(lua_State *L)
  1000. {
  1001. LUA_TRACE_POINT;
  1002. struct rspamd_lua_map *map = lua_check_map(L, 1);
  1003. gboolean ret = FALSE;
  1004. struct rspamd_map_backend *bk;
  1005. unsigned int i;
  1006. if (map != NULL) {
  1007. if (map->map) {
  1008. for (i = 0; i < map->map->backends->len; i++) {
  1009. bk = g_ptr_array_index(map->map->backends, i);
  1010. if (bk->is_signed && bk->protocol == MAP_PROTO_FILE) {
  1011. ret = TRUE;
  1012. break;
  1013. }
  1014. }
  1015. }
  1016. }
  1017. else {
  1018. return luaL_error(L, "invalid arguments");
  1019. }
  1020. lua_pushboolean(L, ret);
  1021. return 1;
  1022. }
  1023. static int
  1024. lua_map_get_proto(lua_State *L)
  1025. {
  1026. LUA_TRACE_POINT;
  1027. struct rspamd_lua_map *map = lua_check_map(L, 1);
  1028. const char *ret = "undefined";
  1029. struct rspamd_map_backend *bk;
  1030. unsigned int i;
  1031. if (map != NULL) {
  1032. for (i = 0; i < map->map->backends->len; i++) {
  1033. bk = g_ptr_array_index(map->map->backends, i);
  1034. switch (bk->protocol) {
  1035. case MAP_PROTO_FILE:
  1036. ret = "file";
  1037. break;
  1038. case MAP_PROTO_HTTP:
  1039. ret = "http";
  1040. break;
  1041. case MAP_PROTO_HTTPS:
  1042. ret = "https";
  1043. break;
  1044. case MAP_PROTO_STATIC:
  1045. ret = "static";
  1046. break;
  1047. }
  1048. lua_pushstring(L, ret);
  1049. }
  1050. }
  1051. else {
  1052. return luaL_error(L, "invalid arguments");
  1053. }
  1054. return map->map->backends->len;
  1055. }
  1056. static int
  1057. lua_map_get_sign_key(lua_State *L)
  1058. {
  1059. LUA_TRACE_POINT;
  1060. struct rspamd_lua_map *map = lua_check_map(L, 1);
  1061. struct rspamd_map_backend *bk;
  1062. unsigned int i;
  1063. GString *ret = NULL;
  1064. if (map != NULL) {
  1065. for (i = 0; i < map->map->backends->len; i++) {
  1066. bk = g_ptr_array_index(map->map->backends, i);
  1067. if (bk->trusted_pubkey) {
  1068. ret = rspamd_pubkey_print(bk->trusted_pubkey,
  1069. RSPAMD_KEYPAIR_PUBKEY | RSPAMD_KEYPAIR_BASE32);
  1070. }
  1071. else {
  1072. ret = NULL;
  1073. }
  1074. if (ret) {
  1075. lua_pushlstring(L, ret->str, ret->len);
  1076. g_string_free(ret, TRUE);
  1077. }
  1078. else {
  1079. lua_pushnil(L);
  1080. }
  1081. }
  1082. }
  1083. else {
  1084. return luaL_error(L, "invalid arguments");
  1085. }
  1086. return map->map->backends->len;
  1087. }
  1088. static int
  1089. lua_map_set_sign_key(lua_State *L)
  1090. {
  1091. LUA_TRACE_POINT;
  1092. struct rspamd_lua_map *map = lua_check_map(L, 1);
  1093. struct rspamd_map_backend *bk;
  1094. const char *pk_str;
  1095. struct rspamd_cryptobox_pubkey *pk;
  1096. gsize len;
  1097. unsigned int i;
  1098. pk_str = lua_tolstring(L, 2, &len);
  1099. if (map && pk_str) {
  1100. pk = rspamd_pubkey_from_base32(pk_str, len, RSPAMD_KEYPAIR_SIGN,
  1101. RSPAMD_CRYPTOBOX_MODE_25519);
  1102. if (!pk) {
  1103. return luaL_error(L, "invalid pubkey string");
  1104. }
  1105. for (i = 0; i < map->map->backends->len; i++) {
  1106. bk = g_ptr_array_index(map->map->backends, i);
  1107. if (bk->trusted_pubkey) {
  1108. /* Unref old pk */
  1109. rspamd_pubkey_unref(bk->trusted_pubkey);
  1110. }
  1111. bk->trusted_pubkey = rspamd_pubkey_ref(pk);
  1112. }
  1113. rspamd_pubkey_unref(pk);
  1114. }
  1115. else {
  1116. return luaL_error(L, "invalid arguments");
  1117. }
  1118. return 0;
  1119. }
  1120. static int
  1121. lua_map_set_callback(lua_State *L)
  1122. {
  1123. LUA_TRACE_POINT;
  1124. struct rspamd_lua_map *map = lua_check_map(L, 1);
  1125. if (!map || map->type != RSPAMD_LUA_MAP_CALLBACK || map->data.cbdata == NULL) {
  1126. return luaL_error(L, "invalid map");
  1127. }
  1128. if (lua_type(L, 2) != LUA_TFUNCTION) {
  1129. return luaL_error(L, "invalid callback");
  1130. }
  1131. lua_pushvalue(L, 2);
  1132. /* Get a reference */
  1133. map->data.cbdata->ref = luaL_ref(L, LUA_REGISTRYINDEX);
  1134. return 0;
  1135. }
  1136. static int
  1137. lua_map_get_uri(lua_State *L)
  1138. {
  1139. LUA_TRACE_POINT;
  1140. struct rspamd_lua_map *map = lua_check_map(L, 1);
  1141. struct rspamd_map_backend *bk;
  1142. unsigned int i;
  1143. if (map != NULL) {
  1144. for (i = 0; i < map->map->backends->len; i++) {
  1145. bk = g_ptr_array_index(map->map->backends, i);
  1146. lua_pushstring(L, bk->uri);
  1147. }
  1148. }
  1149. else {
  1150. return luaL_error(L, "invalid arguments");
  1151. }
  1152. return map->map->backends->len;
  1153. }
  1154. struct lua_map_on_load_cbdata {
  1155. lua_State *L;
  1156. int ref;
  1157. };
  1158. static void
  1159. lua_map_on_load_dtor(gpointer p)
  1160. {
  1161. struct lua_map_on_load_cbdata *cbd = p;
  1162. luaL_unref(cbd->L, LUA_REGISTRYINDEX, cbd->ref);
  1163. g_free(cbd);
  1164. }
  1165. static void
  1166. lua_map_on_load_handler(struct rspamd_map *map, gpointer ud)
  1167. {
  1168. struct lua_map_on_load_cbdata *cbd = ud;
  1169. lua_State *L = cbd->L;
  1170. lua_rawgeti(L, LUA_REGISTRYINDEX, cbd->ref);
  1171. if (lua_pcall(L, 0, 0, 0) != 0) {
  1172. msg_err_map("call to on_load function failed: %s", lua_tostring(L, -1));
  1173. }
  1174. }
  1175. static int
  1176. lua_map_on_load(lua_State *L)
  1177. {
  1178. LUA_TRACE_POINT;
  1179. struct rspamd_lua_map *map = lua_check_map(L, 1);
  1180. if (map == NULL) {
  1181. return luaL_error(L, "invalid arguments");
  1182. }
  1183. if (lua_type(L, 2) == LUA_TFUNCTION) {
  1184. lua_pushvalue(L, 2);
  1185. struct lua_map_on_load_cbdata *cbd = g_malloc(sizeof(struct lua_map_on_load_cbdata));
  1186. cbd->L = L;
  1187. cbd->ref = luaL_ref(L, LUA_REGISTRYINDEX);
  1188. rspamd_map_set_on_load_function(map->map, lua_map_on_load_handler, cbd, lua_map_on_load_dtor);
  1189. }
  1190. else {
  1191. return luaL_error(L, "invalid callback");
  1192. }
  1193. return 0;
  1194. }
  1195. void luaopen_map(lua_State *L)
  1196. {
  1197. rspamd_lua_new_class(L, rspamd_map_classname, maplib_m);
  1198. lua_pop(L, 1);
  1199. }