From: Aki Tuomi Date: Wed, 14 Mar 2018 17:07:53 +0000 (+0200) Subject: mail-lua: Add Lua support plugin for mail storage X-Git-Tag: 2.3.4~64 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=92fbe5111c797e8eb60b44e9fb287099a0ab9400;p=thirdparty%2Fdovecot%2Fcore.git mail-lua: Add Lua support plugin for mail storage --- diff --git a/configure.ac b/configure.ac index 4dc290f643..27b3cebcdb 100644 --- a/configure.ac +++ b/configure.ac @@ -918,6 +918,7 @@ src/plugins/lazy-expunge/Makefile src/plugins/listescape/Makefile src/plugins/mail-filter/Makefile src/plugins/mail-log/Makefile +src/plugins/mail-lua/Makefile src/plugins/mailbox-alias/Makefile src/plugins/notify/Makefile src/plugins/notify-status/Makefile diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index 1c82b7ec58..b5728cb1a6 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am @@ -14,6 +14,10 @@ if HAVE_APPARMOR APPARMOR = apparmor endif +if HAVE_LUA +MAIL_LUA = mail-lua +endif + SUBDIRS = \ acl \ imap-acl \ @@ -29,6 +33,7 @@ SUBDIRS = \ push-notification \ mail-filter \ mail-log \ + $(MAIL_LUA) \ mailbox-alias \ quota \ quota-clone \ diff --git a/src/plugins/mail-lua/Makefile.am b/src/plugins/mail-lua/Makefile.am new file mode 100644 index 0000000000..576b53a863 --- /dev/null +++ b/src/plugins/mail-lua/Makefile.am @@ -0,0 +1,34 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-mail \ + -I$(top_srcdir)/src/lib-index \ + -I$(top_srcdir)/src/lib-storage \ + -I$(top_srcdir)/src/lib-lua \ + $(LUA_CFLAGS) + +NOPLUGIN_LDFLAGS = + +module_LTLIBRARIES = \ + lib01_mail_lua_plugin.la + +lib01_mail_lua_plugin_la_LDFLAGS = -module -avoid-version $(LUA_LIBS) +lib01_mail_lua_plugin_la_LIBADD = \ + ../../lib-lua/libdovecot-lua.la \ + $(LIBDOVECOT) +lib01_mail_lua_plugin_la_SOURCES = \ + mail-lua-plugin.c \ + mail-storage-lua.c + +pkginc_libdir=$(pkgincludedir) +pkginc_lib_HEADERS = \ + mail-lua-plugin.h \ + mail-storage-lua.h + +test_programs = + +check-local: + for bin in $(test_programs); do \ + if ! env $(test_options) $(RUN_TEST) ./$$bin; then exit 1; fi; \ + done + +noinst_PROGRAMS = $(test_programs) diff --git a/src/plugins/mail-lua/mail-lua-plugin.c b/src/plugins/mail-lua/mail-lua-plugin.c new file mode 100644 index 0000000000..bdf10fc51d --- /dev/null +++ b/src/plugins/mail-lua/mail-lua-plugin.c @@ -0,0 +1,139 @@ +/* Copyright (c) 2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "module-dir.h" +#include "mail-lua-plugin.h" +#include "mail-storage-lua.h" +#include "mail-storage-private.h" +#include "mail-storage-hooks.h" +#include "dlua-script-private.h" + +#define MAIL_LUA_SCRIPT "mail_lua_script" +#define MAIL_LUA_USER_CREATED_FN "mail_user_created" +#define MAIL_LUA_USER_DEINIT_FN "mail_user_deinit" +#define MAIL_LUA_USER_CONTEXT(obj) \ + MODULE_CONTEXT(obj, mail_lua_user_module) + +static MODULE_CONTEXT_DEFINE_INIT(mail_lua_user_module, + &mail_user_module_register); + +struct mail_lua_user_context { + union mail_user_module_context module_ctx; + struct dlua_script *script; +}; + +static int mail_lua_call_hook(struct dlua_script *script, + struct mail_user *user, + const char *hook, + const char **error_r) +{ + lua_getglobal(script->L, hook); + + /* not found, disable */ + if (!lua_isfunction(script->L, -1)) + return 0; + + if (user->mail_debug) + e_debug(user->event, "mail-lua: Calling %s(user)", hook); + + dlua_push_mail_user(script, user); + + if (lua_pcall(script->L, 1, 2, 0) != 0) { + *error_r = t_strdup_printf("%s(user) failed: %s", + hook, lua_tostring(script->L, -1)); + return -1; + } + + int ret = lua_tonumber(script->L, -2); + const char *errmsg = lua_tostring(script->L, -1); + + if (ret < 0) { + *error_r = t_strdup_printf("%s(user) failed: %s", + hook, errmsg); + } + + (void)lua_gc(script->L, LUA_GCCOLLECT, 0); + + return ret < 0 ? -1 : 1; +} + +static void mail_lua_user_deinit(struct mail_user *user) +{ + struct mail_lua_user_context *luser = MAIL_LUA_USER_CONTEXT(user); + const char *error; + int ret; + + if (luser == NULL) + return; + + if ((ret = mail_lua_call_hook(luser->script, user, MAIL_LUA_USER_DEINIT_FN, + &error)) < 0) { + e_error(user->event, "mail-lua: %s", error); + } + + dlua_script_unref(&luser->script); + luser->module_ctx.super.deinit(user); +} + +static void mail_lua_user_created(struct mail_user *user) +{ + struct mail_lua_user_context *luser; + struct mail_user_vfuncs *v = user->vlast; + struct dlua_script *script; + const char *error; + const char *script_fn = mail_user_plugin_getenv(user, MAIL_LUA_SCRIPT); + int ret; + + if (script_fn == NULL) + return; + + if (dlua_script_create_file(script_fn, &script, &error) < 0) { + user->error = p_strdup_printf(user->pool, "dlua_script_create_file(%s) failed: %s", + script_fn, error); + return; + } + + dlua_dovecot_register(script); + dlua_register_mail_storage(script); + + /* init */ + if (dlua_script_init(script, &error) < 0) { + user->error = p_strdup_printf(user->pool, "dlua_script_init(%s) failed: %s", + script_fn, error); + dlua_script_unref(&script); + return; + } + + /* call postlogin hook */ + if ((ret = mail_lua_call_hook(script, user, MAIL_LUA_USER_CREATED_FN, + &error)) <= 0) { + if (ret < 0) + user->error = p_strdup(user->pool, error); + dlua_script_unref(&script); + return; + } + + luser = p_new(user->pool, struct mail_lua_user_context, 1); + luser->module_ctx.super = *v; + v->deinit = mail_lua_user_deinit; + luser->script = script; + user->vlast = &luser->module_ctx.super; + + MODULE_CONTEXT_SET(user, mail_lua_user_module, luser); +} + +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); +} + +void mail_lua_plugin_deinit(void) +{ + mail_storage_hooks_remove(&mail_lua_hooks); +} + +const char *mail_lua_plugin_dependencies[] = { NULL }; diff --git a/src/plugins/mail-lua/mail-lua-plugin.h b/src/plugins/mail-lua/mail-lua-plugin.h new file mode 100644 index 0000000000..e3bfb9861c --- /dev/null +++ b/src/plugins/mail-lua/mail-lua-plugin.h @@ -0,0 +1,14 @@ +#ifndef MAIL_LUA_PLUGIN_H +#define MAIL_LUA_PLUGIN_H 1 + +struct dlua_script; +struct mail_user; +struct module; + +void mail_lua_plugin_init(struct module *module); +void mail_lua_plugin_deinit(void); + +bool mail_lua_plugin_get_script(struct mail_user *user, + struct dlua_script **script_r); + +#endif diff --git a/src/plugins/mail-lua/mail-storage-lua.c b/src/plugins/mail-lua/mail-storage-lua.c new file mode 100644 index 0000000000..426567d0fb --- /dev/null +++ b/src/plugins/mail-lua/mail-storage-lua.c @@ -0,0 +1,628 @@ +/* Copyright (c) 2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "str.h" +#include "array.h" +#include "var-expand.h" +#include "dlua-script.h" +#include "dlua-script-private.h" +#include "mail-storage.h" +#include "mail-storage-lua.h" +#include "mail-user.h" + +#define LUA_SCRIPT_STORAGE "storage" +#define LUA_STORAGE_MAIL_USER "struct mail_user" +#define LUA_STORAGE_MAILBOX "struct mailbox" +#define LUA_STORAGE_MAIL "struct mail" + +/** MAIL USER + */ + +void dlua_push_mail_user(struct dlua_script *script, struct mail_user *user) +{ + luaL_checkstack(script->L, 20, "out of memory"); + /* create a table for holding few things */ + lua_createtable(script->L, 0, 20); + luaL_setmetatable(script->L, LUA_STORAGE_MAIL_USER); + + lua_pushlightuserdata(script->L, user); + lua_setfield(script->L, -2, "item"); + +#undef LUA_TABLE_SETNUMBER +#define LUA_TABLE_SETNUMBER(field) \ + lua_pushnumber(script->L, user->field); \ + lua_setfield(script->L, -2, #field); +#undef LUA_TABLE_SETBOOL +#define LUA_TABLE_SETBOOL(field) \ + lua_pushboolean(script->L, user->field); \ + lua_setfield(script->L, -2, #field); +#undef LUA_TABLE_SETSTRING +#define LUA_TABLE_SETSTRING(field) \ + lua_pushstring(script->L, user->field); \ + lua_setfield(script->L, -2, #field); + + const char *home = NULL; + (void)mail_user_get_home(user, &home); + + lua_pushstring(script->L, home); + lua_setfield(script->L, -2, "home"); + + LUA_TABLE_SETSTRING(username); + LUA_TABLE_SETNUMBER(uid); + LUA_TABLE_SETNUMBER(gid); + LUA_TABLE_SETSTRING(service); + LUA_TABLE_SETSTRING(session_id); + LUA_TABLE_SETNUMBER(session_create_time); + + LUA_TABLE_SETBOOL(nonexistent); + LUA_TABLE_SETBOOL(anonymous); + LUA_TABLE_SETBOOL(autocreated); + LUA_TABLE_SETBOOL(mail_debug); + LUA_TABLE_SETBOOL(fuzzy_search); + LUA_TABLE_SETBOOL(dsyncing); + LUA_TABLE_SETBOOL(admin); + LUA_TABLE_SETBOOL(session_restored); +} + +static struct mail_user * +lua_check_storage_mail_user(struct dlua_script *script, int arg) +{ + if (!lua_istable(script->L, arg)) { + (void)luaL_error(script->L, "Bad argument #%d, expected %s got %s", + arg, LUA_STORAGE_MAIL_USER, + lua_typename(script->L, lua_type(script->L, arg))); + } + lua_pushliteral(script->L, "item"); + lua_rawget(script->L, arg); + void *bp = (void*)lua_touserdata(script->L, -1); + lua_pop(script->L, 1); + return (struct mail_user*)bp; +} + +static int lua_storage_mail_user_tostring(lua_State *L) +{ + struct dlua_script *script = dlua_script_from_state(L); + struct mail_user *user = lua_check_storage_mail_user(script, 1); + + lua_pushstring(L, user->username); + return 1; +} + +static int lua_storage_cmp(struct dlua_script *script) +{ + const char *name_a, *name_b; + name_a = lua_tostring(script->L, 1); + name_b = lua_tostring(script->L, 2); + + return strcmp(name_a, name_b); +} + +static int lua_storage_mail_user_eq(lua_State *L) +{ + struct dlua_script *script = dlua_script_from_state(L); + bool res = lua_storage_cmp(script) == 0; + lua_pushboolean(script->L, res); + return 1; +} + +static int lua_storage_mail_user_lt(lua_State *L) +{ + struct dlua_script *script = dlua_script_from_state(L); + bool res = lua_storage_cmp(script) <= 0; + lua_pushboolean(script->L, res); + return 1; +} + +static int lua_storage_mail_user_le(lua_State *L) +{ + struct dlua_script *script = dlua_script_from_state(L); + bool res = lua_storage_cmp(script) < 0; + lua_pushboolean(script->L, res); + return 1; +} + +static int lua_storage_mail_user_var_expand(lua_State *L) +{ + struct dlua_script *script = dlua_script_from_state(L); + struct mail_user *user = lua_check_storage_mail_user(script, 1); + const char *error; + const char *format = luaL_checkstring(script->L, 2); + const struct var_expand_table *table = mail_user_var_expand_table(user); + string_t *str = t_str_new(128); + if (var_expand_with_funcs(str, format, table, mail_user_var_expand_func_table, + user, &error) < 0) { + return luaL_error(script->L, "var_expand(%s) failed: %s", + format, error); + } + lua_pushlstring(script->L, str->data, str->used); + return 1; +} + +static int lua_storage_mail_user_plugin_getenv(lua_State *L) +{ + struct dlua_script *script = dlua_script_from_state(L); + struct mail_user *user = lua_check_storage_mail_user(script, 1); + const char *set = lua_tostring(script->L, 2); + const char *val = mail_user_plugin_getenv(user, set); + lua_pushstring(script->L, val); + return 1; +} + +static int lua_storage_mail_user_mailbox_alloc(lua_State *L) +{ + struct dlua_script *script = dlua_script_from_state(L); + struct mail_user *user = lua_check_storage_mail_user(script, 1); + const char *mboxname = luaL_checkstring(script->L, 2); + enum mailbox_flags flags = luaL_checknumber(script->L, 3); + struct mail_namespace *ns = mail_namespace_find(user->namespaces, mboxname); + if (ns == NULL) { + return luaL_error(script->L, "No namespace found for mailbox %s", + mboxname); + } + struct mailbox *mbox = mailbox_alloc(ns->list, mboxname, flags); + dlua_push_mailbox(script, mbox); + return 1; +} + +static int lua_storage_mail_user_unref(lua_State *L) +{ + struct dlua_script *script = dlua_script_from_state(L); + (void)lua_check_storage_mail_user(script, 1); + + /* reset value to NULL */ + lua_pushnil(script->L); + lua_pushliteral(script->L, "item"); + lua_rawset(script->L, 1); + + return 0; +} + +static luaL_Reg lua_storage_mail_user_methods[] = { + { "__tostring", lua_storage_mail_user_tostring }, + { "__eq", lua_storage_mail_user_eq }, + { "__lt", lua_storage_mail_user_lt }, + { "__le", lua_storage_mail_user_le }, + { "__gc", lua_storage_mail_user_unref }, + { "plugin_getenv", lua_storage_mail_user_plugin_getenv }, + { "var_expand", lua_storage_mail_user_var_expand }, + { "mailbox", lua_storage_mail_user_mailbox_alloc }, + { NULL, NULL } +}; + +static void lua_storage_mail_user_register(struct dlua_script *script) +{ + luaL_newmetatable(script->L, LUA_STORAGE_MAIL_USER); + lua_pushvalue(script->L, -1); + lua_setfield(script->L, -2, "__index"); + luaL_setfuncs(script->L, lua_storage_mail_user_methods, 0); + lua_pop(script->L, 1); +} + +/** End of MAIL USER + */ + +/** MAILBOX + */ + +#define DLUA_MAILBOX_EQUALS(a, b) mailbox_equals((a), mailbox_get_namespace(b), \ + mailbox_get_vname(b)) + +void dlua_push_mailbox(struct dlua_script *script, struct mailbox *box) +{ + luaL_checkstack(script->L, 4, "out of memory"); + /* create a table for holding few things */ + lua_createtable(script->L, 0, 0); + luaL_setmetatable(script->L, LUA_STORAGE_MAILBOX); + + lua_pushlightuserdata(script->L, box); + lua_setfield(script->L, -2, "item"); + + luaL_checkstack(script->L, 2, "out of memory"); + lua_pushstring(script->L, mailbox_get_vname(box)); + lua_setfield(script->L, -2, "vname"); + + lua_pushstring(script->L, mailbox_get_name(box)); + lua_setfield(script->L, -2, "name"); +} + +static struct mailbox * +lua_check_storage_mailbox(struct dlua_script *script, int arg) +{ + if (!lua_istable(script->L, arg)) { + (void)luaL_error(script->L, "Bad argument #%d, expected %s got %s", + arg, LUA_STORAGE_MAILBOX, + lua_typename(script->L, lua_type(script->L, arg))); + } + lua_pushliteral(script->L, "item"); + lua_rawget(script->L, arg); + void *bp = (void*)lua_touserdata(script->L, -1); + lua_pop(script->L, 1); + return (struct mailbox*)bp; +} + +static int lua_storage_mailbox_tostring(lua_State *L) +{ + struct dlua_script *script = dlua_script_from_state(L); + struct mailbox *mbox = lua_check_storage_mailbox(script, 1); + + lua_pushstring(L, mailbox_get_vname(mbox)); + return 1; +} + +/* special case, we want to ensure this is eq when mailboxes + are really equal */ +static int lua_storage_mailbox_eq(lua_State *L) +{ + struct dlua_script *script = dlua_script_from_state(L); + struct mailbox *mbox = lua_check_storage_mailbox(script, 1); + struct mailbox *mbox2 = lua_check_storage_mailbox(script, 2); + lua_pushboolean(script->L, DLUA_MAILBOX_EQUALS(mbox, mbox2)); + return 1; +} + +/* these compare based to mailbox vname */ +static int lua_storage_mailbox_lt(lua_State *L) +{ + struct dlua_script *script = dlua_script_from_state(L); + bool res = lua_storage_cmp(script) <= 0; + lua_pushboolean(script->L, res); + return 1; +} + +static int lua_storage_mailbox_le(lua_State *L) +{ + struct dlua_script *script = dlua_script_from_state(L); + bool res = lua_storage_cmp(script) < 0; + lua_pushboolean(script->L, res); + return 1; +} + +static int lua_storage_mailbox_unref(lua_State *L) +{ + struct dlua_script *script = dlua_script_from_state(L); + struct mailbox *mbox = lua_check_storage_mailbox(script, 1); + + if (mbox != NULL) + mailbox_free(&mbox); + + lua_pushlightuserdata(script->L, mbox); + lua_pushliteral(script->L, "item"); + lua_rawset(script->L, 1); + return 0; +} + +static int lua_storage_mailbox_gc(lua_State *L) +{ + struct dlua_script *script = dlua_script_from_state(L); + (void)lua_check_storage_mailbox(script, 1); + + /* reset value to NULL */ + lua_pushnil(script->L); + lua_pushliteral(script->L, "item"); + lua_rawset(script->L, 1); + + return 0; +} + +static int lua_storage_mailbox_open(lua_State *L) +{ + struct dlua_script *script = dlua_script_from_state(L); + struct mailbox *mbox = lua_check_storage_mailbox(script, 1); + + /* try to open the box */ + if (mailbox_open(mbox) < 0) { + return luaL_error(script->L, "mailbox_open(%s) failed: %s", + mailbox_get_vname(mbox), + mailbox_get_last_error(mbox, NULL)); + } + + return 0; +} + +static int lua_storage_mailbox_close(lua_State *L) +{ + struct dlua_script *script = dlua_script_from_state(L); + struct mailbox *mbox = lua_check_storage_mailbox(script, 1); + + mailbox_close(mbox); + + return 0; +} + +static int lua_storage_mailbox_sync(lua_State *L) +{ + struct dlua_script *script = dlua_script_from_state(L); + struct mailbox *mbox = lua_check_storage_mailbox(script, 1); + enum mailbox_sync_flags flags = luaL_checknumber(script->L, 2); + + if (mailbox_sync(mbox, flags) < 0) { + const char *error = mailbox_get_last_error(mbox, NULL); + return luaL_error(script->L, "mailbox_sync(%s) failed: %s", + mailbox_get_vname(mbox), error); + } + + return 0; +} + +static int lua_storage_mailbox_status(lua_State *L) +{ + struct mailbox_status status; + const char *const *keyword; + struct dlua_script *script = dlua_script_from_state(L); + struct mailbox *mbox = lua_check_storage_mailbox(script, 1); + enum mailbox_status_items items = luaL_checknumber(script->L, 2); + + i_zero(&status); + if (mailbox_get_status(mbox, items, &status) < 0) { + const char *error = mailbox_get_last_error(mbox, NULL); + return luaL_error(script->L, "mailbox_get_status(%s, %u) failed: %s", + mbox, items, error); + } + /* returns a table */ + lua_createtable(script->L, 0, 20); + + lua_pushstring(script->L, mailbox_get_vname(mbox)); + lua_setfield(script->L, -2, "mailbox"); + +#undef LUA_TABLE_SETNUMBER +#define LUA_TABLE_SETNUMBER(field) \ + lua_pushnumber(script->L, status.field); \ + lua_setfield(script->L, -2, #field); +#undef LUA_TABLE_SETBOOL +#define LUA_TABLE_SETBOOL(field) \ + lua_pushboolean(script->L, status.field); \ + lua_setfield(script->L, -2, #field); + + LUA_TABLE_SETNUMBER(messages); + LUA_TABLE_SETNUMBER(recent); + LUA_TABLE_SETNUMBER(unseen); + LUA_TABLE_SETNUMBER(uidvalidity); + LUA_TABLE_SETNUMBER(uidnext); + LUA_TABLE_SETNUMBER(first_unseen_seq); + LUA_TABLE_SETNUMBER(first_recent_uid); + LUA_TABLE_SETNUMBER(highest_modseq); + LUA_TABLE_SETNUMBER(highest_pvt_modseq); + + LUA_TABLE_SETNUMBER(permanent_flags); + LUA_TABLE_SETNUMBER(flags); + + LUA_TABLE_SETBOOL(permanent_keywords); + LUA_TABLE_SETBOOL(allow_new_keywords); + LUA_TABLE_SETBOOL(nonpermanent_modseqs); + LUA_TABLE_SETBOOL(no_modseq_tracking); + LUA_TABLE_SETBOOL(have_guids); + LUA_TABLE_SETBOOL(have_save_guids); + LUA_TABLE_SETBOOL(have_only_guid128); + + if (status.keywords != NULL && array_is_created(status.keywords)) { + int i = 1; + lua_createtable(script->L, array_count(status.keywords), 0); + array_foreach(status.keywords, keyword) { + lua_pushstring(script->L, *keyword); + lua_rawseti(script->L, -2, i++); + } + lua_setfield(script->L, -2, "keywords"); + } + + return 1; +} + +static luaL_Reg lua_storage_mailbox_methods[] = { + { "__tostring", lua_storage_mailbox_tostring }, + { "__eq", lua_storage_mailbox_eq }, + { "__lt", lua_storage_mailbox_lt }, + { "__le", lua_storage_mailbox_le }, + { "__gc", lua_storage_mailbox_gc }, + { "free", lua_storage_mailbox_unref }, + { "status", lua_storage_mailbox_status }, + { "open", lua_storage_mailbox_open }, + { "close", lua_storage_mailbox_close }, + { "sync", lua_storage_mailbox_sync }, + { NULL, NULL } +}; + +static void lua_storage_mailbox_register(struct dlua_script *script) +{ + luaL_newmetatable(script->L, LUA_STORAGE_MAILBOX); + lua_pushvalue(script->L, -1); + lua_setfield(script->L, -2, "__index"); + luaL_setfuncs(script->L, lua_storage_mailbox_methods, 0); + lua_pop(script->L, 1); +} + +/* MAIL */ + +void dlua_push_mail(struct dlua_script *script, struct mail *mail) +{ + luaL_checkstack(script->L, 20, "out of memory"); + /* create a table for holding few things */ + lua_createtable(script->L, 0, 20); + luaL_setmetatable(script->L, LUA_STORAGE_MAIL); + + lua_pushlightuserdata(script->L, mail); + lua_setfield(script->L, -2, "item"); + +#undef LUA_TABLE_SETNUMBER +#define LUA_TABLE_SETNUMBER(field) \ + lua_pushnumber(script->L, mail->field); \ + lua_setfield(script->L, -2, #field); +#undef LUA_TABLE_SETBOOL +#define LUA_TABLE_SETBOOL(field) \ + lua_pushboolean(script->L, mail->field); \ + lua_setfield(script->L, -2, #field); + + LUA_TABLE_SETNUMBER(seq); + LUA_TABLE_SETNUMBER(uid); + LUA_TABLE_SETBOOL(expunged); + + dlua_push_mailbox(script, mail->box); + lua_setfield(script->L, -2, "mailbox"); + +} + +static struct mail * +lua_check_storage_mail(struct dlua_script *script, int arg) +{ + if (!lua_istable(script->L, arg)) { + (void)luaL_error(script->L, "Bad argument #%d, expected %s got %s", + arg, LUA_STORAGE_MAIL, + lua_typename(script->L, lua_type(script->L, arg))); + } + lua_pushliteral(script->L, "item"); + lua_rawget(script->L, arg); + void *bp = (void*)lua_touserdata(script->L, -1); + lua_pop(script->L, 1); + return (struct mail*)bp; +} + +static int lua_storage_mail_tostring(lua_State *L) +{ + struct dlua_script *script = dlua_script_from_state(L); + struct mail *mail = lua_check_storage_mail(script, 1); + + const char *str = + t_strdup_printf("<%s:UID %u>", mailbox_get_vname(mail->box), + mail->uid); + lua_pushstring(script->L, str); + return 1; +} + +static int lua_storage_mail_eq(lua_State *L) +{ + struct dlua_script *script = dlua_script_from_state(L); + struct mail *mail = lua_check_storage_mail(script, 1); + struct mail *mail2 = lua_check_storage_mail(script, 2); + + if (!DLUA_MAILBOX_EQUALS(mail->box, mail2->box)) + lua_pushboolean(script->L, FALSE); + else + lua_pushboolean(script->L, mail->uid != mail2->uid); + return 1; +} + +static int lua_storage_mail_lt(lua_State *L) +{ + struct dlua_script *script = dlua_script_from_state(L); + struct mail *mail = lua_check_storage_mail(script, 1); + struct mail *mail2 = lua_check_storage_mail(script, 2); + + if (!DLUA_MAILBOX_EQUALS(mail->box, mail2->box)) + return luaL_error(script->L, + "For lt, Mail can only be compared within same mailbox"); + else + lua_pushboolean(script->L, mail->uid < mail2->uid); + return 1; +} + +static int lua_storage_mail_le(lua_State *L) +{ + struct dlua_script *script = dlua_script_from_state(L); + struct mail *mail = lua_check_storage_mail(script, 1); + struct mail *mail2 = lua_check_storage_mail(script, 2); + + if (!DLUA_MAILBOX_EQUALS(mail->box, mail2->box)) + return luaL_error(script->L, + "For le, mails can only be within same mailbox"); + else + lua_pushboolean(script->L, mail->uid <= mail2->uid); + + return 1; +} + +static int lua_storage_mail_gc(lua_State *L) +{ + struct dlua_script *script = dlua_script_from_state(L); + (void)lua_check_storage_mail(script, 1); + + /* reset value to NULL */ + lua_pushnil(script->L); + lua_pushliteral(script->L, "item"); + lua_rawset(script->L, 1); + + return 0; +} + +static luaL_Reg lua_storage_mail_methods[] = { + { "__tostring", lua_storage_mail_tostring }, + { "__eq", lua_storage_mail_eq }, + { "__lt", lua_storage_mail_lt }, + { "__le", lua_storage_mail_le }, + { "__gc", lua_storage_mail_gc }, + { NULL, NULL } +}; + +static void lua_storage_mail_register(struct dlua_script *script) +{ + luaL_newmetatable(script->L, LUA_STORAGE_MAIL); + lua_pushvalue(script->L, -1); + lua_setfield(script->L, -2, "__index"); + luaL_setfuncs(script->L, lua_storage_mail_methods, 0); + lua_pop(script->L, 1); +} + +/* end of MAIL */ + +static struct dlua_table_values lua_storage_mail_storage_flags[] = { + DLUA_TABLE_ENUM(STATUS_MESSAGES), + DLUA_TABLE_ENUM(STATUS_RECENT), + DLUA_TABLE_ENUM(STATUS_UIDNEXT), + DLUA_TABLE_ENUM(STATUS_UIDVALIDITY), + DLUA_TABLE_ENUM(STATUS_UNSEEN), + DLUA_TABLE_ENUM(STATUS_FIRST_UNSEEN_SEQ), + DLUA_TABLE_ENUM(STATUS_KEYWORDS), + DLUA_TABLE_ENUM(STATUS_HIGHESTMODSEQ), + DLUA_TABLE_ENUM(STATUS_PERMANENT_FLAGS), + DLUA_TABLE_ENUM(STATUS_FIRST_RECENT_UID), + DLUA_TABLE_ENUM(STATUS_HIGHESTPVTMODSEQ), + + DLUA_TABLE_ENUM(MAILBOX_FLAG_READONLY), + DLUA_TABLE_ENUM(MAILBOX_FLAG_SAVEONLY), + DLUA_TABLE_ENUM(MAILBOX_FLAG_DROP_RECENT), + DLUA_TABLE_ENUM(MAILBOX_FLAG_NO_INDEX_FILES), + DLUA_TABLE_ENUM(MAILBOX_FLAG_KEEP_LOCKED), + DLUA_TABLE_ENUM(MAILBOX_FLAG_IGNORE_ACLS), + DLUA_TABLE_ENUM(MAILBOX_FLAG_AUTO_CREATE), + DLUA_TABLE_ENUM(MAILBOX_FLAG_AUTO_SUBSCRIBE), + + DLUA_TABLE_ENUM(MAILBOX_SYNC_FLAG_FULL_READ), + DLUA_TABLE_ENUM(MAILBOX_SYNC_FLAG_FULL_WRITE), + DLUA_TABLE_ENUM(MAILBOX_SYNC_FLAG_FAST), + DLUA_TABLE_ENUM(MAILBOX_SYNC_FLAG_NO_EXPUNGES), + DLUA_TABLE_ENUM(MAILBOX_SYNC_FLAG_FIX_INCONSISTENT), + DLUA_TABLE_ENUM(MAILBOX_SYNC_FLAG_EXPUNGE), + DLUA_TABLE_ENUM(MAILBOX_SYNC_FLAG_FORCE_RESYNC), + + DLUA_TABLE_END +}; + +static luaL_Reg lua_storage_methods[] = { + { NULL, NULL } +}; + +void dlua_register_mail_storage(struct dlua_script *script) +{ + /* get dlua_dovecot */ + dlua_getdovecot(script); + + /* Create table for holding values */ + lua_newtable(script->L); + + dlua_setmembers(script, lua_storage_mail_storage_flags, -1); + + /* push new metatable to stack */ + luaL_newmetatable(script->L, LUA_SCRIPT_STORAGE); + /* this will register functions to the metatable itself */ + luaL_setfuncs(script->L, lua_storage_methods, 0); + /* point __index to self */ + lua_pushvalue(script->L, -1); + lua_setfield(script->L, -1, "__index"); + /* set table's metatable, pops stack */ + lua_setmetatable(script->L, -2); + + /* register table as member of dovecot */ + lua_setfield(script->L, -2, LUA_SCRIPT_STORAGE); + + lua_storage_mail_user_register(script); + lua_storage_mailbox_register(script); + lua_storage_mail_register(script); +} diff --git a/src/plugins/mail-lua/mail-storage-lua.h b/src/plugins/mail-lua/mail-storage-lua.h new file mode 100644 index 0000000000..7b715ac09c --- /dev/null +++ b/src/plugins/mail-lua/mail-storage-lua.h @@ -0,0 +1,14 @@ +#ifndef MAIL_STORAGE_LUA_H +#define MAIL_STORAGE_LUA_H 1 + +struct mail_user; +struct mailbox; +struct mail; +struct dlua_script; + +void dlua_register_mail_storage(struct dlua_script *script); +void dlua_push_mail_user(struct dlua_script *script, struct mail_user *user); +void dlua_push_mailbox(struct dlua_script *script, struct mailbox *box); +void dlua_push_mail(struct dlua_script *script, struct mail *mail); + +#endif