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;
+}