dovecot-2.1: lib-storage: FS layout mailbox list iteration code ...

dovecot at dovecot.org dovecot at dovecot.org
Sat Dec 10 07:07:20 EET 2011


details:   http://hg.dovecot.org/dovecot-2.1/rev/ab696ead12cc
changeset: 13835:ab696ead12cc
user:      Timo Sirainen <tss at iki.fi>
date:      Sat Dec 10 07:05:56 2011 +0200
description:
lib-storage: FS layout mailbox list iteration code rewrite.
This fixes listing non-ASCII mailboxes. Also the new way no longer keeps
more than one file descriptor open.

diffstat:

 src/lib-storage/list/mailbox-list-fs-flags.c |     4 +-
 src/lib-storage/list/mailbox-list-fs-iter.c  |  1005 +++++++++++--------------
 2 files changed, 454 insertions(+), 555 deletions(-)

diffs (truncated from 1193 to 300 lines):

diff -r 5366e59d573f -r ab696ead12cc src/lib-storage/list/mailbox-list-fs-flags.c
--- a/src/lib-storage/list/mailbox-list-fs-flags.c	Sat Dec 10 07:03:09 2011 +0200
+++ b/src/lib-storage/list/mailbox-list-fs-flags.c	Sat Dec 10 07:05:56 2011 +0200
@@ -117,7 +117,7 @@
 	*flags_r = 0;
 
 	if (*list->set.maildir_name != '\0') {
-		/* maildir_name is set: we the code is common for all
+		/* maildir_name is set: the code is common for all
 		   storage types */
 		return list_is_maildir_mailbox(list, dir, fname, type, flags_r);
 	}
@@ -131,7 +131,7 @@
 	switch (type) {
 	case MAILBOX_LIST_FILE_TYPE_DIR:
 		if ((list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0) {
-			*flags_r |= MAILBOX_NOSELECT | MAILBOX_CHILDREN;
+			*flags_r |= MAILBOX_NOSELECT;
 			return 1;
 		}
 		break;
diff -r 5366e59d573f -r ab696ead12cc src/lib-storage/list/mailbox-list-fs-iter.c
--- a/src/lib-storage/list/mailbox-list-fs-iter.c	Sat Dec 10 07:03:09 2011 +0200
+++ b/src/lib-storage/list/mailbox-list-fs-iter.c	Sat Dec 10 07:05:56 2011 +0200
@@ -14,193 +14,370 @@
 
 struct list_dir_entry {
 	const char *fname;
-	enum mailbox_list_file_type type;
+	enum mailbox_info_flags info_flags;
 };
 
 struct list_dir_context {
-	struct list_dir_context *prev;
+	struct list_dir_context *parent;
 
-	DIR *dirp;
-	char *real_path, *virtual_path;
+	pool_t pool;
+	const char *storage_name;
+	/* this directory's info flags. */
+	enum mailbox_info_flags info_flags;
 
-	const struct list_dir_entry *next_entry;
-	struct list_dir_entry entry;
-	char *entry_fname;
-	struct mailbox_info info;
-
-	unsigned int pattern_pos;
-
-	unsigned int delayed_send:1;
+	/* all files in this directory */
+	ARRAY_DEFINE(entries, struct list_dir_entry);
+	unsigned int entry_idx;
 };
 
 struct fs_list_iterate_context {
 	struct mailbox_list_iterate_context ctx;
 
-	ARRAY_DEFINE(valid_patterns, const char *);
+	const char *const *valid_patterns;
+	/* roots can be either /foo, ~user/bar or baz */
+	ARRAY_DEFINE(roots, const char *);
+	unsigned int root_idx;
 	char sep;
 
-	enum mailbox_info_flags inbox_flags;
-
-	const struct mailbox_info *(*next)(struct fs_list_iterate_context *ctx);
-
 	pool_t info_pool;
 	struct mailbox_info info;
-        struct list_dir_context *dir;
+	/* current directory we're handling */
+	struct list_dir_context *dir;
 
-	unsigned int inbox_match:1;
 	unsigned int inbox_found:1;
-	unsigned int inbox_listed:1;
 };
 
-static const struct mailbox_info *
-fs_list_next(struct fs_list_iterate_context *ctx);
+static int
+fs_get_existence_info_flag(struct fs_list_iterate_context *ctx,
+			   const char *vname,
+			   enum mailbox_info_flags *info_flags)
+{
+	struct mailbox *box;
+	enum mailbox_existence existence;
+	bool auto_boxes;
+	int ret;
+
+	auto_boxes = (ctx->ctx.flags & MAILBOX_LIST_ITER_NO_AUTO_BOXES) == 0;
+	box = mailbox_alloc(ctx->ctx.list, vname, 0);
+	ret = mailbox_exists(box, auto_boxes, &existence);
+	mailbox_free(&box);
+
+	if (ret < 0) {
+		/* this can only be an internal error */
+		mailbox_list_set_internal_error(ctx->ctx.list);
+		return -1;
+	}
+	switch (existence) {
+	case MAILBOX_EXISTENCE_NONE:
+		*info_flags |= MAILBOX_NONEXISTENT;
+		break;
+	case MAILBOX_EXISTENCE_NOSELECT:
+		*info_flags |= MAILBOX_NOSELECT;
+		break;
+	case MAILBOX_EXISTENCE_SELECT:
+		*info_flags |= MAILBOX_SELECT;
+		break;
+	}
+	return 0;
+}
 
 static int
-pattern_get_path_pos(struct fs_list_iterate_context *ctx, const char *pattern,
-		     const char *path, unsigned int *pos_r)
+dir_entry_get(struct fs_list_iterate_context *ctx, const char *dir_path,
+	      struct list_dir_context *dir, const struct dirent *d)
 {
-	unsigned int i, j;
+	const char *storage_name, *vname, *root_dir;
+	struct list_dir_entry *entry;
+	enum imap_match_result match;
+	enum mailbox_info_flags info_flags;
+	int ret;
 
-	if (strncasecmp(path, "INBOX", 5) == 0 && path[5] == ctx->sep) {
-		/* make sure INBOX prefix is matched case-insensitively */
-		char *tmp = t_strdup_noconst(pattern);
+	/* skip . and .. */
+	if (d->d_name[0] == '.' &&
+	    (d->d_name[1] == '\0' ||
+	     (d->d_name[1] == '.' && d->d_name[2] == '\0')))
+		return 0;
 
-		if (strncmp(path, "INBOX", 5) != 0)
-			path = t_strdup_printf("INBOX%c%s", ctx->sep, path + 6);
-
-		for (i = 0; tmp[i] != ctx->sep && tmp[i] != '\0'; i++)
-			tmp[i] = i_toupper(tmp[i]);
-		pattern = tmp;
+	if (strcmp(d->d_name, ctx->ctx.list->set.maildir_name) == 0) {
+		/* mail storage's internal directory (e.g. dbox-Mails).
+		   this also means that the parent is selectable */
+		dir->info_flags &= ~MAILBOX_NOSELECT;
+		dir->info_flags |= MAILBOX_SELECT;
+		return 0;
+	}
+	if (strcmp(d->d_name, ctx->ctx.list->set.subscription_fname) == 0) {
+		/* if this is the subscriptions file, skip it */
+		root_dir = mailbox_list_get_path(ctx->ctx.list, NULL,
+						 MAILBOX_LIST_PATH_TYPE_DIR);
+		if (strcmp(root_dir, dir_path) == 0)
+			return 0;
 	}
 
-	for (i = j = 0; path[i] != '\0'; i++) {
-		if (pattern[j] == '*')
+	/* check the pattern */
+	storage_name = *dir->storage_name == '\0' ? d->d_name :
+		t_strconcat(dir->storage_name, "/", d->d_name, NULL);
+	vname = mailbox_list_get_vname(ctx->ctx.list, storage_name);
+	match = imap_match(ctx->ctx.glob, vname);
+
+	if ((dir->info_flags & (MAILBOX_CHILDREN | MAILBOX_NOCHILDREN |
+				MAILBOX_NOINFERIORS)) == 0 &&
+	    (ctx->ctx.flags & MAILBOX_LIST_ITER_RETURN_CHILDREN) != 0) {
+		/* we don't know yet if the parent has children. need to figure
+		   out if this file is actually a visible mailbox */
+	} else if (match != IMAP_MATCH_YES &&
+		   (match & IMAP_MATCH_CHILDREN) == 0) {
+		/* mailbox doesn't match any patterns, we don't care about it */
+		return 0;
+	}
+	ret = ctx->ctx.list->v.
+		get_mailbox_flags(ctx->ctx.list, dir_path, d->d_name,
+				  mailbox_list_get_file_type(d), &info_flags);
+	if (ret <= 0)
+		return ret;
+	if (!MAILBOX_INFO_FLAGS_FINISHED(info_flags)) {
+		/* mailbox existence isn't known yet. need to figure it out
+		   the hard way. */
+		if (fs_get_existence_info_flag(ctx, vname, &info_flags) < 0)
 			return -1;
+	}
+	if ((info_flags & MAILBOX_NONEXISTENT) != 0)
+		return 0;
 
-		if (pattern[j] == '%') {
-			/* skip until we're at the next hierarchy separator */
-			if (path[i] == ctx->sep) {
-				/* assume that pattern matches. we can't be
-				   sure, but it'll be checked later. */
-				for (j++; pattern[j] != '\0'; j++) {
-					if (pattern[j] == '*')
-						return -1;
-					if (pattern[j] == ctx->sep) {
-						j++;
-						break;
-					}
-				}
-			}
-		} else {
-			if (path[i] != pattern[j]) {
-				/* pattern doesn't match path at all */
-				return 0;
-			}
-			j++;
-		}
+	/* mailbox exists - make sure parent knows it has children */
+	dir->info_flags &= ~(MAILBOX_NOCHILDREN | MAILBOX_NOINFERIORS);
+	dir->info_flags |= MAILBOX_CHILDREN;
+
+	if (match != IMAP_MATCH_YES && (match & IMAP_MATCH_CHILDREN) == 0) {
+		/* this mailbox didn't actually match any pattern,
+		   we just needed to know the children state */
+		return 0;
 	}
-	*pos_r = j;
-	return 1;
+
+	/* entry matched a pattern. we're going to return this. */
+	entry = array_append_space(&dir->entries);
+	entry->fname = p_strdup(dir->pool, d->d_name);
+	entry->info_flags = info_flags;
+	return 0;
 }
 
 static bool
-pattern_has_wildcard_at(struct fs_list_iterate_context *ctx,
-			const char *pattern, const char *path)
+fs_list_get_storage_path(struct fs_list_iterate_context *ctx,
+			 const char *storage_name, const char **path_r)
 {
-	unsigned int pos;
-	int ret;
+	const char *root, *path = storage_name;
 
-	if ((ret = pattern_get_path_pos(ctx, pattern, path, &pos)) < 0)
-		return TRUE;
-	if (ret == 0)
-		return FALSE;
-
-	if (pattern[pos] == '\0')
-		return TRUE;
-
-	for (; pattern[pos] != '\0' && pattern[pos] != ctx->sep; pos++) {
-		if (pattern[pos] == '%' || pattern[pos] == '*')
-			return TRUE;
+	if (*path == '~') {
+		if (!mailbox_list_try_get_absolute_path(ctx->ctx.list, &path)) {
+			/* couldn't expand ~user/ */
+			return FALSE;
+		}
+		/* NOTE: the path may have been translated to a storage_name
+		   instead of path */
 	}
-	return FALSE;
+	if (*path != '/') {
+		/* non-absolute path. add the mailbox root dir as prefix. */
+		root = mailbox_list_get_path(ctx->ctx.list, NULL,
+					     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+		path = t_strconcat(root, "/", path, NULL);
+	}
+	*path_r = path;
+	return TRUE;
 }
 
-static int list_opendir(struct fs_list_iterate_context *ctx,
-			const char *path, const char *list_path, DIR **dirp)
+static int
+fs_list_dir_read(struct fs_list_iterate_context *ctx,
+		 struct list_dir_context *dir)
 {
-	const char *const *patterns;
-	unsigned int i;
+	DIR *fsdir;
+	struct dirent *d;
+	struct list_dir_entry *entry;
+	const char *path, *vname;
+	int ret = 0;
 
-	/* if no patterns have wildcards at this point of the path, we don't
-	   have to readdir() the files. instead we can just go through the
-	   mailboxes listed in patterns. */
-	T_BEGIN {
-		patterns = array_idx(&ctx->valid_patterns, 0);
-		for (i = 0; patterns[i] != NULL; i++) {
-			if (pattern_has_wildcard_at(ctx, patterns[i],
-						    list_path))
-				break;
+	if (!fs_list_get_storage_path(ctx, dir->storage_name, &path))
+		return 0;
+
+	fsdir = opendir(path);
+	if (fsdir == NULL) {


More information about the dovecot-cvs mailing list