[dovecot-cvs] dovecot/src/lib-storage/index/mbox mbox-list.c,1.13,1.14 mbox-storage.c,1.39,1.40 mbox-storage.h,1.14,1.15

cras at procontrol.fi cras at procontrol.fi
Wed Feb 19 21:55:30 EET 2003


Update of /home/cvs/dovecot/src/lib-storage/index/mbox
In directory danu:/tmp/cvs-serv21976/src/lib-storage/index/mbox

Modified Files:
	mbox-list.c mbox-storage.c mbox-storage.h 
Log Message:
Rewrote LIST, LSUB and subscription file handling. LIST replies aren't
sorted anymore by default, it can be enabled with client_workarounds =
list-sort.



Index: mbox-list.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib-storage/index/mbox/mbox-list.c,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -d -r1.13 -r1.14
--- mbox-list.c	11 Feb 2003 19:37:16 -0000	1.13
+++ mbox-list.c	19 Feb 2003 19:55:27 -0000	1.14
@@ -11,255 +11,380 @@
 #include <dirent.h>
 #include <sys/stat.h>
 
-struct find_subscribed_context {
-	mailbox_list_callback_t *callback;
-	void *context;
+#define STAT_GET_MARKED(st) \
+	((st).st_size != 0 && (st).st_atime < (st).st_ctime ? \
+	 MAILBOX_MARKED : MAILBOX_UNMARKED)
+
+struct list_dir_context {
+	struct list_dir_context *prev;
+
+	DIR *dirp;
+	char *real_path, *virtual_path;
 };
 
-struct list_context {
+struct mailbox_list_context {
 	struct mail_storage *storage;
+	enum mailbox_list_flags flags;
+
 	struct imap_match_glob *glob;
-	mailbox_list_callback_t *callback;
-	void *context;
+	struct subsfile_list_context *subsfile_ctx;
 
-	const char *rootdir;
+	int failed;
+
+	struct mailbox_list *(*next)(struct mailbox_list_context *ctx);
+
+	pool_t list_pool;
+	struct mailbox_list list;
+        struct list_dir_context *dir;
 };
 
-static int mbox_find_path(struct list_context *ctx, const char *relative_dir)
+static struct mailbox_list *mbox_list_subs(struct mailbox_list_context *ctx);
+static struct mailbox_list *mbox_list_inbox(struct mailbox_list_context *ctx);
+static struct mailbox_list *mbox_list_path(struct mailbox_list_context *ctx);
+static struct mailbox_list *mbox_list_next(struct mailbox_list_context *ctx);
+
+static const char *mask_get_dir(const char *mask)
 {
-	DIR *dirp;
-	struct dirent *d;
-	struct stat st;
-	const char *dir, *listpath;
-	char fulldir[PATH_MAX], path[PATH_MAX], fullpath[PATH_MAX];
-	int failed, match;
-	size_t len;
+	const char *p, *last_dir;
 
-	t_push();
+	last_dir = NULL;
+	for (p = mask; *p != '\0' && *p != '%' && *p != '*'; p++) {
+		if (*p == '/')
+			last_dir = p;
+	}
 
-	if (relative_dir == NULL)
-		dir = ctx->rootdir;
-	else if (*ctx->rootdir == '\0' && *relative_dir != '\0')
-		dir = relative_dir;
-	else {
-		if (str_path(fulldir, sizeof(fulldir),
-			     ctx->rootdir, relative_dir) < 0) {
-			mail_storage_set_critical(ctx->storage,
-						  "Path too long: %s",
-						  relative_dir);
-			return FALSE;
-		}
+	return last_dir == NULL ? NULL : t_strdup_until(mask, last_dir);
+}
 
-		dir = fulldir;
-	}
+static const char *mbox_get_path(struct mail_storage *storage, const char *name)
+{
+	if (!full_filesystem_access || name == NULL ||
+	    (*name != '/' && *name != '~' && *name != '\0'))
+		return t_strconcat(storage->dir, "/", name, NULL);
+	else
+		return home_expand(name);
+}
 
-	dir = home_expand(dir);
-	dirp = opendir(dir);
-	if (dirp == NULL) {
-		t_pop();
+static int list_opendir(struct mail_storage *storage,
+			const char *path, int root, DIR **dirp)
+{
+	*dirp = opendir(*path == '\0' ? "/" : path);
+	if (*dirp != NULL)
+		return 1;
 
-		if (relative_dir != NULL &&
-		    (errno == ENOENT || errno == ENOTDIR)) {
-			/* probably just race condition with other client
-			   deleting the mailbox. */
-			return TRUE;
-		}
+	if (!root && (errno == ENOENT || errno == ENOTDIR)) {
+		/* probably just race condition with other client
+		   deleting the mailbox. */
+		return 0;
+	}
 
-		if (errno == EACCES) {
-			if (relative_dir != NULL) {
-				/* subfolder, ignore */
-				return TRUE;
-			}
-			mail_storage_set_error(ctx->storage, "Access denied");
-			return FALSE;
+	if (errno == EACCES) {
+		if (!root) {
+			/* subfolder, ignore */
+			return 0;
 		}
-
-		mail_storage_set_critical(ctx->storage,
-					  "opendir(%s) failed: %m", dir);
-		return FALSE;
+		mail_storage_set_error(storage, "Access denied");
+		return -1;
 	}
 
-	failed = FALSE;
-	while ((d = readdir(dirp)) != NULL) {
-		const char *fname = d->d_name;
+	mail_storage_set_critical(storage, "opendir(%s) failed: %m", path);
+	return -1;
+}
 
-		/* skip all hidden files */
-		if (fname[0] == '.')
-			continue;
+struct mailbox_list_context *
+mbox_list_mailbox_init(struct mail_storage *storage, const char *mask,
+		       enum mailbox_list_flags flags, int *sorted)
+{
+	struct mailbox_list_context *ctx;
+	const char *path, *virtual_path;
+	DIR *dirp;
 
-		/* skip all .lock files */
-		len = strlen(fname);
-		if (len > 5 && strcmp(fname+len-5, ".lock") == 0)
-			continue;
+	*sorted = (flags & MAILBOX_LIST_SUBSCRIBED) == 0;
 
-		/* check the mask */
-		if (relative_dir == NULL)
-			listpath = fname;
-		else {
-			if (str_path(path, sizeof(path),
-				     relative_dir, fname) < 0) {
-				mail_storage_set_critical(ctx->storage,
-					"Path too long: %s/%s",
-					relative_dir, fname);
-				failed = TRUE;
-				break;
-			}
-			listpath = path;
-		}
+	/* check that we're not trying to do any "../../" lists */
+	if (!mbox_is_valid_mask(mask)) {
+		mail_storage_set_error(storage, "Invalid mask");
+		return NULL;
+	}
 
-		if ((match = imap_match(ctx->glob, listpath)) < 0)
-			continue;
+	mail_storage_clear_error(storage);
 
-		/* see if it's a directory */
-		if (str_path(fullpath, sizeof(fullpath), dir, fname) < 0) {
-			mail_storage_set_critical(ctx->storage,
-						  "Path too long: %s/%s",
-						  dir, fname);
-			failed = TRUE;
-			break;
+	if ((flags & MAILBOX_LIST_SUBSCRIBED) != 0) {
+		ctx = i_new(struct mailbox_list_context, 1);
+		ctx->storage = storage;
+		ctx->flags = flags;
+		ctx->next = mbox_list_subs;
+		ctx->subsfile_ctx = subsfile_list_init(storage);
+		if (ctx->subsfile_ctx == NULL) {
+			i_free(ctx);
+			return NULL;
 		}
+		ctx->glob = imap_match_init(default_pool, mask, TRUE, '/');
+		return ctx;
+	}
 
-		if (stat(fullpath, &st) < 0) {
-			if (errno == ENOENT)
-				continue; /* just deleted, ignore */
+	/* if we're matching only subdirectories, don't bother scanning the
+	   parent directories */
+	virtual_path = mask_get_dir(mask);
 
-			mail_storage_set_critical(ctx->storage,
-						  "stat(%s) failed: %m",
-						  fullpath);
+	path = mbox_get_path(storage, virtual_path);
+	if (list_opendir(storage, path, TRUE, &dirp) <= 0)
+		return NULL;
+
+	ctx = i_new(struct mailbox_list_context, 1);
+	ctx->storage = storage;
+	ctx->flags = flags;
+	ctx->glob = imap_match_init(default_pool, mask, TRUE, '/');
+	ctx->list_pool = pool_alloconly_create("mbox_list", 1024);
+
+	if (virtual_path == NULL && imap_match(ctx->glob, "INBOX") > 0)
+		ctx->next = mbox_list_inbox;
+	else if (virtual_path != NULL)
+		ctx->next = mbox_list_path;
+	else
+		ctx->next = mbox_list_next;
+
+	ctx->dir = i_new(struct list_dir_context, 1);
+	ctx->dir->dirp = dirp;
+	ctx->dir->real_path = i_strdup(path);
+	ctx->dir->virtual_path = i_strdup(virtual_path);
+	return ctx;
+}
+
+static void list_dir_context_free(struct list_dir_context *dir)
+{
+	(void)closedir(dir->dirp);
+	i_free(dir->real_path);
+	i_free(dir->virtual_path);
+	i_free(dir);
+}
+
+int mbox_list_mailbox_deinit(struct mailbox_list_context *ctx)
+{
+	int failed = ctx->failed;
+
+	if (ctx->subsfile_ctx != NULL) {
+		if (!subsfile_list_deinit(ctx->subsfile_ctx))
 			failed = TRUE;
-			break;
-		}
+	}
 
-		if (S_ISDIR(st.st_mode)) {
-			/* subdirectory, scan it too */
-			t_push();
-			ctx->callback(ctx->storage, listpath, MAILBOX_NOSELECT,
-				      ctx->context);
-			t_pop();
+	while (ctx->dir != NULL) {
+		struct list_dir_context *dir = ctx->dir;
 
-			if (!mbox_find_path(ctx, listpath)) {
-				failed = TRUE;
-				break;
-			}
-		} else if (match > 0 &&
-			   strcmp(fullpath, ctx->storage->inbox_file) != 0 &&
-			   strcasecmp(listpath, "INBOX") != 0) {
-			/* don't match any INBOX here, it's added later.
-			   we might also have ~/mail/inbox, ~/mail/Inbox etc.
-			   Just ignore them for now. */
-			t_push();
-			ctx->callback(ctx->storage, listpath,
-				      MAILBOX_NOINFERIORS, ctx->context);
-			t_pop();
-		}
+		ctx->dir = dir->prev;
+                list_dir_context_free(dir);
 	}
 
-	t_pop();
+	if (ctx->list_pool != NULL)
+		pool_unref(ctx->list_pool);
+	imap_match_deinit(ctx->glob);
+	i_free(ctx);
 
-	(void)closedir(dirp);
 	return !failed;
 }
 
-static const char *mask_get_dir(const char *mask)
+struct mailbox_list *mbox_list_mailbox_next(struct mailbox_list_context *ctx)
 {
-	const char *p, *last_dir;
+	return ctx->next(ctx);
+}
 
-	last_dir = NULL;
-	for (p = mask; *p != '\0' && *p != '%' && *p != '*'; p++) {
-		if (*p == '/')
-			last_dir = p;
+static int list_file(struct mailbox_list_context *ctx, const char *fname)
+{
+        struct list_dir_context *dir;
+	const char *list_path, *real_path, *path;
+	struct stat st;
+	DIR *dirp;
+	size_t len;
+	enum imap_match_result match, match2;
+	int ret;
+
+	/* skip all hidden files */
+	if (fname[0] == '.')
+		return 0;
+
+	/* skip all .lock files */
+	len = strlen(fname);
+	if (len > 5 && strcmp(fname+len-5, ".lock") == 0)
+		return 0;
+
+	/* check the mask */
+	if (ctx->dir->virtual_path == NULL)
+		list_path = fname;
+	else {
+		list_path = t_strconcat(ctx->dir->virtual_path,
+					"/", fname, NULL);
 	}
 
-	return last_dir == NULL ? NULL : t_strdup_until(mask, last_dir);
-}
+	if ((match = imap_match(ctx->glob, list_path)) < 0)
+		return 0;
 
-int mbox_find_mailboxes(struct mail_storage *storage, const char *mask,
-			mailbox_list_callback_t callback, void *context)
-{
-        struct list_context ctx;
-	struct imap_match_glob *glob;
-	const char *relative_dir;
+	/* see if it's a directory */
+	real_path = t_strconcat(ctx->dir->real_path, "/", fname, NULL);
+	if (stat(real_path, &st) < 0) {
+		if (errno == ENOENT)
+			return 0; /* just deleted, ignore */
+		mail_storage_set_critical(ctx->storage, "stat(%s) failed: %m",
+					  real_path);
+		return -1;
+	}
 
-	/* check that we're not trying to do any "../../" lists */
-	if (!mbox_is_valid_mask(mask)) {
-		mail_storage_set_error(storage, "Invalid mask");
-		return FALSE;
+	if (S_ISDIR(st.st_mode)) {
+		/* subdirectory. scan inside it. */
+		path = t_strconcat(list_path, "/", NULL);
+		match2 = imap_match(ctx->glob, path);
+
+		if (match > 0) {
+			ctx->list.flags = MAILBOX_NOSELECT;
+			ctx->list.name = p_strdup(ctx->list_pool, list_path);
+		} else if (match2 > 0) {
+			ctx->list.flags = MAILBOX_NOSELECT;
+			ctx->list.name = p_strdup(ctx->list_pool, path);
+		}
+
+		ret = match2 < 0 ? 0 :
+			list_opendir(ctx->storage, real_path, FALSE, &dirp);
+		if (ret > 0) {
+			dir = i_new(struct list_dir_context, 1);
+			dir->dirp = dirp;
+			dir->real_path = i_strdup(real_path);
+			dir->virtual_path = i_strdup(list_path);
+
+			dir->prev = ctx->dir;
+			ctx->dir = dir;
+		} else if (ret < 0)
+			return -1;
+		return match > 0 || match2 > 0;
+	} else if (match > 0 &&
+		   strcmp(real_path, ctx->storage->inbox_file) != 0 &&
+		   strcasecmp(list_path, "INBOX") != 0) {
+		/* don't match any INBOX here, it's added separately.
+		   we might also have ~/mail/inbox, ~/mail/Inbox etc.
+		   Just ignore them for now. */
+		ctx->list.flags = MAILBOX_NOINFERIORS | STAT_GET_MARKED(st);
+		ctx->list.name = p_strdup(ctx->list_pool, list_path);
+		return 1;
 	}
 
-	mail_storage_clear_error(storage);
+	return 0;
+}
 
-	/* if we're matching only subdirectories, don't bother scanning the
-	   parent directories */
-	relative_dir = mask_get_dir(mask);
+static struct mailbox_list *mbox_list_subs(struct mailbox_list_context *ctx)
+{
+	struct stat st;
+	const char *name, *path, *p;
+	enum imap_match_result match = IMAP_MATCH_NO;
 
-	glob = imap_match_init(mask, TRUE, '/');
-	if (relative_dir == NULL && imap_match(glob, "INBOX") > 0) {
-		/* INBOX exists always, even if the file doesn't. */
-		callback(storage, "INBOX", MAILBOX_NOINFERIORS, context);
+	while ((name = subsfile_list_next(ctx->subsfile_ctx)) != NULL) {
+		match = imap_match(ctx->glob, name);
+		if (match == IMAP_MATCH_YES || match == IMAP_MATCH_PARENT)
+			break;
 	}
 
-	memset(&ctx, 0, sizeof(ctx));
-	ctx.storage = storage;
-	ctx.glob = glob;
-	ctx.callback = callback;
-	ctx.context = context;
+	if (name == NULL)
+		return NULL;
 
-	if (!full_filesystem_access || relative_dir == NULL ||
-	    (*relative_dir != '/' && *relative_dir != '~' &&
-	     *relative_dir != '\0'))
-		ctx.rootdir = storage->dir;
-	else
-		ctx.rootdir = "";
+	ctx->list.flags = 0;
+	ctx->list.name = name;
 
-	if (relative_dir != NULL) {
-		const char *matchdir = t_strconcat(relative_dir, "/", NULL);
+	if ((ctx->flags & MAILBOX_LIST_NO_FLAGS) != 0)
+		return &ctx->list;
 
-		if (imap_match(ctx.glob, matchdir) > 0) {
-			t_push();
-			ctx.callback(ctx.storage, matchdir, MAILBOX_NOSELECT,
-				     ctx.context);
-			t_pop();
+	if (match == IMAP_MATCH_PARENT) {
+		/* placeholder */
+		ctx->list.flags = MAILBOX_NOSELECT;
+		while ((p = strrchr(name, '/')) != NULL) {
+			name = t_strdup_until(name, p);
+			if (imap_match(ctx->glob, name) > 0) {
+				ctx->list.name = name;
+				return &ctx->list;
+			}
 		}
+		i_unreached();
 	}
 
-	if (!mbox_find_path(&ctx, relative_dir))
-		return FALSE;
-
-	return TRUE;
+	t_push();
+	path = mbox_get_path(ctx->storage, ctx->list.name);
+	if (stat(path, &st) == 0) {
+		if (S_ISDIR(st.st_mode))
+			ctx->list.flags = MAILBOX_NOSELECT;
+		else {
+			ctx->list.flags = MAILBOX_NOINFERIORS |
+				STAT_GET_MARKED(st);
+		}
+	} else {
+		if (strcasecmp(ctx->list.name, "INBOX") == 0)
+			ctx->list.flags = MAILBOX_UNMARKED;
+		else
+			ctx->list.flags = MAILBOX_NOSELECT;
+	}
+	t_pop();
+	return &ctx->list;
 }
 
-static int mbox_subs_cb(struct mail_storage *storage, const char *name,
-			void *context)
+static struct mailbox_list *mbox_list_inbox(struct mailbox_list_context *ctx)
 {
-	struct find_subscribed_context *ctx = context;
-	enum mailbox_flags flags;
 	struct stat st;
-	char path[PATH_MAX];
 
-	/* see if the mailbox exists, don't bother with the marked flags */
-	if (strcasecmp(name, "INBOX") == 0) {
-		/* inbox always exists */
-		flags = 0;
-	} else {
-		flags = str_path(path, sizeof(path), storage->dir, name) == 0 &&
-			stat(path, &st) == 0 && !S_ISDIR(st.st_mode) ?
-			0 : MAILBOX_NOSELECT;
+	if (ctx->dir->virtual_path != NULL)
+		ctx->next = mbox_list_path;
+	else
+		ctx->next = mbox_list_next;
+
+	/* INBOX exists always, even if the file doesn't. */
+	ctx->list.flags = MAILBOX_NOINFERIORS;
+	if ((ctx->flags & MAILBOX_LIST_NO_FLAGS) == 0) {
+		if (stat(ctx->storage->inbox_file, &st) < 0)
+			ctx->list.flags |= MAILBOX_UNMARKED;
+		else
+			ctx->list.flags |= STAT_GET_MARKED(st);
 	}
 
-	ctx->callback(storage, name, flags, ctx->context);
-	return TRUE;
+	ctx->list.name = "INBOX";
+	return &ctx->list;
 }
 
-int mbox_find_subscribed(struct mail_storage *storage, const char *mask,
-			 mailbox_list_callback_t callback, void *context)
+static struct mailbox_list *mbox_list_path(struct mailbox_list_context *ctx)
 {
-	struct find_subscribed_context ctx;
+	ctx->next = mbox_list_next;
 
-	ctx.callback = callback;
-	ctx.context = context;
+	ctx->list.flags = MAILBOX_NOSELECT;
+	ctx->list.name = p_strconcat(ctx->list_pool,
+				     ctx->dir->virtual_path, "/", NULL);
 
-	if (subsfile_foreach(storage, mask, mbox_subs_cb, &ctx) <= 0)
-		return FALSE;
+	if (imap_match(ctx->glob, ctx->list.name) > 0)
+		return &ctx->list;
+	else
+		return ctx->next(ctx);
+}
 
-	return TRUE;
+static struct mailbox_list *mbox_list_next(struct mailbox_list_context *ctx)
+{
+	struct list_dir_context *dir;
+	struct dirent *d;
+	int ret;
+
+	p_clear(ctx->list_pool);
+
+	while (ctx->dir != NULL) {
+		/* NOTE: list_file() may change ctx->dir */
+		while ((d = readdir(ctx->dir->dirp)) != NULL) {
+			t_push();
+			ret = list_file(ctx, d->d_name);
+			t_pop();
+
+			if (ret > 0)
+				return &ctx->list;
+			if (ret < 0) {
+				ctx->failed = TRUE;
+				return NULL;
+			}
+		}
+
+		dir = ctx->dir;
+		ctx->dir = dir->prev;
+		list_dir_context_free(dir);
+	}
+
+	/* finished */
+	return NULL;
 }

Index: mbox-storage.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib-storage/index/mbox/mbox-storage.c,v
retrieving revision 1.39
retrieving revision 1.40
diff -u -d -r1.39 -r1.40
--- mbox-storage.c	14 Feb 2003 10:46:44 -0000	1.39
+++ mbox-storage.c	19 Feb 2003 19:55:27 -0000	1.40
@@ -645,9 +645,10 @@
 	mbox_create_mailbox,
 	mbox_delete_mailbox,
 	mbox_rename_mailbox,
-	mbox_find_mailboxes,
+	mbox_list_mailbox_init,
+	mbox_list_mailbox_deinit,
+	mbox_list_mailbox_next,
 	subsfile_set_subscribed,
-	mbox_find_subscribed,
 	mbox_get_mailbox_name_status,
 	mail_storage_get_last_error,
 

Index: mbox-storage.h
===================================================================
RCS file: /home/cvs/dovecot/src/lib-storage/index/mbox/mbox-storage.h,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -d -r1.14 -r1.15
--- mbox-storage.h	22 Jan 2003 19:23:28 -0000	1.14
+++ mbox-storage.h	19 Feb 2003 19:55:27 -0000	1.15
@@ -14,10 +14,11 @@
 			   time_t received_date, int timezone_offset,
 			   struct istream *data);
 
-int mbox_find_mailboxes(struct mail_storage *storage, const char *mask,
-			mailbox_list_callback_t callback, void *context);
-int mbox_find_subscribed(struct mail_storage *storage, const char *mask,
-			 mailbox_list_callback_t callback, void *context);
+struct mailbox_list_context *
+mbox_list_mailbox_init(struct mail_storage *storage, const char *mask,
+		       enum mailbox_list_flags flags, int *sorted);
+int mbox_list_mailbox_deinit(struct mailbox_list_context *ctx);
+struct mailbox_list *mbox_list_mailbox_next(struct mailbox_list_context *ctx);
 
 int mbox_expunge_locked(struct index_mailbox *ibox, int notify);
 




More information about the dovecot-cvs mailing list