1 /* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "array.h"
5 #include "hash.h"
6 #include "imap-match.h"
7 #include "mail-storage.h"
8 #include "mailbox-tree.h"
9 #include "mailbox-list-subscriptions.h"
10 #include "mailbox-list-private.h"
11 #include "mailbox-list-iter-private.h"
12 
13 enum autocreate_match_result {
14 	/* list contains the mailbox */
15 	AUTOCREATE_MATCH_RESULT_YES		= 0x01,
16 	/* list contains children of the mailbox */
17 	AUTOCREATE_MATCH_RESULT_CHILDREN	= 0x02,
18 	/* list contains parents of the mailbox */
19 	AUTOCREATE_MATCH_RESULT_PARENT		= 0x04
20 };
21 
22 struct ns_list_iterate_context {
23 	struct mailbox_list_iterate_context ctx;
24 	struct mailbox_list_iterate_context *backend_ctx;
25 	struct mail_namespace *namespaces, *cur_ns;
26 	struct mailbox_list *error_list;
27 	pool_t pool;
28 	const char **patterns, **patterns_ns_match;
29 	enum mail_namespace_type type_mask;
30 
31 	struct mailbox_info ns_info;
32 	struct mailbox_info inbox_info;
33 	const struct mailbox_info *pending_backend_info;
34 
35 	bool cur_ns_prefix_sent:1;
36 	bool inbox_list:1;
37 	bool inbox_listed:1;
38 	bool inbox_seen:1;
39 };
40 
41 static void mailbox_list_ns_iter_failed(struct ns_list_iterate_context *ctx);
42 static bool ns_match_next(struct ns_list_iterate_context *ctx,
43 			  struct mail_namespace *ns, const char *pattern);
44 static int mailbox_list_match_anything(struct ns_list_iterate_context *ctx,
45 				       struct mail_namespace *ns,
46 				       const char *prefix);
47 
48 static struct mailbox_list_iterate_context mailbox_list_iter_failed;
49 
50 struct mailbox_list_iterate_context *
mailbox_list_iter_init(struct mailbox_list * list,const char * pattern,enum mailbox_list_iter_flags flags)51 mailbox_list_iter_init(struct mailbox_list *list, const char *pattern,
52 		       enum mailbox_list_iter_flags flags)
53 {
54 	const char *patterns[2];
55 
56 	patterns[0] = pattern;
57 	patterns[1] = NULL;
58 	return mailbox_list_iter_init_multiple(list, patterns, flags);
59 }
60 
mailbox_list_iter_subscriptions_refresh(struct mailbox_list * list)61 int mailbox_list_iter_subscriptions_refresh(struct mailbox_list *list)
62 {
63 	struct mail_namespace *ns = list->ns;
64 
65 	if ((ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) == 0) {
66 		/* no subscriptions in this namespace. find where they are. */
67 		ns = mail_namespace_find_subscribable(ns->user->namespaces,
68 						      ns->prefix);
69 		if (ns == NULL) {
70 			/* no subscriptions. avoid crashes by initializing
71 			   a subscriptions tree. */
72 			if (list->subscriptions == NULL) {
73 				char sep = mail_namespace_get_sep(list->ns);
74 				list->subscriptions = mailbox_tree_init(sep);
75 			}
76 			return 0;
77 		}
78 	}
79 	return ns->list->v.subscriptions_refresh(ns->list, list);
80 }
81 
82 static struct mailbox_settings *
mailbox_settings_add_ns_prefix(pool_t pool,struct mail_namespace * ns,struct mailbox_settings * in_set)83 mailbox_settings_add_ns_prefix(pool_t pool, struct mail_namespace *ns,
84 			       struct mailbox_settings *in_set)
85 {
86 	struct mailbox_settings *out_set;
87 
88 	if (ns->prefix_len == 0 || strcasecmp(in_set->name, "INBOX") == 0)
89 		return in_set;
90 
91 	out_set = p_new(pool, struct mailbox_settings, 1);
92 	*out_set = *in_set;
93 	if (*in_set->name == '\0') {
94 		/* namespace prefix itself */
95 		out_set->name = p_strndup(pool, ns->prefix, ns->prefix_len-1);
96 	} else {
97 		out_set->name =
98 			p_strconcat(pool, ns->prefix, in_set->name, NULL);
99 	}
100 	return out_set;
101 }
102 
103 static void
mailbox_list_iter_init_autocreate(struct mailbox_list_iterate_context * ctx)104 mailbox_list_iter_init_autocreate(struct mailbox_list_iterate_context *ctx)
105 {
106 	struct mail_namespace *ns = ctx->list->ns;
107 	struct mailbox_list_autocreate_iterate_context *actx;
108 	struct mailbox_settings *const *box_sets, *set;
109 	struct autocreate_box *autobox;
110 	unsigned int i, count;
111 
112 	if (!array_is_created(&ns->set->mailboxes))
113 		return;
114 	box_sets = array_get(&ns->set->mailboxes, &count);
115 	if (count == 0)
116 		return;
117 
118 	actx = p_new(ctx->pool, struct mailbox_list_autocreate_iterate_context, 1);
119 	ctx->autocreate_ctx = actx;
120 	hash_table_create(&actx->duplicate_vnames, ctx->pool, 0,
121 			  str_hash, strcmp);
122 
123 	/* build the list of mailboxes we need to consider as existing */
124 	p_array_init(&actx->boxes, ctx->pool, 16);
125 	p_array_init(&actx->box_sets, ctx->pool, 16);
126 	p_array_init(&actx->all_ns_box_sets, ctx->pool, 16);
127 	for (i = 0; i < count; i++) {
128 		if (strcmp(box_sets[i]->autocreate, MAILBOX_SET_AUTO_NO) == 0)
129 			continue;
130 
131 		set = mailbox_settings_add_ns_prefix(ctx->pool,
132 						     ns, box_sets[i]);
133 
134 		/* autocreate mailbox belongs to listed namespace */
135 		array_push_back(&actx->all_ns_box_sets, &set);
136 		if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0 ||
137 		    strcmp(set->autocreate, MAILBOX_SET_AUTO_SUBSCRIBE) == 0) {
138 			array_push_back(&actx->box_sets, &set);
139 			autobox = array_append_space(&actx->boxes);
140 			autobox->name = set->name;
141 			autobox->set = set;
142 			if (strcasecmp(autobox->name, "INBOX") == 0) {
143 				/* make sure duplicate INBOX/Inbox/etc.
144 				   won't get created */
145 				autobox->name = "INBOX";
146 			}
147 		}
148 	}
149 }
150 
151 struct mailbox_list_iterate_context *
mailbox_list_iter_init_multiple(struct mailbox_list * list,const char * const * patterns,enum mailbox_list_iter_flags flags)152 mailbox_list_iter_init_multiple(struct mailbox_list *list,
153 				const char *const *patterns,
154 				enum mailbox_list_iter_flags flags)
155 {
156 	struct mailbox_list_iterate_context *ctx;
157 
158 	i_assert(*patterns != NULL);
159 
160 	if ((flags & (MAILBOX_LIST_ITER_SELECT_SUBSCRIBED |
161 		      MAILBOX_LIST_ITER_RETURN_SUBSCRIBED)) != 0) {
162 		if (mailbox_list_iter_subscriptions_refresh(list) < 0)
163 			return &mailbox_list_iter_failed;
164 	}
165 
166 	ctx = list->v.iter_init(list, patterns, flags);
167 	if ((flags & MAILBOX_LIST_ITER_NO_AUTO_BOXES) == 0)
168 		mailbox_list_iter_init_autocreate(ctx);
169 	return ctx;
170 }
171 
172 static bool
ns_match_simple(struct ns_list_iterate_context * ctx,struct mail_namespace * ns)173 ns_match_simple(struct ns_list_iterate_context *ctx, struct mail_namespace *ns)
174 {
175 	if ((ctx->type_mask & ns->type) == 0)
176 		return FALSE;
177 
178 	if ((ctx->ctx.flags & MAILBOX_LIST_ITER_SKIP_ALIASES) != 0) {
179 		if (ns->alias_for != NULL)
180 			return FALSE;
181 	}
182 	return TRUE;
183 }
184 
185 static bool
ns_is_match_within_ns(struct ns_list_iterate_context * ctx,struct mail_namespace * ns,const char * prefix_without_sep,const char * pattern,enum imap_match_result result)186 ns_is_match_within_ns(struct ns_list_iterate_context *ctx,
187 		      struct mail_namespace *ns, const char *prefix_without_sep,
188 		      const char *pattern, enum imap_match_result result)
189 {
190 	if ((ctx->ctx.flags & MAILBOX_LIST_ITER_STAR_WITHIN_NS) == 0) {
191 		switch (result) {
192 		case IMAP_MATCH_YES:
193 		case IMAP_MATCH_CHILDREN:
194 			return TRUE;
195 		case IMAP_MATCH_NO:
196 		case IMAP_MATCH_PARENT:
197 			break;
198 		}
199 		return FALSE;
200 	}
201 
202 	switch (result) {
203 	case IMAP_MATCH_YES:
204 		/* allow matching prefix only when it's done without
205 		   wildcards */
206 		if (strcmp(prefix_without_sep, pattern) == 0)
207 			return TRUE;
208 		break;
209 	case IMAP_MATCH_CHILDREN: {
210 		/* allow this only if there isn't another namespace
211 		   with longer prefix that matches this pattern
212 		   (namespaces are sorted by prefix length) */
213 		struct mail_namespace *tmp;
214 
215 		T_BEGIN {
216 			for (tmp = ns->next; tmp != NULL; tmp = tmp->next) {
217 				if (ns_match_simple(ctx, tmp) &&
218 				    ns_match_next(ctx, tmp, pattern))
219 					break;
220 			}
221 		} T_END;
222 		if (tmp == NULL)
223 			return TRUE;
224 		break;
225 	}
226 	case IMAP_MATCH_NO:
227 	case IMAP_MATCH_PARENT:
228 		break;
229 	}
230 	return FALSE;
231 }
232 
list_pattern_has_wildcards(const char * pattern)233 static bool list_pattern_has_wildcards(const char *pattern)
234 {
235 	for (; *pattern != '\0'; pattern++) {
236 		if (*pattern == '%' || *pattern == '*')
237 			return TRUE;
238 	}
239 	return FALSE;
240 }
241 
ns_match_next(struct ns_list_iterate_context * ctx,struct mail_namespace * ns,const char * pattern)242 static bool ns_match_next(struct ns_list_iterate_context *ctx,
243 			  struct mail_namespace *ns, const char *pattern)
244 {
245 	struct imap_match_glob *glob;
246 	enum imap_match_result result;
247 	const char *prefix_without_sep;
248 	size_t len;
249 
250 	len = ns->prefix_len;
251 	if (len > 0 && ns->prefix[len-1] == mail_namespace_get_sep(ns))
252 		len--;
253 
254 	if ((ns->flags & (NAMESPACE_FLAG_LIST_PREFIX |
255 			  NAMESPACE_FLAG_LIST_CHILDREN)) == 0) {
256 		/* non-listable namespace matches only with exact prefix */
257 		if (strncmp(ns->prefix, pattern, ns->prefix_len) != 0)
258 			return FALSE;
259 		/* with prefix="", list=no we don't want to show anything,
260 		   except when the client explicitly lists a mailbox without
261 		   wildcards (e.g. LIST "" mailbox). this is mainly useful
262 		   for working around client bugs (and supporting a specific
263 		   IMAP client behavior that's not exactly buggy but not very
264 		   good IMAP behavior either). */
265 		if (ns->prefix_len == 0 && list_pattern_has_wildcards(pattern))
266 			return FALSE;
267 	}
268 
269 	prefix_without_sep = t_strndup(ns->prefix, len);
270 	if (*prefix_without_sep == '\0')
271 		result = IMAP_MATCH_CHILDREN;
272 	else {
273 		glob = imap_match_init(pool_datastack_create(), pattern,
274 				       TRUE, mail_namespace_get_sep(ns));
275 		result = imap_match(glob, prefix_without_sep);
276 	}
277 
278 	return ns_is_match_within_ns(ctx, ns, prefix_without_sep,
279 				     pattern, result);
280 }
281 
282 static bool
mailbox_list_ns_match_patterns(struct ns_list_iterate_context * ctx)283 mailbox_list_ns_match_patterns(struct ns_list_iterate_context *ctx)
284 {
285 	struct mail_namespace *ns = ctx->cur_ns;
286 	unsigned int i;
287 
288 	if (!ns_match_simple(ctx, ns))
289 		return FALSE;
290 
291 	/* filter out namespaces whose prefix doesn't match. this same code
292 	   handles both with and without STAR_WITHIN_NS, so the "without" case
293 	   is slower than necessary, but this shouldn't matter much */
294 	T_BEGIN {
295 		for (i = 0; ctx->patterns_ns_match[i] != NULL; i++) {
296 			if (ns_match_next(ctx, ns, ctx->patterns_ns_match[i]))
297 				break;
298 		}
299 	} T_END;
300 
301 	return ctx->patterns_ns_match[i] != NULL;
302 }
303 
304 static bool
iter_next_try_prefix_pattern(struct ns_list_iterate_context * ctx,struct mail_namespace * ns,const char * pattern)305 iter_next_try_prefix_pattern(struct ns_list_iterate_context *ctx,
306 			     struct mail_namespace *ns, const char *pattern)
307 {
308 	struct imap_match_glob *glob;
309 	enum imap_match_result result;
310 	const char *prefix_without_sep;
311 
312 	i_assert(ns->prefix_len > 0);
313 
314 	if ((ns->flags & (NAMESPACE_FLAG_LIST_PREFIX |
315 			  NAMESPACE_FLAG_LIST_CHILDREN)) == 0) {
316 		/* non-listable namespace matches only with exact prefix */
317 		if (strncmp(ns->prefix, pattern, ns->prefix_len) != 0)
318 			return FALSE;
319 	}
320 
321 	prefix_without_sep = t_strndup(ns->prefix, ns->prefix_len-1);
322 	glob = imap_match_init(pool_datastack_create(), pattern,
323 			       TRUE, mail_namespace_get_sep(ns));
324 	result = imap_match(glob, prefix_without_sep);
325 	return result == IMAP_MATCH_YES &&
326 		ns_is_match_within_ns(ctx, ns, prefix_without_sep,
327 				      pattern, result);
328 }
329 
330 static bool
mailbox_list_ns_prefix_match(struct ns_list_iterate_context * ctx,struct mail_namespace * ns)331 mailbox_list_ns_prefix_match(struct ns_list_iterate_context *ctx,
332 			     struct mail_namespace *ns)
333 {
334 	unsigned int i;
335 	bool ret = FALSE;
336 
337 	for (i = 0; ctx->patterns_ns_match[i] != NULL; i++) {
338 		T_BEGIN {
339 			ret = iter_next_try_prefix_pattern(ctx, ns,
340 						ctx->patterns_ns_match[i]);
341 		} T_END;
342 		if (ret)
343 			break;
344 	}
345 	return ret;
346 }
347 
348 static int
ns_prefix_is_visible(struct ns_list_iterate_context * ctx,struct mail_namespace * ns)349 ns_prefix_is_visible(struct ns_list_iterate_context *ctx,
350 		     struct mail_namespace *ns)
351 {
352 	int ret;
353 
354 	if ((ns->flags & NAMESPACE_FLAG_LIST_PREFIX) != 0)
355 		return 1;
356 	if ((ns->flags & NAMESPACE_FLAG_LIST_CHILDREN) != 0) {
357 		if ((ret = mailbox_list_match_anything(ctx, ns, ns->prefix)) != 0)
358 			return ret;
359 	}
360 	return 0;
361 }
362 
363 static int
ns_prefix_has_visible_child_namespace(struct ns_list_iterate_context * ctx,const char * prefix)364 ns_prefix_has_visible_child_namespace(struct ns_list_iterate_context *ctx,
365 				      const char *prefix)
366 {
367 	struct mail_namespace *ns;
368 	size_t prefix_len = strlen(prefix);
369 	int ret;
370 
371 	for (ns = ctx->namespaces; ns != NULL; ns = ns->next) {
372 		if (ns->prefix_len > prefix_len &&
373 		    strncmp(ns->prefix, prefix, prefix_len) == 0) {
374 			ret = ns_prefix_is_visible(ctx, ns);
375 			if (ret != 0)
376 				return ret;
377 		}
378 	}
379 	return 0;
380 }
381 
382 static bool
mailbox_ns_prefix_is_shared_inbox(struct mail_namespace * ns)383 mailbox_ns_prefix_is_shared_inbox(struct mail_namespace *ns)
384 {
385 	return ns->type == MAIL_NAMESPACE_TYPE_SHARED &&
386 		(ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0 &&
387 		!ns->list->mail_set->mail_shared_explicit_inbox;
388 }
389 
390 static bool
mailbox_is_shared_inbox(struct mail_namespace * ns,const char * vname)391 mailbox_is_shared_inbox(struct mail_namespace *ns, const char *vname)
392 {
393 	return mailbox_ns_prefix_is_shared_inbox(ns) &&
394 		strncmp(ns->prefix, vname, ns->prefix_len-1) == 0 &&
395 		vname[ns->prefix_len-1] == '\0';
396 }
397 
398 static int
mailbox_list_match_anything(struct ns_list_iterate_context * ctx,struct mail_namespace * ns,const char * prefix)399 mailbox_list_match_anything(struct ns_list_iterate_context *ctx,
400 			    struct mail_namespace *ns, const char *prefix)
401 {
402 	enum mailbox_list_iter_flags list_flags =
403 		MAILBOX_LIST_ITER_RETURN_NO_FLAGS;
404 	struct mailbox_list_iterate_context *list_iter;
405 	const struct mailbox_info *info;
406 	const char *pattern;
407 	int ret;
408 
409 	if ((ret = ns_prefix_has_visible_child_namespace(ctx, prefix)) != 0)
410 		return ret;
411 
412 	pattern = t_strconcat(prefix, "%", NULL);
413 	list_iter = mailbox_list_iter_init(ns->list, pattern, list_flags);
414 	info = mailbox_list_iter_next(list_iter);
415 	if (info != NULL && mailbox_ns_prefix_is_shared_inbox(ns) &&
416 	    mailbox_is_shared_inbox(ns, info->vname)) {
417 		/* we don't want to see this, try the next one */
418 		info = mailbox_list_iter_next(list_iter);
419 	}
420 	ret = info != NULL ? 1 : 0;
421 	if (mailbox_list_iter_deinit(&list_iter) < 0) {
422 		if (ret == 0)
423 			ret = -1;
424 	}
425 	return ret;
426 }
427 
428 static bool
mailbox_ns_prefix_check_selection_criteria(struct ns_list_iterate_context * ctx)429 mailbox_ns_prefix_check_selection_criteria(struct ns_list_iterate_context *ctx)
430 {
431 	if ((ctx->ctx.flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) {
432 		if ((ctx->ns_info.flags & MAILBOX_SUBSCRIBED) != 0)
433 			return TRUE;
434 		if ((ctx->ctx.flags & MAILBOX_LIST_ITER_SELECT_RECURSIVEMATCH) != 0 &&
435 		    (ctx->ns_info.flags & MAILBOX_CHILD_SUBSCRIBED) != 0)
436 			return TRUE;
437 		return FALSE;
438 	}
439 	return TRUE;
440 }
441 
442 static bool
mailbox_list_ns_prefix_return(struct ns_list_iterate_context * ctx,struct mail_namespace * ns,bool has_children)443 mailbox_list_ns_prefix_return(struct ns_list_iterate_context *ctx,
444 			      struct mail_namespace *ns, bool has_children)
445 {
446 	struct mailbox *box;
447 	enum mailbox_existence existence;
448 	int ret;
449 
450 	if (strncasecmp(ns->prefix, "INBOX", 5) == 0 &&
451 	    ns->prefix[5] == mail_namespace_get_sep(ns)) {
452 		/* prefix=INBOX/ (or prefix=INBOX/something/) namespace exists.
453 		   so we can create children to INBOX. */
454 		ctx->inbox_info.flags &= ENUM_NEGATE(MAILBOX_NOINFERIORS);
455 	}
456 
457 	if (ns->prefix_len == 0 || !mailbox_list_ns_prefix_match(ctx, ns))
458 		return FALSE;
459 
460 	i_zero(&ctx->ns_info);
461 	ctx->ns_info.ns = ns;
462 	ctx->ns_info.vname = p_strndup(ctx->pool, ns->prefix,
463 				       ns->prefix_len-1);
464 	if (ns->special_use_mailboxes)
465 		ctx->ns_info.flags |= MAILBOX_CHILD_SPECIALUSE;
466 
467 	if (strcasecmp(ctx->ns_info.vname, "INBOX") == 0) {
468 		i_assert(!ctx->inbox_listed);
469 		ctx->inbox_listed = TRUE;
470 		ctx->ns_info.flags |= ctx->inbox_info.flags | MAILBOX_SELECT;
471 	}
472 
473 	if ((ctx->ctx.flags & (MAILBOX_LIST_ITER_RETURN_SUBSCRIBED |
474 			       MAILBOX_LIST_ITER_SELECT_SUBSCRIBED)) != 0) {
475 		/* Refresh subscriptions first, this won't cause a duplicate
476 		   call later on as this is only called when the namespace's
477 		   children definitely don't match */
478 		if (mailbox_list_iter_subscriptions_refresh(ns->list) < 0) {
479 			mailbox_list_ns_iter_failed(ctx);
480 			return FALSE;
481 		}
482 		mailbox_list_set_subscription_flags(ns->list,
483 						    ctx->ns_info.vname,
484 						    &ctx->ns_info.flags);
485 	}
486 	if (!mailbox_ns_prefix_check_selection_criteria(ctx))
487 		return FALSE;
488 
489 	/* see if the namespace has children */
490 	if (has_children)
491 		ctx->ns_info.flags |= MAILBOX_CHILDREN;
492 	else if ((ctx->ctx.flags & MAILBOX_LIST_ITER_RETURN_CHILDREN) != 0 ||
493 		 (ns->flags & NAMESPACE_FLAG_LIST_CHILDREN) != 0) {
494 		/* need to check this explicitly */
495 		if ((ret = mailbox_list_match_anything(ctx, ns, ns->prefix)) > 0)
496 			ctx->ns_info.flags |= MAILBOX_CHILDREN;
497 		else if (ret == 0) {
498 			if ((ns->flags & NAMESPACE_FLAG_LIST_CHILDREN) != 0 &&
499 			    !mailbox_ns_prefix_is_shared_inbox(ns)) {
500 				/* no children -> not visible */
501 				return FALSE;
502 			}
503 			ctx->ns_info.flags |= MAILBOX_NOCHILDREN;
504 		}
505 	}
506 
507 	if ((ctx->ns_info.flags & MAILBOX_SELECT) == 0) {
508 		/* see if namespace prefix is selectable */
509 		box = mailbox_alloc(ns->list, ctx->ns_info.vname, 0);
510 		if (mailbox_exists(box, TRUE, &existence) == 0 &&
511 		    existence == MAILBOX_EXISTENCE_SELECT)
512 			ctx->ns_info.flags |= MAILBOX_SELECT;
513 		else
514 			ctx->ns_info.flags |= MAILBOX_NONEXISTENT;
515 		mailbox_free(&box);
516 	}
517 	return TRUE;
518 }
519 
inbox_set_children_flags(struct ns_list_iterate_context * ctx)520 static void inbox_set_children_flags(struct ns_list_iterate_context *ctx)
521 {
522 	struct mail_namespace *ns;
523 	const char *prefix;
524 	int ret;
525 
526 	if ((ctx->ctx.flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) != 0)
527 		return;
528 	if ((ctx->inbox_info.flags & (MAILBOX_CHILDREN | MAILBOX_NOINFERIORS |
529 				      MAILBOX_NOCHILDREN)) != 0)
530 		return;
531 
532 	ns = mail_namespace_find_prefix(ctx->namespaces, "");
533 	if (ns == NULL || (ns->flags & NAMESPACE_FLAG_UNUSABLE) != 0) {
534 		/* prefix="" namespace doesn't exist, and neither does
535 		   anything beginning with prefix=INBOX/ (we checked this
536 		   earlier). there's no way to create children for INBOX. */
537 		ctx->inbox_info.flags |= MAILBOX_NOINFERIORS;
538 		return;
539 	}
540 
541  	/* INBOX namespace doesn't exist and we didn't see any children listed
542 	   for INBOX. this could be because there truly aren't any children,
543 	   or that the list patterns just didn't match them. */
544 	prefix = t_strdup_printf("INBOX%c",
545 				 mail_namespace_get_sep(ctx->inbox_info.ns));
546 	ret = mailbox_list_match_anything(ctx, ctx->inbox_info.ns, prefix);
547 	if (ret > 0)
548 		ctx->inbox_info.flags |= MAILBOX_CHILDREN;
549 	else if (ret == 0)
550 		ctx->inbox_info.flags |= MAILBOX_NOCHILDREN;
551 }
552 
mailbox_list_ns_iter_failed(struct ns_list_iterate_context * ctx)553 static void mailbox_list_ns_iter_failed(struct ns_list_iterate_context *ctx)
554 {
555 	enum mail_error error;
556 	const char *errstr;
557 
558 	if (ctx->cur_ns->list != ctx->error_list) {
559 		errstr = mailbox_list_get_last_error(ctx->cur_ns->list, &error);
560 		mailbox_list_set_error(ctx->error_list, error, errstr);
561 	}
562 	ctx->ctx.failed = TRUE;
563 }
564 
565 static bool
mailbox_list_ns_iter_try_next(struct mailbox_list_iterate_context * _ctx,const struct mailbox_info ** info_r)566 mailbox_list_ns_iter_try_next(struct mailbox_list_iterate_context *_ctx,
567 			      const struct mailbox_info **info_r)
568 {
569 	struct ns_list_iterate_context *ctx =
570 		(struct ns_list_iterate_context *)_ctx;
571 	struct mail_namespace *ns;
572 	const struct mailbox_info *info;
573 	bool has_children;
574 
575 	if (ctx->cur_ns == NULL) {
576 		if (!ctx->inbox_listed && ctx->inbox_list && !_ctx->failed &&
577 		    ((_ctx->flags & MAILBOX_LIST_ITER_NO_AUTO_BOXES) == 0 ||
578 		     ctx->inbox_seen)) {
579 			/* send delayed INBOX reply */
580 			ctx->inbox_listed = TRUE;
581 			inbox_set_children_flags(ctx);
582 			*info_r = &ctx->inbox_info;
583 			return TRUE;
584 		}
585 		*info_r = NULL;
586 		return TRUE;
587 	}
588 
589 	if (ctx->backend_ctx == NULL) {
590 		i_assert(ctx->pending_backend_info == NULL);
591 		if (!mailbox_list_ns_match_patterns(ctx)) {
592 			/* namespace's children don't match the patterns,
593 			   but the namespace prefix itself might */
594 			ns = ctx->cur_ns;
595 			ctx->cur_ns = ctx->cur_ns->next;
596 			if (mailbox_list_ns_prefix_return(ctx, ns, FALSE)) {
597 				*info_r = &ctx->ns_info;
598 				return TRUE;
599 			}
600 			return FALSE;
601 		}
602 		/* start listing this namespace's mailboxes */
603 		ctx->backend_ctx =
604 			mailbox_list_iter_init_multiple(ctx->cur_ns->list,
605 							ctx->patterns,
606 							_ctx->flags);
607 		ctx->cur_ns_prefix_sent = FALSE;
608 	}
609 	if (ctx->pending_backend_info == NULL)
610 		info = mailbox_list_iter_next(ctx->backend_ctx);
611 	else {
612 		info = ctx->pending_backend_info;
613 		ctx->pending_backend_info = NULL;
614 	}
615 	if (!ctx->cur_ns_prefix_sent) {
616 		/* delayed sending of namespace prefix */
617 		ctx->cur_ns_prefix_sent = TRUE;
618 		has_children = info != NULL &&
619 			!mailbox_is_shared_inbox(info->ns, info->vname);
620 		if (mailbox_list_ns_prefix_return(ctx, ctx->cur_ns,
621 						  has_children)) {
622 			ctx->pending_backend_info = info;
623 			*info_r = &ctx->ns_info;
624 			return TRUE;
625 		}
626 	}
627 	if (info != NULL) {
628 		if (strcasecmp(info->vname, "INBOX") == 0 && ctx->inbox_list) {
629 			/* delay sending INBOX reply. we already saved its
630 			   flags at init stage, except for \Noinferiors
631 			   and subscription states */
632 			ctx->inbox_seen = TRUE;
633 			ctx->inbox_info.flags |=
634 				(info->flags & (MAILBOX_NOINFERIORS |
635 						MAILBOX_SUBSCRIBED |
636 						MAILBOX_CHILD_SUBSCRIBED));
637 			return FALSE;
638 		}
639 		if (strncasecmp(info->vname, "INBOX", 5) == 0 &&
640 		    info->vname[5] == mail_namespace_get_sep(info->ns)) {
641 			/* we know now that INBOX has children */
642 			ctx->inbox_info.flags |= MAILBOX_CHILDREN;
643 			ctx->inbox_info.flags &= ENUM_NEGATE(MAILBOX_NOINFERIORS);
644 		}
645 		if (info->ns->prefix_len > 0 &&
646 		    strncmp(info->vname, info->ns->prefix,
647 			    info->ns->prefix_len-1) == 0 &&
648 		    info->vname[info->ns->prefix_len-1] == '\0') {
649 			/* this is an entry for namespace prefix, which we
650 			   already returned. (e.g. shared/$user/INBOX entry
651 			   returned as shared/$user, or when listing
652 			   subscribed namespace prefix). */
653 			return FALSE;
654 		}
655 
656 		*info_r = info;
657 		return TRUE;
658 	}
659 
660 	/* finished with this namespace */
661 	if (mailbox_list_iter_deinit(&ctx->backend_ctx) < 0)
662 		mailbox_list_ns_iter_failed(ctx);
663 	ctx->cur_ns = ctx->cur_ns->next;
664 	return FALSE;
665 }
666 
667 static const struct mailbox_info *
mailbox_list_ns_iter_next(struct mailbox_list_iterate_context * _ctx)668 mailbox_list_ns_iter_next(struct mailbox_list_iterate_context *_ctx)
669 {
670 	const struct mailbox_info *info = NULL;
671 
672 	while (!mailbox_list_ns_iter_try_next(_ctx, &info)) ;
673 	return info;
674 }
675 
676 static int
mailbox_list_ns_iter_deinit(struct mailbox_list_iterate_context * _ctx)677 mailbox_list_ns_iter_deinit(struct mailbox_list_iterate_context *_ctx)
678 {
679 	struct ns_list_iterate_context *ctx =
680 		(struct ns_list_iterate_context *)_ctx;
681 	int ret;
682 
683 	if (ctx->backend_ctx != NULL) {
684 		if (mailbox_list_iter_deinit(&ctx->backend_ctx) < 0)
685 			mailbox_list_ns_iter_failed(ctx);
686 	}
687 	ret = _ctx->failed ? -1 : 0;
688 	pool_unref(&ctx->pool);
689 	return ret;
690 }
691 
692 static const char **
dup_patterns_without_stars(pool_t pool,const char * const * patterns,unsigned int count)693 dup_patterns_without_stars(pool_t pool, const char *const *patterns,
694 			   unsigned int count)
695 {
696 	const char **dup;
697 	unsigned int i;
698 
699 	dup = p_new(pool, const char *, count + 1);
700 	for (i = 0; i < count; i++) {
701 		char *p = p_strdup(pool, patterns[i]);
702 		dup[i] = p;
703 
704 		for (; *p != '\0'; p++) {
705 			if (*p == '*')
706 				*p = '%';
707 		}
708 	}
709 	return dup;
710 }
711 
712 static bool
patterns_match_inbox(struct mail_namespace * namespaces,const char * const * patterns)713 patterns_match_inbox(struct mail_namespace *namespaces,
714 		     const char *const *patterns)
715 {
716 	struct mail_namespace *ns = mail_namespace_find_inbox(namespaces);
717 	struct imap_match_glob *glob;
718 
719 	glob = imap_match_init_multiple(pool_datastack_create(), patterns,
720 					TRUE, mail_namespace_get_sep(ns));
721 	return imap_match(glob, "INBOX") == IMAP_MATCH_YES;
722 }
723 
inbox_info_init(struct ns_list_iterate_context * ctx,struct mail_namespace * namespaces)724 static int inbox_info_init(struct ns_list_iterate_context *ctx,
725 			   struct mail_namespace *namespaces)
726 {
727 	enum mailbox_info_flags flags;
728 	int ret;
729 
730 	ctx->inbox_info.vname = "INBOX";
731 	ctx->inbox_info.ns = mail_namespace_find_inbox(namespaces);
732 	i_assert(ctx->inbox_info.ns != NULL);
733 
734 	if ((ret = mailbox_list_mailbox(ctx->inbox_info.ns->list, "INBOX", &flags)) > 0)
735 		ctx->inbox_info.flags = flags;
736 	else if (ret < 0) {
737 		ctx->cur_ns = ctx->inbox_info.ns;
738 		mailbox_list_ns_iter_failed(ctx);
739 	}
740 	return ret;
741 }
742 
743 struct mailbox_list_iterate_context *
mailbox_list_iter_init_namespaces(struct mail_namespace * namespaces,const char * const * patterns,enum mail_namespace_type type_mask,enum mailbox_list_iter_flags flags)744 mailbox_list_iter_init_namespaces(struct mail_namespace *namespaces,
745 				  const char *const *patterns,
746 				  enum mail_namespace_type type_mask,
747 				  enum mailbox_list_iter_flags flags)
748 {
749 	struct ns_list_iterate_context *ctx;
750 	unsigned int i, count;
751 	pool_t pool;
752 
753 	i_assert(namespaces != NULL);
754 
755 	pool = pool_alloconly_create("mailbox list namespaces", 1024);
756 	ctx = p_new(pool, struct ns_list_iterate_context, 1);
757 	ctx->pool = pool;
758 	ctx->type_mask = type_mask;
759 	ctx->ctx.flags = flags;
760 	ctx->ctx.list = p_new(pool, struct mailbox_list, 1);
761 	ctx->ctx.list->v.iter_next = mailbox_list_ns_iter_next;
762 	ctx->ctx.list->v.iter_deinit = mailbox_list_ns_iter_deinit;
763 	ctx->namespaces = namespaces;
764 	ctx->error_list = namespaces->list;
765 
766 	count = str_array_length(patterns);
767 	ctx->patterns = p_new(pool, const char *, count + 1);
768 	for (i = 0; i < count; i++)
769 		ctx->patterns[i] = p_strdup(pool, patterns[i]);
770 	if (patterns_match_inbox(namespaces, ctx->patterns) &&
771 	    (flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0) {
772 		/* we're going to list the INBOX. get its own flags (i.e. not
773 		   [no]children) immediately, so if we end up seeing something
774 		   else called INBOX (e.g. namespace prefix) we can show it
775 		   immediately with the proper flags. */
776 		ctx->inbox_list = TRUE;
777 		if (inbox_info_init(ctx, namespaces) < 0) {
778 			pool_unref(&pool);
779 			return &mailbox_list_iter_failed;
780 		}
781 	}
782 
783 	if ((flags & MAILBOX_LIST_ITER_STAR_WITHIN_NS) != 0) {
784 		/* create copies of patterns with '*' wildcard changed to '%'.
785 		   this is used only when checking which namespaces to list */
786 		ctx->patterns_ns_match =
787 			dup_patterns_without_stars(pool, ctx->patterns, count);
788 	} else {
789 		ctx->patterns_ns_match = ctx->patterns;
790 	}
791 
792 	ctx->cur_ns = namespaces;
793 	ctx->ctx.list->ns = namespaces;
794 	return &ctx->ctx;
795 }
796 
797 static enum autocreate_match_result
autocreate_box_match(const ARRAY_TYPE (mailbox_settings)* boxes,struct mail_namespace * ns,const char * name,bool only_subscribed,unsigned int * idx_r)798 autocreate_box_match(const ARRAY_TYPE(mailbox_settings) *boxes,
799 		     struct mail_namespace *ns, const char *name,
800 		     bool only_subscribed, unsigned int *idx_r)
801 {
802 	struct mailbox_settings *const *sets;
803 	unsigned int i, count;
804 	size_t len, name_len = strlen(name);
805 	enum autocreate_match_result result = 0;
806 	char sep = mail_namespace_get_sep(ns);
807 
808 	*idx_r = UINT_MAX;
809 
810 	sets = array_get(boxes, &count);
811 	for (i = 0; i < count; i++) {
812 		if (only_subscribed &&
813 		    strcmp(sets[i]->autocreate, MAILBOX_SET_AUTO_SUBSCRIBE) != 0)
814 			continue;
815 		len = I_MIN(name_len, strlen(sets[i]->name));
816 		if (strncmp(name, sets[i]->name, len) != 0)
817 			continue;
818 
819 		if (name[len] == '\0' && sets[i]->name[len] == '\0') {
820 			result |= AUTOCREATE_MATCH_RESULT_YES;
821 			*idx_r = i;
822 		} else if (name[len] == '\0' && sets[i]->name[len] == sep)
823 			result |= AUTOCREATE_MATCH_RESULT_CHILDREN;
824 		else if (name[len] == sep && sets[i]->name[len] == '\0')
825 			result |= AUTOCREATE_MATCH_RESULT_PARENT;
826 	}
827 	return result;
828 }
829 
830 const struct mailbox_info *
mailbox_list_iter_autocreate_filter(struct mailbox_list_iterate_context * ctx,const struct mailbox_info * _info)831 mailbox_list_iter_autocreate_filter(struct mailbox_list_iterate_context *ctx,
832 				    const struct mailbox_info *_info)
833 {
834 	struct mailbox_list_autocreate_iterate_context *actx =
835 		ctx->autocreate_ctx;
836 	if (actx == NULL || _info == NULL)
837 		return _info;
838 	actx->new_info = *_info;
839 	struct mailbox_info *info = &actx->new_info;
840 	enum autocreate_match_result match, match2;
841 	unsigned int idx;
842 
843 	match = autocreate_box_match(&actx->box_sets, ctx->list->ns,
844 				     info->vname, FALSE, &idx);
845 
846 	if (!actx->listing_autoboxes) {
847 		if ((match & AUTOCREATE_MATCH_RESULT_YES) != 0) {
848 			/* we have an exact match in the list.
849 			   don't list it at the end. */
850 			array_delete(&actx->boxes, idx, 1);
851 			array_delete(&actx->box_sets, idx, 1);
852 		}
853 		if ((match & AUTOCREATE_MATCH_RESULT_CHILDREN) != 0 &&
854 		    hash_table_lookup(actx->duplicate_vnames, info->vname) == NULL) {
855 			/* Prevent autocreate-iteration from adding this
856 			   mailbox as a duplicate. For example we're listing %
857 			   and we're here because "foo" was found. However,
858 			   there's also "foo/bar" with auto=create. We're
859 			   telling here to the autocreate iteration code that
860 			   "foo" was already found and it doesn't need to add
861 			   it again. */
862 			char *vname = p_strdup(ctx->pool, info->vname);
863 			hash_table_insert(actx->duplicate_vnames, vname, vname);
864 		}
865 	}
866 
867 	if ((match & AUTOCREATE_MATCH_RESULT_CHILDREN) != 0) {
868 		if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0)
869 			info->flags |= MAILBOX_CHILD_SUBSCRIBED;
870 		else {
871 			info->flags &= ENUM_NEGATE(MAILBOX_NOCHILDREN);
872 			info->flags |= MAILBOX_CHILDREN;
873 		}
874 	}
875 
876 	/* make sure the mailbox existence flags are correct. */
877 	if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0)
878 		match2 = match;
879 	else {
880 		match2 = autocreate_box_match(&actx->all_ns_box_sets,
881 					      ctx->list->ns, info->vname,
882 					      FALSE, &idx);
883 	}
884 	if ((match2 & AUTOCREATE_MATCH_RESULT_YES) != 0)
885 		info->flags &= ENUM_NEGATE(MAILBOX_NONEXISTENT);
886 	if ((match2 & AUTOCREATE_MATCH_RESULT_CHILDREN) != 0) {
887 		info->flags &= ENUM_NEGATE(MAILBOX_NOCHILDREN);
888 		info->flags |= MAILBOX_CHILDREN;
889 	}
890 
891 	if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0 &&
892 	    (ctx->flags & MAILBOX_LIST_ITER_RETURN_SUBSCRIBED) != 0) {
893 		/* we're listing all mailboxes and want \Subscribed flag */
894 		match2 = autocreate_box_match(&actx->all_ns_box_sets,
895 					      ctx->list->ns, info->vname,
896 					      TRUE, &idx);
897 		if ((match2 & AUTOCREATE_MATCH_RESULT_YES) != 0) {
898 			/* mailbox is also marked as autosubscribe */
899 			info->flags |= MAILBOX_SUBSCRIBED;
900 		}
901 		if ((match2 & AUTOCREATE_MATCH_RESULT_CHILDREN) != 0) {
902 			/* mailbox also has a children marked as
903 			   autosubscribe */
904 			info->flags |= MAILBOX_CHILD_SUBSCRIBED;
905 		}
906 	}
907 
908 	if ((match & AUTOCREATE_MATCH_RESULT_PARENT) != 0) {
909 		/* there are autocreate parent boxes.
910 		   set their children flag states. */
911 		struct autocreate_box *autobox;
912 		size_t name_len;
913 		char sep = mail_namespace_get_sep(ctx->list->ns);
914 
915 		array_foreach_modifiable(&actx->boxes, autobox) {
916 			name_len = strlen(autobox->name);
917 			if (!str_begins(info->vname, autobox->name) ||
918 			    info->vname[name_len] != sep)
919 				continue;
920 
921 			if ((info->flags & MAILBOX_NONEXISTENT) == 0)
922 				autobox->flags |= MAILBOX_CHILDREN;
923 			if ((info->flags & MAILBOX_SUBSCRIBED) != 0)
924 				autobox->flags |= MAILBOX_CHILD_SUBSCRIBED;
925 			autobox->child_listed = TRUE;
926 		}
927 	}
928 	return info;
929 }
930 
autocreate_iter_autobox(struct mailbox_list_iterate_context * ctx,const struct autocreate_box * autobox)931 static bool autocreate_iter_autobox(struct mailbox_list_iterate_context *ctx,
932 				    const struct autocreate_box *autobox)
933 {
934 	struct mailbox_list_autocreate_iterate_context *actx =
935 		ctx->autocreate_ctx;
936 	enum imap_match_result match;
937 
938 	i_zero(&actx->new_info);
939 	actx->new_info.ns = ctx->list->ns;
940 	actx->new_info.vname = autobox->name;
941 	actx->new_info.flags = autobox->flags;
942 
943 	if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0)
944 		actx->new_info.flags |= MAILBOX_SUBSCRIBED;
945 
946 	if ((actx->new_info.flags & MAILBOX_CHILDREN) == 0) {
947 		if ((ctx->list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0 &&
948 		    ctx->list->set.maildir_name[0] == '\0') {
949 			/* mailbox format using files (e.g. mbox)
950 			   without DIRNAME specified */
951 			actx->new_info.flags |= MAILBOX_NOINFERIORS;
952 		} else {
953 			actx->new_info.flags |= MAILBOX_NOCHILDREN;
954 		}
955 	}
956 
957 	match = imap_match(ctx->glob, actx->new_info.vname);
958 	if (match == IMAP_MATCH_YES) {
959 		actx->new_info.special_use =
960 			*autobox->set->special_use == '\0' ? NULL :
961 			autobox->set->special_use;
962 		return TRUE;
963 	}
964 	if ((match & IMAP_MATCH_PARENT) != 0 && !autobox->child_listed) {
965 		enum mailbox_info_flags old_flags = actx->new_info.flags;
966 		char sep = mail_namespace_get_sep(ctx->list->ns);
967 		const char *p;
968 		char *vname;
969 
970 		/* e.g. autocreate=foo/bar and we're listing % */
971 		actx->new_info.flags = MAILBOX_NONEXISTENT |
972 			(old_flags & (MAILBOX_CHILDREN |
973 				      MAILBOX_CHILD_SUBSCRIBED));
974 		if ((old_flags & MAILBOX_NONEXISTENT) == 0) {
975 			actx->new_info.flags |= MAILBOX_CHILDREN;
976 			actx->new_info.flags &= ENUM_NEGATE(MAILBOX_NOCHILDREN);
977 		}
978 		if ((old_flags & MAILBOX_SUBSCRIBED) != 0)
979 			actx->new_info.flags |= MAILBOX_CHILD_SUBSCRIBED;
980 		do {
981 			p = strrchr(actx->new_info.vname, sep);
982 			i_assert(p != NULL);
983 			actx->new_info.vname = vname =
984 				p_strdup_until(ctx->pool,
985 					       actx->new_info.vname, p);
986 			match = imap_match(ctx->glob, actx->new_info.vname);
987 		} while (match != IMAP_MATCH_YES);
988 
989 		if (hash_table_lookup(actx->duplicate_vnames, vname) == NULL) {
990 			hash_table_insert(actx->duplicate_vnames, vname, vname);
991 			return TRUE;
992 		}
993 	}
994 	return FALSE;
995 }
996 
997 static const struct mailbox_info *
mailbox_list_iter_next_call(struct mailbox_list_iterate_context * ctx)998 mailbox_list_iter_next_call(struct mailbox_list_iterate_context *ctx)
999 {
1000 	const struct mailbox_info *info;
1001 	const struct mailbox_settings *set;
1002 
1003 	info = ctx->list->v.iter_next(ctx);
1004 	if (info == NULL)
1005 		return NULL;
1006 
1007 	ctx->list->ns->flags |= NAMESPACE_FLAG_USABLE;
1008 	if ((ctx->flags & MAILBOX_LIST_ITER_RETURN_SPECIALUSE) != 0) {
1009 		set = mailbox_settings_find(ctx->list->ns, info->vname);
1010 		if (set != NULL && *set->special_use != '\0') {
1011 			ctx->specialuse_info = *info;
1012 			ctx->specialuse_info.special_use =
1013 				*set->special_use == '\0' ? NULL :
1014 				set->special_use;
1015 			info = &ctx->specialuse_info;
1016 		}
1017 	}
1018 
1019 	return mailbox_list_iter_autocreate_filter(ctx, info);
1020 }
1021 
1022 const struct mailbox_info *
mailbox_list_iter_default_next(struct mailbox_list_iterate_context * ctx)1023 mailbox_list_iter_default_next(struct mailbox_list_iterate_context *ctx)
1024 {
1025 	struct mailbox_list_autocreate_iterate_context *actx =
1026         	ctx->autocreate_ctx;
1027 	const struct autocreate_box *autoboxes, *autobox;
1028 	unsigned int count;
1029 
1030 	if (actx == NULL)
1031 		return NULL;
1032 
1033 	/* do not drop boxes anymore */
1034 	actx->listing_autoboxes = TRUE;
1035 
1036 	/* list missing mailboxes */
1037 	autoboxes = array_get(&actx->boxes, &count);
1038 	while (actx->idx < count) {
1039 		autobox = &autoboxes[actx->idx++];
1040 		if (autocreate_iter_autobox(ctx, autobox))
1041 			return &actx->new_info;
1042 	}
1043 	i_assert(array_count(&actx->boxes) == array_count(&actx->box_sets));
1044 	return NULL;
1045 }
1046 
1047 static bool
special_use_selection(struct mailbox_list_iterate_context * ctx,const struct mailbox_info * info)1048 special_use_selection(struct mailbox_list_iterate_context *ctx,
1049 		      const struct mailbox_info *info)
1050 {
1051 	if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_RECURSIVEMATCH) != 0 &&
1052 	    (ctx->flags & MAILBOX_LIST_ITER_SELECT_SPECIALUSE) != 0) {
1053 		/* LIST (SPECIAL-USE RECURSIVEMATCH) used. for now we support
1054 		   this only for namespace prefixes */
1055 		if ((info->flags & MAILBOX_CHILD_SPECIALUSE) != 0)
1056 			return TRUE;
1057 	}
1058 	return (ctx->flags & MAILBOX_LIST_ITER_SELECT_SPECIALUSE) == 0 ||
1059 		info->special_use != NULL;
1060 }
1061 
1062 const struct mailbox_info *
mailbox_list_iter_next(struct mailbox_list_iterate_context * ctx)1063 mailbox_list_iter_next(struct mailbox_list_iterate_context *ctx)
1064 {
1065 	const struct mailbox_info *info;
1066 
1067 	if (ctx == &mailbox_list_iter_failed)
1068 		return NULL;
1069 	do {
1070 		T_BEGIN {
1071 			info = mailbox_list_iter_next_call(ctx);
1072 		} T_END;
1073 	} while (info != NULL && !special_use_selection(ctx, info));
1074 	return info;
1075 }
1076 
mailbox_list_iter_deinit(struct mailbox_list_iterate_context ** _ctx)1077 int mailbox_list_iter_deinit(struct mailbox_list_iterate_context **_ctx)
1078 {
1079 	struct mailbox_list_iterate_context *ctx = *_ctx;
1080 
1081 	*_ctx = NULL;
1082 
1083 	if (ctx == &mailbox_list_iter_failed)
1084 		return -1;
1085 	if (ctx->autocreate_ctx != NULL)
1086 		hash_table_destroy(&ctx->autocreate_ctx->duplicate_vnames);
1087 	return ctx->list->v.iter_deinit(ctx);
1088 }
1089 
node_fix_parents(struct mailbox_node * node)1090 static void node_fix_parents(struct mailbox_node *node)
1091 {
1092 	/* If we happened to create any of the parents, we need to mark them
1093 	   nonexistent. */
1094 	node = node->parent;
1095 	for (; node != NULL; node = node->parent) {
1096 		if ((node->flags & MAILBOX_MATCHED) == 0)
1097 			node->flags |= MAILBOX_NONEXISTENT;
1098 	}
1099 }
1100 
1101 static void
mailbox_list_iter_update_real(struct mailbox_list_iter_update_context * ctx,const char * name)1102 mailbox_list_iter_update_real(struct mailbox_list_iter_update_context *ctx,
1103 			      const char *name)
1104 {
1105 	struct mail_namespace *ns = ctx->iter_ctx->list->ns;
1106 	struct mailbox_node *node;
1107 	enum mailbox_info_flags create_flags, always_flags;
1108 	enum imap_match_result match;
1109 	const char *p;
1110 	bool created, add_matched;
1111 
1112 	create_flags = MAILBOX_NOCHILDREN;
1113 	always_flags = ctx->leaf_flags;
1114 	add_matched = TRUE;
1115 
1116 	for (;;) {
1117 		created = FALSE;
1118 		match = imap_match(ctx->glob, name);
1119 		if (match == IMAP_MATCH_YES) {
1120 			node = ctx->update_only ?
1121 				mailbox_tree_lookup(ctx->tree_ctx, name) :
1122 				mailbox_tree_get(ctx->tree_ctx, name, &created);
1123 			if (created) {
1124 				node->flags = create_flags;
1125 				if (create_flags != 0)
1126 					node_fix_parents(node);
1127 			}
1128 			if (node != NULL) {
1129 				if (!ctx->update_only && add_matched)
1130 					node->flags |= MAILBOX_MATCHED;
1131 				if ((always_flags & MAILBOX_CHILDREN) != 0)
1132 					node->flags &= ENUM_NEGATE(MAILBOX_NOCHILDREN);
1133 				node->flags |= always_flags;
1134 			}
1135 			/* We don't want to show the parent mailboxes unless
1136 			   something else matches them, but if they are matched
1137 			   we want to show them having child subscriptions */
1138 			add_matched = FALSE;
1139 		} else {
1140 			if ((match & IMAP_MATCH_PARENT) == 0)
1141 				break;
1142 			/* We've a (possibly) non-subscribed parent mailbox
1143 			   which has a subscribed child mailbox. Make sure we
1144 			   return the parent mailbox. */
1145 		}
1146 
1147 		if (!ctx->match_parents)
1148 			break;
1149 
1150 		/* see if parent matches */
1151 		p = strrchr(name, mail_namespace_get_sep(ns));
1152 		if (p == NULL)
1153 			break;
1154 
1155 		name = t_strdup_until(name, p);
1156 		create_flags |= MAILBOX_NONEXISTENT;
1157 		create_flags &= ENUM_NEGATE(MAILBOX_NOCHILDREN);
1158 		always_flags = MAILBOX_CHILDREN | ctx->parent_flags;
1159 	}
1160 }
1161 
mailbox_list_iter_update(struct mailbox_list_iter_update_context * ctx,const char * name)1162 void mailbox_list_iter_update(struct mailbox_list_iter_update_context *ctx,
1163 			      const char *name)
1164 {
1165 	T_BEGIN {
1166 		mailbox_list_iter_update_real(ctx, name);
1167 	} T_END;
1168 }
1169