aboutsummaryrefslogtreecommitdiffstats
path: root/apps/workflowengine/l10n/ast.js
blob: cc24b48c02c948e268577180fb6f3c1f13e56e85 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
OC.L10N.register(
    "workflowengine",
    {
    "The given operator is invalid" : "L'operador apurríu ye inválidu",
    "The given regular expression is invalid" : "La espresión regular apurrida ye inválida",
    "The given file size is invalid" : "El tamañu del ficheru apurríu ye inválidu",
    "The given tag id is invalid" : "La ID d'etiqueta apurrida ye inválida",
    "The given IP range is invalid" : "L'intervalu d'IPs ye inválida",
    "The given IP range is not valid for IPv4" : "L'intervalu d'IPs nun ye válidu pa IPv4",
    "The given IP range is not valid for IPv6" : "L'intervalu d'IPs nun ye válidu pa IPv6",
    "The given time span is invalid" : "L'intervalu de tiempu apurríu nun ye válidu",
    "The given start time is invalid" : "La hora de comienzu ye inválida",
    "The given end time is invalid" : "La hora de fin ye inválida",
    "The given group does not exist" : "El grupu apurríu nun esiste",
    "File" : "Ficheru",
    "File created" : "Creóse'l ficheru",
    "File updated" : "Anovóse'l ficheru",
    "File renamed" : "Renomóse'l ficheru",
    "File deleted" : "Desanicióse'l ficheru",
    "File accessed" : "Accedióse al ficheru",
    "File copied" : "Copióse'l ficheru",
    "Tag assigned" : "Asignóse la etiqueta",
    "Someone" : "Daquień",
    "%s created %s" : "%s creó «%s»",
    "%s modified %s" : "%s modificó «%s»",
    "%s deleted %s" : "%s desanició «%s»",
    "%s accessed %s" : "%s accedió a «%s»",
    "%s renamed %s" : "%s renomó «%s»",
    "%s copied %s" : "%s copió «%s»",
    "%s assigned %s to %s" : "%s asignó «%s» a %s",
    "Operation #%s does not exist" : "La operación #%s nun esiste",
    "Entity %s does not exist" : "La entidá «%s» nun esiste",
    "Entity %s is invalid" : "La entidá «%s» ye inválida",
    "No events are chosen." : "Nun s'escoyó nengún eventu.",
    "Entity %s has no event %s" : "La entidá «%s» nun tien nengún eventu «%s»",
    "Operation %s does not exist" : "La operación «%s» nun esiste",
    "Operation %s is invalid" : "La operación «%s» ye inválida",
    "Invalid check provided" : "Fornióse una comprobación inválida",
    "Check %s does not exist" : "La comprobación «%s» nun esiste",
    "Check %s is invalid" : "La comprobación «%s» ye inválida",
    "Check %s is not allowed with this entity" : "La comprobación «%s» nun ta permitida con esta entidá",
    "Check #%s does not exist" : "La comprobación #%s nun esiste",
    "Check %s is invalid or does not exist" : "La comprobación «%s» ye inválida o nun esiste",
    "Flow" : "Fluxu",
    "Remove filter" : "Quitar la peñera",
    "Folder" : "Carpeta",
    "Images" : "Imáxenes",
    "Office documents" : "Documentos ofimáticos",
    "PDF documents" : "Documentos PDF",
    "Android client" : "Veceru p'Android",
    "iOS client" : "Veceru pa iOS",
    "Desktop client" : "Veceru pa ordenadores",
    "Select groups" : "Seleicionar grupos",
    "Groups" : "Grupos",
    "The configuration is invalid" : "La configuración ye inválida",
    "Active" : "Activa",
    "Save" : "Guardar",
    "and" : "y",
    "Cancel" : "Encaboxar",
    "Delete" : "Desaniciar",
    "Available flows" : "Fluxos disponibles",
    "No flows installed" : "Nun s'instaló nengún fluxu",
    "Ask your administrator to install new flows." : "Pidi a la alministración qu'instale fluxos nuevos.",
    "More flows" : "Más fluxos",
    "Show less" : "Amosar menos",
    "Show more" : "Amosar más",
    "Configured flows" : "Fluxos configuraos",
    "No flows configured" : "Nun se configuró nengún fluxu",
    "matches" : "concasa",
    "does not match" : "nun concasa",
    "is" : "ye",
    "is not" : "nun ye",
    "File name" : "Nome del ficheru",
    "between" : "ente"
},
"nplurals=2; plural=(n != 1);");
**vec; guint i, l; if (str) { vec = g_strsplit_set(str, ",;", -1); if (vec) { l = g_strv_length(vec); for (i = 0; i < l; i++) { str = vec[i]; /* TODO: total shit, rework some day */ if (g_ascii_strcasecmp(str, "virtual") == 0) { ret |= SYMBOL_TYPE_VIRTUAL; ret &= ~SYMBOL_TYPE_NORMAL; ret &= ~SYMBOL_TYPE_CALLBACK; } else if (g_ascii_strcasecmp(str, "callback") == 0) { ret |= SYMBOL_TYPE_CALLBACK; ret &= ~SYMBOL_TYPE_NORMAL; ret &= ~SYMBOL_TYPE_VIRTUAL; } else if (g_ascii_strcasecmp(str, "normal") == 0) { ret |= SYMBOL_TYPE_NORMAL; ret &= ~SYMBOL_TYPE_CALLBACK; ret &= ~SYMBOL_TYPE_VIRTUAL; } else if (g_ascii_strcasecmp(str, "prefilter") == 0) { ret |= SYMBOL_TYPE_PREFILTER | SYMBOL_TYPE_GHOST; } else if (g_ascii_strcasecmp(str, "postfilter") == 0) { ret |= SYMBOL_TYPE_POSTFILTER | SYMBOL_TYPE_GHOST; } else if (g_ascii_strcasecmp(str, "connfilter") == 0 || g_ascii_strcasecmp(str, "conn_filter") == 0) { ret |= SYMBOL_TYPE_CONNFILTER | SYMBOL_TYPE_GHOST; } else if (g_ascii_strcasecmp(str, "idempotent") == 0) { ret |= SYMBOL_TYPE_GHOST | SYMBOL_TYPE_IDEMPOTENT | SYMBOL_TYPE_CALLBACK; } else { gint fl = 0; fl = lua_parse_symbol_flags(str); if (fl == 0) { msg_warn("bad type: %s", str); } else { ret |= fl; } } } g_strfreev(vec); } } return ret; } enum lua_push_symbol_flags_opts { LUA_SYMOPT_FLAG_CREATE_ARRAY = 1u << 0u, LUA_SYMOPT_FLAG_CREATE_MAP = 1u << 1u, LUA_SYMOPT_FLAG_USE_MAP = 1u << 2u, LUA_SYMOPT_FLAG_USE_ARRAY = 1u << 3u, }; #define LUA_SYMOPT_IS_ARRAY(f) ((f) & (LUA_SYMOPT_FLAG_CREATE_ARRAY | LUA_SYMOPT_FLAG_USE_ARRAY)) #define LUA_SYMOPT_IS_CREATE(f) ((f) & (LUA_SYMOPT_FLAG_CREATE_ARRAY | LUA_SYMOPT_FLAG_CREATE_MAP)) #define LUA_OPTION_PUSH(nm) \ do { \ if (LUA_SYMOPT_IS_ARRAY(fl)) { \ lua_pushstring(L, #nm); \ lua_rawseti(L, -2, i++); \ } \ else { \ lua_pushboolean(L, true); \ lua_setfield(L, -2, #nm); \ } \ } while (0) static void lua_push_symbol_flags(lua_State *L, guint flags, enum lua_push_symbol_flags_opts fl) { guint i = 1; if (LUA_SYMOPT_IS_CREATE(fl)) { lua_newtable(L); } if (flags & SYMBOL_TYPE_FINE) { LUA_OPTION_PUSH(fine); } if (flags & SYMBOL_TYPE_EMPTY) { LUA_OPTION_PUSH(empty); } if (flags & SYMBOL_TYPE_EXPLICIT_DISABLE) { LUA_OPTION_PUSH(explicit_disable); } if (flags & SYMBOL_TYPE_EXPLICIT_ENABLE) { LUA_OPTION_PUSH(explicit_enable); } if (flags & SYMBOL_TYPE_IGNORE_PASSTHROUGH) { LUA_OPTION_PUSH(ignore_passthrough); } if (flags & SYMBOL_TYPE_NOSTAT) { LUA_OPTION_PUSH(nostat); } if (flags & SYMBOL_TYPE_IDEMPOTENT) { LUA_OPTION_PUSH(idempotent); } if (flags & SYMBOL_TYPE_MIME_ONLY) { LUA_OPTION_PUSH(mime); } if (flags & SYMBOL_TYPE_TRIVIAL) { LUA_OPTION_PUSH(trivial); } if (flags & SYMBOL_TYPE_SKIPPED) { LUA_OPTION_PUSH(skip); } if (flags & SYMBOL_TYPE_COMPOSITE) { LUA_OPTION_PUSH(composite); } } static gint lua_config_get_symbol_flags(lua_State *L) { struct rspamd_config *cfg = lua_check_config(L, 1); const gchar *name = luaL_checkstring(L, 2); guint flags; if (cfg && name) { flags = rspamd_symcache_get_symbol_flags(cfg->cache, name); if (flags != 0) { lua_push_symbol_flags(L, flags, LUA_SYMOPT_FLAG_CREATE_ARRAY); } else { lua_pushnil(L); } } else { return luaL_error(L, "invalid arguments"); } return 1; } static gint lua_config_register_symbol(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); const gchar *name = NULL, *type_str = NULL, *description = NULL, *group = NULL; double weight = 0, score = NAN, parent_float = NAN; gboolean one_shot = FALSE; gint ret = -1, cbref = -1; guint type = 0, flags = 0; gint64 parent = 0, priority = 0, nshots = 0; GArray *allowed_ids = NULL, *forbidden_ids = NULL; GError *err = NULL; int prev_top = lua_gettop(L); if (cfg) { if (!rspamd_lua_parse_table_arguments(L, 2, &err, RSPAMD_LUA_PARSE_ARGUMENTS_DEFAULT, "name=S;weight=N;callback=F;type=S;priority=I;parent=D;" "score=D;description=S;group=S;one_shot=B;nshots=I", &name, &weight, &cbref, &type_str, &priority, &parent_float, &score, &description, &group, &one_shot, &nshots)) { msg_err_config("bad arguments: %e", err); g_error_free(err); lua_settop(L, prev_top); return luaL_error(L, "invalid arguments"); } /* Deal with flags and ids */ lua_pushstring(L, "flags"); lua_gettable(L, 2); if (lua_type(L, -1) == LUA_TSTRING) { flags = lua_parse_symbol_flags(lua_tostring(L, -1)); } else if (lua_type(L, -1) == LUA_TTABLE) { for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { flags |= lua_parse_symbol_flags(lua_tostring(L, -1)); } } lua_pop(L, 1); /* Clean flags */ lua_pushstring(L, "allowed_ids"); lua_gettable(L, 2); if (lua_type(L, -1) == LUA_TSTRING) { allowed_ids = rspamd_process_id_list(lua_tostring(L, -1)); } else if (lua_type(L, -1) == LUA_TTABLE) { allowed_ids = g_array_sized_new(FALSE, FALSE, sizeof(guint32), rspamd_lua_table_size(L, -1)); for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { guint32 v = lua_tointeger(L, -1); g_array_append_val(allowed_ids, v); } } lua_pop(L, 1); lua_pushstring(L, "forbidden_ids"); lua_gettable(L, 2); if (lua_type(L, -1) == LUA_TSTRING) { forbidden_ids = rspamd_process_id_list(lua_tostring(L, -1)); } else if (lua_type(L, -1) == LUA_TTABLE) { forbidden_ids = g_array_sized_new(FALSE, FALSE, sizeof(guint32), rspamd_lua_table_size(L, -1)); for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { guint32 v = lua_tointeger(L, -1); g_array_append_val(forbidden_ids, v); } } lua_pop(L, 1); if (nshots == 0) { nshots = cfg->default_max_shots; } type = lua_parse_symbol_type(type_str); if (!name && !(type & SYMBOL_TYPE_CALLBACK)) { lua_settop(L, prev_top); return luaL_error(L, "no symbol name but type is not callback"); } else if (!(type & SYMBOL_TYPE_VIRTUAL) && cbref == -1) { lua_settop(L, prev_top); return luaL_error(L, "no callback for symbol %s", name); } if (isnan(parent_float)) { parent = -1; } else { parent = parent_float; } ret = rspamd_register_symbol_fromlua(L, cfg, name, cbref, weight == 0 ? 1.0 : weight, priority, type | flags, parent, allowed_ids, forbidden_ids, FALSE); if (allowed_ids) { g_array_free(allowed_ids, TRUE); } if (forbidden_ids) { g_array_free(forbidden_ids, TRUE); } if (ret != -1) { if (!isnan(score) || group) { if (one_shot) { nshots = 1; } rspamd_config_add_symbol(cfg, name, score, description, group, flags, 0, nshots); lua_pushstring(L, "groups"); lua_gettable(L, 2); if (lua_istable(L, -1)) { for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { if (lua_isstring(L, -1)) { rspamd_config_add_symbol_group(cfg, name, lua_tostring(L, -1)); } else { lua_settop(L, prev_top); return luaL_error(L, "invalid groups element"); } } } lua_pop(L, 1); } lua_pushstring(L, "augmentations"); lua_gettable(L, 2); if (lua_type(L, -1) == LUA_TTABLE) { int tbl_idx = lua_gettop(L); for (lua_pushnil(L); lua_next(L, tbl_idx); lua_pop(L, 1)) { size_t len; const char *augmentation = lua_tolstring(L, -1, &len), *eqsign_pos; /* Find `=` symbol and use it as a separator */ eqsign_pos = memchr(augmentation, '=', len); if (eqsign_pos != NULL && eqsign_pos + 1 < augmentation + len) { rspamd_ftok_t tok; tok.begin = augmentation; tok.len = eqsign_pos - augmentation; char *augentation_name = rspamd_ftokdup(&tok); tok.begin = eqsign_pos + 1; tok.len = (augmentation + len) - tok.begin; char *augmentation_value = rspamd_ftokdup(&tok); if (!rspamd_symcache_add_symbol_augmentation(cfg->cache, ret, augentation_name, augmentation_value)) { lua_settop(L, prev_top); g_free(augmentation_value); g_free(augentation_name); return luaL_error(L, "unknown or invalid augmentation %s in symbol %s", augmentation, name); } g_free(augmentation_value); g_free(augentation_name); } else { /* Just a value */ if (!rspamd_symcache_add_symbol_augmentation(cfg->cache, ret, augmentation, NULL)) { lua_settop(L, prev_top); return luaL_error(L, "unknown augmentation %s in symbol %s", augmentation, name); } } } } } } else { lua_settop(L, prev_top); return luaL_error(L, "invalid arguments"); } lua_settop(L, prev_top); lua_pushinteger(L, ret); return 1; } static gint lua_config_register_symbols(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); gint i, top, idx, ret = -1; const gchar *sym; gdouble weight = 1.0; if (lua_gettop(L) < 3) { if (cfg) { msg_err_config("not enough arguments to register a function"); } lua_error(L); return 0; } if (cfg) { if (lua_type(L, 2) == LUA_TSTRING) { lua_getglobal(L, luaL_checkstring(L, 2)); } else { lua_pushvalue(L, 2); } idx = luaL_ref(L, LUA_REGISTRYINDEX); if (lua_type(L, 3) == LUA_TNUMBER) { weight = lua_tonumber(L, 3); top = 4; } else { top = 3; } sym = luaL_checkstring(L, top++); ret = rspamd_register_symbol_fromlua(L, cfg, sym, idx, weight, 0, SYMBOL_TYPE_CALLBACK, -1, NULL, NULL, FALSE); for (i = top; i <= lua_gettop(L); i++) { if (lua_type(L, i) == LUA_TTABLE) { lua_pushvalue(L, i); lua_pushnil(L); while (lua_next(L, -2)) { lua_pushvalue(L, -2); sym = luaL_checkstring(L, -2); rspamd_symcache_add_symbol(cfg->cache, sym, 0, NULL, NULL, SYMBOL_TYPE_VIRTUAL, ret); lua_pop(L, 2); } lua_pop(L, 1); } else if (lua_type(L, i) == LUA_TSTRING) { sym = luaL_checkstring(L, i); rspamd_symcache_add_symbol(cfg->cache, sym, 0, NULL, NULL, SYMBOL_TYPE_VIRTUAL, ret); } } } lua_pushinteger(L, ret); return 1; } static gint lua_config_register_virtual_symbol(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); const gchar *name; double weight; gint ret = -1, parent = -1; if (cfg) { name = luaL_checkstring(L, 2); weight = luaL_checknumber(L, 3); if (lua_gettop(L) > 3) { parent = lua_tonumber(L, 4); } if (name) { ret = rspamd_symcache_add_symbol(cfg->cache, name, weight > 0 ? 0 : -1, NULL, NULL, SYMBOL_TYPE_VIRTUAL, parent); } } lua_pushinteger(L, ret); return 1; } static gint lua_config_register_callback_symbol(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); const gchar *name = NULL; double weight; gint ret = -1, top = 2; if (cfg) { if (lua_type(L, 2) == LUA_TSTRING) { /* Legacy syntax */ name = luaL_checkstring(L, 2); top++; } weight = luaL_checknumber(L, top); if (lua_type(L, top + 1) == LUA_TSTRING) { lua_getglobal(L, luaL_checkstring(L, top + 1)); } else { lua_pushvalue(L, top + 1); } ret = rspamd_register_symbol_fromlua(L, cfg, name, luaL_ref(L, LUA_REGISTRYINDEX), weight, 0, SYMBOL_TYPE_CALLBACK, -1, NULL, NULL, FALSE); } lua_pushinteger(L, ret); return 1; } static gint lua_config_register_callback_symbol_priority(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); const gchar *name = NULL; double weight; gint priority, ret = -1, top = 2; if (cfg) { if (lua_type(L, 2) == LUA_TSTRING) { /* Legacy syntax */ name = luaL_checkstring(L, 2); top++; } weight = luaL_checknumber(L, top); priority = luaL_checknumber(L, top + 1); if (lua_type(L, top + 2) == LUA_TSTRING) { lua_getglobal(L, luaL_checkstring(L, top + 2)); } else { lua_pushvalue(L, top + 2); } ret = rspamd_register_symbol_fromlua(L, cfg, name, luaL_ref(L, LUA_REGISTRYINDEX), weight, priority, SYMBOL_TYPE_CALLBACK, -1, NULL, NULL, FALSE); } lua_pushinteger(L, ret); return 1; } static gint lua_config_register_dependency(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); const gchar *parent = NULL, *child = NULL; gint child_id; if (cfg == NULL) { lua_error(L); return 0; } if (lua_type(L, 2) == LUA_TNUMBER) { child_id = luaL_checknumber(L, 2); parent = luaL_checkstring(L, 3); return luaL_error(L, "calling for obsolete method to register deps for symbol %d->%s", child_id, parent); } else { child = luaL_checkstring(L, 2); parent = luaL_checkstring(L, 3); if (child != NULL && parent != NULL) { rspamd_symcache_add_delayed_dependency(cfg->cache, child, parent); } } return 0; } static gint lua_config_set_metric_symbol(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); const gchar *description = NULL, *group = NULL, *name = NULL, *flags_str = NULL; double score; gboolean one_shot = FALSE, one_param = FALSE; GError *err = NULL; gdouble priority = 0.0; guint flags = 0; gint64 nshots = 0; if (cfg) { if (lua_type(L, 2) == LUA_TTABLE) { if (!rspamd_lua_parse_table_arguments(L, 2, &err, RSPAMD_LUA_PARSE_ARGUMENTS_DEFAULT, "*name=S;score=N;description=S;" "group=S;one_shot=B;one_param=B;priority=N;flags=S;" "nshots=I", &name, &score, &description, &group, &one_shot, &one_param, &priority, &flags_str, &nshots)) { msg_err_config("bad arguments: %e", err); g_error_free(err); return 0; } } else { name = luaL_checkstring(L, 2); score = luaL_checknumber(L, 3); if (lua_gettop(L) > 3 && lua_type(L, 4) == LUA_TSTRING) { description = luaL_checkstring(L, 4); } if (lua_gettop(L) > 4 && lua_type(L, 5) == LUA_TSTRING) { /* XXX: metrics */ } if (lua_gettop(L) > 5 && lua_type(L, 6) == LUA_TSTRING) { group = luaL_checkstring(L, 6); } if (lua_gettop(L) > 6 && lua_type(L, 7) == LUA_TBOOLEAN) { one_shot = lua_toboolean(L, 7); } } if (nshots == 0) { nshots = cfg->default_max_shots; } if (one_shot) { nshots = 1; } if (one_param) { flags |= RSPAMD_SYMBOL_FLAG_ONEPARAM; } if (flags_str) { if (strstr(flags_str, "one_shot") != NULL) { nshots = 1; } if (strstr(flags_str, "ignore") != NULL) { flags |= RSPAMD_SYMBOL_FLAG_IGNORE_METRIC; } if (strstr(flags_str, "one_param") != NULL) { flags |= RSPAMD_SYMBOL_FLAG_ONEPARAM; } } rspamd_config_add_symbol(cfg, name, score, description, group, flags, (guint) priority, nshots); if (lua_type(L, 2) == LUA_TTABLE) { lua_pushstring(L, "groups"); lua_gettable(L, 2); if (lua_istable(L, -1)) { for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { if (lua_isstring(L, -1)) { rspamd_config_add_symbol_group(cfg, name, lua_tostring(L, -1)); } else { return luaL_error(L, "invalid groups element"); } } } lua_pop(L, 1); } } else { return luaL_error(L, "invalid arguments, rspamd_config expected"); } return 0; } static gint lua_config_set_metric_action(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); const gchar *name = NULL; double threshold = NAN; GError *err = NULL; gdouble priority = 0.0; ucl_object_t *obj_tbl = NULL; if (cfg) { if (lua_type(L, 2) == LUA_TTABLE) { if (!rspamd_lua_parse_table_arguments(L, 2, &err, RSPAMD_LUA_PARSE_ARGUMENTS_DEFAULT, "*action=S;score=N;" "priority=N", &name, &threshold, &priority)) { msg_err_config("bad arguments: %e", err); g_error_free(err); return 0; } } else if (lua_type(L, 2) == LUA_TSTRING && lua_type(L, 3) == LUA_TTABLE) { name = lua_tostring(L, 2); obj_tbl = ucl_object_lua_import(L, 3); if (obj_tbl) { if (name) { rspamd_config_set_action_score(cfg, name, obj_tbl); ucl_object_unref(obj_tbl); } else { ucl_object_unref(obj_tbl); return luaL_error(L, "invalid first argument, action name expected"); } } else { return luaL_error(L, "invalid second argument, table expected"); } } else { return luaL_error(L, "invalid arguments, table expected"); } if (name != NULL && !isnan(threshold) && threshold != 0) { obj_tbl = ucl_object_typed_new(UCL_OBJECT); ucl_object_insert_key(obj_tbl, ucl_object_fromdouble(threshold), "score", 0, false); ucl_object_insert_key(obj_tbl, ucl_object_fromdouble(priority), "priority", 0, false); rspamd_config_set_action_score(cfg, name, obj_tbl); ucl_object_unref(obj_tbl); } } else { return luaL_error(L, "invalid arguments, rspamd_config expected"); } return 0; } static gint lua_config_get_metric_action(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); const gchar *act_name = luaL_checkstring(L, 2); struct rspamd_action *act; if (cfg && act_name) { act = rspamd_config_get_action(cfg, act_name); if (act) { if (!isnan(act->threshold)) { lua_pushnumber(L, act->threshold); } else { lua_pushnil(L); } } else { lua_pushnil(L); } } else { return luaL_error(L, "invalid arguments, rspamd_config expected"); } return 1; } static void lua_config_actions_cb(struct rspamd_action *act, void *cbd) { lua_State *L = (lua_State *) cbd; if (!isnan(act->threshold)) { lua_pushstring(L, act->name); lua_pushnumber(L, act->threshold); lua_settable(L, -3); } } static gint lua_config_get_all_actions(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); if (cfg) { lua_createtable(L, 0, rspamd_config_actions_size(cfg)); rspamd_config_actions_foreach(cfg, lua_config_actions_cb, L); } else { return luaL_error(L, "invalid arguments, rspamd_config expected"); } return 1; } static gint lua_config_add_composite(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); gchar *name; const gchar *expr_str; struct rspamd_composite *composite; gboolean ret = FALSE; if (cfg) { name = rspamd_mempool_strdup(cfg->cfg_pool, luaL_checkstring(L, 2)); expr_str = luaL_checkstring(L, 3); if (name && expr_str) { composite = rspamd_composites_manager_add_from_string(cfg->composites_manager, name, expr_str); if (composite) { rspamd_symcache_add_symbol(cfg->cache, name, 0, NULL, composite, SYMBOL_TYPE_COMPOSITE, -1); ret = TRUE; } } } lua_pushboolean(L, ret); return 1; } static gint lua_config_newindex(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); const gchar *name; GArray *allowed_ids = NULL, *forbidden_ids = NULL; gint id, nshots; guint flags = 0; gboolean optional = FALSE; name = luaL_checkstring(L, 2); if (cfg != NULL && name != NULL && lua_gettop(L) == 3) { if (lua_type(L, 3) == LUA_TFUNCTION) { /* Normal symbol from just a function */ lua_pushvalue(L, 3); rspamd_register_symbol_fromlua(L, cfg, name, luaL_ref(L, LUA_REGISTRYINDEX), 1.0, 0, SYMBOL_TYPE_NORMAL, -1, NULL, NULL, FALSE); } else if (lua_type(L, 3) == LUA_TTABLE) { guint type = SYMBOL_TYPE_NORMAL, priority = 0; gint idx; gdouble weight = 1.0, score = NAN; const char *type_str, *group = NULL, *description = NULL; /* * Table can have the following attributes: * "callback" - should be a callback function * "weight" - optional weight * "priority" - optional priority * "type" - optional type (normal, virtual, callback) * "flags" - optional flags * -- Metric options * "score" - optional default score (overridden by metric) * "group" - optional default group * "one_shot" - optional one shot mode * "description" - optional description */ lua_pushvalue(L, 3); lua_pushstring(L, "callback"); lua_gettable(L, -2); if (lua_type(L, -1) != LUA_TFUNCTION) { lua_pop(L, 2); msg_info_config("cannot find callback definition for %s", name); return 0; } idx = luaL_ref(L, LUA_REGISTRYINDEX); /* Optional fields */ lua_pushstring(L, "weight"); lua_gettable(L, -2); if (lua_type(L, -1) == LUA_TNUMBER) { weight = lua_tonumber(L, -1); } lua_pop(L, 1); lua_pushstring(L, "priority"); lua_gettable(L, -2); if (lua_type(L, -1) == LUA_TNUMBER) { priority = lua_tointeger(L, -1); } lua_pop(L, 1); lua_pushstring(L, "optional"); lua_gettable(L, -2); if (lua_type(L, -1) == LUA_TBOOLEAN) { optional = lua_toboolean(L, -1); } lua_pop(L, 1); lua_pushstring(L, "type"); lua_gettable(L, -2); if (lua_type(L, -1) == LUA_TSTRING) { type_str = lua_tostring(L, -1); type = lua_parse_symbol_type(type_str); } lua_pop(L, 1); /* Deal with flags and ids */ lua_pushstring(L, "flags"); lua_gettable(L, -2); if (lua_type(L, -1) == LUA_TSTRING) { flags = lua_parse_symbol_flags(lua_tostring(L, -1)); } else if (lua_type(L, -1) == LUA_TTABLE) { for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { flags |= lua_parse_symbol_flags(lua_tostring(L, -1)); } } lua_pop(L, 1); /* Clean flags */ lua_pushstring(L, "allowed_ids"); lua_gettable(L, -2); if (lua_type(L, -1) == LUA_TSTRING) { allowed_ids = rspamd_process_id_list(lua_tostring(L, -1)); } else if (lua_type(L, -1) == LUA_TTABLE) { allowed_ids = g_array_sized_new(FALSE, FALSE, sizeof(guint32), rspamd_lua_table_size(L, -1)); for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { guint32 v = lua_tointeger(L, -1); g_array_append_val(allowed_ids, v); } } lua_pop(L, 1); lua_pushstring(L, "forbidden_ids"); lua_gettable(L, -2); if (lua_type(L, -1) == LUA_TSTRING) { forbidden_ids = rspamd_process_id_list(lua_tostring(L, -1)); } else if (lua_type(L, -1) == LUA_TTABLE) { forbidden_ids = g_array_sized_new(FALSE, FALSE, sizeof(guint32), rspamd_lua_table_size(L, -1)); for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { guint32 v = lua_tointeger(L, -1); g_array_append_val(forbidden_ids, v); } } lua_pop(L, 1); id = rspamd_register_symbol_fromlua(L, cfg, name, idx, weight, priority, type | flags, -1, allowed_ids, forbidden_ids, optional); if (allowed_ids) { g_array_free(allowed_ids, TRUE); } if (forbidden_ids) { g_array_free(forbidden_ids, TRUE); } if (id != -1) { /* Check for condition */ lua_pushstring(L, "condition"); lua_gettable(L, -2); if (lua_type(L, -1) == LUA_TFUNCTION) { gint condref; /* Here we pop function from the stack, so no lua_pop is required */ condref = luaL_ref(L, LUA_REGISTRYINDEX); g_assert(name != NULL); rspamd_symcache_add_condition_delayed(cfg->cache, name, L, condref); } else { lua_pop(L, 1); } /* Check for augmentations */ lua_pushstring(L, "augmentations"); lua_gettable(L, -2); if (lua_type(L, -1) == LUA_TTABLE) { int tbl_idx = lua_gettop(L); for (lua_pushnil(L); lua_next(L, tbl_idx); lua_pop(L, 1)) { rspamd_symcache_add_symbol_augmentation(cfg->cache, id, lua_tostring(L, -1), NULL); } } lua_pop(L, 1); } /* * Now check if a symbol has not been registered in any metric and * insert default value if applicable */ struct rspamd_symbol *sym = g_hash_table_lookup(cfg->symbols, name); if (sym == NULL || (sym->flags & RSPAMD_SYMBOL_FLAG_UNSCORED)) { nshots = cfg->default_max_shots; lua_pushstring(L, "score"); lua_gettable(L, -2); if (lua_type(L, -1) == LUA_TNUMBER) { score = lua_tonumber(L, -1); if (sym) { /* Reset unscored flag */ sym->flags &= ~RSPAMD_SYMBOL_FLAG_UNSCORED; } } lua_pop(L, 1); lua_pushstring(L, "group"); lua_gettable(L, -2); if (lua_type(L, -1) == LUA_TSTRING) { group = lua_tostring(L, -1); } lua_pop(L, 1); if (!isnan(score) || group != NULL) { lua_pushstring(L, "description"); lua_gettable(L, -2); if (lua_type(L, -1) == LUA_TSTRING) { description = lua_tostring(L, -1); } lua_pop(L, 1); lua_pushstring(L, "one_shot"); lua_gettable(L, -2); if (lua_type(L, -1) == LUA_TBOOLEAN) { if (lua_toboolean(L, -1)) { nshots = 1; } } lua_pop(L, 1); lua_pushstring(L, "one_param"); lua_gettable(L, -2); if (lua_type(L, -1) == LUA_TBOOLEAN) { if (lua_toboolean(L, -1)) { flags |= RSPAMD_SYMBOL_FLAG_ONEPARAM; } } lua_pop(L, 1); /* * Do not override the existing symbols (using zero priority), * since we are defining default values here */ if (!isnan(score)) { rspamd_config_add_symbol(cfg, name, score, description, group, flags, 0, nshots); } else if (group) { /* Add with zero score */ rspamd_config_add_symbol(cfg, name, NAN, description, group, flags, 0, nshots); } lua_pushstring(L, "groups"); lua_gettable(L, -2); if (lua_istable(L, -1)) { for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { if (lua_isstring(L, -1)) { rspamd_config_add_symbol_group(cfg, name, lua_tostring(L, -1)); } else { return luaL_error(L, "invalid groups element"); } } } lua_pop(L, 1); } } else { /* Fill in missing fields from lua definition if they are not set */ if (sym->description == NULL) { lua_pushstring(L, "description"); lua_gettable(L, -2); if (lua_type(L, -1) == LUA_TSTRING) { description = lua_tostring(L, -1); } lua_pop(L, 1); if (description) { sym->description = rspamd_mempool_strdup(cfg->cfg_pool, description); } } /* If ungrouped and there is a group defined in lua, change the primary group * Otherwise, add to the list of groups for this symbol. */ lua_pushstring(L, "group"); lua_gettable(L, -2); if (lua_type(L, -1) == LUA_TSTRING) { group = lua_tostring(L, -1); } lua_pop(L, 1); if (group) { if (sym->flags & RSPAMD_SYMBOL_FLAG_UNGROUPED) { /* Unset the "ungrouped" group */ sym->gr = NULL; } /* Add the group. If the symbol was ungrouped, this will * clear RSPAMD_SYMBOL_FLAG_UNGROUPED from the flags. */ rspamd_config_add_symbol_group(cfg, name, group); } } /* Remove table from stack */ lua_pop(L, 1); } } else { return luaL_error(L, "invalid arguments"); } return 0; } static gint lua_config_add_condition(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); const gchar *sym = luaL_checkstring(L, 2); gboolean ret = FALSE; gint condref; if (cfg && sym && lua_type(L, 3) == LUA_TFUNCTION) { lua_pushvalue(L, 3); condref = luaL_ref(L, LUA_REGISTRYINDEX); ret = rspamd_symcache_add_condition_delayed(cfg->cache, sym, L, condref); if (!ret) { luaL_unref(L, LUA_REGISTRYINDEX, condref); } } lua_pushboolean(L, ret); return 1; } static gint lua_config_set_peak_cb(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); gint condref; if (cfg && lua_type(L, 2) == LUA_TFUNCTION) { lua_pushvalue(L, 2); condref = luaL_ref(L, LUA_REGISTRYINDEX); rspamd_symcache_set_peak_callback(cfg->cache, condref); } return 0; } static gint lua_config_enable_symbol(lua_State *L) { struct rspamd_config *cfg = lua_check_config(L, 1); const char *sym = luaL_checkstring(L, 2); if (!sym || !cfg) { return luaL_error(L, "invalid arguments"); } rspamd_symcache_enable_symbol_static(cfg->cache, sym); return 0; } static gint lua_config_disable_symbol(lua_State *L) { struct rspamd_config *cfg = lua_check_config(L, 1); const char *sym = luaL_checkstring(L, 2); if (!sym || !cfg) { return luaL_error(L, "invalid arguments"); } rspamd_symcache_disable_symbol_static(cfg->cache, sym); return 0; } static gint lua_config_register_regexp(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); struct rspamd_lua_regexp *re = NULL; rspamd_regexp_t *cache_re; const gchar *type_str = NULL, *header_str = NULL; gsize header_len = 0; GError *err = NULL; enum rspamd_re_type type = RSPAMD_RE_BODY; gboolean pcre_only = FALSE; /* * - `re`* : regular expression object * - `type`*: type of regular expression: * + `mime`: mime regexp * + `rawmime`: raw mime regexp * + `header`: header regexp * + `rawheader`: raw header expression * + `body`: raw body regexp * + `url`: url regexp * - `header`: for header and rawheader regexp means the name of header * - `pcre_only`: allow merely pcre for this regexp */ if (cfg != NULL) { if (!rspamd_lua_parse_table_arguments(L, 2, &err, RSPAMD_LUA_PARSE_ARGUMENTS_DEFAULT, "*re=U{regexp};*type=S;header=S;pcre_only=B", &re, &type_str, &header_str, &pcre_only)) { msg_err_config("cannot get parameters list: %e", err); if (err) { g_error_free(err); } } else { type = rspamd_re_cache_type_from_string(type_str); if ((type == RSPAMD_RE_HEADER || type == RSPAMD_RE_RAWHEADER || type == RSPAMD_RE_MIMEHEADER) && header_str == NULL) { msg_err_config( "header argument is mandatory for header/rawheader regexps"); } else { if (pcre_only) { rspamd_regexp_set_flags(re->re, rspamd_regexp_get_flags(re->re) | RSPAMD_REGEXP_FLAG_PCRE_ONLY); } if (header_str != NULL) { /* Include the last \0 */ header_len = strlen(header_str) + 1; } cache_re = rspamd_re_cache_add(cfg->re_cache, re->re, type, (gpointer) header_str, header_len, -1); /* * XXX: here are dragons! * Actually, lua regexp contains internal rspamd_regexp_t * and it owns it. * However, after this operation we have some OTHER regexp, * which we really would like to use. * So we do the following: * 1) Remove old re and unref it * 2) Replace the internal re with cached one * 3) Increase its refcount to share ownership between cache and * lua object */ if (cache_re != re->re) { rspamd_regexp_unref(re->re); re->re = rspamd_regexp_ref(cache_re); if (pcre_only) { rspamd_regexp_set_flags(re->re, rspamd_regexp_get_flags(re->re) | RSPAMD_REGEXP_FLAG_PCRE_ONLY); } } } } } return 0; } static gint lua_config_replace_regexp(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); struct rspamd_lua_regexp *old_re = NULL, *new_re = NULL; gboolean pcre_only = FALSE; GError *err = NULL; if (cfg != NULL) { if (!rspamd_lua_parse_table_arguments(L, 2, &err, RSPAMD_LUA_PARSE_ARGUMENTS_DEFAULT, "*old_re=U{regexp};*new_re=U{regexp};pcre_only=B", &old_re, &new_re, &pcre_only)) { gint ret = luaL_error(L, "cannot get parameters list: %s", err ? err->message : "invalid arguments"); if (err) { g_error_free(err); } return ret; } else { if (pcre_only) { rspamd_regexp_set_flags(new_re->re, rspamd_regexp_get_flags(new_re->re) | RSPAMD_REGEXP_FLAG_PCRE_ONLY); } rspamd_re_cache_replace(cfg->re_cache, old_re->re, new_re->re); } } return 0; } static gint lua_config_register_worker_script(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); const gchar *worker_type = luaL_checkstring(L, 2), *wtype; struct rspamd_worker_conf *cf; GList *cur; struct rspamd_worker_lua_script *sc; gboolean found = FALSE; if (cfg == NULL || worker_type == NULL || lua_type(L, 3) != LUA_TFUNCTION) { return luaL_error(L, "invalid arguments"); } for (cur = g_list_first(cfg->workers); cur != NULL; cur = g_list_next(cur)) { cf = cur->data; wtype = g_quark_to_string(cf->type); if (g_ascii_strcasecmp(wtype, worker_type) == 0) { sc = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(*sc)); lua_pushvalue(L, 3); sc->cbref = luaL_ref(L, LUA_REGISTRYINDEX); DL_APPEND(cf->scripts, sc); found = TRUE; } } lua_pushboolean(L, found); return 1; } static gint lua_config_add_on_load(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); struct rspamd_config_cfg_lua_script *sc; if (cfg == NULL || lua_type(L, 2) != LUA_TFUNCTION) { return luaL_error(L, "invalid arguments"); } sc = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(*sc)); lua_pushvalue(L, 2); sc->cbref = luaL_ref(L, LUA_REGISTRYINDEX); DL_APPEND(cfg->on_load_scripts, sc); return 0; } static inline int rspamd_post_init_sc_sort(const struct rspamd_config_cfg_lua_script *pra, const struct rspamd_config_cfg_lua_script *prb) { /* Inverse sort */ return prb->priority - pra->priority; } static gint lua_config_add_post_init(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); struct rspamd_config_cfg_lua_script *sc; guint priority = 0; lua_Debug d; gchar tmp[256], *p; if (cfg == NULL || lua_type(L, 2) != LUA_TFUNCTION) { return luaL_error(L, "invalid arguments"); } if (lua_type(L, 3) == LUA_TNUMBER) { priority = lua_tointeger(L, 3); } if (lua_getstack(L, 1, &d) == 1) { (void) lua_getinfo(L, "Sl", &d); if ((p = strrchr(d.short_src, '/')) == NULL) { p = d.short_src; } else { p++; } if (strlen(p) > 200) { rspamd_snprintf(tmp, sizeof(tmp), "%10s...]:%d", p, d.currentline); } else { rspamd_snprintf(tmp, sizeof(tmp), "%s:%d", p, d.currentline); } } sc = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(*sc)); lua_pushvalue(L, 2); sc->cbref = luaL_ref(L, LUA_REGISTRYINDEX); sc->priority = priority; sc->lua_src_pos = rspamd_mempool_strdup(cfg->cfg_pool, tmp); DL_APPEND(cfg->post_init_scripts, sc); DL_SORT(cfg->post_init_scripts, rspamd_post_init_sc_sort); return 0; } static gint lua_config_add_config_unload(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); struct rspamd_config_cfg_lua_script *sc; lua_Debug d; gchar tmp[256], *p; if (cfg == NULL || lua_type(L, 2) != LUA_TFUNCTION) { return luaL_error(L, "invalid arguments"); } if (lua_getstack(L, 1, &d) == 1) { (void) lua_getinfo(L, "Sl", &d); if ((p = strrchr(d.short_src, '/')) == NULL) { p = d.short_src; } else { p++; } if (strlen(p) > 20) { rspamd_snprintf(tmp, sizeof(tmp), "%10s...]:%d", p, d.currentline); } else { rspamd_snprintf(tmp, sizeof(tmp), "%s:%d", p, d.currentline); } } sc = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(*sc)); lua_pushvalue(L, 2); sc->cbref = luaL_ref(L, LUA_REGISTRYINDEX); sc->lua_src_pos = rspamd_mempool_strdup(cfg->cfg_pool, tmp); DL_APPEND(cfg->config_unload_scripts, sc); return 0; } static void lua_periodic_callback_finish(struct thread_entry *thread, int ret); static void lua_periodic_callback_error(struct thread_entry *thread, int ret, const char *msg); struct rspamd_lua_periodic { struct ev_loop *event_loop; struct rspamd_config *cfg; gchar *lua_src_pos; lua_State *L; gdouble timeout; ev_timer ev; gint cbref; gboolean need_jitter; ref_entry_t ref; }; static void lua_periodic_dtor(struct rspamd_lua_periodic *periodic) { luaL_unref(periodic->L, LUA_REGISTRYINDEX, periodic->cbref); ev_timer_stop(periodic->event_loop, &periodic->ev); } static void lua_periodic_fin(gpointer p) { struct rspamd_lua_periodic *periodic = (struct rspamd_lua_periodic *) p; REF_RELEASE(periodic); } static void lua_periodic_callback(struct ev_loop *loop, ev_timer *w, int revents) { struct rspamd_lua_periodic *periodic = (struct rspamd_lua_periodic *) w->data; struct rspamd_config **pcfg, *cfg; struct ev_loop **pev_base; struct thread_entry *thread; lua_State *L; REF_RETAIN(periodic); thread = lua_thread_pool_get_for_config(periodic->cfg); thread->cd = periodic; thread->finish_callback = lua_periodic_callback_finish; thread->error_callback = lua_periodic_callback_error; L = thread->lua_state; lua_rawgeti(L, LUA_REGISTRYINDEX, periodic->cbref); pcfg = lua_newuserdata(L, sizeof(*pcfg)); rspamd_lua_setclass(L, rspamd_config_classname, -1); cfg = periodic->cfg; *pcfg = cfg; pev_base = lua_newuserdata(L, sizeof(*pev_base)); rspamd_lua_setclass(L, rspamd_ev_base_classname, -1); *pev_base = periodic->event_loop; lua_pushnumber(L, ev_now(periodic->event_loop)); lua_thread_call(thread, 3); } static void lua_periodic_callback_finish(struct thread_entry *thread, int ret) { lua_State *L; struct rspamd_lua_periodic *periodic = thread->cd; gboolean plan_more = FALSE; gdouble timeout = 0.0; L = thread->lua_state; ev_now_update(periodic->event_loop); if (ret == 0) { if (lua_type(L, -1) == LUA_TBOOLEAN) { plan_more = lua_toboolean(L, -1); timeout = periodic->timeout; } else if (lua_type(L, -1) == LUA_TNUMBER) { timeout = lua_tonumber(L, -1); plan_more = timeout > 0 ? TRUE : FALSE; } lua_pop(L, 1); /* Return value */ } if (periodic->cfg->cur_worker) { if (periodic->cfg->cur_worker->state != rspamd_worker_state_running) { /* We are terminating, no more periodics */ plan_more = FALSE; } } if (plan_more) { if (periodic->need_jitter) { timeout = rspamd_time_jitter(timeout, 0.0); } periodic->ev.repeat = timeout; ev_timer_again(periodic->event_loop, &periodic->ev); } else { ev_timer_stop(periodic->event_loop, &periodic->ev); } REF_RELEASE(periodic); } static void lua_periodic_callback_error(struct thread_entry *thread, int ret, const char *msg) { struct rspamd_config *cfg; struct rspamd_lua_periodic *periodic = thread->cd; cfg = periodic->cfg; msg_err_config("call to periodic script (registered at %s) failed: %s", periodic->lua_src_pos, msg); lua_periodic_callback_finish(thread, ret); } static gint lua_config_add_periodic(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); struct ev_loop *ev_base = lua_check_ev_base(L, 2); gdouble timeout = lua_tonumber(L, 3); struct rspamd_lua_periodic *periodic; gboolean need_jitter = FALSE; lua_Debug d; gchar tmp[256], *p; if (cfg == NULL || timeout < 0 || lua_type(L, 4) != LUA_TFUNCTION) { return luaL_error(L, "invalid arguments"); } if (lua_type(L, 5) == LUA_TBOOLEAN) { need_jitter = lua_toboolean(L, 5); } if (lua_getstack(L, 1, &d) == 1) { (void) lua_getinfo(L, "Sl", &d); if ((p = strrchr(d.short_src, '/')) == NULL) { p = d.short_src; } else { p++; } if (strlen(p) > 20) { rspamd_snprintf(tmp, sizeof(tmp), "%10s...]:%d", p, d.currentline); } else { rspamd_snprintf(tmp, sizeof(tmp), "%s:%d", p, d.currentline); } } periodic = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(*periodic)); periodic->timeout = timeout; periodic->L = L; periodic->cfg = cfg; periodic->event_loop = ev_base; periodic->need_jitter = need_jitter; periodic->lua_src_pos = rspamd_mempool_strdup(cfg->cfg_pool, tmp); lua_pushvalue(L, 4); periodic->cbref = luaL_ref(L, LUA_REGISTRYINDEX); if (need_jitter) { timeout = rspamd_time_jitter(timeout, 0.0); } ev_timer_init(&periodic->ev, lua_periodic_callback, timeout, 0.0); periodic->ev.data = periodic; ev_timer_start(ev_base, &periodic->ev); REF_INIT_RETAIN(periodic, lua_periodic_dtor); rspamd_mempool_add_destructor(cfg->cfg_pool, lua_periodic_fin, periodic); return 0; } static gint lua_config_get_symbols_count(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); guint res = 0; if (cfg != NULL) { res = rspamd_symcache_stats_symbols_count(cfg->cache); } else { return luaL_error(L, "invalid arguments"); } lua_pushinteger(L, res); return 1; } static gint lua_config_get_symbols_cksum(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); guint64 res = 0, *pres; if (cfg != NULL) { res = rspamd_symcache_get_cksum(cfg->cache); } else { return luaL_error(L, "invalid arguments"); } pres = lua_newuserdata(L, sizeof(res)); *pres = res; rspamd_lua_setclass(L, rspamd_int64_classname, -1); return 1; } static gint lua_config_get_symbols_counters(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); ucl_object_t *counters; if (cfg != NULL) { counters = rspamd_symcache_counters(cfg->cache); ucl_object_push_lua(L, counters, true); ucl_object_unref(counters); } else { return luaL_error(L, "invalid arguments"); } return 1; } struct lua_metric_symbols_cbdata { lua_State *L; struct rspamd_config *cfg; bool is_table; }; static void lua_metric_symbol_inserter(gpointer k, gpointer v, gpointer ud) { struct lua_metric_symbols_cbdata *cbd = (struct lua_metric_symbols_cbdata *) ud; lua_State *L; const gchar *sym = k; struct rspamd_symbol *s = (struct rspamd_symbol *) v; struct rspamd_symbols_group *gr; gint i; L = cbd->L; if (cbd->is_table) { lua_pushstring(L, sym); /* Symbol name */ } lua_createtable(L, 0, 6); lua_pushstring(L, "score"); lua_pushnumber(L, s->score); lua_settable(L, -3); lua_pushstring(L, "description"); lua_pushstring(L, s->description); lua_settable(L, -3); lua_pushstring(L, "flags"); lua_createtable(L, 0, 3); if (s->flags & RSPAMD_SYMBOL_FLAG_IGNORE_METRIC) { lua_pushstring(L, "ignore"); lua_pushboolean(L, true); lua_settable(L, -3); } if (s->flags & RSPAMD_SYMBOL_FLAG_ONEPARAM) { lua_pushstring(L, "oneparam"); lua_pushboolean(L, true); lua_settable(L, -3); } if (s->flags & RSPAMD_SYMBOL_FLAG_UNGROUPED) { lua_pushstring(L, "ungrouped"); lua_pushboolean(L, true); lua_settable(L, -3); } if (s->flags & RSPAMD_SYMBOL_FLAG_DISABLED) { lua_pushstring(L, "disabled"); lua_pushboolean(L, true); lua_settable(L, -3); } if (s->cache_item) { guint sflags = rspamd_symcache_get_symbol_flags(cbd->cfg->cache, sym); lua_push_symbol_flags(L, sflags, LUA_SYMOPT_FLAG_USE_MAP); guint nids; const guint *allowed_ids = rspamd_symcache_get_allowed_settings_ids(cbd->cfg->cache, sym, &nids); if (allowed_ids && nids > 0) { lua_createtable(L, nids, 0); for (i = 0; i < nids; i++) { lua_pushinteger(L, allowed_ids[i]); lua_rawseti(L, -2, i + 1); } lua_setfield(L, -2, "allowed_ids"); } const guint *forbidden_ids = rspamd_symcache_get_forbidden_settings_ids( cbd->cfg->cache, sym, &nids); if (forbidden_ids && nids > 0) { lua_createtable(L, nids, 0); for (i = 0; i < nids; i++) { lua_pushinteger(L, forbidden_ids[i]); lua_rawseti(L, -2, i + 1); } lua_setfield(L, -2, "forbidden_ids"); } } lua_settable(L, -3); /* Flags -> flags_table */ lua_pushstring(L, "nshots"); lua_pushinteger(L, s->nshots); lua_settable(L, -3); if (s->gr) { lua_pushstring(L, "group"); lua_pushstring(L, s->gr->name); lua_settable(L, -3); } if (s->groups && s->groups->len > 0) { lua_pushstring(L, "groups"); lua_createtable(L, s->groups->len, 0); PTR_ARRAY_FOREACH(s->groups, i, gr) { lua_pushstring(L, gr->name); lua_rawseti(L, -2, i + 1); /* Groups[i + 1] = group_name */ } lua_settable(L, -3); /* Groups -> groups_table */ } else { lua_createtable(L, 0, 0); lua_setfield(L, -2, "groups"); } if (cbd->is_table) { lua_settable(L, -3); /* Symname -> table */ } } static gint lua_config_get_symbols(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); if (cfg != NULL) { struct lua_metric_symbols_cbdata cbd; cbd.L = L; cbd.cfg = cfg; cbd.is_table = true; lua_createtable(L, 0, g_hash_table_size(cfg->symbols)); g_hash_table_foreach(cfg->symbols, lua_metric_symbol_inserter, &cbd); } else { return luaL_error(L, "invalid arguments"); } return 1; } static gint lua_config_get_symbol(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); const gchar *sym_name = luaL_checkstring(L, 2); if (cfg != NULL && sym_name != NULL) { struct lua_metric_symbols_cbdata cbd; struct rspamd_symbol *s = g_hash_table_lookup(cfg->symbols, sym_name); if (s) { cbd.L = L; cbd.cfg = cfg; cbd.is_table = false; lua_metric_symbol_inserter((void *) sym_name, s, &cbd); } else { /* No config for a symbol */ lua_pushnil(L); } } else { return luaL_error(L, "invalid arguments"); } return 1; } static gint lua_config_get_symbol_callback(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); const gchar *sym = luaL_checkstring(L, 2); struct rspamd_abstract_callback_data *abs_cbdata; struct lua_callback_data *cbd; if (cfg != NULL && sym != NULL) { abs_cbdata = rspamd_symcache_get_cbdata(cfg->cache, sym); if (abs_cbdata == NULL || abs_cbdata->magic != rspamd_lua_callback_magic) { lua_pushnil(L); } else { cbd = (struct lua_callback_data *) abs_cbdata; if (cbd->cb_is_ref) { lua_rawgeti(L, LUA_REGISTRYINDEX, cbd->callback.ref); } else { lua_getglobal(L, cbd->callback.name); } } } else { return luaL_error(L, "invalid arguments"); } return 1; } static gint lua_config_set_symbol_callback(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); const gchar *sym = luaL_checkstring(L, 2); struct rspamd_abstract_callback_data *abs_cbdata; struct lua_callback_data *cbd; if (cfg != NULL && sym != NULL && lua_type(L, 3) == LUA_TFUNCTION) { abs_cbdata = rspamd_symcache_get_cbdata(cfg->cache, sym); if (abs_cbdata == NULL || abs_cbdata->magic != rspamd_lua_callback_magic) { lua_pushboolean(L, FALSE); } else { cbd = (struct lua_callback_data *) abs_cbdata; if (cbd->cb_is_ref) { luaL_unref(L, LUA_REGISTRYINDEX, cbd->callback.ref); } else { cbd->cb_is_ref = TRUE; } lua_pushvalue(L, 3); cbd->callback.ref = luaL_ref(L, LUA_REGISTRYINDEX); lua_pushboolean(L, TRUE); } } else { return luaL_error(L, "invalid arguments"); } return 1; } static gint lua_config_get_symbol_stat(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); const gchar *sym = luaL_checkstring(L, 2); gdouble freq, stddev, tm; guint hits; if (cfg != NULL && sym != NULL) { if (!rspamd_symcache_stat_symbol(cfg->cache, sym, &freq, &stddev, &tm, &hits)) { lua_pushnil(L); } else { lua_createtable(L, 0, 4); lua_pushstring(L, "frequency"); lua_pushnumber(L, freq); lua_settable(L, -3); lua_pushstring(L, "sttdev"); lua_pushnumber(L, stddev); lua_settable(L, -3); lua_pushstring(L, "time"); lua_pushnumber(L, tm); lua_settable(L, -3); lua_pushstring(L, "hits"); lua_pushinteger(L, hits); lua_settable(L, -3); } } else { return luaL_error(L, "invalid arguments"); } return 1; } static gint lua_config_get_symbol_parent(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); const gchar *sym = luaL_checkstring(L, 2), *parent; if (cfg != NULL && sym != NULL) { parent = rspamd_symcache_get_parent(cfg->cache, sym); if (parent) { lua_pushstring(L, parent); } else { lua_pushnil(L); } } else { return luaL_error(L, "invalid arguments"); } return 1; } static gint lua_config_get_group_symbols(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); const gchar *gr_name = luaL_checkstring(L, 2); if (cfg != NULL && gr_name != NULL) { struct rspamd_symbols_group *group; group = g_hash_table_lookup(cfg->groups, gr_name); if (group == NULL) { lua_pushnil(L); } else { guint i = 1; gpointer k, v; GHashTableIter it; lua_createtable(L, g_hash_table_size(group->symbols), 0); g_hash_table_iter_init(&it, group->symbols); while (g_hash_table_iter_next(&it, &k, &v)) { lua_pushstring(L, k); lua_rawseti(L, -2, i); i++; } } } else { return luaL_error(L, "invalid arguments"); } return 1; } static gint lua_config_get_groups(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); gboolean need_private; struct rspamd_symbols_group *gr; GHashTableIter it; gpointer k, v; if (cfg) { if (lua_isboolean(L, 2)) { need_private = lua_toboolean(L, 2); } else { need_private = !(cfg->public_groups_only); } lua_createtable(L, 0, g_hash_table_size(cfg->groups)); g_hash_table_iter_init(&it, cfg->groups); while (g_hash_table_iter_next(&it, &k, &v)) { gr = (struct rspamd_symbols_group *) v; if (need_private || (gr->flags & RSPAMD_SYMBOL_GROUP_PUBLIC)) { lua_createtable(L, 0, 4); lua_pushstring(L, gr->description); lua_setfield(L, -2, "description"); lua_pushnumber(L, gr->max_score); lua_setfield(L, -2, "max_score"); lua_pushboolean(L, (gr->flags & RSPAMD_SYMBOL_GROUP_PUBLIC) != 0); lua_setfield(L, -2, "is_public"); /* TODO: maybe push symbols as well */ /* Parent table indexed by group name */ lua_setfield(L, -2, gr->name); } } } else { return luaL_error(L, "invalid arguments"); } return 1; } static gint lua_config_register_finish_script(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); struct rspamd_config_cfg_lua_script *sc; if (cfg != NULL && lua_type(L, 2) == LUA_TFUNCTION) { sc = rspamd_mempool_alloc0(cfg->cfg_pool, sizeof(*sc)); lua_pushvalue(L, 2); sc->cbref = luaL_ref(L, LUA_REGISTRYINDEX); DL_APPEND(cfg->on_term_scripts, sc); } else { return luaL_error(L, "invalid arguments"); } return 0; } static inline bool rspamd_lua_config_check_settings_symbols_object(const ucl_object_t *obj) { if (obj == NULL) { /* Semantically valid */ return true; } if (ucl_object_type(obj) == UCL_OBJECT) { /* Key-value mapping - should be okay */ return true; } if (ucl_object_type(obj) == UCL_ARRAY) { /* Okay if empty */ if (obj->len == 0) { return true; } } /* Everything else not okay */ return false; } static gint lua_config_register_settings_id(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); const gchar *settings_name = luaL_checkstring(L, 2); if (cfg != NULL && settings_name) { ucl_object_t *sym_enabled, *sym_disabled; enum rspamd_config_settings_policy policy = RSPAMD_SETTINGS_POLICY_DEFAULT; sym_enabled = ucl_object_lua_import(L, 3); if (!rspamd_lua_config_check_settings_symbols_object(sym_enabled)) { ucl_object_unref(sym_enabled); return luaL_error(L, "invalid symbols enabled"); } sym_disabled = ucl_object_lua_import(L, 4); if (!rspamd_lua_config_check_settings_symbols_object(sym_disabled)) { ucl_object_unref(sym_enabled); ucl_object_unref(sym_disabled); return luaL_error(L, "invalid symbols enabled"); } /* Check policy */ if (lua_isstring(L, 5)) { const gchar *policy_str = lua_tostring(L, 5); if (strcmp(policy_str, "default") == 0) { policy = RSPAMD_SETTINGS_POLICY_DEFAULT; } else if (strcmp(policy_str, "implicit_allow") == 0) { policy = RSPAMD_SETTINGS_POLICY_IMPLICIT_ALLOW; } else if (strcmp(policy_str, "implicit_deny") == 0) { policy = RSPAMD_SETTINGS_POLICY_IMPLICIT_DENY; } else { return luaL_error(L, "invalid settings policy: %s", policy_str); } } else { /* Apply heuristic */ if (!sym_enabled) { policy = RSPAMD_SETTINGS_POLICY_IMPLICIT_ALLOW; } } rspamd_config_register_settings_id(cfg, settings_name, sym_enabled, sym_disabled, policy); if (sym_enabled) { ucl_object_unref(sym_enabled); } if (sym_disabled) { ucl_object_unref(sym_disabled); } } else { return luaL_error(L, "invalid arguments"); } return 0; } static gint lua_config_register_monitored(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); struct rspamd_monitored *m, **pm; const gchar *url, *type; ucl_object_t *params = NULL; url = lua_tostring(L, 2); type = lua_tostring(L, 3); if (cfg != NULL && url != NULL && type != NULL) { if (g_ascii_strcasecmp(type, "dns") == 0) { lua_Debug ar; if (lua_type(L, 4) == LUA_TTABLE) { params = ucl_object_lua_import(L, 4); } /* Get lua line and source */ lua_getstack(L, 1, &ar); lua_getinfo(L, "nSl", &ar); m = rspamd_monitored_create_(cfg->monitored_ctx, url, RSPAMD_MONITORED_DNS, RSPAMD_MONITORED_DEFAULT, params, ar.short_src); if (m) { pm = lua_newuserdata(L, sizeof(*pm)); *pm = m; rspamd_lua_setclass(L, rspamd_monitored_classname, -1); } else { lua_pushnil(L); } if (params) { ucl_object_unref(params); } } else { return luaL_error(L, "invalid monitored type: %s", type); } } else { return luaL_error(L, "invalid arguments"); } return 1; } static gint lua_config_add_doc(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg; const gchar *path = NULL, *option, *doc_string; const gchar *type_str = NULL, *default_value = NULL; ucl_type_t type = UCL_NULL; gboolean required = FALSE; GError *err = NULL; cfg = lua_check_config(L, 1); if (lua_type(L, 2) == LUA_TSTRING) { path = luaL_checkstring(L, 2); } option = luaL_checkstring(L, 3); doc_string = luaL_checkstring(L, 4); if (cfg && option && doc_string) { if (lua_type(L, 5) == LUA_TTABLE) { if (!rspamd_lua_parse_table_arguments(L, 5, &err, RSPAMD_LUA_PARSE_ARGUMENTS_DEFAULT, "type=S;default=S;required=B", &type_str, &default_value, &required)) { msg_err_config("cannot get parameters list: %e", err); if (err) { g_error_free(err); } if (type_str) { if (!ucl_object_string_to_type(type_str, &type)) { msg_err_config("invalid type: %s", type_str); } } } } rspamd_rcl_add_doc_by_path(cfg, path, doc_string, option, type, NULL, 0, default_value, required); } else { return luaL_error(L, "invalid arguments"); } return 0; } static gint lua_config_add_example(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg; const gchar *path = NULL, *option, *doc_string, *example; gsize example_len; cfg = lua_check_config(L, 1); if (lua_type(L, 2) == LUA_TSTRING) { path = luaL_checkstring(L, 2); } option = luaL_checkstring(L, 3); doc_string = luaL_checkstring(L, 4); example = luaL_checklstring(L, 5, &example_len); if (cfg && option && doc_string && example) { rspamd_rcl_add_doc_by_example(cfg, path, doc_string, option, example, example_len); } else { return luaL_error(L, "invalid arguments"); } return 0; } static gint lua_config_get_cpu_flags(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); struct rspamd_cryptobox_library_ctx *crypto_ctx; if (cfg != NULL) { crypto_ctx = cfg->libs_ctx->crypto_ctx; lua_newtable(L); if (crypto_ctx->cpu_config & CPUID_SSSE3) { lua_pushstring(L, "ssse3"); lua_pushboolean(L, true); lua_settable(L, -3); } if (crypto_ctx->cpu_config & CPUID_SSE41) { lua_pushstring(L, "sse41"); lua_pushboolean(L, true); lua_settable(L, -3); } if (crypto_ctx->cpu_config & CPUID_SSE42) { lua_pushstring(L, "sse42"); lua_pushboolean(L, true); lua_settable(L, -3); } if (crypto_ctx->cpu_config & CPUID_SSE2) { lua_pushstring(L, "sse2"); lua_pushboolean(L, true); lua_settable(L, -3); } if (crypto_ctx->cpu_config & CPUID_SSE3) { lua_pushstring(L, "sse3"); lua_pushboolean(L, true); lua_settable(L, -3); } if (crypto_ctx->cpu_config & CPUID_AVX) { lua_pushstring(L, "avx"); lua_pushboolean(L, true); lua_settable(L, -3); } if (crypto_ctx->cpu_config & CPUID_AVX2) { lua_pushstring(L, "avx2"); lua_pushboolean(L, true); lua_settable(L, -3); } } else { return luaL_error(L, "invalid arguments"); } return 1; } static gint lua_config_has_torch(lua_State *L) { msg_warn("use of the obsoleted `has_torch` function"); lua_pushboolean(L, false); return 1; } static gint lua_config_experimental_enabled(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); if (cfg != NULL) { lua_pushboolean(L, cfg->enable_experimental); } else { return luaL_error(L, "invalid arguments"); } return 1; } struct rspamd_lua_include_trace_cbdata { lua_State *L; gint cbref; }; 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) { struct rspamd_lua_include_trace_cbdata *cbdata = (struct rspamd_lua_include_trace_cbdata *) user_data; gint err_idx; lua_State *L; L = cbdata->L; lua_pushcfunction(L, &rspamd_lua_traceback); err_idx = lua_gettop(L); lua_rawgeti(L, LUA_REGISTRYINDEX, cbdata->cbref); /* Current filename */ lua_pushstring(L, ucl_parser_get_cur_file(parser)); /* Included filename */ lua_pushlstring(L, path, pathlen); /* Params */ if (args) { ucl_object_push_lua(L, args, true); } else { lua_newtable(L); } /* Parent */ if (parent) { lua_pushstring(L, ucl_object_key(parent)); } else { lua_pushnil(L); } if (lua_pcall(L, 4, 0, err_idx) != 0) { msg_err("lua call to local include trace failed: %s", lua_tostring(L, -1)); } lua_settop(L, err_idx - 1); } #define LUA_TABLE_TO_HASH(htb, idx) \ do { \ lua_pushstring(L, (idx)); \ lua_gettable(L, -2); \ if (lua_isstring(L, -1)) { \ g_hash_table_insert((htb), (idx), g_strdup(lua_tostring(L, -1))); \ } \ lua_pop(L, 1); \ } while (0) static gint lua_config_load_ucl(lua_State *L) { struct rspamd_config *cfg = lua_check_config(L, 1); const gchar *filename; GHashTable *paths = g_hash_table_new_full(rspamd_str_hash, rspamd_str_equal, NULL, g_free); GError *err = NULL; if (cfg) { if (lua_isstring(L, 2)) { filename = lua_tostring(L, 2); } else { filename = RSPAMD_CONFDIR "/rspamd.conf"; } /* Convert rspamd_paths */ lua_getglobal(L, "rspamd_paths"); if (lua_istable(L, -1)) { LUA_TABLE_TO_HASH(paths, RSPAMD_CONFDIR_INDEX); LUA_TABLE_TO_HASH(paths, RSPAMD_LOCAL_CONFDIR_INDEX); LUA_TABLE_TO_HASH(paths, RSPAMD_RUNDIR_INDEX); LUA_TABLE_TO_HASH(paths, RSPAMD_DBDIR_INDEX); LUA_TABLE_TO_HASH(paths, RSPAMD_LOGDIR_INDEX); LUA_TABLE_TO_HASH(paths, RSPAMD_WWWDIR_INDEX); LUA_TABLE_TO_HASH(paths, RSPAMD_PLUGINSDIR_INDEX); LUA_TABLE_TO_HASH(paths, RSPAMD_RULESDIR_INDEX); LUA_TABLE_TO_HASH(paths, RSPAMD_LUALIBDIR_INDEX); LUA_TABLE_TO_HASH(paths, RSPAMD_PREFIX_INDEX); } lua_pop(L, 1); if (lua_isfunction(L, 3)) { struct rspamd_lua_include_trace_cbdata cbd; lua_pushvalue(L, 3); cbd.cbref = luaL_ref(L, LUA_REGISTRYINDEX); cbd.L = L; if (!rspamd_config_parse_ucl(cfg, filename, paths, lua_include_trace_cb, &cbd, lua_toboolean(L, 4), &err)) { luaL_unref(L, LUA_REGISTRYINDEX, cbd.cbref); lua_pushboolean(L, false); lua_pushfstring(L, "failed to load config: %s", err->message); g_error_free(err); g_hash_table_unref(paths); return 2; } luaL_unref(L, LUA_REGISTRYINDEX, cbd.cbref); } else { if (!rspamd_config_parse_ucl(cfg, filename, paths, NULL, NULL, lua_toboolean(L, 3), &err)) { lua_pushboolean(L, false); lua_pushfstring(L, "failed to load config: %s", err->message); g_error_free(err); g_hash_table_unref(paths); return 2; } } rspamd_rcl_maybe_apply_lua_transform(cfg); rspamd_config_calculate_cksum(cfg); } else { return luaL_error(L, "invalid arguments"); } g_hash_table_unref(paths); lua_pushboolean(L, true); return 1; } #undef IDX_TO_HASH static gint lua_config_parse_rcl(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); GHashTable *excluded = g_hash_table_new_full(rspamd_str_hash, rspamd_str_equal, g_free, NULL); GError *err = NULL; struct rspamd_rcl_sections_map *top; if (cfg) { if (lua_istable(L, 2)) { lua_pushvalue(L, 2); for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { g_hash_table_insert(excluded, g_strdup(lua_tostring(L, -1)), GINT_TO_POINTER(-1)); } lua_pop(L, 1); } top = rspamd_rcl_config_init(cfg, excluded); if (!rspamd_rcl_parse(top, cfg, cfg, cfg->cfg_pool, cfg->cfg_ucl_obj, &err)) { lua_pushboolean(L, false); lua_pushfstring(L, "failed to load config: %s", err->message); g_error_free(err); g_hash_table_unref(excluded); rspamd_rcl_sections_free(top); return 2; } } else { return luaL_error(L, "invalid arguments"); } g_hash_table_unref(excluded); rspamd_rcl_sections_free(top); lua_pushboolean(L, true); return 1; } static gint lua_config_init_modules(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); if (cfg != NULL) { rspamd_lua_post_load_config(cfg); lua_pushboolean(L, rspamd_init_filters(cfg, false, false)); } else { return luaL_error(L, "invalid arguments"); } return 1; } static gint lua_config_init_subsystem(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); const gchar *subsystem = luaL_checkstring(L, 2); gchar **parts; guint nparts, i; if (cfg != NULL && subsystem != NULL) { parts = g_strsplit_set(subsystem, ";,", -1); nparts = g_strv_length(parts); for (i = 0; i < nparts; i++) { if (strcmp(parts[i], "filters") == 0) { rspamd_lua_post_load_config(cfg); rspamd_init_filters(cfg, false, false); } else if (strcmp(parts[i], "langdet") == 0) { if (!cfg->lang_det) { cfg->lang_det = rspamd_language_detector_init(cfg); rspamd_mempool_add_destructor(cfg->cfg_pool, (rspamd_mempool_destruct_t) rspamd_language_detector_unref, cfg->lang_det); } } else if (strcmp(parts[i], "stat") == 0) { rspamd_stat_init(cfg, NULL); } else if (strcmp(parts[i], "dns") == 0) { struct ev_loop *ev_base = lua_check_ev_base(L, 3); if (ev_base) { cfg->dns_resolver = rspamd_dns_resolver_init(rspamd_log_default_logger(), ev_base, cfg); } else { g_strfreev(parts); return luaL_error(L, "no event base specified"); } } else if (strcmp(parts[i], "symcache") == 0) { rspamd_symcache_init(cfg->cache); } else { int ret = luaL_error(L, "invalid param: %s", parts[i]); g_strfreev(parts); return ret; } } g_strfreev(parts); } else { return luaL_error(L, "invalid arguments"); } return 0; } static gint lua_config_register_re_selector(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); const gchar *name = luaL_checkstring(L, 2); const gchar *selector_str = luaL_checkstring(L, 3); const gchar *delimiter = ""; bool flatten = false; gint top = lua_gettop(L); bool res = false; if (cfg && name && selector_str) { if (lua_gettop(L) >= 4) { delimiter = luaL_checkstring(L, 4); if (lua_isboolean(L, 5)) { flatten = lua_toboolean(L, 5); } } if (luaL_dostring(L, "return require \"lua_selectors\"") != 0) { msg_warn_config("cannot require lua_selectors: %s", lua_tostring(L, -1)); } else { if (lua_type(L, -1) != LUA_TTABLE) { msg_warn_config("lua selectors must return " "table and not %s", lua_typename(L, lua_type(L, -1))); } else { lua_pushstring(L, "create_selector_closure"); lua_gettable(L, -2); if (lua_type(L, -1) != LUA_TFUNCTION) { msg_warn_config("create_selector_closure must return " "function and not %s", lua_typename(L, lua_type(L, -1))); } else { gint err_idx, ret; struct rspamd_config **pcfg; lua_pushcfunction(L, &rspamd_lua_traceback); err_idx = lua_gettop(L); /* Push function */ lua_pushvalue(L, -2); pcfg = lua_newuserdata(L, sizeof(*pcfg)); rspamd_lua_setclass(L, rspamd_config_classname, -1); *pcfg = cfg; lua_pushstring(L, selector_str); lua_pushstring(L, delimiter); lua_pushboolean(L, flatten); if ((ret = lua_pcall(L, 4, 1, err_idx)) != 0) { msg_err_config("call to create_selector_closure lua " "script failed (%d): %s", ret, lua_tostring(L, -1)); } else { if (lua_type(L, -1) != LUA_TFUNCTION) { msg_warn_config("create_selector_closure " "invocation must return " "function and not %s", lua_typename(L, lua_type(L, -1))); } else { ret = luaL_ref(L, LUA_REGISTRYINDEX); rspamd_re_cache_add_selector(cfg->re_cache, name, ret); res = true; } } } } } } else { return luaL_error(L, "invalid arguments"); } lua_settop(L, top); lua_pushboolean(L, res); if (res) { msg_info_config("registered regexp selector %s", name); } return 1; } static gint lua_config_get_tld_path(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); if (cfg != NULL) { lua_pushstring(L, cfg->tld_file); } else { return luaL_error(L, "invalid arguments"); } return 1; } static gint lua_config_get_dns_max_requests(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); if (cfg != NULL) { lua_pushinteger(L, cfg->dns_max_requests); } else { return luaL_error(L, "invalid arguments"); } return 1; } static gint lua_config_get_dns_timeout(lua_State *L) { LUA_TRACE_POINT; struct rspamd_config *cfg = lua_check_config(L, 1); if (cfg != NULL) { lua_pushnumber(L, cfg->dns_timeout); } else { return luaL_error(L, "invalid arguments"); } return 1; } static gint lua_monitored_alive(lua_State *L) { LUA_TRACE_POINT; struct rspamd_monitored *m = lua_check_monitored(L, 1); if (m) { lua_pushboolean(L, rspamd_monitored_alive(m)); } else { return luaL_error(L, "invalid arguments"); } return 1; } static gint lua_monitored_offline(lua_State *L) { LUA_TRACE_POINT; struct rspamd_monitored *m = lua_check_monitored(L, 1); if (m) { lua_pushnumber(L, rspamd_monitored_offline_time(m)); } else { return luaL_error(L, "invalid arguments"); } return 1; } static gint lua_monitored_total_offline(lua_State *L) { LUA_TRACE_POINT; struct rspamd_monitored *m = lua_check_monitored(L, 1); if (m) { lua_pushnumber(L, rspamd_monitored_total_offline_time(m)); } else { return luaL_error(L, "invalid arguments"); } return 1; } static gint lua_monitored_latency(lua_State *L) { LUA_TRACE_POINT; struct rspamd_monitored *m = lua_check_monitored(L, 1); if (m) { lua_pushnumber(L, rspamd_monitored_latency(m)); } else { return luaL_error(L, "invalid arguments"); } return 1; } void luaopen_config(lua_State *L) { rspamd_lua_new_class(L, rspamd_config_classname, configlib_m); lua_pop(L, 1); rspamd_lua_new_class(L, rspamd_monitored_classname, monitoredlib_m); lua_pop(L, 1); } void lua_call_finish_script(struct rspamd_config_cfg_lua_script *sc, struct rspamd_task *task) { struct rspamd_task **ptask; struct thread_entry *thread; thread = lua_thread_pool_get_for_task(task); thread->task = task; lua_State *L = thread->lua_state; lua_rawgeti(L, LUA_REGISTRYINDEX, sc->cbref); ptask = lua_newuserdata(L, sizeof(struct rspamd_task *)); rspamd_lua_setclass(L, rspamd_task_classname, -1); *ptask = task; lua_thread_call(thread, 1); }