]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
mail-lua: Add Lua support plugin for mail storage
authorAki Tuomi <aki.tuomi@dovecot.fi>
Wed, 14 Mar 2018 17:07:53 +0000 (19:07 +0200)
committerVille Savolainen <ville.savolainen@dovecot.fi>
Wed, 14 Nov 2018 12:01:53 +0000 (14:01 +0200)
configure.ac
src/plugins/Makefile.am
src/plugins/mail-lua/Makefile.am [new file with mode: 0644]
src/plugins/mail-lua/mail-lua-plugin.c [new file with mode: 0644]
src/plugins/mail-lua/mail-lua-plugin.h [new file with mode: 0644]
src/plugins/mail-lua/mail-storage-lua.c [new file with mode: 0644]
src/plugins/mail-lua/mail-storage-lua.h [new file with mode: 0644]

index 4dc290f64349d66a92894eb7f40467e9405e858a..27b3cebcdb8201d012775582d4a5a0ea6144ac96 100644 (file)
@@ -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
index 1c82b7ec5881e788003f6e7eae6264c7d1b689c7..b5728cb1a6af3cc64a90c8443bcc65a73875376c 100644 (file)
@@ -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 (file)
index 0000000..576b53a
--- /dev/null
@@ -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 (file)
index 0000000..bdf10fc
--- /dev/null
@@ -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 (file)
index 0000000..e3bfb98
--- /dev/null
@@ -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 (file)
index 0000000..426567d
--- /dev/null
@@ -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 (file)
index 0000000..7b715ac
--- /dev/null
@@ -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