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