286
list_namespace_send_prefix(struct cmd_list_context *ctx, bool have_children)
288
struct mail_namespace *const *listed;
289
const struct mailbox_settings *mailbox_set;
291
enum mailbox_existence existence;
293
enum mailbox_info_flags flags;
295
string_t *str, *mutf7_name;
296
bool same_ns, ends_with_sep;
297
char ns_sep = mail_namespace_get_sep(ctx->ns);
299
ctx->cur_ns_send_prefix = FALSE;
301
/* see if we already listed this as a valid mailbox in another
303
array_foreach(&ctx->ns_prefixes_listed, listed) {
304
if (*listed == ctx->ns)
308
name = ns_get_listed_prefix(ctx);
309
len = strlen(ctx->ns->prefix);
310
ends_with_sep = ctx->ns->prefix[len-1] == ns_sep;
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 &&
318
/* INBOX namespace needs to be handled specially. */
319
if (ctx->inbox_found) {
320
/* we're just now going to send it */
324
ctx->inbox_found = TRUE;
325
flags = list_get_inbox_flags(ctx);
326
} else if (!same_ns) {
328
flags = MAILBOX_NONEXISTENT;
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;
336
flags = MAILBOX_NONEXISTENT;
340
if ((flags & MAILBOX_CHILDREN) == 0) {
341
if (have_children || list_namespace_has_children(ctx)) {
342
flags |= MAILBOX_CHILDREN;
343
flags &= ~MAILBOX_NOCHILDREN;
345
flags |= MAILBOX_NOCHILDREN;
349
if ((ctx->ns->flags & NAMESPACE_FLAG_LIST_CHILDREN) != 0 ||
350
(ctx->list_flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) {
352
/* children are going to be listed. */
355
if ((flags & MAILBOX_CHILDREN) == 0) {
356
/* namespace has no children. don't show it. */
359
/* namespace has children but they don't match the list
360
pattern. the prefix itself matches though, so show it. */
363
str = t_str_new(128);
364
str_printfa(str, "* %s (", ctx->lsub ? "LSUB" : "LIST");
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, ' ');
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);
381
client_send_line(ctx->cmd->client, str_c(str));
385
183
list_send_status(struct cmd_list_context *ctx, const char *name,
386
184
const char *mutf7_name, enum mailbox_info_flags flags)
388
186
struct imap_status_result result;
389
187
struct mail_namespace *ns;
392
189
if ((flags & (MAILBOX_NONEXISTENT | MAILBOX_NOSELECT)) != 0) {
393
190
/* doesn't exist, don't even try to get STATUS */
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);
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);
506
260
/* buffer is full, continue later */
511
if (mailbox_list_iter_deinit(&ctx->list_iter) < 0)
514
return ret < 0 ? -1 : 1;
517
static bool list_pattern_has_wildcards(const char *pattern)
519
for (; *pattern != '\0'; pattern++) {
520
if (*pattern == '%' || *pattern == '*')
527
skip_namespace_prefix(const char **prefix, const char **pattern,
528
bool inbox_check, char sep)
530
size_t pattern_len, prefix_len;
533
prefix_len = strlen(*prefix);
534
pattern_len = strlen(*pattern);
536
if (pattern_len < prefix_len) {
537
/* eg. namespace prefix = "INBOX.", pattern = "INBOX" */
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');
553
*prefix += prefix_len;
554
*pattern += prefix_len;
559
skip_namespace_prefix_ref(struct cmd_list_context *ctx,
560
const char **cur_ns_prefix_p,
561
const char **cur_ref_p)
563
const char *cur_ns_prefix = *cur_ns_prefix_p;
564
const char *cur_ref = *cur_ref_p;
566
if (*cur_ref != '\0') {
567
/* reference argument given. skip namespace prefix using it.
569
cur_ns_prefix = foo/bar/
571
-> cur_ns_prefix=bar/, cur_ref=""
572
cur_ref = foo/bar/baz
573
-> cur_ns_prefix="", cur_ref="baz"
575
skip_namespace_prefix(&cur_ns_prefix, &cur_ref, TRUE,
576
mail_namespace_get_sep(ctx->ns));
578
if (*cur_ref != '\0' && *cur_ns_prefix != '\0') {
579
/* reference parameter didn't match with
580
namespace prefix. skip this. */
585
*cur_ns_prefix_p = cur_ns_prefix;
586
*cur_ref_p = cur_ref;
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)
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;
600
if (*cur_ns_prefix == '\0')
603
/* skip namespace prefix using pattern */
604
i_assert(*cur_ref == '\0');
606
skip_namespace_prefix(&cur_ns_prefix, &cur_pattern,
608
mail_namespace_get_sep(ctx->ns));
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;
616
*cur_ns_prefix_p = cur_ns_prefix;
617
*cur_pattern_p = cur_pattern;
620
static enum imap_match_result
621
list_use_inboxcase(struct cmd_list_context *ctx)
623
struct imap_match_glob *inbox_glob;
624
const char *const *pat;
625
enum imap_match_result match, ret;
627
if (*ctx->ns->prefix != '\0' &&
628
(ctx->ns->flags & NAMESPACE_FLAG_INBOX_USER) == 0)
629
return IMAP_MATCH_NO;
631
/* if the original reference and pattern combined produces something
632
that matches INBOX, the INBOX casing is on. */
634
for (pat = ctx->patterns; *pat != NULL; pat++) {
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");
641
if (match == IMAP_MATCH_YES)
642
return IMAP_MATCH_YES;
643
if ((match & IMAP_MATCH_PARENT) != 0)
644
ret = IMAP_MATCH_PARENT;
650
list_want_send_prefix(struct cmd_list_context *ctx, const char *pattern)
652
/* don't send the prefix if we're listing subscribed mailboxes */
653
if ((ctx->list_flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0)
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)
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 == '%')
668
return *pattern == '\0';
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)
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;
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))
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');
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);
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))
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;
727
/* if the pattern contains '*' characters, we'll need to
728
check our children too */
729
for (p = orig_cur_pattern; *p != '\0'; p++) {
733
if ((ctx->list_flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) {
734
/* the namespace prefix itself may be subscribed. */
739
while ((match & IMAP_MATCH_PARENT) != 0) {
740
p = strrchr(cur_ns_prefix, mail_namespace_get_sep(ns));
743
cur_ns_prefix = t_strdup_until(cur_ns_prefix, p);
744
match = imap_match(pat_glob, cur_ns_prefix);
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;
753
return match != IMAP_MATCH_NO;
757
static void list_namespace_init(struct cmd_list_context *ctx)
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 *);
765
cur_ns_prefix = ns->prefix;
768
ctx->cur_ns_skip_trailing_sep = FALSE;
770
if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0)
771
ctx->seen_inbox_namespace = TRUE;
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))
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;
783
t_array_init(&used_patterns, 16);
784
for (pat = ctx->patterns; *pat != NULL; pat++) {
786
/* see if pattern even has a chance of matching the
788
if (list_namespace_match_pattern(ctx, inboxcase, cur_ref,
789
cur_ns_prefix, pattern)) {
790
pattern = mailbox_list_join_refpattern(ns->list,
792
array_append(&used_patterns, &pattern, 1);
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);
804
/* we should still list INBOX */
806
array_append(&used_patterns, &pattern, 1);
808
(void)array_append_space(&used_patterns); /* NULL-terminate */
809
pat = array_idx(&used_patterns, 0);
811
ctx->list_iter = mailbox_list_iter_init_multiple(ns->list, pat,
815
static void list_inbox(struct cmd_list_context *ctx)
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) {
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));
832
static bool cmd_list_continue(struct client_command_context *cmd)
834
struct cmd_list_context *ctx = cmd->context;
838
if (ctx->list_iter != NULL)
839
(void)mailbox_list_iter_deinit(&ctx->list_iter);
842
for (; ctx->ns != NULL; ctx->ns = ctx->ns->next) {
843
if (ctx->list_iter == NULL) {
845
list_namespace_init(ctx);
847
if (ctx->list_iter == NULL)
852
ret = list_namespace_mailboxes(ctx);
855
client_send_list_error(cmd, ctx->ns->list);
861
if (ctx->cur_ns_send_prefix) {
862
/* no mailboxes in this namespace */
863
list_namespace_send_prefix(ctx, FALSE);
265
if (mailbox_list_iter_deinit(&ctx->list_iter) < 0) {
266
client_send_list_error(cmd, ctx->user->namespaces->list);
868
269
client_send_tagline(cmd, !ctx->lsub ?
869
270
"OK List completed." :
870
271
"OK Lsub completed.");
275
static const char *const *
276
list_get_ref_patterns(struct cmd_list_context *ctx, const char *ref,
277
const char *const *patterns)
279
struct mail_namespace *ns;
280
const char *const *pat, *pattern;
281
ARRAY(const char *) full_patterns;
286
ns = mail_namespace_find(ctx->user->namespaces, ref);
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);
293
array_append_zero(&full_patterns); /* NULL-terminate */
294
return array_idx(&full_patterns, 0);
297
static void cmd_list_init(struct cmd_list_context *ctx,
298
const char *const *patterns)
300
enum mail_namespace_type type_mask = MAIL_NAMESPACE_TYPE_MASK_ALL;
303
mailbox_list_iter_init_namespaces(ctx->user->namespaces,
874
308
static void cmd_list_ref_root(struct client *client, const char *ref)
876
310
struct mail_namespace *ns;