/* Copyright (c) 2018 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "istream.h" #include "array.h" #include "var-expand.h" #include "mail-storage.h" #include "mailbox-attribute.h" #include "mail-storage-lua.h" #include "mail-storage-lua-private.h" #include "mail-user.h" #define LUA_STORAGE_MAILBOX "struct mailbox" static int lua_storage_mailbox_gc(lua_State *L); void dlua_push_mailbox(lua_State *L, struct mailbox *box) { luaL_checkstack(L, 4, "out of memory"); /* create a table for holding few things */ lua_createtable(L, 0, 0); luaL_setmetatable(L, LUA_STORAGE_MAILBOX); struct mailbox **ptr = lua_newuserdata(L, sizeof(struct mailbox*)); *ptr = box; lua_createtable(L, 0, 1); lua_pushcfunction(L, lua_storage_mailbox_gc); lua_setfield(L, -2, "__gc"); lua_setmetatable(L, -2); lua_setfield(L, -2, "item"); luaL_checkstack(L, 2, "out of memory"); lua_pushstring(L, mailbox_get_vname(box)); lua_setfield(L, -2, "vname"); lua_pushstring(L, mailbox_get_name(box)); lua_setfield(L, -2, "name"); } static struct mailbox * lua_check_storage_mailbox(lua_State *L, int arg) { if (!lua_istable(L, arg)) { (void)luaL_error(L, "Bad argument #%d, expected %s got %s", arg, LUA_STORAGE_MAILBOX, lua_typename(L, lua_type(L, arg))); } lua_pushliteral(L, "item"); lua_rawget(L, arg); struct mailbox **bp = lua_touserdata(L, -1); lua_pop(L, 1); return *bp; } static int lua_storage_mailbox_tostring(lua_State *L) { DLUA_REQUIRE_ARGS(L, 1); struct mailbox *mbox = lua_check_storage_mailbox(L, 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) { DLUA_REQUIRE_ARGS(L, 2); struct mailbox *mbox = lua_check_storage_mailbox(L, 1); struct mailbox *mbox2 = lua_check_storage_mailbox(L, 2); lua_pushboolean(L, DLUA_MAILBOX_EQUALS(mbox, mbox2)); return 1; } /* these compare based to mailbox vname */ static int lua_storage_mailbox_lt(lua_State *L) { DLUA_REQUIRE_ARGS(L, 2); bool res = lua_storage_cmp(L) <= 0; lua_pushboolean(L, res); return 1; } static int lua_storage_mailbox_le(lua_State *L) { DLUA_REQUIRE_ARGS(L, 2); bool res = lua_storage_cmp(L) < 0; lua_pushboolean(L, res); return 1; } static int lua_storage_mailbox_unref(lua_State *L) { DLUA_REQUIRE_ARGS(L, 1); /* fetch item from table */ lua_pushliteral(L, "item"); lua_rawget(L, 1); struct mailbox **mbox = lua_touserdata(L, -1); if (*mbox != NULL) mailbox_free(mbox); *mbox = NULL; lua_pop(L, 1); return 0; } static int lua_storage_mailbox_gc(lua_State *L) { struct mailbox **mbox = lua_touserdata(L, 1); if (*mbox != NULL) mailbox_free(mbox); return 0; } static int lua_storage_mailbox_open(lua_State *L) { DLUA_REQUIRE_ARGS(L, 1); struct mailbox *mbox = lua_check_storage_mailbox(L, 1); /* try to open the box */ if (mailbox_open(mbox) < 0) { return luaL_error(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) { DLUA_REQUIRE_ARGS(L, 1); struct mailbox *mbox = lua_check_storage_mailbox(L, 1); mailbox_close(mbox); return 0; } static int lua_storage_mailbox_sync(lua_State *L) { DLUA_REQUIRE_ARGS_IN(L, 1, 2); struct mailbox *mbox = lua_check_storage_mailbox(L, 1); enum mailbox_sync_flags flags = 0; if (lua_gettop(L) >= 2) flags = luaL_checkinteger(L, 2); if (mailbox_sync(mbox, flags) < 0) { const char *error = mailbox_get_last_error(mbox, NULL); return luaL_error(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 *keyword; struct mailbox *mbox = lua_check_storage_mailbox(L, 1); /* get items as list of parameters */ enum mailbox_status_items items = 0; if (lua_gettop(L) < 2) return luaL_error(L, "expecting at least 1 parameter"); for(int i = 2; i <= lua_gettop(L); i++) items |= (unsigned int)luaL_checkinteger(L, i); i_zero(&status); if (mailbox_get_status(mbox, items, &status) < 0) { const char *error = mailbox_get_last_error(mbox, NULL); return luaL_error(L, "mailbox_get_status(%s, %u) failed: %s", mailbox_get_vname(mbox), items, error); } /* returns a table */ lua_createtable(L, 0, 20); lua_pushstring(L, mailbox_get_vname(mbox)); lua_setfield(L, -2, "mailbox"); #undef LUA_TABLE_SET_NUMBER #define LUA_TABLE_SET_NUMBER(field) \ lua_pushnumber(L, status.field); \ lua_setfield(L, -2, #field); #undef LUA_TABLE_SET_BOOL #define LUA_TABLE_SET_BOOL(field) \ lua_pushboolean(L, status.field); \ lua_setfield(L, -2, #field); LUA_TABLE_SET_NUMBER(messages); LUA_TABLE_SET_NUMBER(recent); LUA_TABLE_SET_NUMBER(unseen); LUA_TABLE_SET_NUMBER(uidvalidity); LUA_TABLE_SET_NUMBER(uidnext); LUA_TABLE_SET_NUMBER(first_unseen_seq); LUA_TABLE_SET_NUMBER(first_recent_uid); LUA_TABLE_SET_NUMBER(highest_modseq); LUA_TABLE_SET_NUMBER(highest_pvt_modseq); LUA_TABLE_SET_NUMBER(permanent_flags); LUA_TABLE_SET_NUMBER(flags); LUA_TABLE_SET_BOOL(permanent_keywords); LUA_TABLE_SET_BOOL(allow_new_keywords); LUA_TABLE_SET_BOOL(nonpermanent_modseqs); LUA_TABLE_SET_BOOL(no_modseq_tracking); LUA_TABLE_SET_BOOL(have_guids); LUA_TABLE_SET_BOOL(have_save_guids); LUA_TABLE_SET_BOOL(have_only_guid128); if (status.keywords != NULL && array_is_created(status.keywords)) { int i = 1; lua_createtable(L, array_count(status.keywords), 0); array_foreach_elem(status.keywords, keyword) { lua_pushstring(L, keyword); lua_rawseti(L, -2, i++); } lua_setfield(L, -2, "keywords"); } return 1; } static int lua_storage_mailbox_metadata_get(lua_State *L) { if (lua_gettop(L) < 2) return luaL_error(L, "expecting at least 1 parameter"); struct mailbox *mbox = lua_check_storage_mailbox(L, 1); const char *value, *error; size_t value_len; int ret, i, top = lua_gettop(L); ret = 0; for(i = 2; i <= top; i++) { const char *key = lua_tostring(L, i); if (key == NULL) { ret = -1; error = t_strdup_printf("expected string at #%d", i); break; } if ((ret = lua_storage_mailbox_attribute_get(mbox, key, &value, &value_len, &error)) < 0) { break; } else if (ret == 0) { lua_pushnil(L); } else { lua_pushlstring(L, value, value_len); } } if (ret < 0) return luaL_error(L, "%s", error); /* return number of pushed items */ i_assert(i>=2); return i-2; } static int lua_storage_mailbox_metadata_set(lua_State *L) { DLUA_REQUIRE_ARGS(L, 3); struct mailbox *mbox = lua_check_storage_mailbox(L, 1); const char *key = luaL_checkstring(L, 2); const char *value, *error; size_t value_len; value = lua_tolstring(L, 3, &value_len); if (lua_storage_mailbox_attribute_set(mbox, key, value, value_len, &error) < 0) return luaL_error(L, "Cannot set attribute: %s", error); return 0; } static int lua_storage_mailbox_metadata_unset(lua_State *L) { DLUA_REQUIRE_ARGS(L, 2); struct mailbox *mbox = lua_check_storage_mailbox(L, 1); const char *key = luaL_checkstring(L, 2); const char *error; if (lua_storage_mailbox_attribute_set(mbox, key, NULL, 0, &error) < 0) return luaL_error(L, "Cannot unset attribute: %s", error); return 0; } static int lua_storage_mailbox_metadata_list(lua_State *L) { if (lua_gettop(L) < 2) return luaL_error(L, "expecting at least 1 parameter"); struct mailbox *mbox = lua_check_storage_mailbox(L, 1); const struct lua_storage_keyvalue *item; const char *error; ARRAY_TYPE(lua_storage_keyvalue) items; int i, ret; T_BEGIN { t_array_init(&items, 1); ret = 0; for(i = 2; i <= lua_gettop(L); i++) { const char *key = lua_tostring(L, i); if (key == NULL) { ret = -1; error = t_strdup_printf("expected string at #%d", i); break; } if (lua_storage_mailbox_attribute_list(mbox, key, &items, &error) < 0) { ret = -1; break; } } if (ret == 0) { lua_createtable(L, 0, array_count(&items)); array_foreach(&items, item) { /* push value */ lua_pushlstring(L, item->value, item->value_len); /* set field */ lua_setfield(L, -2, item->key); } } } T_END; if (ret == -1) return luaL_error(L, "%s", error); /* stack should have table with items */ 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 }, { "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 }, { "metadata_get", lua_storage_mailbox_metadata_get }, { "metadata_set", lua_storage_mailbox_metadata_set }, { "metadata_unset", lua_storage_mailbox_metadata_unset }, { "metadata_list", lua_storage_mailbox_metadata_list }, { NULL, NULL } }; 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); }