1
/* Copyright (c) 2006-2013 Dovecot authors, see the included COPYING file */
5
#include "imap-match.h"
6
#include "mail-storage.h"
7
#include "mailbox-tree.h"
8
#include "mailbox-list-subscriptions.h"
9
#include "mailbox-list-private.h"
11
enum autocreate_match_result {
12
/* list contains the mailbox */
13
AUTOCREATE_MATCH_RESULT_YES = 0x01,
14
/* list contains children of the mailbox */
15
AUTOCREATE_MATCH_RESULT_CHILDREN = 0x02,
16
/* list contains parents of the mailbox */
17
AUTOCREATE_MATCH_RESULT_PARENT = 0x04
20
struct autocreate_box {
22
const struct mailbox_settings *set;
23
enum mailbox_info_flags flags;
27
ARRAY_DEFINE_TYPE(mailbox_settings, struct mailbox_settings *);
28
struct mailbox_list_autocreate_iterate_context {
30
struct mailbox_info new_info;
31
ARRAY(struct autocreate_box) boxes;
32
ARRAY_TYPE(mailbox_settings) box_sets;
33
ARRAY_TYPE(mailbox_settings) all_ns_box_sets;
36
struct ns_list_iterate_context {
37
struct mailbox_list_iterate_context ctx;
38
struct mailbox_list_iterate_context *backend_ctx;
39
struct mail_namespace *namespaces, *cur_ns;
40
struct mailbox_list *error_list;
42
const char **patterns, **patterns_ns_match;
43
enum mail_namespace_type type_mask;
45
struct mailbox_info ns_info;
46
struct mailbox_info inbox_info;
47
const struct mailbox_info *pending_backend_info;
49
unsigned int cur_ns_prefix_sent:1;
50
unsigned int inbox_list:1;
51
unsigned int inbox_listed:1;
54
static bool ns_match_next(struct ns_list_iterate_context *ctx,
55
struct mail_namespace *ns, const char *pattern);
56
static int mailbox_list_match_anything(struct ns_list_iterate_context *ctx,
57
struct mail_namespace *ns,
60
struct mailbox_list_iterate_context *
61
mailbox_list_iter_init(struct mailbox_list *list, const char *pattern,
62
enum mailbox_list_iter_flags flags)
64
const char *patterns[2];
66
patterns[0] = pattern;
68
return mailbox_list_iter_init_multiple(list, patterns, flags);
71
int mailbox_list_iter_subscriptions_refresh(struct mailbox_list *list)
73
struct mail_namespace *ns = list->ns;
75
if ((ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) == 0) {
76
/* no subscriptions in this namespace. find where they are. */
77
ns = mail_namespace_find_subscribable(ns->user->namespaces,
80
/* no subscriptions. avoid crashes by initializing
81
a subscriptions tree. */
82
if (list->subscriptions == NULL) {
83
char sep = mail_namespace_get_sep(list->ns);
84
list->subscriptions = mailbox_tree_init(sep);
89
return ns->list->v.subscriptions_refresh(ns->list, list);
92
static struct mailbox_settings *
93
mailbox_settings_add_ns_prefix(pool_t pool, struct mail_namespace *ns,
94
struct mailbox_settings *in_set)
96
struct mailbox_settings *out_set;
98
if (ns->prefix_len == 0 || strcasecmp(in_set->name, "INBOX") == 0)
101
out_set = p_new(pool, struct mailbox_settings, 1);
103
if (*in_set->name == '\0') {
104
/* namespace prefix itself */
105
out_set->name = p_strndup(pool, ns->prefix, ns->prefix_len-1);
108
p_strconcat(pool, ns->prefix, in_set->name, NULL);
114
mailbox_list_iter_init_autocreate(struct mailbox_list_iterate_context *ctx)
116
struct mail_namespace *ns = ctx->list->ns;
117
struct mailbox_list_autocreate_iterate_context *actx;
118
struct mailbox_settings *const *box_sets, *set;
119
struct autocreate_box *autobox;
120
unsigned int i, count;
122
if (!array_is_created(&ns->set->mailboxes))
124
box_sets = array_get(&ns->set->mailboxes, &count);
128
actx = p_new(ctx->pool, struct mailbox_list_autocreate_iterate_context, 1);
129
ctx->autocreate_ctx = actx;
131
/* build the list of mailboxes we need to consider as existing */
132
p_array_init(&actx->boxes, ctx->pool, 16);
133
p_array_init(&actx->box_sets, ctx->pool, 16);
134
p_array_init(&actx->all_ns_box_sets, ctx->pool, 16);
135
for (i = 0; i < count; i++) {
136
if (strcmp(box_sets[i]->autocreate, MAILBOX_SET_AUTO_NO) == 0)
139
set = mailbox_settings_add_ns_prefix(ctx->pool,
142
/* autocreate mailbox belongs to listed namespace */
143
array_append(&actx->all_ns_box_sets, &set, 1);
144
if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0 ||
145
strcmp(set->autocreate, MAILBOX_SET_AUTO_SUBSCRIBE) == 0) {
146
array_append(&actx->box_sets, &set, 1);
147
autobox = array_append_space(&actx->boxes);
148
autobox->name = set->name;
150
if (strcasecmp(autobox->name, "INBOX") == 0) {
151
/* make sure duplicate INBOX/Inbox/etc.
153
autobox->name = "INBOX";
159
struct mailbox_list_iterate_context *
160
mailbox_list_iter_init_multiple(struct mailbox_list *list,
161
const char *const *patterns,
162
enum mailbox_list_iter_flags flags)
164
struct mailbox_list_iterate_context *ctx;
167
i_assert(*patterns != NULL);
169
if ((flags & (MAILBOX_LIST_ITER_SELECT_SUBSCRIBED |
170
MAILBOX_LIST_ITER_RETURN_SUBSCRIBED)) != 0)
171
ret = mailbox_list_iter_subscriptions_refresh(list);
173
ctx = list->v.iter_init(list, patterns, flags);
176
else if ((flags & MAILBOX_LIST_ITER_NO_AUTO_BOXES) == 0)
177
mailbox_list_iter_init_autocreate(ctx);
182
ns_match_simple(struct ns_list_iterate_context *ctx, struct mail_namespace *ns)
184
if ((ctx->type_mask & ns->type) == 0)
187
if ((ctx->ctx.flags & MAILBOX_LIST_ITER_SKIP_ALIASES) != 0) {
188
if (ns->alias_for != NULL)
195
ns_is_match_within_ns(struct ns_list_iterate_context *ctx,
196
struct mail_namespace *ns, const char *prefix_without_sep,
197
const char *pattern, enum imap_match_result result)
199
if ((ctx->ctx.flags & MAILBOX_LIST_ITER_STAR_WITHIN_NS) == 0) {
202
case IMAP_MATCH_CHILDREN:
205
case IMAP_MATCH_PARENT:
213
/* allow matching prefix only when it's done without
215
if (strcmp(prefix_without_sep, pattern) == 0)
218
case IMAP_MATCH_CHILDREN: {
219
/* allow this only if there isn't another namespace
220
with longer prefix that matches this pattern
221
(namespaces are sorted by prefix length) */
222
struct mail_namespace *tmp;
225
for (tmp = ns->next; tmp != NULL; tmp = tmp->next) {
226
if (ns_match_simple(ctx, tmp) &&
227
ns_match_next(ctx, tmp, pattern))
236
case IMAP_MATCH_PARENT:
242
static bool ns_match_next(struct ns_list_iterate_context *ctx,
243
struct mail_namespace *ns, const char *pattern)
245
struct imap_match_glob *glob;
246
enum imap_match_result result;
247
const char *prefix_without_sep;
250
len = ns->prefix_len;
251
if (len > 0 && ns->prefix[len-1] == mail_namespace_get_sep(ns))
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)
259
/* prefix="" list=no is never listed */
260
if (ns->prefix_len == 0)
264
prefix_without_sep = t_strndup(ns->prefix, len);
265
if (*prefix_without_sep == '\0')
266
result = IMAP_MATCH_CHILDREN;
268
glob = imap_match_init(pool_datastack_create(), pattern,
269
TRUE, mail_namespace_get_sep(ns));
270
result = imap_match(glob, prefix_without_sep);
273
return ns_is_match_within_ns(ctx, ns, prefix_without_sep,
278
mailbox_list_ns_match_patterns(struct ns_list_iterate_context *ctx)
280
struct mail_namespace *ns = ctx->cur_ns;
283
if (!ns_match_simple(ctx, ns))
286
/* filter out namespaces whose prefix doesn't match. this same code
287
handles both with and without STAR_WITHIN_NS, so the "without" case
288
is slower than necessary, but this shouldn't matter much */
290
for (i = 0; ctx->patterns_ns_match[i] != NULL; i++) {
291
if (ns_match_next(ctx, ns, ctx->patterns_ns_match[i]))
296
return ctx->patterns_ns_match[i] != NULL;
300
iter_next_try_prefix_pattern(struct ns_list_iterate_context *ctx,
301
struct mail_namespace *ns, const char *pattern)
303
struct imap_match_glob *glob;
304
enum imap_match_result result;
305
const char *prefix_without_sep;
307
i_assert(ns->prefix_len > 0);
309
if ((ns->flags & (NAMESPACE_FLAG_LIST_PREFIX |
310
NAMESPACE_FLAG_LIST_CHILDREN)) == 0) {
311
/* non-listable namespace matches only with exact prefix */
312
if (strncmp(ns->prefix, pattern, ns->prefix_len) != 0)
316
prefix_without_sep = t_strndup(ns->prefix, ns->prefix_len-1);
317
glob = imap_match_init(pool_datastack_create(), pattern,
318
TRUE, mail_namespace_get_sep(ns));
319
result = imap_match(glob, prefix_without_sep);
320
return result == IMAP_MATCH_YES &&
321
ns_is_match_within_ns(ctx, ns, prefix_without_sep,
326
mailbox_list_ns_prefix_match(struct ns_list_iterate_context *ctx,
327
struct mail_namespace *ns)
332
for (i = 0; ctx->patterns_ns_match[i] != NULL; i++) {
334
ret = iter_next_try_prefix_pattern(ctx, ns,
335
ctx->patterns_ns_match[i]);
344
ns_prefix_is_visible(struct ns_list_iterate_context *ctx,
345
struct mail_namespace *ns)
347
if ((ns->flags & NAMESPACE_FLAG_LIST_PREFIX) != 0)
349
if ((ns->flags & NAMESPACE_FLAG_LIST_CHILDREN) != 0) {
350
if (mailbox_list_match_anything(ctx, ns, ns->prefix))
357
ns_prefix_has_visible_child_namespace(struct ns_list_iterate_context *ctx,
360
struct mail_namespace *ns;
361
unsigned int prefix_len = strlen(prefix);
363
for (ns = ctx->namespaces; ns != NULL; ns = ns->next) {
364
if (ns->prefix_len > prefix_len &&
365
strncmp(ns->prefix, prefix, prefix_len) == 0 &&
366
ns_prefix_is_visible(ctx, ns))
373
mailbox_ns_prefix_is_shared_inbox(struct mail_namespace *ns)
375
return ns->type == MAIL_NAMESPACE_TYPE_SHARED &&
376
(ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0 &&
377
!ns->list->mail_set->mail_shared_explicit_inbox;
381
mailbox_is_shared_inbox(struct mail_namespace *ns, const char *vname)
383
return mailbox_ns_prefix_is_shared_inbox(ns) &&
384
strncmp(ns->prefix, vname, ns->prefix_len-1) == 0 &&
385
vname[ns->prefix_len-1] == '\0';
389
mailbox_list_match_anything(struct ns_list_iterate_context *ctx,
390
struct mail_namespace *ns, const char *prefix)
392
enum mailbox_list_iter_flags list_flags =
393
MAILBOX_LIST_ITER_RETURN_NO_FLAGS;
394
struct mailbox_list_iterate_context *list_iter;
395
const struct mailbox_info *info;
399
if (ns_prefix_has_visible_child_namespace(ctx, prefix))
402
pattern = t_strconcat(prefix, "%", NULL);
403
list_iter = mailbox_list_iter_init(ns->list, pattern, list_flags);
404
info = mailbox_list_iter_next(list_iter);
405
if (info != NULL && mailbox_ns_prefix_is_shared_inbox(ns) &&
406
mailbox_is_shared_inbox(ns, info->vname)) {
407
/* we don't want to see this, try the next one */
408
info = mailbox_list_iter_next(list_iter);
410
ret = info != NULL ? 1 : 0;
411
if (mailbox_list_iter_deinit(&list_iter) < 0) {
419
mailbox_ns_prefix_check_selection_criteria(struct ns_list_iterate_context *ctx)
421
if ((ctx->ctx.flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) {
422
if ((ctx->ns_info.flags & MAILBOX_SUBSCRIBED) != 0)
424
if ((ctx->ctx.flags & MAILBOX_LIST_ITER_SELECT_RECURSIVEMATCH) != 0 &&
425
(ctx->ns_info.flags & MAILBOX_CHILD_SUBSCRIBED) != 0)
433
mailbox_list_ns_prefix_return(struct ns_list_iterate_context *ctx,
434
struct mail_namespace *ns, bool has_children)
437
enum mailbox_existence existence;
440
if (strncasecmp(ns->prefix, "INBOX", 5) == 0 &&
441
ns->prefix[5] == mail_namespace_get_sep(ns)) {
442
/* prefix=INBOX/ (or prefix=INBOX/something/) namespace exists.
443
so we can create children to INBOX. */
444
ctx->inbox_info.flags &= ~MAILBOX_NOINFERIORS;
447
if (ns->prefix_len == 0 || !mailbox_list_ns_prefix_match(ctx, ns))
450
memset(&ctx->ns_info, 0, sizeof(ctx->ns_info));
451
ctx->ns_info.ns = ns;
452
ctx->ns_info.vname = p_strndup(ctx->pool, ns->prefix,
454
if (ns->special_use_mailboxes)
455
ctx->ns_info.flags |= MAILBOX_CHILD_SPECIALUSE;
457
if (strcasecmp(ctx->ns_info.vname, "INBOX") == 0) {
458
i_assert(!ctx->inbox_listed);
459
ctx->inbox_listed = TRUE;
460
ctx->ns_info.flags |= ctx->inbox_info.flags | MAILBOX_SELECT;
463
if ((ctx->ctx.flags & (MAILBOX_LIST_ITER_RETURN_SUBSCRIBED |
464
MAILBOX_LIST_ITER_SELECT_SUBSCRIBED)) != 0) {
465
mailbox_list_set_subscription_flags(ns->list,
467
&ctx->ns_info.flags);
469
if (!mailbox_ns_prefix_check_selection_criteria(ctx))
472
/* see if the namespace has children */
474
ctx->ns_info.flags |= MAILBOX_CHILDREN;
475
else if ((ctx->ctx.flags & MAILBOX_LIST_ITER_RETURN_CHILDREN) != 0 ||
476
(ns->flags & NAMESPACE_FLAG_LIST_CHILDREN) != 0) {
477
/* need to check this explicitly */
478
if ((ret = mailbox_list_match_anything(ctx, ns, ns->prefix)) > 0)
479
ctx->ns_info.flags |= MAILBOX_CHILDREN;
481
if ((ns->flags & NAMESPACE_FLAG_LIST_CHILDREN) != 0 &&
482
!mailbox_ns_prefix_is_shared_inbox(ns)) {
483
/* no children -> not visible */
486
ctx->ns_info.flags |= MAILBOX_NOCHILDREN;
490
if ((ctx->ctx.flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) == 0 &&
491
(ctx->ns_info.flags & MAILBOX_SELECT) == 0) {
492
/* see if namespace prefix is selectable */
493
box = mailbox_alloc(ns->list, ctx->ns_info.vname, 0);
494
if (mailbox_exists(box, TRUE, &existence) == 0 &&
495
existence == MAILBOX_EXISTENCE_SELECT)
496
ctx->ns_info.flags |= MAILBOX_SELECT;
498
ctx->ns_info.flags |= MAILBOX_NONEXISTENT;
504
static void inbox_set_children_flags(struct ns_list_iterate_context *ctx)
509
if ((ctx->ctx.flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) != 0)
511
if ((ctx->inbox_info.flags & (MAILBOX_CHILDREN | MAILBOX_NOINFERIORS |
512
MAILBOX_NOCHILDREN)) != 0)
515
if (mail_namespace_find_prefix(ctx->namespaces, "") == NULL) {
516
/* prefix="" namespace doesn't exist, and neither does
517
anything beginning with prefix=INBOX/ (we checked this
518
earlier). there's no way to create children for INBOX. */
519
ctx->inbox_info.flags |= MAILBOX_NOINFERIORS;
523
/* INBOX namespace doesn't exist and we didn't see any children listed
524
for INBOX. this could be because there truly aren't any children,
525
or that the list patterns just didn't match them. */
526
prefix = t_strdup_printf("INBOX%c",
527
mail_namespace_get_sep(ctx->inbox_info.ns));
528
ret = mailbox_list_match_anything(ctx, ctx->inbox_info.ns, prefix);
530
ctx->inbox_info.flags |= MAILBOX_CHILDREN;
532
ctx->inbox_info.flags |= MAILBOX_NOCHILDREN;
536
mailbox_list_ns_iter_try_next(struct mailbox_list_iterate_context *_ctx,
537
const struct mailbox_info **info_r)
539
struct ns_list_iterate_context *ctx =
540
(struct ns_list_iterate_context *)_ctx;
541
struct mail_namespace *ns;
542
const struct mailbox_info *info;
543
enum mail_error error;
547
if (ctx->cur_ns == NULL) {
548
if (!ctx->inbox_listed && ctx->inbox_list) {
549
/* send delayed INBOX reply */
550
ctx->inbox_listed = TRUE;
551
inbox_set_children_flags(ctx);
552
*info_r = &ctx->inbox_info;
559
if (ctx->backend_ctx == NULL) {
560
i_assert(ctx->pending_backend_info == NULL);
561
if (!mailbox_list_ns_match_patterns(ctx)) {
562
/* namespace's children don't match the patterns,
563
but the namespace prefix itself might */
565
ctx->cur_ns = ctx->cur_ns->next;
566
if (mailbox_list_ns_prefix_return(ctx, ns, FALSE)) {
567
*info_r = &ctx->ns_info;
572
/* start listing this namespace's mailboxes */
574
mailbox_list_iter_init_multiple(ctx->cur_ns->list,
577
ctx->cur_ns_prefix_sent = FALSE;
579
if (ctx->pending_backend_info == NULL)
580
info = mailbox_list_iter_next(ctx->backend_ctx);
582
info = ctx->pending_backend_info;
583
ctx->pending_backend_info = NULL;
585
if (!ctx->cur_ns_prefix_sent) {
586
/* delayed sending of namespace prefix */
587
ctx->cur_ns_prefix_sent = TRUE;
588
has_children = info != NULL &&
589
!mailbox_is_shared_inbox(info->ns, info->vname);
590
if (mailbox_list_ns_prefix_return(ctx, ctx->cur_ns,
592
ctx->pending_backend_info = info;
593
*info_r = &ctx->ns_info;
598
if (strcasecmp(info->vname, "INBOX") == 0 && ctx->inbox_list) {
599
/* delay sending INBOX reply. we already saved its
600
flags at init stage, except for \Noinferiors
601
and subscription states */
602
ctx->inbox_info.flags |=
603
(info->flags & (MAILBOX_NOINFERIORS |
605
MAILBOX_CHILD_SUBSCRIBED));
608
if (strncasecmp(info->vname, "INBOX", 5) == 0 &&
609
info->vname[5] == mail_namespace_get_sep(info->ns)) {
610
/* we know now that INBOX has children */
611
ctx->inbox_info.flags |= MAILBOX_CHILDREN;
612
ctx->inbox_info.flags &= ~MAILBOX_NOINFERIORS;
614
if (info->ns->prefix_len > 0 &&
615
strncmp(info->vname, info->ns->prefix,
616
info->ns->prefix_len-1) == 0 &&
617
info->vname[info->ns->prefix_len-1] == '\0') {
618
/* this is an entry for namespace prefix, which we
619
already returned. (e.g. shared/$user/INBOX entry
620
returned as shared/$user, or when listing
621
subscribed namespace prefix). */
629
/* finished with this namespace */
630
if (mailbox_list_iter_deinit(&ctx->backend_ctx) < 0) {
631
errstr = mailbox_list_get_last_error(ctx->cur_ns->list,
633
mailbox_list_set_error(ctx->error_list, error, errstr);
636
ctx->cur_ns = ctx->cur_ns->next;
640
static const struct mailbox_info *
641
mailbox_list_ns_iter_next(struct mailbox_list_iterate_context *_ctx)
643
const struct mailbox_info *info = NULL;
645
while (!mailbox_list_ns_iter_try_next(_ctx, &info)) ;
650
mailbox_list_ns_iter_deinit(struct mailbox_list_iterate_context *_ctx)
652
struct ns_list_iterate_context *ctx =
653
(struct ns_list_iterate_context *)_ctx;
654
enum mail_error error;
658
if (ctx->backend_ctx != NULL) {
659
if (mailbox_list_iter_deinit(&ctx->backend_ctx) < 0) {
660
errstr = mailbox_list_get_last_error(ctx->cur_ns->list,
662
mailbox_list_set_error(ctx->error_list, error, errstr);
666
ret = _ctx->failed ? -1 : 0;
667
pool_unref(&ctx->pool);
672
dup_patterns_without_stars(pool_t pool, const char *const *patterns,
678
dup = p_new(pool, const char *, count + 1);
679
for (i = 0; i < count; i++) {
680
char *p = p_strdup(pool, patterns[i]);
683
for (; *p != '\0'; p++) {
692
patterns_match_inbox(struct mail_namespace *namespaces,
693
const char *const *patterns)
695
struct mail_namespace *ns = mail_namespace_find_inbox(namespaces);
696
struct imap_match_glob *glob;
698
glob = imap_match_init_multiple(pool_datastack_create(), patterns,
699
TRUE, mail_namespace_get_sep(ns));
700
return imap_match(glob, "INBOX") == IMAP_MATCH_YES;
703
static void inbox_info_init(struct ns_list_iterate_context *ctx,
704
struct mail_namespace *namespaces)
706
enum mailbox_info_flags flags;
708
ctx->inbox_info.vname = "INBOX";
709
ctx->inbox_info.ns = mail_namespace_find_inbox(namespaces);
710
i_assert(ctx->inbox_info.ns != NULL);
712
if (mailbox_list_mailbox(ctx->inbox_info.ns->list, "INBOX", &flags) > 0)
713
ctx->inbox_info.flags = flags;
716
struct mailbox_list_iterate_context *
717
mailbox_list_iter_init_namespaces(struct mail_namespace *namespaces,
718
const char *const *patterns,
719
enum mail_namespace_type type_mask,
720
enum mailbox_list_iter_flags flags)
722
struct ns_list_iterate_context *ctx;
723
unsigned int i, count;
726
i_assert(namespaces != NULL);
728
pool = pool_alloconly_create("mailbox list namespaces", 1024);
729
ctx = p_new(pool, struct ns_list_iterate_context, 1);
731
ctx->type_mask = type_mask;
732
ctx->ctx.flags = flags;
733
ctx->ctx.list = p_new(pool, struct mailbox_list, 1);
734
ctx->ctx.list->v.iter_next = mailbox_list_ns_iter_next;
735
ctx->ctx.list->v.iter_deinit = mailbox_list_ns_iter_deinit;
736
ctx->namespaces = namespaces;
737
ctx->error_list = namespaces->list;
739
count = str_array_length(patterns);
740
ctx->patterns = p_new(pool, const char *, count + 1);
741
for (i = 0; i < count; i++)
742
ctx->patterns[i] = p_strdup(pool, patterns[i]);
743
if (patterns_match_inbox(namespaces, ctx->patterns) &&
744
(flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0) {
745
/* we're going to list the INBOX. get its own flags (i.e. not
746
[no]children) immediately, so if we end up seeing something
747
else called INBOX (e.g. namespace prefix) we can show it
748
immediately with the proper flags. */
749
ctx->inbox_list = TRUE;
750
inbox_info_init(ctx, namespaces);
753
if ((flags & MAILBOX_LIST_ITER_STAR_WITHIN_NS) != 0) {
754
/* create copies of patterns with '*' wildcard changed to '%'.
755
this is used only when checking which namespaces to list */
756
ctx->patterns_ns_match =
757
dup_patterns_without_stars(pool, ctx->patterns, count);
759
ctx->patterns_ns_match = ctx->patterns;
762
ctx->cur_ns = namespaces;
763
ctx->ctx.list->ns = namespaces;
767
static enum autocreate_match_result
768
autocreate_box_match(const ARRAY_TYPE(mailbox_settings) *boxes,
769
struct mail_namespace *ns, const char *name,
770
bool only_subscribed, unsigned int *idx_r)
772
struct mailbox_settings *const *sets;
773
unsigned int i, count, len, name_len = strlen(name);
774
enum autocreate_match_result result = 0;
775
char sep = mail_namespace_get_sep(ns);
779
sets = array_get(boxes, &count);
780
for (i = 0; i < count; i++) {
781
if (only_subscribed &&
782
strcmp(sets[i]->autocreate, MAILBOX_SET_AUTO_SUBSCRIBE) != 0)
784
len = I_MIN(name_len, strlen(sets[i]->name));
785
if (strncmp(name, sets[i]->name, len) != 0)
788
if (name[len] == '\0' && sets[i]->name[len] == '\0') {
789
result |= AUTOCREATE_MATCH_RESULT_YES;
791
} else if (name[len] == '\0' && sets[i]->name[len] == sep)
792
result |= AUTOCREATE_MATCH_RESULT_CHILDREN;
793
else if (name[len] == sep && sets[i]->name[len] == '\0')
794
result |= AUTOCREATE_MATCH_RESULT_PARENT;
799
static const struct mailbox_info *
800
autocreate_iter_existing(struct mailbox_list_iterate_context *ctx)
802
struct mailbox_list_autocreate_iterate_context *actx =
804
struct mailbox_info *info = &actx->new_info;
805
enum autocreate_match_result match, match2;
808
match = autocreate_box_match(&actx->box_sets, ctx->list->ns,
809
info->vname, FALSE, &idx);
811
if ((match & AUTOCREATE_MATCH_RESULT_YES) != 0) {
812
/* we have an exact match in the list.
813
don't list it at the end. */
814
array_delete(&actx->boxes, idx, 1);
815
array_delete(&actx->box_sets, idx, 1);
818
if ((match & AUTOCREATE_MATCH_RESULT_CHILDREN) != 0) {
819
if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0)
820
info->flags |= MAILBOX_CHILD_SUBSCRIBED;
822
info->flags &= ~MAILBOX_NOCHILDREN;
823
info->flags |= MAILBOX_CHILDREN;
827
/* make sure the mailbox existence flags are correct. */
828
if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0)
831
match2 = autocreate_box_match(&actx->all_ns_box_sets,
832
ctx->list->ns, info->vname,
835
if ((match2 & AUTOCREATE_MATCH_RESULT_YES) != 0)
836
info->flags &= ~MAILBOX_NONEXISTENT;
837
if ((match2 & AUTOCREATE_MATCH_RESULT_CHILDREN) != 0) {
838
info->flags &= ~MAILBOX_NOCHILDREN;
839
info->flags |= MAILBOX_CHILDREN;
842
if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0 &&
843
(ctx->flags & MAILBOX_LIST_ITER_RETURN_SUBSCRIBED) != 0) {
844
/* we're listing all mailboxes and want \Subscribed flag */
845
match2 = autocreate_box_match(&actx->all_ns_box_sets,
846
ctx->list->ns, info->vname,
848
if ((match2 & AUTOCREATE_MATCH_RESULT_YES) != 0) {
849
/* mailbox is also marked as autosubscribe */
850
info->flags |= MAILBOX_SUBSCRIBED;
852
if ((match2 & AUTOCREATE_MATCH_RESULT_CHILDREN) != 0) {
853
/* mailbox also has a children marked as
855
info->flags |= MAILBOX_CHILD_SUBSCRIBED;
859
if ((match & AUTOCREATE_MATCH_RESULT_PARENT) != 0) {
860
/* there are autocreate parent boxes.
861
set their children flag states. */
862
struct autocreate_box *autobox;
863
unsigned int name_len;
864
char sep = mail_namespace_get_sep(ctx->list->ns);
866
array_foreach_modifiable(&actx->boxes, autobox) {
867
name_len = strlen(autobox->name);
868
if (strncmp(info->vname, autobox->name, name_len) != 0 ||
869
info->vname[name_len] != sep)
872
if ((info->flags & MAILBOX_NONEXISTENT) == 0)
873
autobox->flags |= MAILBOX_CHILDREN;
874
if ((info->flags & MAILBOX_SUBSCRIBED) != 0)
875
autobox->flags |= MAILBOX_CHILD_SUBSCRIBED;
876
autobox->child_listed = TRUE;
882
static bool autocreate_iter_autobox(struct mailbox_list_iterate_context *ctx,
883
const struct autocreate_box *autobox)
885
struct mailbox_list_autocreate_iterate_context *actx =
887
enum imap_match_result match;
889
memset(&actx->new_info, 0, sizeof(actx->new_info));
890
actx->new_info.ns = ctx->list->ns;
891
actx->new_info.vname = autobox->name;
892
actx->new_info.flags = autobox->flags;
894
if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0)
895
actx->new_info.flags |= MAILBOX_SUBSCRIBED;
897
if ((actx->new_info.flags & MAILBOX_CHILDREN) == 0)
898
actx->new_info.flags |= MAILBOX_NOCHILDREN;
900
match = imap_match(ctx->glob, actx->new_info.vname);
901
if (match == IMAP_MATCH_YES) {
902
actx->new_info.special_use =
903
*autobox->set->special_use == '\0' ? NULL :
904
autobox->set->special_use;
907
if ((match & IMAP_MATCH_PARENT) != 0 && !autobox->child_listed) {
908
enum mailbox_info_flags old_flags = actx->new_info.flags;
909
char sep = mail_namespace_get_sep(ctx->list->ns);
912
/* e.g. autocreate=foo/bar and we're listing % */
913
actx->new_info.flags = MAILBOX_NONEXISTENT |
914
(old_flags & (MAILBOX_CHILDREN |
915
MAILBOX_CHILD_SUBSCRIBED));
916
if ((old_flags & MAILBOX_NONEXISTENT) == 0) {
917
actx->new_info.flags |= MAILBOX_CHILDREN;
918
actx->new_info.flags &= ~MAILBOX_NOCHILDREN;
920
if ((old_flags & MAILBOX_SUBSCRIBED) != 0)
921
actx->new_info.flags |= MAILBOX_CHILD_SUBSCRIBED;
923
p = strrchr(actx->new_info.vname, sep);
925
actx->new_info.vname =
926
p_strdup_until(ctx->pool,
927
actx->new_info.vname, p);
928
match = imap_match(ctx->glob, actx->new_info.vname);
929
} while (match != IMAP_MATCH_YES);
935
static const struct mailbox_info *
936
mailbox_list_iter_next_call(struct mailbox_list_iterate_context *ctx)
938
const struct mailbox_info *info;
939
const struct mailbox_settings *set;
941
info = ctx->list->v.iter_next(ctx);
945
ctx->list->ns->flags |= NAMESPACE_FLAG_USABLE;
946
if ((ctx->flags & MAILBOX_LIST_ITER_RETURN_SPECIALUSE) != 0) {
947
set = mailbox_settings_find(ctx->list->ns->user, info->vname);
948
if (set != NULL && *set->special_use != '\0') {
949
ctx->specialuse_info = *info;
950
ctx->specialuse_info.special_use =
951
*set->special_use == '\0' ? NULL :
953
info = &ctx->specialuse_info;
959
static const struct mailbox_info *
960
autocreate_iter_next(struct mailbox_list_iterate_context *ctx)
962
struct mailbox_list_autocreate_iterate_context *actx =
964
const struct mailbox_info *info;
965
const struct autocreate_box *autoboxes, *autobox;
968
if (actx->idx == 0) {
969
info = mailbox_list_iter_next_call(ctx);
971
actx->new_info = *info;
972
return autocreate_iter_existing(ctx);
976
/* list missing mailboxes */
977
autoboxes = array_get(&actx->boxes, &count);
978
while (actx->idx < count) {
979
autobox = &autoboxes[actx->idx++];
980
if (autocreate_iter_autobox(ctx, autobox))
981
return &actx->new_info;
983
i_assert(array_count(&actx->boxes) == array_count(&actx->box_sets));
988
special_use_selection(struct mailbox_list_iterate_context *ctx,
989
const struct mailbox_info *info)
991
if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_RECURSIVEMATCH) != 0 &&
992
(ctx->flags & MAILBOX_LIST_ITER_SELECT_SPECIALUSE) != 0) {
993
/* LIST (SPECIAL-USE RECURSIVEMATCH) used. for now we support
994
this only for namespace prefixes */
995
if ((info->flags & MAILBOX_CHILD_SPECIALUSE) != 0)
998
return (ctx->flags & MAILBOX_LIST_ITER_SELECT_SPECIALUSE) == 0 ||
999
info->special_use != NULL;
1002
const struct mailbox_info *
1003
mailbox_list_iter_next(struct mailbox_list_iterate_context *ctx)
1005
const struct mailbox_info *info;
1009
if (ctx->autocreate_ctx != NULL)
1010
info = autocreate_iter_next(ctx);
1012
info = mailbox_list_iter_next_call(ctx);
1014
} while (info != NULL && !special_use_selection(ctx, info));
1018
int mailbox_list_iter_deinit(struct mailbox_list_iterate_context **_ctx)
1020
struct mailbox_list_iterate_context *ctx = *_ctx;
1024
return ctx->list->v.iter_deinit(ctx);
1027
static void node_fix_parents(struct mailbox_node *node)
1029
/* If we happened to create any of the parents, we need to mark them
1031
node = node->parent;
1032
for (; node != NULL; node = node->parent) {
1033
if ((node->flags & MAILBOX_MATCHED) == 0)
1034
node->flags |= MAILBOX_NONEXISTENT;
1039
mailbox_list_iter_update_real(struct mailbox_list_iter_update_context *ctx,
1042
struct mail_namespace *ns = ctx->iter_ctx->list->ns;
1043
struct mailbox_node *node;
1044
enum mailbox_info_flags create_flags, always_flags;
1045
enum imap_match_result match;
1047
bool created, add_matched;
1049
create_flags = MAILBOX_NOCHILDREN;
1050
always_flags = ctx->leaf_flags;
1055
match = imap_match(ctx->glob, name);
1056
if (match == IMAP_MATCH_YES) {
1057
node = ctx->update_only ?
1058
mailbox_tree_lookup(ctx->tree_ctx, name) :
1059
mailbox_tree_get(ctx->tree_ctx, name, &created);
1061
node->flags = create_flags;
1062
if (create_flags != 0)
1063
node_fix_parents(node);
1066
if (!ctx->update_only && add_matched)
1067
node->flags |= MAILBOX_MATCHED;
1068
if ((always_flags & MAILBOX_CHILDREN) != 0)
1069
node->flags &= ~MAILBOX_NOCHILDREN;
1070
node->flags |= always_flags;
1072
/* We don't want to show the parent mailboxes unless
1073
something else matches them, but if they are matched
1074
we want to show them having child subscriptions */
1075
add_matched = FALSE;
1077
if ((match & IMAP_MATCH_PARENT) == 0)
1079
/* We've a (possibly) non-subscribed parent mailbox
1080
which has a subscribed child mailbox. Make sure we
1081
return the parent mailbox. */
1084
if (!ctx->match_parents)
1087
/* see if parent matches */
1088
p = strrchr(name, mail_namespace_get_sep(ns));
1092
name = t_strdup_until(name, p);
1093
create_flags |= MAILBOX_NONEXISTENT;
1094
create_flags &= ~MAILBOX_NOCHILDREN;
1095
always_flags = MAILBOX_CHILDREN | ctx->parent_flags;
1099
void mailbox_list_iter_update(struct mailbox_list_iter_update_context *ctx,
1103
mailbox_list_iter_update_real(ctx, name);