~ubuntu-branches/ubuntu/utopic/dovecot/utopic

« back to all changes in this revision

Viewing changes to src/lib-storage/list/mailbox-list-iter.c

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2014-01-08 09:35:49 UTC
  • mfrom: (4.1.35 sid)
  • Revision ID: package-import@ubuntu.com-20140108093549-i72o93pux8p0dlaf
Tags: 1:2.2.9-1ubuntu1
* Merge from Debian unstable, remaining changes:
  + Add mail-stack-delivery package:
    - Update d/rules
    - d/control: convert existing dovecot-postfix package to a dummy
      package and add new mail-stack-delivery package.
    - Update maintainer scripts.
    - Rename d/dovecot-postfix.* to debian/mail-stack-delivery.*
    - d/mail-stack-delivery.preinst: Move previously installed backups and
      config files to a new package namespace.
    - d/mail-stack-delivery.prerm: Added to handle downgrades.
  + Use Snakeoil SSL certificates by default:
    - d/control: Depend on ssl-cert.
    - d/dovecot-core.postinst: Relax grep for SSL_* a bit.
  + Add autopkgtest to debian/tests/*.
  + Add ufw integration:
    - d/dovecot-core.ufw.profile: new ufw profile.
    - d/rules: install profile in dovecot-core.
    - d/control: dovecot-core - suggest ufw.
  + d/dovecot-core.dirs: Added usr/share/doc/dovecot-core
  + Add apport hook:
    - d/rules, d/source_dovecot.py
  + Add upstart job:
    - d/rules, d/dovecot-core.dovecot.upstart, d/control,
      d/dovecot-core.dirs, dovecot-imapd.{postrm, postinst, prerm},
      d/dovecot-pop3d.{postinst, postrm, prerm}.
      d/mail-stack-deliver.postinst: Convert init script to upstart.
  + Use the autotools-dev dh addon to update config.guess/config.sub for
    arm64.
* Dropped changes, included in Debian:
  - Update Dovecot name to reflect distribution in login greeting.
  - Update Drac plugin for >= 2.0.0 support.
* d/control: Drop dovecot-postfix package as its no longer required.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2006-2013 Dovecot authors, see the included COPYING file */
 
2
 
 
3
#include "lib.h"
 
4
#include "array.h"
 
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"
 
10
 
 
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
 
18
};
 
19
 
 
20
struct autocreate_box {
 
21
        const char *name;
 
22
        const struct mailbox_settings *set;
 
23
        enum mailbox_info_flags flags;
 
24
        bool child_listed;
 
25
};
 
26
 
 
27
ARRAY_DEFINE_TYPE(mailbox_settings, struct mailbox_settings *);
 
28
struct mailbox_list_autocreate_iterate_context {
 
29
        unsigned int idx;
 
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;
 
34
};
 
35
 
 
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;
 
41
        pool_t pool;
 
42
        const char **patterns, **patterns_ns_match;
 
43
        enum mail_namespace_type type_mask;
 
44
 
 
45
        struct mailbox_info ns_info;
 
46
        struct mailbox_info inbox_info;
 
47
        const struct mailbox_info *pending_backend_info;
 
48
 
 
49
        unsigned int cur_ns_prefix_sent:1;
 
50
        unsigned int inbox_list:1;
 
51
        unsigned int inbox_listed:1;
 
52
};
 
53
 
 
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,
 
58
                                       const char *prefix);
 
59
 
 
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)
 
63
{
 
64
        const char *patterns[2];
 
65
 
 
66
        patterns[0] = pattern;
 
67
        patterns[1] = NULL;
 
68
        return mailbox_list_iter_init_multiple(list, patterns, flags);
 
69
}
 
70
 
 
71
int mailbox_list_iter_subscriptions_refresh(struct mailbox_list *list)
 
72
{
 
73
        struct mail_namespace *ns = list->ns;
 
74
 
 
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,
 
78
                                                      ns->prefix);
 
79
                if (ns == NULL) {
 
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);
 
85
                        }
 
86
                        return 0;
 
87
                }
 
88
        }
 
89
        return ns->list->v.subscriptions_refresh(ns->list, list);
 
90
}
 
91
 
 
92
static struct mailbox_settings *
 
93
mailbox_settings_add_ns_prefix(pool_t pool, struct mail_namespace *ns,
 
94
                               struct mailbox_settings *in_set)
 
95
{
 
96
        struct mailbox_settings *out_set;
 
97
 
 
98
        if (ns->prefix_len == 0 || strcasecmp(in_set->name, "INBOX") == 0)
 
99
                return in_set;
 
100
 
 
101
        out_set = p_new(pool, struct mailbox_settings, 1);
 
102
        *out_set = *in_set;
 
103
        if (*in_set->name == '\0') {
 
104
                /* namespace prefix itself */
 
105
                out_set->name = p_strndup(pool, ns->prefix, ns->prefix_len-1);
 
106
        } else {
 
107
                out_set->name =
 
108
                        p_strconcat(pool, ns->prefix, in_set->name, NULL);
 
109
        }
 
110
        return out_set;
 
111
}
 
112
 
 
113
static void
 
114
mailbox_list_iter_init_autocreate(struct mailbox_list_iterate_context *ctx)
 
115
{
 
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;
 
121
 
 
122
        if (!array_is_created(&ns->set->mailboxes))
 
123
                return;
 
124
        box_sets = array_get(&ns->set->mailboxes, &count);
 
125
        if (count == 0)
 
126
                return;
 
127
 
 
128
        actx = p_new(ctx->pool, struct mailbox_list_autocreate_iterate_context, 1);
 
129
        ctx->autocreate_ctx = actx;
 
130
 
 
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)
 
137
                        continue;
 
138
 
 
139
                set = mailbox_settings_add_ns_prefix(ctx->pool,
 
140
                                                     ns, box_sets[i]);
 
141
 
 
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;
 
149
                        autobox->set = set;
 
150
                        if (strcasecmp(autobox->name, "INBOX") == 0) {
 
151
                                /* make sure duplicate INBOX/Inbox/etc.
 
152
                                   won't get created */
 
153
                                autobox->name = "INBOX";
 
154
                        }
 
155
                }
 
156
        }
 
157
}
 
158
 
 
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)
 
163
{
 
164
        struct mailbox_list_iterate_context *ctx;
 
165
        int ret = 0;
 
166
 
 
167
        i_assert(*patterns != NULL);
 
168
 
 
169
        if ((flags & (MAILBOX_LIST_ITER_SELECT_SUBSCRIBED |
 
170
                      MAILBOX_LIST_ITER_RETURN_SUBSCRIBED)) != 0)
 
171
                ret = mailbox_list_iter_subscriptions_refresh(list);
 
172
 
 
173
        ctx = list->v.iter_init(list, patterns, flags);
 
174
        if (ret < 0)
 
175
                ctx->failed = TRUE;
 
176
        else if ((flags & MAILBOX_LIST_ITER_NO_AUTO_BOXES) == 0)
 
177
                mailbox_list_iter_init_autocreate(ctx);
 
178
        return ctx;
 
179
}
 
180
 
 
181
static bool
 
182
ns_match_simple(struct ns_list_iterate_context *ctx, struct mail_namespace *ns)
 
183
{
 
184
        if ((ctx->type_mask & ns->type) == 0)
 
185
                return FALSE;
 
186
 
 
187
        if ((ctx->ctx.flags & MAILBOX_LIST_ITER_SKIP_ALIASES) != 0) {
 
188
                if (ns->alias_for != NULL)
 
189
                        return FALSE;
 
190
        }
 
191
        return TRUE;
 
192
}
 
193
 
 
194
static bool
 
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)
 
198
{
 
199
        if ((ctx->ctx.flags & MAILBOX_LIST_ITER_STAR_WITHIN_NS) == 0) {
 
200
                switch (result) {
 
201
                case IMAP_MATCH_YES:
 
202
                case IMAP_MATCH_CHILDREN:
 
203
                        return TRUE;
 
204
                case IMAP_MATCH_NO:
 
205
                case IMAP_MATCH_PARENT:
 
206
                        break;
 
207
                }
 
208
                return FALSE;
 
209
        }
 
210
 
 
211
        switch (result) {
 
212
        case IMAP_MATCH_YES:
 
213
                /* allow matching prefix only when it's done without
 
214
                   wildcards */
 
215
                if (strcmp(prefix_without_sep, pattern) == 0)
 
216
                        return TRUE;
 
217
                break;
 
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;
 
223
 
 
224
                T_BEGIN {
 
225
                        for (tmp = ns->next; tmp != NULL; tmp = tmp->next) {
 
226
                                if (ns_match_simple(ctx, tmp) &&
 
227
                                    ns_match_next(ctx, tmp, pattern))
 
228
                                        break;
 
229
                        }
 
230
                } T_END;
 
231
                if (tmp == NULL)
 
232
                        return TRUE;
 
233
                break;
 
234
        }
 
235
        case IMAP_MATCH_NO:
 
236
        case IMAP_MATCH_PARENT:
 
237
                break;
 
238
        }
 
239
        return FALSE;
 
240
}
 
241
 
 
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
        unsigned int 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
                /* prefix="" list=no is never listed */
 
260
                if (ns->prefix_len == 0)
 
261
                        return FALSE;
 
262
        }
 
263
 
 
264
        prefix_without_sep = t_strndup(ns->prefix, len);
 
265
        if (*prefix_without_sep == '\0')
 
266
                result = IMAP_MATCH_CHILDREN;
 
267
        else {
 
268
                glob = imap_match_init(pool_datastack_create(), pattern,
 
269
                                       TRUE, mail_namespace_get_sep(ns));
 
270
                result = imap_match(glob, prefix_without_sep);
 
271
        }
 
272
 
 
273
        return ns_is_match_within_ns(ctx, ns, prefix_without_sep,
 
274
                                     pattern, result);
 
275
}
 
276
 
 
277
static bool
 
278
mailbox_list_ns_match_patterns(struct ns_list_iterate_context *ctx)
 
279
{
 
280
        struct mail_namespace *ns = ctx->cur_ns;
 
281
        unsigned int i;
 
282
 
 
283
        if (!ns_match_simple(ctx, ns))
 
284
                return FALSE;
 
285
 
 
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 */
 
289
        T_BEGIN {
 
290
                for (i = 0; ctx->patterns_ns_match[i] != NULL; i++) {
 
291
                        if (ns_match_next(ctx, ns, ctx->patterns_ns_match[i]))
 
292
                                break;
 
293
                }
 
294
        } T_END;
 
295
 
 
296
        return ctx->patterns_ns_match[i] != NULL;
 
297
}
 
298
 
 
299
static bool
 
300
iter_next_try_prefix_pattern(struct ns_list_iterate_context *ctx,
 
301
                             struct mail_namespace *ns, const char *pattern)
 
302
{
 
303
        struct imap_match_glob *glob;
 
304
        enum imap_match_result result;
 
305
        const char *prefix_without_sep;
 
306
 
 
307
        i_assert(ns->prefix_len > 0);
 
308
 
 
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)
 
313
                        return FALSE;
 
314
        }
 
315
 
 
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,
 
322
                                      pattern, result);
 
323
}
 
324
 
 
325
static bool
 
326
mailbox_list_ns_prefix_match(struct ns_list_iterate_context *ctx,
 
327
                             struct mail_namespace *ns)
 
328
{
 
329
        unsigned int i;
 
330
        bool ret = FALSE;
 
331
 
 
332
        for (i = 0; ctx->patterns_ns_match[i] != NULL; i++) {
 
333
                T_BEGIN {
 
334
                        ret = iter_next_try_prefix_pattern(ctx, ns,
 
335
                                                ctx->patterns_ns_match[i]);
 
336
                } T_END;
 
337
                if (ret)
 
338
                        break;
 
339
        }
 
340
        return ret;
 
341
}
 
342
 
 
343
static bool
 
344
ns_prefix_is_visible(struct ns_list_iterate_context *ctx,
 
345
                     struct mail_namespace *ns)
 
346
{
 
347
        if ((ns->flags & NAMESPACE_FLAG_LIST_PREFIX) != 0)
 
348
                return TRUE;
 
349
        if ((ns->flags & NAMESPACE_FLAG_LIST_CHILDREN) != 0) {
 
350
                if (mailbox_list_match_anything(ctx, ns, ns->prefix))
 
351
                        return TRUE;
 
352
        }
 
353
        return FALSE;
 
354
}
 
355
 
 
356
static bool
 
357
ns_prefix_has_visible_child_namespace(struct ns_list_iterate_context *ctx,
 
358
                                      const char *prefix)
 
359
{
 
360
        struct mail_namespace *ns;
 
361
        unsigned int prefix_len = strlen(prefix);
 
362
 
 
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))
 
367
                        return TRUE;
 
368
        }
 
369
        return FALSE;
 
370
}
 
371
 
 
372
static bool
 
373
mailbox_ns_prefix_is_shared_inbox(struct mail_namespace *ns)
 
374
{
 
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;
 
378
}
 
379
 
 
380
static bool
 
381
mailbox_is_shared_inbox(struct mail_namespace *ns, const char *vname)
 
382
{
 
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';
 
386
}
 
387
 
 
388
static int
 
389
mailbox_list_match_anything(struct ns_list_iterate_context *ctx,
 
390
                            struct mail_namespace *ns, const char *prefix)
 
391
{
 
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;
 
396
        const char *pattern;
 
397
        int ret;
 
398
 
 
399
        if (ns_prefix_has_visible_child_namespace(ctx, prefix))
 
400
                return 1;
 
401
 
 
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);
 
409
        }
 
410
        ret = info != NULL ? 1 : 0;
 
411
        if (mailbox_list_iter_deinit(&list_iter) < 0) {
 
412
                if (ret == 0)
 
413
                        ret = -1;
 
414
        }
 
415
        return ret;
 
416
}
 
417
 
 
418
static bool
 
419
mailbox_ns_prefix_check_selection_criteria(struct ns_list_iterate_context *ctx)
 
420
{
 
421
        if ((ctx->ctx.flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) {
 
422
                if ((ctx->ns_info.flags & MAILBOX_SUBSCRIBED) != 0)
 
423
                        return TRUE;
 
424
                if ((ctx->ctx.flags & MAILBOX_LIST_ITER_SELECT_RECURSIVEMATCH) != 0 &&
 
425
                    (ctx->ns_info.flags & MAILBOX_CHILD_SUBSCRIBED) != 0)
 
426
                        return TRUE;
 
427
                return FALSE;
 
428
        }
 
429
        return TRUE;
 
430
}
 
431
 
 
432
static bool
 
433
mailbox_list_ns_prefix_return(struct ns_list_iterate_context *ctx,
 
434
                              struct mail_namespace *ns, bool has_children)
 
435
{
 
436
        struct mailbox *box;
 
437
        enum mailbox_existence existence;
 
438
        int ret;
 
439
 
 
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;
 
445
        }
 
446
 
 
447
        if (ns->prefix_len == 0 || !mailbox_list_ns_prefix_match(ctx, ns))
 
448
                return FALSE;
 
449
 
 
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,
 
453
                                       ns->prefix_len-1);
 
454
        if (ns->special_use_mailboxes)
 
455
                ctx->ns_info.flags |= MAILBOX_CHILD_SPECIALUSE;
 
456
 
 
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;
 
461
        }
 
462
 
 
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,
 
466
                                                    ctx->ns_info.vname,
 
467
                                                    &ctx->ns_info.flags);
 
468
        }
 
469
        if (!mailbox_ns_prefix_check_selection_criteria(ctx))
 
470
                return FALSE;
 
471
 
 
472
        /* see if the namespace has children */
 
473
        if (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;
 
480
                else if (ret == 0) {
 
481
                        if ((ns->flags & NAMESPACE_FLAG_LIST_CHILDREN) != 0 &&
 
482
                            !mailbox_ns_prefix_is_shared_inbox(ns)) {
 
483
                                /* no children -> not visible */
 
484
                                return FALSE;
 
485
                        }
 
486
                        ctx->ns_info.flags |= MAILBOX_NOCHILDREN;
 
487
                }
 
488
        }
 
489
 
 
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;
 
497
                else
 
498
                        ctx->ns_info.flags |= MAILBOX_NONEXISTENT;
 
499
                mailbox_free(&box);
 
500
        }
 
501
        return TRUE;
 
502
}
 
503
 
 
504
static void inbox_set_children_flags(struct ns_list_iterate_context *ctx)
 
505
{
 
506
        const char *prefix;
 
507
        int ret;
 
508
 
 
509
        if ((ctx->ctx.flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) != 0)
 
510
                return;
 
511
        if ((ctx->inbox_info.flags & (MAILBOX_CHILDREN | MAILBOX_NOINFERIORS |
 
512
                                      MAILBOX_NOCHILDREN)) != 0)
 
513
                return;
 
514
 
 
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;
 
520
                return;
 
521
        }
 
522
 
 
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);
 
529
        if (ret > 0)
 
530
                ctx->inbox_info.flags |= MAILBOX_CHILDREN;
 
531
        else if (ret == 0)
 
532
                ctx->inbox_info.flags |= MAILBOX_NOCHILDREN;
 
533
}
 
534
 
 
535
static bool
 
536
mailbox_list_ns_iter_try_next(struct mailbox_list_iterate_context *_ctx,
 
537
                              const struct mailbox_info **info_r)
 
538
{
 
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;
 
544
        const char *errstr;
 
545
        bool has_children;
 
546
 
 
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;
 
553
                        return TRUE;
 
554
                }
 
555
                *info_r = NULL;
 
556
                return TRUE;
 
557
        }
 
558
 
 
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 */
 
564
                        ns = ctx->cur_ns;
 
565
                        ctx->cur_ns = ctx->cur_ns->next;
 
566
                        if (mailbox_list_ns_prefix_return(ctx, ns, FALSE)) {
 
567
                                *info_r = &ctx->ns_info;
 
568
                                return TRUE;
 
569
                        }
 
570
                        return FALSE;
 
571
                }
 
572
                /* start listing this namespace's mailboxes */
 
573
                ctx->backend_ctx =
 
574
                        mailbox_list_iter_init_multiple(ctx->cur_ns->list,
 
575
                                                        ctx->patterns,
 
576
                                                        _ctx->flags);
 
577
                ctx->cur_ns_prefix_sent = FALSE;
 
578
        }
 
579
        if (ctx->pending_backend_info == NULL)
 
580
                info = mailbox_list_iter_next(ctx->backend_ctx);
 
581
        else {
 
582
                info = ctx->pending_backend_info;
 
583
                ctx->pending_backend_info = NULL;
 
584
        }
 
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,
 
591
                                                  has_children)) {
 
592
                        ctx->pending_backend_info = info;
 
593
                        *info_r = &ctx->ns_info;
 
594
                        return TRUE;
 
595
                }
 
596
        }
 
597
        if (info != NULL) {
 
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 |
 
604
                                                MAILBOX_SUBSCRIBED |
 
605
                                                MAILBOX_CHILD_SUBSCRIBED));
 
606
                        return FALSE;
 
607
                }
 
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;
 
613
                }
 
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). */
 
622
                        return FALSE;
 
623
                }
 
624
 
 
625
                *info_r = info;
 
626
                return TRUE;
 
627
        }
 
628
 
 
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,
 
632
                                                     &error);
 
633
                mailbox_list_set_error(ctx->error_list, error, errstr);
 
634
                _ctx->failed = TRUE;
 
635
        }
 
636
        ctx->cur_ns = ctx->cur_ns->next;
 
637
        return FALSE;
 
638
}
 
639
 
 
640
static const struct mailbox_info *
 
641
mailbox_list_ns_iter_next(struct mailbox_list_iterate_context *_ctx)
 
642
{
 
643
        const struct mailbox_info *info = NULL;
 
644
 
 
645
        while (!mailbox_list_ns_iter_try_next(_ctx, &info)) ;
 
646
        return info;
 
647
}
 
648
 
 
649
static int
 
650
mailbox_list_ns_iter_deinit(struct mailbox_list_iterate_context *_ctx)
 
651
{
 
652
        struct ns_list_iterate_context *ctx =
 
653
                (struct ns_list_iterate_context *)_ctx;
 
654
        enum mail_error error;
 
655
        const char *errstr;
 
656
        int ret;
 
657
 
 
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,
 
661
                                                             &error);
 
662
                        mailbox_list_set_error(ctx->error_list, error, errstr);
 
663
                        _ctx->failed = TRUE;
 
664
                }
 
665
        }
 
666
        ret = _ctx->failed ? -1 : 0;
 
667
        pool_unref(&ctx->pool);
 
668
        return ret;
 
669
}
 
670
 
 
671
static const char **
 
672
dup_patterns_without_stars(pool_t pool, const char *const *patterns,
 
673
                           unsigned int count)
 
674
{
 
675
        const char **dup;
 
676
        unsigned int i;
 
677
 
 
678
        dup = p_new(pool, const char *, count + 1);
 
679
        for (i = 0; i < count; i++) {
 
680
                char *p = p_strdup(pool, patterns[i]);
 
681
                dup[i] = p;
 
682
 
 
683
                for (; *p != '\0'; p++) {
 
684
                        if (*p == '*')
 
685
                                *p = '%';
 
686
                }
 
687
        }
 
688
        return dup;
 
689
}
 
690
 
 
691
static bool
 
692
patterns_match_inbox(struct mail_namespace *namespaces,
 
693
                     const char *const *patterns)
 
694
{
 
695
        struct mail_namespace *ns = mail_namespace_find_inbox(namespaces);
 
696
        struct imap_match_glob *glob;
 
697
 
 
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;
 
701
}
 
702
 
 
703
static void inbox_info_init(struct ns_list_iterate_context *ctx,
 
704
                            struct mail_namespace *namespaces)
 
705
{
 
706
        enum mailbox_info_flags flags;
 
707
 
 
708
        ctx->inbox_info.vname = "INBOX";
 
709
        ctx->inbox_info.ns = mail_namespace_find_inbox(namespaces);
 
710
        i_assert(ctx->inbox_info.ns != NULL);
 
711
 
 
712
        if (mailbox_list_mailbox(ctx->inbox_info.ns->list, "INBOX", &flags) > 0)
 
713
                ctx->inbox_info.flags = flags;
 
714
}
 
715
 
 
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)
 
721
{
 
722
        struct ns_list_iterate_context *ctx;
 
723
        unsigned int i, count;
 
724
        pool_t pool;
 
725
 
 
726
        i_assert(namespaces != NULL);
 
727
 
 
728
        pool = pool_alloconly_create("mailbox list namespaces", 1024);
 
729
        ctx = p_new(pool, struct ns_list_iterate_context, 1);
 
730
        ctx->pool = pool;
 
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;
 
738
 
 
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);
 
751
        }
 
752
 
 
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);
 
758
        } else {
 
759
                ctx->patterns_ns_match = ctx->patterns;
 
760
        }
 
761
 
 
762
        ctx->cur_ns = namespaces;
 
763
        ctx->ctx.list->ns = namespaces;
 
764
        return &ctx->ctx;
 
765
}
 
766
 
 
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)
 
771
{
 
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);
 
776
 
 
777
        *idx_r = UINT_MAX;
 
778
 
 
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)
 
783
                        continue;
 
784
                len = I_MIN(name_len, strlen(sets[i]->name));
 
785
                if (strncmp(name, sets[i]->name, len) != 0)
 
786
                        continue;
 
787
 
 
788
                if (name[len] == '\0' && sets[i]->name[len] == '\0') {
 
789
                        result |= AUTOCREATE_MATCH_RESULT_YES;
 
790
                        *idx_r = i;
 
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;
 
795
        }
 
796
        return result;
 
797
}
 
798
 
 
799
static const struct mailbox_info *
 
800
autocreate_iter_existing(struct mailbox_list_iterate_context *ctx)
 
801
{
 
802
        struct mailbox_list_autocreate_iterate_context *actx =
 
803
                ctx->autocreate_ctx;
 
804
        struct mailbox_info *info = &actx->new_info;
 
805
        enum autocreate_match_result match, match2;
 
806
        unsigned int idx;
 
807
 
 
808
        match = autocreate_box_match(&actx->box_sets, ctx->list->ns,
 
809
                                     info->vname, FALSE, &idx);
 
810
 
 
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);
 
816
        }
 
817
 
 
818
        if ((match & AUTOCREATE_MATCH_RESULT_CHILDREN) != 0) {
 
819
                if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0)
 
820
                        info->flags |= MAILBOX_CHILD_SUBSCRIBED;
 
821
                else {
 
822
                        info->flags &= ~MAILBOX_NOCHILDREN;
 
823
                        info->flags |= MAILBOX_CHILDREN;
 
824
                }
 
825
        }
 
826
 
 
827
        /* make sure the mailbox existence flags are correct. */
 
828
        if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0)
 
829
                match2 = match;
 
830
        else {
 
831
                match2 = autocreate_box_match(&actx->all_ns_box_sets,
 
832
                                              ctx->list->ns, info->vname,
 
833
                                              FALSE, &idx);
 
834
        }
 
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;
 
840
        }
 
841
 
 
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,
 
847
                                              TRUE, &idx);
 
848
                if ((match2 & AUTOCREATE_MATCH_RESULT_YES) != 0) {
 
849
                        /* mailbox is also marked as autosubscribe */
 
850
                        info->flags |= MAILBOX_SUBSCRIBED;
 
851
                }
 
852
                if ((match2 & AUTOCREATE_MATCH_RESULT_CHILDREN) != 0) {
 
853
                        /* mailbox also has a children marked as
 
854
                           autosubscribe */
 
855
                        info->flags |= MAILBOX_CHILD_SUBSCRIBED;
 
856
                }
 
857
        }
 
858
 
 
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);
 
865
 
 
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)
 
870
                                continue;
 
871
 
 
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;
 
877
                }
 
878
        }
 
879
        return info;
 
880
}
 
881
 
 
882
static bool autocreate_iter_autobox(struct mailbox_list_iterate_context *ctx,
 
883
                                    const struct autocreate_box *autobox)
 
884
{
 
885
        struct mailbox_list_autocreate_iterate_context *actx =
 
886
                ctx->autocreate_ctx;
 
887
        enum imap_match_result match;
 
888
 
 
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;
 
893
 
 
894
        if ((ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0)
 
895
                actx->new_info.flags |= MAILBOX_SUBSCRIBED;
 
896
 
 
897
        if ((actx->new_info.flags & MAILBOX_CHILDREN) == 0)
 
898
                actx->new_info.flags |= MAILBOX_NOCHILDREN;
 
899
 
 
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;
 
905
                return TRUE;
 
906
        }
 
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);
 
910
                const char *p;
 
911
 
 
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;
 
919
                }
 
920
                if ((old_flags & MAILBOX_SUBSCRIBED) != 0)
 
921
                        actx->new_info.flags |= MAILBOX_CHILD_SUBSCRIBED;
 
922
                do {
 
923
                        p = strrchr(actx->new_info.vname, sep);
 
924
                        i_assert(p != NULL);
 
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);
 
930
                return TRUE;
 
931
        }
 
932
        return FALSE;
 
933
}
 
934
 
 
935
static const struct mailbox_info *
 
936
mailbox_list_iter_next_call(struct mailbox_list_iterate_context *ctx)
 
937
{
 
938
        const struct mailbox_info *info;
 
939
        const struct mailbox_settings *set;
 
940
 
 
941
        info = ctx->list->v.iter_next(ctx);
 
942
        if (info == NULL)
 
943
                return NULL;
 
944
 
 
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 :
 
952
                                set->special_use;
 
953
                        info = &ctx->specialuse_info;
 
954
                }
 
955
        }
 
956
        return info;
 
957
}
 
958
 
 
959
static const struct mailbox_info *
 
960
autocreate_iter_next(struct mailbox_list_iterate_context *ctx)
 
961
{
 
962
        struct mailbox_list_autocreate_iterate_context *actx =
 
963
                ctx->autocreate_ctx;
 
964
        const struct mailbox_info *info;
 
965
        const struct autocreate_box *autoboxes, *autobox;
 
966
        unsigned int count;
 
967
 
 
968
        if (actx->idx == 0) {
 
969
                info = mailbox_list_iter_next_call(ctx);
 
970
                if (info != NULL) {
 
971
                        actx->new_info = *info;
 
972
                        return autocreate_iter_existing(ctx);
 
973
                }
 
974
        }
 
975
 
 
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;
 
982
        }
 
983
        i_assert(array_count(&actx->boxes) == array_count(&actx->box_sets));
 
984
        return NULL;
 
985
}
 
986
 
 
987
static bool
 
988
special_use_selection(struct mailbox_list_iterate_context *ctx,
 
989
                      const struct mailbox_info *info)
 
990
{
 
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)
 
996
                        return TRUE;
 
997
        }
 
998
        return (ctx->flags & MAILBOX_LIST_ITER_SELECT_SPECIALUSE) == 0 ||
 
999
                info->special_use != NULL;
 
1000
}
 
1001
 
 
1002
const struct mailbox_info *
 
1003
mailbox_list_iter_next(struct mailbox_list_iterate_context *ctx)
 
1004
{
 
1005
        const struct mailbox_info *info;
 
1006
 
 
1007
        do {
 
1008
                T_BEGIN {
 
1009
                        if (ctx->autocreate_ctx != NULL)
 
1010
                                info = autocreate_iter_next(ctx);
 
1011
                        else
 
1012
                                info = mailbox_list_iter_next_call(ctx);
 
1013
                } T_END;
 
1014
        } while (info != NULL && !special_use_selection(ctx, info));
 
1015
        return info;
 
1016
}
 
1017
 
 
1018
int mailbox_list_iter_deinit(struct mailbox_list_iterate_context **_ctx)
 
1019
{
 
1020
        struct mailbox_list_iterate_context *ctx = *_ctx;
 
1021
 
 
1022
        *_ctx = NULL;
 
1023
 
 
1024
        return ctx->list->v.iter_deinit(ctx);
 
1025
}
 
1026
 
 
1027
static void node_fix_parents(struct mailbox_node *node)
 
1028
{
 
1029
        /* If we happened to create any of the parents, we need to mark them
 
1030
           nonexistent. */
 
1031
        node = node->parent;
 
1032
        for (; node != NULL; node = node->parent) {
 
1033
                if ((node->flags & MAILBOX_MATCHED) == 0)
 
1034
                        node->flags |= MAILBOX_NONEXISTENT;
 
1035
        }
 
1036
}
 
1037
 
 
1038
static void
 
1039
mailbox_list_iter_update_real(struct mailbox_list_iter_update_context *ctx,
 
1040
                              const char *name)
 
1041
{
 
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;
 
1046
        const char *p;
 
1047
        bool created, add_matched;
 
1048
 
 
1049
        create_flags = MAILBOX_NOCHILDREN;
 
1050
        always_flags = ctx->leaf_flags;
 
1051
        add_matched = TRUE;
 
1052
 
 
1053
        for (;;) {
 
1054
                created = FALSE;
 
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);
 
1060
                        if (created) {
 
1061
                                node->flags = create_flags;
 
1062
                                if (create_flags != 0)
 
1063
                                        node_fix_parents(node);
 
1064
                        }
 
1065
                        if (node != NULL) {
 
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;
 
1071
                        }
 
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;
 
1076
                } else {
 
1077
                        if ((match & IMAP_MATCH_PARENT) == 0)
 
1078
                                break;
 
1079
                        /* We've a (possibly) non-subscribed parent mailbox
 
1080
                           which has a subscribed child mailbox. Make sure we
 
1081
                           return the parent mailbox. */
 
1082
                }
 
1083
 
 
1084
                if (!ctx->match_parents)
 
1085
                        break;
 
1086
 
 
1087
                /* see if parent matches */
 
1088
                p = strrchr(name, mail_namespace_get_sep(ns));
 
1089
                if (p == NULL)
 
1090
                        break;
 
1091
 
 
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;
 
1096
        }
 
1097
}
 
1098
 
 
1099
void mailbox_list_iter_update(struct mailbox_list_iter_update_context *ctx,
 
1100
                              const char *name)
 
1101
{
 
1102
        T_BEGIN {
 
1103
                mailbox_list_iter_update_real(ctx, name);
 
1104
        } T_END;
 
1105
}