/*- * Copyright 2016 Vsevolod Stakhov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "lua_common.h" #include "expression.h" #include "composites.h" #include "unix-std.h" #ifdef HAVE_SYS_UTSNAME_H #include #endif /* * This is implementation of lua routines to handle config file params */ /* Process a single item in 'metrics' table */ static void lua_process_metric (lua_State *L, const gchar *name, struct rspamd_config *cfg) { GList *metric_list; gchar *symbol; const gchar *desc = NULL; struct metric *metric; gdouble *score; struct rspamd_symbol_def *s; /* Get module opt structure */ if ((metric = g_hash_table_lookup (cfg->metrics, name)) == NULL) { metric = rspamd_config_new_metric (cfg, metric, name); } /* Now iterate throught module table */ for (lua_pushnil (L); lua_next (L, -2); lua_pop (L, 1)) { /* key - -2, value - -1 */ symbol = rspamd_mempool_strdup (cfg->cfg_pool, luaL_checkstring (L, -2)); if (symbol != NULL) { if (lua_istable (L, -1)) { /* We got a table, so extract individual attributes */ lua_pushstring (L, "weight"); lua_gettable (L, -2); if (lua_isnumber (L, -1)) { score = rspamd_mempool_alloc (cfg->cfg_pool, sizeof (double)); *score = lua_tonumber (L, -1); } else { msg_warn_config("cannot get weight of symbol: %s", symbol); continue; } lua_pop (L, 1); lua_pushstring (L, "description"); lua_gettable (L, -2); if (lua_isstring (L, -1)) { desc = lua_tostring (L, -1); } lua_pop (L, 1); } else if (lua_isnumber (L, -1)) { /* Just got weight */ score = rspamd_mempool_alloc (cfg->cfg_pool, sizeof (double)); *score = lua_tonumber (L, -1); } else { msg_warn_config("cannot get weight of symbol: %s", symbol); continue; } /* Insert symbol */ if ((s = g_hash_table_lookup (metric->symbols, symbol)) != NULL) { msg_info_config("replacing weight for symbol %s: %.2f -> %.2f", symbol, *s->weight_ptr, *score); s->weight_ptr = score; } else { s = rspamd_mempool_alloc (cfg->cfg_pool, sizeof (*s)); s->name = symbol; s->weight_ptr = score; g_hash_table_insert (metric->symbols, symbol, s); } if (desc) { s->description = rspamd_mempool_strdup (cfg->cfg_pool, desc); } if ((metric_list = g_hash_table_lookup (cfg->metrics_symbols, symbol)) == NULL) { metric_list = g_list_prepend (NULL, metric); rspamd_mempool_add_destructor (cfg->cfg_pool, (rspamd_mempool_destruct_t)g_list_free, metric_list); g_hash_table_insert (cfg->metrics_symbols, symbol, metric_list); } else { /* Slow but keep start element of list in safe */ if (!g_list_find (metric_list, metric)) { metric_list = g_list_append (metric_list, metric); } } } } } /* Do post load initialization based on lua */ void rspamd_lua_post_load_config (struct rspamd_config *cfg) { lua_State *L = cfg->lua_state; const gchar *name, *val; gchar *sym; struct rspamd_expression *expr, *old_expr; ucl_object_t *obj; gsize keylen; GError *err = NULL; /* First check all module options that may be overriden in 'config' global */ lua_getglobal (L, "config"); if (lua_istable (L, -1)) { /* Iterate */ for (lua_pushnil (L); lua_next (L, -2); lua_pop (L, 1)) { /* 'key' is at index -2 and 'value' is at index -1 */ /* Key must be a string and value must be a table */ name = luaL_checklstring (L, -2, &keylen); if (name != NULL && lua_istable (L, -1)) { obj = ucl_object_lua_import (L, lua_gettop (L)); if (obj != NULL) { ucl_object_insert_key_merged (cfg->rcl_obj, obj, name, keylen, true); } } } } /* Check metrics settings */ lua_getglobal (L, "metrics"); if (lua_istable (L, -1)) { /* Iterate */ for (lua_pushnil (L); lua_next (L, -2); lua_pop (L, 1)) { /* 'key' is at index -2 and 'value' is at index -1 */ /* Key must be a string and value must be a table */ name = luaL_checkstring (L, -2); if (name != NULL && lua_istable (L, -1)) { lua_process_metric (L, name, cfg); } } } /* Check composites */ lua_getglobal (L, "composites"); if (lua_istable (L, -1)) { /* Iterate */ for (lua_pushnil (L); lua_next (L, -2); lua_pop (L, 1)) { /* 'key' is at index -2 and 'value' is at index -1 */ /* Key must be a string and value must be a table */ name = luaL_checkstring (L, -2); if (name != NULL && lua_isstring (L, -1)) { val = lua_tostring (L, -1); sym = rspamd_mempool_strdup (cfg->cfg_pool, name); if (!rspamd_parse_expression (val, 0, &composite_expr_subr, NULL, cfg->cfg_pool, &err, &expr)) { msg_err_config("cannot parse composite expression '%s': %s", val, err->message); g_error_free (err); err = NULL; continue; } /* Now check hash table for this composite */ if ((old_expr = g_hash_table_lookup (cfg->composite_symbols, name)) != NULL) { msg_info_config("replacing composite symbol %s", name); g_hash_table_replace (cfg->composite_symbols, sym, expr); } else { g_hash_table_insert (cfg->composite_symbols, sym, expr); rspamd_symbols_cache_add_symbol (cfg->cache, sym, 0, NULL, NULL, SYMBOL_TYPE_COMPOSITE, -1); } } } } } /* Handle lua dynamic config param */ gboolean rspamd_lua_handle_param (struct rspamd_task *task, gchar *mname, gchar *optname, enum lua_var_type expected_type, gpointer *res) { /* xxx: Adopt this for rcl */ /* Option not found */ *res = NULL; return FALSE; } #define FAKE_RES_VAR "rspamd_res" gboolean rspamd_lua_check_condition (struct rspamd_config *cfg, const gchar *condition) { lua_State *L = cfg->lua_state; gchar *hostbuf, *condbuf; gsize hostlen; gboolean res; #ifdef HAVE_SYS_UTSNAME_H struct utsname uts; #endif /* Set some globals for condition */ /* XXX: think what other variables can be useful */ hostlen = sysconf (_SC_HOST_NAME_MAX) + 1; hostbuf = alloca (hostlen); gethostname (hostbuf, hostlen); hostbuf[hostlen - 1] = '\0'; /* Hostname */ lua_pushstring (L, hostbuf); lua_setglobal (L, "hostname"); /* Config file name */ lua_pushstring (L, cfg->cfg_name); lua_setglobal (L, "cfg_name"); /* Check for uname */ #ifdef HAVE_SYS_UTSNAME_H uname (&uts); lua_pushstring (L, uts.sysname); lua_setglobal (L, "osname"); lua_pushstring (L, uts.release); lua_setglobal (L, "osrelease"); #else lua_pushstring (L, "unknown"); lua_setglobal (L, "osname"); lua_pushstring (L, ""); lua_setglobal (L, "osrelease"); #endif #ifdef HAVE_OPENSSL lua_pushboolean (L, TRUE); #else lua_pushboolean (L, FALSE); #endif lua_setglobal (L, "rspamd_supports_rsa"); /* Rspamd paths */ lua_newtable (L); rspamd_lua_table_set (L, "confdir", RSPAMD_CONFDIR); rspamd_lua_table_set (L, "rundir", RSPAMD_RUNDIR); rspamd_lua_table_set (L, "dbdir", RSPAMD_DBDIR); rspamd_lua_table_set (L, "logdir", RSPAMD_LOGDIR); rspamd_lua_table_set (L, "pluginsdir", RSPAMD_PLUGINSDIR); rspamd_lua_table_set (L, "prefix", RSPAMD_PREFIX); lua_setglobal (L, "rspamd_paths"); /* Make fake string */ hostlen = sizeof (FAKE_RES_VAR "=") + strlen (condition); condbuf = g_malloc (hostlen); rspamd_strlcpy (condbuf, FAKE_RES_VAR "=", sizeof (FAKE_RES_VAR "=")); g_strlcat (condbuf, condition, hostlen); /* Evaluate condition */ if (luaL_dostring (L, condbuf) != 0) { msg_err_config("eval of '%s' failed: '%s'", condition, lua_tostring (L, -1)); g_free (condbuf); return FALSE; } /* Get global variable res to get result */ lua_getglobal (L, FAKE_RES_VAR); if (!lua_isboolean (L, -1)) { msg_err_config("bad string evaluated: %s, type: %s", condbuf, lua_typename (L, lua_type (L, -1))); g_free (condbuf); return FALSE; } res = lua_toboolean (L, -1); g_free (condbuf); return res; }