1 /* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "array.h"
5 #include "str.h"
6 #include "file-lock.h"
7 #include "settings-parser.h"
8 #include "mailbox-list-private.h"
9 #include "mail-storage-private.h"
10 #include "mail-storage-settings.h"
11 #include "mail-namespace.h"
12 
13 
14 static struct mail_namespace_settings prefixless_ns_unexpanded_set = {
15 	.name = "",
16 	.type = "private",
17 	.separator = "",
18 	.prefix = "0",
19 	.location = "0fail::LAYOUT=none",
20 	.alias_for = NULL,
21 
22 	.inbox = FALSE,
23 	.hidden = TRUE,
24 	.list = "no",
25 	.subscriptions = FALSE,
26 	.ignore_on_failure = FALSE,
27 	.disabled = FALSE,
28 
29 	.mailboxes = ARRAY_INIT
30 };
31 static struct mail_namespace_settings prefixless_ns_set;
32 
mail_namespace_add_storage(struct mail_namespace * ns,struct mail_storage * storage)33 void mail_namespace_add_storage(struct mail_namespace *ns,
34 				struct mail_storage *storage)
35 {
36 	if (ns->storage == NULL)
37 		ns->storage = storage;
38 	array_push_back(&ns->all_storages, &storage);
39 
40 	if (storage->v.add_list != NULL)
41 		storage->v.add_list(storage, ns->list);
42 	hook_mail_namespace_storage_added(ns);
43 }
44 
mail_namespace_finish_list_init(struct mail_namespace * ns,struct mailbox_list * list)45 void mail_namespace_finish_list_init(struct mail_namespace *ns,
46 				     struct mailbox_list *list)
47 {
48 	ns->list = list;
49 	ns->prefix_len = strlen(ns->prefix);
50 }
51 
mail_namespace_free(struct mail_namespace * ns)52 static void mail_namespace_free(struct mail_namespace *ns)
53 {
54 	struct mail_storage *storage;
55 
56 	if (array_is_created(&ns->all_storages)) {
57 		array_foreach_elem(&ns->all_storages, storage)
58 			mail_storage_unref(&storage);
59 		array_free(&ns->all_storages);
60 	}
61 	if (ns->list != NULL)
62 		mailbox_list_destroy(&ns->list);
63 
64 	if (ns->owner != ns->user && ns->owner != NULL)
65 		mail_user_unref(&ns->owner);
66 	i_free(ns->prefix);
67 	i_free(ns);
68 }
69 
70 static bool
namespace_has_special_use_mailboxes(struct mail_namespace_settings * ns_set)71 namespace_has_special_use_mailboxes(struct mail_namespace_settings *ns_set)
72 {
73 	struct mailbox_settings *box_set;
74 
75 	if (!array_is_created(&ns_set->mailboxes))
76 		return FALSE;
77 
78 	array_foreach_elem(&ns_set->mailboxes, box_set) {
79 		if (box_set->special_use[0] != '\0')
80 			return TRUE;
81 	}
82 	return FALSE;
83 }
84 
mail_namespace_alloc(struct mail_user * user,void * user_all_settings,struct mail_namespace_settings * ns_set,struct mail_namespace_settings * unexpanded_set,struct mail_namespace ** ns_r,const char ** error_r)85 int mail_namespace_alloc(struct mail_user *user,
86 			 void *user_all_settings,
87 			 struct mail_namespace_settings *ns_set,
88 			 struct mail_namespace_settings *unexpanded_set,
89 			 struct mail_namespace **ns_r,
90 			 const char **error_r)
91 {
92 	struct mail_namespace *ns;
93 
94 	ns = i_new(struct mail_namespace, 1);
95 	ns->refcount = 1;
96 	ns->user = user;
97 	ns->prefix = i_strdup(ns_set->prefix);
98 	ns->set = ns_set;
99 	ns->unexpanded_set = unexpanded_set;
100 	ns->user_set = user_all_settings;
101 	ns->mail_set = mail_user_set_get_driver_settings(user->set_info,
102 				ns->user_set, MAIL_STORAGE_SET_DRIVER_NAME);
103 	i_array_init(&ns->all_storages, 2);
104 
105 	if (strcmp(ns_set->type, "private") == 0) {
106 		ns->owner = user;
107 		ns->type = MAIL_NAMESPACE_TYPE_PRIVATE;
108 	} else if (strcmp(ns_set->type, "shared") == 0)
109 		ns->type = MAIL_NAMESPACE_TYPE_SHARED;
110 	else if (strcmp(ns_set->type, "public") == 0)
111 		ns->type = MAIL_NAMESPACE_TYPE_PUBLIC;
112 	else {
113 		*error_r = t_strdup_printf("Unknown namespace type: %s",
114 					   ns_set->type);
115 		mail_namespace_free(ns);
116 		return -1;
117 	}
118 
119 	if (strcmp(ns_set->list, "children") == 0)
120 		ns->flags |= NAMESPACE_FLAG_LIST_CHILDREN;
121 	else if (strcmp(ns_set->list, "yes") == 0)
122 		ns->flags |= NAMESPACE_FLAG_LIST_PREFIX;
123 	else if (strcmp(ns_set->list, "no") != 0) {
124 		*error_r = t_strdup_printf("Invalid list setting value: %s",
125 					   ns_set->list);
126 		mail_namespace_free(ns);
127 		return -1;
128 	}
129 
130 	if (ns_set->inbox) {
131 		ns->flags |= NAMESPACE_FLAG_INBOX_USER |
132 			NAMESPACE_FLAG_INBOX_ANY;
133 	}
134 	if (ns_set->hidden)
135 		ns->flags |= NAMESPACE_FLAG_HIDDEN;
136 	if (ns_set->subscriptions)
137 		ns->flags |= NAMESPACE_FLAG_SUBSCRIPTIONS;
138 
139 	*ns_r = ns;
140 
141 	return 0;
142 }
143 
mail_namespaces_init_add(struct mail_user * user,struct mail_namespace_settings * ns_set,struct mail_namespace_settings * unexpanded_ns_set,struct mail_namespace ** ns_p,const char ** error_r)144 int mail_namespaces_init_add(struct mail_user *user,
145 			     struct mail_namespace_settings *ns_set,
146 			     struct mail_namespace_settings *unexpanded_ns_set,
147 			     struct mail_namespace **ns_p, const char **error_r)
148 {
149 	const struct mail_storage_settings *mail_set =
150 		mail_user_set_get_storage_set(user);
151 	struct mail_namespace *ns;
152 	const char *driver, *error;
153 	int ret;
154 
155 	if (*ns_set->location == '\0')
156 		ns_set->location = mail_set->mail_location;
157 
158 	e_debug(user->event, "Namespace %s: type=%s, prefix=%s, sep=%s, "
159 		"inbox=%s, hidden=%s, list=%s, subscriptions=%s "
160 		"location=%s",
161 		ns_set->name, ns_set->type, ns_set->prefix,
162 		ns_set->separator == NULL ? "" : ns_set->separator,
163 		ns_set->inbox ? "yes" : "no",
164 		ns_set->hidden ? "yes" : "no",
165 		ns_set->list,
166 		ns_set->subscriptions ? "yes" : "no", ns_set->location);
167 
168 	if ((ret = mail_namespace_alloc(user, user->set,
169 					ns_set, unexpanded_ns_set,
170 					&ns, error_r)) < 0)
171 		return ret;
172 
173 	if (ns_set == &prefixless_ns_set) {
174 		/* autocreated prefix="" namespace */
175 		ns->flags |= NAMESPACE_FLAG_UNUSABLE |
176 			NAMESPACE_FLAG_AUTOCREATED;
177 	}
178 
179 	ns->special_use_mailboxes = namespace_has_special_use_mailboxes(ns_set);
180 
181 	if (ns->type == MAIL_NAMESPACE_TYPE_SHARED &&
182 	    (strchr(ns->prefix, '%') != NULL ||
183 	     strchr(ns->set->location, '%') != NULL)) {
184 		/* dynamic shared namespace. the above check catches wrong
185 		   mixed %% usage, but still allows for specifying a shared
186 		   namespace to an explicit location without any %% */
187 		ns->flags |= NAMESPACE_FLAG_NOQUOTA | NAMESPACE_FLAG_NOACL;
188 		driver = MAIL_SHARED_STORAGE_NAME;
189 	} else {
190 		driver = NULL;
191 	}
192 
193 	if (mail_storage_create(ns, driver, 0, &error) < 0) {
194 		*error_r = t_strdup_printf("Namespace '%s': %s",
195 					   ns->prefix, error);
196 		mail_namespace_free(ns);
197 		return -1;
198 	}
199 
200 	*ns_p = ns;
201 	return 0;
202 }
203 
namespace_is_valid_alias_storage(struct mail_namespace * ns,const char ** error_r)204 static bool namespace_is_valid_alias_storage(struct mail_namespace *ns,
205 					     const char **error_r)
206 {
207 	if (strcmp(ns->storage->name, ns->alias_for->storage->name) != 0) {
208 		*error_r = t_strdup_printf(
209 			"Namespace %s can't have alias_for=%s "
210 			"to a different storage type (%s vs %s)",
211 			ns->prefix, ns->alias_for->prefix,
212 			ns->storage->name, ns->alias_for->storage->name);
213 		return FALSE;
214 	}
215 
216 	if ((ns->storage->class_flags & MAIL_STORAGE_CLASS_FLAG_UNIQUE_ROOT) != 0 &&
217 	    ns->storage != ns->alias_for->storage) {
218 		*error_r = t_strdup_printf(
219 			"Namespace %s can't have alias_for=%s "
220 			"to a different storage (different root dirs)",
221 			ns->prefix, ns->alias_for->prefix);
222 		return FALSE;
223 	}
224 	return TRUE;
225 }
226 
227 static int
namespace_set_alias_for(struct mail_namespace * ns,struct mail_namespace * all_namespaces,const char ** error_r)228 namespace_set_alias_for(struct mail_namespace *ns,
229 			struct mail_namespace *all_namespaces,
230 			const char **error_r)
231 {
232 	if (ns->set->alias_for != NULL) {
233 		ns->alias_for = mail_namespace_find_prefix(all_namespaces,
234 							   ns->set->alias_for);
235 		if (ns->alias_for == NULL) {
236 			*error_r = t_strdup_printf("Invalid namespace alias_for: %s",
237 						   ns->set->alias_for);
238 			return -1;
239 		}
240 		if (ns->alias_for->alias_for != NULL) {
241 			*error_r = t_strdup_printf("Chained namespace alias_for: %s",
242 						   ns->set->alias_for);
243 			return -1;
244 		}
245 		if (!namespace_is_valid_alias_storage(ns, error_r))
246 			return -1;
247 
248 		if ((ns->alias_for->flags & NAMESPACE_FLAG_INBOX_USER) != 0) {
249 			/* copy inbox=yes */
250 			ns->flags |= NAMESPACE_FLAG_INBOX_USER;
251 		}
252 
253 		ns->alias_chain_next = ns->alias_for->alias_chain_next;
254 		ns->alias_for->alias_chain_next = ns;
255 	}
256 	return 0;
257 }
258 
get_listindex_path(struct mail_namespace * ns,const char ** path_r)259 static bool get_listindex_path(struct mail_namespace *ns, const char **path_r)
260 {
261 	const char *root;
262 
263 	if (ns->list->set.list_index_fname[0] == '\0' ||
264 	    !mailbox_list_get_root_path(ns->list,
265 					MAILBOX_LIST_PATH_TYPE_LIST_INDEX,
266 					&root))
267 		return FALSE;
268 
269 	*path_r = t_strconcat(root, "/", ns->list->set.list_index_fname, NULL);
270 	return TRUE;
271 }
272 
273 static bool
namespace_has_duplicate_listindex(struct mail_namespace * ns,const char ** error_r)274 namespace_has_duplicate_listindex(struct mail_namespace *ns,
275 				  const char **error_r)
276 {
277 	struct mail_namespace *ns2;
278 	const char *ns_list_index_path, *ns_mailboxes_root;
279 	const char *ns2_list_index_path, *ns2_mailboxes_root;
280 
281 	if (!ns->mail_set->mailbox_list_index) {
282 		/* mailbox list indexes not in use */
283 		return FALSE;
284 	}
285 
286 	if (!get_listindex_path(ns, &ns_list_index_path) ||
287 	    !mailbox_list_get_root_path(ns->list, MAILBOX_LIST_PATH_TYPE_MAILBOX,
288 					&ns_mailboxes_root))
289 		return FALSE;
290 
291 	for (ns2 = ns->next; ns2 != NULL; ns2 = ns2->next) {
292 		if (!get_listindex_path(ns2, &ns2_list_index_path) ||
293 		    !mailbox_list_get_root_path(ns2->list, MAILBOX_LIST_PATH_TYPE_MAILBOX,
294 						&ns2_mailboxes_root))
295 			continue;
296 
297 		if (strcmp(ns_list_index_path, ns2_list_index_path) == 0 &&
298 		    strcmp(ns_mailboxes_root, ns2_mailboxes_root) != 0) {
299 			*error_r = t_strdup_printf(
300 				"Namespaces '%s' and '%s' have different mailboxes paths, but duplicate LISTINDEX path. "
301 				"Add a unique LISTINDEX=<fname>",
302 				ns->prefix, ns2->prefix);
303 			return TRUE;
304 		}
305 	}
306 	return FALSE;
307 }
308 
309 static bool
namespaces_check(struct mail_namespace * namespaces,const char ** error_r)310 namespaces_check(struct mail_namespace *namespaces, const char **error_r)
311 {
312 	struct mail_namespace *ns, *inbox_ns = NULL;
313 	unsigned int subscriptions_count = 0;
314 	bool visible_namespaces = FALSE, have_list_yes = FALSE;
315 	char ns_sep, list_sep = '\0';
316 
317 	for (ns = namespaces; ns != NULL; ns = ns->next) {
318 		ns_sep = mail_namespace_get_sep(ns);
319 		if (mail_namespace_find_prefix(ns->next, ns->prefix) != NULL) {
320 			*error_r = t_strdup_printf(
321 				"Duplicate namespace prefix: \"%s\"",
322 				ns->prefix);
323 			return FALSE;
324 		}
325 		if ((ns->flags & NAMESPACE_FLAG_HIDDEN) == 0)
326 			visible_namespaces = TRUE;
327 		/* check the inbox=yes status before alias_for changes it */
328 		if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) {
329 			if (inbox_ns != NULL) {
330 				*error_r = "There can be only one namespace with "
331 					"inbox=yes";
332 				return FALSE;
333 			}
334 			inbox_ns = ns;
335 		}
336 		if (namespace_set_alias_for(ns, namespaces, error_r) < 0)
337 			return FALSE;
338 		if (namespace_has_duplicate_listindex(ns, error_r))
339 			return FALSE;
340 
341 		if (*ns->prefix != '\0' &&
342 		    (ns->flags & (NAMESPACE_FLAG_LIST_PREFIX |
343 				  NAMESPACE_FLAG_LIST_CHILDREN)) != 0 &&
344 		    ns->prefix[strlen(ns->prefix)-1] != ns_sep) {
345 			*error_r = t_strdup_printf(
346 				"list=yes requires prefix=%s "
347 				"to end with separator %c", ns->prefix, ns_sep);
348 			return FALSE;
349 		}
350 		if (*ns->prefix != '\0' &&
351 		    (ns->flags & (NAMESPACE_FLAG_LIST_PREFIX |
352 				  NAMESPACE_FLAG_LIST_CHILDREN)) != 0 &&
353 		    ns->prefix[0] == ns_sep) {
354 			*error_r = t_strdup_printf(
355 				"list=yes requires prefix=%s "
356 				"not to start with separator", ns->prefix);
357 			return FALSE;
358 		}
359 		if ((ns->flags & (NAMESPACE_FLAG_LIST_PREFIX |
360 				  NAMESPACE_FLAG_LIST_CHILDREN)) != 0) {
361 			if ((ns->flags & NAMESPACE_FLAG_LIST_PREFIX) != 0)
362 				have_list_yes = TRUE;
363 			if (list_sep == '\0')
364 				list_sep = ns_sep;
365 			else if (list_sep != ns_sep) {
366 				*error_r = "All list=yes namespaces must use "
367 					"the same separator";
368 				return FALSE;
369 			}
370 		}
371 		if ((ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) != 0)
372 			subscriptions_count++;
373 	}
374 
375 	if (inbox_ns == NULL) {
376 		*error_r = "inbox=yes namespace missing";
377 		return FALSE;
378 	}
379 	if (!have_list_yes) {
380 		*error_r = "list=yes namespace missing";
381 		return FALSE;
382 	}
383 	if (!visible_namespaces) {
384 		*error_r = "hidden=no namespace missing";
385 		return FALSE;
386 	}
387 	if (subscriptions_count == 0) {
388 		*error_r = "subscriptions=yes namespace missing";
389 		return FALSE;
390 	}
391 	return TRUE;
392 }
393 
mail_namespaces_init_finish(struct mail_namespace * namespaces,const char ** error_r)394 int mail_namespaces_init_finish(struct mail_namespace *namespaces,
395 				const char **error_r)
396 {
397 	struct mail_namespace *ns;
398 	bool prefixless_found = FALSE;
399 
400 	i_assert(namespaces != NULL);
401 
402 	for (ns = namespaces; ns != NULL; ns = ns->next) {
403 		if (ns->prefix_len == 0)
404 			prefixless_found = TRUE;
405 	}
406 	if (!prefixless_found) {
407 		prefixless_ns_set = prefixless_ns_unexpanded_set;
408 		/* a pretty evil way to expand the values */
409 		prefixless_ns_set.prefix++;
410 		prefixless_ns_set.location++;
411 
412 		if (mail_namespaces_init_add(namespaces->user,
413 					     &prefixless_ns_set,
414 					     &prefixless_ns_unexpanded_set,
415 					     &ns, error_r) < 0)
416 			i_unreached();
417 		ns->next = namespaces;
418 		namespaces = ns;
419 	}
420 	if (namespaces->user->autocreated) {
421 		/* e.g. raw user - don't check namespaces' validity */
422 	} else if (!namespaces_check(namespaces, error_r)) {
423 		namespaces->user->error =
424 			t_strconcat("namespace configuration error: ",
425 				    *error_r, NULL);
426 	}
427 
428 	if (namespaces->user->error == NULL) {
429 		mail_user_add_namespace(namespaces->user, &namespaces);
430 		T_BEGIN {
431 			hook_mail_namespaces_created(namespaces);
432 		} T_END;
433 	}
434 
435 	/* allow namespace hooks to return failure via the user error */
436 	if (namespaces->user->error != NULL) {
437 		namespaces->user->namespaces = NULL;
438 		*error_r = t_strdup(namespaces->user->error);
439 		while (namespaces != NULL) {
440 			ns = namespaces;
441 			namespaces = ns->next;
442 			mail_namespace_free(ns);
443 		}
444 		return -1;
445 	}
446 
447 	namespaces->user->namespaces_created = TRUE;
448 	return 0;
449 }
450 
mail_namespaces_init(struct mail_user * user,const char ** error_r)451 int mail_namespaces_init(struct mail_user *user, const char **error_r)
452 {
453 	struct mail_namespace_settings *const *ns_set;
454 	struct mail_namespace_settings *const *unexpanded_ns_set;
455 	struct mail_namespace *namespaces, **ns_p;
456 	unsigned int i, count, count2;
457 
458 	i_assert(user->initialized);
459 
460         namespaces = NULL; ns_p = &namespaces;
461 
462 	if (array_is_created(&user->set->namespaces)) {
463 		ns_set = array_get(&user->set->namespaces, &count);
464 		unexpanded_ns_set =
465 			array_get(&user->unexpanded_set->namespaces, &count2);
466 		i_assert(count == count2);
467 	} else {
468 		ns_set = unexpanded_ns_set = NULL;
469 		count = 0;
470 	}
471 	for (i = 0; i < count; i++) {
472 		if (ns_set[i]->disabled)
473 			continue;
474 
475 		if (mail_namespaces_init_add(user, ns_set[i],
476 					     unexpanded_ns_set[i],
477 					     ns_p, error_r) < 0) {
478 			if (!ns_set[i]->ignore_on_failure) {
479 				mail_namespaces_deinit(&namespaces);
480 				return -1;
481 			}
482 			e_debug(user->event, "Skipping namespace %s: %s",
483 				ns_set[i]->prefix, *error_r);
484 		} else {
485 			ns_p = &(*ns_p)->next;
486 		}
487 	}
488 
489 	if (namespaces == NULL) {
490 		/* no namespaces defined, create a default one */
491 		return mail_namespaces_init_location(user, NULL, error_r);
492 	}
493 	return mail_namespaces_init_finish(namespaces, error_r);
494 }
495 
mail_namespaces_init_location(struct mail_user * user,const char * location,const char ** error_r)496 int mail_namespaces_init_location(struct mail_user *user, const char *location,
497 				  const char **error_r)
498 {
499 	struct mail_namespace_settings *inbox_set, *unexpanded_inbox_set;
500 	struct mail_namespace *ns;
501 	const struct mail_storage_settings *mail_set;
502 	const char *error, *driver, *location_source;
503 	bool default_location = FALSE;
504 	int ret;
505 
506 	i_assert(location == NULL || *location != '\0');
507 
508 	inbox_set = p_new(user->pool, struct mail_namespace_settings, 1);
509 	*inbox_set = mail_namespace_default_settings;
510 	inbox_set->inbox = TRUE;
511 	/* enums must be changed */
512 	inbox_set->type = "private";
513 	inbox_set->list = "yes";
514 
515 	unexpanded_inbox_set = p_new(user->pool, struct mail_namespace_settings, 1);
516 	*unexpanded_inbox_set = *inbox_set;
517 
518 	driver = NULL;
519 	mail_set = mail_user_set_get_storage_set(user);
520 	if (location != NULL) {
521 		inbox_set->location = p_strdup(user->pool, location);
522 		location_source = "mail_location parameter";
523 	} else if (*mail_set->mail_location != '\0') {
524 		location_source = "mail_location setting";
525 		inbox_set->location = mail_set->mail_location;
526 		default_location = TRUE;
527 	} else {
528 		location_source = "environment MAIL";
529 		inbox_set->location = getenv("MAIL");
530 	}
531 	if (inbox_set->location == NULL) {
532 		/* support also maildir-specific environment */
533 		inbox_set->location = getenv("MAILDIR");
534 		if (inbox_set->location == NULL)
535 			inbox_set->location = "";
536 		else {
537 			driver = "maildir";
538 			location_source = "environment MAILDIR";
539 		}
540 	}
541 	if (default_location) {
542 		/* treat this the same as if a namespace was created with
543 		   default settings. dsync relies on finding a namespace
544 		   without explicit location setting. */
545 		unexpanded_inbox_set->location = SETTING_STRVAR_UNEXPANDED;
546 	} else {
547 		unexpanded_inbox_set->location =
548 			p_strconcat(user->pool, SETTING_STRVAR_EXPANDED,
549 				    inbox_set->location, NULL);
550 	}
551 
552 	if ((ret = mail_namespace_alloc(user, user->set,
553 					inbox_set, unexpanded_inbox_set,
554 					&ns, error_r)) < 0)
555 		return ret;
556 
557 	if (mail_storage_create(ns, driver, 0, &error) < 0) {
558 		if (*inbox_set->location != '\0') {
559 			*error_r = t_strdup_printf(
560 				"Initializing mail storage from %s "
561 				"failed: %s", location_source, error);
562 		} else {
563 			*error_r = t_strdup_printf("mail_location not set and "
564 					"autodetection failed: %s", error);
565 		}
566 		mail_namespace_free(ns);
567 		return -1;
568 	}
569 	return mail_namespaces_init_finish(ns, error_r);
570 }
571 
mail_namespaces_init_empty(struct mail_user * user)572 struct mail_namespace *mail_namespaces_init_empty(struct mail_user *user)
573 {
574 	struct mail_namespace *ns;
575 
576 	ns = i_new(struct mail_namespace, 1);
577 	ns->refcount = 1;
578 	ns->user = user;
579 	ns->owner = user;
580 	ns->prefix = i_strdup("");
581 	ns->flags = NAMESPACE_FLAG_INBOX_USER | NAMESPACE_FLAG_INBOX_ANY |
582 		NAMESPACE_FLAG_LIST_PREFIX | NAMESPACE_FLAG_SUBSCRIPTIONS;
583 	ns->user_set = user->set;
584 	ns->mail_set = mail_user_set_get_storage_set(user);
585 	i_array_init(&ns->all_storages, 2);
586 	return ns;
587 }
588 
mail_namespaces_deinit(struct mail_namespace ** _namespaces)589 void mail_namespaces_deinit(struct mail_namespace **_namespaces)
590 {
591 	struct mail_namespace *ns, *next;
592 
593 	/* update *_namespaces as needed, instead of immediately setting it
594 	   to NULL. for example mdbox_storage.destroy() wants to go through
595 	   user's namespaces. */
596 	while (*_namespaces != NULL) {
597 		ns = *_namespaces;
598 		next = ns->next;
599 
600 		mail_namespace_free(ns);
601 		*_namespaces = next;
602 	}
603 }
604 
mail_namespaces_set_storage_callbacks(struct mail_namespace * namespaces,struct mail_storage_callbacks * callbacks,void * context)605 void mail_namespaces_set_storage_callbacks(struct mail_namespace *namespaces,
606 					   struct mail_storage_callbacks *callbacks,
607 					   void *context)
608 {
609 	struct mail_namespace *ns;
610 	struct mail_storage *storage;
611 
612 	for (ns = namespaces; ns != NULL; ns = ns->next) {
613 		array_foreach_elem(&ns->all_storages, storage)
614 			mail_storage_set_callbacks(storage, callbacks, context);
615 	}
616 }
617 
mail_namespace_ref(struct mail_namespace * ns)618 void mail_namespace_ref(struct mail_namespace *ns)
619 {
620 	i_assert(ns->refcount > 0);
621 
622 	ns->refcount++;
623 }
624 
mail_namespace_unref(struct mail_namespace ** _ns)625 void mail_namespace_unref(struct mail_namespace **_ns)
626 {
627 	struct mail_namespace *ns = *_ns;
628 
629 	i_assert(ns->refcount > 0);
630 
631 	*_ns = NULL;
632 
633 	if (--ns->refcount > 0)
634 		return;
635 
636 	i_assert(ns->destroyed);
637 	mail_namespace_free(ns);
638 }
639 
mail_namespace_destroy(struct mail_namespace * ns)640 void mail_namespace_destroy(struct mail_namespace *ns)
641 {
642 	struct mail_namespace **nsp;
643 
644 	i_assert(!ns->destroyed);
645 
646 	/* remove from user's namespaces list */
647 	for (nsp = &ns->user->namespaces; *nsp != NULL; nsp = &(*nsp)->next) {
648 		if (*nsp == ns) {
649 			*nsp = ns->next;
650 			break;
651 		}
652 	}
653 	ns->destroyed = TRUE;
654 
655 	mail_namespace_unref(&ns);
656 }
657 
658 struct mail_storage *
mail_namespace_get_default_storage(struct mail_namespace * ns)659 mail_namespace_get_default_storage(struct mail_namespace *ns)
660 {
661 	return ns->storage;
662 }
663 
mail_namespace_get_sep(struct mail_namespace * ns)664 char mail_namespace_get_sep(struct mail_namespace *ns)
665 {
666 	return *ns->set->separator != '\0' ? *ns->set->separator :
667 		mailbox_list_get_hierarchy_sep(ns->list);
668 }
669 
mail_namespaces_get_root_sep(struct mail_namespace * namespaces)670 char mail_namespaces_get_root_sep(struct mail_namespace *namespaces)
671 {
672 	while ((namespaces->flags & NAMESPACE_FLAG_LIST_PREFIX) == 0)
673 		namespaces = namespaces->next;
674 	return mail_namespace_get_sep(namespaces);
675 }
676 
mail_namespace_is_usable_prefix(struct mail_namespace * ns,const char * mailbox,bool inbox)677 static bool mail_namespace_is_usable_prefix(struct mail_namespace *ns,
678 					    const char *mailbox, bool inbox)
679 {
680 	if (strncmp(ns->prefix, mailbox, ns->prefix_len) == 0) {
681 		/* true exact prefix match */
682 		return TRUE;
683 	}
684 
685 	if (inbox && str_begins(ns->prefix, "INBOX") &&
686 	    strncmp(ns->prefix+5, mailbox+5, ns->prefix_len-5) == 0) {
687 		/* we already checked that mailbox begins with case-insensitive
688 		   INBOX. this namespace also begins with INBOX and the rest
689 		   of the prefix matches too. */
690 		return TRUE;
691 	}
692 
693 	if (strncmp(ns->prefix, mailbox, ns->prefix_len-1) == 0 &&
694 	    mailbox[ns->prefix_len-1] == '\0' &&
695 	    ns->prefix[ns->prefix_len-1] == mail_namespace_get_sep(ns)) {
696 		/* we're trying to access the namespace prefix itself */
697 		return TRUE;
698 	}
699 	return FALSE;
700 }
701 
702 static struct mail_namespace *
mail_namespace_find_mask(struct mail_namespace * namespaces,const char * box,enum namespace_flags flags,enum namespace_flags mask)703 mail_namespace_find_mask(struct mail_namespace *namespaces, const char *box,
704 			 enum namespace_flags flags,
705 			 enum namespace_flags mask)
706 {
707         struct mail_namespace *ns = namespaces;
708 	struct mail_namespace *best = NULL;
709 	size_t best_len = 0;
710 	bool inbox;
711 
712 	inbox = strncasecmp(box, "INBOX", 5) == 0;
713 	if (inbox && box[5] == '\0') {
714 		/* find the INBOX namespace */
715 		while (ns != NULL) {
716 			if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 &&
717 			    (ns->flags & mask) == flags)
718 				return ns;
719 			if (*ns->prefix == '\0')
720 				best = ns;
721 			ns = ns->next;
722 		}
723 		return best;
724 	}
725 
726 	for (; ns != NULL; ns = ns->next) {
727 		if (ns->prefix_len >= best_len && (ns->flags & mask) == flags &&
728 		    mail_namespace_is_usable_prefix(ns, box, inbox)) {
729 			best = ns;
730 			best_len = ns->prefix_len;
731 		}
732 	}
733 	return best;
734 }
735 
736 static struct mail_namespace *
mail_namespace_find_shared(struct mail_namespace * ns,const char * mailbox)737 mail_namespace_find_shared(struct mail_namespace *ns, const char *mailbox)
738 {
739 	struct mailbox_list *list = ns->list;
740 	struct mail_storage *storage;
741 
742 	if (mailbox_list_get_storage(&list, mailbox, &storage) < 0)
743 		return ns;
744 
745 	return mailbox_list_get_namespace(list);
746 }
747 
748 struct mail_namespace *
mail_namespace_find(struct mail_namespace * namespaces,const char * mailbox)749 mail_namespace_find(struct mail_namespace *namespaces, const char *mailbox)
750 {
751 	struct mail_namespace *ns;
752 
753 	ns = mail_namespace_find_mask(namespaces, mailbox, 0, 0);
754 	i_assert(ns != NULL);
755 
756 	if (mail_namespace_is_shared_user_root(ns)) {
757 		/* see if we need to autocreate a namespace for shared user */
758 		if (strchr(mailbox, mail_namespace_get_sep(ns)) != NULL)
759 			return mail_namespace_find_shared(ns, mailbox);
760 	}
761 	return ns;
762 }
763 
764 struct mail_namespace *
mail_namespace_find_unalias(struct mail_namespace * namespaces,const char ** mailbox)765 mail_namespace_find_unalias(struct mail_namespace *namespaces,
766 			    const char **mailbox)
767 {
768 	struct mail_namespace *ns;
769 	const char *storage_name;
770 
771 	ns = mail_namespace_find(namespaces, *mailbox);
772 	if (ns->alias_for != NULL) {
773 		storage_name =
774 			mailbox_list_get_storage_name(ns->list, *mailbox);
775 		ns = ns->alias_for;
776 		*mailbox = mailbox_list_get_vname(ns->list, storage_name);
777 	}
778 	return ns;
779 }
780 
781 struct mail_namespace *
mail_namespace_find_visible(struct mail_namespace * namespaces,const char * mailbox)782 mail_namespace_find_visible(struct mail_namespace *namespaces,
783 			    const char *mailbox)
784 {
785 	return mail_namespace_find_mask(namespaces, mailbox, 0,
786 					NAMESPACE_FLAG_HIDDEN);
787 }
788 
789 struct mail_namespace *
mail_namespace_find_subscribable(struct mail_namespace * namespaces,const char * mailbox)790 mail_namespace_find_subscribable(struct mail_namespace *namespaces,
791 				 const char *mailbox)
792 {
793 	return mail_namespace_find_mask(namespaces, mailbox,
794 					NAMESPACE_FLAG_SUBSCRIPTIONS,
795 					NAMESPACE_FLAG_SUBSCRIPTIONS);
796 }
797 
798 struct mail_namespace *
mail_namespace_find_unsubscribable(struct mail_namespace * namespaces,const char * mailbox)799 mail_namespace_find_unsubscribable(struct mail_namespace *namespaces,
800 				   const char *mailbox)
801 {
802 	return mail_namespace_find_mask(namespaces, mailbox,
803 					0, NAMESPACE_FLAG_SUBSCRIPTIONS);
804 }
805 
806 struct mail_namespace *
mail_namespace_find_inbox(struct mail_namespace * namespaces)807 mail_namespace_find_inbox(struct mail_namespace *namespaces)
808 {
809 	i_assert(namespaces != NULL);
810 
811 	/* there should always be an INBOX */
812 	while ((namespaces->flags & NAMESPACE_FLAG_INBOX_USER) == 0) {
813 		namespaces = namespaces->next;
814 		i_assert(namespaces != NULL);
815 	}
816 	return namespaces;
817 }
818 
819 struct mail_namespace *
mail_namespace_find_prefix(struct mail_namespace * namespaces,const char * prefix)820 mail_namespace_find_prefix(struct mail_namespace *namespaces,
821 			   const char *prefix)
822 {
823         struct mail_namespace *ns;
824 	size_t len = strlen(prefix);
825 
826 	for (ns = namespaces; ns != NULL; ns = ns->next) {
827 		if (ns->prefix_len == len &&
828 		    strcmp(ns->prefix, prefix) == 0)
829 			return ns;
830 	}
831 	return NULL;
832 }
833 
834 struct mail_namespace *
mail_namespace_find_prefix_nosep(struct mail_namespace * namespaces,const char * prefix)835 mail_namespace_find_prefix_nosep(struct mail_namespace *namespaces,
836 				 const char *prefix)
837 {
838         struct mail_namespace *ns;
839 	size_t len = strlen(prefix);
840 
841 	for (ns = namespaces; ns != NULL; ns = ns->next) {
842 		if (ns->prefix_len == len + 1 &&
843 		    strncmp(ns->prefix, prefix, len) == 0 &&
844 		    ns->prefix[len] == mail_namespace_get_sep(ns))
845 			return ns;
846 	}
847 	return NULL;
848 }
849 
mail_namespace_is_shared_user_root(struct mail_namespace * ns)850 bool mail_namespace_is_shared_user_root(struct mail_namespace *ns)
851 {
852 	struct mail_storage *storage;
853 
854 	if (ns->type != MAIL_NAMESPACE_TYPE_SHARED)
855 		return FALSE;
856 	if ((ns->flags & NAMESPACE_FLAG_AUTOCREATED) != 0) {
857 		/* child of the shared root */
858 		return FALSE;
859 	}
860 	/* if we have driver=shared storage, we're a real shared root */
861 	array_foreach_elem(&ns->all_storages, storage) {
862 		if (strcmp(storage->name, MAIL_SHARED_STORAGE_NAME) == 0)
863 			return TRUE;
864 	}
865 	return FALSE;
866 }
867