Mercurial > dovecot > original-hg > dovecot-1.2
view 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 source
/* 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; }