aboutsummaryrefslogtreecommitdiffstats
path: root/tests/Core/Command/Preview
ModeNameSize
-rw-r--r--RepairTest.php4740logstatsplain
"> = FALSE; required = FALSE; keylen = 0; key = NULL; p++; break; case read_class_start: if (*p == '{') { cls = p + 1; state = read_class; } else { if (is_table) { lua_pop(L, 1); } g_set_error(err, lua_error_quark(), 2, "missing classname for " "%.*s", (gint) keylen, key); va_end(ap); return FALSE; } p++; break; case read_class: if (*p == '}') { clslen = p - cls; if (clslen == 0) { if (is_table) { lua_pop(L, 1); } g_set_error(err, lua_error_quark(), 2, "empty classname for " "%*.s", (gint) keylen, key); va_end(ap); return FALSE; } rspamd_snprintf(classbuf, sizeof(classbuf), "rspamd{%*s}", (gint) clslen, cls); /* * We skip class check here for speed in non-table mode */ if (!failed && (!is_table || rspamd_lua_check_class(L, idx, classbuf))) { if (direct_userdata) { void **arg_p = (va_arg(ap, void **)); *arg_p = lua_touserdata(L, idx); } else { *(va_arg(ap, void **)) = *(void **) lua_touserdata(L, idx); } } else { if (!failed) { g_set_error(err, lua_error_quark(), 2, "invalid class for key %.*s, expected %s, got %s", (gint) keylen, key, classbuf, rspamd_lua_class_tostring_buf(L, FALSE, idx)); va_end(ap); return FALSE; } } if (is_table) { lua_pop(L, 1); } else { idx++; } if (failed && required) { g_set_error(err, lua_error_quark(), 2, "required parameter " "%.*s is missing", (gint) keylen, key); va_end(ap); return FALSE; } /* Reset read params */ state = read_semicolon; failed = FALSE; required = FALSE; keylen = 0; key = NULL; } p++; break; case read_semicolon: if (*p == ';' || p == end) { state = read_key; key = NULL; keylen = 0; failed = FALSE; } else { g_set_error(err, lua_error_quark(), 2, "bad format string: %s," " at char %c, position %d", extraction_pattern, *p, (int) (p - extraction_pattern)); va_end(ap); return FALSE; } p++; break; } } va_end(ap); return TRUE; } static void rspamd_lua_traceback_string(lua_State *L, luaL_Buffer *buf) { gint i = 1, r; lua_Debug d; gchar tmp[256]; while (lua_getstack(L, i++, &d)) { lua_getinfo(L, "nSl", &d); r = rspamd_snprintf(tmp, sizeof(tmp), " [%d]:{%s:%d - %s [%s]};", i - 1, d.short_src, d.currentline, (d.name ? d.name : "<unknown>"), d.what); luaL_addlstring(buf, tmp, r); } } gint rspamd_lua_traceback(lua_State *L) { luaL_Buffer b; luaL_buffinit(L, &b); rspamd_lua_get_traceback_string(L, &b); luaL_pushresult(&b); return 1; } void rspamd_lua_get_traceback_string(lua_State *L, luaL_Buffer *buf) { const gchar *msg = lua_tostring(L, -1); if (msg) { luaL_addstring(buf, msg); lua_pop(L, 1); /* Error string */ } else { luaL_addstring(buf, "unknown error"); } luaL_addstring(buf, "; trace:"); rspamd_lua_traceback_string(L, buf); } guint rspamd_lua_table_size(lua_State *L, gint tbl_pos) { guint tbl_size = 0; if (!lua_istable(L, tbl_pos)) { return 0; } #if LUA_VERSION_NUM >= 502 tbl_size = lua_rawlen(L, tbl_pos); #else tbl_size = lua_objlen(L, tbl_pos); #endif return tbl_size; } static void * rspamd_lua_check_udata_common(lua_State *L, gint pos, const gchar *classname, gboolean fatal) { void *p = lua_touserdata(L, pos); guint i, top = lua_gettop(L); khiter_t k; if (p == NULL) { goto err; } else { /* Match class */ if (lua_getmetatable(L, pos)) { struct rspamd_lua_context *ctx = rspamd_lua_ctx_by_state(L); k = kh_get(lua_class_set, ctx->classes, classname); if (k == kh_end(ctx->classes)) { goto err; } lua_rawgeti(L, LUA_REGISTRYINDEX, kh_value(ctx->classes, k)); if (!lua_rawequal(L, -1, -2)) { goto err; } } else { goto err; } } lua_settop(L, top); return p; err: if (fatal) { const gchar *actual_classname = NULL; if (lua_type(L, pos) == LUA_TUSERDATA && lua_getmetatable(L, pos)) { lua_pushstring(L, "__index"); lua_gettable(L, -2); lua_pushstring(L, "class"); lua_gettable(L, -2); actual_classname = lua_tostring(L, -1); } else { actual_classname = lua_typename(L, lua_type(L, pos)); } luaL_Buffer buf; gchar tmp[512]; gint r; luaL_buffinit(L, &buf); r = rspamd_snprintf(tmp, sizeof(tmp), "expected %s at position %d, but userdata has " "%s metatable; trace: ", classname, pos, actual_classname); luaL_addlstring(&buf, tmp, r); rspamd_lua_traceback_string(L, &buf); r = rspamd_snprintf(tmp, sizeof(tmp), " stack(%d): ", top); luaL_addlstring(&buf, tmp, r); for (i = 1; i <= MIN(top, 10); i++) { if (lua_type(L, i) == LUA_TUSERDATA) { const char *clsname; if (lua_getmetatable(L, i)) { lua_pushstring(L, "__index"); lua_gettable(L, -2); lua_pushstring(L, "class"); lua_gettable(L, -2); clsname = lua_tostring(L, -1); } else { clsname = lua_typename(L, lua_type(L, i)); } r = rspamd_snprintf(tmp, sizeof(tmp), "[%d: ud=%s] ", i, clsname); luaL_addlstring(&buf, tmp, r); } else { r = rspamd_snprintf(tmp, sizeof(tmp), "[%d: %s] ", i, lua_typename(L, lua_type(L, i))); luaL_addlstring(&buf, tmp, r); } } luaL_pushresult(&buf); msg_err("lua type error: %s", lua_tostring(L, -1)); } lua_settop(L, top); return NULL; } void * rspamd_lua_check_udata(lua_State *L, gint pos, const gchar *classname) { return rspamd_lua_check_udata_common(L, pos, classname, TRUE); } void * rspamd_lua_check_udata_maybe(lua_State *L, gint pos, const gchar *classname) { return rspamd_lua_check_udata_common(L, pos, classname, FALSE); } struct rspamd_async_session * lua_check_session(lua_State *L, gint pos) { void *ud = rspamd_lua_check_udata(L, pos, "rspamd{session}"); luaL_argcheck(L, ud != NULL, pos, "'session' expected"); return ud ? *((struct rspamd_async_session **) ud) : NULL; } struct ev_loop * lua_check_ev_base(lua_State *L, gint pos) { void *ud = rspamd_lua_check_udata(L, pos, "rspamd{ev_base}"); luaL_argcheck(L, ud != NULL, pos, "'event_base' expected"); return ud ? *((struct ev_loop **) ud) : NULL; } static void rspamd_lua_run_postloads_error(struct thread_entry *thread, int ret, const char *msg); void rspamd_lua_run_postloads(lua_State *L, struct rspamd_config *cfg, struct ev_loop *ev_base, struct rspamd_worker *w) { struct rspamd_config_cfg_lua_script *sc; struct rspamd_config **pcfg; struct ev_loop **pev_base; struct rspamd_worker **pw; /* Execute post load scripts */ LL_FOREACH(cfg->on_load_scripts, sc) { struct thread_entry *thread = lua_thread_pool_get_for_config(cfg); thread->error_callback = rspamd_lua_run_postloads_error; L = thread->lua_state; lua_rawgeti(L, LUA_REGISTRYINDEX, sc->cbref); pcfg = lua_newuserdata(L, sizeof(*pcfg)); *pcfg = cfg; rspamd_lua_setclass(L, "rspamd{config}", -1); pev_base = lua_newuserdata(L, sizeof(*pev_base)); *pev_base = ev_base; rspamd_lua_setclass(L, "rspamd{ev_base}", -1); pw = lua_newuserdata(L, sizeof(*pw)); *pw = w; rspamd_lua_setclass(L, "rspamd{worker}", -1); lua_thread_call(thread, 3); } } void rspamd_lua_run_config_post_init(lua_State *L, struct rspamd_config *cfg) { struct rspamd_config_cfg_lua_script *sc; struct rspamd_config **pcfg; LL_FOREACH(cfg->post_init_scripts, sc) { lua_pushcfunction(L, &rspamd_lua_traceback); gint err_idx = lua_gettop(L); lua_rawgeti(L, LUA_REGISTRYINDEX, sc->cbref); pcfg = lua_newuserdata(L, sizeof(*pcfg)); *pcfg = cfg; rspamd_lua_setclass(L, "rspamd{config}", -1); if (lua_pcall(L, 1, 0, err_idx) != 0) { msg_err_config("cannot run config post init script: %s; priority = %d", lua_tostring(L, -1), sc->priority); } lua_settop(L, err_idx - 1); } } void rspamd_lua_run_config_unload(lua_State *L, struct rspamd_config *cfg) { struct rspamd_config_cfg_lua_script *sc; struct rspamd_config **pcfg; LL_FOREACH(cfg->config_unload_scripts, sc) { lua_pushcfunction(L, &rspamd_lua_traceback); gint err_idx = lua_gettop(L); lua_rawgeti(L, LUA_REGISTRYINDEX, sc->cbref); pcfg = lua_newuserdata(L, sizeof(*pcfg)); *pcfg = cfg; rspamd_lua_setclass(L, "rspamd{config}", -1); if (lua_pcall(L, 1, 0, err_idx) != 0) { msg_err_config("cannot run config post init script: %s", lua_tostring(L, -1)); } lua_settop(L, err_idx - 1); } } static void rspamd_lua_run_postloads_error(struct thread_entry *thread, int ret, const char *msg) { struct rspamd_config *cfg = thread->cfg; msg_err_config("error executing post load code: %s", msg); } struct rspamd_lua_ref_cbdata { lua_State *L; gint cbref; }; static void rspamd_lua_ref_dtor(gpointer p) { struct rspamd_lua_ref_cbdata *cbdata = p; luaL_unref(cbdata->L, LUA_REGISTRYINDEX, cbdata->cbref); } void rspamd_lua_add_ref_dtor(lua_State *L, rspamd_mempool_t *pool, gint ref) { struct rspamd_lua_ref_cbdata *cbdata; if (ref != -1) { cbdata = rspamd_mempool_alloc(pool, sizeof(*cbdata)); cbdata->cbref = ref; cbdata->L = L; rspamd_mempool_add_destructor(pool, rspamd_lua_ref_dtor, cbdata); } } gboolean rspamd_lua_require_function(lua_State *L, const gchar *modname, const gchar *funcname) { gint table_pos, err_pos; lua_pushcfunction(L, &rspamd_lua_traceback); err_pos = lua_gettop(L); lua_getglobal(L, "require"); if (lua_isnil(L, -1)) { lua_remove(L, err_pos); lua_pop(L, 1); return FALSE; } lua_pushstring(L, modname); /* Now try to call */ if (lua_pcall(L, 1, 1, 0) != 0) { lua_remove(L, err_pos); msg_warn("require of %s.%s failed: %s", modname, funcname, lua_tostring(L, -1)); lua_pop(L, 1); return FALSE; } lua_remove(L, err_pos); /* Now we should have a table with results */ if (funcname) { if (!lua_istable(L, -1)) { msg_warn("require of %s.%s failed: not a table but %s", modname, funcname, lua_typename(L, lua_type(L, -1))); lua_pop(L, 1); return FALSE; } table_pos = lua_gettop(L); lua_pushstring(L, funcname); lua_gettable(L, -2); if (lua_type(L, -1) == LUA_TFUNCTION) { /* Remove table, preserve just a function */ lua_remove(L, table_pos); return TRUE; } else { msg_warn("require of %s.%s failed: not a function but %s", modname, funcname, lua_typename(L, lua_type(L, -1))); } lua_pop(L, 2); return FALSE; } else if (lua_isfunction(L, -1)) { return TRUE; } else { msg_warn("require of %s failed: not a function but %s", modname, lua_typename(L, lua_type(L, -1))); lua_pop(L, 1); return FALSE; } } gint rspamd_lua_function_ref_from_str(lua_State *L, const gchar *str, gsize slen, const gchar *modname, GError **err) { gint err_idx, ref_idx; lua_pushcfunction(L, &rspamd_lua_traceback); err_idx = lua_gettop(L); /* Load file */ if (luaL_loadbuffer(L, str, slen, modname) != 0) { g_set_error(err, lua_error_quark(), EINVAL, "%s: cannot load lua script: %s", modname, lua_tostring(L, -1)); lua_settop(L, err_idx - 1); /* Error function */ return LUA_NOREF; } /* Now call it */ if (lua_pcall(L, 0, 1, err_idx) != 0) { g_set_error(err, lua_error_quark(), EINVAL, "%s: cannot init lua script: %s", modname, lua_tostring(L, -1)); lua_settop(L, err_idx - 1); return LUA_NOREF; } if (!lua_isfunction(L, -1)) { g_set_error(err, lua_error_quark(), EINVAL, "%s: cannot init lua script: " "must return function not %s", modname, lua_typename(L, lua_type(L, -1))); lua_settop(L, err_idx - 1); return LUA_NOREF; } ref_idx = luaL_ref(L, LUA_REGISTRYINDEX); lua_settop(L, err_idx - 1); return ref_idx; } gboolean rspamd_lua_try_load_redis(lua_State *L, const ucl_object_t *obj, struct rspamd_config *cfg, gint *ref_id) { gint err_idx; struct rspamd_config **pcfg; lua_pushcfunction(L, &rspamd_lua_traceback); err_idx = lua_gettop(L); /* Obtain function */ if (!rspamd_lua_require_function(L, "lua_redis", "try_load_redis_servers")) { msg_err_config("cannot require lua_redis"); lua_pop(L, 2); return FALSE; } /* Function arguments */ ucl_object_push_lua(L, obj, false); pcfg = lua_newuserdata(L, sizeof(*pcfg)); rspamd_lua_setclass(L, "rspamd{config}", -1); *pcfg = cfg; lua_pushboolean(L, false); /* no_fallback */ if (lua_pcall(L, 3, 1, err_idx) != 0) { msg_err_config("cannot call lua try_load_redis_servers script: %s", lua_tostring(L, -1)); lua_settop(L, 0); return FALSE; } if (lua_istable(L, -1)) { if (ref_id) { /* Ref table */ lua_pushvalue(L, -1); *ref_id = luaL_ref(L, LUA_REGISTRYINDEX); lua_settop(L, 0); } else { /* Leave it on the stack */ lua_insert(L, err_idx); lua_settop(L, err_idx); } return TRUE; } else { lua_settop(L, 0); } return FALSE; } void rspamd_lua_push_full_word(lua_State *L, rspamd_stat_token_t *w) { gint fl_cnt; lua_createtable(L, 4, 0); if (w->stemmed.len > 0) { lua_pushlstring(L, w->stemmed.begin, w->stemmed.len); lua_rawseti(L, -2, 1); } else { lua_pushstring(L, ""); lua_rawseti(L, -2, 1); } if (w->normalized.len > 0) { lua_pushlstring(L, w->normalized.begin, w->normalized.len); lua_rawseti(L, -2, 2); } else { lua_pushstring(L, ""); lua_rawseti(L, -2, 2); } if (w->original.len > 0) { lua_pushlstring(L, w->original.begin, w->original.len); lua_rawseti(L, -2, 3); } else { lua_pushstring(L, ""); lua_rawseti(L, -2, 3); } /* Flags part */ fl_cnt = 1; lua_createtable(L, 4, 0); if (w->flags & RSPAMD_STAT_TOKEN_FLAG_NORMALISED) { lua_pushstring(L, "normalised"); lua_rawseti(L, -2, fl_cnt++); } if (w->flags & RSPAMD_STAT_TOKEN_FLAG_BROKEN_UNICODE) { lua_pushstring(L, "broken_unicode"); lua_rawseti(L, -2, fl_cnt++); } if (w->flags & RSPAMD_STAT_TOKEN_FLAG_UTF) { lua_pushstring(L, "utf"); lua_rawseti(L, -2, fl_cnt++); } if (w->flags & RSPAMD_STAT_TOKEN_FLAG_TEXT) { lua_pushstring(L, "text"); lua_rawseti(L, -2, fl_cnt++); } if (w->flags & RSPAMD_STAT_TOKEN_FLAG_HEADER) { lua_pushstring(L, "header"); lua_rawseti(L, -2, fl_cnt++); } if (w->flags & (RSPAMD_STAT_TOKEN_FLAG_META | RSPAMD_STAT_TOKEN_FLAG_LUA_META)) { lua_pushstring(L, "meta"); lua_rawseti(L, -2, fl_cnt++); } if (w->flags & RSPAMD_STAT_TOKEN_FLAG_STOP_WORD) { lua_pushstring(L, "stop_word"); lua_rawseti(L, -2, fl_cnt++); } if (w->flags & RSPAMD_STAT_TOKEN_FLAG_INVISIBLE_SPACES) { lua_pushstring(L, "invisible_spaces"); lua_rawseti(L, -2, fl_cnt++); } if (w->flags & RSPAMD_STAT_TOKEN_FLAG_STEMMED) { lua_pushstring(L, "stemmed"); lua_rawseti(L, -2, fl_cnt++); } lua_rawseti(L, -2, 4); } gint rspamd_lua_push_words(lua_State *L, GArray *words, enum rspamd_lua_words_type how) { rspamd_stat_token_t *w; guint i, cnt; lua_createtable(L, words->len, 0); for (i = 0, cnt = 1; i < words->len; i++) { w = &g_array_index(words, rspamd_stat_token_t, i); switch (how) { case RSPAMD_LUA_WORDS_STEM: if (w->stemmed.len > 0) { lua_pushlstring(L, w->stemmed.begin, w->stemmed.len); lua_rawseti(L, -2, cnt++); } break; case RSPAMD_LUA_WORDS_NORM: if (w->normalized.len > 0) { lua_pushlstring(L, w->normalized.begin, w->normalized.len); lua_rawseti(L, -2, cnt++); } break; case RSPAMD_LUA_WORDS_RAW: if (w->original.len > 0) { lua_pushlstring(L, w->original.begin, w->original.len); lua_rawseti(L, -2, cnt++); } break; case RSPAMD_LUA_WORDS_FULL: rspamd_lua_push_full_word(L, w); /* Push to the resulting vector */ lua_rawseti(L, -2, cnt++); break; default: break; } } return 1; } gchar * rspamd_lua_get_module_name(lua_State *L) { lua_Debug d; gchar *p; gchar func_buf[128]; 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(func_buf, sizeof(func_buf), "%10s...]:%d", p, d.currentline); } else { rspamd_snprintf(func_buf, sizeof(func_buf), "%s:%d", p, d.currentline); } return g_strdup(func_buf); } return NULL; } bool rspamd_lua_universal_pcall(lua_State *L, gint cbref, const gchar *strloc, gint nret, const gchar *args, GError **err, ...) { va_list ap; const gchar *argp = args, *classname; gint err_idx, nargs = 0; gpointer *cls_ptr; gsize sz; /* Error function */ lua_pushcfunction(L, &rspamd_lua_traceback); err_idx = lua_gettop(L); va_start(ap, err); /* Called function */ if (cbref > 0) { lua_rawgeti(L, LUA_REGISTRYINDEX, cbref); } else { /* Assume that function was on top of the stack */ lua_pushvalue(L, err_idx - 1); } /* * Possible arguments * - i - lua_integer, argument - gint64 * - n - lua_number, argument - gdouble * - s - lua_string, argument - const gchar * (zero terminated) * - l - lua_lstring, argument - (size_t + const gchar *) pair * - u - lua_userdata, argument - (const char * + void *) - classname + pointer * - b - lua_boolean, argument - gboolean (not bool due to varargs promotion) * - f - lua_function, argument - int - position of the function on stack (not lua_registry) * - t - lua_text, argument - int - position of the lua_text on stack (not lua_registry) */ while (*argp) { switch (*argp) { case 'i': lua_pushinteger(L, va_arg(ap, gint64)); nargs++; break; case 'n': lua_pushnumber(L, va_arg(ap, gdouble)); nargs++; break; case 's': lua_pushstring(L, va_arg(ap, const gchar *)); nargs++; break; case 'l': sz = va_arg(ap, gsize); lua_pushlstring(L, va_arg(ap, const gchar *), sz); nargs++; break; case 'b': lua_pushboolean(L, va_arg(ap, gboolean)); nargs++; break; case 'u': classname = va_arg(ap, const gchar *); cls_ptr = (gpointer *) lua_newuserdata(L, sizeof(gpointer)); *cls_ptr = va_arg(ap, gpointer); rspamd_lua_setclass(L, classname, -1); nargs++; break; case 'f': case 't': lua_pushvalue(L, va_arg(ap, gint)); nargs++; break; default: lua_settop(L, err_idx - 1); g_set_error(err, lua_error_quark(), EINVAL, "invalid argument character: %c at %s", *argp, argp); va_end(ap); return false; } argp++; } if (lua_pcall(L, nargs, nret, err_idx) != 0) { g_set_error(err, lua_error_quark(), EBADF, "error when calling lua function from %s: %s", strloc, lua_tostring(L, -1)); lua_settop(L, err_idx - 1); va_end(ap); return false; } lua_remove(L, err_idx); va_end(ap); return true; } #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM <= 502 gint rspamd_lua_geti(lua_State *L, int pos, int i) { pos = lua_absindex(L, pos); lua_pushinteger(L, i); lua_gettable(L, pos); return lua_type(L, -1); } #endif