Mercurial > dovecot > original-hg > dovecot-1.2
view src/lib-storage/list/mailbox-list-maildir-iter.c @ 9532:00cd9aacd03c HEAD
Updated copyright notices to include year 2010.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Mon, 25 Jan 2010 01:18:58 +0200 |
parents | aef90950d50b |
children | 8c8bde0f9120 |
line wrap: on
line source
/* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "str.h" #include "imap-match.h" #include "mailbox-tree.h" #include "mailbox-list-subscriptions.h" #include "mailbox-list-maildir.h" #include <dirent.h> struct maildir_list_iterate_context { struct mailbox_list_iterate_context ctx; pool_t pool; const char *dir; struct mailbox_tree_context *tree_ctx; struct mailbox_tree_iterate_context *tree_iter; struct mailbox_info info; }; static void node_fix_parents(struct mailbox_node *node) { /* Fix parent nodes' children states. also if we happened to create any of the parents, we need to mark them nonexistent. */ node = node->parent; for (; node != NULL; node = node->parent) { if ((node->flags & MAILBOX_MATCHED) == 0) node->flags |= MAILBOX_NONEXISTENT; node->flags |= MAILBOX_CHILDREN; node->flags &= ~MAILBOX_NOCHILDREN; } } static void maildir_fill_parents(struct maildir_list_iterate_context *ctx, struct imap_match_glob *glob, bool update_only, string_t *mailbox, enum mailbox_info_flags flags) { struct mail_namespace *ns = ctx->ctx.list->ns; struct mailbox_node *node; const char *p, *mailbox_c; char hierarchy_sep; bool created; unsigned int prefix_len; if ((ctx->ctx.flags & MAILBOX_LIST_ITER_VIRTUAL_NAMES) != 0) { hierarchy_sep = ns->sep; prefix_len = ns->prefix_len; } else { hierarchy_sep = ns->real_sep; prefix_len = 0; } mailbox_c = str_c(mailbox); 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) != IMAP_MATCH_YES) continue; if (prefix_len > 0 && str_len(mailbox) == prefix_len-1 && strncmp(mailbox_c, ns->prefix, prefix_len - 1) == 0 && mailbox_c[prefix_len-1] == hierarchy_sep) { /* don't return matches to namespace prefix itself */ continue; } created = FALSE; node = update_only ? mailbox_tree_lookup(ctx->tree_ctx, mailbox_c) : mailbox_tree_get(ctx->tree_ctx, mailbox_c, &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 | flags; node->flags &= ~MAILBOX_NOCHILDREN; node_fix_parents(node); } } } static void maildir_set_children(struct maildir_list_iterate_context *ctx, string_t *mailbox) { struct mailbox_node *node; const char *p, *mailbox_c; char hierarchy_sep; if ((ctx->ctx.flags & MAILBOX_LIST_ITER_VIRTUAL_NAMES) != 0) hierarchy_sep = ctx->ctx.list->ns->sep; else hierarchy_sep = ctx->ctx.list->ns->real_sep; /* mark the first existing parent as containing children */ mailbox_c = str_c(mailbox); while ((p = strrchr(mailbox_c, hierarchy_sep)) != NULL) { str_truncate(mailbox, (size_t) (p-mailbox_c)); mailbox_c = str_c(mailbox); node = mailbox_tree_lookup(ctx->tree_ctx, mailbox_c); if (node != NULL) { node->flags &= ~MAILBOX_NOCHILDREN; node->flags |= MAILBOX_CHILDREN; break; } } } static int maildir_fill_readdir(struct maildir_list_iterate_context *ctx, struct imap_match_glob *glob, bool update_only) { struct mail_namespace *ns = ctx->ctx.list->ns; DIR *dirp; struct dirent *d; const char *mailbox_name; string_t *mailbox; enum mailbox_info_flags flags; enum imap_match_result match; struct mailbox_node *node; bool created, virtual_names; char prefix_char; int ret; dirp = opendir(ctx->dir); if (dirp == NULL) { if (errno == EACCES) { mailbox_list_set_critical(ctx->ctx.list, "%s", mail_error_eacces_msg("opendir", ctx->dir)); } else if (errno != ENOENT) { mailbox_list_set_critical(ctx->ctx.list, "opendir(%s) failed: %m", ctx->dir); return -1; } return 0; } virtual_names = (ctx->ctx.flags & MAILBOX_LIST_ITER_VIRTUAL_NAMES) != 0; prefix_char = strcmp(ctx->ctx.list->name, MAILBOX_LIST_NAME_IMAPDIR) != 0 ? ctx->ctx.list->hierarchy_sep : '\0'; mailbox = t_str_new(PATH_MAX); while ((d = readdir(dirp)) != NULL) { const char *fname = d->d_name; if (fname[0] == prefix_char) mailbox_name = fname + 1; else { if (prefix_char != '\0' || fname[0] == '.') continue; mailbox_name = fname; } /* skip . and .. */ if (fname[0] == '.' && (fname[1] == '\0' || (fname[1] == '.' && fname[2] == '\0'))) continue; if (!virtual_names) { str_truncate(mailbox, 0); str_append(mailbox, mailbox_name); mailbox_name = str_c(mailbox); } else { mailbox_name = mail_namespace_get_vname(ns, mailbox, mailbox_name); } /* make sure the pattern matches */ match = imap_match(glob, mailbox_name); if ((match & (IMAP_MATCH_YES | IMAP_MATCH_PARENT)) == 0) continue; /* check if this is an actual mailbox */ flags = 0; T_BEGIN { ret = ctx->ctx.list->v. iter_is_mailbox(&ctx->ctx, ctx->dir, fname, mailbox_name, mailbox_list_get_file_type(d), &flags); } T_END; if (ret <= 0) { if (ret < 0) return -1; continue; } /* 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) { T_BEGIN { maildir_fill_parents(ctx, glob, update_only, mailbox, flags); } T_END; } else { created = FALSE; node = update_only ? mailbox_tree_lookup(ctx->tree_ctx, mailbox_name) : mailbox_tree_get(ctx->tree_ctx, mailbox_name, &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, mailbox); } } } if (closedir(dirp) < 0) { mailbox_list_set_critical(ctx->ctx.list, "readdir(%s) failed: %m", ctx->dir); return -1; } if ((ns->flags & NAMESPACE_FLAG_INBOX) != 0) { /* make sure INBOX is listed */ if (!virtual_names) mailbox_name = "INBOX"; else { mailbox_name = mail_namespace_get_vname(ns, mailbox, "INBOX"); } created = FALSE; node = update_only ? mailbox_tree_lookup(ctx->tree_ctx, mailbox_name) : mailbox_tree_get(ctx->tree_ctx, mailbox_name, &created); if (created) node->flags = MAILBOX_NOCHILDREN; else if (node != NULL) node->flags &= ~MAILBOX_NONEXISTENT; match = imap_match(glob, mailbox_name); if ((match & (IMAP_MATCH_YES | IMAP_MATCH_PARENT)) != 0) { if (!update_only) node->flags |= MAILBOX_MATCHED; } } 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) { struct maildir_list_iterate_context *ctx; struct imap_match_glob *glob; char sep; pool_t pool; int ret; sep = (flags & MAILBOX_LIST_ITER_VIRTUAL_NAMES) != 0 ? _list->ns->sep : _list->ns->real_sep; 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(sep); ctx->info.ns = _list->ns; glob = imap_match_init_multiple(pool, patterns, TRUE, sep); ctx->dir = _list->set.root_dir; 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, glob, FALSE) < 0) { ctx->ctx.failed = TRUE; return &ctx->ctx; } } if ((flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0 || (flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) == 0) { /* Add/update mailbox list with flags */ bool update_only = (flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0; T_BEGIN { ret = maildir_fill_readdir(ctx, glob, update_only); } T_END; 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, 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; } 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; if (ctx->tree_iter != NULL) mailbox_tree_iterate_deinit(&ctx->tree_iter); mailbox_tree_deinit(&ctx->tree_ctx); pool_unref(&ctx->pool); return ret; } const 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; if (ctx->ctx.failed) return NULL; node = mailbox_tree_iterate_next(ctx->tree_iter, &ctx->info.name); if (node == NULL) return NULL; ctx->info.flags = node->flags; return &ctx->info; }