From: Aki Tuomi Date: Tue, 1 Oct 2024 11:44:59 +0000 (+0300) Subject: mail-lua: Add lua_call filter to var_expand X-Git-Tag: 2.4.0~303 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=ee8739fb2cb419b727545e8bcc41bc47f837c916;p=thirdparty%2Fdovecot%2Fcore.git mail-lua: Add lua_call filter to var_expand When mail-lua is loaded, it registers new var_expand filter lua_call(fn, parameter, parameter) This will call function in mail lua script with fn(mail_user, parameter, parameter, .., state) fn must (return 0, value) on success, and (-1, errmsg) on error --- diff --git a/src/plugins/mail-lua/mail-lua-plugin.c b/src/plugins/mail-lua/mail-lua-plugin.c index 04c45fa895..8bc73f02fc 100644 --- a/src/plugins/mail-lua/mail-lua-plugin.c +++ b/src/plugins/mail-lua/mail-lua-plugin.c @@ -2,6 +2,7 @@ #include "lib.h" #include "module-dir.h" +#include "var-expand-private.h" #include "mail-lua-plugin.h" #include "mail-storage-lua.h" #include "mail-storage-private.h" @@ -143,6 +144,61 @@ static void mail_lua_user_created(struct mail_user *user) MODULE_CONTEXT_SET(user, mail_lua_user_module, luser); } +struct mail_lua_script { + char *file; + struct dlua_script *script; +}; + +static ARRAY(struct mail_lua_script) lua_scripts = ARRAY_INIT; + +static int +mail_lua_script_cmp(const char *key, const struct mail_lua_script *script) +{ + return strcmp(key, script->file); +} + +static void mail_lua_scripts_free(void) +{ + struct mail_lua_script *script; + if (array_is_empty(&lua_scripts)) + return; + array_foreach_modifiable(&lua_scripts, script) { + i_free(script->file); + dlua_script_unref(&script->script); + } + array_free(&lua_scripts); +} + +static int mail_lua_script_load(const char *file, struct dlua_script **script_r, + const char **error_r) +{ + /* check if it's already there */ + if (!array_is_empty(&lua_scripts)) { + const struct mail_lua_script *lookup = + array_lsearch(&lua_scripts, file, mail_lua_script_cmp); + if (lookup != NULL) { + *script_r = lookup->script; + return 0; + } + } + struct dlua_script *script; + if (dlua_script_create_file(file, &script, NULL, error_r) < 0) + return -1; + dlua_dovecot_register(script); + if (dlua_script_init(script, error_r) < 0) { + dlua_script_unref(&script); + return -1; + } + /* register the script */ + if (!array_is_created(&lua_scripts)) + i_array_init(&lua_scripts, 1); + struct mail_lua_script *entry = array_append_space(&lua_scripts); + entry->file = i_strdup(file); + entry->script = script; + *script_r = script; + return 0; +} + bool mail_lua_plugin_get_script(struct mail_user *user, struct dlua_script **script_r) { @@ -154,6 +210,161 @@ bool mail_lua_plugin_get_script(struct mail_user *user, return FALSE; } +static int mail_lua_script_call(const char *fn, struct mail_user *user, + ARRAY_TYPE(const_string) *params, + struct var_expand_state *state, + struct dlua_script *script, + const char **error_r) +{ + const char *value; + const char *error; + int npar = 0; + + /* Push user as first parameter */ + if (user != NULL) { + npar++; + dlua_push_mail_user(script->L, user); + } + + /* Any user provided parameters come next */ + array_foreach_elem(params, value) { + lua_pushstring(script->L, value); + npar++; + } + + /* If there is state, push that too */ + if (state->transfer_set) { + lua_pushlstring(script->L, state->transfer->data, + state->transfer->used); + npar++; + } + + /* Call fn(user, provided params.., state) */ + if (dlua_pcall(script->L, fn, npar, 2, &error) < 0) { + *error_r = t_strdup_printf("%s(user) failed: %s", fn, error); + return -1; + } + + int ret = lua_tonumber(script->L, -2); + + if (ret < 0) { + var_expand_state_unset_transfer(state); + const char *errmsg = lua_tostring(script->L, -1); + *error_r = t_strdup_printf("%s(user) failed: %s", + fn, errmsg); + } else { + size_t len; + const void *value = lua_tolstring(script->L, -1, &len); + if (value == NULL) + value = ""; + var_expand_state_set_transfer_data(state, value, len); + } + + lua_pop(script->L, 2); + (void)lua_gc(script->L, LUA_GCCOLLECT, 0); + + return ret; +} + +static int mail_lua_var_expand_lua_file(const struct var_expand_statement *stmt, + struct var_expand_state *state, + const char **error_r) +{ + const char *file; + const char *fn; + const char *value; + + ARRAY_TYPE(const_string) params; + t_array_init(¶ms, 1); + struct var_expand_parameter_iter_context *iter = + var_expand_parameter_iter_init(stmt); + while (var_expand_parameter_iter_more(iter)) { + const struct var_expand_parameter *par = + var_expand_parameter_iter_next(iter); + const char *key = var_expand_parameter_key(par); + if (key != NULL) { + *error_r = t_strdup_printf("Unsupported key '%s'", key); + return -1; + } + switch (var_expand_parameter_idx(par)) { + case 0: + if (var_expand_parameter_string_or_var(state, par, &file, error_r) < 0) + return -1; + break; + case 1: + if (var_expand_parameter_string_or_var(state, par, &fn, error_r) < 0) + return -1; + break; + default: + if (var_expand_parameter_any_or_var(state, par, &value, error_r) < 0) + return -1; + array_push_back(¶ms, &value); + } + } + + struct dlua_script *script; + if (mail_lua_script_load(file, &script, error_r) < 0) + return -1; + + int ret = mail_lua_script_call(fn, NULL, ¶ms, state, script, error_r); + + /* normalize return value */ + return ret < 0 ? -1 : 0; +} + +static int mail_lua_var_expand_lua_call(const struct var_expand_statement *stmt, + struct var_expand_state *state, + const char **error_r) +{ + const char *fn; + const char *value; + + ARRAY_TYPE(const_string) params; + t_array_init(¶ms, 1); + struct var_expand_parameter_iter_context *iter = + var_expand_parameter_iter_init(stmt); + while (var_expand_parameter_iter_more(iter)) { + const struct var_expand_parameter *par = + var_expand_parameter_iter_next(iter); + const char *key = var_expand_parameter_key(par); + if (key != NULL) { + *error_r = t_strdup_printf("Unsupported key '%s'", key); + return -1; + } + if (var_expand_parameter_idx(par) == 0) { + if (var_expand_parameter_string_or_var(state, par, &fn, error_r) < 0) + return -1; + } else { + if (var_expand_parameter_any_or_var(state, par, &value, error_r) < 0) + return -1; + array_push_back(¶ms, &value); + } + } + + if (state->params->event == NULL) { + *error_r = "No mail user available"; + return -1; + } + + struct mail_user *user = + event_get_ptr(state->params->event, SETTINGS_EVENT_MAIL_USER); + if (user == NULL) { + *error_r = "No mail user available"; + return -1; + } + struct dlua_script *script; + + if (!mail_lua_plugin_get_script(user, &script)) { + *error_r = "User has no Lua script loaded"; + return -1; + } + + int ret = mail_lua_script_call(fn, user, ¶ms, state, script, error_r); + + /* normalize return value */ + return ret < 0 ? -1 : 0; +} + static const struct mail_storage_hooks mail_lua_hooks = { .mail_user_created = mail_lua_user_created, }; @@ -161,11 +372,16 @@ static const struct mail_storage_hooks mail_lua_hooks = { void mail_lua_plugin_init(struct module *module) { mail_storage_hooks_add(module, &mail_lua_hooks); + var_expand_register_filter("lua_call", mail_lua_var_expand_lua_call); + var_expand_register_filter("lua_file", mail_lua_var_expand_lua_file); } void mail_lua_plugin_deinit(void) { mail_storage_hooks_remove(&mail_lua_hooks); + mail_lua_scripts_free(); + var_expand_unregister_filter("lua_call"); + var_expand_unregister_filter("lua_file"); } const char *mail_lua_plugin_dependencies[] = { NULL };