[dovecot-cvs] dovecot/src/lib-storage/list index-mailbox-list-sync.c, 1.5, 1.6 mailbox-list-fs-iter.c, 1.2, 1.3 mailbox-list-fs.c, 1.4, 1.5 mailbox-list-maildir-iter.c, 1.2, 1.3 mailbox-list-maildir.c, 1.5, 1.6

tss at dovecot.org tss at dovecot.org
Thu Mar 29 10:59:34 EEST 2007


Update of /var/lib/cvs/dovecot/src/lib-storage/list
In directory talvi:/tmp/cvs-serv11000/lib-storage/list

Modified Files:
	index-mailbox-list-sync.c mailbox-list-fs-iter.c 
	mailbox-list-fs.c mailbox-list-maildir-iter.c 
	mailbox-list-maildir.c 
Log Message:
Moved delete/rename operations to mailbox_list API. Fixed mbox/maildir to
work with either fs/maildir++ directory layout. They can be changed by
appending :LAYOUT=fs|maildir++ to mail_location.



Index: index-mailbox-list-sync.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/lib-storage/list/index-mailbox-list-sync.c,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -d -r1.5 -r1.6
--- index-mailbox-list-sync.c	15 Mar 2007 13:34:50 -0000	1.5
+++ index-mailbox-list-sync.c	29 Mar 2007 07:59:24 -0000	1.6
@@ -470,7 +470,7 @@
 		index_list_next_hook_mailbox_created(box);
 
 	/* FIXME: maildir-only for now */
-	if (strcmp(box->storage->name, "maildir") != 0)
+	if (strcmp(box->storage->list->name, "maildir++") != 0)
 		return;
 
 	ibox = p_new(box->pool, struct index_list_mailbox, 1);

Index: mailbox-list-fs-iter.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/lib-storage/list/mailbox-list-fs-iter.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- mailbox-list-fs-iter.c	25 Nov 2006 22:17:50 -0000	1.2
+++ mailbox-list-fs-iter.c	29 Mar 2007 07:59:24 -0000	1.3
@@ -22,7 +22,8 @@
 	struct imap_match_glob *glob;
 	struct subsfile_list_context *subsfile_ctx;
 
-	bool inbox_found;
+	bool inbox_found, inbox_listed;
+	enum mailbox_info_flags inbox_flags;
 
 	struct mailbox_info *(*next)(struct fs_list_iterate_context *ctx);
 
@@ -121,7 +122,7 @@
 	virtual_path = mask_get_dir(mask);
 
 	path = mailbox_list_get_path(_list, virtual_path,
-				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+				     MAILBOX_LIST_PATH_TYPE_DIR);
 	if (list_opendir(_list, path, TRUE, &dirp) < 0)
 		return &ctx->ctx;
 	/* if user gave invalid directory, we just don't show any results. */
@@ -189,6 +190,41 @@
 	return ctx->next(ctx);
 }
 
+static void
+path_split(const char *path, const char **dir_r, const char **fname_r)
+{
+	const char *p;
+
+	p = strrchr(path, '/');
+	if (p == NULL) {
+		*dir_r = "";
+		*fname_r = path;
+	} else {
+		*dir_r = t_strdup_until(path, p);
+		*fname_r = p + 1;
+	}
+}
+
+static struct mailbox_info *fs_list_inbox(struct fs_list_iterate_context *ctx)
+{
+	const char *inbox_path, *dir, *fname;
+
+	ctx->info.flags = 0;
+	ctx->info.name = "INBOX";
+
+	t_push();
+	inbox_path = mailbox_list_get_path(ctx->ctx.list, "INBOX",
+					   MAILBOX_LIST_PATH_TYPE_DIR);
+	path_split(inbox_path, &dir, &fname);
+	if (ctx->ctx.list->v.iter_is_mailbox(&ctx->ctx, dir, fname,
+					     MAILBOX_LIST_FILE_TYPE_UNKNOWN,
+					     &ctx->info.flags) < 0)
+		ctx->ctx.failed = TRUE;
+	t_pop();
+
+	return &ctx->info;
+}
+
 static int
 list_file(struct fs_list_iterate_context *ctx, const struct dirent *d)
 {
@@ -218,23 +254,50 @@
 
 	/* get the info.flags using callback */
 	ctx->info.flags = 0;
-	ret = ctx->ctx.list->callback(ctx->dir->real_path, fname,
-				      mailbox_list_get_file_type(d),
-				      ctx->ctx.flags, &ctx->info.flags,
-				      ctx->ctx.list->context);
+	ret = ctx->ctx.list->v.
+		iter_is_mailbox(&ctx->ctx, ctx->dir->real_path, fname,
+				mailbox_list_get_file_type(d),
+				&ctx->info.flags);
 	if (ret <= 0)
 		return ret;
 
 	/* make sure we give only one correct INBOX */
 	real_path = t_strconcat(ctx->dir->real_path, "/", fname, NULL);
-	if (strcasecmp(list_path, "INBOX") == 0 &&
-	    (ctx->ctx.list->flags & MAILBOX_LIST_FLAG_INBOX) != 0) {
+	if ((ctx->ctx.list->flags & MAILBOX_LIST_FLAG_INBOX) != 0 &&
+	    strcasecmp(list_path, "INBOX") == 0) {
+		if (ctx->inbox_listed) {
+			/* already listed the INBOX */
+			return 0;
+		}
+
 		inbox_path = mailbox_list_get_path(ctx->ctx.list, "INBOX",
-					MAILBOX_LIST_PATH_TYPE_MAILBOX);
-		if (ctx->inbox_found || strcmp(real_path, inbox_path) != 0)
+						   MAILBOX_LIST_PATH_TYPE_DIR);
+		if (strcmp(real_path, inbox_path) == 0) {
+			/* delay listing in case there's a INBOX/ directory */
+			ctx->inbox_found = TRUE;
+			ctx->inbox_flags = ctx->info.flags;
+			return 0;
+		}
+		if (strcmp(fname, "INBOX") != 0 ||
+		    (ctx->info.flags & MAILBOX_NOINFERIORS) != 0) {
+			/* duplicate INBOX, can't show this */
 			return 0;
+		}
 
-		ctx->inbox_found = TRUE;
+		/* INBOX/ directory. show the INBOX list now */
+		if (!ctx->inbox_found) {
+			enum mailbox_info_flags dir_flags = ctx->info.flags;
+
+			(void)fs_list_inbox(ctx);
+			ctx->info.flags &= ~(MAILBOX_NOINFERIORS |
+					     MAILBOX_NOCHILDREN);
+			ctx->info.flags |= dir_flags;
+			ctx->inbox_found = TRUE;
+		} else {
+			ctx->info.flags &= ~MAILBOX_NOSELECT;
+			ctx->info.flags |= ctx->inbox_flags;
+		}
+		ctx->inbox_listed = TRUE;
 	}
 
 	if ((ctx->info.flags & MAILBOX_NOINFERIORS) == 0) {
@@ -270,21 +333,6 @@
 	return 0;
 }
 
-static void
-path_split(const char *path, const char **dir_r, const char **fname_r)
-{
-	const char *p;
-
-	p = strrchr(path, '/');
-	if (p == NULL) {
-		*dir_r = "";
-		*fname_r = path;
-	} else {
-		*dir_r = t_strdup_until(path, p);
-		*fname_r = p + 1;
-	}
-}
-
 static struct mailbox_info *fs_list_subs(struct fs_list_iterate_context *ctx)
 {
 	const char *name, *path, *p, *dir, *fname;
@@ -321,12 +369,11 @@
 
 	t_push();
 	path = mailbox_list_get_path(ctx->ctx.list, ctx->info.name,
-				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+				     MAILBOX_LIST_PATH_TYPE_DIR);
 	path_split(path, &dir, &fname);
-	if (ctx->ctx.list->callback(dir, fname,
-				    MAILBOX_LIST_FILE_TYPE_UNKNOWN,
-				    ctx->ctx.flags, &ctx->info.flags,
-				    ctx->ctx.list->context) < 0)
+	if (ctx->ctx.list->v.iter_is_mailbox(&ctx->ctx, dir, fname,
+					     MAILBOX_LIST_FILE_TYPE_UNKNOWN,
+					     &ctx->info.flags) < 0)
 		ctx->ctx.failed = TRUE;
 	t_pop();
 	return &ctx->info;
@@ -346,27 +393,6 @@
 		return ctx->next(ctx);
 }
 
-static struct mailbox_info *fs_list_inbox(struct fs_list_iterate_context *ctx)
-{
-	const char *inbox_path, *dir, *fname;
-
-	ctx->info.flags = 0;
-	ctx->info.name = "INBOX";
-
-	t_push();
-	inbox_path = mailbox_list_get_path(ctx->ctx.list, "INBOX",
-					   MAILBOX_LIST_PATH_TYPE_MAILBOX);
-	path_split(inbox_path, &dir, &fname);
-	if (ctx->ctx.list->callback(dir, fname,
-				    MAILBOX_LIST_FILE_TYPE_UNKNOWN,
-				    ctx->ctx.flags, &ctx->info.flags,
-				    ctx->ctx.list->context) < 0)
-		ctx->ctx.failed = TRUE;
-	t_pop();
-
-	return &ctx->info;
-}
-
 static struct mailbox_info *fs_list_next(struct fs_list_iterate_context *ctx)
 {
 	struct list_dir_context *dir;
@@ -399,9 +425,16 @@
 	    (ctx->ctx.list->flags & MAILBOX_LIST_FLAG_INBOX) != 0 &&
 	    ctx->glob != NULL && imap_match(ctx->glob, "INBOX") > 0) {
 		/* show inbox */
+		ctx->inbox_listed = TRUE;
 		ctx->inbox_found = TRUE;
 		return fs_list_inbox(ctx);
 	}
+	if (!ctx->inbox_listed && ctx->inbox_found) {
+		ctx->inbox_listed = TRUE;
+		ctx->info.flags = ctx->inbox_flags;
+		ctx->info.name = "INBOX";
+		return &ctx->info;
+	}
 
 	/* finished */
 	return NULL;

Index: mailbox-list-fs.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/lib-storage/list/mailbox-list-fs.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- mailbox-list-fs.c	1 Mar 2007 12:30:13 -0000	1.4
+++ mailbox-list-fs.c	29 Mar 2007 07:59:24 -0000	1.5
@@ -1,13 +1,18 @@
-/* Copyright (C) 2006 Timo Sirainen */
+/* Copyright (C) 2006-2007 Timo Sirainen */
 
 #include "lib.h"
 #include "hostpid.h"
 #include "home-expand.h"
+#include "mkdir-parents.h"
 #include "subscription-file.h"
 #include "mailbox-list-fs.h"
 
+#include <stdio.h>
+#include <unistd.h>
 #include <sys/stat.h>
 
+#define CREATE_MODE 0770 /* umask() should limit it more */
+
 extern struct mailbox_list fs_mailbox_list;
 
 static struct mailbox_list *fs_list_alloc(void)
@@ -28,8 +33,7 @@
 
 static void fs_list_deinit(struct mailbox_list *_list)
 {
-	struct fs_mailbox_list *list =
-		(struct fs_mailbox_list *)_list;
+	struct fs_mailbox_list *list = (struct fs_mailbox_list *)_list;
 
 	pool_unref(list->list.pool);
 }
@@ -136,8 +140,7 @@
 fs_list_get_path(struct mailbox_list *_list, const char *name,
 		 enum mailbox_list_path_type type)
 {
-	struct fs_mailbox_list *list =
-		(struct fs_mailbox_list *)_list;
+	struct fs_mailbox_list *list = (struct fs_mailbox_list *)_list;
 	const struct mailbox_list_settings *set = &_list->set;
 
 	mailbox_list_clear_error(&list->list);
@@ -158,7 +161,7 @@
 		i_unreached();
 	}
 
-	i_assert(mailbox_list_is_valid_existing_name(_list, name));
+	i_assert(mailbox_list_is_valid_mask(_list, name));
 
 	if ((list->list.flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0 &&
 	    (*name == '/' || *name == '~')) {
@@ -188,10 +191,8 @@
 		break;
 	}
 
-	if (strcmp(name, "INBOX") == 0) {
-		return set->inbox_path != NULL ?
-			set->inbox_path : set->root_dir;
-	}
+	if (strcmp(name, "INBOX") == 0 && set->inbox_path != NULL)
+		return set->inbox_path;
 
 	if (*set->maildir_name == '\0')
 		return t_strdup_printf("%s/%s", set->root_dir, name);
@@ -205,8 +206,7 @@
 fs_list_get_mailbox_name_status(struct mailbox_list *_list, const char *name,
 				enum mailbox_name_status *status)
 {
-	struct fs_mailbox_list *list =
-		(struct fs_mailbox_list *)_list;
+	struct fs_mailbox_list *list = (struct fs_mailbox_list *)_list;
 	struct stat st;
 	const char *path;
 
@@ -245,8 +245,7 @@
 static const char *
 fs_list_get_temp_prefix(struct mailbox_list *_list)
 {
-	struct fs_mailbox_list *list =
-		(struct fs_mailbox_list *)_list;
+	struct fs_mailbox_list *list = (struct fs_mailbox_list *)_list;
 
 	return list->temp_prefix;
 }
@@ -267,8 +266,7 @@
 static int fs_list_set_subscribed(struct mailbox_list *_list,
 				  const char *name, bool set)
 {
-	struct fs_mailbox_list *list =
-		(struct fs_mailbox_list *)_list;
+	struct fs_mailbox_list *list = (struct fs_mailbox_list *)_list;
 	const char *path;
 
 	mailbox_list_clear_error(&list->list);
@@ -280,6 +278,103 @@
 				       name, set);
 }
 
+static int fs_list_delete_mailbox(struct mailbox_list *list, const char *name)
+{
+	/* let the backend handle the rest */
+	return mailbox_list_delete_index_control(list, name);
+}
+
+static bool fs_handle_errors(struct mailbox_list *list)
+{
+	if (ENOACCESS(errno))
+		mailbox_list_set_error(list, MAILBOX_LIST_ERR_NO_PERMISSION);
+	else if (ENOSPACE(errno))
+		mailbox_list_set_error(list, "Not enough disk space");
+	else if (ENOTFOUND(errno))
+		mailbox_list_set_error(list, "Directory structure is broken");
+	else
+		return FALSE;
+	return TRUE;
+}
+
+static int fs_list_rename_mailbox(struct mailbox_list *list,
+				  const char *oldname, const char *newname)
+{
+	const char *oldpath, *newpath, *old_indexdir, *new_indexdir, *p;
+	struct stat st;
+
+	if (!mailbox_list_is_valid_existing_name(list, oldname) ||
+	    !mailbox_list_is_valid_create_name(list, newname)) {
+		mailbox_list_set_error(list, "Invalid mailbox name");
+		return -1;
+	}
+
+	oldpath = mailbox_list_get_path(list, oldname,
+					MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	newpath = mailbox_list_get_path(list, newname,
+					MAILBOX_LIST_PATH_TYPE_MAILBOX);
+
+	/* create the hierarchy */
+	p = strrchr(newpath, '/');
+	if (p != NULL) {
+		p = t_strdup_until(newpath, p);
+		if (mkdir_parents(p, CREATE_MODE) < 0) {
+			if (fs_handle_errors(list))
+				return -1;
+
+			mailbox_list_set_critical(list,
+				"mkdir_parents(%s) failed: %m", p);
+			return -1;
+		}
+	}
+
+	/* first check that the destination mailbox doesn't exist.
+	   this is racy, but we need to be atomic and there's hardly any
+	   possibility that someone actually tries to rename two mailboxes
+	   to same new one */
+	if (lstat(newpath, &st) == 0) {
+		mailbox_list_set_error(list, "Target mailbox already exists");
+		return -1;
+	} else if (errno == ENOTDIR) {
+		mailbox_list_set_error(list,
+			"Target mailbox doesn't allow inferior mailboxes");
+		return -1;
+	} else if (errno != ENOENT && errno != EACCES) {
+		mailbox_list_set_critical(list, "lstat(%s) failed: %m",
+					  newpath);
+		return -1;
+	}
+
+	/* NOTE: renaming INBOX works just fine with us, it's simply recreated
+	   the next time it's needed. */
+	if (rename(oldpath, newpath) < 0) {
+		if (ENOTFOUND(errno)) {
+			mailbox_list_set_error(list, t_strdup_printf(
+				MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, oldname));
+		} else if (!fs_handle_errors(list)) {
+			mailbox_list_set_critical(list,
+				"rename(%s, %s) failed: %m", oldpath, newpath);
+		}
+		return -1;
+	}
+
+	/* we need to rename the index directory as well */
+	old_indexdir = mailbox_list_get_path(list, oldname,
+					     MAILBOX_LIST_PATH_TYPE_INDEX);
+	new_indexdir = mailbox_list_get_path(list, newname,
+					     MAILBOX_LIST_PATH_TYPE_INDEX);
+	if (*old_indexdir != '\0') {
+		if (rename(old_indexdir, new_indexdir) < 0 &&
+		    errno != ENOENT) {
+			mailbox_list_set_critical(list,
+						  "rename(%s, %s) failed: %m",
+						  old_indexdir, new_indexdir);
+		}
+	}
+
+	return 0;
+}
+
 struct mailbox_list fs_mailbox_list = {
 	MEMBER(name) "fs",
 	MEMBER(hierarchy_sep) '/',
@@ -298,6 +393,9 @@
 		fs_list_iter_init,
 		fs_list_iter_next,
 		fs_list_iter_deinit,
-		fs_list_set_subscribed
+		NULL,
+		fs_list_set_subscribed,
+		fs_list_delete_mailbox,
+		fs_list_rename_mailbox
 	}
 };

Index: mailbox-list-maildir-iter.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/lib-storage/list/mailbox-list-maildir-iter.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- mailbox-list-maildir-iter.c	25 Nov 2006 22:17:50 -0000	1.2
+++ mailbox-list-maildir-iter.c	29 Mar 2007 07:59:24 -0000	1.3
@@ -98,10 +98,9 @@
 
 		/* 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);
+		ret = ctx->ctx.list->v.
+			iter_is_mailbox(&ctx->ctx, ctx->dir, fname,
+					mailbox_list_get_file_type(d), &flags);
 		if (ret < 0) {
 			t_pop();
 			return -1;
@@ -149,6 +148,11 @@
 				node->flags |= MAILBOX_FLAG_MATCHED;
 			}
 		}
+		if (node != NULL) {
+			node->flags |= flags & ~(MAILBOX_NOINFERIORS |
+						 MAILBOX_CHILDREN |
+						 MAILBOX_NOCHILDREN);
+		}
 	}
 	t_pop();
 

Index: mailbox-list-maildir.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/lib-storage/list/mailbox-list-maildir.c,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -d -r1.5 -r1.6
--- mailbox-list-maildir.c	18 Jan 2007 14:34:45 -0000	1.5
+++ mailbox-list-maildir.c	29 Mar 2007 07:59:25 -0000	1.6
@@ -1,11 +1,13 @@
-/* Copyright (C) 2006 Timo Sirainen */
+/* Copyright (C) 2006-2007 Timo Sirainen */
 
 #include "lib.h"
+#include "array.h"
 #include "hostpid.h"
 #include "home-expand.h"
 #include "subscription-file.h"
 #include "mailbox-list-maildir.h"
 
+#include <stdio.h>
 #include <sys/stat.h>
 
 extern struct mailbox_list maildir_mailbox_list;
@@ -270,6 +272,164 @@
 				       name, set);
 }
 
+static int rename_dir(struct mailbox_list *list,
+		      enum mailbox_list_path_type type,
+		      const char *oldname, const char *newname)
+{
+	const char *oldpath, *newpath;
+
+	oldpath = mailbox_list_get_path(list, oldname, type);
+	newpath = mailbox_list_get_path(list, newname, type);
+
+	if (strcmp(oldpath, newpath) == 0)
+		return 0;
+
+	if (rename(oldpath, newpath) < 0 && errno != ENOENT) {
+		mailbox_list_set_critical(list, "rename(%s, %s) failed: %m",
+					  oldpath, newpath);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int rename_children(struct mailbox_list *list,
+			   const char *oldname, const char *newname)
+{
+	struct mailbox_list_iterate_context *iter;
+        struct mailbox_info *info;
+	ARRAY_DEFINE(names_arr, const char *);
+	const char *mask, *oldpath, *newpath, *old_listname, *new_listname;
+	const char *const *names;
+	unsigned int i, count;
+	size_t oldnamelen;
+	pool_t pool;
+	int ret;
+
+	ret = 0;
+	oldnamelen = strlen(oldname);
+
+	/* first get the list of the children and save them to memory, because
+	   we can't rely on readdir() not skipping files while the directory
+	   is being modified. this doesn't protect against modifications by
+	   other processes though. */
+	pool = pool_alloconly_create("Maildir++ children list", 1024);
+	i_array_init(&names_arr, 64);
+
+	mask = t_strdup_printf("%s%c*", oldname,
+			       mailbox_list_get_hierarchy_sep(list));
+	iter = mailbox_list_iter_init(list, mask, MAILBOX_LIST_ITER_FAST_FLAGS);
+	while ((info = mailbox_list_iter_next(iter)) != NULL) {
+		const char *name;
+
+		i_assert(oldnamelen <= strlen(info->name));
+
+		name = p_strdup(pool, info->name + oldnamelen);
+		array_append(&names_arr, &name, 1);
+	}
+	if (mailbox_list_iter_deinit(&iter) < 0) {
+		ret = -1;
+		names = NULL; count = 0;
+	} else {
+		names = array_get(&names_arr, &count);
+	}
+
+	for (i = 0; i < count; i++) {
+		t_push();
+
+		old_listname = t_strconcat(oldname, names[i], NULL);
+		new_listname = t_strconcat(newname, names[i], NULL);
+		oldpath = mailbox_list_get_path(list, old_listname,
+						MAILBOX_LIST_PATH_TYPE_MAILBOX);
+		newpath = mailbox_list_get_path(list, new_listname,
+						MAILBOX_LIST_PATH_TYPE_MAILBOX);
+
+		/* FIXME: it's possible to merge two mailboxes if either one of
+		   them doesn't have existing root mailbox. We could check this
+		   but I'm not sure if it's worth it. It could be even
+		   considered as a feature.
+
+		   Anyway, the bug with merging is that if both mailboxes have
+		   identically named child mailbox they conflict. Just ignore
+		   those and leave them under the old mailbox. */
+		if (rename(oldpath, newpath) == 0 || EDESTDIREXISTS(errno))
+			ret = 1;
+		else {
+			mailbox_list_set_critical(list,
+				"rename(%s, %s) failed: %m", oldpath, newpath);
+			ret = -1;
+			t_pop();
+			break;
+		}
+
+		(void)rename_dir(list, MAILBOX_LIST_PATH_TYPE_CONTROL,
+				 old_listname, new_listname);
+		(void)rename_dir(list, MAILBOX_LIST_PATH_TYPE_INDEX,
+				 old_listname, new_listname);
+		t_pop();
+	}
+	array_free(&names_arr);
+	pool_unref(pool);
+
+	return ret;
+}
+
+static int
+maildir_list_delete_mailbox(struct mailbox_list *list, const char *name)
+{
+	/* let the backend handle the rest */
+	return mailbox_list_delete_index_control(list, name);
+}
+
+static int maildir_list_rename_mailbox(struct mailbox_list *list,
+				       const char *oldname, const char *newname)
+{
+	const char *oldpath, *newpath;
+	int ret;
+        bool found;
+
+	if (!mailbox_list_is_valid_existing_name(list, oldname) ||
+	    !mailbox_list_is_valid_create_name(list, newname)) {
+		mailbox_list_set_error(list, "Invalid mailbox name");
+		return -1;
+	}
+
+	/* NOTE: it's possible to rename a nonexisting mailbox which has
+	   children. In that case we should ignore the rename() error. */
+	oldpath = mailbox_list_get_path(list, oldname,
+					MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	newpath = mailbox_list_get_path(list, newname,
+					MAILBOX_LIST_PATH_TYPE_MAILBOX);
+
+	ret = rename(oldpath, newpath);
+	if (ret == 0 || errno == ENOENT) {
+		(void)rename_dir(list, MAILBOX_LIST_PATH_TYPE_CONTROL,
+				 oldname, newname);
+		(void)rename_dir(list, MAILBOX_LIST_PATH_TYPE_INDEX,
+				 oldname, newname);
+
+		found = ret == 0;
+		ret = rename_children(list, oldname, newname);
+		if (ret < 0)
+			return -1;
+		if (!found && ret == 0) {
+			mailbox_list_set_error(list, t_strdup_printf(
+				MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, oldname));
+			return -1;
+		}
+
+		return 0;
+	}
+
+	if (EDESTDIREXISTS(errno)) {
+		mailbox_list_set_error(list, "Target mailbox already exists");
+	} else {
+		mailbox_list_set_critical(list, "rename(%s, %s) failed: %m",
+					  oldpath, newpath);
+	}
+	return -1;
+}
+
 struct mailbox_list maildir_mailbox_list = {
 	MEMBER(name) "maildir++",
 	MEMBER(hierarchy_sep) '.',
@@ -288,6 +448,9 @@
 		maildir_list_iter_init,
 		maildir_list_iter_next,
 		maildir_list_iter_deinit,
-		maildir_list_set_subscribed
+		NULL,
+		maildir_list_set_subscribed,
+		maildir_list_delete_mailbox,
+		maildir_list_rename_mailbox
 	}
 };



More information about the dovecot-cvs mailing list