~ubuntu-branches/ubuntu/trusty/dovecot/trusty-updates

« back to all changes in this revision

Viewing changes to src/imap/cmd-list.c

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2014-01-08 09:35:49 UTC
  • mfrom: (1.15.3) (96.1.1 trusty-proposed)
  • Revision ID: package-import@ubuntu.com-20140108093549-814nkqdcxfbvgktg
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) 2002-2012 Dovecot authors, see the included COPYING file */
 
1
/* Copyright (c) 2002-2013 Dovecot authors, see the included COPYING file */
2
2
 
3
3
#include "imap-common.h"
4
4
#include "array.h"
5
5
#include "str.h"
6
6
#include "strescape.h"
 
7
#include "mailbox-list-iter.h"
7
8
#include "imap-utf7.h"
8
9
#include "imap-quote.h"
9
10
#include "imap-match.h"
10
11
#include "imap-status.h"
11
12
#include "imap-commands.h"
12
 
#include "mail-namespace.h"
 
13
#include "imap-list.h"
13
14
 
14
15
struct cmd_list_context {
15
16
        struct client_command_context *cmd;
16
 
        const char *ref;
17
 
        const char *const *patterns;
 
17
        struct mail_user *user;
 
18
 
18
19
        enum mailbox_list_iter_flags list_flags;
19
20
        struct imap_status_items status_items;
20
 
        enum mailbox_info_flags inbox_flags;
21
21
 
22
 
        struct mail_namespace *ns;
23
22
        struct mailbox_list_iterate_context *list_iter;
24
23
 
25
 
        ARRAY_DEFINE(ns_prefixes_listed, struct mail_namespace *);
26
 
 
27
24
        unsigned int lsub:1;
28
25
        unsigned int lsub_no_unsubscribed:1;
29
 
        unsigned int inbox_found:1;
30
 
        unsigned int seen_inbox_namespace:1;
31
 
        unsigned int cur_ns_match_inbox:1;
32
 
        unsigned int cur_ns_send_prefix:1;
33
 
        unsigned int cur_ns_skip_trailing_sep:1;
34
26
        unsigned int used_listext:1;
35
27
        unsigned int used_status:1;
36
28
};
49
41
        if ((ctx->list_flags & MAILBOX_LIST_ITER_RETURN_CHILDREN) == 0)
50
42
                flags &= ~(MAILBOX_CHILDREN|MAILBOX_NOCHILDREN);
51
43
 
52
 
        if ((flags & MAILBOX_SUBSCRIBED) != 0 &&
53
 
            (ctx->list_flags & MAILBOX_LIST_ITER_RETURN_SUBSCRIBED) != 0)
54
 
                str_append(str, "\\Subscribed ");
55
 
 
56
44
        if ((flags & MAILBOX_CHILD_SUBSCRIBED) != 0 &&
57
45
            (flags & MAILBOX_SUBSCRIBED) == 0 && !ctx->used_listext) {
58
46
                /* LSUB uses \Noselect for this */
59
47
                flags |= MAILBOX_NOSELECT;
60
 
        }
61
 
 
62
 
        if ((flags & MAILBOX_NOSELECT) != 0)
63
 
                str_append(str, "\\Noselect ");
64
 
        if ((flags & MAILBOX_NONEXISTENT) != 0)
65
 
                str_append(str, "\\NonExistent ");
66
 
 
67
 
        if ((flags & MAILBOX_CHILDREN) != 0)
68
 
                str_append(str, "\\HasChildren ");
69
 
        else if ((flags & MAILBOX_NOINFERIORS) != 0)
70
 
                str_append(str, "\\NoInferiors ");
71
 
        else if ((flags & MAILBOX_NOCHILDREN) != 0)
72
 
                str_append(str, "\\HasNoChildren ");
73
 
 
74
 
        if ((flags & MAILBOX_MARKED) != 0)
75
 
                str_append(str, "\\Marked ");
76
 
        if ((flags & MAILBOX_UNMARKED) != 0)
77
 
                str_append(str, "\\UnMarked ");
 
48
        } else if ((ctx->list_flags & MAILBOX_LIST_ITER_RETURN_SUBSCRIBED) == 0)
 
49
                flags &= ~MAILBOX_SUBSCRIBED;
 
50
        imap_mailbox_flags2str(str, flags);
78
51
 
79
52
        if ((ctx->list_flags & MAILBOX_LIST_ITER_RETURN_SPECIALUSE) != 0 &&
80
53
            special_use != NULL) {
 
54
                if (str_len(str) != orig_len)
 
55
                        str_append_c(str, ' ');
81
56
                str_append(str, special_use);
82
 
                str_append_c(str, ' ');
83
57
        }
84
 
        if (str_len(str) != orig_len)
85
 
                str_truncate(str, str_len(str)-1);
86
58
}
87
59
 
88
60
static void
95
67
        if ((flags & MAILBOX_CHILD_SUBSCRIBED) != 0 &&
96
68
            (ctx->list_flags & MAILBOX_LIST_ITER_SELECT_RECURSIVEMATCH) != 0)
97
69
                str_append(str, " (CHILDINFO (\"SUBSCRIBED\"))");
 
70
        if ((flags & MAILBOX_CHILD_SPECIALUSE) != 0 &&
 
71
            (ctx->list_flags & MAILBOX_LIST_ITER_SELECT_RECURSIVEMATCH) != 0)
 
72
                str_append(str, " (CHILDINFO (\"SPECIAL-USE\"))");
98
73
}
99
74
 
100
75
static bool
181
156
        return TRUE;
182
157
}
183
158
 
184
 
static enum mailbox_info_flags
185
 
list_get_inbox_flags(struct cmd_list_context *ctx)
186
 
{
187
 
        struct mail_namespace *ns;
188
 
        struct mailbox_list_iterate_context *list_iter;
189
 
        const struct mailbox_info *info;
190
 
        enum mailbox_info_flags flags = MAILBOX_UNMARKED;
191
 
 
192
 
        if (ctx->seen_inbox_namespace &&
193
 
            (ctx->ns->flags & NAMESPACE_FLAG_INBOX_USER) == 0) {
194
 
                /* INBOX doesn't exist. use the default INBOX flags */
195
 
                return flags;
196
 
        }
197
 
 
198
 
        /* find the INBOX flags */
199
 
        ns = mail_namespace_find_inbox(ctx->cmd->client->user->namespaces);
200
 
        list_iter = mailbox_list_iter_init(ns->list, "INBOX", 0);
201
 
        info = mailbox_list_iter_next(list_iter);
202
 
        if (info != NULL) {
203
 
                i_assert(strcasecmp(info->name, "INBOX") == 0);
204
 
                flags = info->flags;
205
 
        }
206
 
        (void)mailbox_list_iter_deinit(&list_iter);
207
 
        return flags;
208
 
}
209
 
 
210
 
static bool list_namespace_has_children(struct cmd_list_context *ctx)
211
 
{
212
 
        enum mailbox_list_iter_flags list_flags =
213
 
                MAILBOX_LIST_ITER_RETURN_NO_FLAGS;
214
 
        struct mailbox_list_iterate_context *list_iter;
215
 
        const struct mailbox_info *info;
216
 
        bool ret = FALSE;
217
 
 
218
 
        if ((ctx->list_flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0)
219
 
                list_flags |= MAILBOX_LIST_ITER_SELECT_SUBSCRIBED;
220
 
 
221
 
        list_iter = mailbox_list_iter_init(ctx->ns->list,
222
 
                t_strconcat(ctx->ns->prefix, "%", NULL), list_flags);
223
 
        info = mailbox_list_iter_next(list_iter);
224
 
        if (info != NULL)
225
 
                ret = TRUE;
226
 
        if (mailbox_list_iter_deinit(&list_iter) < 0) {
227
 
                /* safer to answer TRUE in error conditions */
228
 
                ret = TRUE;
229
 
        }
230
 
        return ret;
231
 
}
232
 
 
233
159
static const char *ns_prefix_mutf7(struct mail_namespace *ns)
234
160
{
235
161
        string_t *str;
243
169
        return str_c(str);
244
170
}
245
171
 
246
 
static const char *ns_get_listed_prefix(struct cmd_list_context *ctx)
247
 
{
248
 
        struct imap_match_glob *glob;
249
 
        enum imap_match_result match;
250
 
        const char *ns_prefix, *p;
251
 
        bool inboxcase;
252
 
 
253
 
        inboxcase = strncasecmp(ctx->ns->prefix, "INBOX", 5) == 0 &&
254
 
                ctx->ns->prefix[5] == mail_namespace_get_sep(ctx->ns);
255
 
        glob = imap_match_init_multiple(pool_datastack_create(),
256
 
                                        ctx->patterns, inboxcase,
257
 
                                        mail_namespace_get_sep(ctx->ns));
258
 
        ns_prefix = ctx->ns->prefix;
259
 
        match = imap_match(glob, ns_prefix);
260
 
        if (match == IMAP_MATCH_YES) {
261
 
                return !ctx->cur_ns_skip_trailing_sep ? ns_prefix :
262
 
                        t_strndup(ns_prefix, strlen(ns_prefix)-1);
263
 
        }
264
 
 
265
 
        while ((match & IMAP_MATCH_PARENT) != 0) {
266
 
                p = strrchr(ns_prefix, mail_namespace_get_sep(ctx->ns));
267
 
                i_assert(p != NULL);
268
 
                ns_prefix = t_strdup_until(ns_prefix, p);
269
 
                match = imap_match(glob, ns_prefix);
270
 
        }
271
 
        i_assert(match == IMAP_MATCH_YES);
272
 
        return ns_prefix;
273
 
}
274
 
 
275
172
static void list_reply_append_ns_sep_param(string_t *str, char sep)
276
173
{
277
174
        str_append_c(str, '"');
283
180
}
284
181
 
285
182
static void
286
 
list_namespace_send_prefix(struct cmd_list_context *ctx, bool have_children)
287
 
{
288
 
        struct mail_namespace *const *listed;
289
 
        const struct mailbox_settings *mailbox_set;
290
 
        struct mailbox *box;
291
 
        enum mailbox_existence existence;
292
 
        unsigned int len;
293
 
        enum mailbox_info_flags flags;
294
 
        const char *name;
295
 
        string_t *str, *mutf7_name;
296
 
        bool same_ns, ends_with_sep;
297
 
        char ns_sep = mail_namespace_get_sep(ctx->ns);
298
 
 
299
 
        ctx->cur_ns_send_prefix = FALSE;
300
 
 
301
 
        /* see if we already listed this as a valid mailbox in another
302
 
           namespace */
303
 
        array_foreach(&ctx->ns_prefixes_listed, listed) {
304
 
                if (*listed == ctx->ns)
305
 
                        return;
306
 
        }
307
 
 
308
 
        name = ns_get_listed_prefix(ctx);
309
 
        len = strlen(ctx->ns->prefix);
310
 
        ends_with_sep = ctx->ns->prefix[len-1] == ns_sep;
311
 
 
312
 
        /* we may be listing namespace's parent. in such case we always want to
313
 
           set the name as nonexistent. */
314
 
        same_ns = strcmp(name, ctx->ns->prefix) == 0 ||
315
 
                (strncmp(name, ctx->ns->prefix, len - 1) == 0 && ends_with_sep);
316
 
        if (len == 6 && strncasecmp(ctx->ns->prefix, "INBOX", len-1) == 0 &&
317
 
            ends_with_sep) {
318
 
                /* INBOX namespace needs to be handled specially. */
319
 
                if (ctx->inbox_found) {
320
 
                        /* we're just now going to send it */
321
 
                        return;
322
 
                }
323
 
 
324
 
                ctx->inbox_found = TRUE;
325
 
                flags = list_get_inbox_flags(ctx);
326
 
        } else if (!same_ns) {
327
 
                /* parent */
328
 
                flags = MAILBOX_NONEXISTENT;
329
 
        } else {
330
 
                /* see if namespace prefix is selectable */
331
 
                box = mailbox_alloc(ctx->ns->list, name, 0);
332
 
                if (mailbox_exists(box, TRUE, &existence) == 0 &&
333
 
                    existence == MAILBOX_EXISTENCE_SELECT)
334
 
                        flags = MAILBOX_SELECT;
335
 
                else
336
 
                        flags = MAILBOX_NONEXISTENT;
337
 
                mailbox_free(&box);
338
 
        }
339
 
 
340
 
        if ((flags & MAILBOX_CHILDREN) == 0) {
341
 
                if (have_children || list_namespace_has_children(ctx)) {
342
 
                        flags |= MAILBOX_CHILDREN;
343
 
                        flags &= ~MAILBOX_NOCHILDREN;
344
 
                } else {
345
 
                        flags |= MAILBOX_NOCHILDREN;
346
 
                }
347
 
        }
348
 
 
349
 
        if ((ctx->ns->flags & NAMESPACE_FLAG_LIST_CHILDREN) != 0 ||
350
 
            (ctx->list_flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) {
351
 
                if (have_children) {
352
 
                        /* children are going to be listed. */
353
 
                        return;
354
 
                }
355
 
                if ((flags & MAILBOX_CHILDREN) == 0) {
356
 
                        /* namespace has no children. don't show it. */
357
 
                        return;
358
 
                }
359
 
                /* namespace has children but they don't match the list
360
 
                   pattern. the prefix itself matches though, so show it. */
361
 
        }
362
 
 
363
 
        str = t_str_new(128);
364
 
        str_printfa(str, "* %s (", ctx->lsub ? "LSUB" : "LIST");
365
 
        if (ctx->lsub)
366
 
                flags |= MAILBOX_NONEXISTENT;
367
 
        mailbox_set = (ctx->list_flags & MAILBOX_LIST_ITER_RETURN_SPECIALUSE) == 0 ? NULL :
368
 
                mailbox_settings_find(ctx->cmd->client->user, name);
369
 
        mailbox_flags2str(ctx, str, mailbox_set == NULL ? NULL :
370
 
                          mailbox_set->special_use, flags);
371
 
        str_append(str, ") ");
372
 
        list_reply_append_ns_sep_param(str, ns_sep);
373
 
        str_append_c(str, ' ');
374
 
 
375
 
        mutf7_name = t_str_new(64);
376
 
        if (imap_utf8_to_utf7(name, mutf7_name) < 0)
377
 
                i_panic("Namespace prefix not UTF-8: %s", name);
378
 
        imap_quote_append_string(str, str_c(mutf7_name), FALSE);
379
 
        mailbox_childinfo2str(ctx, str, flags);
380
 
 
381
 
        client_send_line(ctx->cmd->client, str_c(str));
382
 
}
383
 
 
384
 
static void
385
183
list_send_status(struct cmd_list_context *ctx, const char *name,
386
184
                 const char *mutf7_name, enum mailbox_info_flags flags)
387
185
{
388
186
        struct imap_status_result result;
389
187
        struct mail_namespace *ns;
390
 
        const char *error;
391
188
 
392
189
        if ((flags & (MAILBOX_NONEXISTENT | MAILBOX_NOSELECT)) != 0) {
393
190
                /* doesn't exist, don't even try to get STATUS */
401
198
 
402
199
        /* if we're listing subscriptions and there are subscriptions=no
403
200
           namespaces, ctx->ns may not point to correct one */
404
 
        ns = mail_namespace_find(ctx->ns->user->namespaces, name);
 
201
        ns = mail_namespace_find(ctx->user->namespaces, name);
405
202
        if (imap_status_get(ctx->cmd, ns, name,
406
 
                            &ctx->status_items, &result, &error) < 0) {
 
203
                            &ctx->status_items, &result) < 0) {
407
204
                client_send_line(ctx->cmd->client,
408
 
                                 t_strconcat("* ", error, NULL));
 
205
                                 t_strconcat("* ", result.errstr, NULL));
409
206
                return;
410
207
        }
411
208
 
413
210
                         &ctx->status_items, &result);
414
211
}
415
212
 
416
 
static bool list_has_empty_prefix_ns(struct mail_user *user)
417
 
{
418
 
        struct mail_namespace *ns;
419
 
 
420
 
        ns = mail_namespace_find_prefix(user->namespaces, "");
421
 
        return ns != NULL && (ns->flags & (NAMESPACE_FLAG_LIST_PREFIX |
422
 
                                           NAMESPACE_FLAG_LIST_CHILDREN)) != 0;
423
 
}
424
 
 
425
 
static int
426
 
list_namespace_mailboxes(struct cmd_list_context *ctx)
427
 
{
 
213
static bool cmd_list_continue(struct client_command_context *cmd)
 
214
{
 
215
        struct cmd_list_context *ctx = cmd->context;
428
216
        const struct mailbox_info *info;
429
 
        struct mail_namespace *ns;
430
217
        enum mailbox_info_flags flags;
431
218
        string_t *str, *mutf7_name;
432
219
        const char *name;
433
220
        int ret = 0;
434
221
 
 
222
        if (cmd->cancel) {
 
223
                if (ctx->list_iter != NULL)
 
224
                        (void)mailbox_list_iter_deinit(&ctx->list_iter);
 
225
                return TRUE;
 
226
        }
435
227
        str = t_str_new(256);
436
228
        mutf7_name = t_str_new(128);
437
229
        while ((info = mailbox_list_iter_next(ctx->list_iter)) != NULL) {
438
 
                name = info->name;
 
230
                name = info->vname;
439
231
                flags = info->flags;
440
232
 
441
 
                if (strcasecmp(name, "INBOX") == 0) {
442
 
                        if (ctx->inbox_found) {
443
 
                                /* we already listed this at the beginning
444
 
                                   of handling INBOX/ namespace */
445
 
                                continue;
446
 
                        }
447
 
                        if ((ctx->ns->flags & NAMESPACE_FLAG_INBOX_USER) == 0) {
448
 
                                /* INBOX is in non-empty prefix namespace,
449
 
                                   and we're now listing prefixless namespace
450
 
                                   that contains INBOX. There's no way we can
451
 
                                   show this mailbox. */
452
 
                                ctx->inbox_flags = flags &
453
 
                                        (MAILBOX_CHILDREN|MAILBOX_NOCHILDREN);
454
 
                                continue;
455
 
                        }
456
 
 
457
 
                        if (*info->ns->prefix != '\0' &&
458
 
                            list_has_empty_prefix_ns(info->ns->user)) {
459
 
                                /* INBOX is in its own namespace, while a
460
 
                                   namespace with prefix="" has its children. */
461
 
                                flags &= ~(MAILBOX_CHILDREN|MAILBOX_NOCHILDREN|
462
 
                                           MAILBOX_NOINFERIORS);
463
 
                                flags |= ctx->inbox_flags;
464
 
                        }
465
 
                        ctx->inbox_found = TRUE;
466
 
                }
467
 
                if (ctx->cur_ns_send_prefix)
468
 
                        list_namespace_send_prefix(ctx, TRUE);
469
 
 
470
 
                /* if there's a namespace with this name, list it as
471
 
                   having children */
472
 
                ns = mail_namespace_find_prefix_nosep(ctx->ns, name);
473
 
                if (ns != NULL) {
474
 
                        flags |= MAILBOX_CHILDREN;
475
 
                        flags &= ~MAILBOX_NOCHILDREN;
476
 
                        array_append(&ctx->ns_prefixes_listed, &ns, 1);
477
 
                }
478
 
 
479
233
                if ((flags & MAILBOX_CHILD_SUBSCRIBED) != 0 &&
480
234
                    (flags & MAILBOX_SUBSCRIBED) == 0 &&
481
235
                    ctx->lsub_no_unsubscribed) {
493
247
                mailbox_flags2str(ctx, str, info->special_use, flags);
494
248
                str_append(str, ") ");
495
249
                list_reply_append_ns_sep_param(str,
496
 
                        mail_namespace_get_sep(ctx->ns));
 
250
                        mail_namespace_get_sep(info->ns));
497
251
                str_append_c(str, ' ');
498
 
                imap_quote_append_string(str, str_c(mutf7_name), FALSE);
 
252
                imap_append_astring(str, str_c(mutf7_name));
499
253
                mailbox_childinfo2str(ctx, str, flags);
500
254
 
501
 
                ret = client_send_line(ctx->cmd->client, str_c(str));
 
255
                ret = client_send_line_next(ctx->cmd->client, str_c(str));
502
256
                if (ctx->used_status) T_BEGIN {
503
257
                        list_send_status(ctx, name, str_c(mutf7_name), flags);
504
258
                } T_END;
505
259
                if (ret == 0) {
506
260
                        /* buffer is full, continue later */
507
 
                        return 0;
508
 
                }
509
 
        }
510
 
 
511
 
        if (mailbox_list_iter_deinit(&ctx->list_iter) < 0)
512
 
                ret = -1;
513
 
 
514
 
        return ret < 0 ? -1 : 1;
515
 
}
516
 
 
517
 
static bool list_pattern_has_wildcards(const char *pattern)
518
 
{
519
 
        for (; *pattern != '\0'; pattern++) {
520
 
                if (*pattern == '%' || *pattern == '*')
521
 
                        return TRUE;
522
 
        }
523
 
        return FALSE;
524
 
}
525
 
 
526
 
static void
527
 
skip_namespace_prefix(const char **prefix, const char **pattern,
528
 
                      bool inbox_check, char sep)
529
 
{
530
 
        size_t pattern_len, prefix_len;
531
 
        bool match;
532
 
 
533
 
        prefix_len = strlen(*prefix);
534
 
        pattern_len = strlen(*pattern);
535
 
 
536
 
        if (pattern_len < prefix_len) {
537
 
                /* eg. namespace prefix = "INBOX.", pattern = "INBOX" */
538
 
                return;
539
 
        }
540
 
 
541
 
        match = strncmp(*prefix, *pattern, prefix_len) == 0;
542
 
        if (!match && inbox_check) {
543
 
                /* try INBOX check. */
544
 
                match = prefix_len >= 5 &&
545
 
                        strncasecmp(*prefix, *pattern, 5) == 0 &&
546
 
                        strncmp(*prefix + 5, *pattern + 5,
547
 
                                prefix_len - 5) == 0 &&
548
 
                        strncasecmp(*prefix, "INBOX", 5) == 0 &&
549
 
                        ((*prefix)[5] == sep || (*prefix)[5] == '\0');
550
 
        }
551
 
 
552
 
        if (match) {
553
 
                *prefix += prefix_len;
554
 
                *pattern += prefix_len;
555
 
        }
556
 
}
557
 
 
558
 
static bool
559
 
skip_namespace_prefix_ref(struct cmd_list_context *ctx,
560
 
                          const char **cur_ns_prefix_p,
561
 
                          const char **cur_ref_p)
562
 
{
563
 
        const char *cur_ns_prefix = *cur_ns_prefix_p;
564
 
        const char *cur_ref = *cur_ref_p;
565
 
 
566
 
        if (*cur_ref != '\0') {
567
 
                /* reference argument given. skip namespace prefix using it.
568
 
 
569
 
                   cur_ns_prefix = foo/bar/
570
 
                   cur_ref = foo/
571
 
                     -> cur_ns_prefix=bar/, cur_ref=""
572
 
                   cur_ref = foo/bar/baz
573
 
                     -> cur_ns_prefix="", cur_ref="baz"
574
 
                   */
575
 
                skip_namespace_prefix(&cur_ns_prefix, &cur_ref, TRUE,
576
 
                                      mail_namespace_get_sep(ctx->ns));
577
 
 
578
 
                if (*cur_ref != '\0' && *cur_ns_prefix != '\0') {
579
 
                        /* reference parameter didn't match with
580
 
                           namespace prefix. skip this. */
581
 
                        return FALSE;
582
 
                }
583
 
        }
584
 
 
585
 
        *cur_ns_prefix_p = cur_ns_prefix;
586
 
        *cur_ref_p = cur_ref;
587
 
        return TRUE;
588
 
}
589
 
 
590
 
static void
591
 
skip_namespace_prefix_pattern(struct cmd_list_context *ctx,
592
 
                              const char **cur_ns_prefix_p,
593
 
                              const char *cur_ref, const char **cur_pattern_p)
594
 
{
595
 
        const char *cur_ns_prefix = *cur_ns_prefix_p;
596
 
        const char *cur_pattern = *cur_pattern_p;
597
 
        const char *old_ns_prefix = cur_ns_prefix;
598
 
        const char *old_pattern = cur_pattern;
599
 
 
600
 
        if (*cur_ns_prefix == '\0')
601
 
                return;
602
 
 
603
 
        /* skip namespace prefix using pattern */
604
 
        i_assert(*cur_ref == '\0');
605
 
 
606
 
        skip_namespace_prefix(&cur_ns_prefix, &cur_pattern,
607
 
                              cur_ref == ctx->ref,
608
 
                              mail_namespace_get_sep(ctx->ns));
609
 
 
610
 
        if (*cur_pattern == '\0' && *cur_ns_prefix == '\0') {
611
 
                /* trying to list the namespace prefix itself. */
612
 
                cur_ns_prefix = old_ns_prefix;
613
 
                cur_pattern = old_pattern;
614
 
        }
615
 
 
616
 
        *cur_ns_prefix_p = cur_ns_prefix;
617
 
        *cur_pattern_p = cur_pattern;
618
 
}
619
 
 
620
 
static enum imap_match_result
621
 
list_use_inboxcase(struct cmd_list_context *ctx)
622
 
{
623
 
        struct imap_match_glob *inbox_glob;
624
 
        const char *const *pat;
625
 
        enum imap_match_result match, ret;
626
 
 
627
 
        if (*ctx->ns->prefix != '\0' &&
628
 
            (ctx->ns->flags & NAMESPACE_FLAG_INBOX_USER) == 0)
629
 
                return IMAP_MATCH_NO;
630
 
 
631
 
        /* if the original reference and pattern combined produces something
632
 
           that matches INBOX, the INBOX casing is on. */
633
 
        ret = IMAP_MATCH_NO;
634
 
        for (pat = ctx->patterns; *pat != NULL; pat++) {
635
 
                inbox_glob =
636
 
                        imap_match_init(pool_datastack_create(),
637
 
                                        t_strconcat(ctx->ref, *pat, NULL),
638
 
                                        TRUE, mail_namespace_get_sep(ctx->ns));
639
 
                match = imap_match(inbox_glob, "INBOX");
640
 
 
641
 
                if (match == IMAP_MATCH_YES)
642
 
                        return IMAP_MATCH_YES;
643
 
                if ((match & IMAP_MATCH_PARENT) != 0)
644
 
                        ret = IMAP_MATCH_PARENT;
645
 
        }
646
 
        return ret;
647
 
}
648
 
 
649
 
static bool
650
 
list_want_send_prefix(struct cmd_list_context *ctx, const char *pattern)
651
 
{
652
 
        /* don't send the prefix if we're listing subscribed mailboxes */
653
 
        if ((ctx->list_flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0)
654
 
                return FALSE;
655
 
 
656
 
        /* send the prefix if namespace is listable. if children are listable
657
 
           we may or may not need to send it. */
658
 
        if ((ctx->ns->flags & (NAMESPACE_FLAG_LIST_PREFIX |
659
 
                               NAMESPACE_FLAG_LIST_CHILDREN)) != 0)
660
 
                return TRUE;
661
 
 
662
 
        /* ..or if pattern is exactly the same as namespace prefix.
663
 
           some clients (mutt) want to do LIST "" prefix. */
664
 
        for (; *pattern != '\0'; pattern++) {
665
 
                if (*pattern == '*' || *pattern == '%')
666
 
                        break;
667
 
        }
668
 
        return *pattern == '\0';
669
 
}
670
 
 
671
 
static bool
672
 
list_namespace_match_pattern(struct cmd_list_context *ctx, bool inboxcase,
673
 
                             const char *cur_ref, const char *cur_ns_prefix,
674
 
                             const char *cur_pattern)
675
 
{
676
 
        const char *orig_cur_pattern = cur_pattern;
677
 
        struct mail_namespace *ns = ctx->ns;
678
 
        struct imap_match_glob *pat_glob;
679
 
        enum imap_match_result match;
680
 
        const char *p;
681
 
        size_t len;
682
 
 
683
 
        skip_namespace_prefix_pattern(ctx, &cur_ns_prefix,
684
 
                                      cur_ref, &cur_pattern);
685
 
        if (*cur_ns_prefix == '\0') {
686
 
                if (*ns->prefix == '\0') {
687
 
                        /* no namespace prefix: if list=no we don't want to
688
 
                           show anything, except when the client does e.g.
689
 
                           LIST "" mailbox. prefix="", list=no namespace is
690
 
                           mainly useful for working around client bugs. */
691
 
                        if ((ns->flags & NAMESPACE_FLAG_LIST_PREFIX) == 0 &&
692
 
                            list_pattern_has_wildcards(cur_pattern))
693
 
                                return FALSE;
694
 
                }
695
 
                return TRUE;
696
 
        }
697
 
 
698
 
        /* namespace prefix still wasn't completely skipped over.
699
 
           for example cur_ns_prefix=INBOX/, pattern=%/% or pattern=IN%.
700
 
           Check that pattern matches namespace prefix. */
701
 
        i_assert(*cur_ref == '\0');
702
 
 
703
 
        /* drop the trailing separator in namespace prefix.
704
 
           don't do it if we're listing only the prefix itself
705
 
           (LIST "" foo/ needs to return "foo/" entry) */
706
 
        len = strlen(cur_ns_prefix);
707
 
        if (cur_ns_prefix[len-1] == mail_namespace_get_sep(ns) &&
708
 
            strcmp(cur_pattern, cur_ns_prefix) != 0) {
709
 
                ctx->cur_ns_skip_trailing_sep = TRUE;
710
 
                cur_ns_prefix = t_strndup(cur_ns_prefix, len-1);
711
 
        }
712
 
 
713
 
        /* hidden and non-listable namespaces are invisible to wildcards */
714
 
        if ((ns->flags & (NAMESPACE_FLAG_LIST_PREFIX |
715
 
                          NAMESPACE_FLAG_LIST_CHILDREN)) == 0 &&
716
 
            list_pattern_has_wildcards(cur_pattern))
717
 
                return FALSE;
718
 
 
719
 
        /* check if this namespace prefix matches the current pattern */
720
 
        pat_glob = imap_match_init(pool_datastack_create(), orig_cur_pattern,
721
 
                                   inboxcase, mail_namespace_get_sep(ns));
722
 
        match = imap_match(pat_glob, cur_ns_prefix);
723
 
        if (match == IMAP_MATCH_YES) {
724
 
                if (list_want_send_prefix(ctx, orig_cur_pattern))
725
 
                        ctx->cur_ns_send_prefix = TRUE;
726
 
 
727
 
                /* if the pattern contains '*' characters, we'll need to
728
 
                   check our children too */
729
 
                for (p = orig_cur_pattern; *p != '\0'; p++) {
730
 
                        if (*p == '*')
731
 
                                return TRUE;
732
 
                }
733
 
                if ((ctx->list_flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) {
734
 
                        /* the namespace prefix itself may be subscribed. */
735
 
                        return TRUE;
736
 
                }
737
 
                return FALSE;
738
 
        } else {
739
 
                while ((match & IMAP_MATCH_PARENT) != 0) {
740
 
                        p = strrchr(cur_ns_prefix, mail_namespace_get_sep(ns));
741
 
                        if (p == NULL)
742
 
                                break;
743
 
                        cur_ns_prefix = t_strdup_until(cur_ns_prefix, p);
744
 
                        match = imap_match(pat_glob, cur_ns_prefix);
745
 
                }
746
 
                if (match == IMAP_MATCH_YES &&
747
 
                    mail_namespace_find_prefix_nosep(ns->user->namespaces,
748
 
                                                     cur_ns_prefix) == NULL) {
749
 
                        /* ns prefix="foo/bar/" and we're listing e.g. % */
750
 
                        if (list_want_send_prefix(ctx, orig_cur_pattern))
751
 
                                ctx->cur_ns_send_prefix = TRUE;
752
 
                }
753
 
                return match != IMAP_MATCH_NO;
754
 
        }
755
 
}
756
 
 
757
 
static void list_namespace_init(struct cmd_list_context *ctx)
758
 
{
759
 
        struct mail_namespace *ns = ctx->ns;
760
 
        const char *cur_ns_prefix, *cur_ref, *const *pat, *pattern;
761
 
        enum imap_match_result inbox_match;
762
 
        ARRAY_DEFINE(used_patterns, const char *);
763
 
        bool inboxcase;
764
 
 
765
 
        cur_ns_prefix = ns->prefix;
766
 
        cur_ref = ctx->ref;
767
 
 
768
 
        ctx->cur_ns_skip_trailing_sep = FALSE;
769
 
 
770
 
        if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0)
771
 
                ctx->seen_inbox_namespace = TRUE;
772
 
 
773
 
        if (*cur_ns_prefix != '\0') {
774
 
                /* namespace has a prefix. see if we can skip over it. */
775
 
                if (!skip_namespace_prefix_ref(ctx, &cur_ns_prefix, &cur_ref))
776
 
                        return;
777
 
        }
778
 
 
779
 
        inbox_match = list_use_inboxcase(ctx);
780
 
        ctx->cur_ns_match_inbox = inbox_match == IMAP_MATCH_YES;
781
 
        inboxcase = (inbox_match & (IMAP_MATCH_YES | IMAP_MATCH_PARENT)) != 0;
782
 
 
783
 
        t_array_init(&used_patterns, 16);
784
 
        for (pat = ctx->patterns; *pat != NULL; pat++) {
785
 
                pattern = *pat;
786
 
                /* see if pattern even has a chance of matching the
787
 
                   namespace prefix */
788
 
                if (list_namespace_match_pattern(ctx, inboxcase, cur_ref,
789
 
                                                 cur_ns_prefix, pattern)) {
790
 
                        pattern = mailbox_list_join_refpattern(ns->list,
791
 
                                                        ctx->ref, pattern);
792
 
                        array_append(&used_patterns, &pattern, 1);
793
 
                }
794
 
        }
795
 
 
796
 
        if (array_count(&used_patterns) == 0) {
797
 
                if (!ctx->cur_ns_match_inbox) {
798
 
                        /* it's possible that the namespace prefix matched,
799
 
                           even though its children didn't */
800
 
                        if (ctx->cur_ns_send_prefix)
801
 
                                list_namespace_send_prefix(ctx, FALSE);
802
 
                        return;
803
 
                }
804
 
                /* we should still list INBOX */
805
 
                pattern = "INBOX";
806
 
                array_append(&used_patterns, &pattern, 1);
807
 
        }
808
 
        (void)array_append_space(&used_patterns); /* NULL-terminate */
809
 
        pat = array_idx(&used_patterns, 0);
810
 
 
811
 
        ctx->list_iter = mailbox_list_iter_init_multiple(ns->list, pat,
812
 
                                                         ctx->list_flags);
813
 
}
814
 
 
815
 
static void list_inbox(struct cmd_list_context *ctx)
816
 
{
817
 
        string_t *str;
818
 
 
819
 
        /* INBOX always exists */
820
 
        if (!ctx->inbox_found && ctx->cur_ns_match_inbox &&
821
 
            (ctx->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 &&
822
 
            (ctx->list_flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0) {
823
 
                str = t_str_new(64);
824
 
                str_append(str, "* LIST (\\Unmarked) ");
825
 
                list_reply_append_ns_sep_param(str,
826
 
                        mail_namespace_get_sep(ctx->ns));
827
 
                str_append(str, " \"INBOX\"");
828
 
                client_send_line(ctx->cmd->client, str_c(str));
829
 
        }
830
 
}
831
 
 
832
 
static bool cmd_list_continue(struct client_command_context *cmd)
833
 
{
834
 
        struct cmd_list_context *ctx = cmd->context;
835
 
        int ret;
836
 
 
837
 
        if (cmd->cancel) {
838
 
                if (ctx->list_iter != NULL)
839
 
                        (void)mailbox_list_iter_deinit(&ctx->list_iter);
840
 
                return TRUE;
841
 
        }
842
 
        for (; ctx->ns != NULL; ctx->ns = ctx->ns->next) {
843
 
                if (ctx->list_iter == NULL) {
844
 
                        T_BEGIN {
845
 
                                list_namespace_init(ctx);
846
 
                        } T_END;
847
 
                        if (ctx->list_iter == NULL)
848
 
                                continue;
849
 
                }
850
 
 
851
 
                T_BEGIN {
852
 
                        ret = list_namespace_mailboxes(ctx);
853
 
                } T_END;
854
 
                if (ret < 0) {
855
 
                        client_send_list_error(cmd, ctx->ns->list);
856
 
                        return TRUE;
857
 
                }
858
 
                if (ret == 0)
859
 
                        return FALSE;
860
 
 
861
 
                if (ctx->cur_ns_send_prefix) {
862
 
                        /* no mailboxes in this namespace */
863
 
                        list_namespace_send_prefix(ctx, FALSE);
864
 
                }
865
 
                list_inbox(ctx);
866
 
        }
867
 
 
 
261
                        return FALSE;
 
262
                }
 
263
        }
 
264
 
 
265
        if (mailbox_list_iter_deinit(&ctx->list_iter) < 0) {
 
266
                client_send_list_error(cmd, ctx->user->namespaces->list);
 
267
                return TRUE;
 
268
        }
868
269
        client_send_tagline(cmd, !ctx->lsub ?
869
270
                            "OK List completed." :
870
271
                            "OK Lsub completed.");
871
272
        return TRUE;
872
273
}
873
274
 
 
275
static const char *const *
 
276
list_get_ref_patterns(struct cmd_list_context *ctx, const char *ref,
 
277
                      const char *const *patterns)
 
278
{
 
279
        struct mail_namespace *ns;
 
280
        const char *const *pat, *pattern;
 
281
        ARRAY(const char *) full_patterns;
 
282
 
 
283
        if (*ref == '\0')
 
284
                return patterns;
 
285
 
 
286
        ns = mail_namespace_find(ctx->user->namespaces, ref);
 
287
 
 
288
        t_array_init(&full_patterns, 16);
 
289
        for (pat = patterns; *pat != NULL; pat++) {
 
290
                pattern = mailbox_list_join_refpattern(ns->list, ref, *pat);
 
291
                array_append(&full_patterns, &pattern, 1);
 
292
        }
 
293
        array_append_zero(&full_patterns); /* NULL-terminate */
 
294
        return array_idx(&full_patterns, 0);
 
295
}
 
296
 
 
297
static void cmd_list_init(struct cmd_list_context *ctx,
 
298
                          const char *const *patterns)
 
299
{
 
300
        enum mail_namespace_type type_mask = MAIL_NAMESPACE_TYPE_MASK_ALL;
 
301
 
 
302
        ctx->list_iter =
 
303
                mailbox_list_iter_init_namespaces(ctx->user->namespaces,
 
304
                                                  patterns, type_mask,
 
305
                                                  ctx->list_flags);
 
306
}
 
307
 
874
308
static void cmd_list_ref_root(struct client *client, const char *ref)
875
309
{
876
310
        struct mail_namespace *ns;
897
331
        str_printfa(str, "%c\" ", ns_sep);
898
332
        if (*ns_prefix != '\0') {
899
333
                /* non-hidden namespace, use it as the root name */
900
 
                imap_quote_append_string(str, ns_prefix, FALSE);
 
334
                imap_append_astring(str, ns_prefix);
901
335
        } else {
902
336
                /* Hidden namespace or empty namespace prefix. We could just
903
337
                   return an empty root name, but it's safer to emulate what
907
341
 
908
342
                if (p == NULL)
909
343
                        str_append(str, "\"\"");
910
 
                else {
911
 
                        imap_quote_append_string(str,
912
 
                                                 t_strdup_until(ref, p + 1),
913
 
                                                 FALSE);
914
 
                }
 
344
                else
 
345
                        imap_append_astring(str, t_strdup_until(ref, p + 1));
915
346
        }
916
347
        client_send_line(client, str_c(str));
917
348
}
922
353
        const struct imap_arg *args, *list_args;
923
354
        unsigned int arg_count;
924
355
        struct cmd_list_context *ctx;
925
 
        ARRAY_DEFINE(patterns, const char *) = ARRAY_INIT;
926
 
        const char *pattern, *const *patterns_strarr;
 
356
        ARRAY(const char *) patterns = ARRAY_INIT;
 
357
        const char *ref, *pattern, *const *patterns_strarr;
927
358
        string_t *str;
928
359
 
929
360
        /* [(<selection options>)] <reference> <pattern>|(<pattern list>)
933
364
 
934
365
        ctx = p_new(cmd->pool, struct cmd_list_context, 1);
935
366
        ctx->cmd = cmd;
936
 
        ctx->ns = client->user->namespaces;
937
367
        ctx->lsub = lsub;
 
368
        ctx->user = client->user;
938
369
 
939
370
        cmd->context = ctx;
940
371
 
946
377
                args++;
947
378
        }
948
379
 
949
 
        if (!imap_arg_get_astring(&args[0], &ctx->ref)) {
 
380
        if (!imap_arg_get_astring(&args[0], &ref)) {
950
381
                client_send_command_error(cmd, "Invalid reference.");
951
382
                return TRUE;
952
383
        }
953
384
        str = t_str_new(64);
954
 
        if (imap_utf7_to_utf8(ctx->ref, str) == 0)
955
 
                ctx->ref = p_strdup(cmd->pool, str_c(str));
 
385
        if (imap_utf7_to_utf8(ref, str) == 0)
 
386
                ref = p_strdup(cmd->pool, str_c(str));
956
387
        str_truncate(str, 0);
957
388
 
958
389
        if (imap_arg_get_list_full(&args[1], &list_args, &arg_count)) {
966
397
                                return TRUE;
967
398
                        }
968
399
                        if (imap_utf7_to_utf8(pattern, str) == 0)
969
 
                                pattern = t_strdup(str_c(str));
 
400
                                pattern = p_strdup(cmd->pool, str_c(str));
970
401
                        array_append(&patterns, &pattern, 1);
971
402
                        str_truncate(str, 0);
972
403
                }
977
408
                        return TRUE;
978
409
                }
979
410
                if (imap_utf7_to_utf8(pattern, str) == 0)
980
 
                        pattern = str_c(str);
 
411
                        pattern = p_strdup(cmd->pool, str_c(str));
981
412
 
982
413
                p_array_init(&patterns, cmd->pool, 1);
983
414
                array_append(&patterns, &pattern, 1);
1012
443
                ctx->list_flags |= MAILBOX_LIST_ITER_RETURN_CHILDREN |
1013
444
                        MAILBOX_LIST_ITER_RETURN_SPECIALUSE;
1014
445
        }
1015
 
        ctx->list_flags |= MAILBOX_LIST_ITER_SHOW_EXISTING_PARENT;
1016
446
 
1017
447
        if (!IMAP_ARG_IS_EOL(args)) {
1018
448
                client_send_command_error(cmd, "Extra arguments.");
1019
449
                return TRUE;
1020
450
        }
1021
451
 
1022
 
        (void)array_append_space(&patterns); /* NULL-terminate */
 
452
        array_append_zero(&patterns); /* NULL-terminate */
1023
453
        patterns_strarr = array_idx(&patterns, 0);
1024
454
        if (!ctx->used_listext && !lsub && *patterns_strarr[0] == '\0') {
1025
455
                /* Only LIST ref "" gets us here */
1026
 
                cmd_list_ref_root(client, ctx->ref);
 
456
                cmd_list_ref_root(client, ref);
1027
457
                client_send_tagline(cmd, "OK List completed.");
1028
458
        } else {
1029
 
                ctx->patterns = patterns_strarr;
1030
 
                p_array_init(&ctx->ns_prefixes_listed, cmd->pool, 8);
 
459
                patterns_strarr =
 
460
                        list_get_ref_patterns(ctx, ref, patterns_strarr);
 
461
                cmd_list_init(ctx, patterns_strarr);
1031
462
 
1032
463
                if (!cmd_list_continue(cmd)) {
1033
464
                        /* unfinished */