#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"
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)
{
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,
};
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 };