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