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