static struct mailbox_node * imapc_list_update_tree(struct imapc_mailbox_list *list, struct mailbox_tree_context *tree, const struct imap_arg *args) { struct mailbox_node *node; const struct imap_arg *flags; const char *name, *flag; enum mailbox_info_flags info_flag, info_flags = 0; bool created; if (!imap_arg_get_list(&args[0], &flags) || args[1].type == IMAP_ARG_EOL || !imap_arg_get_astring(&args[2], &name)) return NULL; while (imap_arg_get_atom(flags, &flag)) { if (imap_list_flag_parse(flag, &info_flag)) info_flags |= info_flag; flags++; } T_BEGIN { const char *vname = imapc_list_to_vname(list, name); if ((info_flags & MAILBOX_NONEXISTENT) != 0) node = mailbox_tree_lookup(tree, vname); else node = mailbox_tree_get(tree, vname, &created); } T_END; if (node != NULL) node->flags = info_flags; return node; }
void mailbox_list_set_subscription_flags(struct mailbox_list *list, const char *vname, enum mailbox_info_flags *flags) { struct mailbox_node *node; *flags &= ~(MAILBOX_SUBSCRIBED | MAILBOX_CHILD_SUBSCRIBED); node = mailbox_tree_lookup(list->subscriptions, vname); if (node != NULL) { *flags |= node->flags & MAILBOX_SUBSCRIBED; /* the only reason why node might have a child is if one of them is subscribed */ if (node->children != NULL) *flags |= MAILBOX_CHILD_SUBSCRIBED; } }
static int maildir_fill_inbox(struct maildir_list_iterate_context *ctx, struct imap_match_glob *glob, const char *inbox_name, bool update_only) { struct mailbox_node *node; enum mailbox_info_flags flags; enum imap_match_result match; bool created; int ret; if ((ctx->ctx.flags & MAILBOX_LIST_ITER_NO_AUTO_BOXES) == 0) { /* always show INBOX */ } else { /* INBOX may be Maildir root or completely elsewhere. show it only if it has already been created */ ret = mailbox_list_mailbox(ctx->ctx.list, "INBOX", &flags); if (ret < 0) return -1; if ((flags & MAILBOX_NONEXISTENT) != 0) update_only = TRUE; } if (update_only) { node = mailbox_tree_lookup(ctx->tree_ctx, inbox_name); if (node != NULL) node->flags &= ~MAILBOX_NONEXISTENT; return 0; } /* add the INBOX only if it matches the patterns */ match = imap_match(glob, inbox_name); if (match == IMAP_MATCH_PARENT) maildir_fill_parents(ctx, glob, FALSE, inbox_name); else if (match == IMAP_MATCH_YES) { node = mailbox_tree_get(ctx->tree_ctx, inbox_name, &created); if (created) node->flags = MAILBOX_NOCHILDREN; else node->flags &= ~MAILBOX_NONEXISTENT; node->flags |= MAILBOX_MATCHED; } return 0; }
static void maildir_fill_parents(struct maildir_list_iterate_context *ctx, struct imap_match_glob *glob, bool update_only, const char *vname) { struct mail_namespace *ns = ctx->ctx.list->ns; struct mailbox_node *node; const char *p; unsigned int vname_len = strlen(vname); bool created; char ns_sep = mail_namespace_get_sep(ns); while ((p = strrchr(vname, ns_sep)) != NULL) { vname = t_strdup_until(vname, p); if (imap_match(glob, vname) != IMAP_MATCH_YES) continue; if (ns->prefix_len > 0 && vname_len == ns->prefix_len-1 && strncmp(vname, ns->prefix, ns->prefix_len - 1) == 0 && vname[ns->prefix_len-1] == ns_sep) { /* don't return matches to namespace prefix itself */ continue; } created = FALSE; node = update_only ? mailbox_tree_lookup(ctx->tree_ctx, vname) : mailbox_tree_get(ctx->tree_ctx, vname, &created); if (node != NULL) { if (created) { /* we haven't yet seen this mailbox, but we might see it later */ node->flags = MAILBOX_NONEXISTENT; } if (!update_only) node->flags |= MAILBOX_MATCHED; node->flags |= MAILBOX_CHILDREN; node->flags &= ~MAILBOX_NOCHILDREN; node_fix_parents(node); } } }
static const struct mailbox_info * acl_mailbox_list_iter_next_info(struct acl_mailbox_list_iterate_context *ctx) { struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(ctx->ctx.list); const struct mailbox_info *info; while ((info = alist->module_ctx.super.iter_next(ctx->super_ctx)) != NULL) { /* if we've a list of mailboxes with LOOKUP rights, skip the mailboxes not in the list (since we know they can't be visible to us). */ if (ctx->lookup_boxes == NULL || mailbox_tree_lookup(ctx->lookup_boxes, info->vname) != NULL) break; if (ctx->ctx.list->ns->user->mail_debug) { i_debug("acl: Mailbox not in dovecot-acl-list: %s", info->vname); } } return info; }
static void maildir_set_children(struct maildir_list_iterate_context *ctx, const char *vname) { struct mailbox_node *node; const char *p; char hierarchy_sep; hierarchy_sep = mail_namespace_get_sep(ctx->ctx.list->ns); /* mark the first existing parent as containing children */ while ((p = strrchr(vname, hierarchy_sep)) != NULL) { vname = t_strdup_until(vname, p); node = mailbox_tree_lookup(ctx->tree_ctx, vname); if (node != NULL) { node->flags &= ~MAILBOX_NOCHILDREN; node->flags |= MAILBOX_CHILDREN; break; } } }
static bool iter_mailbox_has_visible_children(struct acl_mailbox_list_iterate_context *ctx, bool only_nonpatterns, bool subscribed) { struct mailbox_list_iterate_context *iter; const struct mailbox_info *info; string_t *pattern; const char *prefix; unsigned int i, prefix_len; bool stars = FALSE, ret = FALSE; /* do we have child mailboxes with LOOKUP right that don't match the list pattern? */ if (ctx->lookup_boxes != NULL) { /* we have a list of mailboxes with LOOKUP rights. before starting the slow list iteration, check check first if there even are any children with LOOKUP rights. */ struct mailbox_node *node; node = mailbox_tree_lookup(ctx->lookup_boxes, ctx->info.vname); i_assert(node != NULL); if (node->children == NULL) return FALSE; } /* if mailbox name has '*' characters in it, they'll conflict with the LIST wildcard. replace then with '%' and verify later that all results have the correct prefix. */ pattern = t_str_new(128); for (i = 0; ctx->info.vname[i] != '\0'; i++) { if (ctx->info.vname[i] != '*') str_append_c(pattern, ctx->info.vname[i]); else { stars = TRUE; str_append_c(pattern, '%'); } } if (i > 0 && ctx->info.vname[i-1] != ctx->sep) str_append_c(pattern, ctx->sep); str_append_c(pattern, '*'); prefix = str_c(pattern); prefix_len = str_len(pattern) - 1; iter = mailbox_list_iter_init(ctx->ctx.list, str_c(pattern), (!subscribed ? 0 : MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) | MAILBOX_LIST_ITER_RETURN_NO_FLAGS); while ((info = mailbox_list_iter_next(iter)) != NULL) { if (only_nonpatterns && imap_match(ctx->ctx.glob, info->vname) == IMAP_MATCH_YES) { /* at least one child matches also the original list patterns. we don't need to show this mailbox. */ ret = FALSE; break; } if (!stars || strncmp(info->vname, prefix, prefix_len) == 0) ret = TRUE; } (void)mailbox_list_iter_deinit(&iter); return ret; }
static int maildir_fill_readdir_entry(struct maildir_list_iterate_context *ctx, struct imap_match_glob *glob, const struct dirent *d, bool update_only) { struct mailbox_list *list = ctx->ctx.list; const char *fname, *storage_name, *vname; enum mailbox_info_flags flags; enum imap_match_result match; struct mailbox_node *node; bool created; int ret; fname = d->d_name; if (fname[0] == ctx->prefix_char) storage_name = fname + 1; else { if (ctx->prefix_char != '\0' || fname[0] == '.') return 0; storage_name = fname; } /* skip . and .. */ if (fname[0] == '.' && (fname[1] == '\0' || (fname[1] == '.' && fname[2] == '\0'))) return 0; vname = mailbox_list_get_vname(list, storage_name); if (!uni_utf8_str_is_valid(vname)) { /* the storage_name is completely invalid, rename it to something more sensible. we could do this for all names that aren't valid mUTF-7, but that might lead to accidents in future when UTF-8 storage names are used */ const char *src = t_strdup_printf("%s/%s", ctx->dir, fname); string_t *destvname = t_str_new(128); string_t *dest = t_str_new(128); (void)uni_utf8_get_valid_data((const void *)fname, strlen(fname), destvname); str_append(dest, ctx->dir); str_append_c(dest, '/'); (void)imap_utf8_to_utf7(str_c(destvname), dest); if (rename(src, str_c(dest)) < 0 && errno != ENOENT) i_error("rename(%s, %s) failed: %m", src, str_c(dest)); /* just skip this in this iteration, we'll see it on the next list */ return 0; } /* make sure the pattern matches */ match = imap_match(glob, vname); if ((match & (IMAP_MATCH_YES | IMAP_MATCH_PARENT)) == 0) return 0; /* check if this is an actual mailbox */ if (maildir_delete_trash_dir(ctx, fname)) return 0; if ((ctx->ctx.flags & MAILBOX_LIST_ITER_SKIP_ALIASES) != 0) { ret = mailbox_list_dirent_is_alias_symlink(list, ctx->dir, d); if (ret != 0) return ret < 0 ? -1 : 0; } T_BEGIN { ret = list->v.get_mailbox_flags(list, ctx->dir, fname, mailbox_list_get_file_type(d), &flags); } T_END; if (ret <= 0) return ret; /* we know the children flags ourself, so ignore if any of them were set. */ flags &= ~(MAILBOX_NOINFERIORS | MAILBOX_CHILDREN | MAILBOX_NOCHILDREN); if ((match & IMAP_MATCH_PARENT) != 0) maildir_fill_parents(ctx, glob, update_only, vname); else { created = FALSE; node = update_only ? mailbox_tree_lookup(ctx->tree_ctx, vname) : mailbox_tree_get(ctx->tree_ctx, vname, &created); if (node != NULL) { if (created) node->flags = MAILBOX_NOCHILDREN; else node->flags &= ~MAILBOX_NONEXISTENT; if (!update_only) node->flags |= MAILBOX_MATCHED; node->flags |= flags; node_fix_parents(node); } else { i_assert(update_only); maildir_set_children(ctx, vname); } } return 0; }