1 /* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "array.h"
5 #include "path-util.h"
6 #include "ioloop.h"
7 #include "file-create-locked.h"
8 #include "mkdir-parents.h"
9 #include "hex-binary.h"
10 #include "str.h"
11 #include "sha1.h"
12 #include "hash.h"
13 #include "home-expand.h"
14 #include "time-util.h"
15 #include "unichar.h"
16 #include "settings-parser.h"
17 #include "iostream-ssl.h"
18 #include "fs-api-private.h"
19 #include "imap-utf7.h"
20 #include "mailbox-log.h"
21 #include "mailbox-tree.h"
22 #include "mail-storage-private.h"
23 #include "mail-storage-hooks.h"
24 #include "mailbox-list-private.h"
25 
26 #include <time.h>
27 #include <ctype.h>
28 #include <unistd.h>
29 #include <dirent.h>
30 #include <sys/stat.h>
31 
32 #define MAILBOX_LIST_LOCK_FNAME "mailboxes.lock"
33 #define MAILBOX_LIST_LOCK_SECS 60
34 
35 #define MAILBOX_LIST_FS_CONTEXT(obj) \
36 	MODULE_CONTEXT(obj, mailbox_list_fs_module)
37 
38 struct mailbox_list_fs_context {
39 	union fs_api_module_context module_ctx;
40 	struct mailbox_list *list;
41 };
42 
43 struct mailbox_list_module_register mailbox_list_module_register = { 0 };
44 
45 static ARRAY(const struct mailbox_list *) mailbox_list_drivers;
46 static MODULE_CONTEXT_DEFINE_INIT(mailbox_list_fs_module,
47 				  &fs_api_module_register);
48 
mailbox_lists_init(void)49 void mailbox_lists_init(void)
50 {
51 	i_array_init(&mailbox_list_drivers, 4);
52 }
53 
mailbox_lists_deinit(void)54 void mailbox_lists_deinit(void)
55 {
56 	array_free(&mailbox_list_drivers);
57 }
58 
mailbox_list_driver_find(const char * name,unsigned int * idx_r)59 static bool mailbox_list_driver_find(const char *name, unsigned int *idx_r)
60 {
61 	const struct mailbox_list *const *drivers;
62 	unsigned int i, count;
63 
64 	drivers = array_get(&mailbox_list_drivers, &count);
65 	for (i = 0; i < count; i++) {
66 		if (strcasecmp(drivers[i]->name, name) == 0) {
67 			*idx_r = i;
68 			return TRUE;
69 		}
70 	}
71 	return FALSE;
72 }
73 
mailbox_list_register(const struct mailbox_list * list)74 void mailbox_list_register(const struct mailbox_list *list)
75 {
76 	unsigned int idx;
77 
78 	if (mailbox_list_driver_find(list->name, &idx)) {
79 		i_fatal("mailbox_list_register(%s): duplicate driver",
80 			list->name);
81 	}
82 
83 	array_push_back(&mailbox_list_drivers, &list);
84 }
85 
mailbox_list_unregister(const struct mailbox_list * list)86 void mailbox_list_unregister(const struct mailbox_list *list)
87 {
88 	unsigned int idx;
89 
90 	if (!mailbox_list_driver_find(list->name, &idx)) {
91 		i_fatal("mailbox_list_unregister(%s): unknown driver",
92 			list->name);
93 	}
94 	array_delete(&mailbox_list_drivers, idx, 1);
95 }
96 
97 const struct mailbox_list *
mailbox_list_find_class(const char * driver)98 mailbox_list_find_class(const char *driver)
99 {
100 	unsigned int idx;
101 
102 	if (!mailbox_list_driver_find(driver, &idx))
103 		return NULL;
104 
105 	return array_idx_elem(&mailbox_list_drivers, idx);
106 }
107 
mailbox_list_create(const char * driver,struct mail_namespace * ns,const struct mailbox_list_settings * set,enum mailbox_list_flags flags,struct mailbox_list ** list_r,const char ** error_r)108 int mailbox_list_create(const char *driver, struct mail_namespace *ns,
109 			const struct mailbox_list_settings *set,
110 			enum mailbox_list_flags flags,
111 			struct mailbox_list **list_r, const char **error_r)
112 {
113 	const struct mailbox_list *class;
114 	struct mailbox_list *list;
115 
116 	i_assert(ns->list == NULL ||
117 		 (flags & MAILBOX_LIST_FLAG_SECONDARY) != 0);
118 
119 	i_assert(set->subscription_fname == NULL ||
120 		 *set->subscription_fname != '\0');
121 
122 	if ((class = mailbox_list_find_class(driver)) == NULL) {
123 		*error_r = "Unknown driver name";
124 		return -1;
125 	}
126 
127 	if ((class->props & MAILBOX_LIST_PROP_NO_MAILDIR_NAME) != 0 &&
128 	    *set->maildir_name != '\0') {
129 		*error_r = "maildir_name not supported by this driver";
130 		return -1;
131 	}
132 	if ((class->props & MAILBOX_LIST_PROP_NO_ALT_DIR) != 0 &&
133 	    set->alt_dir != NULL) {
134 		*error_r = "alt_dir not supported by this driver";
135 		return -1;
136 	}
137 
138 	i_assert(set->root_dir == NULL || *set->root_dir != '\0' ||
139 		 (class->props & MAILBOX_LIST_PROP_NO_ROOT) != 0);
140 
141 	list = class->v.alloc();
142 	array_create(&list->module_contexts, list->pool, sizeof(void *), 5);
143 
144 	list->ns = ns;
145 	list->mail_set = ns->mail_set;
146 	list->flags = flags;
147 	list->root_permissions.file_create_mode = (mode_t)-1;
148 	list->root_permissions.dir_create_mode = (mode_t)-1;
149 	list->root_permissions.file_create_gid = (gid_t)-1;
150 	list->changelog_timestamp = (time_t)-1;
151 	if (set->no_noselect)
152 		list->props |= MAILBOX_LIST_PROP_NO_NOSELECT;
153 
154 	/* copy settings */
155 	if (set->root_dir != NULL) {
156 		list->set.root_dir = p_strdup(list->pool, set->root_dir);
157 		list->set.index_dir = set->index_dir == NULL ||
158 			strcmp(set->index_dir, set->root_dir) == 0 ? NULL :
159 			p_strdup(list->pool, set->index_dir);
160 		list->set.index_pvt_dir = set->index_pvt_dir == NULL ||
161 			strcmp(set->index_pvt_dir, set->root_dir) == 0 ? NULL :
162 			p_strdup(list->pool, set->index_pvt_dir);
163 		list->set.index_cache_dir = set->index_cache_dir == NULL ||
164 			strcmp(set->index_cache_dir, set->root_dir) == 0 ? NULL :
165 			p_strdup(list->pool, set->index_cache_dir);
166 		list->set.control_dir = set->control_dir == NULL ||
167 			strcmp(set->control_dir, set->root_dir) == 0 ? NULL :
168 			p_strdup(list->pool, set->control_dir);
169 	}
170 
171 	list->set.inbox_path = p_strdup(list->pool, set->inbox_path);
172 	list->set.subscription_fname =
173 		p_strdup(list->pool, set->subscription_fname);
174 	list->set.list_index_fname =
175 		p_strdup(list->pool, set->list_index_fname);
176 	list->set.list_index_dir =
177 		p_strdup(list->pool, set->list_index_dir);
178 	list->set.maildir_name =
179 		p_strdup(list->pool, set->maildir_name);
180 	list->set.mailbox_dir_name =
181 		p_strdup(list->pool, set->mailbox_dir_name);
182 	list->set.alt_dir = p_strdup(list->pool, set->alt_dir);
183 	list->set.alt_dir_nocheck = set->alt_dir_nocheck;
184 	list->set.volatile_dir = p_strdup(list->pool, set->volatile_dir);
185 	list->set.index_control_use_maildir_name =
186 		set->index_control_use_maildir_name;
187 	list->set.iter_from_index_dir = set->iter_from_index_dir;
188 	list->set.no_noselect = set->no_noselect;
189 	list->set.no_fs_validation = set->no_fs_validation;
190 
191 	if (*set->mailbox_dir_name == '\0')
192 		list->set.mailbox_dir_name = "";
193 	else if (set->mailbox_dir_name[strlen(set->mailbox_dir_name)-1] == '/') {
194 		list->set.mailbox_dir_name =
195 			p_strdup(list->pool, set->mailbox_dir_name);
196 	} else {
197 		list->set.mailbox_dir_name =
198 			p_strconcat(list->pool, set->mailbox_dir_name, "/", NULL);
199 	}
200 	if (set->storage_name_escape_char != '\0')
201 		list->set.storage_name_escape_char = set->storage_name_escape_char;
202 	list->set.vname_escape_char = set->vname_escape_char;
203 	list->set.utf8 = set->utf8;
204 
205 	if (list->v.init != NULL) {
206 		if (list->v.init(list, error_r) < 0) {
207 			list->v.deinit(list);
208 			return -1;
209 		}
210 	}
211 
212 	e_debug(ns->user->event,
213 		"%s: root=%s, index=%s, indexpvt=%s, control=%s, inbox=%s, alt=%s",
214 		list->name,
215 		list->set.root_dir == NULL ? "" : list->set.root_dir,
216 		list->set.index_dir == NULL ? "" : list->set.index_dir,
217 		list->set.index_pvt_dir == NULL ? "" : list->set.index_pvt_dir,
218 		list->set.control_dir == NULL ?
219 		"" : list->set.control_dir,
220 		list->set.inbox_path == NULL ?
221 		"" : list->set.inbox_path,
222 		list->set.alt_dir == NULL ? "" : list->set.alt_dir);
223 	if ((flags & MAILBOX_LIST_FLAG_SECONDARY) == 0)
224 		mail_namespace_finish_list_init(ns, list);
225 
226 	*list_r = list;
227 
228 	hook_mailbox_list_created(list);
229 	return 0;
230 }
231 
fix_path(struct mail_user * user,const char * path,bool expand_home,const char ** path_r,const char ** error_r)232 static int fix_path(struct mail_user *user, const char *path, bool expand_home,
233 		    const char **path_r, const char **error_r)
234 {
235 	size_t len = strlen(path);
236 
237 	if (len > 1 && path[len-1] == '/')
238 		path = t_strndup(path, len-1);
239 	if (!expand_home) {
240 		/* no ~ expansion */
241 	} else if (path[0] == '~' && path[1] != '/' && path[1] != '\0') {
242 		/* ~otheruser/dir */
243 		if (home_try_expand(&path) < 0) {
244 			*error_r = t_strconcat(
245 				"No home directory for system user. "
246 				"Can't expand ", t_strcut(path, '/'),
247 				" for ", NULL);
248 			return -1;
249 		}
250 	} else {
251 		if (mail_user_try_home_expand(user, &path) < 0) {
252 			*error_r = "Home directory not set for user. "
253 				"Can't expand ~/ for ";
254 			return -1;
255 		}
256 	}
257 	*path_r = path;
258 	return 0;
259 }
260 
split_next_arg(const char * const ** _args)261 static const char *split_next_arg(const char *const **_args)
262 {
263 	const char *const *args = *_args;
264 	const char *str = args[0];
265 
266 	args++;
267 	while (*args != NULL && **args == '\0') {
268 		args++;
269 		if (*args == NULL) {
270 			/* string ends with ":", just ignore it. */
271 			break;
272 		}
273 		str = t_strconcat(str, ":", *args, NULL);
274 		args++;
275 	}
276 	*_args = args;
277 	return str;
278 }
279 
mailbox_list_settings_init_defaults(struct mailbox_list_settings * set_r)280 void mailbox_list_settings_init_defaults(struct mailbox_list_settings *set_r)
281 {
282 	i_zero(set_r);
283 	set_r->mailbox_dir_name = "";
284 	set_r->maildir_name = "";
285 	set_r->list_index_fname = MAILBOX_LIST_INDEX_DEFAULT_PREFIX;
286 }
287 
288 static int
mailbox_list_settings_parse_full(struct mail_user * user,const char * data,bool expand_home,struct mailbox_list_settings * set_r,const char ** error_r)289 mailbox_list_settings_parse_full(struct mail_user *user, const char *data,
290 				 bool expand_home,
291 				 struct mailbox_list_settings *set_r,
292 				 const char **error_r)
293 {
294 	const char *const *tmp, *key, *value, **dest, *str, *fname, *error;
295 
296 	*error_r = NULL;
297 
298 	mailbox_list_settings_init_defaults(set_r);
299 	if (*data == '\0')
300 		return 0;
301 
302 	/* <root dir> */
303 	tmp = t_strsplit(data, ":");
304 	str = split_next_arg(&tmp);
305 	if (fix_path(user, str, expand_home, &set_r->root_dir, &error) < 0) {
306 		*error_r = t_strconcat(error, "mail root dir in: ", data, NULL);
307 		return -1;
308 	}
309 	if (str_begins(set_r->root_dir, "INBOX=")) {
310 		/* probably mbox user trying to avoid root_dir */
311 		*error_r = t_strconcat("Mail root directory not given: ",
312 				       data, NULL);
313 		return -1;
314 	}
315 
316 	while (*tmp != NULL) {
317 		str = split_next_arg(&tmp);
318 		if (strcmp(str, "UTF-8") == 0) {
319 			set_r->utf8 = TRUE;
320 			continue;
321 		}
322 
323 		value = strchr(str, '=');
324 		if (value == NULL) {
325 			key = str;
326 			value = "";
327 		} else {
328 			key = t_strdup_until(str, value);
329 			value++;
330 		}
331 
332 		if (strcmp(key, "INBOX") == 0)
333 			dest = &set_r->inbox_path;
334 		else if (strcmp(key, "INDEX") == 0)
335 			dest = &set_r->index_dir;
336 		else if (strcmp(key, "INDEXPVT") == 0)
337 			dest = &set_r->index_pvt_dir;
338 		else if (strcmp(key, "INDEXCACHE") == 0)
339 			dest = &set_r->index_cache_dir;
340 		else if (strcmp(key, "CONTROL") == 0)
341 			dest = &set_r->control_dir;
342 		else if (strcmp(key, "ALT") == 0)
343 			dest = &set_r->alt_dir;
344 		else if (strcmp(key, "ALTNOCHECK") == 0) {
345 			set_r->alt_dir_nocheck = TRUE;
346 			continue;
347 		} else if (strcmp(key, "LAYOUT") == 0)
348 			dest = &set_r->layout;
349 		else if (strcmp(key, "SUBSCRIPTIONS") == 0)
350 			dest = &set_r->subscription_fname;
351 		else if (strcmp(key, "DIRNAME") == 0)
352 			dest = &set_r->maildir_name;
353 		else if (strcmp(key, "MAILBOXDIR") == 0)
354 			dest = &set_r->mailbox_dir_name;
355 		else if (strcmp(key, "VOLATILEDIR") == 0)
356 			dest = &set_r->volatile_dir;
357 		else if (strcmp(key, "LISTINDEX") == 0)
358 			dest = &set_r->list_index_fname;
359 		else if (strcmp(key, "FULLDIRNAME") == 0) {
360 			set_r->index_control_use_maildir_name = TRUE;
361 			dest = &set_r->maildir_name;
362 		} else if (strcmp(key, "BROKENCHAR") == 0) {
363 			if (strlen(value) != 1) {
364 				*error_r = "BROKENCHAR value must be a single character";
365 				return -1;
366 			}
367 			set_r->vname_escape_char = value[0];
368 			continue;
369 		} else if (strcmp(key, "ITERINDEX") == 0) {
370 			set_r->iter_from_index_dir = TRUE;
371 			continue;
372 		} else if (strcmp(key, "NO-NOSELECT") == 0) {
373 			set_r->no_noselect = TRUE;
374 			continue;
375 		} else if (strcmp(key, "NO-FS-VALIDATION") == 0) {
376 			set_r->no_fs_validation = TRUE;
377 			continue;
378 		} else {
379 			*error_r = t_strdup_printf("Unknown setting: %s", key);
380 			return -1;
381 		}
382 		if (fix_path(user, value, expand_home, dest, &error) < 0) {
383 			*error_r = t_strconcat(error, key, " in: ", data, NULL);
384 			return -1;
385 		}
386 	}
387 
388 	if (set_r->index_dir != NULL && strcmp(set_r->index_dir, "MEMORY") == 0)
389 		set_r->index_dir = "";
390 	if (set_r->iter_from_index_dir &&
391 	    (set_r->index_dir == NULL || set_r->index_dir[0] == '\0')) {
392 		*error_r = "ITERINDEX requires INDEX to be explicitly set";
393 		return -1;
394 	}
395 	if (set_r->list_index_fname != NULL &&
396 	    (fname = strrchr(set_r->list_index_fname, '/')) != NULL) {
397 		/* non-default LISTINDEX directory */
398 		set_r->list_index_dir =
399 			t_strdup_until(set_r->list_index_fname, fname);
400 		set_r->list_index_fname = fname+1;
401 		if (set_r->list_index_dir[0] != '/' &&
402 		    set_r->index_dir != NULL && set_r->index_dir[0] == '\0') {
403 			*error_r = "LISTINDEX directory is relative but INDEX=MEMORY";
404 			return -1;
405 		}
406 	}
407 	return 0;
408 }
409 
mailbox_list_settings_parse(struct mail_user * user,const char * data,struct mailbox_list_settings * set_r,const char ** error_r)410 int mailbox_list_settings_parse(struct mail_user *user, const char *data,
411 				struct mailbox_list_settings *set_r,
412 				const char **error_r)
413 {
414 	return mailbox_list_settings_parse_full(user, data, TRUE,
415 						set_r, error_r);
416 }
417 
mailbox_list_get_unexpanded_path(struct mailbox_list * list,enum mailbox_list_path_type type)418 const char *mailbox_list_get_unexpanded_path(struct mailbox_list *list,
419 					     enum mailbox_list_path_type type)
420 {
421 	const struct mail_storage_settings *mail_set;
422 	const char *location = list->ns->unexpanded_set->location;
423 	struct mail_user *user = list->ns->user;
424 	struct mailbox_list_settings set;
425 	const char *p, *path, *error;
426 
427 	if (*location == SETTING_STRVAR_EXPANDED[0]) {
428 		/* set using -o or userdb lookup. */
429 		return "";
430 	}
431 
432 	i_assert(*location == SETTING_STRVAR_UNEXPANDED[0]);
433 	location++;
434 
435 	if (*location == '\0') {
436 		mail_set = mail_user_set_get_driver_settings(user->set_info,
437 			user->unexpanded_set, MAIL_STORAGE_SET_DRIVER_NAME);
438 		i_assert(mail_set != NULL);
439 		location = mail_set->mail_location;
440 		if (*location == SETTING_STRVAR_EXPANDED[0])
441 			return "";
442 		i_assert(*location == SETTING_STRVAR_UNEXPANDED[0]);
443 		location++;
444 	}
445 
446 	/* type:settings */
447 	p = strchr(location, ':');
448 	if (p == NULL)
449 		return "";
450 
451 	if (mailbox_list_settings_parse_full(user, p + 1, FALSE,
452 					     &set, &error) < 0)
453 		return "";
454 	if (!mailbox_list_set_get_root_path(&set, type, &path))
455 		return "";
456 	return path;
457 }
458 
need_escape_dirstart(const char * vname,const char * maildir_name)459 static bool need_escape_dirstart(const char *vname, const char *maildir_name)
460 {
461 	size_t len;
462 
463 	if (vname[0] == '.') {
464 		if (vname[1] == '\0' || vname[1] == '/')
465 			return TRUE; /* "." */
466 		if (vname[1] == '.' && (vname[2] == '\0' || vname[2] == '/'))
467 			return TRUE; /* ".." */
468 	}
469 	if (*maildir_name != '\0') {
470 		len = strlen(maildir_name);
471 		if (str_begins(vname, maildir_name) &&
472 		    (vname[len] == '\0' || vname[len] == '/'))
473 			return TRUE; /* e.g. dbox-Mails */
474 	}
475 	return FALSE;
476 }
477 
478 const char *
mailbox_list_escape_name_params(const char * vname,const char * ns_prefix,char ns_sep,char list_sep,char escape_char,const char * maildir_name)479 mailbox_list_escape_name_params(const char *vname, const char *ns_prefix,
480 				char ns_sep, char list_sep, char escape_char,
481 				const char *maildir_name)
482 {
483 	size_t ns_prefix_len = strlen(ns_prefix);
484 	string_t *escaped_name = t_str_new(64);
485 	bool dirstart = TRUE;
486 
487 	i_assert(escape_char != '\0');
488 
489 	/* no escaping of namespace prefix */
490 	if (str_begins(vname, ns_prefix)) {
491 		str_append_data(escaped_name, vname, ns_prefix_len);
492 		vname += ns_prefix_len;
493 	}
494 
495 	/* escape the mailbox name */
496 	if (*vname == '~') {
497 		str_printfa(escaped_name, "%c%02x", escape_char, *vname);
498 		vname++;
499 		dirstart = FALSE;
500 	}
501 	for (; *vname != '\0'; vname++) {
502 		if (*vname == ns_sep)
503 			str_append_c(escaped_name, list_sep);
504 		else if (*vname == list_sep ||
505 			 *vname == escape_char ||
506 			 *vname == '/' ||
507 			 (dirstart &&
508 			  need_escape_dirstart(vname, maildir_name))) {
509 			str_printfa(escaped_name, "%c%02x",
510 				    escape_char, *vname);
511 		} else {
512 			str_append_c(escaped_name, *vname);
513 		}
514 		dirstart = *vname == '/';
515 	}
516 	return str_c(escaped_name);
517 }
518 
mailbox_list_name_unescape(const char ** _name,char escape_char)519 void mailbox_list_name_unescape(const char **_name, char escape_char)
520 {
521 	const char *p, *name = *_name;
522 	unsigned char chr;
523 
524 	if ((p = strchr(name, escape_char)) == NULL)
525 		return;
526 
527 	string_t *str = t_str_new(strlen(name)*2);
528 	str_append_data(str, name, p - name);
529 	while (*p != '\0') {
530 		if (*p == escape_char &&
531 		    imap_escaped_utf8_hex_to_char(p+1, &chr) == 0) {
532 			str_append_c(str, chr);
533 			p += 3;
534 		} else {
535 			str_append_c(str, *p++);
536 		}
537 	}
538 	*_name = str_c(str);
539 }
540 
541 static bool
mailbox_list_vname_prepare(struct mailbox_list * list,const char ** _vname)542 mailbox_list_vname_prepare(struct mailbox_list *list, const char **_vname)
543 {
544 	struct mail_namespace *ns = list->ns;
545 	const char *vname = *_vname;
546 
547 	if (strcasecmp(vname, "INBOX") == 0 &&
548 	    (list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) {
549 		/* INBOX is case sensitive. Normalize it into "INBOX". */
550 		vname = "INBOX";
551 	} else if (ns->prefix_len > 0) {
552 		/* skip namespace prefix, except if this is INBOX */
553 		if (strncmp(ns->prefix, vname, ns->prefix_len) == 0) {
554 			vname += ns->prefix_len;
555 			if (strcmp(vname, "INBOX") == 0 &&
556 			    (list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 &&
557 			    list->set.storage_name_escape_char != '\0') {
558 				/* prefix/INBOX - this is troublesome, because
559 				   it ends up conflicting with the INBOX name.
560 				   Handle this in a bit kludgy way by escaping
561 				   the initial "I" character. */
562 				*_vname = t_strdup_printf("%c49NBOX",
563 					list->set.storage_name_escape_char);
564 				return TRUE;
565 			}
566 		} else if (strncmp(ns->prefix, vname, ns->prefix_len-1) == 0 &&
567 			 strlen(vname) == ns->prefix_len-1 &&
568 			 ns->prefix[ns->prefix_len-1] == mail_namespace_get_sep(ns)) {
569 			/* trying to access the namespace prefix itself */
570 			vname = "";
571 		} else {
572 			/* we're converting a nonexistent mailbox name,
573 			   such as a LIST pattern. */
574 		}
575 	}
576 	if (*vname == '\0' && ns->type == MAIL_NAMESPACE_TYPE_SHARED &&
577 	    (ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0 &&
578 	    !list->mail_set->mail_shared_explicit_inbox) {
579 		/* opening shared/$user. it's the same as INBOX. */
580 		vname = "INBOX";
581 	}
582 	*_vname = vname;
583 	return FALSE;
584 }
585 
586 static const char *
mailbox_list_default_get_storage_name_part(struct mailbox_list * list,const char * vname_part)587 mailbox_list_default_get_storage_name_part(struct mailbox_list *list,
588 					   const char *vname_part)
589 {
590 	const char *storage_name = vname_part;
591 	string_t *str;
592 
593 	if (!list->set.utf8) {
594 		/* UTF-8 -> mUTF-7 conversion */
595 		str = t_str_new(strlen(storage_name)*2);
596 		if (imap_escaped_utf8_to_utf7(storage_name,
597 					      list->set.vname_escape_char,
598 					      str) < 0)
599 			i_panic("Mailbox name not UTF-8: %s", vname_part);
600 		storage_name = str_c(str);
601 	} else if (list->set.vname_escape_char != '\0') {
602 		mailbox_list_name_unescape(&storage_name,
603 					   list->set.vname_escape_char);
604 	}
605 	if (list->set.storage_name_escape_char != '\0') {
606 		storage_name = mailbox_list_escape_name_params(storage_name,
607 				list->ns->prefix,
608 				'\0', /* no separator conversion */
609 				mailbox_list_get_hierarchy_sep(list),
610 				list->set.storage_name_escape_char,
611 				list->set.maildir_name);
612 	}
613 	return storage_name;
614 }
615 
mailbox_list_default_get_storage_name(struct mailbox_list * list,const char * vname)616 const char *mailbox_list_default_get_storage_name(struct mailbox_list *list,
617 						  const char *vname)
618 {
619 	const char *prepared_name = vname;
620 	const char list_sep = mailbox_list_get_hierarchy_sep(list);
621 	const char ns_sep = mail_namespace_get_sep(list->ns);
622 
623 	if (mailbox_list_vname_prepare(list, &prepared_name))
624 		return prepared_name;
625 	if (list->ns->type == MAIL_NAMESPACE_TYPE_SHARED &&
626 	    (list->ns->flags & NAMESPACE_FLAG_AUTOCREATED) == 0 &&
627 	    list_sep != ns_sep &&
628 	    list->set.storage_name_escape_char == '\0') {
629 		/* Accessing shared namespace root. This is just the initial
630 		   lookup that ends up as parameter to
631 		   shared_storage_get_namespace(). That then finds/creates the
632 		   actual shared namespace, which gets used to generate the
633 		   proper storage_name. So the only thing that's really
634 		   necessary here is to just skip over the shared namespace
635 		   prefix and leave the rest of the name untouched. The only
636 		   exception is if there is a storage_name_escape_char set, in
637 		   this case the storage name must be handled. */
638 		return prepared_name;
639 	}
640 
641 	const char sep[] = { ns_sep, '\0' };
642 	const char *const *parts = t_strsplit(prepared_name, sep);
643 	string_t *storage_name = t_str_new(128);
644 	for (unsigned int i = 0; parts[i] != NULL; i++) {
645 		if (i > 0)
646 			str_append_c(storage_name, list_sep);
647 		str_append(storage_name,
648 			   mailbox_list_default_get_storage_name_part(list, parts[i]));
649 	}
650 	return str_c(storage_name);
651 }
652 
mailbox_list_get_storage_name(struct mailbox_list * list,const char * vname)653 const char *mailbox_list_get_storage_name(struct mailbox_list *list,
654 					  const char *vname)
655 {
656 	return list->v.get_storage_name(list, vname);
657 }
658 
659 const char *
mailbox_list_unescape_name_params(const char * src,const char * ns_prefix,char ns_sep,char list_sep,char escape_char)660 mailbox_list_unescape_name_params(const char *src, const char *ns_prefix,
661 				  char ns_sep, char list_sep, char escape_char)
662 {
663 	size_t ns_prefix_len = strlen(ns_prefix);
664 	string_t *dest = t_str_new(strlen(src));
665 	unsigned int num;
666 
667 	if (str_begins(src, ns_prefix)) {
668 		str_append_data(dest, src, ns_prefix_len);
669 		src += ns_prefix_len;
670 	}
671 
672 	for (; *src != '\0'; src++) {
673 		if (*src == escape_char &&
674 		    i_isxdigit(src[1]) && i_isxdigit(src[2])) {
675 			if (src[1] >= '0' && src[1] <= '9')
676 				num = src[1] - '0';
677 			else
678 				num = i_toupper(src[1]) - 'A' + 10;
679 			num *= 16;
680 			if (src[2] >= '0' && src[2] <= '9')
681 				num += src[2] - '0';
682 			else
683 				num += i_toupper(src[2]) - 'A' + 10;
684 
685 			str_append_c(dest, num);
686 			src += 2;
687 		} else if (*src == list_sep)
688 			str_append_c(dest, ns_sep);
689 		else
690 			str_append_c(dest, *src);
691 	}
692 	return str_c(dest);
693 }
694 
695 static bool
mailbox_list_storage_name_prepare(struct mailbox_list * list,const char ** _storage_name)696 mailbox_list_storage_name_prepare(struct mailbox_list *list,
697 				  const char **_storage_name)
698 {
699 	const char *name = *_storage_name;
700 
701 	if ((list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 &&
702 	    strcmp(name, "INBOX") == 0 &&
703 	    list->ns->user == list->ns->owner) {
704 		/* user's INBOX - use as-is. NOTE: don't do case-insensitive
705 		   comparison, otherwise we can't differentiate between INBOX
706 		   and <ns prefix>/inBox. */
707 		return TRUE;
708 	}
709 	if (strcmp(name, "INBOX") == 0 &&
710 	    list->ns->type == MAIL_NAMESPACE_TYPE_SHARED &&
711 	    (list->ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0 &&
712 	    !list->mail_set->mail_shared_explicit_inbox) {
713 		/* convert to shared/$user, we don't really care about the
714 		   INBOX suffix here. */
715 		name = "";
716 	}
717 	if (name[0] == '\0') {
718 		/* return namespace prefix without the separator */
719 		if (list->ns->prefix_len == 0)
720 			*_storage_name = list->ns->prefix;
721 		else {
722 			*_storage_name =
723 				t_strndup(list->ns->prefix,
724 					  list->ns->prefix_len - 1);
725 		}
726 		return TRUE;
727 	}
728 	return FALSE;
729 }
730 
mailbox_list_name_escape(const char * name,const char * escape_chars,string_t * dest)731 void mailbox_list_name_escape(const char *name, const char *escape_chars,
732 			      string_t *dest)
733 {
734 	for (unsigned int i = 0; name[i] != '\0'; i++) {
735 		if (strchr(escape_chars, name[i]) != NULL)
736 			str_printfa(dest, "%c%02x", escape_chars[0], name[i]);
737 		else
738 			str_append_c(dest, name[i]);
739 	}
740 }
741 
742 static const char *
mailbox_list_default_get_vname_part(struct mailbox_list * list,const char * storage_name_part)743 mailbox_list_default_get_vname_part(struct mailbox_list *list,
744 				    const char *storage_name_part)
745 {
746 	const char *vname = storage_name_part;
747 	char escape_chars[] = {
748 		list->set.vname_escape_char,
749 		mail_namespace_get_sep(list->ns),
750 		'\0'
751 	};
752 
753 	if (list->set.storage_name_escape_char != '\0') {
754 		vname = mailbox_list_unescape_name_params(vname,
755 				list->ns->prefix,
756 				'\0', '\0', /* no separator conversion */
757 				list->set.storage_name_escape_char);
758 	}
759 
760 	if (!list->set.utf8) {
761 		/* mUTF-7 -> UTF-8 conversion */
762 		string_t *str = t_str_new(strlen(vname));
763 		if (escape_chars[0] != '\0') {
764 			imap_utf7_to_utf8_escaped(vname, escape_chars, str);
765 			vname = str_c(str);
766 		} else if (imap_utf7_to_utf8(vname, str) == 0)
767 			vname = str_c(str);
768 		else {
769 			/* Invalid mUTF7, but no escape character. This mailbox
770 			   can't be accessible, so just return it as the
771 			   original mUTF7 name. */
772 		}
773 	} else if (list->set.vname_escape_char != '\0') {
774 		string_t *str = t_str_new(strlen(vname));
775 		mailbox_list_name_escape(vname, escape_chars, str);
776 		vname = str_c(str);
777 	}
778 	return vname;
779 }
780 
mailbox_list_default_get_vname(struct mailbox_list * list,const char * storage_name)781 const char *mailbox_list_default_get_vname(struct mailbox_list *list,
782 					   const char *storage_name)
783 {
784 	if (mailbox_list_storage_name_prepare(list, &storage_name))
785 		return storage_name;
786 
787 	char ns_sep = mail_namespace_get_sep(list->ns);
788 	char sep[] = { mailbox_list_get_hierarchy_sep(list), '\0' };
789 	const char *const *parts = t_strsplit(storage_name, sep);
790 	string_t *vname = t_str_new(128);
791 	str_append(vname, list->ns->prefix);
792 	for (unsigned int i = 0; parts[i] != NULL; i++) {
793 		if (i > 0)
794 			str_append_c(vname, ns_sep);
795 		str_append(vname,
796 			   mailbox_list_default_get_vname_part(list, parts[i]));
797 	}
798 	return str_c(vname);
799 }
800 
mailbox_list_get_vname(struct mailbox_list * list,const char * name)801 const char *mailbox_list_get_vname(struct mailbox_list *list, const char *name)
802 {
803 	return list->v.get_vname(list, name);
804 }
805 
mailbox_list_destroy(struct mailbox_list ** _list)806 void mailbox_list_destroy(struct mailbox_list **_list)
807 {
808 	struct mailbox_list *list = *_list;
809 
810 	*_list = NULL;
811 	i_free_and_null(list->error_string);
812 	i_free(list->last_internal_error);
813 
814 	if (hash_table_is_created(list->guid_cache)) {
815 		hash_table_destroy(&list->guid_cache);
816 		pool_unref(&list->guid_cache_pool);
817 	}
818 
819 	if (list->subscriptions != NULL)
820 		mailbox_tree_deinit(&list->subscriptions);
821 	if (list->changelog != NULL)
822 		mailbox_log_free(&list->changelog);
823 
824 	if (array_is_created(&list->error_stack)) {
825 		i_assert(array_count(&list->error_stack) == 0);
826 		array_free(&list->error_stack);
827 	}
828 	list->v.deinit(list);
829 }
830 
mailbox_list_get_driver_name(const struct mailbox_list * list)831 const char *mailbox_list_get_driver_name(const struct mailbox_list *list)
832 {
833 	return list->name;
834 }
835 
836 const struct mailbox_list_settings *
mailbox_list_get_settings(const struct mailbox_list * list)837 mailbox_list_get_settings(const struct mailbox_list *list)
838 {
839 	return &list->set;
840 }
841 
mailbox_list_get_flags(const struct mailbox_list * list)842 enum mailbox_list_flags mailbox_list_get_flags(const struct mailbox_list *list)
843 {
844 	return list->flags;
845 }
846 
847 struct mail_namespace *
mailbox_list_get_namespace(const struct mailbox_list * list)848 mailbox_list_get_namespace(const struct mailbox_list *list)
849 {
850 	return list->ns;
851 }
852 
get_dir_mode(mode_t mode)853 static mode_t get_dir_mode(mode_t mode)
854 {
855 	/* add the execute bit if either read or write bit is set */
856 	if ((mode & 0600) != 0) mode |= 0100;
857 	if ((mode & 0060) != 0) mode |= 0010;
858 	if ((mode & 0006) != 0) mode |= 0001;
859 	return mode;
860 }
861 
862 struct mail_user *
mailbox_list_get_user(const struct mailbox_list * list)863 mailbox_list_get_user(const struct mailbox_list *list)
864 {
865 	return list->ns->user;
866 }
867 
868 static int
mailbox_list_get_storage_driver(struct mailbox_list * list,const char * driver,struct mail_storage ** storage_r)869 mailbox_list_get_storage_driver(struct mailbox_list *list, const char *driver,
870 				struct mail_storage **storage_r)
871 {
872 	struct mail_storage *storage;
873 	const char *error, *data;
874 
875 	array_foreach_elem(&list->ns->all_storages, storage) {
876 		if (strcmp(storage->name, driver) == 0) {
877 			*storage_r = storage;
878 			return 0;
879 		}
880 	}
881 
882 	data = i_strchr_to_next(list->ns->set->location, ':');
883 	if (data == NULL)
884 		data = "";
885 	if (mail_storage_create_full(list->ns, driver, data, 0,
886 				     storage_r, &error) < 0) {
887 		mailbox_list_set_critical(list,
888 			"Namespace %s: Failed to create storage '%s': %s",
889 			list->ns->prefix, driver, error);
890 		return -1;
891 	}
892 	return 0;
893 }
894 
mailbox_list_get_storage(struct mailbox_list ** list,const char * vname,struct mail_storage ** storage_r)895 int mailbox_list_get_storage(struct mailbox_list **list, const char *vname,
896 			     struct mail_storage **storage_r)
897 {
898 	const struct mailbox_settings *set;
899 
900 	if ((*list)->v.get_storage != NULL)
901 		return (*list)->v.get_storage(list, vname, storage_r);
902 
903 	set = mailbox_settings_find((*list)->ns, vname);
904 	if (set != NULL && set->driver != NULL && set->driver[0] != '\0') {
905 		return mailbox_list_get_storage_driver(*list, set->driver,
906 						       storage_r);
907 	}
908 	*storage_r = mail_namespace_get_default_storage((*list)->ns);
909 	return 0;
910 }
911 
mailbox_list_get_default_storage(struct mailbox_list * list,struct mail_storage ** storage)912 void mailbox_list_get_default_storage(struct mailbox_list *list,
913 				      struct mail_storage **storage)
914 {
915 	*storage = mail_namespace_get_default_storage(list->ns);
916 }
917 
mailbox_list_get_hierarchy_sep(struct mailbox_list * list)918 char mailbox_list_get_hierarchy_sep(struct mailbox_list *list)
919 {
920 	/* the current API doesn't allow returning an error, so imap code
921 	   looks at the list's last error. make sure the error is cleared
922 	   so the error-check doesn't return something irrelevant */
923 	mailbox_list_clear_error(list);
924 	return list->v.get_hierarchy_sep(list);
925 }
926 
927 static bool
mailbox_list_get_permissions_stat(struct mailbox_list * list,const char * path,struct mailbox_permissions * permissions_r)928 mailbox_list_get_permissions_stat(struct mailbox_list *list, const char *path,
929 				  struct mailbox_permissions *permissions_r)
930 {
931 	struct stat st;
932 
933 	if (stat(path, &st) < 0) {
934 		if (errno == EACCES) {
935 			mailbox_list_set_critical(list, "%s",
936 				mail_error_eacces_msg("stat", path));
937 		} else if (!ENOTFOUND(errno)) {
938 			mailbox_list_set_critical(list, "stat(%s) failed: %m",
939 						  path);
940 		} else {
941 			e_debug(list->ns->user->event,
942 				"Namespace %s: %s doesn't exist yet, "
943 				"using default permissions",
944 				list->ns->prefix, path);
945 		}
946 		return FALSE;
947 	}
948 
949 	permissions_r->file_uid = st.st_uid;
950 	permissions_r->file_gid = st.st_gid;
951 	permissions_r->file_create_mode = (st.st_mode & 0666) | 0600;
952 	permissions_r->dir_create_mode = (st.st_mode & 0777) | 0700;
953 	permissions_r->file_create_gid_origin = path;
954 
955 	if (!S_ISDIR(st.st_mode)) {
956 		/* we're getting permissions from a file.
957 		   apply +x modes as necessary. */
958 		permissions_r->dir_create_mode =
959 			get_dir_mode(permissions_r->dir_create_mode);
960 	}
961 
962 	if (S_ISDIR(st.st_mode) && (st.st_mode & S_ISGID) != 0) {
963 		/* directory's GID is used automatically for new files */
964 		permissions_r->file_create_gid = (gid_t)-1;
965 	} else if ((st.st_mode & 0070) >> 3 == (st.st_mode & 0007)) {
966 		/* group has same permissions as world, so don't bother
967 		   changing it */
968 		permissions_r->file_create_gid = (gid_t)-1;
969 	} else if (getegid() == st.st_gid) {
970 		/* using our own gid, no need to change it */
971 		permissions_r->file_create_gid = (gid_t)-1;
972 	} else {
973 		permissions_r->file_create_gid = st.st_gid;
974 	}
975 	if (!S_ISDIR(st.st_mode) &&
976 	    permissions_r->file_create_gid != (gid_t)-1) {
977 		/* we need to stat() the parent directory to see if
978 		   it has setgid-bit set */
979 		const char *p = strrchr(path, '/');
980 		const char *parent_path = p == NULL ? NULL :
981 			t_strdup_until(path, p);
982 		if (parent_path != NULL &&
983 		    stat(parent_path, &st) == 0 &&
984 		    (st.st_mode & S_ISGID) != 0) {
985 			/* directory's GID is used automatically for
986 			   new files */
987 			permissions_r->file_create_gid = (gid_t)-1;
988 		}
989 	}
990 	return TRUE;
991 }
992 
993 static void ATTR_NULL(2)
mailbox_list_get_permissions_internal(struct mailbox_list * list,const char * name,struct mailbox_permissions * permissions_r)994 mailbox_list_get_permissions_internal(struct mailbox_list *list,
995 				      const char *name,
996 				      struct mailbox_permissions *permissions_r)
997 {
998 	const char *path = NULL, *parent_name, *p;
999 
1000 	i_zero(permissions_r);
1001 
1002 	/* use safe defaults */
1003 	permissions_r->file_uid = (uid_t)-1;
1004 	permissions_r->file_gid = (gid_t)-1;
1005 	permissions_r->file_create_mode = 0600;
1006 	permissions_r->dir_create_mode = 0700;
1007 	permissions_r->file_create_gid = (gid_t)-1;
1008 	permissions_r->file_create_gid_origin = "defaults";
1009 
1010 	if (list->set.iter_from_index_dir ||
1011 	    (list->flags & MAILBOX_LIST_FLAG_NO_MAIL_FILES) != 0) {
1012 		/* a) iterating from index dir. Use the index dir's permissions
1013 		   as well, since they might be in a faster storage.
1014 
1015 		   b) mail files don't exist in storage, but index files
1016 		   might. */
1017 		(void)mailbox_list_get_path(list, name,
1018 			MAILBOX_LIST_PATH_TYPE_INDEX, &path);
1019 	}
1020 
1021 	if (name != NULL && path == NULL) {
1022 		if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR,
1023 					  &path) < 0)
1024 			name = NULL;
1025 	}
1026 	if (name == NULL && path == NULL) {
1027 		(void)mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_DIR,
1028 						 &path);
1029 	}
1030 
1031 	if (path == NULL) {
1032 		/* no filesystem support in storage */
1033 	} else if (mailbox_list_get_permissions_stat(list, path, permissions_r)) {
1034 		/* got permissions from the given path */
1035 		permissions_r->gid_origin_is_mailbox_path = name != NULL;
1036 	} else if (name != NULL) {
1037 		/* path couldn't be stat()ed, try parent mailbox */
1038 		p = strrchr(name, mailbox_list_get_hierarchy_sep(list));
1039 		if (p == NULL) {
1040 			/* return root defaults */
1041 			parent_name = NULL;
1042 		} else {
1043 			parent_name = t_strdup_until(name, p);
1044 		}
1045 		mailbox_list_get_permissions(list, parent_name,
1046 					     permissions_r);
1047 		return;
1048 	} else {
1049 		/* assume current defaults for mailboxes that don't exist or
1050 		   can't be looked up for some other reason */
1051 		permissions_r->file_uid = geteuid();
1052 		permissions_r->file_gid = getegid();
1053 	}
1054 	if (name == NULL) {
1055 		mailbox_permissions_copy(&list->root_permissions, permissions_r,
1056 					 list->pool);
1057 	}
1058 
1059 	if (name == NULL) {
1060 		e_debug(list->ns->user->event,
1061 			"Namespace %s: Using permissions from %s: "
1062 			"mode=0%o gid=%s", list->ns->prefix,
1063 			path != NULL ? path : "",
1064 			(int)permissions_r->dir_create_mode,
1065 			permissions_r->file_create_gid == (gid_t)-1 ? "default" :
1066 			dec2str(permissions_r->file_create_gid));
1067 	}
1068 }
1069 
mailbox_list_get_permissions(struct mailbox_list * list,const char * name,struct mailbox_permissions * permissions_r)1070 void mailbox_list_get_permissions(struct mailbox_list *list, const char *name,
1071 				  struct mailbox_permissions *permissions_r)
1072 {
1073 	mailbox_list_get_permissions_internal(list, name, permissions_r);
1074 }
1075 
mailbox_list_get_root_permissions(struct mailbox_list * list,struct mailbox_permissions * permissions_r)1076 void mailbox_list_get_root_permissions(struct mailbox_list *list,
1077 				       struct mailbox_permissions *permissions_r)
1078 {
1079 	if (list->root_permissions.file_create_mode != (mode_t)-1)
1080 		*permissions_r = list->root_permissions;
1081 	else {
1082 		mailbox_list_get_permissions_internal(list, NULL,
1083 						      permissions_r);
1084 	}
1085 }
1086 
mailbox_permissions_copy(struct mailbox_permissions * dest,const struct mailbox_permissions * src,pool_t pool)1087 void mailbox_permissions_copy(struct mailbox_permissions *dest,
1088 			      const struct mailbox_permissions *src,
1089 			      pool_t pool)
1090 {
1091 	*dest = *src;
1092 	dest->file_create_gid_origin =
1093 		p_strdup(pool, src->file_create_gid_origin);
1094 }
1095 
1096 static const char *
get_expanded_path(const char * unexpanded_start,const char * unexpanded_stop,const char * expanded_full)1097 get_expanded_path(const char *unexpanded_start, const char *unexpanded_stop,
1098 		  const char *expanded_full)
1099 {
1100 	const char *ret;
1101 	unsigned int i, slash_count = 0, slash2_count = 0;
1102 
1103 	/* get the expanded path up to the same amount of '/' characters.
1104 	   if there isn't the same amount of '/' characters, it means %variable
1105 	   expansion added more of them and we can't handle this. */
1106 	for (i = 0; unexpanded_start+i != unexpanded_stop; i++) {
1107 		if (unexpanded_start[i] == '/')
1108 			slash_count++;
1109 	}
1110 	for (; unexpanded_start[i] != '\0'; i++) {
1111 		if (unexpanded_start[i] == '/')
1112 			slash2_count++;
1113 	}
1114 
1115 	for (i = 0; expanded_full[i] != '\0'; i++) {
1116 		if (expanded_full[i] == '/') {
1117 			if (slash_count == 0)
1118 				break;
1119 			slash_count--;
1120 		}
1121 	}
1122 	if (slash_count != 0)
1123 		return "";
1124 
1125 	ret = t_strndup(expanded_full, i);
1126 	for (; expanded_full[i] != '\0'; i++) {
1127 		if (expanded_full[i] == '/') {
1128 			if (slash2_count == 0)
1129 				return "";
1130 			slash2_count--;
1131 		}
1132 	}
1133 	if (slash2_count != 0)
1134 		return "";
1135 	return ret;
1136 }
1137 
1138 static int
mailbox_list_try_mkdir_root_parent(struct mailbox_list * list,enum mailbox_list_path_type type,struct mailbox_permissions * perm,const char ** error_r)1139 mailbox_list_try_mkdir_root_parent(struct mailbox_list *list,
1140 				   enum mailbox_list_path_type type,
1141 				   struct mailbox_permissions *perm,
1142 				   const char **error_r)
1143 {
1144 	const char *expanded, *unexpanded, *root_dir, *p;
1145 	struct stat st;
1146 	bool home = FALSE;
1147 
1148 	/* get the directory path up to last %variable. for example
1149 	   unexpanded path may be "/var/mail/%d/%2n/%n/Maildir", and we want
1150 	   to get expanded="/var/mail/domain/nn" */
1151 	unexpanded = mailbox_list_get_unexpanded_path(list, type);
1152 	p = strrchr(unexpanded, '%');
1153 	if ((p == unexpanded && p[1] == 'h') ||
1154 	    (p == NULL && unexpanded[0] == '~')) {
1155 		/* home directory used */
1156 		if (!mailbox_list_get_root_path(list, type, &expanded))
1157 			i_unreached();
1158 		home = TRUE;
1159 	} else if (p == NULL) {
1160 		return 0;
1161 	} else {
1162 		while (p != unexpanded && *p != '/') p--;
1163 		if (p == unexpanded)
1164 			return 0;
1165 
1166 		if (!mailbox_list_get_root_path(list, type, &expanded))
1167 			i_unreached();
1168 		expanded = get_expanded_path(unexpanded, p, expanded);
1169 		if (*expanded == '\0')
1170 			return 0;
1171 	}
1172 
1173 	/* get the first existing parent directory's permissions */
1174 	if (stat_first_parent(expanded, &root_dir, &st) < 0) {
1175 		*error_r = errno == EACCES ?
1176 			mail_error_eacces_msg("stat", root_dir) :
1177 			t_strdup_printf("stat(%s) failed: %m", root_dir);
1178 		return -1;
1179 	}
1180 
1181 	/* if the parent directory doesn't have setgid-bit enabled, we don't
1182 	   copy any permissions from it. */
1183 	if ((st.st_mode & S_ISGID) == 0)
1184 		return 0;
1185 
1186 	if (!home) {
1187 		/* assuming we have e.g. /var/vmail/%d/%n directory, here we
1188 		   want to create up to /var/vmail/%d with permissions from
1189 		   the parent directory. we never want to create the %n
1190 		   directory itself. */
1191 		if (root_dir == expanded) {
1192 			/* this is the %n directory */
1193 		} else {
1194 			if (mkdir_parents_chgrp(expanded, st.st_mode,
1195 						(gid_t)-1, root_dir) < 0 &&
1196 			    errno != EEXIST) {
1197 				*error_r = t_strdup_printf(
1198 					"mkdir(%s) failed: %m", expanded);
1199 				return -1;
1200 			}
1201 		}
1202 		if (perm->file_create_gid == (gid_t)-1 &&
1203 		    (perm->dir_create_mode & S_ISGID) == 0) {
1204 			/* change the group for user directories */
1205 			perm->dir_create_mode |= S_ISGID;
1206 			perm->file_create_gid = getegid();
1207 			perm->file_create_gid_origin = "egid";
1208 			perm->gid_origin_is_mailbox_path = FALSE;
1209 		}
1210 	} else {
1211 		/* when using %h and the parent has setgid-bit,
1212 		   copy the permissions from it for the home we're creating */
1213 		perm->file_create_mode = st.st_mode & 0666;
1214 		perm->dir_create_mode = st.st_mode;
1215 		perm->file_create_gid = (gid_t)-1;
1216 		perm->file_create_gid_origin = "parent";
1217 		perm->gid_origin_is_mailbox_path = FALSE;
1218 	}
1219 	return 0;
1220 }
1221 
mailbox_list_try_mkdir_root(struct mailbox_list * list,const char * path,enum mailbox_list_path_type type,const char ** error_r)1222 int mailbox_list_try_mkdir_root(struct mailbox_list *list, const char *path,
1223 				enum mailbox_list_path_type type,
1224 				const char **error_r)
1225 {
1226 	const char *root_dir;
1227 	struct stat st;
1228 	struct mailbox_permissions perm;
1229 
1230 	if (stat(path, &st) == 0) {
1231 		/* looks like it already exists, don't bother checking
1232 		   further. */
1233 		if (!S_ISDIR(st.st_mode)) {
1234 			*error_r = t_strdup_printf(
1235 				"Root directory is a file: %s", path);
1236 			return -1;
1237 		}
1238 		return 0;
1239 	}
1240 
1241 	mailbox_list_get_root_permissions(list, &perm);
1242 
1243 	if (!mailbox_list_get_root_path(list, type, &root_dir))
1244 		i_unreached();
1245 	i_assert(str_begins(path, root_dir));
1246 	if (strcmp(root_dir, path) != 0 && stat(root_dir, &st) == 0) {
1247 		/* creating a subdirectory under an already existing root dir.
1248 		   use the root's permissions */
1249 	} else {
1250 		if (mailbox_list_try_mkdir_root_parent(list, type,
1251 						       &perm, error_r) < 0)
1252 			return -1;
1253 	}
1254 
1255 	/* the rest of the directories exist only for one user. create them
1256 	   with default directory permissions */
1257 	if (mkdir_parents_chgrp(path, perm.dir_create_mode,
1258 				perm.file_create_gid,
1259 				perm.file_create_gid_origin) < 0 &&
1260 	    errno != EEXIST) {
1261 		if (errno == EACCES)
1262 			*error_r = mail_error_create_eacces_msg("mkdir", path);
1263 		else
1264 			*error_r = t_strdup_printf("mkdir(%s) failed: %m", path);
1265 		return -1;
1266 	}
1267 	return 0;
1268 }
1269 
mailbox_list_mkdir_root(struct mailbox_list * list,const char * path,enum mailbox_list_path_type type)1270 int mailbox_list_mkdir_root(struct mailbox_list *list, const char *path,
1271 			    enum mailbox_list_path_type type)
1272 {
1273 	const char *error;
1274 
1275 	if (mailbox_list_try_mkdir_root(list, path, type, &error) < 0) {
1276 		mailbox_list_set_critical(list, "%s", error);
1277 		return -1;
1278 	}
1279 	if (type == MAILBOX_LIST_PATH_TYPE_INDEX)
1280 		list->index_root_dir_created = TRUE;
1281 	return 0;
1282 }
1283 
1284 static bool
mailbox_list_is_valid_fs_name(struct mailbox_list * list,const char * name,const char ** error_r)1285 mailbox_list_is_valid_fs_name(struct mailbox_list *list, const char *name,
1286 			      const char **error_r)
1287 {
1288 	bool ret, allow_internal_dirs;
1289 
1290 	*error_r = NULL;
1291 
1292 	if (list->mail_set->mail_full_filesystem_access ||
1293 	    list->set.no_fs_validation)
1294 		return TRUE;
1295 
1296 	/* either the list backend uses '/' as the hierarchy separator or
1297 	   it doesn't use filesystem at all (PROP_NO_ROOT) */
1298 	if ((list->props & MAILBOX_LIST_PROP_NO_ROOT) == 0 &&
1299 	    mailbox_list_get_hierarchy_sep(list) != '/' &&
1300 	    strchr(name, '/') != NULL) {
1301 		*error_r = "Name must not have '/' characters";
1302 		return FALSE;
1303 	}
1304 
1305 	/* make sure it's not absolute path */
1306 	if (*name == '/') {
1307 		*error_r = "Begins with '/'";
1308 		return FALSE;
1309 	}
1310 	if (*name == '~') {
1311 		*error_r = "Begins with '~'";
1312 		return FALSE;
1313 	}
1314 
1315 	/* make sure the mailbox name doesn't contain any foolishness:
1316 	   "../" could give access outside the mailbox directory.
1317 	   "./" and "//" could fool ACL checks.
1318 
1319 	   some mailbox formats have reserved directory names, such as
1320 	   Maildir's cur/new/tmp. if any of those would conflict with the
1321 	   mailbox directory name, it's not valid. */
1322 	allow_internal_dirs = list->v.is_internal_name == NULL ||
1323 		*list->set.maildir_name != '\0' ||
1324 		(list->props & MAILBOX_LIST_PROP_NO_INTERNAL_NAMES) != 0;
1325 	T_BEGIN {
1326 		const char *const *names;
1327 
1328 		names = t_strsplit(name, "/");
1329 		for (; *names != NULL; names++) {
1330 			const char *n = *names;
1331 
1332 			if (*n == '\0') {
1333 				*error_r = "Has adjacent '/' chars";
1334 				break; /* // */
1335 			}
1336 			if (*n == '.') {
1337 				if (n[1] == '\0') {
1338 					*error_r = "Contains '.' part";
1339 					break; /* ./ */
1340 				}
1341 				if (n[1] == '.' && n[2] == '\0') {
1342 					*error_r = "Contains '..' part";
1343 					break; /* ../ */
1344 				}
1345 			}
1346 			if (*list->set.maildir_name != '\0' &&
1347 			    strcmp(list->set.maildir_name, n) == 0) {
1348 				/* don't allow maildir_name to be used as part
1349 				   of the mailbox name */
1350 				*error_r = "Contains reserved name";
1351 				break;
1352 			}
1353 			if (!allow_internal_dirs &&
1354 			    list->v.is_internal_name(list, n)) {
1355 				*error_r = "Contains reserved name";
1356 				break;
1357 			}
1358 		}
1359 		ret = *names == NULL;
1360 	} T_END;
1361 
1362 	return ret;
1363 }
1364 
1365 
mailbox_list_is_valid_name(struct mailbox_list * list,const char * name,const char ** error_r)1366 bool mailbox_list_is_valid_name(struct mailbox_list *list,
1367 				const char *name, const char **error_r)
1368 {
1369 	if (*name == '\0') {
1370 		if (*list->ns->prefix != '\0') {
1371 			/* an ugly way to get to mailbox root (e.g. Maildir/
1372 			   when it's not the INBOX) */
1373 			return TRUE;
1374 		}
1375 		*error_r = "Name is empty";
1376 		return FALSE;
1377 	}
1378 
1379 	return mailbox_list_is_valid_fs_name(list, name, error_r);
1380 }
1381 
mailbox_list_get_path(struct mailbox_list * list,const char * name,enum mailbox_list_path_type type,const char ** path_r)1382 int mailbox_list_get_path(struct mailbox_list *list, const char *name,
1383 			  enum mailbox_list_path_type type,
1384 			  const char **path_r)
1385 {
1386 	int ret;
1387 
1388 	if ((ret = list->v.get_path(list, name, type, path_r)) <= 0)
1389 		*path_r = NULL;
1390 	else
1391 		i_assert(*path_r != NULL);
1392 	return ret;
1393 }
1394 
mailbox_list_get_root_path(struct mailbox_list * list,enum mailbox_list_path_type type,const char ** path_r)1395 bool mailbox_list_get_root_path(struct mailbox_list *list,
1396 				enum mailbox_list_path_type type,
1397 				const char **path_r)
1398 {
1399 	int ret;
1400 
1401 	if ((ret = list->v.get_path(list, NULL, type, path_r)) < 0)
1402 		i_unreached();
1403 	if (ret == 0)
1404 		*path_r = NULL;
1405 	else
1406 		i_assert(*path_r != NULL);
1407 	return ret > 0;
1408 }
1409 
mailbox_list_get_root_forced(struct mailbox_list * list,enum mailbox_list_path_type type)1410 const char *mailbox_list_get_root_forced(struct mailbox_list *list,
1411 					 enum mailbox_list_path_type type)
1412 {
1413 	const char *path;
1414 
1415 	if (!mailbox_list_get_root_path(list, type, &path))
1416 		i_unreached();
1417 	return path;
1418 }
1419 
mailbox_list_set_get_root_path(const struct mailbox_list_settings * set,enum mailbox_list_path_type type,const char ** path_r)1420 bool mailbox_list_set_get_root_path(const struct mailbox_list_settings *set,
1421 				    enum mailbox_list_path_type type,
1422 				    const char **path_r)
1423 {
1424 	const char *path = NULL;
1425 
1426 	switch (type) {
1427 	case MAILBOX_LIST_PATH_TYPE_DIR:
1428 		path = set->root_dir;
1429 		break;
1430 	case MAILBOX_LIST_PATH_TYPE_ALT_DIR:
1431 		path = set->alt_dir;
1432 		break;
1433 	case MAILBOX_LIST_PATH_TYPE_MAILBOX:
1434 		if (*set->mailbox_dir_name == '\0')
1435 			path = set->root_dir;
1436 		else {
1437 			path = t_strconcat(set->root_dir, "/",
1438 					   set->mailbox_dir_name, NULL);
1439 			path = t_strndup(path, strlen(path)-1);
1440 		}
1441 		break;
1442 	case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX:
1443 		if (*set->mailbox_dir_name == '\0')
1444 			path = set->alt_dir;
1445 		else if (set->alt_dir != NULL) {
1446 			path = t_strconcat(set->alt_dir, "/",
1447 					   set->mailbox_dir_name, NULL);
1448 			path = t_strndup(path, strlen(path)-1);
1449 		}
1450 		break;
1451 	case MAILBOX_LIST_PATH_TYPE_CONTROL:
1452 		path = set->control_dir != NULL ?
1453 			set->control_dir : set->root_dir;
1454 		break;
1455 	case MAILBOX_LIST_PATH_TYPE_LIST_INDEX:
1456 		if (set->list_index_dir != NULL) {
1457 			if (set->list_index_dir[0] == '/') {
1458 				path = set->list_index_dir;
1459 				break;
1460 			}
1461 			/* relative path */
1462 			if (!mailbox_list_set_get_root_path(set,
1463 					MAILBOX_LIST_PATH_TYPE_INDEX, &path))
1464 				i_unreached();
1465 			path = t_strconcat(path, "/", set->list_index_dir, NULL);
1466 			break;
1467 		}
1468 		/* fall through - default to index directory */
1469 	case MAILBOX_LIST_PATH_TYPE_INDEX_CACHE:
1470 		if (set->index_cache_dir != NULL &&
1471 		    type == MAILBOX_LIST_PATH_TYPE_INDEX_CACHE) {
1472 			path = set->index_cache_dir;
1473 			break;
1474 		}
1475 		/* fall through */
1476 	case MAILBOX_LIST_PATH_TYPE_INDEX:
1477 		if (set->index_dir != NULL) {
1478 			if (set->index_dir[0] == '\0') {
1479 				/* in-memory indexes */
1480 				return 0;
1481 			}
1482 			path = set->index_dir;
1483 		} else {
1484 			path = set->root_dir;
1485 		}
1486 		break;
1487 	case MAILBOX_LIST_PATH_TYPE_INDEX_PRIVATE:
1488 		path = set->index_pvt_dir;
1489 		break;
1490 	}
1491 	*path_r = path;
1492 	return path != NULL;
1493 }
1494 
mailbox_list_get_temp_prefix(struct mailbox_list * list)1495 const char *mailbox_list_get_temp_prefix(struct mailbox_list *list)
1496 {
1497 	return list->v.get_temp_prefix(list, FALSE);
1498 }
1499 
mailbox_list_get_global_temp_prefix(struct mailbox_list * list)1500 const char *mailbox_list_get_global_temp_prefix(struct mailbox_list *list)
1501 {
1502 	return list->v.get_temp_prefix(list, TRUE);
1503 }
1504 
mailbox_list_join_refpattern(struct mailbox_list * list,const char * ref,const char * pattern)1505 const char *mailbox_list_join_refpattern(struct mailbox_list *list,
1506 					 const char *ref, const char *pattern)
1507 {
1508 	if (list->v.join_refpattern != NULL)
1509 		return list->v.join_refpattern(list, ref, pattern);
1510 
1511 	/* the default implementation: */
1512 	if (*ref != '\0') {
1513 		/* merge reference and pattern */
1514 		pattern = t_strconcat(ref, pattern, NULL);
1515 	}
1516 	return pattern;
1517 }
1518 
mailbox_has_children(struct mailbox_list * list,const char * name)1519 int mailbox_has_children(struct mailbox_list *list, const char *name)
1520 {
1521 	struct mailbox_list_iterate_context *iter;
1522 	const char *pattern;
1523 	int ret;
1524 
1525 	pattern = t_strdup_printf("%s%c%%", name,
1526 				  mail_namespace_get_sep(list->ns));
1527 	iter = mailbox_list_iter_init(list, pattern,
1528 				      MAILBOX_LIST_ITER_RETURN_NO_FLAGS);
1529 	ret = mailbox_list_iter_next(iter) != NULL ? 1 : 0;
1530 	if (mailbox_list_iter_deinit(&iter) < 0)
1531 		ret = -1;
1532 	return ret;
1533 }
1534 
mailbox_list_mailbox(struct mailbox_list * list,const char * name,enum mailbox_info_flags * flags_r)1535 int mailbox_list_mailbox(struct mailbox_list *list, const char *name,
1536 			 enum mailbox_info_flags *flags_r)
1537 {
1538 	const char *path, *fname, *rootdir, *dir, *inbox;
1539 	size_t len;
1540 
1541 	*flags_r = 0;
1542 
1543 	if ((list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 &&
1544 	    strcasecmp(name, "INBOX") == 0) {
1545 		/* special handling for INBOX, mainly because with Maildir++
1546 		   layout it needs to check if the cur/ directory exists,
1547 		   which the Maildir++ layout backend itself can't do.. */
1548 		struct mailbox *box;
1549 		enum mailbox_existence existence;
1550 		int ret;
1551 
1552 		/* kludge: with imapc backend we can get here with
1553 		   list=Maildir++ (for indexes), but list->ns->list=imapc */
1554 		box = mailbox_alloc(list->ns->list, "INBOX", 0);
1555 		ret = mailbox_exists(box, FALSE, &existence);
1556 		if (ret < 0) {
1557 			const char *errstr;
1558 			enum mail_error error;
1559 
1560 			/* internal error or with imapc we can get here with
1561 			   login failures */
1562 			errstr = mailbox_get_last_error(box, &error);
1563 			mailbox_list_set_error(list, error, errstr);
1564 		}
1565 		mailbox_free(&box);
1566 		if (ret < 0)
1567 			return -1;
1568 		switch (existence) {
1569 		case MAILBOX_EXISTENCE_NONE:
1570 		case MAILBOX_EXISTENCE_NOSELECT:
1571 			*flags_r |= MAILBOX_NONEXISTENT;
1572 			return 0;
1573 		case MAILBOX_EXISTENCE_SELECT:
1574 			break;
1575 		}
1576 		return 1;
1577 	}
1578 
1579 	if (list->v.get_mailbox_flags == NULL) {
1580 		/* can't do this optimized. do it the slow way. */
1581 		struct mailbox_list_iterate_context *iter;
1582 		const struct mailbox_info *info;
1583 		const char *vname;
1584 
1585 		vname = mailbox_list_get_vname(list, name);
1586 		iter = mailbox_list_iter_init(list, vname, 0);
1587 		info = mailbox_list_iter_next(iter);
1588 		if (info == NULL)
1589 			*flags_r = MAILBOX_NONEXISTENT;
1590 		else
1591 			*flags_r = info->flags;
1592 		return mailbox_list_iter_deinit(&iter);
1593 	}
1594 
1595 	if (!list->set.iter_from_index_dir) {
1596 		rootdir = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_MAILBOX);
1597 		if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR, &path) <= 0)
1598 			i_unreached();
1599 	} else {
1600 		rootdir = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_INDEX);
1601 		if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_INDEX, &path) <= 0)
1602 			i_unreached();
1603 	}
1604 
1605 	fname = strrchr(path, '/');
1606 	if (fname == NULL) {
1607 		fname = path;
1608 		dir = "/";
1609 	} else {
1610 		dir = t_strdup_until(path, fname);
1611 		fname++;
1612 	}
1613 
1614 	len = strlen(rootdir);
1615 	if (str_begins(path, rootdir) && path[len] == '/') {
1616 		/* looking up a regular mailbox under mail root dir */
1617 	} else if ((list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 &&
1618 		   strcasecmp(name, "INBOX") == 0) {
1619 		/* looking up INBOX that's elsewhere */
1620 	} else {
1621 		/* looking up the root dir itself */
1622 		dir = path;
1623 		fname = "";
1624 	}
1625 	if (*fname == '\0' && *name == '\0' &&
1626 	    (list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) {
1627 		/* if INBOX is in e.g. ~/Maildir, it shouldn't be possible to
1628 		   access it also via namespace prefix. */
1629 		if (mailbox_list_get_path(list, "INBOX",
1630 					  MAILBOX_LIST_PATH_TYPE_MAILBOX,
1631 					  &inbox) <= 0)
1632 			i_unreached();
1633 		if (strcmp(inbox, dir) == 0) {
1634 			*flags_r |= MAILBOX_NONEXISTENT;
1635 			return 0;
1636 		}
1637 	}
1638 	return list->v.get_mailbox_flags(list, dir, fname,
1639 					 MAILBOX_LIST_FILE_TYPE_UNKNOWN,
1640 					 flags_r);
1641 }
1642 
mailbox_list_init_changelog(struct mailbox_list * list)1643 static bool mailbox_list_init_changelog(struct mailbox_list *list)
1644 {
1645 	struct mailbox_permissions perm;
1646 	const char *path;
1647 
1648 	if (list->changelog != NULL)
1649 		return TRUE;
1650 
1651 	/* don't do this in mailbox_list_create(), because _get_path() might be
1652 	   overridden by storage (mbox). */
1653 	if (!mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_INDEX, &path))
1654 		return FALSE;
1655 
1656 	path = t_strconcat(path, "/"MAILBOX_LOG_FILE_NAME, NULL);
1657 	list->changelog = mailbox_log_alloc(list->ns->user->event, path);
1658 
1659 	mailbox_list_get_root_permissions(list, &perm);
1660 	mailbox_log_set_permissions(list->changelog, perm.file_create_mode,
1661 				    perm.file_create_gid,
1662 				    perm.file_create_gid_origin);
1663 	return TRUE;
1664 }
1665 
mailbox_list_mkdir_missing_index_root(struct mailbox_list * list)1666 int mailbox_list_mkdir_missing_index_root(struct mailbox_list *list)
1667 {
1668 	const char *index_dir;
1669 
1670 	if (list->index_root_dir_created)
1671 		return 1;
1672 
1673 	/* If index root dir hasn't been created yet, do it now.
1674 	   Do this here even if the index directory is the same as mail root
1675 	   directory, because it may not have been created elsewhere either. */
1676 	if (!mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_INDEX,
1677 					&index_dir))
1678 		return 0;
1679 
1680 	if (mailbox_list_mkdir_root(list, index_dir,
1681 				    MAILBOX_LIST_PATH_TYPE_INDEX) < 0)
1682 		return -1;
1683 	list->index_root_dir_created = TRUE;
1684 	return 1;
1685 }
1686 
mailbox_list_mkdir_missing_list_index_root(struct mailbox_list * list)1687 int mailbox_list_mkdir_missing_list_index_root(struct mailbox_list *list)
1688 {
1689 	const char *index_dir;
1690 
1691 	if (list->set.list_index_dir == NULL)
1692 		return mailbox_list_mkdir_missing_index_root(list);
1693 
1694 	/* LISTINDEX points outside the index root directory */
1695 	if (list->list_index_root_dir_created)
1696 		return 1;
1697 
1698 	if (!mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_LIST_INDEX,
1699 					&index_dir))
1700 		return 0;
1701 	if (mailbox_list_mkdir_root(list, index_dir,
1702 				    MAILBOX_LIST_PATH_TYPE_LIST_INDEX) < 0)
1703 		return -1;
1704 	list->list_index_root_dir_created = TRUE;
1705 	return 1;
1706 }
1707 
mailbox_list_add_change(struct mailbox_list * list,enum mailbox_log_record_type type,const guid_128_t mailbox_guid)1708 void mailbox_list_add_change(struct mailbox_list *list,
1709 			     enum mailbox_log_record_type type,
1710 			     const guid_128_t mailbox_guid)
1711 {
1712 	struct mailbox_log_record rec;
1713 	time_t stamp;
1714 
1715 	if (!mailbox_list_init_changelog(list) ||
1716 	    guid_128_is_empty(mailbox_guid))
1717 		return;
1718 
1719 	if (mailbox_list_mkdir_missing_index_root(list) <= 0)
1720 		return;
1721 
1722 	stamp = list->changelog_timestamp != (time_t)-1 ?
1723 		list->changelog_timestamp : ioloop_time;
1724 
1725 	i_zero(&rec);
1726 	rec.type = type;
1727 	memcpy(rec.mailbox_guid, mailbox_guid, sizeof(rec.mailbox_guid));
1728 	mailbox_log_record_set_timestamp(&rec, stamp);
1729 	(void)mailbox_log_append(list->changelog, &rec);
1730 }
1731 
mailbox_list_set_subscribed(struct mailbox_list * list,const char * name,bool set)1732 int mailbox_list_set_subscribed(struct mailbox_list *list,
1733 				const char *name, bool set)
1734 {
1735 	int ret;
1736 
1737 	/* make sure we'll refresh the file on next list */
1738 	list->subscriptions_mtime = (time_t)-1;
1739 
1740 	if ((ret = list->v.set_subscribed(list, name, set)) <= 0)
1741 		return ret;
1742 	return 0;
1743 }
1744 
mailbox_list_delete_dir(struct mailbox_list * list,const char * name)1745 int mailbox_list_delete_dir(struct mailbox_list *list, const char *name)
1746 {
1747 	const char *error;
1748 
1749 	if (!mailbox_list_is_valid_name(list, name, &error) || *name == '\0') {
1750 		mailbox_list_set_error(list, MAIL_ERROR_PARAMS,
1751 				       "Invalid mailbox name");
1752 		return -1;
1753 	}
1754 	return list->v.delete_dir(list, name);
1755 }
1756 
mailbox_list_delete_symlink(struct mailbox_list * list,const char * name)1757 int mailbox_list_delete_symlink(struct mailbox_list *list, const char *name)
1758 {
1759 	const char *error;
1760 
1761 	if (!mailbox_list_is_valid_name(list, name, &error) || *name == '\0') {
1762 		mailbox_list_set_error(list, MAIL_ERROR_PARAMS,
1763 				       "Invalid mailbox name");
1764 		return -1;
1765 	}
1766 	return list->v.delete_symlink(list, name);
1767 }
1768 
mailbox_name_get_sha128(const char * name,guid_128_t guid_128_r)1769 void mailbox_name_get_sha128(const char *name, guid_128_t guid_128_r)
1770 {
1771 	unsigned char sha[SHA1_RESULTLEN];
1772 
1773 	sha1_get_digest(name, strlen(name), sha);
1774 	memcpy(guid_128_r, sha, I_MIN(GUID_128_SIZE, sizeof(sha)));
1775 }
1776 
mailbox_list_get_changelog(struct mailbox_list * list)1777 struct mailbox_log *mailbox_list_get_changelog(struct mailbox_list *list)
1778 {
1779 	return !mailbox_list_init_changelog(list) ? NULL : list->changelog;
1780 }
1781 
mailbox_list_set_changelog_timestamp(struct mailbox_list * list,time_t stamp)1782 void mailbox_list_set_changelog_timestamp(struct mailbox_list *list,
1783 					  time_t stamp)
1784 {
1785 	list->changelog_timestamp = stamp;
1786 }
1787 
1788 enum mailbox_list_file_type
mailbox_list_get_file_type(const struct dirent * d ATTR_UNUSED)1789 mailbox_list_get_file_type(const struct dirent *d ATTR_UNUSED)
1790 {
1791 	enum mailbox_list_file_type type;
1792 
1793 #ifdef HAVE_DIRENT_D_TYPE
1794 	switch (d->d_type) {
1795 	case DT_UNKNOWN:
1796 		type = MAILBOX_LIST_FILE_TYPE_UNKNOWN;
1797 		break;
1798 	case DT_REG:
1799 		type = MAILBOX_LIST_FILE_TYPE_FILE;
1800 		break;
1801 	case DT_DIR:
1802 		type = MAILBOX_LIST_FILE_TYPE_DIR;
1803 		break;
1804 	case DT_LNK:
1805 		type = MAILBOX_LIST_FILE_TYPE_SYMLINK;
1806 		break;
1807 	default:
1808 		type = MAILBOX_LIST_FILE_TYPE_OTHER;
1809 		break;
1810 	}
1811 #else
1812 	type = MAILBOX_LIST_FILE_TYPE_UNKNOWN;
1813 #endif
1814 	return type;
1815 }
1816 
mailbox_list_dirent_is_alias_symlink(struct mailbox_list * list,const char * dir_path,const struct dirent * d)1817 int mailbox_list_dirent_is_alias_symlink(struct mailbox_list *list,
1818 					 const char *dir_path,
1819 					 const struct dirent *d)
1820 {
1821 	struct stat st;
1822 	int ret;
1823 
1824 	if (mailbox_list_get_file_type(d) == MAILBOX_LIST_FILE_TYPE_SYMLINK)
1825 		return 1;
1826 
1827 	T_BEGIN {
1828 		const char *path, *linkpath, *error;
1829 
1830 		path = t_strconcat(dir_path, "/", d->d_name, NULL);
1831 		if (lstat(path, &st) < 0) {
1832 			mailbox_list_set_critical(list,
1833 						  "lstat(%s) failed: %m", path);
1834 			ret = -1;
1835 		} else if (!S_ISLNK(st.st_mode)) {
1836 			ret = 0;
1837 		} else if (t_readlink(path, &linkpath, &error) < 0) {
1838 			e_error(list->ns->user->event,
1839 				"t_readlink(%s) failed: %s", path, error);
1840 			ret = -1;
1841 		} else {
1842 			/* it's an alias only if it points to the same
1843 			   directory */
1844 			ret = strchr(linkpath, '/') == NULL ? 1 : 0;
1845 		}
1846 	} T_END;
1847 	return ret;
1848 }
1849 
1850 
1851 static bool
mailbox_list_try_get_home_path(struct mailbox_list * list,const char ** name)1852 mailbox_list_try_get_home_path(struct mailbox_list *list, const char **name)
1853 {
1854 	if ((*name)[1] == '/') {
1855 		/* ~/dir - use the configured home directory */
1856 		if (mail_user_try_home_expand(list->ns->user, name) < 0)
1857 			return FALSE;
1858 	} else {
1859 		/* ~otheruser/dir - assume we're using system users */
1860 		if (home_try_expand(name) < 0)
1861 			return FALSE;
1862 	}
1863 	return TRUE;
1864 }
1865 
mailbox_list_try_get_absolute_path(struct mailbox_list * list,const char ** name)1866 bool mailbox_list_try_get_absolute_path(struct mailbox_list *list,
1867 					const char **name)
1868 {
1869 	const char *root_dir, *path, *mailbox_name;
1870 	size_t len;
1871 
1872 	if (!list->mail_set->mail_full_filesystem_access)
1873 		return FALSE;
1874 
1875 	if (**name == '~') {
1876 		/* try to expand home directory */
1877 		if (!mailbox_list_try_get_home_path(list, name)) {
1878 			/* fallback to using actual "~name" mailbox */
1879 			return FALSE;
1880 		}
1881 	} else {
1882 		if (**name != '/')
1883 			return FALSE;
1884 	}
1885 
1886 	/* okay, we have an absolute path now. but check first if it points to
1887 	   same directory as one of our regular mailboxes. */
1888 	root_dir = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_MAILBOX);
1889 	len = strlen(root_dir);
1890 	if (str_begins(*name, root_dir) && (*name)[len] == '/') {
1891 		mailbox_name = *name + len + 1;
1892 		if (mailbox_list_get_path(list, mailbox_name,
1893 					  MAILBOX_LIST_PATH_TYPE_MAILBOX,
1894 					  &path) <= 0)
1895 			return FALSE;
1896 		if (strcmp(path, *name) == 0) {
1897 			/* yeah, we can replace the full path with mailbox
1898 			   name. this way we can use indexes. */
1899 			*name = mailbox_name;
1900 			return FALSE;
1901 		}
1902 	}
1903 	return TRUE;
1904 }
1905 
mailbox_list_get_last_error(struct mailbox_list * list,enum mail_error * error_r)1906 const char *mailbox_list_get_last_error(struct mailbox_list *list,
1907 					enum mail_error *error_r)
1908 {
1909 	if (list->error == MAIL_ERROR_NONE) {
1910 		if (error_r != NULL)
1911 			*error_r = MAIL_ERROR_TEMP;
1912 		return list->error_string != NULL ? list->error_string :
1913 			"BUG: Unknown internal list error";
1914 	}
1915 
1916 	if (list->error_string == NULL) {
1917 		/* This shouldn't happen.. */
1918 		list->error_string =
1919 			i_strdup_printf("BUG: Unknown 0x%x list error",
1920 					list->error);
1921 	}
1922 
1923 	if (error_r != NULL)
1924 		*error_r = list->error;
1925 	return list->error_string;
1926 }
1927 
mailbox_list_get_last_mail_error(struct mailbox_list * list)1928 enum mail_error mailbox_list_get_last_mail_error(struct mailbox_list *list)
1929 {
1930 	return list->error;
1931 }
1932 
mailbox_list_get_last_internal_error(struct mailbox_list * list,enum mail_error * error_r)1933 const char *mailbox_list_get_last_internal_error(struct mailbox_list *list,
1934 						 enum mail_error *error_r)
1935 {
1936 	if (error_r != NULL)
1937 		*error_r = list->error;
1938 	if (list->last_error_is_internal) {
1939 		i_assert(list->last_internal_error != NULL);
1940 		return list->last_internal_error;
1941 	}
1942 	return mailbox_list_get_last_error(list, error_r);
1943 }
1944 
mailbox_list_clear_error(struct mailbox_list * list)1945 void mailbox_list_clear_error(struct mailbox_list *list)
1946 {
1947 	i_free_and_null(list->error_string);
1948 
1949 	i_free(list->last_internal_error);
1950 	list->last_error_is_internal = FALSE;
1951 	list->error = MAIL_ERROR_NONE;
1952 }
1953 
mailbox_list_set_error(struct mailbox_list * list,enum mail_error error,const char * string)1954 void mailbox_list_set_error(struct mailbox_list *list,
1955 			    enum mail_error error, const char *string)
1956 {
1957 	if (list->error_string != string) {
1958 		i_free(list->error_string);
1959 		list->error_string = i_strdup(string);
1960 	}
1961 
1962 	list->last_error_is_internal = FALSE;
1963 	list->error = error;
1964 }
1965 
mailbox_list_set_internal_error(struct mailbox_list * list)1966 void mailbox_list_set_internal_error(struct mailbox_list *list)
1967 {
1968 	const char *str;
1969 
1970 	str = t_strflocaltime(MAIL_ERRSTR_CRITICAL_MSG_STAMP, ioloop_time);
1971 	i_free(list->error_string);
1972 	list->error_string = i_strdup(str);
1973 	list->error = MAIL_ERROR_TEMP;
1974 
1975 	/* this function doesn't set last_internal_error, so
1976 	   last_error_is_internal can't be TRUE. */
1977 	list->last_error_is_internal = FALSE;
1978 	i_free(list->last_internal_error);
1979 }
1980 
mailbox_list_set_critical(struct mailbox_list * list,const char * fmt,...)1981 void mailbox_list_set_critical(struct mailbox_list *list, const char *fmt, ...)
1982 {
1983 	char *old_error = list->error_string;
1984 	char *old_internal_error = list->last_internal_error;
1985 	va_list va;
1986 
1987 	list->error_string = NULL;
1988 	list->last_internal_error = NULL;
1989 	/* critical errors may contain sensitive data, so let user
1990 	   see only "Internal error" with a timestamp to make it
1991 	   easier to look from log files the actual error message. */
1992 	mailbox_list_set_internal_error(list);
1993 
1994 	va_start(va, fmt);
1995 	list->last_internal_error = i_strdup_vprintf(fmt, va);
1996 	va_end(va);
1997 	list->last_error_is_internal = TRUE;
1998 	e_error(list->ns->user->event, "%s", list->last_internal_error);
1999 
2000 	/* free the old_error and old_internal_error only after the new error
2001 	   is generated, because they may be one of the parameters. */
2002 	i_free(old_error);
2003 	i_free(old_internal_error);
2004 }
2005 
mailbox_list_set_error_from_errno(struct mailbox_list * list)2006 bool mailbox_list_set_error_from_errno(struct mailbox_list *list)
2007 {
2008 	const char *error_string;
2009 	enum mail_error error;
2010 
2011 	if (!mail_error_from_errno(&error, &error_string))
2012 		return FALSE;
2013 
2014 	mailbox_list_set_error(list, error, error_string);
2015 	return TRUE;
2016 }
2017 
mailbox_list_last_error_push(struct mailbox_list * list)2018 void mailbox_list_last_error_push(struct mailbox_list *list)
2019 {
2020 	struct mail_storage_error *err;
2021 
2022 	if (!array_is_created(&list->error_stack))
2023 		i_array_init(&list->error_stack, 2);
2024 	err = array_append_space(&list->error_stack);
2025 	err->error_string = i_strdup(list->error_string);
2026 	err->error = list->error;
2027 	err->last_error_is_internal = list->last_error_is_internal;
2028 	if (err->last_error_is_internal)
2029 		err->last_internal_error = i_strdup(list->last_internal_error);
2030 }
2031 
mailbox_list_last_error_pop(struct mailbox_list * list)2032 void mailbox_list_last_error_pop(struct mailbox_list *list)
2033 {
2034 	unsigned int count = array_count(&list->error_stack);
2035 	const struct mail_storage_error *err =
2036 		array_idx(&list->error_stack, count-1);
2037 
2038 	i_free(list->error_string);
2039 	i_free(list->last_internal_error);
2040 	list->error_string = err->error_string;
2041 	list->error = err->error;
2042 	list->last_error_is_internal = err->last_error_is_internal;
2043 	list->last_internal_error = err->last_internal_error;
2044 	array_delete(&list->error_stack, count-1, 1);
2045 }
2046 
mailbox_list_init_fs(struct mailbox_list * list,struct event * event_parent,const char * driver,const char * args,const char * root_dir,struct fs ** fs_r,const char ** error_r)2047 int mailbox_list_init_fs(struct mailbox_list *list, struct event *event_parent,
2048 			 const char *driver,
2049 			 const char *args, const char *root_dir,
2050 			 struct fs **fs_r, const char **error_r)
2051 {
2052 	struct fs_settings fs_set;
2053 	struct ssl_iostream_settings ssl_set;
2054 	struct mailbox_list_fs_context *ctx;
2055 	struct fs *parent_fs;
2056 
2057 	i_zero(&fs_set);
2058 	mail_user_init_fs_settings(list->ns->user, &fs_set, &ssl_set);
2059 	/* fs_set.event_parent points to user->event by default */
2060 	if (event_parent != NULL)
2061 		fs_set.event_parent = event_parent;
2062 	fs_set.root_path = root_dir;
2063 	fs_set.temp_file_prefix = mailbox_list_get_global_temp_prefix(list);
2064 
2065 	if (fs_init(driver, args, &fs_set, fs_r, error_r) < 0)
2066 		return -1;
2067 
2068 	/* add mailbox_list context to the parent fs, which allows
2069 	   mailbox_list_fs_get_list() to work */
2070 	for (parent_fs = *fs_r; parent_fs->parent != NULL;
2071 	     parent_fs = parent_fs->parent) ;
2072 
2073 	ctx = p_new(list->pool, struct mailbox_list_fs_context, 1);
2074 	ctx->list = list;
2075 	MODULE_CONTEXT_SET(parent_fs, mailbox_list_fs_module, ctx);
2076 
2077 	/* a bit kludgy notification to the fs that we're now finished setting
2078 	   up the module context. */
2079 	(void)fs_get_properties(*fs_r);
2080 	return 0;
2081 }
2082 
mailbox_list_fs_get_list(struct fs * fs)2083 struct mailbox_list *mailbox_list_fs_get_list(struct fs *fs)
2084 {
2085 	struct mailbox_list_fs_context *ctx;
2086 
2087 	while (fs->parent != NULL)
2088 		fs = fs->parent;
2089 
2090 	ctx = MAILBOX_LIST_FS_CONTEXT(fs);
2091 	return ctx == NULL ? NULL : ctx->list;
2092 }
2093 
mailbox_list_lock(struct mailbox_list * list)2094 int mailbox_list_lock(struct mailbox_list *list)
2095 {
2096 	struct mailbox_permissions perm;
2097 	struct file_create_settings set;
2098 	const char *lock_dir, *lock_fname, *lock_path, *error;
2099 
2100 	if (list->lock_refcount > 0) {
2101 		list->lock_refcount++;
2102 		return 0;
2103 	}
2104 
2105 	mailbox_list_get_root_permissions(list, &perm);
2106 	i_zero(&set);
2107 	set.lock_timeout_secs = list->mail_set->mail_max_lock_timeout == 0 ?
2108 		MAILBOX_LIST_LOCK_SECS :
2109 		I_MIN(MAILBOX_LIST_LOCK_SECS, list->mail_set->mail_max_lock_timeout);
2110 	set.lock_settings.lock_method = list->mail_set->parsed_lock_method;
2111 	set.mode = perm.file_create_mode;
2112 	set.gid = perm.file_create_gid;
2113 	set.gid_origin = perm.file_create_gid_origin;
2114 
2115 	lock_fname = MAILBOX_LIST_LOCK_FNAME;
2116 	if (list->set.volatile_dir != NULL) {
2117 		/* Use VOLATILEDIR. It's shared with all mailbox_lists, so use
2118 		   hash of the namespace prefix as a way to make this lock name
2119 		   unique across the namespaces. */
2120 		unsigned char ns_prefix_hash[SHA1_RESULTLEN];
2121 		sha1_get_digest(list->ns->prefix, list->ns->prefix_len,
2122 				ns_prefix_hash);
2123 		lock_fname = t_strconcat(MAILBOX_LIST_LOCK_FNAME,
2124 			binary_to_hex(ns_prefix_hash, sizeof(ns_prefix_hash)), NULL);
2125 		lock_dir = list->set.volatile_dir;
2126 		set.mkdir_mode = 0700;
2127 	} else if (mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_INDEX,
2128 					      &lock_dir)) {
2129 		/* use index root directory */
2130 		if (mailbox_list_mkdir_missing_index_root(list) < 0)
2131 			return -1;
2132 	} else if (mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_DIR,
2133 					      &lock_dir)) {
2134 		/* use mailbox root directory */
2135 		if (mailbox_list_mkdir_root(list, lock_dir,
2136 					    MAILBOX_LIST_PATH_TYPE_DIR) < 0)
2137 			return -1;
2138 	} else {
2139 		/* No filesystem used by mailbox list (e.g. imapc).
2140 		   Just assume it's locked */
2141 		list->lock_refcount = 1;
2142 		return 0;
2143 	}
2144 	lock_path = t_strdup_printf("%s/%s", lock_dir, lock_fname);
2145 	if (mail_storage_lock_create(lock_path, &set, list->mail_set,
2146 				     &list->lock, &error) <= 0) {
2147 		mailbox_list_set_critical(list,
2148 			"Couldn't create mailbox list lock %s: %s",
2149 			lock_path, error);
2150 		return -1;
2151 	}
2152 
2153 	list->lock_refcount = 1;
2154 	return 0;
2155 }
2156 
mailbox_list_unlock(struct mailbox_list * list)2157 void mailbox_list_unlock(struct mailbox_list *list)
2158 {
2159 	i_assert(list->lock_refcount > 0);
2160 	if (--list->lock_refcount > 0)
2161 		return;
2162 
2163 	file_lock_free(&list->lock);
2164 }
2165