Mercurial > dovecot > original-hg > dovecot-1.2
diff src/lib-storage/list/mailbox-list-maildir-iter.c @ 4808:93bc9770f938 HEAD
Initial code for separation of mailbox accessing and directory layout
handling. It's not yet possible to change the default layouts though.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Thu, 16 Nov 2006 02:16:31 +0200 |
parents | |
children | 967de900c73a |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-storage/list/mailbox-list-maildir-iter.c Thu Nov 16 02:16:31 2006 +0200 @@ -0,0 +1,371 @@ +/* Copyright (C) 2002-2006 Timo Sirainen */ + +#include "lib.h" +#include "str.h" +#include "home-expand.h" +#include "imap-match.h" +#include "subscription-file.h" +#include "mailbox-tree.h" +#include "mailbox-list-maildir.h" + +#include <dirent.h> + +#define MAILBOX_FLAG_MATCHED 0x40000000 + +struct maildir_list_iterate_context { + struct mailbox_list_iterate_context ctx; + pool_t pool; + + const char *dir, *prefix; + + struct mailbox_tree_context *tree_ctx; + + string_t *node_path; + size_t parent_pos; + struct mailbox_node *root, *next_node; + struct mailbox_info info; +}; + +static void maildir_nodes_fix(struct mailbox_node *node, bool is_subs) +{ + while (node != NULL) { + if (node->children != NULL) { + node->flags |= MAILBOX_CHILDREN; + node->flags &= ~MAILBOX_NOCHILDREN; + maildir_nodes_fix(node->children, is_subs); + } else if ((node->flags & MAILBOX_PLACEHOLDER) != 0) { + if (!is_subs) { + node->flags &= ~MAILBOX_PLACEHOLDER; + node->flags |= MAILBOX_NOSELECT; + } + } + node = node->next; + } +} + +static int +maildir_fill_readdir(struct maildir_list_iterate_context *ctx, + struct imap_match_glob *glob, bool update_only) +{ + DIR *dirp; + struct dirent *d; + const char *p, *mailbox_c; + string_t *mailbox; + enum mailbox_info_flags flags; + enum imap_match_result match; + struct mailbox_node *node; + bool created; + char hierarchy_sep; + int ret; + + dirp = opendir(ctx->dir); + if (dirp == NULL) { + if (errno != ENOENT) { + mailbox_list_set_critical(ctx->ctx.list, + "opendir(%s) failed: %m", ctx->dir); + return -1; + } + return 0; + } + + hierarchy_sep = ctx->ctx.list->hierarchy_sep; + + t_push(); + mailbox = t_str_new(PATH_MAX); + while ((d = readdir(dirp)) != NULL) { + const char *fname = d->d_name; + + if (fname[0] != hierarchy_sep) + continue; + + /* skip . and .. */ + if (fname[0] == '.' && + (fname[1] == '\0' || (fname[1] == '.' && fname[2] == '\0'))) + continue; + + /* make sure the mask matches */ + str_truncate(mailbox, 0); + str_append(mailbox, ctx->prefix); + str_append(mailbox, fname + 1); + mailbox_c = str_c(mailbox); + + match = imap_match(glob, mailbox_c); + + if (match != IMAP_MATCH_YES && + match != IMAP_MATCH_PARENT) + continue; + + /* check if this is an actual mailbox */ + flags = 0; + ret = ctx->ctx.list->callback(ctx->dir, fname, + mailbox_list_get_file_type(d), + ctx->ctx.flags, &flags, + ctx->ctx.list->context); + if (ret < 0) { + t_pop(); + return -1; + } + if (ret == 0) + continue; + + if (match == IMAP_MATCH_PARENT) { + t_push(); + while ((p = strrchr(mailbox_c, + hierarchy_sep)) != NULL) { + str_truncate(mailbox, (size_t) (p-mailbox_c)); + mailbox_c = str_c(mailbox); + if (imap_match(glob, mailbox_c) > 0) + break; + } + i_assert(p != NULL); + + created = FALSE; + node = update_only ? + mailbox_tree_update(ctx->tree_ctx, mailbox_c) : + mailbox_tree_get(ctx->tree_ctx, + mailbox_c, &created); + if (node != NULL) { + if (created) + node->flags = MAILBOX_PLACEHOLDER; + + node->flags |= MAILBOX_CHILDREN | + MAILBOX_FLAG_MATCHED; + node->flags &= ~MAILBOX_NOCHILDREN; + } + + t_pop(); + } else { + created = FALSE; + node = update_only ? + mailbox_tree_update(ctx->tree_ctx, mailbox_c) : + mailbox_tree_get(ctx->tree_ctx, + mailbox_c, &created); + + if (node != NULL) { + if (created) + node->flags = MAILBOX_NOCHILDREN; + node->flags &= ~(MAILBOX_PLACEHOLDER | + MAILBOX_NONEXISTENT); + node->flags |= MAILBOX_FLAG_MATCHED; + } + } + } + t_pop(); + + if (closedir(dirp) < 0) { + mailbox_list_set_critical(ctx->ctx.list, + "readdir(%s) failed: %m", ctx->dir); + return -1; + } + + if ((ctx->ctx.list->flags & MAILBOX_LIST_FLAG_INBOX) != 0 && + (ctx->ctx.flags & MAILBOX_LIST_ITER_SUBSCRIBED) == 0) { + /* make sure INBOX is there */ + node = mailbox_tree_get(ctx->tree_ctx, "INBOX", &created); + if (created) + node->flags = MAILBOX_NOCHILDREN; + else + node->flags &= ~MAILBOX_PLACEHOLDER; + + switch (imap_match(glob, "INBOX")) { + case IMAP_MATCH_YES: + case IMAP_MATCH_PARENT: + node->flags |= MAILBOX_FLAG_MATCHED; + break; + default: + break; + } + } + maildir_nodes_fix(mailbox_tree_get(ctx->tree_ctx, NULL, NULL), + (ctx->ctx.flags & MAILBOX_LIST_ITER_SUBSCRIBED) != 0); + return 0; +} + +static int maildir_fill_subscribed(struct maildir_list_iterate_context *ctx, + struct imap_match_glob *glob) +{ + struct subsfile_list_context *subsfile_ctx; + const char *path, *name, *p; + struct mailbox_node *node; + char hierarchy_sep; + bool created; + + path = t_strconcat(ctx->ctx.list->set.control_dir != NULL ? + ctx->ctx.list->set.control_dir : + ctx->ctx.list->set.root_dir, + "/", ctx->ctx.list->set.subscription_fname, NULL); + subsfile_ctx = subsfile_list_init(ctx->ctx.list, path); + + hierarchy_sep = ctx->ctx.list->hierarchy_sep; + while ((name = subsfile_list_next(subsfile_ctx)) != NULL) { + switch (imap_match(glob, name)) { + case IMAP_MATCH_YES: + node = mailbox_tree_get(ctx->tree_ctx, name, NULL); + node->flags = MAILBOX_FLAG_MATCHED; + if ((ctx->ctx.flags & + MAILBOX_LIST_ITER_FAST_FLAGS) == 0) { + node->flags |= MAILBOX_NONEXISTENT | + MAILBOX_NOCHILDREN; + } + break; + case IMAP_MATCH_PARENT: + /* placeholder */ + while ((p = strrchr(name, hierarchy_sep)) != NULL) { + name = t_strdup_until(name, p); + if (imap_match(glob, name) > 0) + break; + } + i_assert(p != NULL); + + node = mailbox_tree_get(ctx->tree_ctx, name, &created); + if (created) node->flags = MAILBOX_PLACEHOLDER; + node->flags |= MAILBOX_FLAG_MATCHED | MAILBOX_CHILDREN; + node->flags &= ~MAILBOX_NOCHILDREN; + break; + default: + break; + } + } + + return subsfile_list_deinit(subsfile_ctx); +} + +struct mailbox_list_iterate_context * +maildir_list_iter_init(struct mailbox_list *_list, + const char *ref, const char *mask, + enum mailbox_list_iter_flags flags) +{ + struct maildir_mailbox_list *list = + (struct maildir_mailbox_list *)_list; + struct maildir_list_iterate_context *ctx; + struct imap_match_glob *glob; + const char *dir, *p; + pool_t pool; + + mailbox_list_clear_error(&list->list); + + pool = pool_alloconly_create("maildir_list", 1024); + ctx = p_new(pool, struct maildir_list_iterate_context, 1); + ctx->ctx.list = _list; + ctx->ctx.flags = flags; + ctx->pool = pool; + ctx->tree_ctx = mailbox_tree_init(_list->hierarchy_sep); + + if (*ref != '\0') { + /* join reference + mask */ + mask = t_strconcat(ref, mask, NULL); + } + + glob = imap_match_init(pool, mask, TRUE, _list->hierarchy_sep); + + ctx->dir = _list->set.root_dir; + ctx->prefix = ""; + + if ((flags & MAILBOX_LIST_ITER_SUBSCRIBED) != 0) { + if (maildir_fill_subscribed(ctx, glob) < 0) { + ctx->ctx.failed = TRUE; + return &ctx->ctx; + } + } else if ((list->list.flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0 && + (p = strrchr(mask, '/')) != NULL) { + dir = t_strdup_until(mask, p); + ctx->prefix = p_strdup_until(pool, mask, p+1); + + if (*mask != '/' && *mask != '~') + dir = t_strconcat(_list->set.root_dir, "/", dir, NULL); + ctx->dir = p_strdup(pool, home_expand(dir)); + } + + if ((flags & MAILBOX_LIST_ITER_SUBSCRIBED) == 0 || + (ctx->ctx.flags & MAILBOX_LIST_ITER_FAST_FLAGS) == 0) { + bool update_only = (flags & MAILBOX_LIST_ITER_SUBSCRIBED) != 0; + if (maildir_fill_readdir(ctx, glob, update_only) < 0) { + ctx->ctx.failed = TRUE; + return &ctx->ctx; + } + } + + ctx->node_path = str_new(pool, 256); + ctx->root = mailbox_tree_get(ctx->tree_ctx, NULL, NULL); + return &ctx->ctx; +} + +int maildir_list_iter_deinit(struct mailbox_list_iterate_context *_ctx) +{ + struct maildir_list_iterate_context *ctx = + (struct maildir_list_iterate_context *)_ctx; + int ret = ctx->ctx.failed ? -1 : 0; + + mailbox_tree_deinit(ctx->tree_ctx); + pool_unref(ctx->pool); + return ret; +} + +static struct mailbox_node *find_next(struct mailbox_node **node, + string_t *path, char hierarchy_sep) +{ + struct mailbox_node *child; + size_t len; + + while (*node != NULL) { + if (((*node)->flags & MAILBOX_FLAG_MATCHED) != 0) + return *node; + + if ((*node)->children != NULL) { + len = str_len(path); + if (len != 0) + str_append_c(path, hierarchy_sep); + str_append(path, (*node)->name); + + child = find_next(&(*node)->children, path, + hierarchy_sep); + if (child != NULL) + return child; + + str_truncate(path, len); + } + + *node = (*node)->next; + } + + return NULL; +} + +struct mailbox_info * +maildir_list_iter_next(struct mailbox_list_iterate_context *_ctx) +{ + struct maildir_list_iterate_context *ctx = + (struct maildir_list_iterate_context *)_ctx; + struct mailbox_node *node; + + for (node = ctx->next_node; node != NULL; node = node->next) { + if ((node->flags & MAILBOX_FLAG_MATCHED) != 0) + break; + } + + if (node == NULL) { + if (ctx->root == NULL) + return NULL; + + str_truncate(ctx->node_path, 0); + node = find_next(&ctx->root, ctx->node_path, + ctx->ctx.list->hierarchy_sep); + ctx->parent_pos = str_len(ctx->node_path); + + if (node == NULL) + return NULL; + } + ctx->next_node = node->next; + + i_assert((node->flags & MAILBOX_FLAG_MATCHED) != 0); + node->flags &= ~MAILBOX_FLAG_MATCHED; + + str_truncate(ctx->node_path, ctx->parent_pos); + if (ctx->parent_pos != 0) + str_append_c(ctx->node_path, ctx->ctx.list->hierarchy_sep); + str_append(ctx->node_path, node->name); + + ctx->info.name = str_c(ctx->node_path); + ctx->info.flags = node->flags; + return &ctx->info; +}