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 "libmime/message.h"
18 #include "libutil/expression.h"
19 #include "src/libserver/composites/composites.h"
20 #include "libserver/cfg_file_private.h"
21 #include "libmime/lang_detection.h"
22 #include "lua/lua_map.h"
23 #include "lua/lua_thread_pool.h"
24 #include "utlist.h"
25 #include <math.h>
26
27 /***
28 * This module is used to configure rspamd and is normally available as global
29 * variable named `rspamd_config`. Unlike other modules, it is not necessary to
30 * require it before usage.
31 * @module rspamd_config
32 * @example
33 -- Register some callback symbol
34 local function foo(task)
35 -- do something
36 end
37 rspamd_config:register_symbol('SYMBOL', 1.0, foo)
38
39 -- Get configuration
40 local tab = rspamd_config:get_all_opt('module') -- get table for module's options
41 local opts = rspamd_config:get_key('options') -- get content of the specified key in rspamd configuration
42 */
43
44 /* Config file methods */
45 /***
46 * @method rspamd_config:get_module_opt(mname, optname)
47 * Returns value of specified option `optname` for a module `mname`,
48 * @param {string} mname name of module
49 * @param {string} optname option to get
50 * @return {string or table} value of the option or `nil` if option is not found
51 */
52 LUA_FUNCTION_DEF (config, get_module_opt);
53 /***
54 * @method rspamd_config:get_all_opt(mname)
55 * Returns value of all options for a module `mname`, flattening values into a single table consisting
56 * of all sections with such a name.
57 * @param {string} mname name of module
58 * @return {table} table of all options for `mname` or `nil` if a module's configuration is not found
59 */
60 LUA_FUNCTION_DEF (config, get_all_opt);
61
62 /***
63 * @method rspamd_config:get_ucl()
64 * Returns full configuration as a native Lua object (ucl to lua conversion).
65 * This method uses caching if possible.
66 * @return {table} table of all options in the configuration
67 */
68 LUA_FUNCTION_DEF (config, get_ucl);
69 /***
70 * @method rspamd_config:get_mempool()
71 * Returns static configuration memory pool.
72 * @return {mempool} [memory pool](mempool.md) object
73 */
74 LUA_FUNCTION_DEF (config, get_mempool);
75 /***
76 * @method rspamd_config:get_resolver()
77 * Returns DNS resolver.
78 * @return {dns_resolver} opaque DNS resolver pointer if any
79 */
80 LUA_FUNCTION_DEF (config, get_resolver);
81 /***
82 * @method rspamd_config:add_radix_map(mapline[, description])
83 * Creates new dynamic map of IP/mask addresses.
84 * @param {string} mapline URL for a map
85 * @param {string} description optional map description
86 * @return {map} radix tree object
87 * @example
88 local ip_map = rspamd_config:add_radix_map ('file:///path/to/file', 'my radix map')
89 ...
90 local function foo(task)
91 local ip = task:get_from_ip()
92 if ip_map:get_key(ip) then
93 return true
94 end
95 return false
96 end
97 */
98
99 /***
100 * @method rspamd_config:radix_from_config(mname, optname)
101 * Creates new embedded map of IP/mask addresses from config.
102 * @param {string} mname name of module
103 * @param {string} optname option to get
104 * @return {map} radix tree object
105 * @example
106 local ip_map = rspamd_config:radix_from_config ('mymodule', 'ips')
107 ...
108 local function foo(task)
109 local ip = task:get_from_ip()
110 if ip_map:get_key(ip) then
111 return true
112 end
113 return false
114 end
115 */
116 /***
117 * @method rspamd_config:radix_from_ucl(obj)
118 * Creates new embedded map of IP/mask addresses from object.
119 * @param {ucl} obj object
120 * @return {map} radix tree object
121 */
122 /***
123 * @method rspamd_config:add_hash_map(mapline[, description])
124 * Creates new dynamic map string objects.
125 * @param {string} mapline URL for a map
126 * @param {string} description optional map description
127 * @return {map} hash set object
128 * @example
129 local hash_map = rspamd_config:add_hash_map ('file:///path/to/file', 'my hash map')
130 ...
131 local function foo(task)
132 local from = task:get_from()
133 if hash_map:get_key(from['user']) then
134 return true
135 end
136 return false
137 end
138 */
139 /***
140 * @method rspamd_config:add_kv_map(mapline[, description])
141 * Creates new dynamic map of key/values associations.
142 * @param {string} mapline URL for a map
143 * @param {string} description optional map description
144 * @return {map} hash table object
145 * @example
146 local kv_map = rspamd_config:add_kv_map ('file:///path/to/file', 'my kv map')
147 ...
148 local function foo(task)
149 local from = task:get_from()
150 if from then
151 local value = kv_map:get_key(from['user'])
152 if value then
153 return true,value
154 end
155 end
156 return false
157 end
158 */
159 /***
160 * @method rspamd_config:add_map({args})
161 * Creates new dynamic map according to the attributes passed.
162 *
163 * - `type`: type of map to be created, can be one of the following set:
164 * + `set`: set of strings
165 * + `radix`: map of IP addresses to strings
166 * + `map`: map of strings to strings
167 * + `regexp`: map of regexps to strings
168 * + `callback`: map processed by lua callback
169 * - `url`: url to load map from
170 * - `description`: map's description
171 * - `callback`: lua callback for the map
172 *
173 * @return {map} `true` if map has been added
174 * @example
175
176 local str = ''
177 local function process_map(in)
178 str = in
179 end
180
181 rspamd_config:add_map('http://example.com/map', "settings map", process_map)
182 */
183 /***
184 * @method rspamd_config:get_maps()
185 * Get all maps defined as an array of rspamd{map} objects
186 *
187 * @return {table|rspamd{map}}
188 */
189 /***
190 * @method rspamd_config:get_classifier(name)
191 * Returns classifier config.
192 * @param {string} name name of classifier (e.g. `bayes`)
193 * @return {classifier} classifier object or `nil`
194 */
195 LUA_FUNCTION_DEF (config, get_classifier);
196 /***
197 * @method rspamd_config:register_symbol(table)
198 * Register symbol of a specified type in rspamd. This function accepts table of arguments:
199 *
200 * - `name`: name of symbol (can be missing for callback symbols)
201 * - `callback`: function to be called for symbol's check (can be absent for virtual symbols)
202 * - `weight`: weight of symbol (should normally be 1 or missing)
203 * - `priority`: priority of symbol (normally 0 or missing)
204 * - `type`: type of symbol:
205 * + `normal`: executed after prefilters, according to dependency graph or in undefined order
206 * + `callback`: a check that merely inserts virtual symbols
207 * + `connfilter`: executed early; before message body is available
208 * + `idempotent`: cannot change result in any way; executed last
209 * + `postfilter`: executed after most other checks
210 * + `prefilter`: executed before most other checks
211 * + `virtual`: a symbol inserted by its parent check
212 * - `flags`: various flags split by commas or spaces:
213 * + `nice` if symbol can produce negative score;
214 * + `empty` if symbol can be called for empty messages
215 * + `skip` if symbol should be skipped now
216 * + `nostat` if symbol should be excluded from stat tokens
217 * + `trivial` symbol is trivial (e.g. no network requests)
218 * + `explicit_disable` requires explicit disabling (e.g. via settings)
219 * + `ignore_passthrough` executed even if passthrough result has been set
220 * - `parent`: id of parent symbol (useful for virtual symbols)
221 *
222 * @return {number} id of symbol registered
223 */
224 LUA_FUNCTION_DEF (config, register_symbol);
225 /***
226 * @method rspamd_config:register_symbols(callback, [weight], callback_name, [, symbol, ...])
227 * Register callback function to be called for a set of symbols with initial weight.
228 * @param {function} callback callback function to be called for a specified symbol
229 * @param {number} weight initial weight of symbol (can be less than zero to specify non-spam symbols)
230 * @param {string} callback_name symbolic name of callback
231 * @param {list of strings} symbol list of symbols registered by this function
232 */
233 LUA_FUNCTION_DEF (config, register_symbols);
234 /***
235 * @method rspamd_config:register_virtual_symbol(name, weight,)
236 * Register virtual symbol that is not associated with any callback.
237 *
238 * **This method is deprecated and should not be used in newly written code **
239 * @param {string} virtual name symbol's name
240 * @param {number} weight initial weight of symbol (can be less than zero to specify non-spam symbols)
241 */
242 LUA_FUNCTION_DEF (config, register_virtual_symbol);
243 /***
244 * @method rspamd_config:register_callback_symbol(name, weight, callback)
245 * Register callback function to be called for a specified symbol with initial weight. Symbol itself is
246 * not registered in the metric and is not intended to be visible by a user.
247 *
248 * **This method is deprecated and should not be used in newly written code **
249 * @param {string} name symbol's name (just for unique id purposes)
250 * @param {number} weight initial weight of symbol (can be less than zero to specify non-spam symbols)
251 * @param {function} callback callback function to be called for a specified symbol
252 */
253 LUA_FUNCTION_DEF (config, register_callback_symbol);
254 LUA_FUNCTION_DEF (config, register_callback_symbol_priority);
255
256 /***
257 * @method rspamd_config:register_dependency(id|name, depname)
258 * Create a dependency on symbol identified by name for symbol identified by ID or name.
259 * This affects order of checks only (a symbol is still checked if its dependencys are disabled).
260 * @param {number|string} id id or name of source (numeric id is returned by all register_*_symbol)
261 * @param {string} depname dependency name
262 * @example
263 local function cb(task)
264 ...
265 end
266
267 local id = rspamd_config:register_symbol('SYM', 1.0, cb)
268 rspamd_config:register_dependency(id, 'OTHER_SYM')
269 -- Alternative form
270 -- Symbol MY_RULE needs result from SPF_CHECK
271 rspamd_config:register_dependency('MY_RULE', 'SPF_CHECK')
272 */
273 LUA_FUNCTION_DEF (config, register_dependency);
274
275 /***
276 * @method rspamd_config:get_symbol_flags(name)
277 * Returns symbol flags
278 * @param {string} name symbols's name
279 * @return {table|string} list of flags for symbol or nil
280 */
281 LUA_FUNCTION_DEF (config, get_symbol_flags);
282
283 /***
284 * @method rspamd_config:add_symbol_flags(name, flags)
285 * Adds flags to a symbol
286 * @param {string} name symbols's name
287 * @param {table|string} flags flags to add
288 * @return {table|string} new set of flags
289 */
290 LUA_FUNCTION_DEF (config, add_symbol_flags);
291
292 /**
293 * @method rspamd_config:register_re_selector(name, selector_str, [delimiter, [flatten]])
294 * Registers selector with the specific name to use in regular expressions in form
295 * name=/re/$ or name=/re/{selector}
296 * @param {string} name name of the selector
297 * @param {string} selector_str selector definition
298 * @param {string} delimiter delimiter to use when joining strings if flatten is false
299 * @param {bool} flatten if true then selector will return a table of captures instead of a single string
300 * @return true if selector has been registered
301 */
302 LUA_FUNCTION_DEF (config, register_re_selector);
303
304 /**
305 * @method rspamd_config:set_symbol({table})
306 * Sets the value of a specified symbol in a metric. This function accepts table with the following elements:
307 *
308 * - `name`: name of symbol (string)
309 * - `score`: score for symbol (number)
310 * - `metric`: name of metric (string, optional)
311 * - `description`: description of symbol (string, optional)
312 * - `group`: name of group for symbol (string, optional)
313 * - `one_shot`: turn off multiple hits for a symbol (boolean, optional)
314 * - `one_param`: turn off multiple options for a symbol (boolean, optional)
315 * - `flags`: comma separated string of flags:
316 * + `ignore`: do not strictly check validity of symbol and corresponding rule
317 * + `one_shot`: turn off multiple hits for a symbol
318 * + `one_param`: allow only one parameter for a symbol
319 * - `priority`: priority of symbol's definition
320 */
321 LUA_FUNCTION_DEF (config, set_metric_symbol);
322
323 /**
324 * @method rspamd_config:set_action({table})
325 * Sets the score of a specified action in a metric. This function accepts table with the following elements:
326 *
327 * - `action`: name of action (string)
328 * - `score`: score for action (number)
329 * - `metric`: name of metric (string, optional)
330 * - `priority`: priority of action's definition
331 */
332 LUA_FUNCTION_DEF (config, set_metric_action);
333
334 /**
335 * @method rspamd_config:get_symbol(name)
336 * Gets metric data for a specific symbol identified by `name`:
337 *
338 * - `score`: score for symbol (number)
339 * - `description`: description of symbol (string, optional)
340 * - `group`: name of group for symbol (string, optional)
341 * - `one_shot`: turn off multiple hits for a symbol (boolean, optional)
342 * - `flags`: comma separated string of flags:
343 * + `ignore`: do not strictly check validity of symbol and corresponding rule
344 * + `one_shot`: turn off multiple hits for a symbol
345 *
346 * @param {string} name name of symbol
347 * @return {table} symbol's definition or nil in case of undefined symbol
348 */
349 LUA_FUNCTION_DEF (config, get_metric_symbol);
350
351 /**
352 * @method rspamd_config:get_action(name)
353 * Gets data for a specific action in config. This function returns number reperesenting action's score
354 *
355 * @param {string} name name of action
356 * @return {number} action's score or nil in case of undefined score or action
357 */
358 LUA_FUNCTION_DEF (config, get_metric_action);
359
360 /**
361 * @method rspamd_config:get_all_actions()
362 * Gets data for all action in config
363 * @return {table|str->num} action's score or nil in case of undefined score or action
364 */
365 LUA_FUNCTION_DEF (config, get_all_actions);
366
367 /**
368 * @method rspamd_config:add_composite(name, expression)
369 * @param {string} name name of composite symbol
370 * @param {string} expression symbolic expression of the composite rule
371 * @return {bool} true if a composite has been added successfully
372 */
373 LUA_FUNCTION_DEF (config, add_composite);
374 /***
375 * @method rspamd_config:register_pre_filter(callback[, order])
376 * Register function to be called prior to symbols processing.
377 * @param {function} callback callback function
378 * @param {number} order filters are called from lower orders to higher orders, order is equal to 0 by default
379 * @example
380 local function check_function(task)
381 -- It is possible to manipulate the task object here: set settings, set pre-action and so on
382 ...
383 end
384
385 rspamd_config:register_pre_filter(check_function)
386 */
387 LUA_FUNCTION_DEF (config, register_pre_filter);
388 /***
389 * @method rspamd_config:register_post_filter(callback[, order])
390 * Register function to be called after symbols are processed.
391 *
392 * @param {function} callback callback function
393 * @param {number} order filters are called from lower orders to higher orders, order is equal to 0 by default
394 */
395 LUA_FUNCTION_DEF (config, register_post_filter);
396 /* XXX: obsoleted */
397 LUA_FUNCTION_DEF (config, register_module_option);
398 /* XXX: not needed now */
399 LUA_FUNCTION_DEF (config, get_api_version);
400 /***
401 * @method rspamd_config:get_key(name)
402 * Returns configuration section with the specified `name`.
403 * @param {string} name name of config section
404 * @return {variant} specific value of section
405 * @example
406
407 local set_section = rspamd_config:get_key("settings")
408 if type(set_section) == "string" then
409 -- Just a map of ucl
410 if rspamd_config:add_map(set_section, "settings map", process_settings_map) then
411 rspamd_config:register_pre_filter(check_settings)
412 end
413 elseif type(set_section) == "table" then
414 if process_settings_table(set_section) then
415 rspamd_config:register_pre_filter(check_settings)
416 end
417 end
418 */
419 LUA_FUNCTION_DEF (config, get_key);
420
421 /***
422 * @method rspamd_config:add_condition(symbol, condition)
423 * Adds condition callback for specified symbol
424 * @param {string} symbol symbol's name
425 * @param {function} condition condition callback
426 * @return {boolean} true if condition has been added
427 * @example
428
429 rspamd_config:add_condition('FUZZY_DENIED', function(task)
430 if some_map:find_key(task:get_from()) then return false end
431 return true
432 end)
433 */
434 LUA_FUNCTION_DEF (config, add_condition);
435
436 /***
437 * @method rspamd_config:enable_symbol(symbol)
438 * Enables execution for the specified symbol
439 * @param {string} symbol symbol's name
440 */
441 LUA_FUNCTION_DEF (config, enable_symbol);
442
443 /***
444 * @method rspamd_config:disable_symbol(symbol, [disable_parent=true])
445 * Disables execution for the specified symbol
446 * @param {string} symbol symbol's name
447 * @param {boolean} disable_parent if true then disable parent execution in case of a virtual symbol
448 */
449 LUA_FUNCTION_DEF (config, disable_symbol);
450
451 /***
452 * @method rspamd_config:get_symbol_parent(symbol)
453 * Returns a parent symbol for specific symbol (or symbol itself if top level)
454 * @param {string} symbol symbol's name
455 */
456 LUA_FUNCTION_DEF (config, get_symbol_parent);
457
458 /***
459 * @method rspamd_config:get_group_symbols(group)
460 * Returns list of symbols for a specific group
461 * @param {string} group group's name
462 * @available 2.0+
463 * @return {list|string} list of all symbols in a specific group
464 */
465 LUA_FUNCTION_DEF (config, get_group_symbols);
466
467 /***
468 * @method rspamd_config:get_groups([need_private])
469 * Returns list of all groups defined
470 * @param {boolean} need_private optional flag to include private groups
471 * @available 2.3+
472 * @return {list|table} list of all groups
473 */
474 LUA_FUNCTION_DEF (config, get_groups);
475
476 /***
477 * @method rspamd_config:register_settings_id(name, symbols_enabled, symbols_disabled)
478 * Register new static settings id in config
479 * @param {string} name id name (not numeric!)
480 * @param {map|string->string} symbols_enabled map from symbol's name to boolean (currently)
481 * @param {map|string->string} symbols_disabled map from symbol's name to boolean (currently)
482 * @available 2.0+
483 */
484 LUA_FUNCTION_DEF (config, register_settings_id);
485
486 /***
487 * @method rspamd_config:__newindex(name, callback)
488 * This metamethod is called if new indicies are added to the `rspamd_config` object.
489 * Technically, it is the equivalent of @see rspamd_config:register_symbol where `weight` is 1.0.
490 * There is also table form invocation that allows to control more things:
491 *
492 * - `callback`: has the same meaning and acts as function of task
493 * - `score`: default score for a symbol
494 * - `group`: default group for a symbol
495 * - `description`: default symbol's description
496 * - `priority`: additional priority value
497 * - `one_shot`: default value for one shot attribute
498 * - `condition`: function of task that can enable or disable this specific rule's execution
499 * @param {string} name index name
500 * @param {function/table} callback callback to be called
501 * @return {number} id of the new symbol added
502 * @example
503 rspamd_config.R_EMPTY_IMAGE = function (task)
504 parts = task:get_text_parts()
505 if parts then
506 for _,part in ipairs(parts) do
507 if part:is_empty() then
508 images = task:get_images()
509 if images then
510 -- Symbol `R_EMPTY_IMAGE` is inserted
511 return true
512 end
513 return false
514 end
515 end
516 end
517 return false
518 end
519
520 rspamd_config.SYMBOL = {
521 callback = function(task)
522 ...
523 end,
524 score = 5.1,
525 description = 'sample symbol',
526 group = 'sample symbols',
527 condition = function(task)
528 if task:get_from()[1]['addr'] == 'user@example.com' then
529 return false
530 end
531 return true
532 end
533 }
534 */
535 LUA_FUNCTION_DEF (config, newindex);
536
537 /***
538 * @method rspamd_config:register_regexp(params)
539 * Registers new re for further cached usage
540 * Params is the table with the following fields (mandatory fields are marked with `*`):
541 * - `re`* : regular expression object
542 * - `type`*: type of regular expression:
543 * + `mime`: mime regexp
544 * + `rawmime`: raw mime regexp
545 * + `header`: header regexp
546 * + `rawheader`: raw header expression
547 * + `body`: raw body regexp
548 * + `url`: url regexp
549 * - `header`: for header and rawheader regexp means the name of header
550 * - `pcre_only`: flag regexp as pcre only regexp
551 */
552 LUA_FUNCTION_DEF (config, register_regexp);
553
554 /***
555 * @method rspamd_config:replace_regexp(params)
556 * Replaces regexp with a new one
557 * Params is the table with the following fields (mandatory fields are marked with `*`):
558 * - `old_re`* : old regular expression object (must be in the cache)
559 * - `new_re`* : old regular expression object (must not be in the cache)
560 */
561 LUA_FUNCTION_DEF (config, replace_regexp);
562
563 /***
564 * @method rspamd_config:register_worker_script(worker_type, script)
565 * Registers the following script for workers of a specified type. The exact type
566 * of script function depends on worker type
567 * @param {string} worker_type worker type (e.g. "normal")
568 * @param {function} script script for a worker
569 * @return {boolean} `true` if a script has been registered
570 */
571 LUA_FUNCTION_DEF (config, register_worker_script);
572
573 /***
574 * @method rspamd_config:add_on_load(function(cfg, ev_base, worker) ... end)
575 * Registers the following script to be executed when configuration is completely loaded
576 * and the worker is already started (forked)
577 * @param {function} script function to be executed
578 * @example
579 rspamd_config:add_on_load(function(cfg, ev_base, worker)
580 rspamd_config:add_periodic(ev_base, 1.0, function(cfg, ev_base)
581 local logger = require "rspamd_logger"
582 logger.infox(cfg, "periodic function in worker %s", worker:get_name())
583 return true
584 end)
585 end)
586 */
587 LUA_FUNCTION_DEF (config, add_on_load);
588
589 /***
590 * @method rspamd_config:add_periodic(event_base, timeout, function(cfg, ev_base) ... end, [jitter = false])
591 * Registers function to be periodically executed by Rspamd
592 * @param {ev_base} event_base event base that is needed for async events
593 * @param {number} timeout time in seconds (could be fractional)
594 * @param {function} script function to be executed
595 * @param {boolean} jitter `true` if timeout jittering is needed
596 * @example
597 rspamd_config:add_on_load(function(cfg, ev_base)
598 rspamd_config:add_periodic(ev_base, 1.0, function(cfg, ev_base)
599 local logger = require "rspamd_logger"
600 logger.infox(cfg, "periodic function")
601 return true -- if return numeric, a new interval is set. if return false, then the periodic event is removed
602 end)
603 end)
604 */
605 LUA_FUNCTION_DEF (config, add_periodic);
606
607 /***
608 * @method rspamd_config:add_post_init(function(cfg) ... end)
609 * Registers the following script to be executed when configuration is completely loaded
610 * @available 2.0+
611 * @param {function} script function to be executed
612 */
613 LUA_FUNCTION_DEF (config, add_post_init);
614
615 /***
616 * @method rspamd_config:add_config_unload(function(cfg) ... end)
617 * Registers the following script to be executed when configuration is unloaded
618 * @available 2.0+
619 * @param {function} script function to be executed
620 */
621 LUA_FUNCTION_DEF (config, add_config_unload);
622
623 /***
624 * @method rspamd_config:get_symbols_count()
625 * Returns number of symbols registered in rspamd configuration
626 * @return {number} number of symbols registered in the configuration
627 */
628 LUA_FUNCTION_DEF (config, get_symbols_count);
629
630 /***
631 * @method rspamd_config:get_symbols_cksum()
632 * Returns checksum for all symbols in the cache
633 * @return {int64} boxed value of the 64 bit checksum
634 */
635 LUA_FUNCTION_DEF (config, get_symbols_cksum);
636
637 /***
638 * @method rspamd_config:get_symbols_counters()
639 * Returns table of all counters in the cache (weights, frequencies etc)
640 * @return {table|tables} all symbols indexed by name
641 */
642 LUA_FUNCTION_DEF (config, get_symbols_counters);
643
644 /***
645 * @method rspamd_config:get_symbols()
646 * Returns table of all scores defined in config. From version 2.0 returns table:
647 * - name
648 * - score
649 * - flags (e.g. `ignore` or `oneparam`)
650 * - nshots (== maxhits)
651 * - group - main group
652 * - groups - array of all groups
653 * @available 2.0+
654 * @return {table|tables} all symbols indexed by name
655 */
656 LUA_FUNCTION_DEF (config, get_symbols);
657
658 /***
659 * @method rspamd_config:get_symbol_callback(name)
660 * Returns callback function for the specified symbol if it is a lua registered callback
661 * @return {function} callback function or nil
662 */
663 LUA_FUNCTION_DEF (config, get_symbol_callback);
664
665 /***
666 * @method rspamd_config:get_symbol_stat(name)
667 * Returns table with statistics for a specific symbol:
668 * - `frequency`: frequency for symbol's hits
669 * - `stddev`: standard deviation of `frequency`
670 * - `time`: average time in seconds (floating point)
671 * - `count`: total number of hits
672 * @return {table} symbol stats
673 */
674 LUA_FUNCTION_DEF (config, get_symbol_stat);
675
676 /***
677 * @method rspamd_config:set_symbol_callback(name, callback)
678 * Sets callback for the specified symbol
679 * @return {boolean} true if function has been replaced
680 */
681 LUA_FUNCTION_DEF (config, set_symbol_callback);
682
683 /***
684 * @method rspamd_config:register_finish_script(callback)
685 * Adds new callback that is called on worker process termination when all
686 * tasks pending are processed
687 *
688 * @param callback {function} a function with one argument (rspamd_task)
689 */
690 LUA_FUNCTION_DEF (config, register_finish_script);
691
692 /***
693 * @method rspamd_config:register_monitored(url, type, [{params}])
694 * Registers monitored resource to watch its availability. Supported types:
695 *
696 * - `dns`: DNS monitored object
697 *
698 * Params are optional table specific for each type. For DNS it supports the
699 * following options:
700 *
701 * - `prefix`: prefix to add before making request
702 * - `type`: type of request (e.g. 'a' or 'txt')
703 * - `ipnet`: array of ip/networks to expect on reply
704 * - `rcode`: expected return code (e.g. `nxdomain`)
705 *
706 * Returned object has the following methods:
707 *
708 * - `alive`: returns `true` if monitored resource is alive
709 * - `offline`: returns number of seconds of the current offline period (or 0 if alive)
710 * - `total_offline`: returns number of seconds of the overall offline
711 * - `latency`: returns the current average latency in seconds (or 0 if offline)
712 *
713 * @param {string} url resource to monitor
714 * @param {string} type type of monitoring
715 * @param {table} opts optional parameters
716 * @return {rspamd_monitored} rspamd monitored object
717 */
718 LUA_FUNCTION_DEF (config, register_monitored);
719
720 /***
721 * @method rspamd_config:add_doc(path, option, doc_string, [{params}])
722 * Adds new documentation string for an option `option` at path `path`
723 * Options defines optional params, such as:
724 *
725 * - `default`: default option value
726 * - `type`: type of an option (`string`, `number`, `object`, `array` etc)
727 * - `reqired`: if an option is required
728 *
729 * @param {string} path documentation path (e.g. module name)
730 * @param {string} option name of the option
731 * @param {string} doc_string documentation string
732 * @param {table} params optional parameters
733 */
734 LUA_FUNCTION_DEF (config, add_doc);
735
736 /***
737 * @method rspamd_config:add_example(path, option, doc_string, example)
738 * Adds new documentation
739 *
740 * @param {string} path documentation path (e.g. module name or nil for top)
741 * @param {string} option name of the option
742 * @param {string} doc_string documentation string
743 * @param {string} example example in ucl format, comments are also parsed
744 */
745 LUA_FUNCTION_DEF (config, add_example);
746
747 /***
748 * @method rspamd_config:set_peak_cb(function)
749 * Sets a function that will be called when frequency of some symbol goes out of
750 * stddev * 2 over the last period of refreshment.
751 *
752 * @example
753 rspamd_config:set_peak_cb(function(ev_base, sym, mean, stddev, value, error)
754 -- ev_base: event base for async events (e.g. redis)
755 -- sym: symbol's name
756 -- mean: mean frequency value
757 -- stddev: standard deviation of frequency
758 -- value: current frequency value
759 -- error: squared error
760 local logger = require "rspamd_logger"
761 logger.infox(rspamd_config, "symbol %s has changed frequency significantly: %s(%s) over %s(%s)",
762 sym, value, error, mean, stddev)
763 end)
764 */
765 LUA_FUNCTION_DEF (config, set_peak_cb);
766 /***
767 * @method rspamd_config:get_cpu_flags()
768 * Returns architecture dependent flags supported by the CPU
769 * Currently, only x86 flags are supported:
770 * - 'ssse3'
771 * - 'sse42'
772 * - 'avx'
773 * - 'avx2'
774 * @return {table} flag -> true table
775 */
776 LUA_FUNCTION_DEF (config, get_cpu_flags);
777
778 /***
779 * @method rspamd_config:has_torch()
780 * Returns true if Rspamd is compiled with torch support and the runtime CPU
781 * supports sse4.2 required for torch.
782 * @return {boolean} true if torch is compiled and supported
783 */
784 LUA_FUNCTION_DEF (config, has_torch);
785
786 /***
787 * @method rspamd_config:experimental_enabled()
788 * Returns true if experimental plugins are enabled
789 * @return {boolean} true if experimental plugins are enabled
790 */
791 LUA_FUNCTION_DEF (config, experimental_enabled);
792
793 /***
794 * @method rspamd_config:load_ucl(filename[, include_trace])
795 * Loads config from the UCL file (but does not perform parsing using rcl)
796 * @param {string} filename file to load
797 * @return true or false + error message
798 */
799 LUA_FUNCTION_DEF (config, load_ucl);
800
801 /***
802 * @method rspamd_config:parse_rcl([skip_sections])
803 * Parses RCL using loaded ucl file
804 * @param {table|string} sections to skip
805 * @return true or false + error message
806 */
807 LUA_FUNCTION_DEF (config, parse_rcl);
808
809 /***
810 * @method rspamd_config:init_modules()
811 * Initialize lua and internal modules
812 * @return true or false
813 */
814 LUA_FUNCTION_DEF (config, init_modules);
815
816 /***
817 * @method rspamd_config:init_subsystem(str)
818 * Initialize config subsystem from a comma separated list:
819 * - `modules` - init modules
820 * - `langdet` - language detector
821 * - `dns` - DNS resolver
822 * - TODO: add more
823 */
824 LUA_FUNCTION_DEF (config, init_subsystem);
825
826 /***
827 * @method rspamd_config:get_tld_path()
828 * Returns path to TLD file
829 * @return {string} path to tld file
830 */
831 LUA_FUNCTION_DEF (config, get_tld_path);
832
833 /***
834 * @method rspamd_config:get_dns_max_requests()
835 * Returns limit of DNS requests per task
836 * @return {number} number of dns requests allowed
837 */
838 LUA_FUNCTION_DEF (config, get_dns_max_requests);
839
840 static const struct luaL_reg configlib_m[] = {
841 LUA_INTERFACE_DEF (config, get_module_opt),
842 LUA_INTERFACE_DEF (config, get_mempool),
843 LUA_INTERFACE_DEF (config, get_resolver),
844 LUA_INTERFACE_DEF (config, get_all_opt),
845 LUA_INTERFACE_DEF (config, get_ucl),
846 LUA_INTERFACE_DEF (config, add_radix_map),
847 LUA_INTERFACE_DEF (config, radix_from_config),
848 LUA_INTERFACE_DEF (config, radix_from_ucl),
849 LUA_INTERFACE_DEF (config, add_hash_map),
850 LUA_INTERFACE_DEF (config, add_kv_map),
851 LUA_INTERFACE_DEF (config, add_map),
852 LUA_INTERFACE_DEF (config, get_maps),
853 LUA_INTERFACE_DEF (config, get_classifier),
854 LUA_INTERFACE_DEF (config, register_symbol),
855 LUA_INTERFACE_DEF (config, register_symbols),
856 LUA_INTERFACE_DEF (config, register_virtual_symbol),
857 LUA_INTERFACE_DEF (config, register_callback_symbol),
858 LUA_INTERFACE_DEF (config, register_callback_symbol_priority),
859 LUA_INTERFACE_DEF (config, register_dependency),
860 LUA_INTERFACE_DEF (config, register_settings_id),
861 LUA_INTERFACE_DEF (config, get_symbol_flags),
862 LUA_INTERFACE_DEF (config, add_symbol_flags),
863 LUA_INTERFACE_DEF (config, set_metric_symbol),
864 {"set_symbol", lua_config_set_metric_symbol},
865 LUA_INTERFACE_DEF (config, set_metric_action),
866 {"set_action", lua_config_set_metric_action},
867 LUA_INTERFACE_DEF (config, get_metric_symbol),
868 {"get_symbol", lua_config_get_metric_symbol},
869 LUA_INTERFACE_DEF (config, get_metric_action),
870 {"get_action", lua_config_get_metric_action},
871 LUA_INTERFACE_DEF (config, get_all_actions),
872 LUA_INTERFACE_DEF (config, add_composite),
873 LUA_INTERFACE_DEF (config, register_module_option),
874 LUA_INTERFACE_DEF (config, register_pre_filter),
875 LUA_INTERFACE_DEF (config, register_post_filter),
876 LUA_INTERFACE_DEF (config, get_api_version),
877 LUA_INTERFACE_DEF (config, get_key),
878 LUA_INTERFACE_DEF (config, add_condition),
879 LUA_INTERFACE_DEF (config, enable_symbol),
880 LUA_INTERFACE_DEF (config, disable_symbol),
881 LUA_INTERFACE_DEF (config, register_regexp),
882 LUA_INTERFACE_DEF (config, replace_regexp),
883 LUA_INTERFACE_DEF (config, register_worker_script),
884 LUA_INTERFACE_DEF (config, register_re_selector),
885 LUA_INTERFACE_DEF (config, add_on_load),
886 LUA_INTERFACE_DEF (config, add_periodic),
887 LUA_INTERFACE_DEF (config, add_post_init),
888 LUA_INTERFACE_DEF (config, add_config_unload),
889 LUA_INTERFACE_DEF (config, get_symbols_count),
890 LUA_INTERFACE_DEF (config, get_symbols_cksum),
891 LUA_INTERFACE_DEF (config, get_symbols_counters),
892 {"get_symbols_scores", lua_config_get_symbols},
893 LUA_INTERFACE_DEF (config, get_symbols),
894 LUA_INTERFACE_DEF (config, get_groups),
895 LUA_INTERFACE_DEF (config, get_symbol_callback),
896 LUA_INTERFACE_DEF (config, set_symbol_callback),
897 LUA_INTERFACE_DEF (config, get_symbol_stat),
898 LUA_INTERFACE_DEF (config, get_symbol_parent),
899 LUA_INTERFACE_DEF (config, get_group_symbols),
900 LUA_INTERFACE_DEF (config, register_finish_script),
901 LUA_INTERFACE_DEF (config, register_monitored),
902 LUA_INTERFACE_DEF (config, add_doc),
903 LUA_INTERFACE_DEF (config, add_example),
904 LUA_INTERFACE_DEF (config, set_peak_cb),
905 LUA_INTERFACE_DEF (config, get_cpu_flags),
906 LUA_INTERFACE_DEF (config, has_torch),
907 LUA_INTERFACE_DEF (config, experimental_enabled),
908 LUA_INTERFACE_DEF (config, load_ucl),
909 LUA_INTERFACE_DEF (config, parse_rcl),
910 LUA_INTERFACE_DEF (config, init_modules),
911 LUA_INTERFACE_DEF (config, init_subsystem),
912 LUA_INTERFACE_DEF (config, get_tld_path),
913 LUA_INTERFACE_DEF (config, get_dns_max_requests),
914 {"__tostring", rspamd_lua_class_tostring},
915 {"__newindex", lua_config_newindex},
916 {NULL, NULL}
917 };
918
919 LUA_FUNCTION_DEF (monitored, alive);
920 LUA_FUNCTION_DEF (monitored, latency);
921 LUA_FUNCTION_DEF (monitored, offline);
922 LUA_FUNCTION_DEF (monitored, total_offline);
923
924 static const struct luaL_reg monitoredlib_m[] = {
925 LUA_INTERFACE_DEF (monitored, alive),
926 LUA_INTERFACE_DEF (monitored, latency),
927 LUA_INTERFACE_DEF (monitored, offline),
928 LUA_INTERFACE_DEF (monitored, total_offline),
929 {"__tostring", rspamd_lua_class_tostring},
930 {NULL, NULL}
931 };
932
933 static const guint64 rspamd_lua_callback_magic = 0x32c118af1e3263c7ULL;
934
935 struct rspamd_config *
lua_check_config(lua_State * L,gint pos)936 lua_check_config (lua_State * L, gint pos)
937 {
938 void *ud = rspamd_lua_check_udata (L, pos, "rspamd{config}");
939 luaL_argcheck (L, ud != NULL, pos, "'config' expected");
940 return ud ? *((struct rspamd_config **)ud) : NULL;
941 }
942
943 static struct rspamd_monitored *
lua_check_monitored(lua_State * L,gint pos)944 lua_check_monitored (lua_State * L, gint pos)
945 {
946 void *ud = rspamd_lua_check_udata (L, pos, "rspamd{monitored}");
947 luaL_argcheck (L, ud != NULL, pos, "'monitored' expected");
948 return ud ? *((struct rspamd_monitored **)ud) : NULL;
949 }
950
951 /*** Config functions ***/
952 static gint
lua_config_get_api_version(lua_State * L)953 lua_config_get_api_version (lua_State *L)
954 {
955 msg_warn ("get_api_version is deprecated, do not use it");
956 lua_pushnumber (L, 100);
957
958 return 1;
959 }
960
961 static gint
lua_config_get_module_opt(lua_State * L)962 lua_config_get_module_opt (lua_State * L)
963 {
964 LUA_TRACE_POINT;
965 struct rspamd_config *cfg = lua_check_config (L, 1);
966 const gchar *mname, *optname;
967 const ucl_object_t *obj;
968
969 if (cfg) {
970 mname = luaL_checkstring (L, 2);
971 optname = luaL_checkstring (L, 3);
972
973 if (mname && optname) {
974 obj = rspamd_config_get_module_opt (cfg, mname, optname);
975 if (obj) {
976 return ucl_object_push_lua (L, obj, TRUE);
977 }
978 }
979 }
980 lua_pushnil (L);
981 return 1;
982 }
983
984 static int
lua_config_get_mempool(lua_State * L)985 lua_config_get_mempool (lua_State * L)
986 {
987 LUA_TRACE_POINT;
988 rspamd_mempool_t **ppool;
989 struct rspamd_config *cfg = lua_check_config (L, 1);
990
991 if (cfg != NULL) {
992 ppool = lua_newuserdata (L, sizeof (rspamd_mempool_t *));
993 rspamd_lua_setclass (L, "rspamd{mempool}", -1);
994 *ppool = cfg->cfg_pool;
995 }
996 else {
997 lua_pushnil (L);
998 }
999 return 1;
1000 }
1001
1002 static int
lua_config_get_resolver(lua_State * L)1003 lua_config_get_resolver (lua_State * L)
1004 {
1005 LUA_TRACE_POINT;
1006 struct rspamd_dns_resolver **pres;
1007 struct rspamd_config *cfg = lua_check_config (L, 1);
1008
1009 if (cfg != NULL && cfg->dns_resolver) {
1010 pres = lua_newuserdata (L, sizeof (*pres));
1011 rspamd_lua_setclass (L, "rspamd{resolver}", -1);
1012 *pres = cfg->dns_resolver;
1013 }
1014 else {
1015 lua_pushnil (L);
1016 }
1017
1018 return 1;
1019 }
1020
1021 static gint
lua_config_get_all_opt(lua_State * L)1022 lua_config_get_all_opt (lua_State * L)
1023 {
1024 LUA_TRACE_POINT;
1025 struct rspamd_config *cfg = lua_check_config (L, 1);
1026 const gchar *mname;
1027 const ucl_object_t *obj, *cur, *cur_elt;
1028 ucl_object_iter_t it = NULL;
1029 gint i;
1030
1031 if (cfg) {
1032 mname = luaL_checkstring (L, 2);
1033
1034 if (mname) {
1035 obj = ucl_obj_get_key (cfg->rcl_obj, mname);
1036 /* Flatten object */
1037 if (obj != NULL && (ucl_object_type (obj) == UCL_OBJECT ||
1038 ucl_object_type (obj) == UCL_ARRAY)) {
1039
1040 lua_newtable (L);
1041 it = ucl_object_iterate_new (obj);
1042
1043 LL_FOREACH (obj, cur) {
1044 it = ucl_object_iterate_reset (it, cur);
1045
1046 while ((cur_elt = ucl_object_iterate_safe (it, true))) {
1047 lua_pushstring (L, ucl_object_key (cur_elt));
1048 ucl_object_push_lua (L, cur_elt, true);
1049 lua_settable (L, -3);
1050 }
1051 }
1052
1053 ucl_object_iterate_free (it);
1054
1055 return 1;
1056 }
1057 else if (obj != NULL) {
1058 lua_newtable (L);
1059 i = 1;
1060
1061 LL_FOREACH (obj, cur) {
1062 lua_pushinteger (L, i++);
1063 ucl_object_push_lua (L, cur, true);
1064 lua_settable (L, -3);
1065 }
1066
1067 return 1;
1068 }
1069 }
1070 }
1071 lua_pushnil (L);
1072
1073 return 1;
1074 }
1075
1076 struct rspamd_lua_cached_config {
1077 lua_State *L;
1078 gint ref;
1079 };
1080
1081 static void
lua_config_ucl_dtor(gpointer p)1082 lua_config_ucl_dtor (gpointer p)
1083 {
1084 struct rspamd_lua_cached_config *cached = p;
1085
1086 luaL_unref (cached->L, LUA_REGISTRYINDEX, cached->ref);
1087 }
1088
1089 static gint
lua_config_get_ucl(lua_State * L)1090 lua_config_get_ucl (lua_State * L)
1091 {
1092 LUA_TRACE_POINT;
1093 struct rspamd_config *cfg = lua_check_config (L, 1);
1094 struct rspamd_lua_cached_config *cached;
1095
1096 if (cfg) {
1097 cached = rspamd_mempool_get_variable (cfg->cfg_pool, "ucl_cached");
1098
1099 if (cached) {
1100 lua_rawgeti (L, LUA_REGISTRYINDEX, cached->ref);
1101 }
1102 else {
1103 if (cfg->rcl_obj) {
1104 ucl_object_push_lua(L, cfg->rcl_obj, true);
1105 lua_pushvalue(L, -1);
1106 cached = rspamd_mempool_alloc (cfg->cfg_pool, sizeof(*cached));
1107 cached->L = L;
1108 cached->ref = luaL_ref(L, LUA_REGISTRYINDEX);
1109 rspamd_mempool_set_variable(cfg->cfg_pool, "ucl_cached",
1110 cached, lua_config_ucl_dtor);
1111 }
1112 else {
1113 lua_pushnil (L);
1114 }
1115 }
1116 }
1117 else {
1118 return luaL_error (L, "invalid arguments");
1119 }
1120
1121 return 1;
1122 }
1123
1124
1125 static gint
lua_config_get_classifier(lua_State * L)1126 lua_config_get_classifier (lua_State * L)
1127 {
1128 LUA_TRACE_POINT;
1129 struct rspamd_config *cfg = lua_check_config (L, 1);
1130 struct rspamd_classifier_config *clc = NULL, **pclc = NULL;
1131 const gchar *name;
1132 GList *cur;
1133
1134 if (cfg) {
1135 name = luaL_checkstring (L, 2);
1136
1137 cur = g_list_first (cfg->classifiers);
1138 while (cur) {
1139 clc = cur->data;
1140 if (g_ascii_strcasecmp (clc->name, name) == 0) {
1141 pclc = &clc;
1142 break;
1143 }
1144 cur = g_list_next (cur);
1145 }
1146 if (pclc) {
1147 pclc = lua_newuserdata (L,
1148 sizeof (struct rspamd_classifier_config *));
1149 rspamd_lua_setclass (L, "rspamd{classifier}", -1);
1150 *pclc = clc;
1151 return 1;
1152 }
1153 }
1154
1155 lua_pushnil (L);
1156 return 1;
1157
1158 }
1159
1160 struct lua_callback_data {
1161 guint64 magic;
1162 lua_State *L;
1163 gchar *symbol;
1164
1165 union {
1166 gchar *name;
1167 gint ref;
1168 } callback;
1169 gboolean cb_is_ref;
1170
1171 /* Dynamic data */
1172 gint stack_level;
1173 gint order;
1174 struct rspamd_symcache_item *item;
1175 };
1176
1177 /*
1178 * Unref symbol if it is local reference
1179 */
1180 static void
lua_destroy_cfg_symbol(gpointer ud)1181 lua_destroy_cfg_symbol (gpointer ud)
1182 {
1183 struct lua_callback_data *cd = ud;
1184
1185 /* Unref callback */
1186 if (cd->cb_is_ref) {
1187 luaL_unref (cd->L, LUA_REGISTRYINDEX, cd->callback.ref);
1188 }
1189 }
1190
1191 static gint
lua_config_register_module_option(lua_State * L)1192 lua_config_register_module_option (lua_State *L)
1193 {
1194 return 0;
1195 }
1196
1197 static gint
rspamd_compare_order_func(gconstpointer a,gconstpointer b)1198 rspamd_compare_order_func (gconstpointer a, gconstpointer b)
1199 {
1200 const struct lua_callback_data *cb1 = a, *cb2 = b;
1201
1202 /* order of call goes from lower to higher */
1203 return cb2->order - cb1->order;
1204 }
1205
1206 static void
lua_metric_symbol_callback(struct rspamd_task * task,struct rspamd_symcache_item * item,gpointer ud)1207 lua_metric_symbol_callback (struct rspamd_task *task,
1208 struct rspamd_symcache_item *item,
1209 gpointer ud)
1210 {
1211 struct lua_callback_data *cd = ud;
1212 struct rspamd_task **ptask;
1213 gint level = lua_gettop (cd->L), nresults, err_idx, ret;
1214 lua_State *L = cd->L;
1215 struct rspamd_symbol_result *s;
1216
1217 cd->item = item;
1218 rspamd_symcache_item_async_inc (task, item, "lua symbol");
1219 lua_pushcfunction (L, &rspamd_lua_traceback);
1220 err_idx = lua_gettop (L);
1221
1222 level ++;
1223
1224 if (cd->cb_is_ref) {
1225 lua_rawgeti (L, LUA_REGISTRYINDEX, cd->callback.ref);
1226 }
1227 else {
1228 lua_getglobal (L, cd->callback.name);
1229 }
1230
1231 ptask = lua_newuserdata (L, sizeof (struct rspamd_task *));
1232 rspamd_lua_setclass (L, "rspamd{task}", -1);
1233 *ptask = task;
1234
1235 if ((ret = lua_pcall (L, 1, LUA_MULTRET, err_idx)) != 0) {
1236 msg_err_task ("call to (%s) failed (%d): %s", cd->symbol, ret,
1237 lua_tostring (L, -1));
1238 lua_settop (L, err_idx); /* Not -1 here, as err_func is popped below */
1239 }
1240 else {
1241 nresults = lua_gettop (L) - level;
1242
1243 if (nresults >= 1) {
1244 /* Function returned boolean, so maybe we need to insert result? */
1245 gint res = 0;
1246 gint i;
1247 gdouble flag = 1.0;
1248 gint type;
1249
1250 type = lua_type (cd->L, level + 1);
1251
1252 if (type == LUA_TBOOLEAN) {
1253 res = lua_toboolean (L, level + 1);
1254 }
1255 else if (type == LUA_TNUMBER) {
1256 res = lua_tonumber (L, level + 1);
1257 }
1258 else if (type == LUA_TNIL) {
1259 /* Can happen sometimes... */
1260 res = FALSE;
1261 }
1262 else {
1263 g_assert_not_reached ();
1264 }
1265
1266 if (res) {
1267 gint first_opt = 2;
1268
1269 if (lua_type (L, level + 2) == LUA_TNUMBER) {
1270 flag = lua_tonumber (L, level + 2);
1271 /* Shift opt index */
1272 first_opt = 3;
1273 }
1274 else {
1275 flag = res;
1276 }
1277
1278 s = rspamd_task_insert_result (task, cd->symbol, flag, NULL);
1279
1280 if (s) {
1281 guint last_pos = lua_gettop (L);
1282
1283 for (i = level + first_opt; i <= last_pos; i++) {
1284 if (lua_type (L, i) == LUA_TSTRING) {
1285 gsize optlen;
1286 const char *opt = lua_tolstring (L, i, &optlen);
1287
1288 rspamd_task_add_result_option (task, s, opt, optlen);
1289 }
1290 else if (lua_type (L, i) == LUA_TUSERDATA) {
1291 struct rspamd_lua_text *t = lua_check_text (L, i);
1292
1293 if (t) {
1294 rspamd_task_add_result_option (task, s, t->start,
1295 t->len);
1296 }
1297 }
1298 else if (lua_type (L, i) == LUA_TTABLE) {
1299 gsize objlen = rspamd_lua_table_size (L, i);
1300
1301 for (guint j = 1; j <= objlen; j ++) {
1302 lua_rawgeti (L, i, j);
1303
1304 if (lua_type (L, -1) == LUA_TSTRING) {
1305 gsize optlen;
1306 const char *opt = lua_tolstring (L, -1, &optlen);
1307
1308 rspamd_task_add_result_option (task, s, opt, optlen);
1309 }
1310 else if (lua_type (L, -1) == LUA_TUSERDATA) {
1311 struct rspamd_lua_text *t = lua_check_text (L, -1);
1312
1313 if (t) {
1314 rspamd_task_add_result_option (task, s, t->start,
1315 t->len);
1316 }
1317 }
1318
1319 lua_pop (L, 1);
1320 }
1321 }
1322 }
1323 }
1324 }
1325
1326 lua_pop (L, nresults);
1327 }
1328 }
1329
1330 lua_pop (L, 1); /* Error function */
1331 rspamd_symcache_item_async_dec_check (task, cd->item, "lua symbol");
1332 g_assert (lua_gettop (L) == level - 1);
1333 }
1334
1335 static void lua_metric_symbol_callback_return (struct thread_entry *thread_entry,
1336 int ret);
1337
1338 static void lua_metric_symbol_callback_error (struct thread_entry *thread_entry,
1339 int ret,
1340 const char *msg);
1341
1342 static void
lua_metric_symbol_callback_coro(struct rspamd_task * task,struct rspamd_symcache_item * item,gpointer ud)1343 lua_metric_symbol_callback_coro (struct rspamd_task *task,
1344 struct rspamd_symcache_item *item,
1345 gpointer ud)
1346 {
1347 struct lua_callback_data *cd = ud;
1348 struct rspamd_task **ptask;
1349 struct thread_entry *thread_entry;
1350
1351 rspamd_symcache_item_async_inc (task, item, "lua coro symbol");
1352 thread_entry = lua_thread_pool_get_for_task (task);
1353
1354 g_assert(thread_entry->cd == NULL);
1355 thread_entry->cd = cd;
1356
1357 lua_State *thread = thread_entry->lua_state;
1358 cd->stack_level = lua_gettop (thread);
1359 cd->item = item;
1360
1361 if (cd->cb_is_ref) {
1362 lua_rawgeti (thread, LUA_REGISTRYINDEX, cd->callback.ref);
1363 }
1364 else {
1365 lua_getglobal (thread, cd->callback.name);
1366 }
1367
1368 ptask = lua_newuserdata (thread, sizeof (struct rspamd_task *));
1369 rspamd_lua_setclass (thread, "rspamd{task}", -1);
1370 *ptask = task;
1371
1372 thread_entry->finish_callback = lua_metric_symbol_callback_return;
1373 thread_entry->error_callback = lua_metric_symbol_callback_error;
1374
1375 lua_thread_call (thread_entry, 1);
1376 }
1377
1378 static void
lua_metric_symbol_callback_error(struct thread_entry * thread_entry,int ret,const char * msg)1379 lua_metric_symbol_callback_error (struct thread_entry *thread_entry,
1380 int ret,
1381 const char *msg)
1382 {
1383 struct lua_callback_data *cd = thread_entry->cd;
1384 struct rspamd_task *task = thread_entry->task;
1385 msg_err_task ("call to coroutine (%s) failed (%d): %s", cd->symbol, ret, msg);
1386
1387 rspamd_symcache_item_async_dec_check (task, cd->item, "lua coro symbol");
1388 }
1389
1390 static void
lua_metric_symbol_callback_return(struct thread_entry * thread_entry,int ret)1391 lua_metric_symbol_callback_return (struct thread_entry *thread_entry, int ret)
1392 {
1393 struct lua_callback_data *cd = thread_entry->cd;
1394 struct rspamd_task *task = thread_entry->task;
1395 int nresults;
1396 struct rspamd_symbol_result *s;
1397
1398 (void)ret;
1399
1400 lua_State *L = thread_entry->lua_state;
1401
1402 nresults = lua_gettop (L) - cd->stack_level;
1403
1404 if (nresults >= 1) {
1405 /* Function returned boolean, so maybe we need to insert result? */
1406 gint res = 0;
1407 gint i;
1408 gdouble flag = 1.0;
1409 gint type;
1410
1411 type = lua_type (L, cd->stack_level + 1);
1412
1413 if (type == LUA_TBOOLEAN) {
1414 res = lua_toboolean (L, cd->stack_level + 1);
1415 }
1416 else if (type == LUA_TFUNCTION) {
1417 g_assert_not_reached ();
1418 }
1419 else {
1420 res = lua_tonumber (L, cd->stack_level + 1);
1421 }
1422
1423 if (res) {
1424 gint first_opt = 2;
1425
1426 if (lua_type (L, cd->stack_level + 2) == LUA_TNUMBER) {
1427 flag = lua_tonumber (L, cd->stack_level + 2);
1428 /* Shift opt index */
1429 first_opt = 3;
1430 }
1431 else {
1432 flag = res;
1433 }
1434
1435 s = rspamd_task_insert_result (task, cd->symbol, flag, NULL);
1436
1437 if (s) {
1438 guint last_pos = lua_gettop (L);
1439
1440 for (i = cd->stack_level + first_opt; i <= last_pos; i++) {
1441 if (lua_type (L, i) == LUA_TSTRING) {
1442 gsize optlen;
1443 const char *opt = lua_tolstring (L, i, &optlen);
1444
1445 rspamd_task_add_result_option (task, s, opt, optlen);
1446 }
1447 else if (lua_type (L, i) == LUA_TUSERDATA) {
1448 struct rspamd_lua_text *t = lua_check_text (L, i);
1449
1450 if (t) {
1451 rspamd_task_add_result_option (task, s, t->start,
1452 t->len);
1453 }
1454 }
1455 else if (lua_type (L, i) == LUA_TTABLE) {
1456 gsize objlen = rspamd_lua_table_size (L, i);
1457
1458 for (guint j = 1; j <= objlen; j ++) {
1459 lua_rawgeti (L, i, j);
1460
1461 if (lua_type (L, -1) == LUA_TSTRING) {
1462 gsize optlen;
1463 const char *opt = lua_tolstring (L, -1, &optlen);
1464
1465 rspamd_task_add_result_option (task, s, opt, optlen);
1466 }
1467 else if (lua_type (L, -1) == LUA_TUSERDATA) {
1468 struct rspamd_lua_text *t = lua_check_text (L, -1);
1469
1470 if (t) {
1471 rspamd_task_add_result_option (task, s, t->start,
1472 t->len);
1473 }
1474 }
1475
1476 lua_pop (L, 1);
1477 }
1478 }
1479 }
1480 }
1481
1482 }
1483
1484 lua_pop (L, nresults);
1485 }
1486
1487 g_assert (lua_gettop (L) == cd->stack_level); /* we properly cleaned up the stack */
1488
1489 cd->stack_level = 0;
1490 rspamd_symcache_item_async_dec_check (task, cd->item, "lua coro symbol");
1491 }
1492
1493 static guint32*
rspamd_process_id_list(const gchar * entries,guint32 * plen)1494 rspamd_process_id_list (const gchar *entries, guint32 *plen)
1495 {
1496 gchar **sym_elts;
1497 guint32 *ids, nids;
1498
1499 sym_elts = g_strsplit_set (entries, ",;", -1);
1500 nids = g_strv_length (sym_elts);
1501
1502 ids = g_malloc (nids * sizeof (guint32));
1503
1504 for (guint i = 0; i < nids; i ++) {
1505 ids[i] = rspamd_config_name_to_id (sym_elts[i], strlen (sym_elts[i]));
1506 }
1507
1508 *plen = nids;
1509 g_strfreev (sym_elts);
1510
1511 return ids;
1512 }
1513
1514 static gint
rspamd_register_symbol_fromlua(lua_State * L,struct rspamd_config * cfg,const gchar * name,gint ref,gdouble weight,gint priority,enum rspamd_symbol_type type,gint parent,const gchar * allowed_ids,const gchar * forbidden_ids,gboolean optional)1515 rspamd_register_symbol_fromlua (lua_State *L,
1516 struct rspamd_config *cfg,
1517 const gchar *name,
1518 gint ref,
1519 gdouble weight,
1520 gint priority,
1521 enum rspamd_symbol_type type,
1522 gint parent,
1523 const gchar *allowed_ids,
1524 const gchar *forbidden_ids,
1525 gboolean optional)
1526 {
1527 struct lua_callback_data *cd;
1528 gint ret = -1;
1529 guint32 *ids, nids;
1530
1531 if (priority == 0 && weight < 0) {
1532 priority = 1;
1533 }
1534
1535 if ((ret = rspamd_symcache_find_symbol (cfg->cache, name)) != -1) {
1536 if (optional) {
1537 msg_debug_config ("duplicate symbol: %s, skip registering", name);
1538
1539 return ret;
1540 }
1541 else {
1542 msg_err_config ("duplicate symbol: %s, skip registering", name);
1543
1544 return -1;
1545 }
1546 }
1547
1548 if (allowed_ids && !(type & SYMBOL_TYPE_EXPLICIT_DISABLE)) {
1549 /* Mark symbol as explicit allow */
1550 msg_info_config ("mark symbol %s as explicit enable as its execution is"
1551 "allowed merely on specific settings ids", name);
1552 type |= SYMBOL_TYPE_EXPLICIT_ENABLE;
1553 }
1554
1555 if (ref != -1) {
1556 cd = rspamd_mempool_alloc0 (cfg->cfg_pool,
1557 sizeof (struct lua_callback_data));
1558 cd->magic = rspamd_lua_callback_magic;
1559 cd->cb_is_ref = TRUE;
1560 cd->callback.ref = ref;
1561 cd->L = L;
1562
1563 if (name) {
1564 cd->symbol = rspamd_mempool_strdup (cfg->cfg_pool, name);
1565 }
1566
1567 if (type & SYMBOL_TYPE_USE_CORO) {
1568 ret = rspamd_symcache_add_symbol (cfg->cache,
1569 name,
1570 priority,
1571 lua_metric_symbol_callback_coro,
1572 cd,
1573 type,
1574 parent);
1575 }
1576 else {
1577 ret = rspamd_symcache_add_symbol (cfg->cache,
1578 name,
1579 priority,
1580 lua_metric_symbol_callback,
1581 cd,
1582 type,
1583 parent);
1584 }
1585 rspamd_mempool_add_destructor (cfg->cfg_pool,
1586 (rspamd_mempool_destruct_t)lua_destroy_cfg_symbol,
1587 cd);
1588 }
1589 else {
1590 /* No callback */
1591 ret = rspamd_symcache_add_symbol (cfg->cache,
1592 name,
1593 priority,
1594 NULL,
1595 NULL,
1596 type,
1597 parent);
1598 }
1599
1600 if (allowed_ids) {
1601 ids = rspamd_process_id_list (allowed_ids, &nids);
1602
1603 if (nids > 0) {
1604 GString *dbg = g_string_new ("");
1605
1606 for (guint i = 0; i < nids; i ++) {
1607 rspamd_printf_gstring (dbg, "%ud,", ids[i]);
1608 }
1609
1610 dbg->len --;
1611
1612 msg_debug_config ("allowed ids for %s are: %v", name, dbg);
1613 g_string_free (dbg, TRUE);
1614
1615 rspamd_symcache_set_allowed_settings_ids (cfg->cache, name,
1616 ids, nids);
1617 }
1618
1619 g_free (ids);
1620 }
1621
1622 if (forbidden_ids) {
1623 ids = rspamd_process_id_list (forbidden_ids, &nids);
1624
1625 if (nids > 0) {
1626 GString *dbg = g_string_new ("");
1627
1628 for (guint i = 0; i < nids; i ++) {
1629 rspamd_printf_gstring (dbg, "%ud,", ids[i]);
1630 }
1631
1632 dbg->len --;
1633
1634 msg_debug_config ("forbidden ids for %s are: %v", name, dbg);
1635 g_string_free (dbg, TRUE);
1636
1637 rspamd_symcache_set_forbidden_settings_ids (cfg->cache, name,
1638 ids, nids);
1639 }
1640
1641 g_free (ids);
1642 }
1643
1644 return ret;
1645 }
1646
1647 static gint
lua_config_register_post_filter(lua_State * L)1648 lua_config_register_post_filter (lua_State *L)
1649 {
1650 LUA_TRACE_POINT;
1651 struct rspamd_config *cfg = lua_check_config (L, 1);
1652 gint order = 0, cbref, ret;
1653
1654 if (cfg) {
1655 if (lua_type (L, 3) == LUA_TNUMBER) {
1656 order = lua_tonumber (L, 3);
1657 }
1658
1659 if (lua_type (L, 2) == LUA_TFUNCTION) {
1660 lua_pushvalue (L, 2);
1661 /* Get a reference */
1662 cbref = luaL_ref (L, LUA_REGISTRYINDEX);
1663 }
1664 else {
1665 return luaL_error (L, "invalid type for callback: %s",
1666 lua_typename (L, lua_type (L, 2)));
1667 }
1668
1669 msg_warn_config ("register_post_filter function is deprecated, "
1670 "use register_symbol instead");
1671
1672 ret = rspamd_register_symbol_fromlua (L,
1673 cfg,
1674 NULL,
1675 cbref,
1676 1.0,
1677 order,
1678 SYMBOL_TYPE_POSTFILTER|SYMBOL_TYPE_CALLBACK,
1679 -1,
1680 NULL, NULL,
1681 FALSE);
1682
1683 lua_pushboolean (L, ret);
1684 }
1685 else {
1686 return luaL_error (L, "invalid arguments");
1687 }
1688
1689 return 1;
1690 }
1691
1692 static gint
lua_config_register_pre_filter(lua_State * L)1693 lua_config_register_pre_filter (lua_State *L)
1694 {
1695 LUA_TRACE_POINT;
1696 struct rspamd_config *cfg = lua_check_config (L, 1);
1697 gint order = 0, cbref, ret;
1698
1699 if (cfg) {
1700 if (lua_type (L, 3) == LUA_TNUMBER) {
1701 order = lua_tonumber (L, 3);
1702 }
1703
1704 if (lua_type (L, 2) == LUA_TFUNCTION) {
1705 lua_pushvalue (L, 2);
1706 /* Get a reference */
1707 cbref = luaL_ref (L, LUA_REGISTRYINDEX);
1708 }
1709 else {
1710 return luaL_error (L, "invalid type for callback: %s",
1711 lua_typename (L, lua_type (L, 2)));
1712 }
1713
1714 msg_warn_config ("register_pre_filter function is deprecated, "
1715 "use register_symbol instead");
1716
1717 ret = rspamd_register_symbol_fromlua (L,
1718 cfg,
1719 NULL,
1720 cbref,
1721 1.0,
1722 order,
1723 SYMBOL_TYPE_PREFILTER|SYMBOL_TYPE_CALLBACK,
1724 -1,
1725 NULL, NULL,
1726 FALSE);
1727
1728 lua_pushboolean (L, ret);
1729 }
1730 else {
1731 return luaL_error (L, "invalid arguments");
1732 }
1733
1734 return 1;
1735 }
1736
1737 static gint
lua_config_get_key(lua_State * L)1738 lua_config_get_key (lua_State *L)
1739 {
1740 LUA_TRACE_POINT;
1741 struct rspamd_config *cfg = lua_check_config (L, 1);
1742 const gchar *name;
1743 size_t namelen;
1744 const ucl_object_t *val;
1745
1746 name = luaL_checklstring(L, 2, &namelen);
1747 if (name && cfg) {
1748 val = ucl_object_lookup_len(cfg->rcl_obj, name, namelen);
1749 if (val != NULL) {
1750 ucl_object_push_lua (L, val, val->type != UCL_ARRAY);
1751 }
1752 else {
1753 lua_pushnil (L);
1754 }
1755 }
1756 else {
1757 return luaL_error (L, "invalid arguments");
1758 }
1759
1760 return 1;
1761 }
1762
1763 static guint
lua_parse_symbol_flags(const gchar * str)1764 lua_parse_symbol_flags (const gchar *str)
1765 {
1766 guint ret = 0;
1767
1768 if (str) {
1769 if (strstr (str, "fine") != NULL) {
1770 ret |= SYMBOL_TYPE_FINE;
1771 }
1772 if (strstr (str, "nice") != NULL) {
1773 ret |= SYMBOL_TYPE_FINE;
1774 }
1775 if (strstr (str, "empty") != NULL) {
1776 ret |= SYMBOL_TYPE_EMPTY;
1777 }
1778 if (strstr (str, "skip") != NULL) {
1779 ret |= SYMBOL_TYPE_SKIPPED;
1780 }
1781 if (strstr (str, "nostat") != NULL) {
1782 ret |= SYMBOL_TYPE_NOSTAT;
1783 }
1784 if (strstr (str, "idempotent") != NULL) {
1785 ret |= SYMBOL_TYPE_IDEMPOTENT;
1786 }
1787 if (strstr (str, "trivial") != NULL) {
1788 ret |= SYMBOL_TYPE_TRIVIAL;
1789 }
1790 if (strstr (str, "ghost") != NULL) {
1791 ret |= SYMBOL_TYPE_GHOST;
1792 }
1793 if (strstr (str, "mime") != NULL) {
1794 ret |= SYMBOL_TYPE_MIME_ONLY;
1795 }
1796 if (strstr (str, "ignore_passthrough") != NULL) {
1797 ret |= SYMBOL_TYPE_IGNORE_PASSTHROUGH;
1798 }
1799 if (strstr (str, "explicit_disable") != NULL) {
1800 ret |= SYMBOL_TYPE_EXPLICIT_DISABLE;
1801 }
1802 if (strstr (str, "explicit_enable") != NULL) {
1803 ret |= SYMBOL_TYPE_EXPLICIT_ENABLE;
1804 }
1805 if (strstr (str, "coro") != NULL) {
1806 ret |= SYMBOL_TYPE_USE_CORO;
1807 }
1808 }
1809
1810 return ret;
1811 }
1812
1813 static guint
lua_parse_symbol_type(const gchar * str)1814 lua_parse_symbol_type (const gchar *str)
1815 {
1816 guint ret = SYMBOL_TYPE_NORMAL;
1817 gchar **vec;
1818 guint i, l;
1819
1820 if (str) {
1821 vec = g_strsplit_set (str, ",;", -1);
1822
1823 if (vec) {
1824 l = g_strv_length (vec);
1825
1826 for (i = 0; i < l; i ++) {
1827 str = vec[i];
1828
1829 if (g_ascii_strcasecmp (str, "virtual") == 0) {
1830 ret |= SYMBOL_TYPE_VIRTUAL;
1831 ret &= ~SYMBOL_TYPE_NORMAL;
1832 ret &= ~SYMBOL_TYPE_CALLBACK;
1833 }
1834 else if (g_ascii_strcasecmp (str, "callback") == 0) {
1835 ret |= SYMBOL_TYPE_CALLBACK;
1836 ret &= ~SYMBOL_TYPE_NORMAL;
1837 ret &= ~SYMBOL_TYPE_VIRTUAL;
1838 }
1839 else if (g_ascii_strcasecmp (str, "normal") == 0) {
1840 ret |= SYMBOL_TYPE_NORMAL;
1841 ret &= ~SYMBOL_TYPE_CALLBACK;
1842 ret &= ~SYMBOL_TYPE_VIRTUAL;
1843 }
1844 else if (g_ascii_strcasecmp (str, "prefilter") == 0) {
1845 ret |= SYMBOL_TYPE_PREFILTER | SYMBOL_TYPE_GHOST;
1846 }
1847 else if (g_ascii_strcasecmp (str, "postfilter") == 0) {
1848 ret |= SYMBOL_TYPE_POSTFILTER | SYMBOL_TYPE_GHOST;
1849 }
1850 else if (g_ascii_strcasecmp (str, "connfilter") == 0 ||
1851 g_ascii_strcasecmp (str, "conn_filter") == 0) {
1852 ret |= SYMBOL_TYPE_CONNFILTER | SYMBOL_TYPE_GHOST;
1853 }
1854 else if (g_ascii_strcasecmp (str, "idempotent") == 0) {
1855 ret |= SYMBOL_TYPE_POSTFILTER | SYMBOL_TYPE_GHOST |
1856 SYMBOL_TYPE_IDEMPOTENT | SYMBOL_TYPE_CALLBACK;
1857 }
1858 else {
1859 gint fl = 0;
1860
1861 fl = lua_parse_symbol_flags (str);
1862
1863 if (fl == 0) {
1864 msg_warn ("bad type: %s", str);
1865 }
1866 else {
1867 ret |= fl;
1868 }
1869 }
1870 }
1871
1872 g_strfreev (vec);
1873 }
1874 }
1875
1876 return ret;
1877 }
1878
1879 enum lua_push_symbol_flags_opts {
1880 LUA_SYMOPT_FLAG_CREATE_ARRAY = 1u << 0u,
1881 LUA_SYMOPT_FLAG_CREATE_MAP = 1u << 1u,
1882 LUA_SYMOPT_FLAG_USE_MAP = 1u << 2u,
1883 LUA_SYMOPT_FLAG_USE_ARRAY = 1u << 3u,
1884 };
1885
1886 #define LUA_SYMOPT_IS_ARRAY(f) ((f) & (LUA_SYMOPT_FLAG_CREATE_ARRAY|LUA_SYMOPT_FLAG_USE_ARRAY))
1887 #define LUA_SYMOPT_IS_CREATE(f) ((f) & (LUA_SYMOPT_FLAG_CREATE_ARRAY|LUA_SYMOPT_FLAG_CREATE_MAP))
1888 #define LUA_OPTION_PUSH(nm) do { \
1889 if (LUA_SYMOPT_IS_ARRAY(fl)) { \
1890 lua_pushstring (L, #nm); \
1891 lua_rawseti (L, -2, i++); \
1892 } \
1893 else { \
1894 lua_pushboolean (L, true); \
1895 lua_setfield (L, -2, #nm); \
1896 } \
1897 } while(0)
1898
1899 static void
lua_push_symbol_flags(lua_State * L,guint flags,enum lua_push_symbol_flags_opts fl)1900 lua_push_symbol_flags (lua_State *L, guint flags, enum lua_push_symbol_flags_opts fl)
1901 {
1902 guint i = 1;
1903
1904 if (LUA_SYMOPT_IS_CREATE (fl)) {
1905 lua_newtable (L);
1906 }
1907
1908 if (flags & SYMBOL_TYPE_FINE) {
1909 LUA_OPTION_PUSH (fine);
1910 }
1911
1912 if (flags & SYMBOL_TYPE_EMPTY) {
1913 LUA_OPTION_PUSH (empty);
1914 }
1915
1916 if (flags & SYMBOL_TYPE_EXPLICIT_DISABLE) {
1917 LUA_OPTION_PUSH (explicit_disable);
1918 }
1919
1920 if (flags & SYMBOL_TYPE_EXPLICIT_ENABLE) {
1921 LUA_OPTION_PUSH (explicit_enable);
1922 }
1923
1924 if (flags & SYMBOL_TYPE_IGNORE_PASSTHROUGH) {
1925 LUA_OPTION_PUSH (ignore_passthrough);
1926 }
1927
1928 if (flags & SYMBOL_TYPE_NOSTAT) {
1929 LUA_OPTION_PUSH (nostat);
1930 }
1931
1932 if (flags & SYMBOL_TYPE_IDEMPOTENT) {
1933 LUA_OPTION_PUSH (idempotent);
1934 }
1935
1936 if (flags & SYMBOL_TYPE_MIME_ONLY) {
1937 LUA_OPTION_PUSH (mime);
1938 }
1939
1940 if (flags & SYMBOL_TYPE_TRIVIAL) {
1941 LUA_OPTION_PUSH (trivial);
1942 }
1943
1944 if (flags & SYMBOL_TYPE_SKIPPED) {
1945 LUA_OPTION_PUSH (skip);
1946 }
1947
1948 if (flags & SYMBOL_TYPE_COMPOSITE) {
1949 LUA_OPTION_PUSH (composite);
1950 }
1951 }
1952
1953 static gint
lua_config_get_symbol_flags(lua_State * L)1954 lua_config_get_symbol_flags (lua_State *L)
1955 {
1956 struct rspamd_config *cfg = lua_check_config (L, 1);
1957 const gchar *name = luaL_checkstring (L, 2);
1958 guint flags;
1959
1960 if (cfg && name) {
1961 flags = rspamd_symcache_get_symbol_flags (cfg->cache,
1962 name);
1963
1964 if (flags != 0) {
1965 lua_push_symbol_flags (L, flags, LUA_SYMOPT_FLAG_CREATE_ARRAY);
1966 }
1967 else {
1968 lua_pushnil (L);
1969 }
1970 }
1971 else {
1972 return luaL_error (L, "invalid arguments");
1973 }
1974
1975 return 1;
1976 }
1977
1978 static gint
lua_config_add_symbol_flags(lua_State * L)1979 lua_config_add_symbol_flags (lua_State *L)
1980 {
1981 struct rspamd_config *cfg = lua_check_config (L, 1);
1982 const gchar *name = luaL_checkstring (L, 2);
1983 guint flags, new_flags = 0;
1984
1985 if (cfg && name && lua_istable (L, 3)) {
1986
1987 for (lua_pushnil (L); lua_next (L, 3); lua_pop (L, 1)) {
1988 new_flags |= lua_parse_symbol_flags (lua_tostring (L, -1));
1989 }
1990
1991 flags = rspamd_symcache_get_symbol_flags (cfg->cache,
1992 name);
1993
1994 if (flags != 0) {
1995 rspamd_symcache_add_symbol_flags (cfg->cache, name, new_flags);
1996 /* Push old flags */
1997 lua_push_symbol_flags (L, flags, LUA_SYMOPT_FLAG_CREATE_ARRAY);
1998 }
1999 else {
2000 lua_pushnil (L);
2001 }
2002 }
2003 else {
2004 return luaL_error (L, "invalid arguments");
2005 }
2006
2007 return 1;
2008 }
2009
2010 static gint
lua_config_register_symbol(lua_State * L)2011 lua_config_register_symbol (lua_State * L)
2012 {
2013 LUA_TRACE_POINT;
2014 struct rspamd_config *cfg = lua_check_config (L, 1);
2015 const gchar *name = NULL, *flags_str = NULL, *type_str = NULL,
2016 *description = NULL, *group = NULL, *allowed_ids = NULL,
2017 *forbidden_ids = NULL;
2018 double weight = 0, score = NAN, parent_float = NAN;
2019 gboolean one_shot = FALSE;
2020 gint ret = -1, cbref = -1, type, flags = 0;
2021 gint64 parent = 0, priority = 0, nshots = 0;
2022 GError *err = NULL;
2023
2024 if (cfg) {
2025 if (!rspamd_lua_parse_table_arguments (L, 2, &err,
2026 RSPAMD_LUA_PARSE_ARGUMENTS_DEFAULT,
2027 "name=S;weight=N;callback=F;flags=S;type=S;priority=I;parent=D;"
2028 "score=D;description=S;group=S;one_shot=B;nshots=I;"
2029 "allowed_ids=S;forbidden_ids=S",
2030 &name, &weight, &cbref, &flags_str, &type_str,
2031 &priority, &parent_float,
2032 &score, &description, &group, &one_shot, &nshots,
2033 &allowed_ids, &forbidden_ids)) {
2034 msg_err_config ("bad arguments: %e", err);
2035 g_error_free (err);
2036
2037 return luaL_error (L, "invalid arguments");
2038 }
2039
2040 if (nshots == 0) {
2041 nshots = cfg->default_max_shots;
2042 }
2043
2044 type = lua_parse_symbol_type (type_str);
2045
2046 if (!name && !(type & SYMBOL_TYPE_CALLBACK)) {
2047 return luaL_error (L, "no symbol name but type is not callback");
2048 }
2049 else if (!(type & SYMBOL_TYPE_VIRTUAL) && cbref == -1) {
2050 return luaL_error (L, "no callback for symbol %s", name);
2051 }
2052
2053 if (flags_str) {
2054 type |= lua_parse_symbol_flags (flags_str);
2055 }
2056
2057 if (isnan (parent_float)) {
2058 parent = -1;
2059 }
2060 else {
2061 parent = parent_float;
2062 }
2063
2064 ret = rspamd_register_symbol_fromlua (L,
2065 cfg,
2066 name,
2067 cbref,
2068 weight == 0 ? 1.0 : weight,
2069 priority,
2070 type,
2071 parent,
2072 allowed_ids, forbidden_ids,
2073 FALSE);
2074
2075 if (!isnan (score) || group) {
2076 if (one_shot) {
2077 nshots = 1;
2078 }
2079
2080 rspamd_config_add_symbol (cfg, name,
2081 score, description, group, flags,
2082 0, nshots);
2083
2084 lua_pushstring (L, "groups");
2085 lua_gettable (L, 2);
2086
2087 if (lua_istable (L, -1)) {
2088 for (lua_pushnil (L); lua_next (L, -2); lua_pop (L, 1)) {
2089 if (lua_isstring (L, -1)) {
2090 rspamd_config_add_symbol_group (cfg, name,
2091 lua_tostring (L, -1));
2092 }
2093 else {
2094 return luaL_error (L, "invalid groups element");
2095 }
2096 }
2097 }
2098
2099 lua_pop (L, 1);
2100 }
2101 }
2102 else {
2103 return luaL_error (L, "invalid arguments");
2104 }
2105
2106 lua_pushinteger (L, ret);
2107
2108 return 1;
2109 }
2110
2111 static gint
lua_config_register_symbols(lua_State * L)2112 lua_config_register_symbols (lua_State *L)
2113 {
2114 LUA_TRACE_POINT;
2115 struct rspamd_config *cfg = lua_check_config (L, 1);
2116 gint i, top, idx, ret = -1;
2117 const gchar *sym;
2118 gdouble weight = 1.0;
2119
2120 if (lua_gettop (L) < 3) {
2121 if (cfg) {
2122 msg_err_config ("not enough arguments to register a function");
2123 }
2124
2125 lua_error (L);
2126
2127 return 0;
2128 }
2129 if (cfg) {
2130 if (lua_type (L, 2) == LUA_TSTRING) {
2131 lua_getglobal (L, luaL_checkstring (L, 2));
2132 }
2133 else {
2134 lua_pushvalue (L, 2);
2135 }
2136 idx = luaL_ref (L, LUA_REGISTRYINDEX);
2137
2138 if (lua_type (L, 3) == LUA_TNUMBER) {
2139 weight = lua_tonumber (L, 3);
2140 top = 4;
2141 }
2142 else {
2143 top = 3;
2144 }
2145 sym = luaL_checkstring (L, top ++);
2146 ret = rspamd_register_symbol_fromlua (L,
2147 cfg,
2148 sym,
2149 idx,
2150 weight,
2151 0,
2152 SYMBOL_TYPE_CALLBACK,
2153 -1,
2154 NULL, NULL,
2155 FALSE);
2156
2157 for (i = top; i <= lua_gettop (L); i++) {
2158 if (lua_type (L, i) == LUA_TTABLE) {
2159 lua_pushvalue (L, i);
2160 lua_pushnil (L);
2161 while (lua_next (L, -2)) {
2162 lua_pushvalue (L, -2);
2163 sym = luaL_checkstring (L, -2);
2164 rspamd_symcache_add_symbol (cfg->cache, sym,
2165 0, NULL, NULL,
2166 SYMBOL_TYPE_VIRTUAL, ret);
2167 lua_pop (L, 2);
2168 }
2169 lua_pop (L, 1);
2170 }
2171 else if (lua_type (L, i) == LUA_TSTRING) {
2172 sym = luaL_checkstring (L, i);
2173 rspamd_symcache_add_symbol (cfg->cache, sym,
2174 0, NULL, NULL,
2175 SYMBOL_TYPE_VIRTUAL, ret);
2176 }
2177 }
2178 }
2179
2180 lua_pushinteger (L, ret);
2181
2182 return 1;
2183 }
2184
2185 static gint
lua_config_register_virtual_symbol(lua_State * L)2186 lua_config_register_virtual_symbol (lua_State * L)
2187 {
2188 LUA_TRACE_POINT;
2189 struct rspamd_config *cfg = lua_check_config (L, 1);
2190 const gchar *name;
2191 double weight;
2192 gint ret = -1, parent = -1;
2193
2194 if (cfg) {
2195 name = luaL_checkstring (L, 2);
2196 weight = luaL_checknumber (L, 3);
2197
2198 if (lua_gettop (L) > 3) {
2199 parent = lua_tonumber (L, 4);
2200 }
2201
2202 if (name) {
2203 ret = rspamd_symcache_add_symbol (cfg->cache, name,
2204 weight > 0 ? 0 : -1, NULL, NULL,
2205 SYMBOL_TYPE_VIRTUAL, parent);
2206 }
2207 }
2208
2209 lua_pushinteger (L, ret);
2210
2211 return 1;
2212 }
2213
2214 static gint
lua_config_register_callback_symbol(lua_State * L)2215 lua_config_register_callback_symbol (lua_State * L)
2216 {
2217 LUA_TRACE_POINT;
2218 struct rspamd_config *cfg = lua_check_config (L, 1);
2219 const gchar *name = NULL;
2220 double weight;
2221 gint ret = -1, top = 2;
2222
2223 if (cfg) {
2224 if (lua_type (L, 2) == LUA_TSTRING) {
2225 /* Legacy syntax */
2226 name = luaL_checkstring (L, 2);
2227 top ++;
2228 }
2229
2230 weight = luaL_checknumber (L, top);
2231
2232 if (lua_type (L, top + 1) == LUA_TSTRING) {
2233 lua_getglobal (L, luaL_checkstring (L, top + 1));
2234 }
2235 else {
2236 lua_pushvalue (L, top + 1);
2237 }
2238 ret = rspamd_register_symbol_fromlua (L,
2239 cfg,
2240 name,
2241 luaL_ref (L, LUA_REGISTRYINDEX),
2242 weight,
2243 0,
2244 SYMBOL_TYPE_CALLBACK,
2245 -1,
2246 NULL, NULL,
2247 FALSE);
2248 }
2249
2250 lua_pushinteger (L, ret);
2251
2252 return 1;
2253 }
2254
2255 static gint
lua_config_register_callback_symbol_priority(lua_State * L)2256 lua_config_register_callback_symbol_priority (lua_State * L)
2257 {
2258 LUA_TRACE_POINT;
2259 struct rspamd_config *cfg = lua_check_config (L, 1);
2260 const gchar *name = NULL;
2261 double weight;
2262 gint priority, ret = -1, top = 2;
2263
2264 if (cfg) {
2265 if (lua_type (L, 2) == LUA_TSTRING) {
2266 /* Legacy syntax */
2267 name = luaL_checkstring (L, 2);
2268 top ++;
2269 }
2270
2271 weight = luaL_checknumber (L, top);
2272 priority = luaL_checknumber (L, top + 1);
2273
2274 if (lua_type (L, top + 2) == LUA_TSTRING) {
2275 lua_getglobal (L, luaL_checkstring (L, top + 2));
2276 }
2277 else {
2278 lua_pushvalue (L, top + 2);
2279 }
2280
2281 ret = rspamd_register_symbol_fromlua (L,
2282 cfg,
2283 name,
2284 luaL_ref (L, LUA_REGISTRYINDEX),
2285 weight,
2286 priority,
2287 SYMBOL_TYPE_CALLBACK,
2288 -1,
2289 NULL, NULL,
2290 FALSE);
2291 }
2292
2293 lua_pushinteger (L, ret);
2294
2295 return 1;
2296 }
2297
2298
2299 static gint
lua_config_register_dependency(lua_State * L)2300 lua_config_register_dependency (lua_State * L)
2301 {
2302 LUA_TRACE_POINT;
2303 struct rspamd_config *cfg = lua_check_config (L, 1);
2304 const gchar *parent = NULL, *child = NULL;
2305 gint child_id;
2306
2307 if (cfg == NULL) {
2308 lua_error (L);
2309 return 0;
2310 }
2311
2312 if (lua_type (L, 2) == LUA_TNUMBER) {
2313 child_id = luaL_checknumber (L, 2);
2314 parent = luaL_checkstring (L, 3);
2315
2316 msg_warn_config ("calling for obsolete method to register deps for symbol %d->%s",
2317 child_id, parent);
2318
2319 if (child_id > 0 && parent != NULL) {
2320
2321 rspamd_symcache_add_dependency (cfg->cache, child_id, parent,
2322 -1);
2323 }
2324 }
2325 else {
2326 child = luaL_checkstring (L,2);
2327 parent = luaL_checkstring (L, 3);
2328
2329 if (child != NULL && parent != NULL) {
2330 rspamd_symcache_add_delayed_dependency (cfg->cache, child,
2331 parent);
2332 }
2333 }
2334
2335 return 0;
2336 }
2337
2338 static gint
lua_config_set_metric_symbol(lua_State * L)2339 lua_config_set_metric_symbol (lua_State * L)
2340 {
2341 LUA_TRACE_POINT;
2342 struct rspamd_config *cfg = lua_check_config (L, 1);
2343 const gchar *description = NULL,
2344 *group = NULL, *name = NULL, *flags_str = NULL;
2345 double score;
2346 gboolean one_shot = FALSE, one_param = FALSE;
2347 GError *err = NULL;
2348 gdouble priority = 0.0;
2349 guint flags = 0;
2350 gint64 nshots = 0;
2351
2352 if (cfg) {
2353
2354 if (lua_type (L, 2) == LUA_TTABLE) {
2355 if (!rspamd_lua_parse_table_arguments (L, 2, &err,
2356 RSPAMD_LUA_PARSE_ARGUMENTS_DEFAULT,
2357 "*name=S;score=N;description=S;"
2358 "group=S;one_shot=B;one_param=B;priority=N;flags=S;"
2359 "nshots=I",
2360 &name, &score, &description,
2361 &group, &one_shot, &one_param,
2362 &priority, &flags_str, &nshots)) {
2363 msg_err_config ("bad arguments: %e", err);
2364 g_error_free (err);
2365
2366 return 0;
2367 }
2368 }
2369 else {
2370 name = luaL_checkstring (L, 2);
2371 score = luaL_checknumber (L, 3);
2372
2373 if (lua_gettop (L) > 3 && lua_type (L, 4) == LUA_TSTRING) {
2374 description = luaL_checkstring (L, 4);
2375 }
2376 if (lua_gettop (L) > 4 && lua_type (L, 5) == LUA_TSTRING) {
2377 /* XXX: metrics */
2378 }
2379 if (lua_gettop (L) > 5 && lua_type (L, 6) == LUA_TSTRING) {
2380 group = luaL_checkstring (L, 6);
2381 }
2382 if (lua_gettop (L) > 6 && lua_type (L, 7) == LUA_TBOOLEAN) {
2383 one_shot = lua_toboolean (L, 7);
2384 }
2385 }
2386
2387 if (nshots == 0) {
2388 nshots = cfg->default_max_shots;
2389 }
2390
2391 if (one_shot) {
2392 nshots = 1;
2393 }
2394 if (one_param) {
2395 flags |= RSPAMD_SYMBOL_FLAG_ONEPARAM;
2396 }
2397
2398 if (flags_str) {
2399 if (strstr (flags_str, "one_shot") != NULL) {
2400 nshots = 1;
2401 }
2402 if (strstr (flags_str, "ignore") != NULL) {
2403 flags |= RSPAMD_SYMBOL_FLAG_IGNORE_METRIC;
2404 }
2405 if (strstr (flags_str, "one_param") != NULL) {
2406 flags |= RSPAMD_SYMBOL_FLAG_ONEPARAM;
2407 }
2408 }
2409
2410 rspamd_config_add_symbol (cfg, name,
2411 score, description, group, flags, (guint) priority, nshots);
2412
2413
2414 if (lua_type (L, 2) == LUA_TTABLE) {
2415 lua_pushstring (L, "groups");
2416 lua_gettable (L, 2);
2417
2418 if (lua_istable (L, -1)) {
2419 for (lua_pushnil (L); lua_next (L, -2); lua_pop (L, 1)) {
2420 if (lua_isstring (L, -1)) {
2421 rspamd_config_add_symbol_group (cfg, name,
2422 lua_tostring (L, -1));
2423 } else {
2424 return luaL_error (L, "invalid groups element");
2425 }
2426 }
2427 }
2428
2429 lua_pop (L, 1);
2430 }
2431 }
2432 else {
2433 return luaL_error (L, "invalid arguments, rspamd_config expected");
2434 }
2435
2436 return 0;
2437 }
2438
2439 static gint
lua_config_get_metric_symbol(lua_State * L)2440 lua_config_get_metric_symbol (lua_State * L)
2441 {
2442 LUA_TRACE_POINT;
2443 struct rspamd_config *cfg = lua_check_config (L, 1);
2444 const gchar *sym_name = luaL_checkstring (L, 2);
2445 struct rspamd_symbol *sym_def;
2446 struct rspamd_symbols_group *sym_group;
2447 guint i;
2448
2449 if (cfg && sym_name) {
2450 sym_def = g_hash_table_lookup (cfg->symbols, sym_name);
2451
2452 if (sym_def == NULL) {
2453 lua_pushnil (L);
2454 }
2455 else {
2456 lua_createtable (L, 0, 3);
2457 lua_pushstring (L, "score");
2458 lua_pushnumber (L, sym_def->score);
2459 lua_settable (L, -3);
2460
2461 if (sym_def->description) {
2462 lua_pushstring (L, "description");
2463 lua_pushstring (L, sym_def->description);
2464 lua_settable (L, -3);
2465 }
2466
2467 if (sym_def->gr) {
2468 lua_pushstring (L, "group");
2469 lua_pushstring (L, sym_def->gr->name);
2470 lua_settable (L, -3);
2471 }
2472
2473 lua_pushstring (L, "groups");
2474 lua_createtable (L, sym_def->groups->len, 0);
2475
2476 PTR_ARRAY_FOREACH (sym_def->groups, i, sym_group) {
2477 lua_pushstring (L, sym_group->name);
2478 lua_rawseti (L, -2, i + 1);
2479 }
2480
2481 lua_settable (L, -3);
2482 }
2483 }
2484 else {
2485 luaL_error (L, "Invalid arguments");
2486 }
2487
2488 return 1;
2489 }
2490
2491 static gint
lua_config_set_metric_action(lua_State * L)2492 lua_config_set_metric_action (lua_State * L)
2493 {
2494 LUA_TRACE_POINT;
2495 struct rspamd_config *cfg = lua_check_config (L, 1);
2496 const gchar *name = NULL;
2497 double threshold = NAN;
2498 GError *err = NULL;
2499 gdouble priority = 0.0;
2500 ucl_object_t *obj_tbl = NULL;
2501
2502 if (cfg) {
2503
2504 if (lua_type (L, 2) == LUA_TTABLE) {
2505 if (!rspamd_lua_parse_table_arguments (L, 2, &err,
2506 RSPAMD_LUA_PARSE_ARGUMENTS_DEFAULT,
2507 "*action=S;score=N;"
2508 "priority=N",
2509 &name, &threshold,
2510 &priority)) {
2511 msg_err_config ("bad arguments: %e", err);
2512 g_error_free (err);
2513
2514 return 0;
2515 }
2516 }
2517 else if (lua_type (L, 2) == LUA_TSTRING && lua_type (L, 3) == LUA_TTABLE) {
2518 name = lua_tostring (L, 2);
2519 obj_tbl = ucl_object_lua_import (L, 3);
2520
2521 if (obj_tbl) {
2522 if (name) {
2523 rspamd_config_set_action_score (cfg, name, obj_tbl);
2524 ucl_object_unref (obj_tbl);
2525 }
2526 else {
2527 ucl_object_unref (obj_tbl);
2528 return luaL_error (L, "invalid first argument, action name expected");
2529 }
2530 }
2531 else {
2532 return luaL_error (L, "invalid second argument, table expected");
2533 }
2534 }
2535 else {
2536 return luaL_error (L, "invalid arguments, table expected");
2537 }
2538
2539 if (name != NULL && !isnan (threshold) && threshold != 0) {
2540 obj_tbl = ucl_object_typed_new (UCL_OBJECT);
2541 ucl_object_insert_key (obj_tbl, ucl_object_fromdouble (threshold),
2542 "score", 0, false);
2543 ucl_object_insert_key (obj_tbl, ucl_object_fromdouble (priority),
2544 "priority", 0, false);
2545 rspamd_config_set_action_score (cfg, name, obj_tbl);
2546 ucl_object_unref (obj_tbl);
2547 }
2548 }
2549 else {
2550 return luaL_error (L, "invalid arguments, rspamd_config expected");
2551 }
2552
2553 return 0;
2554 }
2555
2556 static gint
lua_config_get_metric_action(lua_State * L)2557 lua_config_get_metric_action (lua_State * L)
2558 {
2559 LUA_TRACE_POINT;
2560 struct rspamd_config *cfg = lua_check_config (L, 1);
2561 const gchar *act_name = luaL_checkstring (L, 2);
2562 struct rspamd_action *act;
2563
2564 if (cfg && act_name) {
2565 act = rspamd_config_get_action (cfg, act_name);
2566
2567 if (act) {
2568 if (!isnan (act->threshold)) {
2569 lua_pushnumber (L, act->threshold);
2570 }
2571 else {
2572 lua_pushnil (L);
2573 }
2574 }
2575 else {
2576 lua_pushnil (L);
2577 }
2578 }
2579 else {
2580 return luaL_error (L, "invalid arguments, rspamd_config expected");
2581 }
2582
2583 return 1;
2584 }
2585
2586 static gint
lua_config_get_all_actions(lua_State * L)2587 lua_config_get_all_actions (lua_State * L)
2588 {
2589 LUA_TRACE_POINT;
2590 struct rspamd_config *cfg = lua_check_config (L, 1);
2591 struct rspamd_action *act, *tmp;
2592
2593 if (cfg) {
2594 lua_createtable (L, 0, HASH_COUNT (cfg->actions));
2595
2596 HASH_ITER (hh, cfg->actions, act, tmp) {
2597 if (!isnan (act->threshold)) {
2598 lua_pushstring (L, act->name);
2599 lua_pushnumber (L, act->threshold);
2600 lua_settable (L, -3);
2601 }
2602 }
2603 }
2604 else {
2605 return luaL_error (L, "invalid arguments, rspamd_config expected");
2606 }
2607
2608 return 1;
2609 }
2610
2611 static gint
lua_config_add_composite(lua_State * L)2612 lua_config_add_composite (lua_State * L)
2613 {
2614 LUA_TRACE_POINT;
2615 struct rspamd_config *cfg = lua_check_config (L, 1);
2616 gchar *name;
2617 const gchar *expr_str;
2618 struct rspamd_composite *composite;
2619 gboolean ret = FALSE;
2620
2621 if (cfg) {
2622 name = rspamd_mempool_strdup (cfg->cfg_pool, luaL_checkstring (L, 2));
2623 expr_str = luaL_checkstring (L, 3);
2624
2625 if (name && expr_str) {
2626 composite = rspamd_composites_manager_add_from_string(cfg->composites_manager,
2627 name, expr_str);
2628
2629 if (composite) {
2630 rspamd_symcache_add_symbol (cfg->cache, name,
2631 0, NULL, composite, SYMBOL_TYPE_COMPOSITE, -1);
2632 ret = TRUE;
2633 }
2634 }
2635 }
2636
2637 lua_pushboolean (L, ret);
2638
2639 return 1;
2640 }
2641
2642 static gint
lua_config_newindex(lua_State * L)2643 lua_config_newindex (lua_State *L)
2644 {
2645 LUA_TRACE_POINT;
2646 struct rspamd_config *cfg = lua_check_config (L, 1);
2647 const gchar *name, *allowed_ids = NULL, *forbidden_ids = NULL;
2648 gint id, nshots, flags = 0;
2649 gboolean optional = FALSE;
2650
2651 name = luaL_checkstring (L, 2);
2652
2653 if (cfg != NULL && name != NULL && lua_gettop (L) == 3) {
2654
2655 if (lua_type (L, 3) == LUA_TFUNCTION) {
2656 /* Normal symbol from just a function */
2657 lua_pushvalue (L, 3);
2658 rspamd_register_symbol_fromlua (L,
2659 cfg,
2660 name,
2661 luaL_ref (L, LUA_REGISTRYINDEX),
2662 1.0,
2663 0,
2664 SYMBOL_TYPE_NORMAL,
2665 -1,
2666 NULL, NULL,
2667 FALSE);
2668 }
2669 else if (lua_type (L, 3) == LUA_TTABLE) {
2670 gint type = SYMBOL_TYPE_NORMAL, priority = 0, idx;
2671 gdouble weight = 1.0, score = NAN;
2672 const char *type_str, *group = NULL, *description = NULL;
2673
2674 /*
2675 * Table can have the following attributes:
2676 * "callback" - should be a callback function
2677 * "weight" - optional weight
2678 * "priority" - optional priority
2679 * "type" - optional type (normal, virtual, callback)
2680 * "flags" - optional flags
2681 * -- Metric options
2682 * "score" - optional default score (overridden by metric)
2683 * "group" - optional default group
2684 * "one_shot" - optional one shot mode
2685 * "description" - optional description
2686 */
2687 lua_pushvalue (L, 3);
2688 lua_pushstring (L, "callback");
2689 lua_gettable (L, -2);
2690
2691 if (lua_type (L, -1) != LUA_TFUNCTION) {
2692 lua_pop (L, 2);
2693 msg_info_config ("cannot find callback definition for %s",
2694 name);
2695 return 0;
2696 }
2697 idx = luaL_ref (L, LUA_REGISTRYINDEX);
2698
2699 /* Optional fields */
2700 lua_pushstring (L, "weight");
2701 lua_gettable (L, -2);
2702
2703 if (lua_type (L, -1) == LUA_TNUMBER) {
2704 weight = lua_tonumber (L, -1);
2705 }
2706 lua_pop (L, 1);
2707
2708 lua_pushstring (L, "priority");
2709 lua_gettable (L, -2);
2710
2711 if (lua_type (L, -1) == LUA_TNUMBER) {
2712 priority = lua_tonumber (L, -1);
2713 }
2714 lua_pop (L, 1);
2715
2716 lua_pushstring (L, "optional");
2717 lua_gettable (L, -2);
2718
2719 if (lua_type (L, -1) == LUA_TBOOLEAN) {
2720 optional = lua_toboolean (L, -1);
2721 }
2722 lua_pop (L, 1);
2723
2724 lua_pushstring (L, "type");
2725 lua_gettable (L, -2);
2726
2727 if (lua_type (L, -1) == LUA_TSTRING) {
2728 type_str = lua_tostring (L, -1);
2729 type = lua_parse_symbol_type (type_str);
2730 }
2731 lua_pop (L, 1);
2732
2733 lua_pushstring (L, "flags");
2734 lua_gettable (L, -2);
2735
2736 if (lua_type (L, -1) == LUA_TSTRING) {
2737 type_str = lua_tostring (L, -1);
2738 type |= lua_parse_symbol_flags (type_str);
2739 }
2740 lua_pop (L, 1);
2741
2742 lua_pushstring (L, "allowed_ids");
2743 lua_gettable (L, -2);
2744
2745 if (lua_type (L, -1) == LUA_TSTRING) {
2746 allowed_ids = lua_tostring (L, -1);
2747 }
2748 lua_pop (L, 1);
2749
2750 lua_pushstring (L, "forbidden_ids");
2751 lua_gettable (L, -2);
2752
2753 if (lua_type (L, -1) == LUA_TSTRING) {
2754 forbidden_ids = lua_tostring (L, -1);
2755 }
2756 lua_pop (L, 1);
2757
2758 id = rspamd_register_symbol_fromlua (L,
2759 cfg,
2760 name,
2761 idx,
2762 weight,
2763 priority,
2764 type,
2765 -1,
2766 allowed_ids, forbidden_ids,
2767 optional);
2768
2769 if (id != -1) {
2770 /* Check for condition */
2771 lua_pushstring (L, "condition");
2772 lua_gettable (L, -2);
2773
2774 if (lua_type (L, -1) == LUA_TFUNCTION) {
2775 gint condref;
2776
2777 /* Here we pop function from the stack, so no lua_pop is required */
2778 condref = luaL_ref (L, LUA_REGISTRYINDEX);
2779 g_assert (name != NULL);
2780 rspamd_symcache_add_condition_delayed (cfg->cache,
2781 name, L, condref);
2782 }
2783 else {
2784 lua_pop (L, 1);
2785 }
2786 }
2787
2788 /*
2789 * Now check if a symbol has not been registered in any metric and
2790 * insert default value if applicable
2791 */
2792 struct rspamd_symbol *sym = g_hash_table_lookup (cfg->symbols, name);
2793 if (sym == NULL || (sym->flags & RSPAMD_SYMBOL_FLAG_UNSCORED)) {
2794 nshots = cfg->default_max_shots;
2795
2796 lua_pushstring (L, "score");
2797 lua_gettable (L, -2);
2798 if (lua_type (L, -1) == LUA_TNUMBER) {
2799 score = lua_tonumber (L, -1);
2800
2801 if (sym) {
2802 /* Reset unscored flag */
2803 sym->flags &= ~RSPAMD_SYMBOL_FLAG_UNSCORED;
2804 }
2805 }
2806 lua_pop (L, 1);
2807
2808 lua_pushstring (L, "group");
2809 lua_gettable (L, -2);
2810 if (lua_type (L, -1) == LUA_TSTRING) {
2811 group = lua_tostring (L, -1);
2812 }
2813 lua_pop (L, 1);
2814
2815 if (!isnan (score) || group != NULL) {
2816 lua_pushstring (L, "description");
2817 lua_gettable (L, -2);
2818
2819 if (lua_type (L, -1) == LUA_TSTRING) {
2820 description = lua_tostring (L, -1);
2821 }
2822 lua_pop (L, 1);
2823
2824 lua_pushstring (L, "one_shot");
2825 lua_gettable (L, -2);
2826
2827 if (lua_type (L, -1) == LUA_TBOOLEAN) {
2828 if (lua_toboolean (L, -1)) {
2829 nshots = 1;
2830 }
2831 }
2832 lua_pop (L, 1);
2833
2834 lua_pushstring (L, "one_param");
2835 lua_gettable (L, -2);
2836
2837 if (lua_type (L, -1) == LUA_TBOOLEAN) {
2838 if (lua_toboolean (L, -1)) {
2839 flags |= RSPAMD_SYMBOL_FLAG_ONEPARAM;
2840 }
2841 }
2842 lua_pop (L, 1);
2843
2844 /*
2845 * Do not override the existing symbols (using zero priority),
2846 * since we are defining default values here
2847 */
2848 if (!isnan (score)) {
2849 rspamd_config_add_symbol (cfg, name, score,
2850 description, group, flags, 0, nshots);
2851 }
2852 else if (group) {
2853 /* Add with zero score */
2854 rspamd_config_add_symbol (cfg, name, NAN,
2855 description, group, flags, 0, nshots);
2856 }
2857
2858 lua_pushstring (L, "groups");
2859 lua_gettable (L, -2);
2860
2861 if (lua_istable (L, -1)) {
2862 for (lua_pushnil (L); lua_next (L, -2); lua_pop (L, 1)) {
2863 if (lua_isstring (L, -1)) {
2864 rspamd_config_add_symbol_group (cfg, name,
2865 lua_tostring (L, -1));
2866 }
2867 else {
2868 return luaL_error (L, "invalid groups element");
2869 }
2870 }
2871 }
2872
2873 lua_pop (L, 1);
2874 }
2875 }
2876
2877 /* Remove table from stack */
2878 lua_pop (L, 1);
2879 }
2880 }
2881 else {
2882 return luaL_error (L, "invalid arguments");
2883 }
2884
2885 return 0;
2886 }
2887
2888 static gint
lua_config_add_condition(lua_State * L)2889 lua_config_add_condition (lua_State *L)
2890 {
2891 LUA_TRACE_POINT;
2892 struct rspamd_config *cfg = lua_check_config (L, 1);
2893 const gchar *sym = luaL_checkstring (L, 2);
2894 gboolean ret = FALSE;
2895 gint condref;
2896
2897 if (cfg && sym && lua_type (L, 3) == LUA_TFUNCTION) {
2898 lua_pushvalue (L, 3);
2899 condref = luaL_ref (L, LUA_REGISTRYINDEX);
2900
2901 ret = rspamd_symcache_add_condition_delayed (cfg->cache, sym, L,
2902 condref);
2903
2904 if (!ret) {
2905 luaL_unref (L, LUA_REGISTRYINDEX, condref);
2906 }
2907 }
2908
2909 lua_pushboolean (L, ret);
2910 return 1;
2911 }
2912
2913 static gint
lua_config_set_peak_cb(lua_State * L)2914 lua_config_set_peak_cb (lua_State *L)
2915 {
2916 LUA_TRACE_POINT;
2917 struct rspamd_config *cfg = lua_check_config (L, 1);
2918 gint condref;
2919
2920 if (cfg && lua_type (L, 2) == LUA_TFUNCTION) {
2921 lua_pushvalue (L, 2);
2922 condref = luaL_ref (L, LUA_REGISTRYINDEX);
2923 rspamd_symcache_set_peak_callback (cfg->cache,
2924 condref);
2925 }
2926
2927 return 0;
2928 }
2929
2930 static gint
lua_config_enable_symbol(lua_State * L)2931 lua_config_enable_symbol (lua_State *L)
2932 {
2933 LUA_TRACE_POINT;
2934 struct rspamd_config *cfg = lua_check_config (L, 1);
2935 const gchar *sym = luaL_checkstring (L, 2);
2936
2937 if (cfg && sym) {
2938 rspamd_symcache_enable_symbol_perm (cfg->cache, sym);
2939 }
2940 else {
2941 return luaL_error (L, "invalid arguments");
2942 }
2943
2944 return 0;
2945 }
2946
2947 static gint
lua_config_disable_symbol(lua_State * L)2948 lua_config_disable_symbol (lua_State *L)
2949 {
2950 LUA_TRACE_POINT;
2951 struct rspamd_config *cfg = lua_check_config (L, 1);
2952 const gchar *sym = luaL_checkstring (L, 2);
2953 gboolean disable_parent = TRUE;
2954
2955 if (cfg && sym) {
2956 if (lua_isboolean (L, 3)) {
2957 disable_parent = lua_toboolean (L, 3);
2958 }
2959
2960 rspamd_symcache_disable_symbol_perm (cfg->cache, sym, disable_parent);
2961 }
2962 else {
2963 return luaL_error (L, "invalid arguments");
2964 }
2965
2966 return 0;
2967 }
2968
2969 static gint
lua_config_register_regexp(lua_State * L)2970 lua_config_register_regexp (lua_State *L)
2971 {
2972 LUA_TRACE_POINT;
2973 struct rspamd_config *cfg = lua_check_config (L, 1);
2974 struct rspamd_lua_regexp *re = NULL;
2975 rspamd_regexp_t *cache_re;
2976 const gchar *type_str = NULL, *header_str = NULL;
2977 gsize header_len = 0;
2978 GError *err = NULL;
2979 enum rspamd_re_type type = RSPAMD_RE_BODY;
2980 gboolean pcre_only = FALSE;
2981
2982 /*
2983 * - `re`* : regular expression object
2984 * - `type`*: type of regular expression:
2985 * + `mime`: mime regexp
2986 * + `rawmime`: raw mime regexp
2987 * + `header`: header regexp
2988 * + `rawheader`: raw header expression
2989 * + `body`: raw body regexp
2990 * + `url`: url regexp
2991 * - `header`: for header and rawheader regexp means the name of header
2992 * - `pcre_only`: allow merely pcre for this regexp
2993 */
2994 if (cfg != NULL) {
2995 if (!rspamd_lua_parse_table_arguments (L, 2, &err,
2996 RSPAMD_LUA_PARSE_ARGUMENTS_DEFAULT,
2997 "*re=U{regexp};*type=S;header=S;pcre_only=B",
2998 &re, &type_str, &header_str, &pcre_only)) {
2999 msg_err_config ("cannot get parameters list: %e", err);
3000
3001 if (err) {
3002 g_error_free (err);
3003 }
3004 }
3005 else {
3006 type = rspamd_re_cache_type_from_string (type_str);
3007
3008 if ((type == RSPAMD_RE_HEADER ||
3009 type == RSPAMD_RE_RAWHEADER ||
3010 type == RSPAMD_RE_MIMEHEADER) &&
3011 header_str == NULL) {
3012 msg_err_config (
3013 "header argument is mandatory for header/rawheader regexps");
3014 }
3015 else {
3016 if (pcre_only) {
3017 rspamd_regexp_set_flags (re->re,
3018 rspamd_regexp_get_flags (re->re) | RSPAMD_REGEXP_FLAG_PCRE_ONLY);
3019 }
3020
3021 if (header_str != NULL) {
3022 /* Include the last \0 */
3023 header_len = strlen (header_str) + 1;
3024 }
3025
3026 cache_re = rspamd_re_cache_add (cfg->re_cache, re->re, type,
3027 (gpointer) header_str, header_len, -1);
3028
3029 /*
3030 * XXX: here are dragons!
3031 * Actually, lua regexp contains internal rspamd_regexp_t
3032 * and it owns it.
3033 * However, after this operation we have some OTHER regexp,
3034 * which we really would like to use.
3035 * So we do the following:
3036 * 1) Remove old re and unref it
3037 * 2) Replace the internal re with cached one
3038 * 3) Increase its refcount to share ownership between cache and
3039 * lua object
3040 */
3041 if (cache_re != re->re) {
3042 rspamd_regexp_unref (re->re);
3043 re->re = rspamd_regexp_ref (cache_re);
3044
3045 if (pcre_only) {
3046 rspamd_regexp_set_flags (re->re,
3047 rspamd_regexp_get_flags (re->re) | RSPAMD_REGEXP_FLAG_PCRE_ONLY);
3048 }
3049 }
3050 }
3051 }
3052 }
3053
3054 return 0;
3055 }
3056
3057 static gint
lua_config_replace_regexp(lua_State * L)3058 lua_config_replace_regexp (lua_State *L)
3059 {
3060 LUA_TRACE_POINT;
3061 struct rspamd_config *cfg = lua_check_config (L, 1);
3062 struct rspamd_lua_regexp *old_re = NULL, *new_re = NULL;
3063 gboolean pcre_only = FALSE;
3064 GError *err = NULL;
3065
3066 if (cfg != NULL) {
3067 if (!rspamd_lua_parse_table_arguments (L, 2, &err,
3068 RSPAMD_LUA_PARSE_ARGUMENTS_DEFAULT,
3069 "*old_re=U{regexp};*new_re=U{regexp};pcre_only=B",
3070 &old_re, &new_re, &pcre_only)) {
3071 gint ret = luaL_error (L, "cannot get parameters list: %s",
3072 err ? err->message : "invalid arguments");
3073
3074 if (err) {
3075 g_error_free (err);
3076 }
3077
3078 return ret;
3079 }
3080 else {
3081
3082 if (pcre_only) {
3083 rspamd_regexp_set_flags (new_re->re,
3084 rspamd_regexp_get_flags (new_re->re) | RSPAMD_REGEXP_FLAG_PCRE_ONLY);
3085 }
3086
3087 rspamd_re_cache_replace (cfg->re_cache, old_re->re, new_re->re);
3088 }
3089 }
3090
3091 return 0;
3092 }
3093
3094 static gint
lua_config_register_worker_script(lua_State * L)3095 lua_config_register_worker_script (lua_State *L)
3096 {
3097 LUA_TRACE_POINT;
3098 struct rspamd_config *cfg = lua_check_config (L, 1);
3099 const gchar *worker_type = luaL_checkstring (L, 2), *wtype;
3100 struct rspamd_worker_conf *cf;
3101 GList *cur;
3102 struct rspamd_worker_lua_script *sc;
3103 gboolean found = FALSE;
3104
3105 if (cfg == NULL || worker_type == NULL || lua_type (L, 3) != LUA_TFUNCTION) {
3106 return luaL_error (L, "invalid arguments");
3107 }
3108
3109 for (cur = g_list_first (cfg->workers); cur != NULL; cur = g_list_next (cur)) {
3110 cf = cur->data;
3111 wtype = g_quark_to_string (cf->type);
3112
3113 if (g_ascii_strcasecmp (wtype, worker_type) == 0) {
3114 sc = rspamd_mempool_alloc0 (cfg->cfg_pool, sizeof (*sc));
3115 lua_pushvalue (L, 3);
3116 sc->cbref = luaL_ref (L, LUA_REGISTRYINDEX);
3117 DL_APPEND (cf->scripts, sc);
3118 found = TRUE;
3119 }
3120 }
3121
3122 lua_pushboolean (L, found);
3123
3124 return 1;
3125 }
3126
3127 static gint
lua_config_add_on_load(lua_State * L)3128 lua_config_add_on_load (lua_State *L)
3129 {
3130 LUA_TRACE_POINT;
3131 struct rspamd_config *cfg = lua_check_config (L, 1);
3132 struct rspamd_config_cfg_lua_script *sc;
3133
3134 if (cfg == NULL || lua_type (L, 2) != LUA_TFUNCTION) {
3135 return luaL_error (L, "invalid arguments");
3136 }
3137
3138 sc = rspamd_mempool_alloc0 (cfg->cfg_pool, sizeof (*sc));
3139 lua_pushvalue (L, 2);
3140 sc->cbref = luaL_ref (L, LUA_REGISTRYINDEX);
3141 DL_APPEND (cfg->on_load_scripts, sc);
3142
3143 return 0;
3144 }
3145
3146 static inline int
rspamd_post_init_sc_sort(const struct rspamd_config_cfg_lua_script * pra,const struct rspamd_config_cfg_lua_script * prb)3147 rspamd_post_init_sc_sort (const struct rspamd_config_cfg_lua_script *pra,
3148 const struct rspamd_config_cfg_lua_script *prb)
3149 {
3150 /* Inverse sort */
3151 return prb->priority - pra->priority;
3152 }
3153
3154 static gint
lua_config_add_post_init(lua_State * L)3155 lua_config_add_post_init (lua_State *L)
3156 {
3157 LUA_TRACE_POINT;
3158 struct rspamd_config *cfg = lua_check_config (L, 1);
3159 struct rspamd_config_cfg_lua_script *sc;
3160 guint priority = 0;
3161 lua_Debug d;
3162 gchar tmp[256], *p;
3163
3164 if (cfg == NULL || lua_type (L, 2) != LUA_TFUNCTION) {
3165 return luaL_error (L, "invalid arguments");
3166 }
3167
3168 if (lua_type (L, 3) == LUA_TNUMBER) {
3169 priority = lua_tointeger (L , 3);
3170 }
3171
3172 if (lua_getstack (L, 1, &d) == 1) {
3173 (void) lua_getinfo (L, "Sl", &d);
3174 if ((p = strrchr (d.short_src, '/')) == NULL) {
3175 p = d.short_src;
3176 }
3177 else {
3178 p++;
3179 }
3180
3181 if (strlen (p) > 200) {
3182 rspamd_snprintf (tmp, sizeof (tmp), "%10s...]:%d", p,
3183 d.currentline);
3184 }
3185 else {
3186 rspamd_snprintf (tmp, sizeof (tmp), "%s:%d", p,
3187 d.currentline);
3188 }
3189 }
3190
3191 sc = rspamd_mempool_alloc0 (cfg->cfg_pool, sizeof (*sc));
3192 lua_pushvalue (L, 2);
3193 sc->cbref = luaL_ref (L, LUA_REGISTRYINDEX);
3194 sc->priority = priority;
3195 sc->lua_src_pos = rspamd_mempool_strdup (cfg->cfg_pool, tmp);
3196 DL_APPEND (cfg->post_init_scripts, sc);
3197 DL_SORT (cfg->post_init_scripts, rspamd_post_init_sc_sort);
3198
3199 return 0;
3200 }
3201
3202 static gint
lua_config_add_config_unload(lua_State * L)3203 lua_config_add_config_unload (lua_State *L)
3204 {
3205 LUA_TRACE_POINT;
3206 struct rspamd_config *cfg = lua_check_config (L, 1);
3207 struct rspamd_config_cfg_lua_script *sc;
3208 lua_Debug d;
3209 gchar tmp[256], *p;
3210
3211 if (cfg == NULL || lua_type (L, 2) != LUA_TFUNCTION) {
3212 return luaL_error (L, "invalid arguments");
3213 }
3214
3215 if (lua_getstack (L, 1, &d) == 1) {
3216 (void) lua_getinfo (L, "Sl", &d);
3217 if ((p = strrchr (d.short_src, '/')) == NULL) {
3218 p = d.short_src;
3219 }
3220 else {
3221 p++;
3222 }
3223
3224 if (strlen (p) > 20) {
3225 rspamd_snprintf (tmp, sizeof (tmp), "%10s...]:%d", p,
3226 d.currentline);
3227 }
3228 else {
3229 rspamd_snprintf (tmp, sizeof (tmp), "%s:%d", p,
3230 d.currentline);
3231 }
3232 }
3233
3234 sc = rspamd_mempool_alloc0 (cfg->cfg_pool, sizeof (*sc));
3235 lua_pushvalue (L, 2);
3236 sc->cbref = luaL_ref (L, LUA_REGISTRYINDEX);
3237 sc->lua_src_pos = rspamd_mempool_strdup (cfg->cfg_pool, tmp);
3238 DL_APPEND (cfg->config_unload_scripts, sc);
3239
3240 return 0;
3241 }
3242
3243
3244 static void lua_periodic_callback_finish (struct thread_entry *thread, int ret);
3245 static void lua_periodic_callback_error (struct thread_entry *thread, int ret, const char *msg);
3246
3247 struct rspamd_lua_periodic {
3248 struct ev_loop *event_loop;
3249 struct rspamd_config *cfg;
3250 gchar *lua_src_pos;
3251 lua_State *L;
3252 gdouble timeout;
3253 ev_timer ev;
3254 gint cbref;
3255 gboolean need_jitter;
3256 ref_entry_t ref;
3257 };
3258
3259 static void
lua_periodic_dtor(struct rspamd_lua_periodic * periodic)3260 lua_periodic_dtor (struct rspamd_lua_periodic *periodic)
3261 {
3262 luaL_unref (periodic->L, LUA_REGISTRYINDEX, periodic->cbref);
3263 ev_timer_stop (periodic->event_loop, &periodic->ev);
3264 }
3265
3266 static void
lua_periodic_fin(gpointer p)3267 lua_periodic_fin (gpointer p)
3268 {
3269 struct rspamd_lua_periodic *periodic = (struct rspamd_lua_periodic *)p;
3270
3271 REF_RELEASE (periodic);
3272 }
3273
3274 static void
lua_periodic_callback(struct ev_loop * loop,ev_timer * w,int revents)3275 lua_periodic_callback (struct ev_loop *loop, ev_timer *w, int revents)
3276 {
3277 struct rspamd_lua_periodic *periodic = (struct rspamd_lua_periodic *)w->data;
3278 struct rspamd_config **pcfg, *cfg;
3279 struct ev_loop **pev_base;
3280 struct thread_entry *thread;
3281 lua_State *L;
3282
3283 REF_RETAIN (periodic);
3284 thread = lua_thread_pool_get_for_config (periodic->cfg);
3285 thread->cd = periodic;
3286 thread->finish_callback = lua_periodic_callback_finish;
3287 thread->error_callback = lua_periodic_callback_error;
3288
3289 L = thread->lua_state;
3290
3291 lua_rawgeti (L, LUA_REGISTRYINDEX, periodic->cbref);
3292 pcfg = lua_newuserdata (L, sizeof (*pcfg));
3293 rspamd_lua_setclass (L, "rspamd{config}", -1);
3294 cfg = periodic->cfg;
3295 *pcfg = cfg;
3296 pev_base = lua_newuserdata (L, sizeof (*pev_base));
3297 rspamd_lua_setclass (L, "rspamd{ev_base}", -1);
3298 *pev_base = periodic->event_loop;
3299 lua_pushnumber (L, ev_now (periodic->event_loop));
3300
3301 lua_thread_call (thread, 3);
3302 }
3303
3304 static void
lua_periodic_callback_finish(struct thread_entry * thread,int ret)3305 lua_periodic_callback_finish (struct thread_entry *thread, int ret)
3306 {
3307 lua_State *L;
3308 struct rspamd_lua_periodic *periodic = thread->cd;
3309 gboolean plan_more = FALSE;
3310 gdouble timeout = 0.0;
3311
3312 L = thread->lua_state;
3313
3314 ev_now_update (periodic->event_loop);
3315
3316 if (ret == 0) {
3317 if (lua_type (L, -1) == LUA_TBOOLEAN) {
3318 plan_more = lua_toboolean (L, -1);
3319 timeout = periodic->timeout;
3320 }
3321 else if (lua_type (L, -1) == LUA_TNUMBER) {
3322 timeout = lua_tonumber (L, -1);
3323 plan_more = timeout > 0 ? TRUE : FALSE;
3324 }
3325
3326 lua_pop (L, 1); /* Return value */
3327 }
3328
3329 if (periodic->cfg->cur_worker) {
3330 if (periodic->cfg->cur_worker->state != rspamd_worker_state_running) {
3331 /* We are terminating, no more periodics */
3332 plan_more = FALSE;
3333 }
3334 }
3335
3336 if (plan_more) {
3337 if (periodic->need_jitter) {
3338 timeout = rspamd_time_jitter (timeout, 0.0);
3339 }
3340
3341 periodic->ev.repeat = timeout;
3342 ev_timer_again (periodic->event_loop, &periodic->ev);
3343 }
3344 else {
3345 ev_timer_stop (periodic->event_loop, &periodic->ev);
3346 }
3347
3348 REF_RELEASE (periodic);
3349 }
3350
3351 static void
lua_periodic_callback_error(struct thread_entry * thread,int ret,const char * msg)3352 lua_periodic_callback_error (struct thread_entry *thread, int ret, const char *msg)
3353 {
3354 struct rspamd_config *cfg;
3355 struct rspamd_lua_periodic *periodic = thread->cd;
3356 cfg = periodic->cfg;
3357
3358 msg_err_config ("call to periodic script (registered at %s) failed: %s",
3359 periodic->lua_src_pos, msg);
3360
3361 lua_periodic_callback_finish (thread, ret);
3362 }
3363
3364
3365 static gint
lua_config_add_periodic(lua_State * L)3366 lua_config_add_periodic (lua_State *L)
3367 {
3368 LUA_TRACE_POINT;
3369 struct rspamd_config *cfg = lua_check_config (L, 1);
3370 struct ev_loop *ev_base = lua_check_ev_base (L, 2);
3371 gdouble timeout = lua_tonumber (L, 3);
3372 struct rspamd_lua_periodic *periodic;
3373 gboolean need_jitter = FALSE;
3374 lua_Debug d;
3375 gchar tmp[256], *p;
3376
3377 if (cfg == NULL || timeout < 0 || lua_type (L, 4) != LUA_TFUNCTION) {
3378 return luaL_error (L, "invalid arguments");
3379 }
3380
3381 if (lua_type (L, 5) == LUA_TBOOLEAN) {
3382 need_jitter = lua_toboolean (L, 5);
3383 }
3384
3385 if (lua_getstack (L, 1, &d) == 1) {
3386 (void) lua_getinfo (L, "Sl", &d);
3387 if ((p = strrchr (d.short_src, '/')) == NULL) {
3388 p = d.short_src;
3389 }
3390 else {
3391 p++;
3392 }
3393
3394 if (strlen (p) > 20) {
3395 rspamd_snprintf (tmp, sizeof (tmp), "%10s...]:%d", p,
3396 d.currentline);
3397 }
3398 else {
3399 rspamd_snprintf (tmp, sizeof (tmp), "%s:%d", p,
3400 d.currentline);
3401 }
3402 }
3403
3404 periodic = rspamd_mempool_alloc0 (cfg->cfg_pool, sizeof (*periodic));
3405 periodic->timeout = timeout;
3406 periodic->L = L;
3407 periodic->cfg = cfg;
3408 periodic->event_loop = ev_base;
3409 periodic->need_jitter = need_jitter;
3410 periodic->lua_src_pos = rspamd_mempool_strdup (cfg->cfg_pool, tmp);
3411 lua_pushvalue (L, 4);
3412 periodic->cbref = luaL_ref (L, LUA_REGISTRYINDEX);
3413
3414 if (need_jitter) {
3415 timeout = rspamd_time_jitter (timeout, 0.0);
3416 }
3417
3418 ev_timer_init (&periodic->ev, lua_periodic_callback, timeout, 0.0);
3419 periodic->ev.data = periodic;
3420 ev_timer_start (ev_base, &periodic->ev);
3421 REF_INIT_RETAIN (periodic, lua_periodic_dtor);
3422
3423 rspamd_mempool_add_destructor (cfg->cfg_pool, lua_periodic_fin,
3424 periodic);
3425
3426 return 0;
3427 }
3428
3429 static gint
lua_config_get_symbols_count(lua_State * L)3430 lua_config_get_symbols_count (lua_State *L)
3431 {
3432 LUA_TRACE_POINT;
3433 struct rspamd_config *cfg = lua_check_config (L, 1);
3434 guint res = 0;
3435
3436 if (cfg != NULL) {
3437 res = rspamd_symcache_stats_symbols_count (cfg->cache);
3438 }
3439 else {
3440 return luaL_error (L, "invalid arguments");
3441 }
3442
3443 lua_pushinteger (L, res);
3444
3445 return 1;
3446 }
3447
3448 static gint
lua_config_get_symbols_cksum(lua_State * L)3449 lua_config_get_symbols_cksum (lua_State *L)
3450 {
3451 LUA_TRACE_POINT;
3452 struct rspamd_config *cfg = lua_check_config (L, 1);
3453 guint64 res = 0, *pres;
3454
3455 if (cfg != NULL) {
3456 res = rspamd_symcache_get_cksum (cfg->cache);
3457 }
3458 else {
3459 return luaL_error (L, "invalid arguments");
3460 }
3461
3462 pres = lua_newuserdata (L, sizeof (res));
3463 *pres = res;
3464 rspamd_lua_setclass (L, "rspamd{int64}", -1);
3465
3466 return 1;
3467 }
3468
3469 static gint
lua_config_get_symbols_counters(lua_State * L)3470 lua_config_get_symbols_counters (lua_State *L)
3471 {
3472 LUA_TRACE_POINT;
3473 struct rspamd_config *cfg = lua_check_config (L, 1);
3474 ucl_object_t *counters;
3475
3476 if (cfg != NULL) {
3477 counters = rspamd_symcache_counters (cfg->cache);
3478 ucl_object_push_lua (L, counters, true);
3479 ucl_object_unref (counters);
3480 }
3481 else {
3482 return luaL_error (L, "invalid arguments");
3483 }
3484
3485 return 1;
3486 }
3487
3488 struct lua_metric_symbols_cbdata {
3489 lua_State *L;
3490 struct rspamd_config *cfg;
3491 };
3492
3493 static void
lua_metric_symbol_inserter(gpointer k,gpointer v,gpointer ud)3494 lua_metric_symbol_inserter (gpointer k, gpointer v, gpointer ud)
3495 {
3496 struct lua_metric_symbols_cbdata *cbd = (struct lua_metric_symbols_cbdata *)ud;
3497 lua_State *L;
3498 const gchar *sym = k;
3499 struct rspamd_symbol *s = (struct rspamd_symbol *) v;
3500 struct rspamd_symbols_group *gr;
3501 gint i;
3502
3503 L = cbd->L;
3504
3505 lua_pushstring (L, sym); /* Symbol name */
3506
3507 lua_createtable (L, 0, 6);
3508 lua_pushstring (L, "score");
3509 lua_pushnumber (L, s->score);
3510 lua_settable (L, -3);
3511 lua_pushstring (L, "description");
3512 lua_pushstring (L, s->description);
3513 lua_settable (L, -3);
3514
3515 lua_pushstring (L, "flags");
3516 lua_createtable (L, 0, 3);
3517
3518 if (s->flags & RSPAMD_SYMBOL_FLAG_IGNORE_METRIC) {
3519 lua_pushstring (L, "ignore");
3520 lua_pushboolean (L, true);
3521 lua_settable (L, -3);
3522 }
3523 if (s->flags & RSPAMD_SYMBOL_FLAG_ONEPARAM) {
3524 lua_pushstring (L, "oneparam");
3525 lua_pushboolean (L, true);
3526 lua_settable (L, -3);
3527 }
3528 if (s->flags & RSPAMD_SYMBOL_FLAG_UNGROUPPED) {
3529 lua_pushstring (L, "ungroupped");
3530 lua_pushboolean (L, true);
3531 lua_settable (L, -3);
3532 }
3533 if (s->flags & RSPAMD_SYMBOL_FLAG_DISABLED) {
3534 lua_pushstring (L, "disabled");
3535 lua_pushboolean (L, true);
3536 lua_settable (L, -3);
3537 }
3538
3539 if (s->cache_item) {
3540 guint sflags = rspamd_symcache_get_symbol_flags (cbd->cfg->cache, sym);
3541
3542 lua_push_symbol_flags (L, sflags, LUA_SYMOPT_FLAG_USE_MAP);
3543
3544 guint nids;
3545 const guint *allowed_ids = rspamd_symcache_get_allowed_settings_ids (cbd->cfg->cache,
3546 sym, &nids);
3547
3548 if (allowed_ids && nids > 0) {
3549 lua_createtable (L, nids, 0);
3550
3551 for (i = 0; i < nids; i ++) {
3552 lua_pushinteger (L, allowed_ids[i]);
3553 lua_rawseti (L, -2, i + 1);
3554 }
3555
3556 lua_setfield (L, -2, "allowed_ids");
3557 }
3558
3559 const guint *forbidden_ids = rspamd_symcache_get_forbidden_settings_ids (
3560 cbd->cfg->cache,
3561 sym, &nids);
3562
3563 if (forbidden_ids && nids > 0) {
3564 lua_createtable (L, nids, 0);
3565
3566 for (i = 0; i < nids; i ++) {
3567 lua_pushinteger (L, forbidden_ids[i]);
3568 lua_rawseti (L, -2, i + 1);
3569 }
3570
3571 lua_setfield (L, -2, "forbidden_ids");
3572 }
3573 }
3574
3575 lua_settable (L, -3); /* Flags -> flags_table */
3576
3577 lua_pushstring (L, "nshots");
3578 lua_pushinteger (L, s->nshots);
3579 lua_settable (L, -3);
3580
3581 if (s->gr) {
3582 lua_pushstring (L, "group");
3583 lua_pushstring (L, s->gr->name);
3584 lua_settable (L, -3);
3585 }
3586
3587 if (s->groups && s->groups->len > 0) {
3588 lua_pushstring (L, "groups");
3589 lua_createtable (L, s->groups->len, 0);
3590
3591 PTR_ARRAY_FOREACH (s->groups, i, gr) {
3592 lua_pushstring (L, gr->name);
3593 lua_rawseti (L, -2, i + 1); /* Groups[i + 1] = group_name */
3594 }
3595
3596 lua_settable (L, -3); /* Groups -> groups_table */
3597 }
3598 else {
3599 lua_createtable (L, 0, 0);
3600 lua_setfield (L, -2, "groups");
3601 }
3602
3603 lua_settable (L, -3); /* Symname -> table */
3604 }
3605
3606 static gint
lua_config_get_symbols(lua_State * L)3607 lua_config_get_symbols (lua_State *L)
3608 {
3609 LUA_TRACE_POINT;
3610 struct rspamd_config *cfg = lua_check_config (L, 1);
3611
3612 if (cfg != NULL) {
3613 struct lua_metric_symbols_cbdata cbd;
3614
3615 cbd.L = L;
3616 cbd.cfg = cfg;
3617
3618 lua_createtable (L, 0, g_hash_table_size (cfg->symbols));
3619 g_hash_table_foreach (cfg->symbols,
3620 lua_metric_symbol_inserter,
3621 &cbd);
3622 }
3623 else {
3624 return luaL_error (L, "invalid arguments");
3625 }
3626
3627 return 1;
3628 }
3629
3630
3631 static gint
lua_config_get_symbol_callback(lua_State * L)3632 lua_config_get_symbol_callback (lua_State *L)
3633 {
3634 LUA_TRACE_POINT;
3635 struct rspamd_config *cfg = lua_check_config (L, 1);
3636 const gchar *sym = luaL_checkstring (L, 2);
3637 struct rspamd_abstract_callback_data *abs_cbdata;
3638 struct lua_callback_data *cbd;
3639
3640 if (cfg != NULL && sym != NULL) {
3641 abs_cbdata = rspamd_symcache_get_cbdata (cfg->cache, sym);
3642
3643 if (abs_cbdata == NULL || abs_cbdata->magic != rspamd_lua_callback_magic) {
3644 lua_pushnil (L);
3645 }
3646 else {
3647 cbd = (struct lua_callback_data *)abs_cbdata;
3648
3649 if (cbd->cb_is_ref) {
3650 lua_rawgeti (L, LUA_REGISTRYINDEX, cbd->callback.ref);
3651 }
3652 else {
3653 lua_getglobal (L, cbd->callback.name);
3654 }
3655 }
3656 }
3657 else {
3658 return luaL_error (L, "invalid arguments");
3659 }
3660
3661 return 1;
3662 }
3663
3664 static gint
lua_config_set_symbol_callback(lua_State * L)3665 lua_config_set_symbol_callback (lua_State *L)
3666 {
3667 LUA_TRACE_POINT;
3668 struct rspamd_config *cfg = lua_check_config (L, 1);
3669 const gchar *sym = luaL_checkstring (L, 2);
3670 struct rspamd_abstract_callback_data *abs_cbdata;
3671 struct lua_callback_data *cbd;
3672
3673 if (cfg != NULL && sym != NULL && lua_type (L, 3) == LUA_TFUNCTION) {
3674 abs_cbdata = rspamd_symcache_get_cbdata (cfg->cache, sym);
3675
3676 if (abs_cbdata == NULL || abs_cbdata->magic != rspamd_lua_callback_magic) {
3677 lua_pushboolean (L, FALSE);
3678 }
3679 else {
3680 cbd = (struct lua_callback_data *)abs_cbdata;
3681
3682 if (cbd->cb_is_ref) {
3683 luaL_unref (L, LUA_REGISTRYINDEX, cbd->callback.ref);
3684 }
3685 else {
3686 cbd->cb_is_ref = TRUE;
3687 }
3688
3689 lua_pushvalue (L, 3);
3690 cbd->callback.ref = luaL_ref (L, LUA_REGISTRYINDEX);
3691 lua_pushboolean (L, TRUE);
3692 }
3693 }
3694 else {
3695 return luaL_error (L, "invalid arguments");
3696 }
3697
3698 return 1;
3699 }
3700
3701 static gint
lua_config_get_symbol_stat(lua_State * L)3702 lua_config_get_symbol_stat (lua_State *L)
3703 {
3704 LUA_TRACE_POINT;
3705 struct rspamd_config *cfg = lua_check_config (L, 1);
3706 const gchar *sym = luaL_checkstring (L, 2);
3707 gdouble freq, stddev, tm;
3708 guint hits;
3709
3710 if (cfg != NULL && sym != NULL) {
3711 if (!rspamd_symcache_stat_symbol (cfg->cache, sym, &freq,
3712 &stddev, &tm, &hits)) {
3713 lua_pushnil (L);
3714 }
3715 else {
3716 lua_createtable (L, 0, 4);
3717 lua_pushstring (L, "frequency");
3718 lua_pushnumber (L, freq);
3719 lua_settable (L, -3);
3720 lua_pushstring (L, "sttdev");
3721 lua_pushnumber (L, stddev);
3722 lua_settable (L, -3);
3723 lua_pushstring (L, "time");
3724 lua_pushnumber (L, tm);
3725 lua_settable (L, -3);
3726 lua_pushstring (L, "hits");
3727 lua_pushinteger (L, hits);
3728 lua_settable (L, -3);
3729 }
3730 }
3731 else {
3732 return luaL_error (L, "invalid arguments");
3733 }
3734
3735 return 1;
3736 }
3737
3738 static gint
lua_config_get_symbol_parent(lua_State * L)3739 lua_config_get_symbol_parent (lua_State *L)
3740 {
3741 LUA_TRACE_POINT;
3742 struct rspamd_config *cfg = lua_check_config (L, 1);
3743 const gchar *sym = luaL_checkstring (L, 2), *parent;
3744
3745 if (cfg != NULL && sym != NULL) {
3746 parent = rspamd_symcache_get_parent (cfg->cache, sym);
3747
3748 if (parent) {
3749 lua_pushstring (L, parent);
3750 }
3751 else {
3752 lua_pushnil (L);
3753 }
3754 }
3755 else {
3756 return luaL_error (L, "invalid arguments");
3757 }
3758
3759 return 1;
3760 }
3761
3762 static gint
lua_config_get_group_symbols(lua_State * L)3763 lua_config_get_group_symbols (lua_State *L)
3764 {
3765 LUA_TRACE_POINT;
3766 struct rspamd_config *cfg = lua_check_config (L, 1);
3767 const gchar *gr_name = luaL_checkstring (L, 2);
3768
3769 if (cfg != NULL && gr_name != NULL) {
3770 struct rspamd_symbols_group *group;
3771
3772 group = g_hash_table_lookup (cfg->groups, gr_name);
3773
3774 if (group == NULL) {
3775 lua_pushnil (L);
3776 }
3777 else {
3778 guint i = 1;
3779 gpointer k, v;
3780 GHashTableIter it;
3781
3782 lua_createtable (L, g_hash_table_size (group->symbols), 0);
3783 g_hash_table_iter_init (&it, group->symbols);
3784
3785 while (g_hash_table_iter_next (&it, &k, &v)) {
3786 lua_pushstring (L, k);
3787 lua_rawseti (L, -2, i);
3788 i ++;
3789 }
3790 }
3791 }
3792 else {
3793 return luaL_error (L, "invalid arguments");
3794 }
3795
3796 return 1;
3797 }
3798
3799 static gint
lua_config_get_groups(lua_State * L)3800 lua_config_get_groups (lua_State *L)
3801 {
3802 LUA_TRACE_POINT;
3803 struct rspamd_config *cfg = lua_check_config (L, 1);
3804 gboolean need_private;
3805 struct rspamd_symbols_group *gr;
3806 GHashTableIter it;
3807 gpointer k, v;
3808
3809 if (cfg) {
3810 if (lua_isboolean (L, 2)) {
3811 need_private = lua_toboolean (L, 2);
3812 }
3813 else {
3814 need_private = !(cfg->public_groups_only);
3815 }
3816
3817 lua_createtable (L, 0, g_hash_table_size (cfg->groups));
3818 g_hash_table_iter_init (&it, cfg->groups);
3819
3820 while (g_hash_table_iter_next (&it, &k, &v)) {
3821 gr = (struct rspamd_symbols_group *)v;
3822
3823 if (need_private || (gr->flags & RSPAMD_SYMBOL_GROUP_PUBLIC)) {
3824 lua_createtable (L, 0, 4);
3825
3826 lua_pushstring (L, gr->description);
3827 lua_setfield (L, -2, "description");
3828 lua_pushnumber (L, gr->max_score);
3829 lua_setfield (L, -2, "max_score");
3830 lua_pushboolean (L, (gr->flags & RSPAMD_SYMBOL_GROUP_PUBLIC) != 0);
3831 lua_setfield (L, -2, "is_public");
3832 /* TODO: maybe push symbols as well */
3833
3834 /* Parent table indexed by group name */
3835 lua_setfield (L, -2, gr->name);
3836 }
3837 }
3838 }
3839 else {
3840 return luaL_error (L, "invalid arguments");
3841 }
3842
3843 return 1;
3844 }
3845
3846 static gint
lua_config_register_finish_script(lua_State * L)3847 lua_config_register_finish_script (lua_State *L)
3848 {
3849 LUA_TRACE_POINT;
3850 struct rspamd_config *cfg = lua_check_config (L, 1);
3851 struct rspamd_config_cfg_lua_script *sc;
3852
3853 if (cfg != NULL && lua_type (L, 2) == LUA_TFUNCTION) {
3854 sc = rspamd_mempool_alloc0 (cfg->cfg_pool, sizeof (*sc));
3855 lua_pushvalue (L, 2);
3856 sc->cbref = luaL_ref (L, LUA_REGISTRYINDEX);
3857 DL_APPEND (cfg->on_term_scripts, sc);
3858 }
3859 else {
3860 return luaL_error (L, "invalid arguments");
3861 }
3862
3863 return 0;
3864 }
3865
3866 static inline bool
rspamd_lua_config_check_settings_symbols_object(const ucl_object_t * obj)3867 rspamd_lua_config_check_settings_symbols_object (const ucl_object_t *obj)
3868 {
3869 if (obj == NULL) {
3870 /* Semantically valid */
3871 return true;
3872 }
3873
3874 if (ucl_object_type (obj) == UCL_OBJECT) {
3875 /* Key-value mapping - should be okay */
3876 return true;
3877 }
3878
3879 if (ucl_object_type (obj) == UCL_ARRAY) {
3880 /* Okay if empty */
3881 if (obj->len == 0) {
3882 return true;
3883 }
3884 }
3885
3886 /* Everything else not okay */
3887 return false;
3888 }
3889
3890 static gint
lua_config_register_settings_id(lua_State * L)3891 lua_config_register_settings_id (lua_State *L)
3892 {
3893 LUA_TRACE_POINT;
3894 struct rspamd_config *cfg = lua_check_config (L, 1);
3895 const gchar *settings_name = luaL_checkstring (L, 2);
3896
3897 if (cfg != NULL && settings_name) {
3898 ucl_object_t *sym_enabled, *sym_disabled;
3899 enum rspamd_config_settings_policy policy = RSPAMD_SETTINGS_POLICY_DEFAULT;
3900
3901 sym_enabled = ucl_object_lua_import (L, 3);
3902
3903 if (!rspamd_lua_config_check_settings_symbols_object (sym_enabled)) {
3904 ucl_object_unref (sym_enabled);
3905
3906 return luaL_error (L, "invalid symbols enabled");
3907 }
3908
3909 sym_disabled = ucl_object_lua_import (L, 4);
3910
3911 if (!rspamd_lua_config_check_settings_symbols_object (sym_disabled)) {
3912 ucl_object_unref (sym_enabled);
3913 ucl_object_unref (sym_disabled);
3914
3915 return luaL_error (L, "invalid symbols enabled");
3916 }
3917
3918 /* Check policy */
3919 if (lua_isstring (L, 5)) {
3920 const gchar *policy_str = lua_tostring (L, 5);
3921
3922 if (strcmp (policy_str, "default") == 0) {
3923 policy = RSPAMD_SETTINGS_POLICY_DEFAULT;
3924 }
3925 else if (strcmp (policy_str, "implicit_allow") == 0) {
3926 policy = RSPAMD_SETTINGS_POLICY_IMPLICIT_ALLOW;
3927 }
3928 else if (strcmp (policy_str, "implicit_deny") == 0) {
3929 policy = RSPAMD_SETTINGS_POLICY_IMPLICIT_DENY;
3930 }
3931 else {
3932 return luaL_error (L, "invalid settings policy: %s", policy_str);
3933 }
3934 }
3935 else {
3936 /* Apply heuristic */
3937 if (!sym_enabled) {
3938 policy = RSPAMD_SETTINGS_POLICY_IMPLICIT_ALLOW;
3939 }
3940 }
3941
3942 rspamd_config_register_settings_id (cfg, settings_name, sym_enabled,
3943 sym_disabled, policy);
3944
3945 if (sym_enabled) {
3946 ucl_object_unref (sym_enabled);
3947 }
3948
3949 if (sym_disabled) {
3950 ucl_object_unref (sym_disabled);
3951 }
3952 }
3953 else {
3954 return luaL_error (L, "invalid arguments");
3955 }
3956
3957 return 0;
3958 }
3959
3960 static gint
lua_config_register_monitored(lua_State * L)3961 lua_config_register_monitored (lua_State *L)
3962 {
3963 LUA_TRACE_POINT;
3964 struct rspamd_config *cfg = lua_check_config (L, 1);
3965 struct rspamd_monitored *m, **pm;
3966 const gchar *url, *type;
3967 ucl_object_t *params = NULL;
3968
3969 url = lua_tostring (L, 2);
3970 type = lua_tostring (L, 3);
3971
3972 if (cfg != NULL && url != NULL && type != NULL) {
3973 if (g_ascii_strcasecmp (type, "dns") == 0) {
3974 lua_Debug ar;
3975
3976 if (lua_type (L, 4) == LUA_TTABLE) {
3977 params = ucl_object_lua_import (L, 4);
3978 }
3979
3980 /* Get lua line and source */
3981 lua_getstack (L, 1, &ar);
3982 lua_getinfo (L, "nSl", &ar);
3983
3984 m = rspamd_monitored_create_ (cfg->monitored_ctx, url,
3985 RSPAMD_MONITORED_DNS, RSPAMD_MONITORED_DEFAULT,
3986 params, ar.short_src);
3987
3988 if (m) {
3989 pm = lua_newuserdata (L, sizeof (*pm));
3990 *pm = m;
3991 rspamd_lua_setclass (L, "rspamd{monitored}", -1);
3992 }
3993 else {
3994 lua_pushnil (L);
3995 }
3996
3997 if (params) {
3998 ucl_object_unref (params);
3999 }
4000 }
4001 else {
4002 return luaL_error (L, "invalid monitored type: %s", type);
4003 }
4004 }
4005 else {
4006 return luaL_error (L, "invalid arguments");
4007 }
4008
4009 return 1;
4010 }
4011
4012 static gint
lua_config_add_doc(lua_State * L)4013 lua_config_add_doc (lua_State *L)
4014 {
4015 LUA_TRACE_POINT;
4016 struct rspamd_config *cfg;
4017 const gchar *path = NULL, *option, *doc_string;
4018 const gchar *type_str = NULL, *default_value = NULL;
4019 ucl_type_t type = UCL_NULL;
4020 gboolean required = FALSE;
4021 GError *err = NULL;
4022
4023 cfg = lua_check_config (L, 1);
4024
4025 if (lua_type (L, 2 ) == LUA_TSTRING) {
4026 path = luaL_checkstring (L, 2);
4027 }
4028
4029 option = luaL_checkstring (L, 3);
4030 doc_string = luaL_checkstring (L, 4);
4031
4032 if (cfg && option && doc_string) {
4033 if (lua_type (L, 5) == LUA_TTABLE) {
4034 if (!rspamd_lua_parse_table_arguments (L, 5, &err,
4035 RSPAMD_LUA_PARSE_ARGUMENTS_DEFAULT,
4036 "type=S;default=S;required=B",
4037 &type_str, &default_value, &required)) {
4038 msg_err_config ("cannot get parameters list: %e", err);
4039
4040 if (err) {
4041 g_error_free (err);
4042 }
4043
4044 if (type_str) {
4045 if (!ucl_object_string_to_type (type_str, &type)) {
4046 msg_err_config ("invalid type: %s", type_str);
4047 }
4048 }
4049 }
4050 }
4051
4052 rspamd_rcl_add_doc_by_path (cfg, path, doc_string, option,
4053 type, NULL, 0, default_value, required);
4054 }
4055 else {
4056 return luaL_error (L, "invalid arguments");
4057 }
4058
4059 return 0;
4060 }
4061
4062 static gint
lua_config_add_example(lua_State * L)4063 lua_config_add_example (lua_State *L)
4064 {
4065 LUA_TRACE_POINT;
4066 struct rspamd_config *cfg;
4067 const gchar *path = NULL, *option, *doc_string, *example;
4068 gsize example_len;
4069
4070 cfg = lua_check_config (L, 1);
4071
4072 if (lua_type (L, 2 ) == LUA_TSTRING) {
4073 path = luaL_checkstring (L, 2);
4074 }
4075
4076 option = luaL_checkstring (L, 3);
4077 doc_string = luaL_checkstring (L, 4);
4078 example = luaL_checklstring (L, 5, &example_len);
4079
4080 if (cfg && option && doc_string && example) {
4081
4082 rspamd_rcl_add_doc_by_example (cfg, path, doc_string, option,
4083 example, example_len);
4084 }
4085 else {
4086 return luaL_error (L, "invalid arguments");
4087 }
4088
4089 return 0;
4090 }
4091
4092 static gint
lua_config_get_cpu_flags(lua_State * L)4093 lua_config_get_cpu_flags (lua_State *L)
4094 {
4095 LUA_TRACE_POINT;
4096 struct rspamd_config *cfg = lua_check_config (L, 1);
4097 struct rspamd_cryptobox_library_ctx *crypto_ctx;
4098
4099 if (cfg != NULL) {
4100 crypto_ctx = cfg->libs_ctx->crypto_ctx;
4101 lua_newtable (L);
4102
4103 if (crypto_ctx->cpu_config & CPUID_SSSE3) {
4104 lua_pushstring (L, "ssse3");
4105 lua_pushboolean (L, true);
4106 lua_settable (L, -3);
4107 }
4108 if (crypto_ctx->cpu_config & CPUID_SSE41) {
4109 lua_pushstring (L, "sse41");
4110 lua_pushboolean (L, true);
4111 lua_settable (L, -3);
4112 }
4113 if (crypto_ctx->cpu_config & CPUID_SSE42) {
4114 lua_pushstring (L, "sse42");
4115 lua_pushboolean (L, true);
4116 lua_settable (L, -3);
4117 }
4118 if (crypto_ctx->cpu_config & CPUID_SSE2) {
4119 lua_pushstring (L, "sse2");
4120 lua_pushboolean (L, true);
4121 lua_settable (L, -3);
4122 }
4123 if (crypto_ctx->cpu_config & CPUID_SSE3) {
4124 lua_pushstring (L, "sse3");
4125 lua_pushboolean (L, true);
4126 lua_settable (L, -3);
4127 }
4128 if (crypto_ctx->cpu_config & CPUID_AVX) {
4129 lua_pushstring (L, "avx");
4130 lua_pushboolean (L, true);
4131 lua_settable (L, -3);
4132 }
4133 if (crypto_ctx->cpu_config & CPUID_AVX2) {
4134 lua_pushstring (L, "avx2");
4135 lua_pushboolean (L, true);
4136 lua_settable (L, -3);
4137 }
4138 }
4139 else {
4140 return luaL_error (L, "invalid arguments");
4141 }
4142
4143 return 1;
4144 }
4145
4146 static gint
lua_config_has_torch(lua_State * L)4147 lua_config_has_torch (lua_State *L)
4148 {
4149 msg_warn ("use of the obsoleted `has_torch` function");
4150 lua_pushboolean (L, false);
4151
4152 return 1;
4153 }
4154
4155 static gint
lua_config_experimental_enabled(lua_State * L)4156 lua_config_experimental_enabled (lua_State *L)
4157 {
4158 LUA_TRACE_POINT;
4159 struct rspamd_config *cfg = lua_check_config (L, 1);
4160
4161 if (cfg != NULL) {
4162 lua_pushboolean (L, cfg->enable_experimental);
4163 }
4164 else {
4165 return luaL_error (L, "invalid arguments");
4166 }
4167
4168 return 1;
4169 }
4170
4171 struct rspamd_lua_include_trace_cbdata {
4172 lua_State *L;
4173 gint cbref;
4174 };
4175
4176 static void
lua_include_trace_cb(struct ucl_parser * parser,const ucl_object_t * parent,const ucl_object_t * args,const char * path,size_t pathlen,void * user_data)4177 lua_include_trace_cb (struct ucl_parser *parser,
4178 const ucl_object_t *parent,
4179 const ucl_object_t *args,
4180 const char *path,
4181 size_t pathlen,
4182 void *user_data)
4183 {
4184 struct rspamd_lua_include_trace_cbdata *cbdata =
4185 (struct rspamd_lua_include_trace_cbdata *)user_data;
4186 gint err_idx;
4187 lua_State *L;
4188
4189 L = cbdata->L;
4190 lua_pushcfunction (L, &rspamd_lua_traceback);
4191 err_idx = lua_gettop (L);
4192
4193 lua_rawgeti (L, LUA_REGISTRYINDEX, cbdata->cbref);
4194 /* Current filename */
4195 lua_pushstring (L, ucl_parser_get_cur_file (parser));
4196 /* Included filename */
4197 lua_pushlstring (L, path, pathlen);
4198 /* Params */
4199 if (args) {
4200 ucl_object_push_lua (L, args, true);
4201 }
4202 else {
4203 lua_newtable (L);
4204 }
4205 /* Parent */
4206 if (parent) {
4207 lua_pushstring (L, ucl_object_key (parent));
4208 }
4209 else {
4210 lua_pushnil (L);
4211 }
4212
4213 if (lua_pcall (L, 4, 0, err_idx) != 0) {
4214 msg_err ("lua call to local include trace failed: %s", lua_tostring (L, -1));
4215 }
4216
4217 lua_settop (L, err_idx - 1);
4218 }
4219
4220 #define LUA_TABLE_TO_HASH(htb, idx) do { \
4221 lua_pushstring (L, (idx)); \
4222 lua_gettable (L, -2); \
4223 if (lua_isstring (L, -1)) { \
4224 g_hash_table_insert ((htb), (idx), g_strdup (lua_tostring (L, -1))); \
4225 } \
4226 lua_pop (L, 1); \
4227 } while(0)
4228
4229 static gint
lua_config_load_ucl(lua_State * L)4230 lua_config_load_ucl (lua_State *L)
4231 {
4232 struct rspamd_config *cfg = lua_check_config (L, 1);
4233 const gchar *filename;
4234 GHashTable *paths = g_hash_table_new_full (rspamd_str_hash, rspamd_str_equal,
4235 NULL, g_free);
4236 GError *err = NULL;
4237
4238 if (cfg) {
4239 if (lua_isstring (L, 2)) {
4240 filename = lua_tostring (L, 2);
4241 }
4242 else {
4243 filename = RSPAMD_CONFDIR "/rspamd.conf";
4244 }
4245
4246 /* Convert rspamd_paths */
4247 lua_getglobal (L, "rspamd_paths");
4248
4249 if (lua_istable (L, -1)) {
4250 LUA_TABLE_TO_HASH(paths, RSPAMD_CONFDIR_INDEX);
4251 LUA_TABLE_TO_HASH(paths, RSPAMD_LOCAL_CONFDIR_INDEX);
4252 LUA_TABLE_TO_HASH(paths, RSPAMD_RUNDIR_INDEX);
4253 LUA_TABLE_TO_HASH(paths, RSPAMD_DBDIR_INDEX);
4254 LUA_TABLE_TO_HASH(paths, RSPAMD_LOGDIR_INDEX);
4255 LUA_TABLE_TO_HASH(paths, RSPAMD_WWWDIR_INDEX);
4256 LUA_TABLE_TO_HASH(paths, RSPAMD_PLUGINSDIR_INDEX);
4257 LUA_TABLE_TO_HASH(paths, RSPAMD_RULESDIR_INDEX);
4258 LUA_TABLE_TO_HASH(paths, RSPAMD_LUALIBDIR_INDEX);
4259 LUA_TABLE_TO_HASH(paths, RSPAMD_PREFIX_INDEX);
4260 }
4261
4262 lua_pop (L, 1);
4263
4264 if (lua_isfunction (L, 3)) {
4265 struct rspamd_lua_include_trace_cbdata cbd;
4266
4267 lua_pushvalue (L, 3);
4268 cbd.cbref = luaL_ref (L, LUA_REGISTRYINDEX);
4269 cbd.L = L;
4270
4271 if (!rspamd_config_parse_ucl (cfg, filename, paths,
4272 lua_include_trace_cb, &cbd, lua_toboolean (L, 4), &err)) {
4273 luaL_unref (L, LUA_REGISTRYINDEX, cbd.cbref);
4274 lua_pushboolean (L, false);
4275 lua_pushfstring (L, "failed to load config: %s", err->message);
4276 g_error_free (err);
4277 g_hash_table_unref (paths);
4278
4279 return 2;
4280 }
4281
4282 luaL_unref (L, LUA_REGISTRYINDEX, cbd.cbref);
4283 }
4284 else {
4285 if (!rspamd_config_parse_ucl (cfg, filename, paths, NULL, NULL,
4286 lua_toboolean (L, 3), &err)) {
4287 lua_pushboolean (L, false);
4288 lua_pushfstring (L, "failed to load config: %s", err->message);
4289 g_error_free (err);
4290 g_hash_table_unref (paths);
4291
4292 return 2;
4293 }
4294 }
4295
4296 rspamd_rcl_maybe_apply_lua_transform (cfg);
4297 rspamd_config_calculate_cksum (cfg);
4298 }
4299 else {
4300 return luaL_error (L, "invalid arguments");
4301 }
4302
4303 g_hash_table_unref (paths);
4304 lua_pushboolean (L, true);
4305
4306 return 1;
4307 }
4308
4309 #undef IDX_TO_HASH
4310
4311 static gint
lua_config_parse_rcl(lua_State * L)4312 lua_config_parse_rcl (lua_State *L)
4313 {
4314 LUA_TRACE_POINT;
4315 struct rspamd_config *cfg = lua_check_config (L, 1);
4316 GHashTable *excluded = g_hash_table_new_full (rspamd_str_hash, rspamd_str_equal,
4317 g_free, NULL);
4318 GError *err = NULL;
4319 struct rspamd_rcl_section *top;
4320
4321 if (cfg) {
4322 if (lua_istable (L, 2)) {
4323 lua_pushvalue (L, 2);
4324
4325 for (lua_pushnil (L); lua_next (L, -2); lua_pop (L, 1)) {
4326 g_hash_table_insert (excluded, g_strdup (lua_tostring (L, -1)),
4327 GINT_TO_POINTER (-1));
4328 }
4329
4330 lua_pop (L, 1);
4331 }
4332
4333 top = rspamd_rcl_config_init (cfg, excluded);
4334
4335 if (!rspamd_rcl_parse (top, cfg, cfg, cfg->cfg_pool, cfg->rcl_obj, &err)) {
4336 lua_pushboolean (L, false);
4337 lua_pushfstring (L, "failed to load config: %s", err->message);
4338 g_error_free (err);
4339 g_hash_table_unref (excluded);
4340 rspamd_rcl_section_free (top);
4341
4342 return 2;
4343 }
4344 }
4345 else {
4346 return luaL_error (L, "invalid arguments");
4347 }
4348
4349 g_hash_table_unref (excluded);
4350 rspamd_rcl_section_free (top);
4351 lua_pushboolean (L, true);
4352
4353 return 1;
4354 }
4355
4356 static gint
lua_config_init_modules(lua_State * L)4357 lua_config_init_modules (lua_State *L)
4358 {
4359 LUA_TRACE_POINT;
4360 struct rspamd_config *cfg = lua_check_config (L, 1);
4361
4362 if (cfg != NULL) {
4363 rspamd_lua_post_load_config (cfg);
4364 lua_pushboolean (L, rspamd_init_filters (cfg, false, false));
4365 }
4366 else {
4367 return luaL_error (L, "invalid arguments");
4368 }
4369
4370 return 1;
4371 }
4372
4373 static gint
lua_config_init_subsystem(lua_State * L)4374 lua_config_init_subsystem (lua_State *L)
4375 {
4376 LUA_TRACE_POINT;
4377 struct rspamd_config *cfg = lua_check_config (L, 1);
4378 const gchar *subsystem = luaL_checkstring (L, 2);
4379 gchar **parts;
4380 guint nparts, i;
4381
4382 if (cfg != NULL && subsystem != NULL) {
4383 parts = g_strsplit_set (subsystem, ";,", -1);
4384 nparts = g_strv_length (parts);
4385
4386 for (i = 0; i < nparts; i ++) {
4387 if (strcmp (parts[i], "filters") == 0) {
4388 rspamd_lua_post_load_config (cfg);
4389 rspamd_init_filters (cfg, false, false);
4390 }
4391 else if (strcmp (parts[i], "langdet") == 0) {
4392 if (!cfg->lang_det) {
4393 cfg->lang_det = rspamd_language_detector_init (cfg);
4394 rspamd_mempool_add_destructor (cfg->cfg_pool,
4395 (rspamd_mempool_destruct_t) rspamd_language_detector_unref,
4396 cfg->lang_det);
4397 }
4398 }
4399 else if (strcmp (parts[i], "stat") == 0) {
4400 rspamd_stat_init (cfg, NULL);
4401 }
4402 else if (strcmp (parts[i], "dns") == 0) {
4403 struct ev_loop *ev_base = lua_check_ev_base (L, 3);
4404
4405 if (ev_base) {
4406 cfg->dns_resolver = rspamd_dns_resolver_init (rspamd_log_default_logger (),
4407 ev_base,
4408 cfg);
4409 }
4410 else {
4411 g_strfreev (parts);
4412
4413 return luaL_error (L, "no event base specified");
4414 }
4415 }
4416 else if (strcmp (parts[i], "symcache") == 0) {
4417 rspamd_symcache_init (cfg->cache);
4418 }
4419 else {
4420 int ret = luaL_error (L, "invalid param: %s", parts[i]);
4421 g_strfreev (parts);
4422
4423 return ret;
4424 }
4425 }
4426
4427 g_strfreev (parts);
4428 }
4429 else {
4430 return luaL_error (L, "invalid arguments");
4431 }
4432
4433 return 0;
4434 }
4435
4436 static gint
lua_config_register_re_selector(lua_State * L)4437 lua_config_register_re_selector (lua_State *L)
4438 {
4439 LUA_TRACE_POINT;
4440 struct rspamd_config *cfg = lua_check_config (L, 1);
4441 const gchar *name = luaL_checkstring (L, 2);
4442 const gchar *selector_str = luaL_checkstring (L, 3);
4443 const gchar *delimiter = "";
4444 bool flatten = false;
4445 gint top = lua_gettop (L);
4446 bool res = false;
4447
4448 if (cfg && name && selector_str) {
4449 if (lua_gettop (L) >= 4) {
4450 delimiter = luaL_checkstring (L, 4);
4451
4452 if (lua_isboolean (L, 5)) {
4453 flatten = lua_toboolean (L, 5);
4454 }
4455 }
4456
4457 if (luaL_dostring (L, "return require \"lua_selectors\"") != 0) {
4458 msg_warn_config ("cannot require lua_selectors: %s",
4459 lua_tostring (L, -1));
4460 }
4461 else {
4462 if (lua_type (L, -1) != LUA_TTABLE) {
4463 msg_warn_config ("lua selectors must return "
4464 "table and not %s",
4465 lua_typename (L, lua_type (L, -1)));
4466 }
4467 else {
4468 lua_pushstring (L, "create_selector_closure");
4469 lua_gettable (L, -2);
4470
4471 if (lua_type (L, -1) != LUA_TFUNCTION) {
4472 msg_warn_config ("create_selector_closure must return "
4473 "function and not %s",
4474 lua_typename (L, lua_type (L, -1)));
4475 }
4476 else {
4477 gint err_idx, ret;
4478 struct rspamd_config **pcfg;
4479
4480 lua_pushcfunction (L, &rspamd_lua_traceback);
4481 err_idx = lua_gettop (L);
4482
4483 /* Push function */
4484 lua_pushvalue (L, -2);
4485
4486 pcfg = lua_newuserdata (L, sizeof (*pcfg));
4487 rspamd_lua_setclass (L, "rspamd{config}", -1);
4488 *pcfg = cfg;
4489 lua_pushstring (L, selector_str);
4490 lua_pushstring (L, delimiter);
4491 lua_pushboolean (L, flatten);
4492
4493 if ((ret = lua_pcall (L, 4, 1, err_idx)) != 0) {
4494 msg_err_config ("call to create_selector_closure lua "
4495 "script failed (%d): %s", ret,
4496 lua_tostring (L, -1));
4497 }
4498 else {
4499 if (lua_type (L, -1) != LUA_TFUNCTION) {
4500 msg_warn_config ("create_selector_closure "
4501 "invocation must return "
4502 "function and not %s",
4503 lua_typename (L, lua_type (L, -1)));
4504 }
4505 else {
4506 ret = luaL_ref (L, LUA_REGISTRYINDEX);
4507 rspamd_re_cache_add_selector (cfg->re_cache,
4508 name, ret);
4509 res = true;
4510 }
4511 }
4512 }
4513 }
4514 }
4515 }
4516 else {
4517 return luaL_error (L, "invalid arguments");
4518 }
4519
4520 lua_settop (L, top);
4521 lua_pushboolean (L, res);
4522
4523 if (res) {
4524 msg_info_config ("registered regexp selector %s", name);
4525 }
4526
4527 return 1;
4528 }
4529
4530 static gint
lua_config_get_tld_path(lua_State * L)4531 lua_config_get_tld_path (lua_State *L)
4532 {
4533 LUA_TRACE_POINT;
4534 struct rspamd_config *cfg = lua_check_config (L, 1);
4535
4536 if (cfg != NULL) {
4537 lua_pushstring (L, cfg->tld_file);
4538 }
4539 else {
4540 return luaL_error (L, "invalid arguments");
4541 }
4542
4543 return 1;
4544 }
4545
4546 static gint
lua_config_get_dns_max_requests(lua_State * L)4547 lua_config_get_dns_max_requests (lua_State *L)
4548 {
4549 LUA_TRACE_POINT;
4550 struct rspamd_config *cfg = lua_check_config (L, 1);
4551
4552 if (cfg != NULL) {
4553 lua_pushinteger (L, cfg->dns_max_requests);
4554 }
4555 else {
4556 return luaL_error (L, "invalid arguments");
4557 }
4558
4559 return 1;
4560 }
4561
4562 static gint
lua_monitored_alive(lua_State * L)4563 lua_monitored_alive (lua_State *L)
4564 {
4565 LUA_TRACE_POINT;
4566 struct rspamd_monitored *m = lua_check_monitored (L, 1);
4567
4568 if (m) {
4569 lua_pushboolean (L, rspamd_monitored_alive (m));
4570 }
4571 else {
4572 return luaL_error (L, "invalid arguments");
4573 }
4574
4575 return 1;
4576 }
4577
4578 static gint
lua_monitored_offline(lua_State * L)4579 lua_monitored_offline (lua_State *L)
4580 {
4581 LUA_TRACE_POINT;
4582 struct rspamd_monitored *m = lua_check_monitored (L, 1);
4583
4584 if (m) {
4585 lua_pushnumber (L, rspamd_monitored_offline_time (m));
4586 }
4587 else {
4588 return luaL_error (L, "invalid arguments");
4589 }
4590
4591 return 1;
4592 }
4593
4594 static gint
lua_monitored_total_offline(lua_State * L)4595 lua_monitored_total_offline (lua_State *L)
4596 {
4597 LUA_TRACE_POINT;
4598 struct rspamd_monitored *m = lua_check_monitored (L, 1);
4599
4600 if (m) {
4601 lua_pushnumber (L, rspamd_monitored_total_offline_time (m));
4602 }
4603 else {
4604 return luaL_error (L, "invalid arguments");
4605 }
4606
4607 return 1;
4608 }
4609
4610 static gint
lua_monitored_latency(lua_State * L)4611 lua_monitored_latency (lua_State *L)
4612 {
4613 LUA_TRACE_POINT;
4614 struct rspamd_monitored *m = lua_check_monitored (L, 1);
4615
4616 if (m) {
4617 lua_pushnumber (L, rspamd_monitored_latency (m));
4618 }
4619 else {
4620 return luaL_error (L, "invalid arguments");
4621 }
4622
4623 return 1;
4624 }
4625
4626 void
luaopen_config(lua_State * L)4627 luaopen_config (lua_State * L)
4628 {
4629 rspamd_lua_new_class (L, "rspamd{config}", configlib_m);
4630
4631 lua_pop (L, 1);
4632
4633 rspamd_lua_new_class (L, "rspamd{monitored}", monitoredlib_m);
4634
4635 lua_pop (L, 1);
4636 }
4637
4638 void
lua_call_finish_script(struct rspamd_config_cfg_lua_script * sc,struct rspamd_task * task)4639 lua_call_finish_script (struct rspamd_config_cfg_lua_script *sc,
4640 struct rspamd_task *task) {
4641
4642 struct rspamd_task **ptask;
4643 struct thread_entry *thread;
4644
4645 thread = lua_thread_pool_get_for_task (task);
4646 thread->task = task;
4647
4648 lua_State *L = thread->lua_state;
4649
4650 lua_rawgeti (L, LUA_REGISTRYINDEX, sc->cbref);
4651
4652 ptask = lua_newuserdata (L, sizeof (struct rspamd_task *));
4653 rspamd_lua_setclass (L, "rspamd{task}", - 1);
4654 *ptask = task;
4655
4656 lua_thread_call (thread, 1);
4657 }
4658