1 /* Copyright (c) 2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "str.h"
5 #include "istream.h"
6 #include "array.h"
7 #include "var-expand.h"
8 #include "mail-storage.h"
9 #include "mailbox-attribute.h"
10 #include "mail-storage-lua.h"
11 #include "mail-storage-lua-private.h"
12 #include "mail-user.h"
13
14 #define LUA_STORAGE_MAILBOX "struct mailbox"
15
16 static int lua_storage_mailbox_gc(lua_State *L);
17
dlua_push_mailbox(lua_State * L,struct mailbox * box)18 void dlua_push_mailbox(lua_State *L, struct mailbox *box)
19 {
20 luaL_checkstack(L, 4, "out of memory");
21 /* create a table for holding few things */
22 lua_createtable(L, 0, 0);
23 luaL_setmetatable(L, LUA_STORAGE_MAILBOX);
24
25 struct mailbox **ptr = lua_newuserdata(L, sizeof(struct mailbox*));
26 *ptr = box;
27 lua_createtable(L, 0, 1);
28 lua_pushcfunction(L, lua_storage_mailbox_gc);
29 lua_setfield(L, -2, "__gc");
30 lua_setmetatable(L, -2);
31 lua_setfield(L, -2, "item");
32
33 luaL_checkstack(L, 2, "out of memory");
34 lua_pushstring(L, mailbox_get_vname(box));
35 lua_setfield(L, -2, "vname");
36
37 lua_pushstring(L, mailbox_get_name(box));
38 lua_setfield(L, -2, "name");
39 }
40
41 static struct mailbox *
lua_check_storage_mailbox(lua_State * L,int arg)42 lua_check_storage_mailbox(lua_State *L, int arg)
43 {
44 if (!lua_istable(L, arg)) {
45 (void)luaL_error(L, "Bad argument #%d, expected %s got %s",
46 arg, LUA_STORAGE_MAILBOX,
47 lua_typename(L, lua_type(L, arg)));
48 }
49 lua_pushliteral(L, "item");
50 lua_rawget(L, arg);
51 struct mailbox **bp = lua_touserdata(L, -1);
52 lua_pop(L, 1);
53 return *bp;
54 }
55
lua_storage_mailbox_tostring(lua_State * L)56 static int lua_storage_mailbox_tostring(lua_State *L)
57 {
58 DLUA_REQUIRE_ARGS(L, 1);
59 struct mailbox *mbox = lua_check_storage_mailbox(L, 1);
60
61 lua_pushstring(L, mailbox_get_vname(mbox));
62 return 1;
63 }
64
65 /* special case, we want to ensure this is eq when mailboxes
66 are really equal */
lua_storage_mailbox_eq(lua_State * L)67 static int lua_storage_mailbox_eq(lua_State *L)
68 {
69 DLUA_REQUIRE_ARGS(L, 2);
70 struct mailbox *mbox = lua_check_storage_mailbox(L, 1);
71 struct mailbox *mbox2 = lua_check_storage_mailbox(L, 2);
72 lua_pushboolean(L, DLUA_MAILBOX_EQUALS(mbox, mbox2));
73 return 1;
74 }
75
76 /* these compare based to mailbox vname */
lua_storage_mailbox_lt(lua_State * L)77 static int lua_storage_mailbox_lt(lua_State *L)
78 {
79 DLUA_REQUIRE_ARGS(L, 2);
80 bool res = lua_storage_cmp(L) <= 0;
81 lua_pushboolean(L, res);
82 return 1;
83 }
84
lua_storage_mailbox_le(lua_State * L)85 static int lua_storage_mailbox_le(lua_State *L)
86 {
87 DLUA_REQUIRE_ARGS(L, 2);
88 bool res = lua_storage_cmp(L) < 0;
89 lua_pushboolean(L, res);
90 return 1;
91 }
92
lua_storage_mailbox_unref(lua_State * L)93 static int lua_storage_mailbox_unref(lua_State *L)
94 {
95 DLUA_REQUIRE_ARGS(L, 1);
96 /* fetch item from table */
97 lua_pushliteral(L, "item");
98 lua_rawget(L, 1);
99 struct mailbox **mbox = lua_touserdata(L, -1);
100 if (*mbox != NULL)
101 mailbox_free(mbox);
102 *mbox = NULL;
103 lua_pop(L, 1);
104 return 0;
105 }
106
lua_storage_mailbox_gc(lua_State * L)107 static int lua_storage_mailbox_gc(lua_State *L)
108 {
109 struct mailbox **mbox = lua_touserdata(L, 1);
110
111 if (*mbox != NULL)
112 mailbox_free(mbox);
113
114 return 0;
115 }
116
lua_storage_mailbox_open(lua_State * L)117 static int lua_storage_mailbox_open(lua_State *L)
118 {
119 DLUA_REQUIRE_ARGS(L, 1);
120 struct mailbox *mbox = lua_check_storage_mailbox(L, 1);
121
122 /* try to open the box */
123 if (mailbox_open(mbox) < 0) {
124 return luaL_error(L, "mailbox_open(%s) failed: %s",
125 mailbox_get_vname(mbox),
126 mailbox_get_last_error(mbox, NULL));
127 }
128
129 return 0;
130 }
131
lua_storage_mailbox_close(lua_State * L)132 static int lua_storage_mailbox_close(lua_State *L)
133 {
134 DLUA_REQUIRE_ARGS(L, 1);
135 struct mailbox *mbox = lua_check_storage_mailbox(L, 1);
136
137 mailbox_close(mbox);
138
139 return 0;
140 }
141
lua_storage_mailbox_sync(lua_State * L)142 static int lua_storage_mailbox_sync(lua_State *L)
143 {
144 DLUA_REQUIRE_ARGS_IN(L, 1, 2);
145 struct mailbox *mbox = lua_check_storage_mailbox(L, 1);
146 enum mailbox_sync_flags flags = 0;
147
148 if (lua_gettop(L) >= 2)
149 flags = luaL_checkinteger(L, 2);
150
151 if (mailbox_sync(mbox, flags) < 0) {
152 const char *error = mailbox_get_last_error(mbox, NULL);
153 return luaL_error(L, "mailbox_sync(%s) failed: %s",
154 mailbox_get_vname(mbox), error);
155 }
156
157 return 0;
158 }
159
lua_storage_mailbox_status(lua_State * L)160 static int lua_storage_mailbox_status(lua_State *L)
161 {
162 struct mailbox_status status;
163 const char *keyword;
164 struct mailbox *mbox = lua_check_storage_mailbox(L, 1);
165 /* get items as list of parameters */
166 enum mailbox_status_items items = 0;
167
168 if (lua_gettop(L) < 2)
169 return luaL_error(L, "expecting at least 1 parameter");
170 for(int i = 2; i <= lua_gettop(L); i++)
171 items |= (unsigned int)luaL_checkinteger(L, i);
172
173 i_zero(&status);
174 if (mailbox_get_status(mbox, items, &status) < 0) {
175 const char *error = mailbox_get_last_error(mbox, NULL);
176 return luaL_error(L, "mailbox_get_status(%s, %u) failed: %s",
177 mailbox_get_vname(mbox), items, error);
178 }
179 /* returns a table */
180 lua_createtable(L, 0, 20);
181
182 lua_pushstring(L, mailbox_get_vname(mbox));
183 lua_setfield(L, -2, "mailbox");
184
185 #undef LUA_TABLE_SET_NUMBER
186 #define LUA_TABLE_SET_NUMBER(field) \
187 lua_pushnumber(L, status.field); \
188 lua_setfield(L, -2, #field);
189 #undef LUA_TABLE_SET_BOOL
190 #define LUA_TABLE_SET_BOOL(field) \
191 lua_pushboolean(L, status.field); \
192 lua_setfield(L, -2, #field);
193
194 LUA_TABLE_SET_NUMBER(messages);
195 LUA_TABLE_SET_NUMBER(recent);
196 LUA_TABLE_SET_NUMBER(unseen);
197 LUA_TABLE_SET_NUMBER(uidvalidity);
198 LUA_TABLE_SET_NUMBER(uidnext);
199 LUA_TABLE_SET_NUMBER(first_unseen_seq);
200 LUA_TABLE_SET_NUMBER(first_recent_uid);
201 LUA_TABLE_SET_NUMBER(highest_modseq);
202 LUA_TABLE_SET_NUMBER(highest_pvt_modseq);
203
204 LUA_TABLE_SET_NUMBER(permanent_flags);
205 LUA_TABLE_SET_NUMBER(flags);
206
207 LUA_TABLE_SET_BOOL(permanent_keywords);
208 LUA_TABLE_SET_BOOL(allow_new_keywords);
209 LUA_TABLE_SET_BOOL(nonpermanent_modseqs);
210 LUA_TABLE_SET_BOOL(no_modseq_tracking);
211 LUA_TABLE_SET_BOOL(have_guids);
212 LUA_TABLE_SET_BOOL(have_save_guids);
213 LUA_TABLE_SET_BOOL(have_only_guid128);
214
215 if (status.keywords != NULL && array_is_created(status.keywords)) {
216 int i = 1;
217 lua_createtable(L, array_count(status.keywords), 0);
218 array_foreach_elem(status.keywords, keyword) {
219 lua_pushstring(L, keyword);
220 lua_rawseti(L, -2, i++);
221 }
222 lua_setfield(L, -2, "keywords");
223 }
224
225 return 1;
226 }
227
lua_storage_mailbox_metadata_get(lua_State * L)228 static int lua_storage_mailbox_metadata_get(lua_State *L)
229 {
230 if (lua_gettop(L) < 2)
231 return luaL_error(L, "expecting at least 1 parameter");
232 struct mailbox *mbox = lua_check_storage_mailbox(L, 1);
233 const char *value, *error;
234 size_t value_len;
235 int ret, i, top = lua_gettop(L);
236
237 ret = 0;
238 for(i = 2; i <= top; i++) {
239 const char *key = lua_tostring(L, i);
240 if (key == NULL) {
241 ret = -1;
242 error = t_strdup_printf("expected string at #%d", i);
243 break;
244 }
245
246 if ((ret = lua_storage_mailbox_attribute_get(mbox, key, &value,
247 &value_len, &error)) < 0) {
248 break;
249 } else if (ret == 0) {
250 lua_pushnil(L);
251 } else {
252 lua_pushlstring(L, value, value_len);
253 }
254 }
255
256 if (ret < 0)
257 return luaL_error(L, "%s", error);
258
259 /* return number of pushed items */
260 i_assert(i>=2);
261 return i-2;
262 }
263
lua_storage_mailbox_metadata_set(lua_State * L)264 static int lua_storage_mailbox_metadata_set(lua_State *L)
265 {
266 DLUA_REQUIRE_ARGS(L, 3);
267 struct mailbox *mbox = lua_check_storage_mailbox(L, 1);
268 const char *key = luaL_checkstring(L, 2);
269 const char *value, *error;
270 size_t value_len;
271
272 value = lua_tolstring(L, 3, &value_len);
273
274 if (lua_storage_mailbox_attribute_set(mbox, key, value, value_len, &error) < 0)
275 return luaL_error(L, "Cannot set attribute: %s", error);
276
277 return 0;
278 }
279
lua_storage_mailbox_metadata_unset(lua_State * L)280 static int lua_storage_mailbox_metadata_unset(lua_State *L)
281 {
282 DLUA_REQUIRE_ARGS(L, 2);
283 struct mailbox *mbox = lua_check_storage_mailbox(L, 1);
284 const char *key = luaL_checkstring(L, 2);
285 const char *error;
286
287 if (lua_storage_mailbox_attribute_set(mbox, key, NULL, 0, &error) < 0)
288 return luaL_error(L, "Cannot unset attribute: %s", error);
289
290 return 0;
291 }
292
lua_storage_mailbox_metadata_list(lua_State * L)293 static int lua_storage_mailbox_metadata_list(lua_State *L)
294 {
295 if (lua_gettop(L) < 2)
296 return luaL_error(L, "expecting at least 1 parameter");
297 struct mailbox *mbox = lua_check_storage_mailbox(L, 1);
298 const struct lua_storage_keyvalue *item;
299 const char *error;
300 ARRAY_TYPE(lua_storage_keyvalue) items;
301 int i, ret;
302
303 T_BEGIN {
304 t_array_init(&items, 1);
305
306 ret = 0;
307 for(i = 2; i <= lua_gettop(L); i++) {
308 const char *key = lua_tostring(L, i);
309
310 if (key == NULL) {
311 ret = -1;
312 error = t_strdup_printf("expected string at #%d", i);
313 break;
314 }
315
316 if (lua_storage_mailbox_attribute_list(mbox, key, &items,
317 &error) < 0) {
318 ret = -1;
319 break;
320 }
321 }
322
323 if (ret == 0) {
324 lua_createtable(L, 0, array_count(&items));
325 array_foreach(&items, item) {
326 /* push value */
327 lua_pushlstring(L, item->value,
328 item->value_len);
329 /* set field */
330 lua_setfield(L, -2, item->key);
331 }
332 }
333 } T_END;
334
335 if (ret == -1)
336 return luaL_error(L, "%s", error);
337
338 /* stack should have table with items */
339 return 1;
340 }
341
342 static luaL_Reg lua_storage_mailbox_methods[] = {
343 { "__tostring", lua_storage_mailbox_tostring },
344 { "__eq", lua_storage_mailbox_eq },
345 { "__lt", lua_storage_mailbox_lt },
346 { "__le", lua_storage_mailbox_le },
347 { "free", lua_storage_mailbox_unref },
348 { "status", lua_storage_mailbox_status },
349 { "open", lua_storage_mailbox_open },
350 { "close", lua_storage_mailbox_close },
351 { "sync", lua_storage_mailbox_sync },
352 { "metadata_get", lua_storage_mailbox_metadata_get },
353 { "metadata_set", lua_storage_mailbox_metadata_set },
354 { "metadata_unset", lua_storage_mailbox_metadata_unset },
355 { "metadata_list", lua_storage_mailbox_metadata_list },
356 { NULL, NULL }
357 };
358
lua_storage_mailbox_register(struct dlua_script * script)359 void lua_storage_mailbox_register(struct dlua_script *script)
360 {
361 luaL_newmetatable(script->L, LUA_STORAGE_MAILBOX);
362 lua_pushvalue(script->L, -1);
363 lua_setfield(script->L, -2, "__index");
364 luaL_setfuncs(script->L, lua_storage_mailbox_methods, 0);
365 lua_pop(script->L, 1);
366 }
367