# HG changeset patch # User Timo Sirainen # Date 1296617584 -7200 # Node ID 4e4c7f982fd514dfac9234eb645dd1f6393f2ce0 # Parent fa4b84059ae25f56684903b58cf9166ffdca2a4d lib-storage: Cleaned up subscription listing internally. The subscription listing code is now mostly separated from the mailbox listing code. diff -r fa4b84059ae2 -r 4e4c7f982fd5 src/lib-storage/index/imapc/imapc-list.c --- a/src/lib-storage/index/imapc/imapc-list.c Wed Feb 02 05:31:46 2011 +0200 +++ b/src/lib-storage/index/imapc/imapc-list.c Wed Feb 02 05:33:04 2011 +0200 @@ -296,6 +296,12 @@ return ret; } +static int +imapc_list_subscriptions_refresh(struct mailbox_list *_list ATTR_UNUSED) +{ + return 0; +} + static int imapc_list_set_subscribed(struct mailbox_list *_list, const char *name, bool set) { @@ -391,6 +397,7 @@ imapc_list_iter_deinit, NULL, NULL, + imapc_list_subscriptions_refresh, imapc_list_set_subscribed, imapc_list_create_mailbox_dir, imapc_list_delete_mailbox, diff -r fa4b84059ae2 -r 4e4c7f982fd5 src/lib-storage/index/shared/shared-list.c --- a/src/lib-storage/index/shared/shared-list.c Wed Feb 02 05:31:46 2011 +0200 +++ b/src/lib-storage/index/shared/shared-list.c Wed Feb 02 05:33:04 2011 +0200 @@ -210,6 +210,12 @@ return 0; } +static int +shared_list_subscriptions_refresh(struct mailbox_list *list ATTR_UNUSED) +{ + return 0; +} + static int shared_list_set_subscribed(struct mailbox_list *list, const char *name, bool set) { @@ -329,6 +335,7 @@ shared_list_iter_deinit, NULL, NULL, + shared_list_subscriptions_refresh, shared_list_set_subscribed, shared_list_create_mailbox_dir, shared_list_delete_mailbox, diff -r fa4b84059ae2 -r 4e4c7f982fd5 src/lib-storage/list/index-mailbox-list.c --- a/src/lib-storage/list/index-mailbox-list.c Wed Feb 02 05:31:46 2011 +0200 +++ b/src/lib-storage/list/index-mailbox-list.c Wed Feb 02 05:33:04 2011 +0200 @@ -205,8 +205,8 @@ MAILBOX_LIST_ITER_RETURN_SUBSCRIBED)) != 0) { /* we'll need to know the subscriptions */ ctx->subs_tree = mailbox_tree_init(sep); - if (mailbox_list_subscriptions_fill(&ctx->ctx, ctx->subs_tree, - ctx->glob, FALSE) < 0) { + /*if (mailbox_list_subscriptions_fill(&ctx->ctx, ctx->subs_tree, + ctx->glob, FALSE) < 0)*/ { /* let the backend handle this failure */ return FALSE; } diff -r fa4b84059ae2 -r 4e4c7f982fd5 src/lib-storage/list/mailbox-list-fs-iter.c --- a/src/lib-storage/list/mailbox-list-fs-iter.c Wed Feb 02 05:31:46 2011 +0200 +++ b/src/lib-storage/list/mailbox-list-fs-iter.c Wed Feb 02 05:33:04 2011 +0200 @@ -36,8 +36,6 @@ struct mailbox_list_iterate_context ctx; ARRAY_DEFINE(valid_patterns, char *); - struct mailbox_tree_context *subs_tree; - struct mailbox_tree_iterate_context *tree_iter; char sep; enum mailbox_info_flags inbox_flags; @@ -54,8 +52,6 @@ }; static const struct mailbox_info * -fs_list_subs(struct fs_list_iterate_context *ctx); -static const struct mailbox_info * fs_list_next(struct fs_list_iterate_context *ctx); static int @@ -217,6 +213,13 @@ unsigned int prefix_len; int ret; + if ((flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) { + /* we're listing only subscribed mailboxes. we can't optimize + it, so just use the generic code. */ + return mailbox_list_subscriptions_iter_init(_list, patterns, + flags); + } + ctx = i_new(struct fs_list_iterate_context, 1); ctx->ctx.list = _list; ctx->ctx.flags = flags; @@ -261,25 +264,6 @@ ctx->ctx.glob = imap_match_init_multiple(default_pool, patterns, TRUE, ctx->sep); - if ((flags & (MAILBOX_LIST_ITER_SELECT_SUBSCRIBED | - MAILBOX_LIST_ITER_RETURN_SUBSCRIBED)) != 0) { - /* we want to return MAILBOX_SUBSCRIBED flags, possibly for all - mailboxes. Build a mailbox tree of all the subscriptions. */ - ctx->subs_tree = mailbox_tree_init(ctx->sep); - if (mailbox_list_subscriptions_fill(&ctx->ctx, ctx->subs_tree, - ctx->ctx.glob, FALSE) < 0) { - ctx->ctx.failed = TRUE; - return &ctx->ctx; - } - } - - if ((flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) { - ctx->next = fs_list_subs; - ctx->tree_iter = mailbox_tree_iterate_init(ctx->subs_tree, NULL, - MAILBOX_MATCHED); - return &ctx->ctx; - } - vpath = _list->ns->prefix; rootdir = list_get_rootdir(ctx, &vpath); if (rootdir == NULL) { @@ -319,6 +303,9 @@ unsigned int i, count; int ret = ctx->ctx.failed ? -1 : 0; + if ((_ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) + return mailbox_list_subscriptions_iter_deinit(_ctx); + patterns = array_get_modifiable(&ctx->valid_patterns, &count); for (i = 0; i < count; i++) i_free(patterns[i]); @@ -331,10 +318,6 @@ list_dir_context_free(dir); } - if (ctx->tree_iter != NULL) - mailbox_tree_iterate_deinit(&ctx->tree_iter); - if (ctx->subs_tree != NULL) - mailbox_tree_deinit(&ctx->subs_tree); if (ctx->info_pool != NULL) pool_unref(&ctx->info_pool); if (ctx->ctx.glob != NULL) @@ -352,7 +335,10 @@ (struct fs_list_iterate_context *)_ctx; const struct mailbox_info *info; - if (ctx->ctx.failed) + if ((_ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) + return mailbox_list_subscriptions_iter_next(_ctx); + + if (_ctx->failed) return NULL; T_BEGIN { @@ -362,37 +348,6 @@ return info; } -static void -path_split(const char *path, const char **dir_r, const char **fname_r) -{ - const char *p; - - p = strrchr(path, '/'); - if (p == NULL) { - *dir_r = ""; - *fname_r = path; - } else { - *dir_r = t_strdup_until(path, p); - *fname_r = p + 1; - } -} - -static enum mailbox_info_flags -fs_list_get_subscription_flags(struct fs_list_iterate_context *ctx, - const char *mailbox) -{ - struct mailbox_node *node; - - if (ctx->subs_tree == NULL) - return 0; - - node = mailbox_tree_lookup(ctx->subs_tree, mailbox); - if (node == NULL) - return 0; - - return node->flags & (MAILBOX_SUBSCRIBED | MAILBOX_CHILD_SUBSCRIBED); -} - static void inbox_flags_set(struct fs_list_iterate_context *ctx) { struct mail_namespace *ns = ctx->ctx.list->ns; @@ -433,7 +388,8 @@ (ctx->info.flags & MAILBOX_NONEXISTENT) != 0) return NULL; - ctx->info.flags |= fs_list_get_subscription_flags(ctx, "INBOX"); + mailbox_list_set_subscription_flags(ctx->ctx.list, "INBOX", + &ctx->info.flags); inbox_flags_set(ctx); /* we got here because we didn't see INBOX among other mailboxes, which means it has no children. */ @@ -613,7 +569,8 @@ return 1; } - ctx->info.flags |= fs_list_get_subscription_flags(ctx, list_path); + mailbox_list_set_subscription_flags(ctx->ctx.list, list_path, + &ctx->info.flags); /* make sure we give only one correct INBOX */ if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) { @@ -646,61 +603,6 @@ return 0; } -static const struct mailbox_info * -fs_list_subs(struct fs_list_iterate_context *ctx) -{ - struct mailbox_node *node; - enum mailbox_info_flags flags; - struct mail_namespace *ns; - const char *path, *dir, *fname, *subs_name, *storage_name; - unsigned int len; - struct stat st; - - node = mailbox_tree_iterate_next(ctx->tree_iter, &ctx->info.name); - if (node == NULL) - return NULL; - - /* subscription list has real knowledge of only subscription flags */ - flags = node->flags & (MAILBOX_SUBSCRIBED | MAILBOX_CHILD_SUBSCRIBED); - - if ((ctx->ctx.flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) != 0 && - (ctx->ctx.flags & MAILBOX_LIST_ITER_RETURN_CHILDREN) == 0) { - ctx->info.flags = flags; - return &ctx->info; - } - - /* see if this is for another subscriptions=no namespace */ - subs_name = ctx->info.name; - ns = mail_namespace_find_unsubscribable(ctx->info.ns->user->namespaces, - subs_name); - if (ns == NULL) - ns = ctx->info.ns; - - /* if name ends with hierarchy separator, drop the separator */ - len = strlen(subs_name); - if (len > 0 && subs_name[len-1] == mail_namespace_get_sep(ns)) - subs_name = t_strndup(subs_name, len-1); - - storage_name = mailbox_list_get_storage_name(ns->list, subs_name); - if (!mailbox_list_is_valid_pattern(ns->list, storage_name)) { - /* broken entry in subscriptions file */ - ctx->info.flags = MAILBOX_NONEXISTENT; - } else { - struct mailbox_list *list = ns->list; - - path = mailbox_list_get_path(list, storage_name, - MAILBOX_LIST_PATH_TYPE_DIR); - path_split(path, &dir, &fname); - if (list->v.get_mailbox_flags(list, dir, fname, - MAILBOX_LIST_FILE_TYPE_UNKNOWN, - &st, &ctx->info.flags) < 0) - ctx->ctx.failed = TRUE; - } - - ctx->info.flags |= flags; - return &ctx->info; -} - static const struct list_dir_entry * fs_list_dir_next(struct fs_list_iterate_context *ctx) { diff -r fa4b84059ae2 -r 4e4c7f982fd5 src/lib-storage/list/mailbox-list-fs.c --- a/src/lib-storage/list/mailbox-list-fs.c Wed Feb 02 05:31:46 2011 +0200 +++ b/src/lib-storage/list/mailbox-list-fs.c Wed Feb 02 05:33:04 2011 +0200 @@ -6,6 +6,7 @@ #include "mailbox-log.h" #include "subscription-file.h" #include "mail-storage.h" +#include "mailbox-list-subscriptions.h" #include "mailbox-list-delete.h" #include "mailbox-list-fs.h" @@ -593,6 +594,7 @@ fs_list_iter_deinit, fs_list_get_mailbox_flags, NULL, + mailbox_list_subscriptions_refresh, fs_list_set_subscribed, fs_list_create_mailbox_dir, fs_list_delete_mailbox, diff -r fa4b84059ae2 -r 4e4c7f982fd5 src/lib-storage/list/mailbox-list-maildir-iter.c --- a/src/lib-storage/list/mailbox-list-maildir-iter.c Wed Feb 02 05:31:46 2011 +0200 +++ b/src/lib-storage/list/mailbox-list-maildir-iter.c Wed Feb 02 05:33:04 2011 +0200 @@ -388,55 +388,6 @@ } } -static int -maildir_fill_other_ns_subscriptions(struct maildir_list_iterate_context *ctx, - struct mail_namespace *ns) -{ - struct mailbox_list_iterate_context *iter; - const struct mailbox_info *info; - struct mailbox_node *node; - - iter = mailbox_list_iter_init(ns->list, "*", - MAILBOX_LIST_ITER_RETURN_CHILDREN); - while ((info = mailbox_list_iter_next(iter)) != NULL) { - node = mailbox_tree_lookup(ctx->tree_ctx, info->name); - if (node != NULL) { - node->flags &= ~MAILBOX_NONEXISTENT; - node->flags |= info->flags; - } - } - if (mailbox_list_iter_deinit(&iter) < 0) { - enum mail_error error; - const char *errstr; - - errstr = mailbox_list_get_last_error(ns->list, &error); - mailbox_list_set_error(ctx->ctx.list, error, errstr); - return -1; - } - return 0; -} - -static int -maildir_fill_other_subscriptions(struct maildir_list_iterate_context *ctx) -{ - struct mail_namespace *ns; - const char *path; - - ns = ctx->ctx.list->ns->user->namespaces; - for (; ns != NULL; ns = ns->next) { - if ((ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) != 0 || - ns->prefix_len == 0) - continue; - - path = t_strndup(ns->prefix, ns->prefix_len-1); - if (mailbox_tree_lookup(ctx->tree_ctx, path) != NULL) { - if (maildir_fill_other_ns_subscriptions(ctx, ns) < 0) - return -1; - } - } - return 0; -} - struct mailbox_list_iterate_context * maildir_list_iter_init(struct mailbox_list *_list, const char *const *patterns, enum mailbox_list_iter_flags flags) @@ -466,11 +417,7 @@ if ((flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) { /* Listing only subscribed mailboxes. Flags are set later if needed. */ - if (mailbox_list_subscriptions_fill(&ctx->ctx, ctx->tree_ctx, - ctx->ctx.glob, FALSE) < 0) { - ctx->ctx.failed = TRUE; - return &ctx->ctx; - } + mailbox_list_subscriptions_fill(&ctx->ctx, ctx->tree_ctx); } if ((flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0 || @@ -489,28 +436,6 @@ } } - if ((flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0 && - (flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) == 0) { - /* if there are subscriptions=no namespaces, we may have some - of their subscriptions whose flags need to be filled */ - ret = maildir_fill_other_subscriptions(ctx); - if (ret < 0) { - ctx->ctx.failed = TRUE; - return &ctx->ctx; - } - } - - if ((flags & MAILBOX_LIST_ITER_RETURN_SUBSCRIBED) != 0 && - (flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0) { - /* we're listing all mailboxes but we want to know - \Subscribed flags */ - if (mailbox_list_subscriptions_fill(&ctx->ctx, ctx->tree_ctx, - ctx->ctx.glob, TRUE) < 0) { - ctx->ctx.failed = TRUE; - return &ctx->ctx; - } - } - ctx->tree_iter = mailbox_tree_iterate_init(ctx->tree_ctx, NULL, MAILBOX_MATCHED); return &ctx->ctx; @@ -544,5 +469,12 @@ return NULL; ctx->info.flags = node->flags; + if ((_ctx->flags & MAILBOX_LIST_ITER_RETURN_SUBSCRIBED) != 0 && + (_ctx->flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0) { + /* we're listing all mailboxes but we want to know + \Subscribed flags */ + mailbox_list_set_subscription_flags(_ctx->list, ctx->info.name, + &ctx->info.flags); + } return &ctx->info; } diff -r fa4b84059ae2 -r 4e4c7f982fd5 src/lib-storage/list/mailbox-list-maildir.c --- a/src/lib-storage/list/mailbox-list-maildir.c Wed Feb 02 05:31:46 2011 +0200 +++ b/src/lib-storage/list/mailbox-list-maildir.c Wed Feb 02 05:33:04 2011 +0200 @@ -7,6 +7,7 @@ #include "mkdir-parents.h" #include "str.h" #include "subscription-file.h" +#include "mailbox-list-subscriptions.h" #include "mailbox-list-delete.h" #include "mailbox-list-maildir.h" @@ -635,6 +636,7 @@ maildir_list_iter_deinit, maildir_list_get_mailbox_flags, NULL, + mailbox_list_subscriptions_refresh, maildir_list_set_subscribed, maildir_list_create_mailbox_dir, maildir_list_delete_mailbox, @@ -668,6 +670,7 @@ maildir_list_iter_deinit, maildir_list_get_mailbox_flags, NULL, + mailbox_list_subscriptions_refresh, maildir_list_set_subscribed, maildir_list_create_mailbox_dir, maildir_list_delete_mailbox, diff -r fa4b84059ae2 -r 4e4c7f982fd5 src/lib-storage/list/mailbox-list-none.c --- a/src/lib-storage/list/mailbox-list-none.c Wed Feb 02 05:31:46 2011 +0200 +++ b/src/lib-storage/list/mailbox-list-none.c Wed Feb 02 05:33:04 2011 +0200 @@ -71,6 +71,12 @@ return GLOBAL_TEMP_PREFIX; } +static int +none_list_subscriptions_refresh(struct mailbox_list *list ATTR_UNUSED) +{ + return 0; +} + static int none_list_set_subscribed(struct mailbox_list *list, const char *name ATTR_UNUSED, bool set ATTR_UNUSED) @@ -180,6 +186,7 @@ none_list_iter_deinit, none_list_get_mailbox_flags, NULL, + none_list_subscriptions_refresh, none_list_set_subscribed, none_list_create_mailbox_dir, none_list_delete_mailbox, diff -r fa4b84059ae2 -r 4e4c7f982fd5 src/lib-storage/list/mailbox-list-subscriptions.c --- a/src/lib-storage/list/mailbox-list-subscriptions.c Wed Feb 02 05:31:46 2011 +0200 +++ b/src/lib-storage/list/mailbox-list-subscriptions.c Wed Feb 02 05:33:04 2011 +0200 @@ -1,18 +1,33 @@ /* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */ #include "lib.h" +#include "ioloop.h" +#include "array.h" +#include "imap-match.h" #include "subscription-file.h" +#include "mailbox-tree.h" #include "mailbox-list-private.h" #include "mailbox-list-subscriptions.h" +#include + +struct subscriptions_mailbox_list_iterate_context { + struct mailbox_list_iterate_context ctx; + struct mailbox_tree_context *tree; + struct mailbox_tree_iterate_context *iter; + struct mailbox_info info; +}; + static int -mailbox_list_subscription_fill_one(struct mailbox_list_iter_update_context *update_ctx, - struct mail_namespace *default_ns, +mailbox_list_subscription_fill_one(struct mailbox_list *list, const char *name) { + struct mail_namespace *ns, *default_ns = list->ns; struct mail_namespace *namespaces = default_ns->user->namespaces; - struct mail_namespace *ns; + struct mailbox_node *node; const char *vname; + unsigned int len; + bool created; /* default_ns is whatever namespace we're currently listing. if we have e.g. prefix="" and prefix=pub/ namespaces with @@ -38,7 +53,7 @@ /* we'll need to get the namespace autocreated. one easy way is to just ask if a mailbox name under it is valid, and it gets created */ - (void)mailbox_list_is_valid_existing_name(ns->list, name); + (void)mailbox_list_is_valid_existing_name(list, name); ns = mail_namespace_find_unsubscribable(namespaces, name); i_assert(ns != NULL && (ns->flags & NAMESPACE_FLAG_AUTOCREATED) != 0); @@ -53,75 +68,220 @@ ns = default_ns; } - if (!mailbox_list_is_valid_existing_name(ns->list, name)) { + len = strlen(name); + if (len > 0 && name[len-1] == mail_namespace_get_sep(ns)) { + /* entry ends with hierarchy separator, remove it. + this exists mainly for backwards compatibility with old + Dovecot versions and non-Dovecot software that added them */ + name = t_strndup(name, len-1); + } + + if (!mailbox_list_is_valid_existing_name(list, name)) { /* we'll only get into trouble if we show this */ return -1; } else { - vname = mailbox_list_get_vname(ns->list, name); - mailbox_list_iter_update(update_ctx, vname); + vname = mailbox_list_get_vname(list, name); + node = mailbox_tree_get(list->subscriptions, vname, &created); + node->flags = MAILBOX_SUBSCRIBED; } return 0; } -static int -mailbox_list_subscriptions_fill_real(struct mailbox_list_iterate_context *ctx, - struct mailbox_tree_context *tree_ctx, - struct imap_match_glob *glob, - bool update_only) +int mailbox_list_subscriptions_refresh(struct mailbox_list *list) { - struct mail_namespace *ns, *default_ns = ctx->list->ns; - struct mailbox_list *list = ctx->list; - struct mailbox_list_iter_update_context update_ctx; + struct mail_namespace *ns = list->ns; struct subsfile_list_context *subsfile_ctx; + struct stat st; const char *path, *name; - if ((ctx->list->ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) == 0) { - /* need to list these using another namespace */ - ns = mail_namespace_find_subscribable(default_ns->user->namespaces, - default_ns->prefix); + if ((ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) == 0) { + /* no subscriptions in this namespace. find where they are. */ + ns = mail_namespace_find_subscribable(ns->user->namespaces, + ns->prefix); if (ns == NULL) { /* no subscriptions */ return 0; } - list = ns->list; } - path = t_strconcat(list->set.control_dir != NULL ? - list->set.control_dir : list->set.root_dir, - "/", list->set.subscription_fname, NULL); - subsfile_ctx = subsfile_list_init(list, path); + path = t_strconcat(ns->list->set.control_dir != NULL ? + ns->list->set.control_dir : ns->list->set.root_dir, + "/", ns->list->set.subscription_fname, NULL); + if (stat(path, &st) < 0) { + if (errno == ENOENT) { + /* no subscriptions */ + mailbox_tree_clear(list->subscriptions); + list->subscriptions_mtime = 0; + return 0; + } + mailbox_list_set_critical(list, "stat(%s) failed: %m", path); + return -1; + } + if (st.st_mtime == list->subscriptions_mtime && + st.st_mtime < list->subscriptions_read_time-1) { + /* we're up to date */ + return 0; + } - memset(&update_ctx, 0, sizeof(update_ctx)); - update_ctx.iter_ctx = ctx; - update_ctx.tree_ctx = tree_ctx; - update_ctx.glob = glob; - update_ctx.leaf_flags = MAILBOX_SUBSCRIBED; - update_ctx.parent_flags = MAILBOX_CHILD_SUBSCRIBED; - update_ctx.update_only = update_only; - update_ctx.match_parents = - (ctx->flags & MAILBOX_LIST_ITER_SELECT_RECURSIVEMATCH) != 0; + mailbox_tree_clear(list->subscriptions); + list->subscriptions_read_time = ioloop_time; + subsfile_ctx = subsfile_list_init(list, path); + if (subsfile_list_fstat(subsfile_ctx, &st) == 0) + list->subscriptions_mtime = st.st_mtime; while ((name = subsfile_list_next(subsfile_ctx)) != NULL) T_BEGIN { - if (mailbox_list_subscription_fill_one(&update_ctx, default_ns, - name) < 0) { + if (mailbox_list_subscription_fill_one(list, name) < 0) { i_warning("Subscriptions file %s: " "Ignoring invalid entry: %s", path, name); } } T_END; - return subsfile_list_deinit(subsfile_ctx); + + if (subsfile_list_deinit(&subsfile_ctx) < 0) { + list->subscriptions_mtime = (time_t)-1; + return -1; + } + return 0; +} + +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; + } +} + +void mailbox_list_subscriptions_fill(struct mailbox_list_iterate_context *ctx, + struct mailbox_tree_context *tree) +{ + struct mailbox_list_iter_update_context update_ctx; + struct mailbox_tree_iterate_context *iter; + struct mailbox_node *node; + const char *name; + + memset(&update_ctx, 0, sizeof(update_ctx)); + update_ctx.iter_ctx = ctx; + update_ctx.tree_ctx = tree; + update_ctx.glob = ctx->glob; + update_ctx.leaf_flags = MAILBOX_SUBSCRIBED; + update_ctx.parent_flags = MAILBOX_CHILD_SUBSCRIBED; + update_ctx.match_parents = + (ctx->flags & MAILBOX_LIST_ITER_SELECT_RECURSIVEMATCH) != 0; + + iter = mailbox_tree_iterate_init(ctx->list->subscriptions, NULL, + MAILBOX_SUBSCRIBED); + while ((node = mailbox_tree_iterate_next(iter, &name)) != NULL) + mailbox_list_iter_update(&update_ctx, name); + mailbox_tree_iterate_deinit(&iter); +} + +struct mailbox_list_iterate_context * +mailbox_list_subscriptions_iter_init(struct mailbox_list *list, + const char *const *patterns, + enum mailbox_list_iter_flags flags) +{ + struct subscriptions_mailbox_list_iterate_context *ctx; + char sep = mail_namespace_get_sep(list->ns); + + ctx = i_new(struct subscriptions_mailbox_list_iterate_context, 1); + ctx->ctx.list = list; + ctx->ctx.flags = flags; + ctx->ctx.glob = imap_match_init_multiple(default_pool, patterns, + TRUE, sep); + array_create(&ctx->ctx.module_contexts, default_pool, sizeof(void *), 5); + + ctx->tree = mailbox_tree_init(sep); + mailbox_list_subscriptions_fill(&ctx->ctx, ctx->tree); + + ctx->info.ns = list->ns; + ctx->iter = mailbox_tree_iterate_init(ctx->tree, NULL, 0); + return &ctx->ctx; } -int mailbox_list_subscriptions_fill(struct mailbox_list_iterate_context *ctx, - struct mailbox_tree_context *tree_ctx, - struct imap_match_glob *glob, - bool update_only) +static void +path_split(const char *path, const char **dir_r, const char **fname_r) +{ + const char *p; + + p = strrchr(path, '/'); + if (p == NULL) { + *dir_r = ""; + *fname_r = path; + } else { + *dir_r = t_strdup_until(path, p); + *fname_r = p + 1; + } +} + +const struct mailbox_info * +mailbox_list_subscriptions_iter_next(struct mailbox_list_iterate_context *_ctx) { - int ret; + struct subscriptions_mailbox_list_iterate_context *ctx = + (struct subscriptions_mailbox_list_iterate_context *)_ctx; + struct mailbox_list *list = _ctx->list; + struct mailbox_node *node; + enum mailbox_info_flags subs_flags; + const char *path, *vname, *dir, *fname, *storage_name; + struct stat st; + + node = mailbox_tree_iterate_next(ctx->iter, &vname); + if (node == NULL) + return NULL; + + ctx->info.name = vname; + subs_flags = node->flags & (MAILBOX_SUBSCRIBED | + MAILBOX_CHILD_SUBSCRIBED); + + if ((_ctx->flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) != 0 && + (_ctx->flags & MAILBOX_LIST_ITER_RETURN_CHILDREN) == 0) { + /* don't care about flags, just return it */ + ctx->info.flags = subs_flags; + return &ctx->info; + } - T_BEGIN { - ret = mailbox_list_subscriptions_fill_real(ctx, tree_ctx, glob, - update_only); - } T_END; + storage_name = mailbox_list_get_storage_name(list, vname); + if (!mailbox_list_is_valid_pattern(list, storage_name)) { + /* broken entry in subscriptions file */ + ctx->info.flags = MAILBOX_NONEXISTENT; + } else { + path = mailbox_list_get_path(list, storage_name, + MAILBOX_LIST_PATH_TYPE_DIR); + path_split(path, &dir, &fname); + if (list->v.get_mailbox_flags(list, dir, fname, + MAILBOX_LIST_FILE_TYPE_UNKNOWN, + &st, &ctx->info.flags) < 0) + _ctx->failed = TRUE; + } + + ctx->info.flags &= ~(MAILBOX_SUBSCRIBED | MAILBOX_CHILD_SUBSCRIBED); + ctx->info.flags |= + node->flags & (MAILBOX_SUBSCRIBED | MAILBOX_CHILD_SUBSCRIBED); + return &ctx->info; +} + +int mailbox_list_subscriptions_iter_deinit(struct mailbox_list_iterate_context *_ctx) +{ + struct subscriptions_mailbox_list_iterate_context *ctx = + (struct subscriptions_mailbox_list_iterate_context *)_ctx; + int ret = _ctx->failed ? -1 : 0; + + mailbox_tree_iterate_deinit(&ctx->iter); + mailbox_tree_deinit(&ctx->tree); + if (_ctx->glob != NULL) + imap_match_deinit(&_ctx->glob); + array_free(&_ctx->module_contexts); + i_free(ctx); return ret; } diff -r fa4b84059ae2 -r 4e4c7f982fd5 src/lib-storage/list/mailbox-list-subscriptions.h --- a/src/lib-storage/list/mailbox-list-subscriptions.h Wed Feb 02 05:31:46 2011 +0200 +++ b/src/lib-storage/list/mailbox-list-subscriptions.h Wed Feb 02 05:33:04 2011 +0200 @@ -1,12 +1,31 @@ #ifndef MAILBOX_LIST_SUBSCRIPTIONS_H #define MAILBOX_LIST_SUBSCRIPTIONS_H +enum mailbox_info_flags; +enum mailbox_list_iter_flags; +struct mailbox_tree_context; struct mailbox_list_iterate_context; -struct mailbox_tree_context; + +int mailbox_list_subscriptions_refresh(struct mailbox_list *list); + +/* Set MAILBOX_SUBSCRIBED and MAILBOX_CHILD_SUBSCRIBED flags, + clearing them if they already are there when they shouldn't. */ +void mailbox_list_set_subscription_flags(struct mailbox_list *list, + const char *vname, + enum mailbox_info_flags *flags); -int mailbox_list_subscriptions_fill(struct mailbox_list_iterate_context *ctx, - struct mailbox_tree_context *tree_ctx, - struct imap_match_glob *glob, - bool update_only); +/* Add subscriptions matching the iteration to the given tree */ +void mailbox_list_subscriptions_fill(struct mailbox_list_iterate_context *ctx, + struct mailbox_tree_context *tree); + +/* Iterate through subscriptions, call mailbox_list.get_mailbox_flags() + if necessary for mailboxes to get their flags. */ +struct mailbox_list_iterate_context * +mailbox_list_subscriptions_iter_init(struct mailbox_list *list, + const char *const *patterns, + enum mailbox_list_iter_flags flags); +const struct mailbox_info * +mailbox_list_subscriptions_iter_next(struct mailbox_list_iterate_context *ctx); +int mailbox_list_subscriptions_iter_deinit(struct mailbox_list_iterate_context *ctx); #endif diff -r fa4b84059ae2 -r 4e4c7f982fd5 src/lib-storage/list/subscription-file.c --- a/src/lib-storage/list/subscription-file.c Wed Feb 02 05:31:46 2011 +0200 +++ b/src/lib-storage/list/subscription-file.c Wed Feb 02 05:33:04 2011 +0200 @@ -224,10 +224,13 @@ return ctx; } -int subsfile_list_deinit(struct subsfile_list_context *ctx) +int subsfile_list_deinit(struct subsfile_list_context **_ctx) { + struct subsfile_list_context *ctx = *_ctx; int ret = ctx->failed ? -1 : 0; + *_ctx = NULL; + if (ctx->input != NULL) i_stream_destroy(&ctx->input); i_free(ctx->path); @@ -235,6 +238,21 @@ return ret; } +int subsfile_list_fstat(struct subsfile_list_context *ctx, struct stat *st_r) +{ + const struct stat *st; + + if (ctx->failed) + return -1; + + if ((st = i_stream_stat(ctx->input, FALSE)) == NULL) { + ctx->failed = TRUE; + return -1; + } + *st_r = *st; + return 0; +} + const char *subsfile_list_next(struct subsfile_list_context *ctx) { const char *line; diff -r fa4b84059ae2 -r 4e4c7f982fd5 src/lib-storage/list/subscription-file.h --- a/src/lib-storage/list/subscription-file.h Wed Feb 02 05:31:46 2011 +0200 +++ b/src/lib-storage/list/subscription-file.h Wed Feb 02 05:33:04 2011 +0200 @@ -1,15 +1,19 @@ #ifndef SUBSCRIPTION_FILE_H #define SUBSCRIPTION_FILE_H +struct stat; struct mailbox_list; /* Initialize new subscription file listing. */ struct subsfile_list_context * subsfile_list_init(struct mailbox_list *list, const char *path); - /* Deinitialize subscription file listing. Returns 0 if ok, or -1 if some error occurred while listing. */ -int subsfile_list_deinit(struct subsfile_list_context *ctx); +int subsfile_list_deinit(struct subsfile_list_context **ctx); + +/* Call fstat() for subscription file */ +int subsfile_list_fstat(struct subsfile_list_context *ctx, struct stat *st_r); + /* Returns the next subscribed mailbox, or NULL. */ const char *subsfile_list_next(struct subsfile_list_context *ctx); diff -r fa4b84059ae2 -r 4e4c7f982fd5 src/lib-storage/mailbox-list-private.h --- a/src/lib-storage/mailbox-list-private.h Wed Feb 02 05:31:46 2011 +0200 +++ b/src/lib-storage/mailbox-list-private.h Wed Feb 02 05:33:04 2011 +0200 @@ -72,6 +72,7 @@ If it does, mailbox deletion assumes it can safely delete it. */ bool (*is_internal_name)(struct mailbox_list *list, const char *name); + int (*subscriptions_refresh)(struct mailbox_list *list); int (*set_subscribed)(struct mailbox_list *list, const char *name, bool set); int (*create_mailbox_dir)(struct mailbox_list *list, const char *name, @@ -112,6 +113,9 @@ /* origin (e.g. path) where the file_create_gid was got from */ const char *file_create_gid_origin; + struct mailbox_tree_context *subscriptions; + time_t subscriptions_mtime, subscriptions_read_time; + struct mailbox_log *changelog; time_t changelog_timestamp; diff -r fa4b84059ae2 -r 4e4c7f982fd5 src/lib-storage/mailbox-list.c --- a/src/lib-storage/mailbox-list.c Wed Feb 02 05:31:46 2011 +0200 +++ b/src/lib-storage/mailbox-list.c Wed Feb 02 05:33:04 2011 +0200 @@ -151,6 +151,7 @@ list->dir_create_mode = (mode_t)-1; list->file_create_gid = (gid_t)-1; list->changelog_timestamp = (time_t)-1; + list->subscriptions = mailbox_tree_init(mail_namespace_get_sep(ns)); /* copy settings */ if (set->root_dir != NULL) { @@ -461,6 +462,7 @@ *_list = NULL; i_free_and_null(list->error_string); + mailbox_tree_deinit(&list->subscriptions); if (list->changelog != NULL) mailbox_log_free(&list->changelog); list->v.deinit(list); @@ -863,9 +865,19 @@ const char *const *patterns, enum mailbox_list_iter_flags flags) { + struct mailbox_list_iterate_context *ctx; + int ret = 0; + i_assert(*patterns != NULL); - return list->v.iter_init(list, patterns, flags); + if ((flags & (MAILBOX_LIST_ITER_SELECT_SUBSCRIBED | + MAILBOX_LIST_ITER_RETURN_SUBSCRIBED)) != 0) + ret = list->v.subscriptions_refresh(list); + + ctx = list->v.iter_init(list, patterns, flags); + if (ret < 0) + ctx->failed = TRUE; + return ctx; } static bool @@ -1232,6 +1244,9 @@ uint8_t guid[MAIL_GUID_128_SIZE]; int ret; + /* make sure we'll refresh the file on next list */ + list->subscriptions_mtime = (time_t)-1; + if ((ret = list->v.set_subscribed(list, name, set)) <= 0) return ret; diff -r fa4b84059ae2 -r 4e4c7f982fd5 src/lib-storage/mailbox-tree.c --- a/src/lib-storage/mailbox-tree.c Wed Feb 02 05:31:46 2011 +0200 +++ b/src/lib-storage/mailbox-tree.c Wed Feb 02 05:33:04 2011 +0200 @@ -27,12 +27,9 @@ struct mailbox_tree_context *mailbox_tree_init(char separator) { struct mailbox_tree_context *tree; - pool_t pool; - pool = pool_alloconly_create(MEMPOOL_GROWING"mailbox_tree", 10240); - - tree = p_new(pool, struct mailbox_tree_context, 1); - tree->pool = pool; + tree = i_new(struct mailbox_tree_context, 1); + tree->pool = pool_alloconly_create(MEMPOOL_GROWING"mailbox_tree", 10240); tree->separator = separator; return tree; } @@ -43,6 +40,13 @@ *_tree = NULL; pool_unref(&tree->pool); + i_free(tree); +} + +void mailbox_tree_clear(struct mailbox_tree_context *tree) +{ + p_clear(tree->pool); + tree->nodes = NULL; } static struct mailbox_node * diff -r fa4b84059ae2 -r 4e4c7f982fd5 src/lib-storage/mailbox-tree.h --- a/src/lib-storage/mailbox-tree.h Wed Feb 02 05:31:46 2011 +0200 +++ b/src/lib-storage/mailbox-tree.h Wed Feb 02 05:33:04 2011 +0200 @@ -15,6 +15,8 @@ struct mailbox_tree_context *mailbox_tree_init(char separator); void mailbox_tree_deinit(struct mailbox_tree_context **tree); +void mailbox_tree_clear(struct mailbox_tree_context *tree); + struct mailbox_node * mailbox_tree_get(struct mailbox_tree_context *tree, const char *path, bool *created);