changeset 12575:c39557b7662d

Merged fixes from v2.0 tree.
author Timo Sirainen <tss@iki.fi>
date Sat, 01 Jan 2011 15:59:02 +0200
parents 917f3699af5b (diff) c1e71f579adc (current diff)
children 1e425fe18dea
files TODO src/imap/imap-search.c src/lib-storage/index/maildir/maildir-sync.c src/lib-storage/mail-storage.h src/plugins/acl/acl-mailbox-list.c
diffstat 126 files changed, 2277 insertions(+), 2438 deletions(-) [+]
line wrap: on
line diff
--- a/TODO	Sat Jan 01 14:34:14 2011 +0200
+++ b/TODO	Sat Jan 01 15:59:02 2011 +0200
@@ -1,3 +1,23 @@
+ - notify_sync() could have "what changed" struct with old/new flags
+ - mailbox deletion could really be "rmdir", which fails if everything isn't
+   already expunged. IMAP DELETE could fail with "mailbox changed during
+   deletion, aborting" if new messages arrive
+     - if doing any changes, figure out how to handle symlinked mailboxes
+ - maildir: copy dovecot-shared file from parent mailbox, not root.
+ - is mail_index_sync_have_any() really necessary? especially that it returns
+   TRUE for ext rec changes ... why?...
+ - mailbox listing to use UTF8 everywhere
+    - move namespace sep vs. real_sep check from imap client_find_namespace()
+      to lib-storage mailbox name validity check
+ - rewrite autocreate plugin by making it list virtual mailbox names and only
+   lazily autocreate create them when mailbox is opened
+ - login-common API change to fix compiling with
+   LDFLAGS="-Wl,--as-needed -Wl,--no-undefined"
+ - if global ACL path points to a file instead of a directory, read all of
+   them from there. support wildcards in names.
+ - remove 3 unnecessary stat() calls when opening mailbox. they all come from
+   mailbox_list_get_permissions_full()
+
  - master passdb preserves userdb_* extra fields. should it preserve
    non-userdb_* extra fields too?
  - imap, pop3: if client init fails, wait a second or two before disconnecting
--- a/src/doveadm/doveadm-mail-fetch.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/doveadm/doveadm-mail-fetch.c	Sat Jan 01 15:59:02 2011 +0200
@@ -63,11 +63,12 @@
 
 static int fetch_mailbox_guid(struct fetch_cmd_context *ctx)
 {
-	uint8_t guid[MAIL_GUID_128_SIZE];
+	struct mailbox_metadata metadata;
 
-	if (mailbox_get_guid(ctx->mail->box, guid) < 0)
+	if (mailbox_get_metadata(ctx->mail->box, MAILBOX_METADATA_GUID,
+				 &metadata) < 0)
 		return -1;
-	doveadm_print(mail_guid_128_to_string(guid));
+	doveadm_print(mail_guid_128_to_string(metadata.guid));
 	return 0;
 }
 
--- a/src/doveadm/doveadm-mail-mailbox-status.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/doveadm/doveadm-mail-mailbox-status.c	Sat Jan 01 15:59:02 2011 +0200
@@ -12,18 +12,24 @@
 #define ALL_STATUS_ITEMS \
 	(STATUS_MESSAGES | STATUS_RECENT | \
 	 STATUS_UIDNEXT | STATUS_UIDVALIDITY | \
-	 STATUS_UNSEEN | STATUS_HIGHESTMODSEQ | STATUS_VIRTUAL_SIZE)
+	 STATUS_UNSEEN | STATUS_HIGHESTMODSEQ)
+#define ALL_METADATA_ITEMS \
+	(MAILBOX_METADATA_VIRTUAL_SIZE | MAILBOX_METADATA_GUID)
 
 #define TOTAL_STATUS_ITEMS \
-	(STATUS_MESSAGES | STATUS_RECENT | STATUS_UNSEEN | STATUS_VIRTUAL_SIZE)
+	(STATUS_MESSAGES | STATUS_RECENT | STATUS_UNSEEN)
+#define TOTAL_METADATA_ITEMS \
+	(MAILBOX_METADATA_VIRTUAL_SIZE)
 
 struct status_cmd_context {
 	struct doveadm_mail_cmd_context ctx;
 	struct mail_search_args *search_args;
-	enum mailbox_status_items items;
+
+	enum mailbox_status_items status_items;
+	enum mailbox_metadata_items metadata_items;
 	struct mailbox_status total_status;
+	struct mailbox_metadata total_metadata;
 
-	unsigned int guid:1;
 	unsigned int total_sum:1;
 };
 
@@ -37,40 +43,42 @@
 		const char *field = *fields;
 
 		if (strcmp(field, "all") == 0) {
-			if (ctx->total_sum)
-				ctx->items |= TOTAL_STATUS_ITEMS;
-			else {
-				ctx->items |= ALL_STATUS_ITEMS;
-				ctx->guid = TRUE;
+			if (ctx->total_sum) {
+				ctx->status_items |= TOTAL_STATUS_ITEMS;
+				ctx->metadata_items |= TOTAL_METADATA_ITEMS;
+			} else {
+				ctx->status_items |= ALL_STATUS_ITEMS;
+				ctx->metadata_items |= ALL_METADATA_ITEMS;
 			}
 		} else if (strcmp(field, "messages") == 0)
-			ctx->items |= STATUS_MESSAGES;
+			ctx->status_items |= STATUS_MESSAGES;
 		else if (strcmp(field, "recent") == 0)
-			ctx->items |= STATUS_RECENT;
+			ctx->status_items |= STATUS_RECENT;
 		else if (strcmp(field, "uidnext") == 0)
-			ctx->items |= STATUS_UIDNEXT;
+			ctx->status_items |= STATUS_UIDNEXT;
 		else if (strcmp(field, "uidvalidity") == 0)
-			ctx->items |= STATUS_UIDVALIDITY;
+			ctx->status_items |= STATUS_UIDVALIDITY;
 		else if (strcmp(field, "unseen") == 0)
-			ctx->items |= STATUS_UNSEEN;
+			ctx->status_items |= STATUS_UNSEEN;
 		else if (strcmp(field, "highestmodseq") == 0)
-			ctx->items |= STATUS_HIGHESTMODSEQ;
+			ctx->status_items |= STATUS_HIGHESTMODSEQ;
 		else if (strcmp(field, "vsize") == 0)
-			ctx->items |= STATUS_VIRTUAL_SIZE;
+			ctx->metadata_items |= MAILBOX_METADATA_VIRTUAL_SIZE;
 		else if (strcmp(field, "guid") == 0)
-			ctx->guid = TRUE;
+			ctx->metadata_items |= MAILBOX_METADATA_GUID;
 		else
 			i_fatal("Unknown status field: %s", field);
 
 		if (ctx->total_sum &&
-		    ((ctx->items & ~TOTAL_STATUS_ITEMS) != 0 || ctx->guid))
+		    ((ctx->status_items & ~TOTAL_STATUS_ITEMS) != 0 ||
+		     (ctx->metadata_items & ~TOTAL_METADATA_ITEMS) != 0))
 			i_fatal("Status field %s can't be used with -t", field);
 	}
 }
 
 static void status_output(struct status_cmd_context *ctx, struct mailbox *box,
 			  const struct mailbox_status *status,
-			  uint8_t mailbox_guid[MAIL_GUID_128_SIZE])
+			  const struct mailbox_metadata *metadata)
 {
 	string_t *name;
 
@@ -83,34 +91,35 @@
 		doveadm_print(str_c(name));
 	}
 
-	if ((ctx->items & STATUS_MESSAGES) != 0)
+	if ((ctx->status_items & STATUS_MESSAGES) != 0)
 		doveadm_print_num(status->messages);
-	if ((ctx->items & STATUS_RECENT) != 0)
+	if ((ctx->status_items & STATUS_RECENT) != 0)
 		doveadm_print_num(status->recent);
-	if ((ctx->items & STATUS_UIDNEXT) != 0)
+	if ((ctx->status_items & STATUS_UIDNEXT) != 0)
 		doveadm_print_num(status->uidnext);
-	if ((ctx->items & STATUS_UIDVALIDITY) != 0)
+	if ((ctx->status_items & STATUS_UIDVALIDITY) != 0)
 		doveadm_print_num(status->uidvalidity);
-	if ((ctx->items & STATUS_UNSEEN) != 0)
+	if ((ctx->status_items & STATUS_UNSEEN) != 0)
 		doveadm_print_num(status->unseen);
-	if ((ctx->items & STATUS_HIGHESTMODSEQ) != 0)
+	if ((ctx->status_items & STATUS_HIGHESTMODSEQ) != 0)
 		doveadm_print_num(status->highest_modseq);
-	if ((ctx->items & STATUS_VIRTUAL_SIZE) != 0)
-		doveadm_print_num(status->virtual_size);
-	if (ctx->guid)
-		doveadm_print(mail_guid_128_to_string(mailbox_guid));
+	if ((ctx->metadata_items & MAILBOX_METADATA_VIRTUAL_SIZE) != 0)
+		doveadm_print_num(metadata->virtual_size);
+	if ((ctx->metadata_items & MAILBOX_METADATA_GUID) != 0)
+		doveadm_print(mail_guid_128_to_string(metadata->guid));
 }
 
 static void
 status_sum(struct status_cmd_context *ctx,
-	   const struct mailbox_status *status)
+	   const struct mailbox_status *status,
+	   const struct mailbox_metadata *metadata)
 {
 	struct mailbox_status *dest = &ctx->total_status;
 
 	dest->messages += status->messages;
 	dest->recent += status->recent;
 	dest->unseen += status->unseen;
-	dest->virtual_size += status->virtual_size;
+	ctx->total_metadata.virtual_size += metadata->virtual_size;
 }
 
 static void
@@ -118,7 +127,7 @@
 {
 	struct mailbox *box;
 	struct mailbox_status status;
-	uint8_t mailbox_guid[MAIL_GUID_128_SIZE];
+	struct mailbox_metadata metadata;
 	string_t *mailbox_name = t_str_new(128);
 
 	if (imap_utf7_to_utf8(info->name, mailbox_name) < 0) {
@@ -126,20 +135,17 @@
 		str_append(mailbox_name, info->name);
 	}
 
-	if (doveadm_mailbox_find_and_sync(ctx->ctx.cur_mail_user,
-					  str_c(mailbox_name), &box) < 0) {
+	box = doveadm_mailbox_find(ctx->ctx.cur_mail_user, str_c(mailbox_name));
+	if (mailbox_get_status(box, ctx->status_items, &status) < 0 ||
+	    mailbox_get_metadata(box, ctx->metadata_items, &metadata) < 0) {
 		ctx->ctx.failed = TRUE;
+		mailbox_free(&box);
 		return;
 	}
-	mailbox_get_status(box, ctx->items, &status);
-	if (ctx->guid) {
-		if (mailbox_get_guid(box, mailbox_guid) < 0)
-			memset(mailbox_guid, 0, sizeof(mailbox_guid));
-	}
 	if (!ctx->total_sum)
-		status_output(ctx, box, &status, mailbox_guid);
+		status_output(ctx, box, &status, &metadata);
 	else
-		status_sum(ctx, &status);
+		status_sum(ctx, &status, &metadata);
 	mailbox_free(&box);
 }
 
@@ -165,8 +171,10 @@
 	}
 	doveadm_mail_list_iter_deinit(&iter);
 
-	if (ctx->total_sum)
-		status_output(ctx, NULL, &ctx->total_status, NULL);
+	if (ctx->total_sum) {
+		status_output(ctx, NULL, &ctx->total_status,
+			      &ctx->total_metadata);
+	}
 }
 
 static void cmd_mailbox_status_init(struct doveadm_mail_cmd_context *_ctx,
@@ -185,21 +193,21 @@
 		doveadm_print_header("mailbox", "mailbox",
 				     DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE);
 	}
-	if ((ctx->items & STATUS_MESSAGES) != 0)
+	if ((ctx->status_items & STATUS_MESSAGES) != 0)
 		doveadm_print_header_simple("messages");
-	if ((ctx->items & STATUS_RECENT) != 0)
+	if ((ctx->status_items & STATUS_RECENT) != 0)
 		doveadm_print_header_simple("recent");
-	if ((ctx->items & STATUS_UIDNEXT) != 0)
+	if ((ctx->status_items & STATUS_UIDNEXT) != 0)
 		doveadm_print_header_simple("uidnext");
-	if ((ctx->items & STATUS_UIDVALIDITY) != 0)
+	if ((ctx->status_items & STATUS_UIDVALIDITY) != 0)
 		doveadm_print_header_simple("uidvalidity");
-	if ((ctx->items & STATUS_UNSEEN) != 0)
+	if ((ctx->status_items & STATUS_UNSEEN) != 0)
 		doveadm_print_header_simple("unseen");
-	if ((ctx->items & STATUS_HIGHESTMODSEQ) != 0)
+	if ((ctx->status_items & STATUS_HIGHESTMODSEQ) != 0)
 		doveadm_print_header_simple("highestmodseq");
-	if ((ctx->items & STATUS_VIRTUAL_SIZE) != 0)
+	if ((ctx->metadata_items & MAILBOX_METADATA_VIRTUAL_SIZE) != 0)
 		doveadm_print_header_simple("vsize");
-	if (ctx->guid)
+	if ((ctx->metadata_items & MAILBOX_METADATA_GUID) != 0)
 		doveadm_print_header_simple("guid");
 }
 
--- a/src/doveadm/doveadm-mail-search.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/doveadm/doveadm-mail-search.c	Sat Jan 01 15:59:02 2011 +0200
@@ -16,7 +16,7 @@
 	struct doveadm_mail_iter *iter;
 	struct mailbox_transaction_context *trans;
 	struct mail *mail;
-	uint8_t guid[MAIL_GUID_128_SIZE];
+	struct mailbox_metadata metadata;
 	const char *guid_str;
 	int ret = 0;
 
@@ -24,10 +24,12 @@
 		return -1;
 
 	mail = mail_alloc(trans, 0, NULL);
-	if (mailbox_get_guid(mail->box, guid) < 0)
+
+	if (mailbox_get_metadata(mail->box, MAILBOX_METADATA_GUID,
+				 &metadata) < 0)
 		ret = -1;
 	else {
-		guid_str = mail_guid_128_to_string(guid);
+		guid_str = mail_guid_128_to_string(metadata.guid);
 		while (doveadm_mail_iter_next(iter, mail)) {
 			doveadm_print(guid_str);
 			T_BEGIN {
--- a/src/doveadm/doveadm-mail.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/doveadm/doveadm-mail.c	Sat Jan 01 15:59:02 2011 +0200
@@ -71,13 +71,11 @@
 	return ctx;
 }
 
-static int mailbox_find_and_open(struct mail_user *user, const char *mailbox,
-				 struct mailbox **box_r)
+struct mailbox *
+doveadm_mailbox_find(struct mail_user *user, const char *mailbox)
 {
 	struct mail_namespace *ns;
-	struct mailbox *box;
 	string_t *str;
-	const char *orig_mailbox = mailbox;
 
 	str = t_str_new(128);
 	if (imap_utf8_to_utf7(mailbox, str) < 0)
@@ -88,10 +86,19 @@
 	if (ns == NULL)
 		i_fatal("Can't find namespace for mailbox %s", mailbox);
 
-	box = mailbox_alloc(ns->list, mailbox, MAILBOX_FLAG_KEEP_RECENT |
-			    MAILBOX_FLAG_IGNORE_ACLS);
+	return mailbox_alloc(ns->list, mailbox, MAILBOX_FLAG_KEEP_RECENT |
+			     MAILBOX_FLAG_IGNORE_ACLS);
+}
+
+static int
+doveadm_mailbox_find_and_open(struct mail_user *user, const char *mailbox,
+			      struct mailbox **box_r)
+{
+	struct mailbox *box;
+
+	box = doveadm_mailbox_find(user, mailbox);
 	if (mailbox_open(box) < 0) {
-		i_error("Opening mailbox %s failed: %s", orig_mailbox,
+		i_error("Opening mailbox %s failed: %s", mailbox,
 			mail_storage_get_last_error(mailbox_get_storage(box),
 						    NULL));
 		mailbox_free(&box);
@@ -104,12 +111,13 @@
 int doveadm_mailbox_find_and_sync(struct mail_user *user, const char *mailbox,
 				  struct mailbox **box_r)
 {
-	if (mailbox_find_and_open(user, mailbox, box_r) < 0)
+	if (doveadm_mailbox_find_and_open(user, mailbox, box_r) < 0)
 		return -1;
 	if (mailbox_sync(*box_r, MAILBOX_SYNC_FLAG_FULL_READ) < 0) {
 		i_error("Syncing mailbox %s failed: %s", mailbox,
 			mail_storage_get_last_error(mailbox_get_storage(*box_r),
 						    NULL));
+		mailbox_free(box_r);
 		return -1;
 	}
 	return 0;
@@ -143,7 +151,7 @@
 	struct mail_storage *storage;
 	struct mailbox *box;
 
-	if (mailbox_find_and_open(user, ctx->mailbox, &box) < 0) {
+	if (doveadm_mailbox_find_and_open(user, ctx->mailbox, &box) < 0) {
 		_ctx->failed = TRUE;
 		return;
 	}
--- a/src/doveadm/doveadm-mail.h	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/doveadm/doveadm-mail.h	Sat Jan 01 15:59:02 2011 +0200
@@ -89,6 +89,8 @@
 			     const char **error_r);
 void doveadm_mail_server_flush(void);
 
+struct mailbox *
+doveadm_mailbox_find(struct mail_user *user, const char *mailbox);
 int doveadm_mailbox_find_and_sync(struct mail_user *user, const char *mailbox,
 				  struct mailbox **box_r);
 struct mail_search_args *
--- a/src/dsync/dsync-worker-local.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/dsync/dsync-worker-local.c	Sat Jan 01 15:59:02 2011 +0200
@@ -510,13 +510,18 @@
 		(struct local_dsync_worker_mailbox_iter *)_iter;
 	struct local_dsync_worker *worker =
 		(struct local_dsync_worker *)_iter->worker;
-	enum mailbox_flags flags =
+	const enum mailbox_flags flags =
 		MAILBOX_FLAG_READONLY | MAILBOX_FLAG_KEEP_RECENT;
+	const enum mailbox_status_items status_items =
+		STATUS_UIDNEXT | STATUS_UIDVALIDITY |
+		STATUS_HIGHESTMODSEQ;
+	const enum mailbox_metadata_items metadata_items =
+		MAILBOX_METADATA_CACHE_FIELDS | MAILBOX_METADATA_GUID;
 	const struct mailbox_info *info;
 	const char *storage_name;
 	struct mailbox *box;
 	struct mailbox_status status;
-	uint8_t mailbox_guid[MAIL_GUID_128_SIZE];
+	struct mailbox_metadata metadata;
 	struct local_dsync_mailbox_change *change;
 	struct local_dsync_dir_change *dir_change, change_lookup;
 	struct local_dsync_mailbox *old_lbox;
@@ -554,8 +559,8 @@
 	}
 
 	box = mailbox_alloc(info->ns->list, storage_name, flags);
-	if (mailbox_sync(box, 0) < 0 ||
-	    mailbox_get_guid(box, mailbox_guid) < 0) {
+	if (mailbox_get_status(box, status_items, &status) < 0 ||
+	    mailbox_get_metadata(box, metadata_items, &metadata) < 0) {
 		struct mail_storage *storage = mailbox_get_storage(box);
 
 		i_error("Failed to sync mailbox %s: %s", info->name,
@@ -565,16 +570,13 @@
 		return -1;
 	}
 
-	mailbox_get_status(box, STATUS_UIDNEXT | STATUS_UIDVALIDITY |
-			   STATUS_HIGHESTMODSEQ | STATUS_CACHE_FIELDS, &status);
-
-	change = hash_table_lookup(worker->mailbox_changes_hash, mailbox_guid);
+	change = hash_table_lookup(worker->mailbox_changes_hash, metadata.guid);
 	if (change != NULL) {
 		/* it shouldn't be marked as deleted, but drop it to be sure */
 		change->deleted_mailbox = FALSE;
 	}
 
-	memcpy(dsync_box_r->mailbox_guid.guid, mailbox_guid,
+	memcpy(dsync_box_r->mailbox_guid.guid, metadata.guid,
 	       sizeof(dsync_box_r->mailbox_guid.guid));
 	dsync_box_r->uid_validity = status.uidvalidity;
 	dsync_box_r->uid_next = status.uidnext;
@@ -582,7 +584,7 @@
 	dsync_box_r->highest_modseq = status.highest_modseq;
 
 	p_clear(iter->ret_pool);
-	fields = array_get(status.cache_fields, &field_count);
+	fields = array_get(metadata.cache_fields, &field_count);
 	p_array_init(&dsync_box_r->cache_fields, iter->ret_pool, field_count);
 	for (i = 0; i < field_count; i++) {
 		const char *field_name = p_strdup(iter->ret_pool, fields[i]);
@@ -787,7 +789,7 @@
 	enum mailbox_flags flags = MAILBOX_FLAG_KEEP_RECENT;
 	struct local_dsync_mailbox *lbox;
 	struct mailbox *box;
-	uint8_t mailbox_guid[MAIL_GUID_128_SIZE];
+	struct mailbox_metadata metadata;
 
 	lbox = hash_table_lookup(worker->mailbox_hash, guid);
 	if (lbox == NULL) {
@@ -798,7 +800,7 @@
 
 	box = mailbox_alloc(lbox->ns->list, lbox->storage_name, flags);
 	if (mailbox_sync(box, 0) < 0 ||
-	    mailbox_get_guid(box, mailbox_guid) < 0) {
+	    mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) < 0) {
 		struct mail_storage *storage = mailbox_get_storage(box);
 
 		i_error("Failed to sync mailbox %s: %s", lbox->storage_name,
@@ -807,10 +809,10 @@
 		return -1;
 	}
 
-	if (memcmp(mailbox_guid, guid->guid, sizeof(guid->guid)) != 0) {
+	if (memcmp(metadata.guid, guid->guid, sizeof(guid->guid)) != 0) {
 		i_error("Mailbox %s changed its GUID (%s -> %s)",
 			lbox->storage_name, dsync_guid_to_str(guid),
-			mail_guid_128_to_string(mailbox_guid));
+			mail_guid_128_to_string(metadata.guid));
 		mailbox_free(&box);
 		return -1;
 	}
@@ -935,7 +937,7 @@
 	array_clear(&iter->expunges);
 	iter->expunges_set = TRUE;
 
-	mailbox_get_status(box, STATUS_UIDNEXT, &status);
+	mailbox_get_open_status(box, STATUS_UIDNEXT, &status);
 	if (prev_uid + 1 >= status.uidnext) {
 		/* no expunged messages at the end of mailbox */
 		return FALSE;
@@ -1523,7 +1525,7 @@
 		keywords = mailbox_keywords_create_valid(worker->mail->box,
 							 msg->keywords);
 		mail_update_keywords(worker->mail, MODIFY_REPLACE, keywords);
-		mailbox_keywords_unref(worker->mail->box, &keywords);
+		mailbox_keywords_unref(&keywords);
 		mail_update_modseq(worker->mail, msg->modseq);
 	}
 }
@@ -1574,7 +1576,7 @@
 		mailbox_keywords_create_valid(box, msg->keywords);
 	mailbox_save_set_flags(save_ctx, msg->flags, keywords);
 	if (keywords != NULL)
-		mailbox_keywords_unref(box, &keywords);
+		mailbox_keywords_unref(&keywords);
 	mailbox_save_set_uid(save_ctx, msg->uid);
 	mailbox_save_set_save_date(save_ctx, msg->save_date);
 	mailbox_save_set_min_modseq(save_ctx, msg->modseq);
--- a/src/imap/cmd-append.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/imap/cmd-append.c	Sat Jan 01 15:59:02 2011 +0200
@@ -351,7 +351,7 @@
 	ret = mailbox_save_begin(&ctx->save_ctx, ctx->input);
 
 	if (keywords != NULL)
-		mailbox_keywords_unref(ctx->box, &keywords);
+		mailbox_keywords_unref(&keywords);
 
 	if (ret < 0) {
 		/* save initialization failed */
@@ -445,47 +445,6 @@
 	return FALSE;
 }
 
-static struct mailbox *
-get_mailbox(struct client_command_context *cmd, const char *name)
-{
-	struct mail_namespace *ns;
-	struct mailbox *box;
-	enum mailbox_name_status status;
-	const char *storage_name;
-
-	ns = client_find_namespace(cmd, name, &storage_name, &status);
-	if (ns == NULL)
-		return NULL;
-
-	switch (status) {
-	case MAILBOX_NAME_EXISTS_MAILBOX:
-		break;
-	case MAILBOX_NAME_EXISTS_DIR:
-		status = MAILBOX_NAME_VALID;
-		/* fall through */
-	case MAILBOX_NAME_VALID:
-	case MAILBOX_NAME_INVALID:
-	case MAILBOX_NAME_NOINFERIORS:
-		client_fail_mailbox_name_status(cmd, name, "TRYCREATE", status);
-		return NULL;
-	}
-
-	if (cmd->client->mailbox != NULL &&
-	    mailbox_equals(cmd->client->mailbox, ns, storage_name))
-		return cmd->client->mailbox;
-
-	box = mailbox_alloc(ns->list, storage_name, MAILBOX_FLAG_SAVEONLY |
-			    MAILBOX_FLAG_KEEP_RECENT);
-	if (mailbox_open(box) < 0) {
-		client_send_storage_error(cmd, mailbox_get_storage(box));
-		mailbox_free(&box);
-		return NULL;
-	}
-	if (cmd->client->enabled_features != 0)
-		mailbox_enable(box, cmd->client->enabled_features);
-	return box;
-}
-
 bool cmd_append(struct client_command_context *cmd)
 {
 	struct client *client = cmd->client;
@@ -509,8 +468,7 @@
 	ctx = p_new(cmd->pool, struct cmd_append_context, 1);
 	ctx->cmd = cmd;
 	ctx->client = client;
-	ctx->box = get_mailbox(cmd, mailbox);
-	if (ctx->box == NULL)
+	if (client_open_save_dest_box(cmd, mailbox, &ctx->box) < 0)
 		ctx->failed = TRUE;
 	else {
 		ctx->storage = mailbox_get_storage(ctx->box);
--- a/src/imap/cmd-copy.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/imap/cmd-copy.c	Sat Jan 01 15:59:02 2011 +0200
@@ -85,13 +85,11 @@
 bool cmd_copy(struct client_command_context *cmd)
 {
 	struct client *client = cmd->client;
-	struct mail_namespace *dest_ns;
 	struct mail_storage *dest_storage;
 	struct mailbox *destbox;
 	struct mailbox_transaction_context *t;
         struct mail_search_args *search_args;
-	const char *messageset, *mailbox, *storage_name, *src_uidset;
-	enum mailbox_name_status status;
+	const char *messageset, *mailbox, *src_uidset;
 	enum mailbox_sync_flags sync_flags = 0;
 	enum imap_sync_flags imap_flags = 0;
 	struct mail_transaction_commit_changes changes;
@@ -110,41 +108,9 @@
 	if (ret <= 0)
 		return ret < 0;
 
-	/* open the destination mailbox */
-	dest_ns = client_find_namespace(cmd, mailbox, &storage_name, &status);
-	if (dest_ns == NULL)
+	if (client_open_save_dest_box(cmd, mailbox, &destbox) < 0)
 		return TRUE;
 
-	switch (status) {
-	case MAILBOX_NAME_EXISTS_MAILBOX:
-		break;
-	case MAILBOX_NAME_EXISTS_DIR:
-		status = MAILBOX_NAME_VALID;
-		/* fall through */
-	case MAILBOX_NAME_VALID:
-	case MAILBOX_NAME_INVALID:
-	case MAILBOX_NAME_NOINFERIORS:
-		client_fail_mailbox_name_status(cmd, mailbox,
-						"TRYCREATE", status);
-		return NULL;
-	}
-
-	if (mailbox_equals(client->mailbox, dest_ns, storage_name))
-		destbox = client->mailbox;
-	else {
-		destbox = mailbox_alloc(dest_ns->list, storage_name,
-					MAILBOX_FLAG_SAVEONLY |
-					MAILBOX_FLAG_KEEP_RECENT);
-		if (mailbox_open(destbox) < 0) {
-			client_send_storage_error(cmd,
-				mailbox_get_storage(destbox));
-			mailbox_free(&destbox);
-			return TRUE;
-		}
-		if (client->enabled_features != 0)
-			mailbox_enable(destbox, client->enabled_features);
-	}
-
 	t = mailbox_transaction_begin(destbox,
 				      MAILBOX_TRANSACTION_FLAG_EXTERNAL |
 				      MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS);
--- a/src/imap/cmd-create.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/imap/cmd-create.c	Sat Jan 01 15:59:02 2011 +0200
@@ -7,7 +7,6 @@
 
 bool cmd_create(struct client_command_context *cmd)
 {
-	enum mailbox_name_status status;
 	struct mail_namespace *ns;
 	const char *mailbox, *storage_name;
 	struct mailbox *box;
@@ -18,7 +17,7 @@
 	if (!client_read_string_args(cmd, 1, &mailbox))
 		return FALSE;
 
-	ns = client_find_namespace(cmd, mailbox, &storage_name, NULL);
+	ns = client_find_namespace(cmd, mailbox, &storage_name);
 	if (ns == NULL)
 		return TRUE;
 
@@ -34,26 +33,18 @@
 		   informing us that it wants to create children under this
 		   mailbox. */
                 directory = TRUE;
-		storage_name = t_strndup(storage_name, strlen(storage_name)-1);
 		mailbox = t_strndup(mailbox, len-1);
+
+		/* drop also from storage_name. it's already dropped when
+		   WORKAROUND_TB_EXTRA_MAILBOX_SEP is enabled */
+		len = strlen(storage_name);
+		if (storage_name[len-1] == ns->real_sep)
+			storage_name = t_strndup(storage_name, len-1);
 	}
 
-	ns = client_find_namespace(cmd, mailbox, &storage_name, &status);
+	ns = client_find_namespace(cmd, mailbox, &storage_name);
 	if (ns == NULL)
 		return TRUE;
-	switch (status) {
-	case MAILBOX_NAME_VALID:
-		break;
-	case MAILBOX_NAME_EXISTS_DIR:
-		if (!directory)
-			break;
-		/* fall through */
-	case MAILBOX_NAME_EXISTS_MAILBOX:
-	case MAILBOX_NAME_INVALID:
-	case MAILBOX_NAME_NOINFERIORS:
-		client_fail_mailbox_name_status(cmd, mailbox, NULL, status);
-		return TRUE;
-	}
 
 	box = mailbox_alloc(ns->list, storage_name, 0);
 	if (mailbox_create(box, NULL, directory) < 0)
--- a/src/imap/cmd-delete.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/imap/cmd-delete.c	Sat Jan 01 15:59:02 2011 +0200
@@ -6,7 +6,6 @@
 bool cmd_delete(struct client_command_context *cmd)
 {
 	struct client *client = cmd->client;
-	enum mailbox_name_status status;
 	struct mail_namespace *ns;
 	struct mailbox *box;
 	const char *name, *storage_name;
@@ -21,19 +20,9 @@
 		return TRUE;
 	}
 
-	ns = client_find_namespace(cmd, name, &storage_name, &status);
+	ns = client_find_namespace(cmd, name, &storage_name);
 	if (ns == NULL)
 		return TRUE;
-	switch (status) {
-	case MAILBOX_NAME_EXISTS_MAILBOX:
-	case MAILBOX_NAME_EXISTS_DIR:
-		break;
-	case MAILBOX_NAME_VALID:
-	case MAILBOX_NAME_INVALID:
-	case MAILBOX_NAME_NOINFERIORS:
-		client_fail_mailbox_name_status(cmd, name, NULL, status);
-		return TRUE;
-	}
 
 	box = mailbox_alloc(ns->list, storage_name, 0);
 	if (client->mailbox != NULL &&
--- a/src/imap/cmd-enable.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/imap/cmd-enable.c	Sat Jan 01 15:59:02 2011 +0200
@@ -21,13 +21,13 @@
 		}
 		str = t_str_ucase(str);
 		if (strcmp(str, "CONDSTORE") == 0) {
-			client_enable(cmd->client, MAILBOX_FEATURE_CONDSTORE);
-			str_append(reply, " CONDSTORE");
-		}
-		else if (strcmp(str, "QRESYNC") == 0) {
-			client_enable(cmd->client, MAILBOX_FEATURE_QRESYNC |
-				      MAILBOX_FEATURE_CONDSTORE);
-			str_append(reply, " QRESYNC");
+			if (client_enable(cmd->client,
+					  MAILBOX_FEATURE_CONDSTORE) == 0)
+				str_append(reply, " CONDSTORE");
+		} else if (strcmp(str, "QRESYNC") == 0) {
+			if (client_enable(cmd->client, MAILBOX_FEATURE_QRESYNC |
+					  MAILBOX_FEATURE_CONDSTORE) == 0)
+				str_append(reply, " QRESYNC");
 		}
 	}
 	if (str_len(reply) > 9)
--- a/src/imap/cmd-rename.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/imap/cmd-rename.c	Sat Jan 01 15:59:02 2011 +0200
@@ -6,7 +6,6 @@
 
 bool cmd_rename(struct client_command_context *cmd)
 {
-	enum mailbox_name_status status;
 	struct mail_namespace *old_ns, *new_ns;
 	struct mailbox *old_box, *new_box;
 	const char *oldname, *newname, *storage_oldname, *storage_newname;
@@ -16,33 +15,12 @@
 	if (!client_read_string_args(cmd, 2, &oldname, &newname))
 		return FALSE;
 
-	old_ns = client_find_namespace(cmd, oldname, &storage_oldname, &status);
+	old_ns = client_find_namespace(cmd, oldname, &storage_oldname);
 	if (old_ns == NULL)
 		return TRUE;
-	switch (status) {
-	case MAILBOX_NAME_EXISTS_MAILBOX:
-	case MAILBOX_NAME_EXISTS_DIR:
-		break;
-	case MAILBOX_NAME_VALID:
-	case MAILBOX_NAME_INVALID:
-	case MAILBOX_NAME_NOINFERIORS:
-		client_fail_mailbox_name_status(cmd, oldname, NULL, status);
-		return TRUE;
-	}
-
-	new_ns = client_find_namespace(cmd, newname, &storage_newname, &status);
+	new_ns = client_find_namespace(cmd, newname, &storage_newname);
 	if (new_ns == NULL)
 		return TRUE;
-	switch (status) {
-	case MAILBOX_NAME_VALID:
-		break;
-	case MAILBOX_NAME_EXISTS_MAILBOX:
-	case MAILBOX_NAME_EXISTS_DIR:
-	case MAILBOX_NAME_INVALID:
-	case MAILBOX_NAME_NOINFERIORS:
-		client_fail_mailbox_name_status(cmd, newname, NULL, status);
-		return TRUE;
-	}
 
 	if (old_ns == new_ns) {
 		/* disallow box -> box/child, because it may break clients and
--- a/src/imap/cmd-select.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/imap/cmd-select.c	Sat Jan 01 15:59:02 2011 +0200
@@ -276,7 +276,7 @@
 	struct client *client = ctx->cmd->client;
 	struct mailbox_status status;
 	enum mailbox_flags flags = 0;
-	int ret;
+	int ret = 0;
 
 	if (readonly)
 		flags |= MAILBOX_FLAG_READONLY | MAILBOX_FLAG_KEEP_RECENT;
@@ -289,16 +289,17 @@
 	}
 
 	if (client->enabled_features != 0)
-		mailbox_enable(ctx->box, client->enabled_features);
-	if (mailbox_sync(ctx->box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) {
+		ret = mailbox_enable(ctx->box, client->enabled_features);
+	if (ret < 0 ||
+	    mailbox_sync(ctx->box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) {
 		client_send_storage_error(ctx->cmd,
 					  mailbox_get_storage(ctx->box));
 		return -1;
 	}
-	mailbox_get_status(ctx->box, STATUS_MESSAGES | STATUS_RECENT |
-			   STATUS_FIRST_UNSEEN_SEQ | STATUS_UIDVALIDITY |
-			   STATUS_UIDNEXT | STATUS_KEYWORDS |
-			   STATUS_HIGHESTMODSEQ, &status);
+	mailbox_get_open_status(ctx->box, STATUS_MESSAGES | STATUS_RECENT |
+				STATUS_FIRST_UNSEEN_SEQ | STATUS_UIDVALIDITY |
+				STATUS_UIDNEXT | STATUS_KEYWORDS |
+				STATUS_HIGHESTMODSEQ, &status);
 
 	client->mailbox = ctx->box;
 	client->select_counter++;
@@ -372,7 +373,6 @@
 	struct client *client = cmd->client;
 	struct imap_select_context *ctx;
 	const struct imap_arg *args, *list_args;
-	enum mailbox_name_status status;
 	const char *mailbox, *storage_name;
 	int ret;
 
@@ -388,24 +388,11 @@
 
 	ctx = p_new(cmd->pool, struct imap_select_context, 1);
 	ctx->cmd = cmd;
-	ctx->ns = client_find_namespace(cmd, mailbox, &storage_name, &status);
+	ctx->ns = client_find_namespace(cmd, mailbox, &storage_name);
 	if (ctx->ns == NULL) {
 		close_selected_mailbox(client);
 		return TRUE;
 	}
-	switch (status) {
-	case MAILBOX_NAME_EXISTS_MAILBOX:
-		break;
-	case MAILBOX_NAME_EXISTS_DIR:
-		status = MAILBOX_NAME_VALID;
-		/* fall through */
-	case MAILBOX_NAME_VALID:
-	case MAILBOX_NAME_INVALID:
-	case MAILBOX_NAME_NOINFERIORS:
-		client_fail_mailbox_name_status(cmd, mailbox, NULL, status);
-		close_selected_mailbox(client);
-		return TRUE;
-	}
 
 	if (imap_arg_get_list(&args[1], &list_args)) {
 		if (!select_parse_options(ctx, list_args)) {
@@ -423,7 +410,7 @@
 	if (ctx->condstore) {
 		/* Enable while no mailbox is opened to avoid sending
 		   HIGHESTMODSEQ for previously opened mailbox */
-		client_enable(client, MAILBOX_FEATURE_CONDSTORE);
+		(void)client_enable(client, MAILBOX_FEATURE_CONDSTORE);
 	}
 
 	ret = select_open(ctx, storage_name, readonly);
--- a/src/imap/cmd-status.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/imap/cmd-status.c	Sat Jan 01 15:59:02 2011 +0200
@@ -9,7 +9,6 @@
 bool cmd_status(struct client_command_context *cmd)
 {
 	struct client *client = cmd->client;
-	enum mailbox_name_status status;
 	const struct imap_arg *args, *list_args;
 	struct imap_status_items items;
 	struct imap_status_result result;
@@ -31,21 +30,9 @@
 	if (imap_status_parse_items(cmd, list_args, &items) < 0)
 		return TRUE;
 
-	ns = client_find_namespace(cmd, mailbox, &storage_name, &status);
+	ns = client_find_namespace(cmd, mailbox, &storage_name);
 	if (ns == NULL)
 		return TRUE;
-	switch (status) {
-	case MAILBOX_NAME_EXISTS_MAILBOX:
-		break;
-	case MAILBOX_NAME_EXISTS_DIR:
-		status = MAILBOX_NAME_VALID;
-		/* fall through */
-	case MAILBOX_NAME_VALID:
-	case MAILBOX_NAME_INVALID:
-	case MAILBOX_NAME_NOINFERIORS:
-		client_fail_mailbox_name_status(cmd, mailbox, NULL, status);
-		return TRUE;
-	}
 
 	selected_mailbox = client->mailbox != NULL &&
 		mailbox_equals(client->mailbox, ns, storage_name);
--- a/src/imap/cmd-store.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/imap/cmd-store.c	Sat Jan 01 15:59:02 2011 +0200
@@ -62,8 +62,8 @@
 							  "Invalid modseq");
 				return FALSE;
 			}
-			client_enable(ctx->cmd->client,
-				      MAILBOX_FEATURE_CONDSTORE);
+			(void)client_enable(ctx->cmd->client,
+					    MAILBOX_FEATURE_CONDSTORE);
 		} else {
 			client_send_command_error(ctx->cmd,
 						  "Unknown STORE modifier");
@@ -201,7 +201,7 @@
 	mail_free(&mail);
 
 	if (ctx.keywords != NULL)
-		mailbox_keywords_unref(client->mailbox, &ctx.keywords);
+		mailbox_keywords_unref(&ctx.keywords);
 
 	ret = mailbox_search_deinit(&search_ctx);
 	if (ret < 0)
--- a/src/imap/cmd-subscribe.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/imap/cmd-subscribe.c	Sat Jan 01 15:59:02 2011 +0200
@@ -26,9 +26,39 @@
 	return FALSE;
 }
 
+static bool
+subscribe_is_valid_name(struct client_command_context *cmd, const char *mailbox)
+{
+	enum mailbox_name_status name_status;
+	struct mail_namespace *ns;
+	const char *storage_name;
+
+	if (have_listable_namespace_prefix(cmd->client->user->namespaces,
+					   mailbox)) {
+		/* subscribing to a listable namespace prefix, allow it. */
+		return TRUE;
+	}
+
+	/* see if the mailbox exists */
+	ns = client_find_namespace(cmd, mailbox, &storage_name);
+	if (ns == NULL)
+		return FALSE;
+
+	if (mailbox_list_get_mailbox_name_status(ns->list, storage_name,
+						 &name_status) < 0) {
+		client_send_list_error(cmd, ns->list);
+		return FALSE;
+	}
+	if (name_status == MAILBOX_NAME_NONEXISTENT) {
+		client_send_tagline(cmd, t_strdup_printf(
+			"NO "MAIL_ERRSTR_MAILBOX_NOT_FOUND, mailbox));
+		return FALSE;
+	}
+	return TRUE;
+}
+
 bool cmd_subscribe_full(struct client_command_context *cmd, bool subscribe)
 {
-	enum mailbox_name_status status;
 	struct mail_namespace *ns, *box_ns;
 	const char *mailbox, *storage_name, *subs_name, *subs_name2 = NULL;
 	bool unsubscribed_mailbox2;
@@ -37,9 +67,13 @@
 	if (!client_read_string_args(cmd, 1, &mailbox))
 		return FALSE;
 
-	box_ns = client_find_namespace(cmd, mailbox, &storage_name, NULL);
+	box_ns = client_find_namespace(cmd, mailbox, &storage_name);
 	if (box_ns == NULL)
 		return TRUE;
+	if (!mailbox_list_is_valid_existing_name(box_ns->list, storage_name)) {
+		client_send_tagline(cmd, "NO [CANNOT] Invalid mailbox name");
+		return TRUE;
+	}
 
 	/* now find a namespace where the subscription can be added to */
 	subs_name = mailbox;
@@ -69,39 +103,9 @@
 		subs_name = t_strndup(subs_name, strlen(subs_name)-1);
 	}
 
-	if (have_listable_namespace_prefix(cmd->client->user->namespaces,
-					   mailbox)) {
-		/* subscribing to a listable namespace prefix, allow it. */
-	} else if (subscribe) {
-		if (client_find_namespace(cmd, mailbox,
-					  &storage_name, &status) == NULL)
-			return TRUE;
-		switch (status) {
-		case MAILBOX_NAME_EXISTS_MAILBOX:
-		case MAILBOX_NAME_EXISTS_DIR:
-			break;
-		case MAILBOX_NAME_VALID:
-		case MAILBOX_NAME_INVALID:
-		case MAILBOX_NAME_NOINFERIORS:
-			client_fail_mailbox_name_status(cmd, mailbox,
-							NULL, status);
+	if (subscribe) {
+		if (!subscribe_is_valid_name(cmd, mailbox))
 			return TRUE;
-		}
-	} else {
-		if (client_find_namespace(cmd, mailbox,
-					  &storage_name, &status) == NULL)
-			return TRUE;
-		switch (status) {
-		case MAILBOX_NAME_EXISTS_MAILBOX:
-		case MAILBOX_NAME_EXISTS_DIR:
-		case MAILBOX_NAME_VALID:
-			break;
-		case MAILBOX_NAME_INVALID:
-		case MAILBOX_NAME_NOINFERIORS:
-			client_fail_mailbox_name_status(cmd, mailbox,
-							NULL, status);
-			return TRUE;
-		}
 	}
 
 	unsubscribed_mailbox2 = FALSE;
--- a/src/imap/imap-client.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/imap/imap-client.c	Sat Jan 01 15:59:02 2011 +0200
@@ -911,27 +911,35 @@
 	return TRUE;
 }
 
-void client_enable(struct client *client, enum mailbox_feature features)
+int client_enable(struct client *client, enum mailbox_feature features)
 {
 	struct mailbox_status status;
+	int ret;
 
 	if ((client->enabled_features & features) == features)
-		return;
+		return 0;
 
 	client->enabled_features |= features;
 	if (client->mailbox == NULL)
-		return;
+		return 0;
 
-	mailbox_enable(client->mailbox, features);
-	if ((features & MAILBOX_FEATURE_CONDSTORE) != 0) {
+	ret = mailbox_enable(client->mailbox, features);
+	if ((features & MAILBOX_FEATURE_CONDSTORE) != 0 && ret == 0) {
 		/* CONDSTORE being enabled while mailbox is selected.
 		   Notify client of the latest HIGHESTMODSEQ. */
-		mailbox_get_status(client->mailbox,
-				   STATUS_HIGHESTMODSEQ, &status);
-		client_send_line(client, t_strdup_printf(
-			"* OK [HIGHESTMODSEQ %llu] Highest",
-			(unsigned long long)status.highest_modseq));
+		ret = mailbox_get_status(client->mailbox,
+					 STATUS_HIGHESTMODSEQ, &status);
+		if (ret == 0) {
+			client_send_line(client, t_strdup_printf(
+				"* OK [HIGHESTMODSEQ %llu] Highest",
+				(unsigned long long)status.highest_modseq));
+		}
 	}
+	if (ret < 0) {
+		client_send_untagged_storage_error(client,
+			mailbox_get_storage(client->mailbox));
+	}
+	return ret;
 }
 
 struct imap_search_update *
--- a/src/imap/imap-client.h	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/imap/imap-client.h	Sat Jan 01 15:59:02 2011 +0200
@@ -192,7 +192,7 @@
    have to wait for an existing SEARCH SAVE to finish. */
 bool client_handle_search_save_ambiguity(struct client_command_context *cmd);
 
-void client_enable(struct client *client, enum mailbox_feature features);
+int client_enable(struct client *client, enum mailbox_feature features);
 
 struct imap_search_update *
 client_search_update_lookup(struct client *client, const char *tag,
--- a/src/imap/imap-commands-util.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/imap/imap-commands-util.c	Sat Jan 01 15:59:02 2011 +0200
@@ -13,16 +13,9 @@
 #include "mail-namespace.h"
 #include "imap-commands-util.h"
 
-/* Maximum length for mailbox name, including it's path. This isn't fully
-   exact since the user can create folder hierarchy with small names, then
-   rename them to larger names. Mail storages should set more strict limits
-   to them, mbox/maildir currently allow paths only up to PATH_MAX. */
-#define MAILBOX_MAX_NAME_LEN 512
-
 struct mail_namespace *
 client_find_namespace(struct client_command_context *cmd, const char *mailbox,
-		      const char **storage_name_r,
-		      enum mailbox_name_status *mailbox_status_r)
+		      const char **storage_name_r)
 {
 	struct mail_namespace *namespaces = cmd->client->user->namespaces;
 	struct mail_namespace *ns;
@@ -39,17 +32,6 @@
 		return NULL;
 	}
 
-	if (mailbox_status_r == NULL) {
-		*storage_name_r = storage_name;
-		return ns;
-	}
-
-	/* make sure it even looks valid */
-	if (*storage_name == '\0' && !(*mailbox != '\0' && ns->list)) {
-		client_send_tagline(cmd, "NO Empty mailbox name.");
-		return NULL;
-	}
-
 	storage_name_len = strlen(storage_name);
 	if ((cmd->client->set->parsed_workarounds &
 	     		WORKAROUND_TB_EXTRA_MAILBOX_SEP) != 0 &&
@@ -74,66 +56,10 @@
 		}
 	}
 
-	/* make sure two hierarchy separators aren't next to each others */
-	for (p = storage_name+1; *p != '\0'; p++) {
-		if (p[0] == ns->real_sep && p[-1] == ns->real_sep) {
-			client_send_tagline(cmd, "NO Invalid mailbox name.");
-			return NULL;
-		}
-	}
-
-	if (storage_name_len > MAILBOX_MAX_NAME_LEN) {
-		client_send_tagline(cmd, "NO Mailbox name too long.");
-		return NULL;
-	}
-
-	/* check what our storage thinks of it */
-	if (mailbox_list_get_mailbox_name_status(ns->list, storage_name,
-						 mailbox_status_r) < 0) {
-		client_send_list_error(cmd, ns->list);
-		return NULL;
-	}
 	*storage_name_r = storage_name;
 	return ns;
 }
 
-void client_fail_mailbox_name_status(struct client_command_context *cmd,
-				     const char *mailbox_name,
-				     const char *resp_code,
-				     enum mailbox_name_status status)
-{
-	switch (status) {
-	case MAILBOX_NAME_EXISTS_MAILBOX:
-	case MAILBOX_NAME_EXISTS_DIR:
-		client_send_tagline(cmd, t_strconcat(
-			"NO [", IMAP_RESP_CODE_ALREADYEXISTS,
-			"] Mailbox already exists: ",
-			str_sanitize(mailbox_name, MAILBOX_MAX_NAME_LEN),
-			NULL));
-		break;
-	case MAILBOX_NAME_VALID:
-		if (resp_code == NULL)
-			resp_code = "";
-		else
-			resp_code = t_strconcat("[", resp_code, "] ", NULL);
-		client_send_tagline(cmd, t_strconcat(
-			"NO ", resp_code, "Mailbox doesn't exist: ",
-			str_sanitize(mailbox_name, MAILBOX_MAX_NAME_LEN),
-			NULL));
-		break;
-	case MAILBOX_NAME_INVALID:
-		client_send_tagline(cmd, t_strconcat(
-			"NO Invalid mailbox name: ",
-			str_sanitize(mailbox_name, MAILBOX_MAX_NAME_LEN),
-			NULL));
-		break;
-	case MAILBOX_NAME_NOINFERIORS:
-		client_send_tagline(cmd,
-			"NO Parent mailbox doesn't allow child mailboxes.");
-		break;
-	}
-}
-
 bool client_verify_open_mailbox(struct client_command_context *cmd)
 {
 	if (cmd->client->mailbox != NULL)
@@ -144,6 +70,49 @@
 	}
 }
 
+int client_open_save_dest_box(struct client_command_context *cmd,
+			      const char *name, struct mailbox **destbox_r)
+{
+	struct mail_namespace *ns;
+	struct mailbox *box;
+	const char *storage_name, *error_string;
+	enum mail_error error;
+
+	ns = client_find_namespace(cmd, name, &storage_name);
+	if (ns == NULL)
+		return -1;
+
+	if (cmd->client->mailbox != NULL &&
+	    mailbox_equals(cmd->client->mailbox, ns, storage_name)) {
+		*destbox_r = cmd->client->mailbox;
+		return 0;
+	}
+	box = mailbox_alloc(ns->list, storage_name,
+			    MAILBOX_FLAG_SAVEONLY | MAILBOX_FLAG_KEEP_RECENT);
+	if (mailbox_open(box) < 0) {
+		struct mail_storage *storage = mailbox_get_storage(box);
+
+		error_string = mail_storage_get_last_error(storage, &error);
+		if (error == MAIL_ERROR_NOTFOUND) {
+			client_send_tagline(cmd,  t_strdup_printf(
+				"NO [TRYCREATE] %s", error_string));
+		} else {
+			client_send_storage_error(cmd, storage);
+		}
+		mailbox_free(&box);
+		return -1;
+	}
+	if (cmd->client->enabled_features != 0) {
+		if (mailbox_enable(box, cmd->client->enabled_features) < 0) {
+			client_send_storage_error(cmd, mailbox_get_storage(box));
+			mailbox_free(&box);
+			return -1;
+		}
+	}
+	*destbox_r = box;
+	return 0;
+}
+
 const char *
 imap_get_error_string(struct client_command_context *cmd,
 		      const char *error_string, enum mail_error error)
--- a/src/imap/imap-commands-util.h	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/imap/imap-commands-util.h	Sat Jan 01 15:59:02 2011 +0200
@@ -13,19 +13,16 @@
    or mailbox name is invalid, sends a tagged NO reply to client. */
 struct mail_namespace *
 client_find_namespace(struct client_command_context *cmd, const char *mailbox,
-		      const char **storage_name_r,
-		      enum mailbox_name_status *mailbox_status_r);
-
-/* Send tagged NO reply based on mailbox name status. */
-void client_fail_mailbox_name_status(struct client_command_context *cmd,
-				     const char *mailbox_name,
-				     const char *resp_code,
-				     enum mailbox_name_status status);
+		      const char **storage_name_r);
 
 /* Returns TRUE if mailbox is selected. If not, sends "No mailbox selected"
    error message to client. */
 bool client_verify_open_mailbox(struct client_command_context *cmd);
 
+/* Open APPEND/COPY destination mailbox. */
+int client_open_save_dest_box(struct client_command_context *cmd,
+			      const char *name, struct mailbox **destbox_r);
+
 const char *
 imap_get_error_string(struct client_command_context *cmd,
 		      const char *error_string, enum mail_error error);
--- a/src/imap/imap-fetch.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/imap/imap-fetch.c	Sat Jan 01 15:59:02 2011 +0200
@@ -159,7 +159,7 @@
 	i_assert(array_count(ctx->qresync_sample_uidset) == count);
 	i_assert(count > 0);
 
-	mailbox_get_status(ctx->box, STATUS_MESSAGES, &status);
+	mailbox_get_open_status(ctx->box, STATUS_MESSAGES, &status);
 
 	/* FIXME: we could do removals from the middle as well */
 	for (i = 0; i < count && seqs[i] <= status.messages; i++) {
@@ -245,7 +245,7 @@
 					  uid_filter[i].seq2);
 	}
 
-	mailbox_get_status(ctx->box, STATUS_UIDNEXT, &status);
+	mailbox_get_open_status(ctx->box, STATUS_UIDNEXT, &status);
 	seq_range_array_remove_range(expunged_uids, status.uidnext,
 				     (uint32_t)-1);
 
@@ -751,7 +751,7 @@
 fetch_modseq_init(struct imap_fetch_context *ctx, const char *name,
 		  const struct imap_arg **args ATTR_UNUSED)
 {
-	client_enable(ctx->client, MAILBOX_FEATURE_CONDSTORE);
+	(void)client_enable(ctx->client, MAILBOX_FEATURE_CONDSTORE);
 	imap_fetch_add_handler(ctx, TRUE, FALSE, name, NULL,
 			       fetch_modseq, NULL);
 	return TRUE;
--- a/src/imap/imap-search.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/imap/imap-search.c	Sat Jan 01 15:59:02 2011 +0200
@@ -528,7 +528,7 @@
 
 	if (ctx->have_modseqs) {
 		ctx->return_options |= SEARCH_RETURN_MODSEQ;
-		client_enable(cmd->client, MAILBOX_FEATURE_CONDSTORE);
+		(void)client_enable(cmd->client, MAILBOX_FEATURE_CONDSTORE);
 	}
 
 	ctx->box = cmd->client->mailbox;
--- a/src/imap/imap-status.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/imap/imap-status.c	Sat Jan 01 15:59:02 2011 +0200
@@ -11,7 +11,8 @@
 			    struct imap_status_items *items_r)
 {
 	const char *item;
-	enum mailbox_status_items items;
+	enum mailbox_status_items status = 0;
+	enum mailbox_metadata_items metadata = 0;
 
 	if (IMAP_ARG_IS_EOL(args)) {
 		client_send_command_error(cmd, "Empty status list.");
@@ -19,7 +20,6 @@
 	}
 
 	memset(items_r, 0, sizeof(*items_r));
-	items = 0;
 	for (; !IMAP_ARG_IS_EOL(args); args++) {
 		if (!imap_arg_get_atom(args, &item)) {
 			/* list may contain only atoms */
@@ -30,21 +30,21 @@
 
 		item = t_str_ucase(item);
 		if (strcmp(item, "MESSAGES") == 0)
-			items |= STATUS_MESSAGES;
+			status |= STATUS_MESSAGES;
 		else if (strcmp(item, "RECENT") == 0)
-			items |= STATUS_RECENT;
+			status |= STATUS_RECENT;
 		else if (strcmp(item, "UIDNEXT") == 0)
-			items |= STATUS_UIDNEXT;
+			status |= STATUS_UIDNEXT;
 		else if (strcmp(item, "UIDVALIDITY") == 0)
-			items |= STATUS_UIDVALIDITY;
+			status |= STATUS_UIDVALIDITY;
 		else if (strcmp(item, "UNSEEN") == 0)
-			items |= STATUS_UNSEEN;
+			status |= STATUS_UNSEEN;
 		else if (strcmp(item, "HIGHESTMODSEQ") == 0)
-			items |= STATUS_HIGHESTMODSEQ;
+			status |= STATUS_HIGHESTMODSEQ;
 		else if (strcmp(item, "X-SIZE") == 0)
-			items |= STATUS_VIRTUAL_SIZE;
+			metadata |= MAILBOX_METADATA_VIRTUAL_SIZE;
 		else if (strcmp(item, "X-GUID") == 0)
-			items_r->guid = TRUE;
+			metadata |= MAILBOX_METADATA_GUID;
 		else {
 			client_send_tagline(cmd, t_strconcat(
 				"BAD Invalid status item ", item, NULL));
@@ -52,7 +52,8 @@
 		}
 	}
 
-	items_r->mailbox_items = items;
+	items_r->status = status;
+	items_r->metadata = metadata;
 	return 0;
 }
 
@@ -75,19 +76,19 @@
 		box = mailbox_alloc(ns->list, mailbox,
 				    MAILBOX_FLAG_READONLY |
 				    MAILBOX_FLAG_KEEP_RECENT);
-		if (client->enabled_features != 0)
-			mailbox_enable(box, client->enabled_features);
+		if (client->enabled_features != 0) {
+			if (mailbox_enable(box, client->enabled_features) < 0)
+				ret = -1;
+		}
 	}
 
-	if ((items->mailbox_items & STATUS_HIGHESTMODSEQ) != 0)
-		client_enable(client, MAILBOX_FEATURE_CONDSTORE);
+	if ((items->status & STATUS_HIGHESTMODSEQ) != 0)
+		(void)client_enable(client, MAILBOX_FEATURE_CONDSTORE);
 
-	ret = box == client->mailbox ? 0 : mailbox_sync(box, 0);
-	if (ret == 0) {
-		mailbox_get_status(box, items->mailbox_items,
-				   &result_r->status);
-		if (items->guid)
-			ret = mailbox_get_guid(box, result_r->mailbox_guid);
+	ret = mailbox_get_status(box, items->status, &result_r->status);
+	if (items->metadata != 0 && ret == 0) {
+		ret = mailbox_get_metadata(box, items->metadata,
+					   &result_r->metadata);
 	}
 
 	if (ret < 0) {
@@ -115,27 +116,27 @@
 	str_append(str, " (");
 
 	prefix_len = str_len(str);
-	if ((items->mailbox_items & STATUS_MESSAGES) != 0)
+	if ((items->status & STATUS_MESSAGES) != 0)
 		str_printfa(str, "MESSAGES %u ", status->messages);
-	if ((items->mailbox_items & STATUS_RECENT) != 0)
+	if ((items->status & STATUS_RECENT) != 0)
 		str_printfa(str, "RECENT %u ", status->recent);
-	if ((items->mailbox_items & STATUS_UIDNEXT) != 0)
+	if ((items->status & STATUS_UIDNEXT) != 0)
 		str_printfa(str, "UIDNEXT %u ", status->uidnext);
-	if ((items->mailbox_items & STATUS_UIDVALIDITY) != 0)
+	if ((items->status & STATUS_UIDVALIDITY) != 0)
 		str_printfa(str, "UIDVALIDITY %u ", status->uidvalidity);
-	if ((items->mailbox_items & STATUS_UNSEEN) != 0)
+	if ((items->status & STATUS_UNSEEN) != 0)
 		str_printfa(str, "UNSEEN %u ", status->unseen);
-	if ((items->mailbox_items & STATUS_HIGHESTMODSEQ) != 0) {
+	if ((items->status & STATUS_HIGHESTMODSEQ) != 0) {
 		str_printfa(str, "HIGHESTMODSEQ %llu ",
 			    (unsigned long long)status->highest_modseq);
 	}
-	if ((items->mailbox_items & STATUS_VIRTUAL_SIZE) != 0) {
+	if ((items->metadata & MAILBOX_METADATA_VIRTUAL_SIZE) != 0) {
 		str_printfa(str, "X-SIZE %llu ",
-			    (unsigned long long)status->virtual_size);
+			    (unsigned long long)result->metadata.virtual_size);
 	}
-	if (items->guid) {
+	if ((items->metadata & MAILBOX_METADATA_GUID) != 0) {
 		str_printfa(str, "X-GUID %s ",
-			    mail_guid_128_to_string(result->mailbox_guid));
+			    mail_guid_128_to_string(result->metadata.guid));
 	}
 
 	if (str_len(str) != prefix_len)
--- a/src/imap/imap-status.h	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/imap/imap-status.h	Sat Jan 01 15:59:02 2011 +0200
@@ -2,14 +2,13 @@
 #define IMAP_STATUS_H
 
 struct imap_status_items {
-	enum mailbox_status_items mailbox_items;
-
-	unsigned int guid:1;
+	enum mailbox_status_items status;
+	enum mailbox_metadata_items metadata;
 };
 
 struct imap_status_result {
 	struct mailbox_status status;
-	uint8_t mailbox_guid[MAIL_GUID_128_SIZE];
+	struct mailbox_metadata metadata;
 };
 
 int imap_status_parse_items(struct client_command_context *cmd,
--- a/src/imap/imap-sync.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/imap/imap-sync.c	Sat Jan 01 15:59:02 2011 +0200
@@ -226,9 +226,9 @@
 		i_free(ctx);
 		return -1;
 	}
-	mailbox_get_status(ctx->box, STATUS_UIDVALIDITY |
-			   STATUS_MESSAGES | STATUS_RECENT |
-			   STATUS_HIGHESTMODSEQ, &status);
+	mailbox_get_open_status(ctx->box, STATUS_UIDVALIDITY |
+				STATUS_MESSAGES | STATUS_RECENT |
+				STATUS_HIGHESTMODSEQ, &status);
 
 	ret = mailbox_transaction_commit(&ctx->t);
 
--- a/src/lib-imap/imap-bodystructure.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-imap/imap-bodystructure.c	Sat Jan 01 15:59:02 2011 +0200
@@ -360,6 +360,7 @@
 static void part_write_body(const struct message_part *part,
 			    string_t *str, bool extended)
 {
+#define NVL(str, nullstr) ((str) != NULL ? (str) : (nullstr))
 	struct message_part_body_data *data = part->context;
 	bool text;
 
--- a/src/lib-imap/imap-envelope.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-imap/imap-envelope.c	Sat Jan 01 15:59:02 2011 +0200
@@ -182,6 +182,7 @@
 void imap_envelope_write_part_data(struct message_part_envelope_data *data,
 				   string_t *str)
 {
+#define NVL(str, nullstr) ((str) != NULL ? (str) : (nullstr))
 	static const char *empty_envelope =
 		"NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL";
 
--- a/src/lib-lda/duplicate.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-lda/duplicate.c	Sat Jan 01 15:59:02 2011 +0200
@@ -232,15 +232,15 @@
 	pool_unref(&file->pool);
 }
 
-int duplicate_check(struct duplicate_context *ctx,
-		    const void *id, size_t id_size, const char *user)
+bool duplicate_check(struct duplicate_context *ctx,
+		     const void *id, size_t id_size, const char *user)
 {
 	struct duplicate d;
 
 	if (ctx->file == NULL) {
 		if (ctx->path == NULL) {
 			/* duplicate database disabled */
-			return 0;
+			return FALSE;
 		}
 		ctx->file = duplicate_file_new(ctx);
 	}
--- a/src/lib-lda/duplicate.h	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-lda/duplicate.h	Sat Jan 01 15:59:02 2011 +0200
@@ -6,8 +6,8 @@
 
 #define DUPLICATE_DEFAULT_KEEP (3600 * 24)
 
-int duplicate_check(struct duplicate_context *ctx,
-		    const void *id, size_t id_size, const char *user);
+bool duplicate_check(struct duplicate_context *ctx,
+		     const void *id, size_t id_size, const char *user);
 void duplicate_mark(struct duplicate_context *ctx,
 		    const void *id, size_t id_size,
                     const char *user, time_t timestamp);
--- a/src/lib-lda/mail-deliver.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-lda/mail-deliver.c	Sat Jan 01 15:59:02 2011 +0200
@@ -253,7 +253,7 @@
 	else
 		mail_deliver_log_cache_var_expand_table(ctx);
 	if (kw != NULL)
-		mailbox_keywords_unref(box, &kw);
+		mailbox_keywords_unref(&kw);
 	mail_free(&ctx->dest_mail);
 
 	if (ret < 0)
--- a/src/lib-storage/Makefile.am	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/Makefile.am	Sat Jan 01 15:59:02 2011 +0200
@@ -5,6 +5,7 @@
 
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/lib \
+	-I$(top_srcdir)/src/lib-test \
 	-I$(top_srcdir)/src/lib-auth \
 	-I$(top_srcdir)/src/lib-dict \
 	-I$(top_srcdir)/src/lib-master \
@@ -33,6 +34,9 @@
 	mail-storage-settings.c \
 	mail-thread.c \
 	mail-user.c \
+	mailbox-get.c \
+	mailbox-header.c \
+	mailbox-keywords.c \
 	mailbox-list.c \
 	mailbox-search-result.c \
 	mailbox-tree.c \
@@ -86,6 +90,25 @@
 test_headers = \
 	test-mail-storage.h
 
+test_programs = \
+	test-mailbox-get
+
+noinst_PROGRAMS = $(test_programs)
+
+test_libs = \
+	$(top_builddir)/src/lib-test/libtest.la \
+	$(top_builddir)/src/lib/liblib.la
+
+test_mailbox_get_SOURCES = test-mailbox-get.c
+test_mailbox_get_LDADD = mailbox-get.lo $(test_libs)
+test_mailbox_get_DEPENDENCIES = mailbox-get.lo $(test_libs)
+
+check: check-am check-test
+check-test: all-am
+	for bin in $(test_programs); do \
+	  if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \
+	done
+
 pkginc_libdir=$(pkgincludedir)
 pkginc_lib_HEADERS = $(headers)
 noinst_HEADERS = $(test_headers)
--- a/src/lib-storage/index/Makefile.am	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/Makefile.am	Sat Jan 01 15:59:02 2011 +0200
@@ -15,7 +15,6 @@
 	istream-attachment.c \
 	istream-mail-stats.c \
 	index-attachment.c \
-	index-fetch.c \
 	index-mail.c \
 	index-mail-headers.c \
 	index-mailbox-check.c \
@@ -49,24 +48,5 @@
 	index-sync-private.h \
 	index-thread-private.h
 
-test_programs = \
-	test-index-fetch
-
-noinst_PROGRAMS = $(test_programs)
-
-test_libs = \
-	$(top_builddir)/src/lib-test/libtest.la \
-	$(top_builddir)/src/lib/liblib.la
-
-test_index_fetch_SOURCES = test-index-fetch.c
-test_index_fetch_LDADD = index-fetch.lo $(test_libs)
-test_index_fetch_DEPENDENCIES = index-fetch.lo $(test_libs)
-
-check: check-am check-test
-check-test: all-am
-	for bin in $(test_programs); do \
-	  if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \
-	done
-
 pkginc_libdir=$(pkgincludedir)
 pkginc_lib_HEADERS = $(headers)
--- a/src/lib-storage/index/cydir/cydir-storage.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/cydir/cydir-storage.c	Sat Jan 01 15:59:02 2011 +0200
@@ -55,10 +55,6 @@
 
 	index_storage_mailbox_alloc(&mbox->box, name, flags,
 				    CYDIR_INDEX_PREFIX);
-	mail_index_set_fsync_mode(mbox->box.index,
-				  storage->set->parsed_fsync_mode,
-				  MAIL_INDEX_SYNC_TYPE_APPEND |
-				  MAIL_INDEX_SYNC_TYPE_EXPUNGE);
 
 	ibox = INDEX_STORAGE_CONTEXT(&mbox->box);
 	ibox->save_commit_pre = cydir_transaction_save_commit_pre;
@@ -71,9 +67,10 @@
 
 static int cydir_mailbox_open(struct mailbox *box)
 {
+	const char *box_path = mailbox_get_path(box);
 	struct stat st;
 
-	if (stat(box->path, &st) == 0) {
+	if (stat(box_path, &st) == 0) {
 		/* exists, open it */
 	} else if (errno == ENOENT) {
 		mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
@@ -81,14 +78,20 @@
 		return -1;
 	} else if (errno == EACCES) {
 		mail_storage_set_critical(box->storage, "%s",
-			mail_error_eacces_msg("stat", box->path));
+			mail_error_eacces_msg("stat", box_path));
 		return -1;
 	} else {
 		mail_storage_set_critical(box->storage, "stat(%s) failed: %m",
-					  box->path);
+					  box_path);
 		return -1;
 	}
-	return index_storage_mailbox_open(box, FALSE);
+	if (index_storage_mailbox_open(box, FALSE) < 0)
+		return -1;
+	mail_index_set_fsync_mode(box->index,
+				  box->storage->set->parsed_fsync_mode,
+				  MAIL_INDEX_SYNC_TYPE_APPEND |
+				  MAIL_INDEX_SYNC_TYPE_EXPUNGE);
+	return 0;
 }
 
 static int
@@ -105,12 +108,10 @@
 
 static void cydir_notify_changes(struct mailbox *box)
 {
-	struct cydir_mailbox *mbox = (struct cydir_mailbox *)box;
-
 	if (box->notify_callback == NULL)
-		index_mailbox_check_remove_all(&mbox->box);
+		index_mailbox_check_remove_all(box);
 	else
-		index_mailbox_check_add(&mbox->box, mbox->box.path);
+		index_mailbox_check_add(box, mailbox_get_path(box));
 }
 
 struct mail_storage cydir_storage = {
@@ -154,21 +155,8 @@
 		index_transaction_begin,
 		index_transaction_commit,
 		index_transaction_rollback,
-		index_transaction_set_max_modseq,
-		index_keywords_create,
-		index_keywords_create_from_indexes,
-		index_keywords_ref,
-		index_keywords_unref,
-		index_keyword_is_valid,
-		index_storage_get_seq_range,
-		index_storage_get_uid_range,
-		index_storage_get_expunges,
-		NULL,
-		NULL,
 		NULL,
 		index_mail_alloc,
-		index_header_lookup_init,
-		index_header_lookup_deinit,
 		index_storage_search_init,
 		index_storage_search_deinit,
 		index_storage_search_next_nonblock,
@@ -179,7 +167,6 @@
 		cydir_save_finish,
 		cydir_save_cancel,
 		mail_storage_copy,
-		NULL,
 		index_storage_is_inconsistent
 	}
 };
--- a/src/lib-storage/index/dbox-common/dbox-mail.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/dbox-common/dbox-mail.c	Sat Jan 01 15:59:02 2011 +0200
@@ -167,8 +167,8 @@
 	string_t *str;
 
 	str = str_new(imail->data_pool, 64);
-	if (mail_cache_lookup_field(imail->trans->cache_view, str,
-				    imail->mail.mail.seq,
+	if (mail_cache_lookup_field(imail->mail.mail.transaction->cache_view,
+				    str, imail->mail.mail.seq,
 				    ibox->cache_fields[cache_field].idx) > 0) {
 		*value_r = str_c(str);
 		return 0;
--- a/src/lib-storage/index/dbox-common/dbox-storage.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/dbox-common/dbox-storage.c	Sat Jan 01 15:59:02 2011 +0200
@@ -113,21 +113,31 @@
 
 int dbox_mailbox_open(struct mailbox *box)
 {
-	if (dbox_cleanup_if_exists(box->list, box->path)) {
-		return index_storage_mailbox_open(box, FALSE);
-	} else if (errno == ENOENT) {
+	const char *box_path = mailbox_get_path(box);
+
+	if (dbox_cleanup_if_exists(box->list, box_path))
+		;
+	else if (errno == ENOENT) {
 		mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
 			T_MAIL_ERR_MAILBOX_NOT_FOUND(box->name));
 		return -1;
 	} else if (errno == EACCES) {
 		mail_storage_set_critical(box->storage, "%s",
-			mail_error_eacces_msg("stat", box->path));
+			mail_error_eacces_msg("stat", box_path));
 		return -1;
 	} else {
 		mail_storage_set_critical(box->storage,
-					  "stat(%s) failed: %m", box->path);
+					  "stat(%s) failed: %m", box_path);
 		return -1;
 	}
+
+	if (index_storage_mailbox_open(box, FALSE) < 0)
+		return -1;
+	mail_index_set_fsync_mode(box->index,
+				  box->storage->set->parsed_fsync_mode,
+				  MAIL_INDEX_SYNC_TYPE_APPEND |
+				  MAIL_INDEX_SYNC_TYPE_EXPUNGE);
+	return 0;
 }
 
 static int dir_is_empty(struct mail_storage *storage, const char *path)
--- a/src/lib-storage/index/dbox-multi/mdbox-mail.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/dbox-multi/mdbox-mail.c	Sat Jan 01 15:59:02 2011 +0200
@@ -29,7 +29,7 @@
 		mail_index_lookup_uid(view, seq, &uid);
 		mail_storage_set_critical(&mbox->storage->storage.storage,
 			"mdbox %s: map uid lost for uid %u",
-			mbox->box.path, uid);
+			mailbox_get_path(&mbox->box), uid);
 		mdbox_storage_set_corrupted(mbox->storage);
 		return -1;
 	}
@@ -48,7 +48,7 @@
 	if (cur_map_uid_validity != mbox->map_uid_validity) {
 		mail_storage_set_critical(&mbox->storage->storage.storage,
 			"mdbox %s: map uidvalidity mismatch (%u vs %u)",
-			mbox->box.path, mbox->map_uid_validity,
+			mailbox_get_path(&mbox->box), mbox->map_uid_validity,
 			cur_map_uid_validity);
 		mdbox_storage_set_corrupted(mbox->storage);
 		return -1;
--- a/src/lib-storage/index/dbox-multi/mdbox-map.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/dbox-multi/mdbox-map.c	Sat Jan 01 15:59:02 2011 +0200
@@ -77,8 +77,9 @@
 	map->ref_ext_id = mail_index_ext_register(map->index, "ref", 0,
 				sizeof(uint16_t), sizeof(uint16_t));
 
-	mailbox_list_get_permissions(root_list, NULL, &map->create_mode,
-				     &map->create_gid, &map->create_gid_origin);
+	mailbox_list_get_root_permissions(root_list, &map->create_mode,
+					  &map->create_gid,
+					  &map->create_gid_origin);
 	mail_index_set_permissions(map->index, map->create_mode,
 				   map->create_gid, map->create_gid_origin);
 	return map;
--- a/src/lib-storage/index/dbox-multi/mdbox-save.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/dbox-multi/mdbox-save.c	Sat Jan 01 15:59:02 2011 +0200
@@ -364,10 +364,11 @@
 	(void)mdbox_map_atomic_finish(&ctx->atomic);
 
 	if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER) {
-		if (fdatasync_path(ctx->mbox->box.path) < 0) {
+		const char *box_path = mailbox_get_path(&ctx->mbox->box);
+
+		if (fdatasync_path(box_path) < 0) {
 			mail_storage_set_critical(storage,
-				"fdatasync_path(%s) failed: %m",
-				ctx->mbox->box.path);
+				"fdatasync_path(%s) failed: %m", box_path);
 		}
 	}
 	mdbox_transaction_save_rollback(_ctx);
--- a/src/lib-storage/index/dbox-multi/mdbox-storage.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/dbox-multi/mdbox-storage.c	Sat Jan 01 15:59:02 2011 +0200
@@ -101,10 +101,6 @@
 	mbox->box.mail_vfuncs = &mdbox_mail_vfuncs;
 
 	index_storage_mailbox_alloc(&mbox->box, name, flags, DBOX_INDEX_PREFIX);
-	mail_index_set_fsync_mode(mbox->box.index,
-				  storage->set->parsed_fsync_mode,
-				  MAIL_INDEX_SYNC_TYPE_APPEND |
-				  MAIL_INDEX_SYNC_TYPE_EXPUNGE);
 
 	ibox = INDEX_STORAGE_CONTEXT(&mbox->box);
 	ibox->save_commit_pre = mdbox_transaction_save_commit_pre;
@@ -114,6 +110,16 @@
 		MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY;
 
 	mbox->storage = (struct mdbox_storage *)storage;
+	return &mbox->box;
+}
+
+static int mdbox_mailbox_open(struct mailbox *box)
+{
+	struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)box;
+
+	if (dbox_mailbox_open(box) < 0)
+		return -1;
+
 	mbox->ext_id =
 		mail_index_ext_register(mbox->box.index, "mdbox", 0,
 					sizeof(struct mdbox_mail_index_record),
@@ -124,7 +130,7 @@
 	mbox->guid_ext_id =
 		mail_index_ext_register(mbox->box.index, "guid",
 					0, MAIL_GUID_128_SIZE, 1);
-	return &mbox->box;
+	return 0;
 }
 
 static void mdbox_mailbox_close(struct mailbox *box)
@@ -149,7 +155,7 @@
 	    (!mbox->creating || data_size != 0)) {
 		mail_storage_set_critical(&mbox->storage->storage.storage,
 			"mdbox %s: Invalid dbox header size: %"PRIuSIZE_T,
-			mbox->box.path, data_size);
+			mailbox_get_path(&mbox->box), data_size);
 		mdbox_storage_set_corrupted(mbox->storage);
 		return -1;
 	}
@@ -294,9 +300,9 @@
 }
 
 static int
-mdbox_mailbox_get_guid(struct mailbox *box, uint8_t guid[MAIL_GUID_128_SIZE])
+mdbox_mailbox_get_guid(struct mdbox_mailbox *mbox,
+		       uint8_t guid[MAIL_GUID_128_SIZE])
 {
-	struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)box;
 	struct mdbox_index_header hdr;
 
 	if (mdbox_read_header(mbox, &hdr) < 0)
@@ -304,7 +310,7 @@
 
 	if (mail_guid_128_is_empty(hdr.mailbox_guid)) {
 		/* regenerate it */
-		if (mdbox_write_index_header(box, NULL, NULL) < 0 ||
+		if (mdbox_write_index_header(&mbox->box, NULL, NULL) < 0 ||
 		    mdbox_read_header(mbox, &hdr) < 0)
 			return -1;
 	}
@@ -313,6 +319,20 @@
 }
 
 static int
+mdbox_mailbox_get_metadata(struct mailbox *box,
+			   enum mailbox_metadata_items items,
+			   struct mailbox_metadata *metadata_r)
+{
+	struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)box;
+
+	if ((items & MAILBOX_METADATA_GUID) != 0) {
+		if (mdbox_mailbox_get_guid(mbox, metadata_r->guid) < 0)
+			return -1;
+	}
+	return index_mailbox_get_metadata(box, items, metadata_r);
+}
+
+static int
 mdbox_mailbox_update(struct mailbox *box, const struct mailbox_update *update)
 {
 	if (!box->opened) {
@@ -389,7 +409,7 @@
 		index_storage_is_readonly,
 		index_storage_allow_new_keywords,
 		index_storage_mailbox_enable,
-		dbox_mailbox_open,
+		mdbox_mailbox_open,
 		mdbox_mailbox_close,
 		index_storage_mailbox_free,
 		dbox_mailbox_create,
@@ -397,7 +417,7 @@
 		mdbox_mailbox_delete,
 		index_storage_mailbox_rename,
 		index_storage_get_status,
-		mdbox_mailbox_get_guid,
+		mdbox_mailbox_get_metadata,
 		NULL,
 		NULL,
 		mdbox_storage_sync_init,
@@ -408,21 +428,8 @@
 		index_transaction_begin,
 		index_transaction_commit,
 		index_transaction_rollback,
-		index_transaction_set_max_modseq,
-		index_keywords_create,
-		index_keywords_create_from_indexes,
-		index_keywords_ref,
-		index_keywords_unref,
-		index_keyword_is_valid,
-		index_storage_get_seq_range,
-		index_storage_get_uid_range,
-		index_storage_get_expunges,
-		NULL,
-		NULL,
 		NULL,
 		dbox_mail_alloc,
-		index_header_lookup_init,
-		index_header_lookup_deinit,
 		index_storage_search_init,
 		index_storage_search_deinit,
 		index_storage_search_next_nonblock,
@@ -433,7 +440,6 @@
 		mdbox_save_finish,
 		mdbox_save_cancel,
 		mdbox_copy,
-		NULL,
 		index_storage_is_inconsistent
 	}
 };
--- a/src/lib-storage/index/dbox-multi/mdbox-sync.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/dbox-multi/mdbox-sync.c	Sat Jan 01 15:59:02 2011 +0200
@@ -264,7 +264,7 @@
 		if (storage_rebuilt) {
 			mail_storage_set_critical(storage,
 				"mdbox %s: Storage keeps breaking",
-				mbox->box.path);
+				mailbox_get_path(&mbox->box));
 			return -1;
 		}
 
--- a/src/lib-storage/index/dbox-single/sdbox-file.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/dbox-single/sdbox-file.c	Sat Jan 01 15:59:02 2011 +0200
@@ -22,7 +22,7 @@
 	i_free(file->file.primary_path);
 	i_free(file->file.alt_path);
 	file->file.primary_path =
-		i_strdup_printf("%s/%s", box->path, fname);
+		i_strdup_printf("%s/%s", mailbox_get_path(box), fname);
 
 	alt_path = mailbox_list_get_path(box->list, box->name,
 					 MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX);
@@ -46,7 +46,7 @@
 		} else {
 			file->file.primary_path =
 				i_strdup_printf("%s/%s",
-						file->mbox->box.path,
+						mailbox_get_path(&mbox->box),
 						dbox_generate_tmp_filename());
 		}
 	} T_END;
@@ -151,7 +151,7 @@
 
 	old_path = file->file.cur_path;
 	new_fname = t_strdup_printf(SDBOX_MAIL_FILE_FORMAT, uid);
-	new_path = t_strdup_printf("%s/%s", file->mbox->box.path,
+	new_path = t_strdup_printf("%s/%s", mailbox_get_path(&file->mbox->box),
 				   new_fname);
 	if (rename(old_path, new_path) < 0) {
 		mail_storage_set_critical(&file->file.storage->storage,
@@ -221,43 +221,44 @@
 {
 	struct sdbox_file *sfile = (struct sdbox_file *)file;
 	struct mailbox *box = &sfile->mbox->box;
+	const struct mailbox_permissions *perm = mailbox_get_permissions(box);
 	const char *p, *dir;
 	mode_t old_mask;
 	int fd;
 
-	old_mask = umask(0666 & ~box->file_create_mode);
+	old_mask = umask(0666 & ~perm->file_create_mode);
 	fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
 	umask(old_mask);
 	if (fd == -1 && errno == ENOENT && parents &&
 	    (p = strrchr(path, '/')) != NULL) {
 		dir = t_strdup_until(path, p);
-		if (mkdir_parents_chgrp(dir, box->dir_create_mode,
-					box->file_create_gid,
-					box->file_create_gid_origin) < 0) {
+		if (mkdir_parents_chgrp(dir, perm->dir_create_mode,
+					perm->file_create_gid,
+					perm->file_create_gid_origin) < 0) {
 			mail_storage_set_critical(box->storage,
 				"mkdir_parents(%s) failed: %m", dir);
 			return -1;
 		}
 		/* try again */
-		old_mask = umask(0666 & ~box->file_create_mode);
+		old_mask = umask(0666 & ~perm->file_create_mode);
 		fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
 		umask(old_mask);
 	}
 	if (fd == -1) {
 		mail_storage_set_critical(box->storage,
 			"open(%s, O_CREAT) failed: %m", path);
-	} else if (box->file_create_gid == (gid_t)-1) {
+	} else if (perm->file_create_gid == (gid_t)-1) {
 		/* no group change */
-	} else if (fchown(fd, (uid_t)-1, box->file_create_gid) < 0) {
+	} else if (fchown(fd, (uid_t)-1, perm->file_create_gid) < 0) {
 		if (errno == EPERM) {
 			mail_storage_set_critical(box->storage, "%s",
 				eperm_error_get_chgrp("fchown", path,
-					box->file_create_gid,
-					box->file_create_gid_origin));
+					perm->file_create_gid,
+					perm->file_create_gid_origin));
 		} else {
 			mail_storage_set_critical(box->storage,
 				"fchown(%s, -1, %ld) failed: %m",
-				path, (long)box->file_create_gid);
+				path, (long)perm->file_create_gid);
 		}
 		/* continue anyway */
 	}
--- a/src/lib-storage/index/dbox-single/sdbox-save.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/dbox-single/sdbox-save.c	Sat Jan 01 15:59:02 2011 +0200
@@ -307,10 +307,11 @@
 		ctx->ctx.failed = TRUE;
 
 	if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER) {
-		if (fdatasync_path(ctx->mbox->box.path) < 0) {
+		const char *box_path = mailbox_get_path(&ctx->mbox->box);
+
+		if (fdatasync_path(box_path) < 0) {
 			mail_storage_set_critical(storage,
-				"fdatasync_path(%s) failed: %m",
-				ctx->mbox->box.path);
+				"fdatasync_path(%s) failed: %m", box_path);
 		}
 	}
 	sdbox_transaction_save_rollback(_ctx);
--- a/src/lib-storage/index/dbox-single/sdbox-storage.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/dbox-single/sdbox-storage.c	Sat Jan 01 15:59:02 2011 +0200
@@ -48,10 +48,6 @@
 	mbox->box.mail_vfuncs = &sdbox_mail_vfuncs;
 
 	index_storage_mailbox_alloc(&mbox->box, name, flags, DBOX_INDEX_PREFIX);
-	mail_index_set_fsync_mode(mbox->box.index,
-				  storage->set->parsed_fsync_mode,
-				  MAIL_INDEX_SYNC_TYPE_APPEND |
-				  MAIL_INDEX_SYNC_TYPE_EXPUNGE);
 
 	ibox = INDEX_STORAGE_CONTEXT(&mbox->box);
 	ibox->save_commit_pre = sdbox_transaction_save_commit_pre;
@@ -61,9 +57,6 @@
 		MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY;
 
 	mbox->storage = (struct sdbox_storage *)storage;
-	mbox->hdr_ext_id =
-		mail_index_ext_register(mbox->box.index, "dbox-hdr",
-					sizeof(struct sdbox_index_header), 0, 0);
 	return &mbox->box;
 }
 
@@ -84,7 +77,7 @@
 			mail_storage_set_critical(
 				&mbox->storage->storage.storage,
 				"sdbox %s: Invalid dbox header size",
-				mbox->box.path);
+				mailbox_get_path(&mbox->box));
 		}
 		ret = -1;
 	} else {
@@ -226,6 +219,9 @@
 
 	if (dbox_mailbox_open(box) < 0)
 		return -1;
+	mbox->hdr_ext_id =
+		mail_index_ext_register(box->index, "dbox-hdr",
+					sizeof(struct sdbox_index_header), 0, 0);
 
 	/* get/generate mailbox guid */
 	if (sdbox_read_header(mbox, &hdr, FALSE) < 0) {
@@ -307,12 +303,17 @@
 }
 
 static int
-sdbox_mailbox_get_guid(struct mailbox *box, uint8_t guid[MAIL_GUID_128_SIZE])
+sdbox_mailbox_get_metadata(struct mailbox *box,
+			   enum mailbox_metadata_items items,
+			   struct mailbox_metadata *metadata_r)
 {
 	struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)box;
 
-	memcpy(guid, mbox->mailbox_guid, MAIL_GUID_128_SIZE);
-	return 0;
+	if ((items & MAILBOX_METADATA_GUID) != 0) {
+		memcpy(metadata_r->guid, mbox->mailbox_guid,
+		       MAIL_GUID_128_SIZE);
+	}
+	return index_mailbox_get_metadata(box, items, metadata_r);
 }
 
 static int
@@ -374,7 +375,7 @@
 		sdbox_mailbox_delete,
 		index_storage_mailbox_rename,
 		index_storage_get_status,
-		sdbox_mailbox_get_guid,
+		sdbox_mailbox_get_metadata,
 		NULL,
 		NULL,
 		sdbox_storage_sync_init,
@@ -385,21 +386,8 @@
 		index_transaction_begin,
 		index_transaction_commit,
 		index_transaction_rollback,
-		index_transaction_set_max_modseq,
-		index_keywords_create,
-		index_keywords_create_from_indexes,
-		index_keywords_ref,
-		index_keywords_unref,
-		index_keyword_is_valid,
-		index_storage_get_seq_range,
-		index_storage_get_uid_range,
-		index_storage_get_expunges,
-		NULL,
-		NULL,
 		NULL,
 		dbox_mail_alloc,
-		index_header_lookup_init,
-		index_header_lookup_deinit,
 		index_storage_search_init,
 		index_storage_search_deinit,
 		index_storage_search_next_nonblock,
@@ -410,7 +398,6 @@
 		sdbox_save_finish,
 		sdbox_save_cancel,
 		sdbox_copy,
-		NULL,
 		index_storage_is_inconsistent
 	}
 };
--- a/src/lib-storage/index/dbox-single/sdbox-sync-rebuild.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/dbox-single/sdbox-sync-rebuild.c	Sat Jan 01 15:59:02 2011 +0200
@@ -73,7 +73,7 @@
 
 	if (str_to_uint32(fname, &uid) < 0 || uid == 0) {
 		i_warning("sdbox %s: Ignoring invalid filename %s",
-			  ctx->box->path, fname);
+			  mailbox_get_path(ctx->box), fname);
 		return 0;
 	}
 
@@ -146,14 +146,15 @@
 static int
 sdbox_sync_index_rebuild_singles(struct dbox_sync_rebuild_context *ctx)
 {
-	const char *alt_path;
+	const char *path, *alt_path;
 	int ret = 0;
 
+	path = mailbox_get_path(ctx->box);
 	alt_path = mailbox_list_get_path(ctx->box->list, ctx->box->name,
 					 MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX);
 
 	sdbox_sync_set_uidvalidity(ctx);
-	if (sdbox_sync_index_rebuild_dir(ctx, ctx->box->path, TRUE) < 0)
+	if (sdbox_sync_index_rebuild_dir(ctx, path, TRUE) < 0)
 		ret = -1;
 	else if (alt_path != NULL)
 		ret = sdbox_sync_index_rebuild_dir(ctx, alt_path, FALSE);
--- a/src/lib-storage/index/dbox-single/sdbox-sync.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/dbox-single/sdbox-sync.c	Sat Jan 01 15:59:02 2011 +0200
@@ -202,12 +202,12 @@
 			if (i >= SDBOX_REBUILD_COUNT) {
 				mail_storage_set_critical(storage,
 					"sdbox %s: Index keeps breaking",
-					ctx->mbox->box.path);
+					mailbox_get_path(&ctx->mbox->box));
 				ret = -1;
 			} else {
 				/* do a full resync and try again. */
 				i_warning("sdbox %s: Rebuilding index",
-					  ctx->mbox->box.path);
+					  mailbox_get_path(&ctx->mbox->box));
 				rebuild = FALSE;
 				ret = sdbox_sync_index_rebuild(mbox,
 							       force_rebuild);
--- a/src/lib-storage/index/index-attachment.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/index-attachment.c	Sat Jan 01 15:59:02 2011 +0200
@@ -147,7 +147,6 @@
 static bool save_is_attachment(struct mail_save_context *ctx,
 			       struct message_part *part)
 {
-	struct mailbox *box = ctx->transaction->box;
 	struct mail_attachment_part apart;
 
 	if ((part->flags & MESSAGE_PART_FLAG_MULTIPART) != 0) {
@@ -155,14 +154,14 @@
 		   but they're never themselves */
 		return FALSE;
 	}
-	if (box->v.save_is_attachment == NULL)
+	if (ctx->part_is_attachment == NULL)
 		return TRUE;
 
 	memset(&apart, 0, sizeof(apart));
 	apart.part = part;
 	apart.content_type = ctx->attach->part.content_type;
 	apart.content_disposition = ctx->attach->part.content_disposition;
-	return box->v.save_is_attachment(ctx, &apart);
+	return ctx->part_is_attachment(ctx, &apart);
 }
 
 static int index_attachment_save_temp_open_fd(struct mail_storage *storage)
--- a/src/lib-storage/index/index-fetch.c	Sat Jan 01 14:34:14 2011 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,140 +0,0 @@
-/* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "array.h"
-#include "mail-index-modseq.h"
-#include "index-storage.h"
-#include "index-mail.h"
-
-void index_storage_get_seq_range(struct mailbox *box,
-				 uint32_t uid1, uint32_t uid2,
-				 uint32_t *seq1_r, uint32_t *seq2_r)
-{
-	mail_index_lookup_seq_range(box->view, uid1, uid2, seq1_r, seq2_r);
-}
-
-void index_storage_get_uid_range(struct mailbox *box,
-				 const ARRAY_TYPE(seq_range) *seqs,
-				 ARRAY_TYPE(seq_range) *uids)
-{
-	const struct seq_range *range;
-	unsigned int i, count;
-	uint32_t seq, uid;
-
-	range = array_get(seqs, &count);
-	for (i = 0; i < count; i++) {
-		if (range[i].seq2 == (uint32_t)-1) {
-			i_assert(count == i-1);
-			mail_index_lookup_uid(box->view, range[i].seq1, &uid);
-			seq_range_array_add_range(uids, uid, (uint32_t)-1);
-			break;
-		}
-		for (seq = range[i].seq1; seq <= range[i].seq2; seq++) {
-			mail_index_lookup_uid(box->view, seq, &uid);
-			seq_range_array_add(uids, 0, uid);
-		}
-	}
-}
-
-static void
-add_expunges(ARRAY_TYPE(mailbox_expunge_rec) *expunges,
-	     const struct mail_transaction_expunge *src, size_t src_size,
-	     const ARRAY_TYPE(seq_range) *uids_filter)
-{
-	const struct mail_transaction_expunge *end;
-	struct mailbox_expunge_rec *expunge;
-	uint32_t uid;
-
-	end = src + src_size / sizeof(*src);
-	for (; src != end; src++) {
-		for (uid = src->uid1; uid <= src->uid2; uid++) {
-			if (seq_range_exists(uids_filter, uid)) {
-				expunge = array_append_space(expunges);
-				expunge->uid = uid;
-			}
-		}
-	}
-}
-
-static void
-add_guid_expunges(ARRAY_TYPE(mailbox_expunge_rec) *expunges,
-		  const struct mail_transaction_expunge_guid *src,
-		  size_t src_size, const ARRAY_TYPE(seq_range) *uids_filter)
-{
-	const struct mail_transaction_expunge_guid *end;
-	struct mailbox_expunge_rec *expunge;
-
-	end = src + src_size / sizeof(*src);
-	for (; src != end; src++) {
-		if (seq_range_exists(uids_filter, src->uid)) {
-			expunge = array_append_space(expunges);
-			expunge->uid = src->uid;
-			memcpy(expunge->guid_128, src->guid_128,
-			       sizeof(expunge->guid_128));
-		}
-	}
-}
-
-bool index_storage_get_expunges(struct mailbox *box, uint64_t prev_modseq,
-				const ARRAY_TYPE(seq_range) *uids_filter,
-				ARRAY_TYPE(mailbox_expunge_rec) *expunges)
-{
-	struct mail_transaction_log_view *log_view;
-	const struct mail_transaction_header *thdr;
-	const void *tdata;
-	uint32_t log_seq, tail_seq = 0;
-	uoff_t log_offset;
-	bool reset;
-	int ret;
-
-	if (!mail_index_modseq_get_next_log_offset(box->view, prev_modseq,
-						   &log_seq, &log_offset)) {
-		log_seq = 1;
-		log_offset = 0;
-	}
-	if (log_seq > box->view->log_file_head_seq ||
-	    (log_seq == box->view->log_file_head_seq &&
-	     log_offset >= box->view->log_file_head_offset)) {
-		/* we haven't seen this high expunges at all */
-		return TRUE;
-	}
-
-	log_view = mail_transaction_log_view_open(box->index->log);
-	ret = mail_transaction_log_view_set(log_view, log_seq, log_offset,
-					    box->view->log_file_head_seq,
-					    box->view->log_file_head_offset,
-					    &reset);
-	if (ret == 0) {
-		mail_transaction_log_get_tail(box->index->log, &tail_seq);
-		i_assert(tail_seq > log_seq);
-		ret = mail_transaction_log_view_set(log_view, tail_seq, 0,
-					box->view->log_file_head_seq,
-					box->view->log_file_head_offset,
-					&reset);
-		i_assert(ret != 0);
-	}
-	if (ret <= 0) {
-		mail_transaction_log_view_close(&log_view);
-		return FALSE;
-	}
-
-	while ((ret = mail_transaction_log_view_next(log_view,
-						     &thdr, &tdata)) > 0) {
-		if ((thdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) {
-			/* skip expunge requests */
-			continue;
-		}
-		switch (thdr->type & MAIL_TRANSACTION_TYPE_MASK) {
-		case MAIL_TRANSACTION_EXPUNGE:
-			add_expunges(expunges, tdata, thdr->size, uids_filter);
-			break;
-		case MAIL_TRANSACTION_EXPUNGE_GUID:
-			add_guid_expunges(expunges, tdata, thdr->size,
-					  uids_filter);
-			break;
-		}
-	}
-
-	mail_transaction_log_view_close(&log_view);
-	return ret < 0 || tail_seq != 0 ? FALSE : TRUE;
-}
--- a/src/lib-storage/index/index-mail-headers.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/index-mail-headers.c	Sat Jan 01 15:59:02 2011 +0200
@@ -15,17 +15,6 @@
 #include "index-storage.h"
 #include "index-mail.h"
 
-#include <stdlib.h>
-
-struct index_header_lookup_ctx {
-	struct mailbox_header_lookup_ctx ctx;
-	pool_t pool;
-
-	unsigned int count;
-	unsigned int *idx;
-	const char **name;
-};
-
 static const enum message_header_parser_flags hdr_parser_flags =
 	MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP |
 	MESSAGE_HEADER_PARSER_FLAG_DROP_CR;
@@ -44,6 +33,7 @@
 
 static void index_mail_parse_header_finish(struct index_mail *mail)
 {
+	struct mail *_mail = &mail->mail.mail;
 	const struct index_mail_line *lines;
 	const unsigned char *header, *data;
 	const uint8_t *match;
@@ -68,9 +58,8 @@
 		while (match_idx < lines[i].field_idx &&
 		       match_idx < match_count) {
 			if (HEADER_MATCH_USABLE(mail, match[match_idx]) &&
-			    mail_cache_field_can_add(mail->trans->cache_trans,
-						     mail->data.seq,
-						     match_idx)) {
+			    mail_cache_field_can_add(_mail->transaction->cache_trans,
+						     _mail->seq, match_idx)) {
 				/* this header doesn't exist. remember that. */
 				i_assert((match[match_idx] &
 					  HEADER_MATCH_FLAG_FOUND) == 0);
@@ -87,9 +76,8 @@
 			match_idx++;
 		}
 
-		if (!mail_cache_field_can_add(mail->trans->cache_trans,
-					      mail->data.seq,
-					      lines[i].field_idx)) {
+		if (!mail_cache_field_can_add(_mail->transaction->cache_trans,
+					      _mail->seq, lines[i].field_idx)) {
 			/* header is already cached */
 			j = i + 1;
 			continue;
@@ -132,9 +120,8 @@
 
 	for (; match_idx < match_count; match_idx++) {
 		if (HEADER_MATCH_USABLE(mail, match[match_idx]) &&
-		    mail_cache_field_can_add(mail->trans->cache_trans,
-					     mail->data.seq,
-					     match_idx)) {
+		    mail_cache_field_can_add(_mail->transaction->cache_trans,
+					     _mail->seq, match_idx)) {
 			/* this header doesn't exist. remember that. */
 			i_assert((match[match_idx] &
 				  HEADER_MATCH_FLAG_FOUND) == 0);
@@ -176,17 +163,18 @@
 
 static void index_mail_parse_header_register_all_wanted(struct index_mail *mail)
 {
+	struct mail *_mail = &mail->mail.mail;
 	const struct mail_cache_field *all_cache_fields;
 	unsigned int i, count;
 
 	all_cache_fields =
-		mail_cache_register_get_list(mail->mail.mail.box->cache,
+		mail_cache_register_get_list(_mail->box->cache,
 					     pool_datastack_create(), &count);
 	for (i = 0; i < count; i++) {
 		if (strncasecmp(all_cache_fields[i].name, "hdr.", 4) != 0)
 			continue;
-		if (!mail_cache_field_want_add(mail->trans->cache_trans,
-					       mail->data.seq, i))
+		if (!mail_cache_field_want_add(_mail->transaction->cache_trans,
+					       _mail->seq, i))
 			continue;
 
 		array_idx_set(&mail->header_match, all_cache_fields[i].idx,
@@ -195,10 +183,8 @@
 }
 
 void index_mail_parse_header_init(struct index_mail *mail,
-				  struct mailbox_header_lookup_ctx *_headers)
+				  struct mailbox_header_lookup_ctx *headers)
 {
-	struct index_header_lookup_ctx *headers =
-		(struct index_header_lookup_ctx *)_headers;
 	const uint8_t *match;
 	unsigned int i, field_idx, match_count;
 
@@ -265,6 +251,7 @@
 
 static void index_mail_parse_finish_imap_envelope(struct index_mail *mail)
 {
+	struct mail *_mail = &mail->mail.mail;
 	const unsigned int cache_field_envelope =
 		mail->ibox->cache_fields[MAIL_CACHE_IMAP_ENVELOPE].idx;
 	string_t *str;
@@ -273,8 +260,8 @@
 	imap_envelope_write_part_data(mail->data.envelope_data, str);
 	mail->data.envelope = str_c(str);
 
-	if (mail_cache_field_can_add(mail->trans->cache_trans,
-				     mail->data.seq, cache_field_envelope)) {
+	if (mail_cache_field_can_add(_mail->transaction->cache_trans,
+				     _mail->seq, cache_field_envelope)) {
 		index_mail_cache_add_idx(mail, cache_field_envelope,
 					 str_data(str), str_len(str));
 	}
@@ -596,6 +583,7 @@
 index_mail_get_raw_headers(struct index_mail *mail, const char *field,
 			   const char *const **value_r)
 {
+	struct mail *_mail = &mail->mail.mail;
 	const char *headers[2], *value;
 	struct mailbox_header_lookup_ctx *headers_ctx;
 	unsigned char *data;
@@ -607,12 +595,12 @@
 
 	i_assert(field != NULL);
 
-	field_idx = get_header_field_idx(mail->mail.mail.box, field,
+	field_idx = get_header_field_idx(_mail->box, field,
 					 MAIL_CACHE_DECISION_TEMP);
 
 	dest = str_new(mail->data_pool, 128);
-	if (mail_cache_lookup_headers(mail->trans->cache_view, dest,
-				      mail->data.seq, &field_idx, 1) <= 0) {
+	if (mail_cache_lookup_headers(_mail->transaction->cache_view, dest,
+				      _mail->seq, &field_idx, 1) <= 0) {
 		/* not in cache / error - first see if it's already parsed */
 		p_free(mail->data_pool, dest);
 
@@ -620,8 +608,8 @@
 		    index_mail_header_is_parsed(mail, field_idx) < 0) {
 			/* parse */
 			headers[0] = field; headers[1] = NULL;
-			headers_ctx = mailbox_header_lookup_init(
-						mail->mail.mail.box, headers);
+			headers_ctx = mailbox_header_lookup_init(_mail->box,
+								 headers);
 			ret = index_mail_parse_headers(mail, headers_ctx);
 			mailbox_header_lookup_unref(&headers_ctx);
 			if (ret < 0)
@@ -800,26 +788,24 @@
 }
 
 int index_mail_get_header_stream(struct mail *_mail,
-				 struct mailbox_header_lookup_ctx *_headers,
+				 struct mailbox_header_lookup_ctx *headers,
 				 struct istream **stream_r)
 {
 	struct index_mail *mail = (struct index_mail *)_mail;
-	struct index_header_lookup_ctx *headers =
-		(struct index_header_lookup_ctx *)_headers;
 	struct istream *input;
 	string_t *dest;
 
-	i_assert(_headers->box == _mail->box);
+	i_assert(headers->box == _mail->box);
 
 	if (mail->data.save_bodystructure_header) {
 		/* we have to parse the header. */
-		if (index_mail_parse_headers(mail, _headers) < 0)
+		if (index_mail_parse_headers(mail, headers) < 0)
 			return -1;
 	}
 
 	dest = str_new(mail->data_pool, 256);
-	if (mail_cache_lookup_headers(mail->trans->cache_view, dest,
-				      mail->data.seq, headers->idx,
+	if (mail_cache_lookup_headers(_mail->transaction->cache_view, dest,
+				      _mail->seq, headers->idx,
 				      headers->count) > 0) {
 		mail->mail.stats_cache_hit_count++;
 		if (mail->data.filter_stream != NULL)
@@ -833,13 +819,13 @@
 	/* not in cache / error */
 	p_free(mail->data_pool, dest);
 
-	if (mail_get_stream(&mail->mail.mail, NULL, NULL, &input) < 0)
+	if (mail_get_stream(_mail, NULL, NULL, &input) < 0)
 		return -1;
 
 	if (mail->data.filter_stream != NULL)
 		i_stream_destroy(&mail->data.filter_stream);
 
-	index_mail_parse_header_init(mail, _headers);
+	index_mail_parse_header_init(mail, headers);
 	mail->data.filter_stream =
 		i_stream_create_header_filter(mail->data.stream,
 					      HEADER_FILTER_INCLUDE |
@@ -849,73 +835,3 @@
 	*stream_r = mail->data.filter_stream;
 	return 0;
 }
-
-static struct mailbox_header_lookup_ctx *
-index_header_lookup_init_real(struct mailbox *box, const char *const headers[])
-{
-	struct mail_cache_field *fields, header_field = {
-		NULL, 0, MAIL_CACHE_FIELD_HEADER, 0,
-		MAIL_CACHE_DECISION_TEMP
-	};
-	struct index_header_lookup_ctx *ctx;
-	const char *const *name;
-	const char **sorted_headers;
-	pool_t pool;
-	unsigned int i, count;
-
-	i_assert(*headers != NULL);
-
-	for (count = 0, name = headers; *name != NULL; name++)
-		count++;
-
-	/* @UNSAFE: headers need to be sorted for filter stream. */
-	sorted_headers = t_new(const char *, count);
-	memcpy(sorted_headers, headers, count * sizeof(*sorted_headers));
-	qsort(sorted_headers, count, sizeof(*sorted_headers), i_strcasecmp_p);
-	headers = sorted_headers;
-
-	/* @UNSAFE */
-	fields = t_new(struct mail_cache_field, count);
-	for (i = 0; i < count; i++) {
-		header_field.name = t_strconcat("hdr.", headers[i], NULL);
-		fields[i] = header_field;
-	}
-	mail_cache_register_fields(box->cache, fields, count);
-
-	pool = pool_alloconly_create("index_header_lookup_ctx", 1024);
-	ctx = p_new(pool, struct index_header_lookup_ctx, 1);
-	ctx->ctx.box = box;
-	ctx->ctx.refcount = 1;
-	ctx->pool = pool;
-	ctx->count = count;
-
-	ctx->idx = p_new(pool, unsigned int, count);
-	ctx->name = p_new(pool, const char *, count + 1);
-
-	/* @UNSAFE */
-	for (i = 0; i < count; i++) {
-		ctx->idx[i] = fields[i].idx;
-		ctx->name[i] = p_strdup(pool, headers[i]);
-	}
-	ctx->ctx.headers = ctx->name;
-	return &ctx->ctx;
-}
-
-struct mailbox_header_lookup_ctx *
-index_header_lookup_init(struct mailbox *box, const char *const headers[])
-{
-	struct mailbox_header_lookup_ctx *ctx;
-
-	T_BEGIN {
-		ctx = index_header_lookup_init_real(box, headers);
-	} T_END;
-	return ctx;
-}
-
-void index_header_lookup_deinit(struct mailbox_header_lookup_ctx *_ctx)
-{
-	struct index_header_lookup_ctx *ctx =
-		(struct index_header_lookup_ctx *)_ctx;
-
-	pool_unref(&ctx->pool);
-}
--- a/src/lib-storage/index/index-mail.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/index-mail.c	Sat Jan 01 15:59:02 2011 +0200
@@ -45,8 +45,8 @@
 {
 	int ret;
 
-	ret = mail_cache_lookup_field(mail->trans->cache_view, buf,
-				      mail->data.seq, field_idx);
+	ret = mail_cache_lookup_field(mail->mail.mail.transaction->cache_view,
+				      buf, mail->data.seq, field_idx);
 	if (ret > 0)
 		mail->mail.stats_cache_hit_count++;
 	return ret;
@@ -54,7 +54,7 @@
 
 static struct message_part *get_unserialized_parts(struct index_mail *mail)
 {
-	unsigned int field_idx =
+	const unsigned int field_idx =
 		mail->ibox->cache_fields[MAIL_CACHE_MESSAGE_PARTS].idx;
 	struct message_part *parts;
 	buffer_t *part_buf;
@@ -102,7 +102,7 @@
 				       enum index_cache_field field,
 				       void *data, size_t data_size)
 {
-	unsigned int field_idx = mail->ibox->cache_fields[field].idx;
+	const unsigned int field_idx = mail->ibox->cache_fields[field].idx;
 	buffer_t buf;
 	int ret;
 
@@ -421,21 +421,21 @@
 void index_mail_cache_add_idx(struct index_mail *mail, unsigned int field_idx,
 			      const void *data, size_t data_size)
 {
-	const struct mail_storage_settings *set =
-		mail->mail.mail.box->storage->set;
+	struct mail *_mail = &mail->mail.mail;
+	const struct mail_storage_settings *set = _mail->box->storage->set;
 	const struct mail_index_header *hdr;
 
 	if (set->mail_cache_min_mail_count > 0) {
 		/* First check if we've configured caching not to be used with
 		   low enough message count. */
-		hdr = mail_index_get_header(mail->mail.mail.box->view);
+		hdr = mail_index_get_header(_mail->box->view);
 		if (hdr->messages_count < set->mail_cache_min_mail_count)
 			return;
 	}
 
 	if (!mail->data.no_caching &&
 	    mail->data.dont_cache_field_idx != field_idx) {
-		mail_cache_add(mail->trans->cache_trans, mail->data.seq,
+		mail_cache_add(_mail->transaction->cache_trans, _mail->seq,
 			       field_idx, data, data_size);
 	}
 }
@@ -449,29 +449,36 @@
 
 static bool want_plain_bodystructure_cached(struct index_mail *mail)
 {
+	const unsigned int cache_field_body =
+		mail->ibox->cache_fields[MAIL_CACHE_IMAP_BODY].idx;
+	const unsigned int cache_field_bodystructure =
+		mail->ibox->cache_fields[MAIL_CACHE_IMAP_BODYSTRUCTURE].idx;
+	struct mail *_mail = &mail->mail.mail;
+
 	if ((mail->wanted_fields & (MAIL_FETCH_IMAP_BODY |
 				    MAIL_FETCH_IMAP_BODYSTRUCTURE)) != 0)
 		return TRUE;
 
-	if (mail_cache_field_want_add(mail->trans->cache_trans, mail->data.seq,
-		mail->ibox->cache_fields[MAIL_CACHE_IMAP_BODY].idx))
+	if (mail_cache_field_want_add(_mail->transaction->cache_trans,
+				      _mail->seq, cache_field_body))
 		return TRUE;
-	if (mail_cache_field_want_add(mail->trans->cache_trans, mail->data.seq,
-		mail->ibox->cache_fields[MAIL_CACHE_IMAP_BODYSTRUCTURE].idx))
+	if (mail_cache_field_want_add(_mail->transaction->cache_trans,
+				      _mail->seq, cache_field_bodystructure))
 		return TRUE;
 	return FALSE;
 }
 
 static void index_mail_body_parsed_cache_flags(struct index_mail *mail)
 {
+	struct mail *_mail = &mail->mail.mail;
 	struct index_mail_data *data = &mail->data;
 	unsigned int cache_flags_idx;
 	uint32_t cache_flags = data->cache_flags;
 	bool want_cached;
 
 	cache_flags_idx = mail->ibox->cache_fields[MAIL_CACHE_FLAGS].idx;
-	want_cached = mail_cache_field_want_add(mail->trans->cache_trans,
-						data->seq, cache_flags_idx);
+	want_cached = mail_cache_field_want_add(_mail->transaction->cache_trans,
+						_mail->seq, cache_flags_idx);
 
 	if (data->parsed_bodystructure &&
 	    imap_bodystructure_is_plain_7bit(data->parts) &&
@@ -490,12 +497,12 @@
 			 MAIL_CACHE_FLAG_HAS_NULS |
 			 MAIL_CACHE_FLAG_HAS_NO_NULS);
 	if ((data->parts->flags & MESSAGE_PART_FLAG_HAS_NULS) != 0) {
-		mail->mail.mail.has_nuls = TRUE;
-		mail->mail.mail.has_no_nuls = FALSE;
+		_mail->has_nuls = TRUE;
+		_mail->has_no_nuls = FALSE;
 		cache_flags |= MAIL_CACHE_FLAG_HAS_NULS;
 	} else {
-		mail->mail.mail.has_nuls = FALSE;
-		mail->mail.mail.has_no_nuls = TRUE;
+		_mail->has_nuls = FALSE;
+		_mail->has_no_nuls = TRUE;
 		cache_flags |= MAIL_CACHE_FLAG_HAS_NO_NULS;
 	}
 
@@ -513,20 +520,21 @@
 
 static void index_mail_body_parsed_cache_message_parts(struct index_mail *mail)
 {
+	struct mail *_mail = &mail->mail.mail;
 	struct index_mail_data *data = &mail->data;
-	unsigned int cache_field =
+	const unsigned int cache_field =
 		mail->ibox->cache_fields[MAIL_CACHE_MESSAGE_PARTS].idx;
 	enum mail_cache_decision_type decision;
 	buffer_t *buffer;
 
 	if (data->messageparts_saved_to_cache ||
-	    mail_cache_field_exists(mail->trans->cache_view, mail->data.seq,
+	    mail_cache_field_exists(_mail->transaction->cache_view, _mail->seq,
 				    cache_field) != 0) {
 		/* already cached */
 		return;
 	}
 
-	decision = mail_cache_field_get_decision(mail->mail.mail.box->cache,
+	decision = mail_cache_field_get_decision(_mail->box->cache,
 						 cache_field);
 	if (decision == (MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED)) {
 		/* we never want it cached */
@@ -554,12 +562,13 @@
 index_mail_body_parsed_cache_bodystructure(struct index_mail *mail,
 					   enum index_cache_field field)
 {
+	struct mail *_mail = &mail->mail.mail;
 	struct index_mail_data *data = &mail->data;
-	unsigned int cache_field_parts =
+	const unsigned int cache_field_parts =
 		mail->ibox->cache_fields[MAIL_CACHE_MESSAGE_PARTS].idx;
-	unsigned int cache_field_body =
+	const unsigned int cache_field_body =
 		mail->ibox->cache_fields[MAIL_CACHE_IMAP_BODY].idx;
-	unsigned int cache_field_bodystructure =
+	const unsigned int cache_field_bodystructure =
 		mail->ibox->cache_fields[MAIL_CACHE_IMAP_BODYSTRUCTURE].idx;
 	enum mail_cache_decision_type dec;
 	string_t *str;
@@ -569,8 +578,8 @@
 
 	if ((data->cache_flags & MAIL_CACHE_FLAG_TEXT_PLAIN_7BIT_ASCII) != 0) {
 		if (data->messageparts_saved_to_cache ||
-		    mail_cache_field_exists(mail->trans->cache_view, data->seq,
-					    cache_field_parts) > 0) {
+		    mail_cache_field_exists(_mail->transaction->cache_view,
+					    _mail->seq, cache_field_parts) > 0) {
 			/* cached it as flag + message_parts */
 			plain_bodystructure = TRUE;
 		}
@@ -587,12 +596,12 @@
 	else if (field == MAIL_CACHE_IMAP_BODYSTRUCTURE ||
 		 (mail->wanted_fields & MAIL_FETCH_IMAP_BODYSTRUCTURE) != 0) {
 		cache_bodystructure =
-			mail_cache_field_can_add(mail->trans->cache_trans,
-				data->seq, cache_field_bodystructure);
+			mail_cache_field_can_add(_mail->transaction->cache_trans,
+				_mail->seq, cache_field_bodystructure);
 	} else {
 		cache_bodystructure =
-			mail_cache_field_want_add(mail->trans->cache_trans,
-				data->seq, cache_field_bodystructure);
+			mail_cache_field_want_add(_mail->transaction->cache_trans,
+				_mail->seq, cache_field_bodystructure);
 	}
 	if (cache_bodystructure) {
 		str = str_new(mail->data_pool, 128);
@@ -604,13 +613,13 @@
 		bodystructure_cached = TRUE;
 	} else {
 		bodystructure_cached =
-			mail_cache_field_exists(mail->trans->cache_view,
-				data->seq, cache_field_bodystructure) > 0;
+			mail_cache_field_exists(_mail->transaction->cache_view,
+				_mail->seq, cache_field_bodystructure) > 0;
 	}
 
 	/* normally don't cache both BODY and BODYSTRUCTURE, but do it
 	   if BODY is forced to be cached */
-	dec = mail_cache_field_get_decision(mail->mail.mail.box->cache,
+	dec = mail_cache_field_get_decision(_mail->box->cache,
 					    cache_field_body);
 	if (plain_bodystructure ||
 	    (bodystructure_cached &&
@@ -618,12 +627,12 @@
 		cache_body = FALSE;
 	else if (field == MAIL_CACHE_IMAP_BODY) {
 		cache_body =
-			mail_cache_field_can_add(mail->trans->cache_trans,
-				data->seq, cache_field_body);
+			mail_cache_field_can_add(_mail->transaction->cache_trans,
+				_mail->seq, cache_field_body);
 	} else {
 		cache_body =
-			mail_cache_field_want_add(mail->trans->cache_trans,
-				data->seq, cache_field_body);
+			mail_cache_field_want_add(_mail->transaction->cache_trans,
+				_mail->seq, cache_field_body);
 	}
 
 	if (cache_body) {
@@ -639,6 +648,7 @@
 static bool
 index_mail_want_cache(struct index_mail *mail, enum index_cache_field field)
 {
+	struct mail *_mail = &mail->mail.mail;
 	enum mail_fetch_field fetch_field;
 	unsigned int cache_field;
 
@@ -667,11 +677,11 @@
 
 	cache_field = mail->ibox->cache_fields[field].idx;
 	if ((mail->data.cache_fetch_fields & fetch_field) != 0) {
-		return mail_cache_field_can_add(mail->trans->cache_trans,
-						mail->data.seq, cache_field);
+		return mail_cache_field_can_add(_mail->transaction->cache_trans,
+						_mail->seq, cache_field);
 	} else {
-		return mail_cache_field_want_add(mail->trans->cache_trans,
-						 mail->data.seq, cache_field);
+		return mail_cache_field_want_add(_mail->transaction->cache_trans,
+						 _mail->seq, cache_field);
 	}
 }
 
@@ -804,7 +814,8 @@
 
 enum index_mail_access_part index_mail_get_access_part(struct index_mail *mail)
 {
-	struct mail_cache_field *cache_fields = mail->ibox->cache_fields;
+	struct mail *_mail = &mail->mail.mail;
+	const struct mail_cache_field *cache_fields = mail->ibox->cache_fields;
 
 	if ((mail->data.access_part & (READ_HDR | PARSE_HDR)) != 0 &&
 	    (mail->data.access_part & (READ_BODY | PARSE_BODY)) != 0)
@@ -812,12 +823,11 @@
 
 	/* lazy virtual size access check */
 	if ((mail->wanted_fields & MAIL_FETCH_VIRTUAL_SIZE) != 0) {
-		unsigned int cache_field =
+		const unsigned int cache_field =
 			cache_fields[MAIL_CACHE_VIRTUAL_FULL_SIZE].idx;
 
-		if (mail_cache_field_exists(mail->trans->cache_view,
-					    mail->mail.mail.seq,
-					    cache_field) <= 0)
+		if (mail_cache_field_exists(_mail->transaction->cache_view,
+					    _mail->seq, cache_field) <= 0)
 			mail->data.access_part |= READ_HDR | READ_BODY;
 	}
 	return mail->data.access_part;
@@ -979,14 +989,14 @@
 {
 	struct index_mail *mail = (struct index_mail *)_mail;
 	struct index_mail_data *data = &mail->data;
-	struct mail_cache_field *cache_fields = mail->ibox->cache_fields;
+	const struct mail_cache_field *cache_fields = mail->ibox->cache_fields;
 	string_t *str;
 
 	switch (field) {
 	case MAIL_FETCH_IMAP_BODY: {
-		unsigned int body_cache_field =
+		const unsigned int body_cache_field =
                         cache_fields[MAIL_CACHE_IMAP_BODY].idx;
-		unsigned int bodystructure_cache_field =
+		const unsigned int bodystructure_cache_field =
                         cache_fields[MAIL_CACHE_IMAP_BODYSTRUCTURE].idx;
 
 		if (data->body != NULL) {
@@ -1036,7 +1046,7 @@
 		return 0;
 	}
 	case MAIL_FETCH_IMAP_BODYSTRUCTURE: {
-		unsigned int bodystructure_cache_field =
+		const unsigned int bodystructure_cache_field =
                         cache_fields[MAIL_CACHE_IMAP_BODYSTRUCTURE].idx;
 
 		if (data->bodystructure != NULL) {
@@ -1109,36 +1119,31 @@
 }
 
 void index_mail_init(struct index_mail *mail,
-		     struct mailbox_transaction_context *_t,
+		     struct mailbox_transaction_context *t,
 		     enum mail_fetch_field wanted_fields,
-		     struct mailbox_header_lookup_ctx *_wanted_headers)
+		     struct mailbox_header_lookup_ctx *wanted_headers)
 {
-	struct index_transaction_context *t =
-		(struct index_transaction_context *)_t;
-	struct index_header_lookup_ctx *wanted_headers =
-		(struct index_header_lookup_ctx *)_wanted_headers;
 	const struct mail_index_header *hdr;
 
 	array_create(&mail->mail.module_contexts, mail->mail.pool,
 		     sizeof(void *), 5);
 
-	mail->mail.v = *_t->box->mail_vfuncs;
-	mail->mail.mail.box = _t->box;
-	mail->mail.mail.transaction = &t->mailbox_ctx;
+	mail->mail.v = *t->box->mail_vfuncs;
+	mail->mail.mail.box = t->box;
+	mail->mail.mail.transaction = t;
 	mail->mail.wanted_fields = wanted_fields;
-	mail->mail.wanted_headers = _wanted_headers;
+	mail->mail.wanted_headers = wanted_headers;
 
-	hdr = mail_index_get_header(_t->box->view);
+	hdr = mail_index_get_header(t->box->view);
 	mail->uid_validity = hdr->uid_validity;
 
 	t->mail_ref_count++;
 	mail->data_pool = pool_alloconly_create("index_mail", 16384);
-	mail->ibox = INDEX_STORAGE_CONTEXT(_t->box);
-	mail->trans = t;
+	mail->ibox = INDEX_STORAGE_CONTEXT(t->box);
 	mail->wanted_fields = wanted_fields;
 	if (wanted_headers != NULL) {
 		mail->wanted_headers = wanted_headers;
-		mailbox_header_lookup_ref(_wanted_headers);
+		mailbox_header_lookup_ref(wanted_headers);
 	}
 }
 
@@ -1198,7 +1203,8 @@
 
 static void check_envelope(struct index_mail *mail)
 {
-	unsigned int cache_field_envelope =
+	struct mail *_mail = &mail->mail.mail;
+	const unsigned int cache_field_envelope =
 		mail->ibox->cache_fields[MAIL_CACHE_IMAP_ENVELOPE].idx;
 	unsigned int cache_field_hdr;
 
@@ -1208,20 +1214,18 @@
 	}
 
 	/* if "imap.envelope" is cached, that's all we need */
-	if (mail_cache_field_exists(mail->trans->cache_view,
-				    mail->mail.mail.seq,
-				    cache_field_envelope) > 0)
+	if (mail_cache_field_exists(_mail->transaction->cache_view,
+				    _mail->seq, cache_field_envelope) > 0)
 		return;
 
 	/* don't waste time doing full checks for all required
 	   headers. assume that if we have "hdr.message-id" cached,
 	   we don't need to parse the header. */
-	cache_field_hdr = mail_cache_register_lookup(mail->mail.mail.box->cache,
+	cache_field_hdr = mail_cache_register_lookup(_mail->box->cache,
 						     "hdr.message-id");
 	if (cache_field_hdr == (unsigned int)-1 ||
-	    mail_cache_field_exists(mail->trans->cache_view,
-				    mail->mail.mail.seq,
-				    cache_field_hdr) <= 0)
+	    mail_cache_field_exists(_mail->transaction->cache_view,
+				    _mail->seq, cache_field_hdr) <= 0)
 		mail->data.access_part |= PARSE_HDR;
 	mail->data.save_envelope = TRUE;
 }
@@ -1230,8 +1234,8 @@
 {
 	struct index_mail *mail = (struct index_mail *)_mail;
 	struct index_mail_data *data = &mail->data;
-	struct mail_cache_field *cache_fields = mail->ibox->cache_fields;
-	struct mail_cache_view *cache_view = mail->trans->cache_view;
+	const struct mail_cache_field *cache_fields = mail->ibox->cache_fields;
+	struct mail_cache_view *cache_view = _mail->transaction->cache_view;
 	const struct mail_index_header *hdr;
 	struct istream *input;
 
@@ -1266,7 +1270,7 @@
 	/* see if wanted_fields can tell us if we need to read/parse
 	   header/body */
 	if ((mail->wanted_fields & MAIL_FETCH_MESSAGE_PARTS) != 0) {
-		unsigned int cache_field =
+		const unsigned int cache_field =
 			cache_fields[MAIL_CACHE_MESSAGE_PARTS].idx;
 
 		if (mail_cache_field_exists(cache_view, seq,
@@ -1282,9 +1286,9 @@
 	if ((mail->wanted_fields & MAIL_FETCH_IMAP_BODY) != 0 &&
 	    (data->cache_flags & MAIL_CACHE_FLAG_TEXT_PLAIN_7BIT_ASCII) == 0) {
 		/* we need either imap.body or imap.bodystructure */
-		unsigned int cache_field1 =
+		const unsigned int cache_field1 =
 			cache_fields[MAIL_CACHE_IMAP_BODY].idx;
-		unsigned int cache_field2 =
+		const unsigned int cache_field2 =
 			cache_fields[MAIL_CACHE_IMAP_BODYSTRUCTURE].idx;
 
 		if (mail_cache_field_exists(cache_view,
@@ -1299,7 +1303,7 @@
 
 	if ((mail->wanted_fields & MAIL_FETCH_IMAP_BODYSTRUCTURE) != 0 &&
 	    (data->cache_flags & MAIL_CACHE_FLAG_TEXT_PLAIN_7BIT_ASCII) == 0) {
-		unsigned int cache_field =
+		const unsigned int cache_field =
 			cache_fields[MAIL_CACHE_IMAP_BODYSTRUCTURE].idx;
 
                 if (mail_cache_field_exists(cache_view, seq,
@@ -1311,7 +1315,7 @@
 	}
 
 	if ((mail->wanted_fields & MAIL_FETCH_DATE) != 0) {
-		unsigned int cache_field =
+		const unsigned int cache_field =
 			cache_fields[MAIL_CACHE_SENT_DATE].idx;
 
 		if (mail_cache_field_exists(cache_view, seq,
@@ -1369,8 +1373,8 @@
 
 	mail->mail.v.close(_mail);
 
-	i_assert(mail->trans->mail_ref_count > 0);
-	mail->trans->mail_ref_count--;
+	i_assert(_mail->transaction->mail_ref_count > 0);
+	_mail->transaction->mail_ref_count--;
 
 	if (mail->header_data != NULL)
 		buffer_free(&mail->header_data);
@@ -1543,7 +1547,7 @@
 	}
 
 	/* make sure we don't cache invalid values */
-	mail_cache_transaction_reset(imail->trans->cache_trans);
+	mail_cache_transaction_reset(mail->transaction->cache_trans);
 	imail->data.no_caching = TRUE;
 	imail->data.forced_no_caching = TRUE;
 	mail_cache_set_corrupted(mail->box->cache,
--- a/src/lib-storage/index/index-mail.h	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/index-mail.h	Sat Jan 01 15:59:02 2011 +0200
@@ -120,11 +120,10 @@
 	struct index_mailbox_context *ibox;
 
 	pool_t data_pool;
-	struct index_transaction_context *trans;
 	uint32_t uid_validity;
 
 	enum mail_fetch_field wanted_fields;
-	struct index_header_lookup_ctx *wanted_headers;
+	struct mailbox_header_lookup_ctx *wanted_headers;
 
 	int pop3_state;
 
--- a/src/lib-storage/index/index-search.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/index-search.c	Sat Jan 01 15:59:02 2011 +0200
@@ -88,7 +88,7 @@
 static void search_init_arg(struct mail_search_arg *arg,
 			    struct index_search_context *ctx)
 {
-	uint8_t guid[MAIL_GUID_128_SIZE];
+	struct mailbox_metadata metadata;
 	bool match;
 
 	switch (arg->type) {
@@ -105,12 +105,13 @@
 		ctx->have_index_args = TRUE;
 		break;
 	case SEARCH_MAILBOX_GUID:
-		if (mailbox_get_guid(ctx->box, guid) < 0) {
+		if (mailbox_get_metadata(ctx->box, MAILBOX_METADATA_GUID,
+					 &metadata) < 0) {
 			/* result will be unknown */
 			break;
 		}
 
-		match = strcmp(mail_guid_128_to_string(guid),
+		match = strcmp(mail_guid_128_to_string(metadata.guid),
 			       arg->value.str) == 0;
 		if (match != arg->not)
 			arg->match_always = TRUE;
@@ -1079,7 +1080,7 @@
 	if (gettimeofday(&ctx->last_nonblock_timeval, NULL) < 0)
 		i_fatal("gettimeofday() failed: %m");
 
-	mailbox_get_status(t->box, STATUS_MESSAGES, &status);
+	mailbox_get_open_status(t->box, STATUS_MESSAGES, &status);
 	ctx->mail_ctx.progress_max = status.messages;
 
 	i_array_init(&ctx->mail_ctx.results, 5);
--- a/src/lib-storage/index/index-status.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/index-status.c	Sat Jan 01 15:59:02 2011 +0200
@@ -7,9 +7,54 @@
 #include "index-storage.h"
 #include "mail-index-modseq.h"
 
+int index_storage_get_status(struct mailbox *box,
+			     enum mailbox_status_items items,
+			     struct mailbox_status *status_r)
+{
+	const struct mail_index_header *hdr;
+
+	memset(status_r, 0, sizeof(struct mailbox_status));
+
+	if (!box->opened) {
+		if (mailbox_open(box) < 0)
+			return -1;
+		if (mailbox_sync(box, 0) < 0)
+			return -1;
+	}
+
+	/* we can get most of the status items without any trouble */
+	hdr = mail_index_get_header(box->view);
+	status_r->messages = hdr->messages_count;
+	if ((items & STATUS_RECENT) != 0) {
+		status_r->recent = index_mailbox_get_recent_count(box);
+		i_assert(status_r->recent <= status_r->messages);
+	}
+	status_r->unseen = hdr->messages_count - hdr->seen_messages_count;
+	status_r->uidvalidity = hdr->uid_validity;
+	status_r->uidnext = hdr->next_uid;
+	status_r->nonpermanent_modseqs = mail_index_is_in_memory(box->index);
+	if ((items & STATUS_HIGHESTMODSEQ) != 0) {
+		status_r->highest_modseq =
+			mail_index_modseq_get_highest(box->view);
+		if (status_r->highest_modseq == 0) {
+			/* modseqs not enabled yet, but we can't return 0 */
+			status_r->highest_modseq = 1;
+		}
+	}
+
+	if ((items & STATUS_FIRST_UNSEEN_SEQ) != 0) {
+		mail_index_lookup_first(box->view, 0, MAIL_SEEN,
+					&status_r->first_unseen_seq);
+	}
+
+	if ((items & STATUS_KEYWORDS) != 0)
+		status_r->keywords = mail_index_get_keywords(box->index);
+	return 0;
+}
+
 static void
-index_storage_get_status_cache_fields(struct mailbox *box,
-				      struct mailbox_status *status_r)
+get_metadata_cache_fields(struct mailbox *box,
+			  struct mailbox_metadata *metadata_r)
 {
 	const struct mail_cache_field *fields;
 	enum mail_cache_decision_type dec;
@@ -26,12 +71,12 @@
 		if (dec != MAIL_CACHE_DECISION_NO)
 			array_append(cache_fields, &fields[i].name, 1);
 	}
-	status_r->cache_fields = cache_fields;
+	metadata_r->cache_fields = cache_fields;
 }
 
-static void
-index_storage_virtual_size_add_new(struct mailbox *box,
-				   struct index_vsize_header *vsize_hdr)
+static int
+virtual_size_add_new(struct mailbox *box,
+		     struct index_vsize_header *vsize_hdr)
 {
 	struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box);
 	const struct mail_index_header *hdr;
@@ -94,18 +139,21 @@
 	mail_index_update_header_ext(trans->itrans, ibox->vsize_hdr_ext_id,
 				     0, vsize_hdr, sizeof(*vsize_hdr));
 	(void)mailbox_transaction_commit(&trans);
-
+	return ret;
 }
 
-static void
-index_storage_get_status_virtual_size(struct mailbox *box,
-				      struct mailbox_status *status_r)
+static int
+get_metadata_virtual_size(struct mailbox *box,
+			  struct mailbox_metadata *metadata_r)
 {
 	struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box);
 	struct index_vsize_header vsize_hdr;
+	struct mailbox_status status;
 	const void *data;
 	size_t size;
+	int ret;
 
+	mailbox_get_open_status(box, STATUS_MESSAGES | STATUS_UIDNEXT, &status);
 	mail_index_get_header_ext(box->view, ibox->vsize_hdr_ext_id,
 				  &data, &size);
 	if (size == sizeof(vsize_hdr))
@@ -119,61 +167,32 @@
 		memset(&vsize_hdr, 0, sizeof(vsize_hdr));
 	}
 
-	if (vsize_hdr.highest_uid + 1 == status_r->uidnext &&
-	    vsize_hdr.message_count == status_r->messages) {
+	if (vsize_hdr.highest_uid + 1 == status.uidnext &&
+	    vsize_hdr.message_count == status.messages) {
 		/* up to date */
-		status_r->virtual_size = vsize_hdr.vsize;
-		return;
+		metadata_r->virtual_size = vsize_hdr.vsize;
+		return 0;
 	}
-	if (vsize_hdr.highest_uid >= status_r->uidnext) {
+	if (vsize_hdr.highest_uid >= status.uidnext) {
 		mail_storage_set_critical(box->storage,
 			"vsize-hdr has invalid highest-uid (%u >= %u)",
-			vsize_hdr.highest_uid, status_r->uidnext);
+			vsize_hdr.highest_uid, status.uidnext);
 		memset(&vsize_hdr, 0, sizeof(vsize_hdr));
 	}
-	index_storage_virtual_size_add_new(box, &vsize_hdr);
-	status_r->virtual_size = vsize_hdr.vsize;
+	ret = virtual_size_add_new(box, &vsize_hdr);
+	metadata_r->virtual_size = vsize_hdr.vsize;
+	return ret;
 }
 
-void index_storage_get_status(struct mailbox *box,
-			      enum mailbox_status_items items,
-			      struct mailbox_status *status_r)
+int index_mailbox_get_metadata(struct mailbox *box,
+			       enum mailbox_metadata_items items,
+			       struct mailbox_metadata *metadata_r)
 {
-	const struct mail_index_header *hdr;
-
-	i_assert(box->opened);
-
-	memset(status_r, 0, sizeof(struct mailbox_status));
-
-	/* we can get most of the status items without any trouble */
-	hdr = mail_index_get_header(box->view);
-	status_r->messages = hdr->messages_count;
-	if ((items & STATUS_RECENT) != 0) {
-		status_r->recent = index_mailbox_get_recent_count(box);
-		i_assert(status_r->recent <= status_r->messages);
+	if ((items & MAILBOX_METADATA_CACHE_FIELDS) != 0)
+		get_metadata_cache_fields(box, metadata_r);
+	if ((items & MAILBOX_METADATA_VIRTUAL_SIZE) != 0) {
+		if (get_metadata_virtual_size(box, metadata_r) < 0)
+			return -1;
 	}
-	status_r->unseen = hdr->messages_count - hdr->seen_messages_count;
-	status_r->uidvalidity = hdr->uid_validity;
-	status_r->uidnext = hdr->next_uid;
-	status_r->nonpermanent_modseqs = mail_index_is_in_memory(box->index);
-	if ((items & STATUS_HIGHESTMODSEQ) != 0) {
-		status_r->highest_modseq =
-			mail_index_modseq_get_highest(box->view);
-		if (status_r->highest_modseq == 0) {
-			/* modseqs not enabled yet, but we can't return 0 */
-			status_r->highest_modseq = 1;
-		}
-	}
-
-	if ((items & STATUS_FIRST_UNSEEN_SEQ) != 0) {
-		mail_index_lookup_first(box->view, 0, MAIL_SEEN,
-					&status_r->first_unseen_seq);
-	}
-
-	if ((items & STATUS_KEYWORDS) != 0)
-		status_r->keywords = mail_index_get_keywords(box->index);
-	if ((items & STATUS_CACHE_FIELDS) != 0)
-		index_storage_get_status_cache_fields(box, status_r);
-	if ((items & STATUS_VIRTUAL_SIZE) != 0)
-		index_storage_get_status_virtual_size(box, status_r);
+	return 0;
 }
--- a/src/lib-storage/index/index-storage.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/index-storage.c	Sat Jan 01 15:59:02 2011 +0200
@@ -5,7 +5,6 @@
 #include "istream.h"
 #include "ioloop.h"
 #include "str.h"
-#include "imap-parser.h"
 #include "mkdir-parents.h"
 #include "mail-index-alloc-cache.h"
 #include "mail-index-private.h"
@@ -27,22 +26,6 @@
 struct index_storage_module index_storage_module =
 	MODULE_CONTEXT_INIT(&mail_storage_module_register);
 
-static struct mail_index *
-index_storage_alloc(struct mailbox_list *list, const char *name,
-		    enum mailbox_flags flags, const char *prefix)
-{
-	const char *index_dir, *mailbox_path;
-
-	mailbox_path = mailbox_list_get_path(list, name,
-					     MAILBOX_LIST_PATH_TYPE_MAILBOX);
-	index_dir = (flags & MAILBOX_FLAG_NO_INDEX_FILES) != 0 ? "" :
-		mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_INDEX);
-	if (*index_dir == '\0')
-		index_dir = NULL;
-
-	return mail_index_alloc_cache_get(mailbox_path, index_dir, prefix);
-}
-
 static void set_cache_decisions(const char *set, const char *fields,
 				enum mail_cache_decision_type dec)
 {
@@ -150,6 +133,23 @@
 	ibox->last_notify_type = MAILBOX_LOCK_NOTIFY_NONE;
 }
 
+static struct mail_index *
+index_mailbox_alloc_index(struct mailbox *box)
+{
+	const char *index_dir, *mailbox_path;
+
+	mailbox_path = mailbox_list_get_path(box->list, box->name,
+					     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	index_dir = (box->flags & MAILBOX_FLAG_NO_INDEX_FILES) != 0 ? "" :
+		mailbox_list_get_path(box->list, box->name,
+				      MAILBOX_LIST_PATH_TYPE_INDEX);
+	if (*index_dir == '\0')
+		index_dir = NULL;
+
+	return mail_index_alloc_cache_get(mailbox_path, index_dir,
+					  box->index_prefix);
+}
+
 int index_storage_mailbox_open(struct mailbox *box, bool move_to_memory)
 {
 	struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box);
@@ -162,19 +162,21 @@
 	if (move_to_memory)
 		ibox->index_flags &= ~MAIL_INDEX_OPEN_FLAG_CREATE;
 
-	if ((index_flags & MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY) != 0) {
-		if (mail_index_is_in_memory(box->index)) {
-			mail_storage_set_critical(box->storage,
-				"Couldn't create index file");
-			return -1;
-		}
-	}
-
 	if (mailbox_list_create_missing_index_dir(box->list, box->name) < 0) {
 		mail_storage_set_internal_error(box->storage);
 		return -1;
 	}
 
+	box->index = index_mailbox_alloc_index(box);
+	mail_index_set_fsync_mode(box->index,
+				  box->storage->set->parsed_fsync_mode, 0);
+	mail_index_set_lock_method(box->index,
+		box->storage->set->parsed_lock_method,
+		mail_storage_get_lock_timeout(box->storage, -1U));
+
+	/* make sure mail_index_set_permissions() has been called */
+	(void)mailbox_get_permissions(box);
+
 	ret = mail_index_open(box->index, index_flags);
 	if (ret <= 0 || move_to_memory) {
 		if ((index_flags & MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY) != 0) {
@@ -191,6 +193,13 @@
 				i_panic("in-memory index creation failed");
 		}
 	}
+	if ((index_flags & MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY) != 0) {
+		if (mail_index_is_in_memory(box->index)) {
+			mail_storage_set_critical(box->storage,
+				"Couldn't create index file");
+			return -1;
+		}
+	}
 
 	box->cache = mail_index_get_cache(box->index);
 	index_cache_register_defaults(box);
@@ -221,7 +230,6 @@
 				 const char *index_prefix)
 {
 	struct index_mailbox_context *ibox;
-	const char *path;
 	string_t *vname;
 
 	i_assert(name != NULL);
@@ -230,7 +238,9 @@
 	vname = t_str_new(128);
 	mail_namespace_get_vname(box->list->ns, vname, name);
 	box->vname = p_strdup(box->pool, str_c(vname));
+
 	box->flags = flags;
+	box->index_prefix = p_strdup(box->pool, index_prefix);
 
 	p_array_init(&box->search_results, box->pool, 16);
 	array_create(&box->module_contexts,
@@ -242,24 +252,10 @@
 	ibox->next_lock_notify = time(NULL) + LOCK_NOTIFY_INTERVAL;
 	MODULE_CONTEXT_SET(box, index_storage_module, ibox);
 
-	path = mailbox_list_get_path(box->list, name,
-				     MAILBOX_LIST_PATH_TYPE_MAILBOX);
-	box->path = p_strdup(box->pool, path);
-	box->index = index_storage_alloc(box->list, name, flags, index_prefix);
 	box->inbox_user = strcmp(name, "INBOX") == 0 &&
 		(box->list->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0;
 	box->inbox_any = strcmp(name, "INBOX") == 0 &&
 		(box->list->ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0;
-	if (box->file_create_mode == 0)
-		mailbox_refresh_permissions(box);
-	mail_index_set_permissions(box->index, box->file_create_mode,
-				   box->file_create_gid,
-				   box->file_create_gid_origin);
-	mail_index_set_fsync_mode(box->index,
-				  box->storage->set->parsed_fsync_mode, 0);
-	mail_index_set_lock_method(box->index,
-		box->storage->set->parsed_lock_method,
-		mail_storage_get_lock_timeout(box->storage, -1U));
 }
 
 int index_storage_mailbox_enable(struct mailbox *box,
@@ -301,7 +297,8 @@
 
 void index_storage_mailbox_free(struct mailbox *box)
 {
-	mail_index_alloc_cache_unref(&box->index);
+	if (box->index != NULL)
+		mail_index_alloc_cache_unref(&box->index);
 }
 
 void index_storage_mailbox_update_cache_fields(struct mailbox *box,
@@ -426,7 +423,7 @@
 
 int index_storage_mailbox_delete(struct mailbox *box)
 {
-	uint8_t mailbox_guid[MAIL_GUID_128_SIZE];
+	struct mailbox_metadata metadata;
 
 	if (!box->opened) {
 		/* \noselect mailbox, try deleting only the directory */
@@ -436,7 +433,7 @@
 	if (mailbox_mark_index_deleted(box, TRUE) < 0)
 		return -1;
 
-	if (mailbox_get_guid(box, mailbox_guid) < 0)
+	if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) < 0)
 		return -1;
 
 	/* Make sure the indexes are closed before trying to delete the
@@ -452,7 +449,7 @@
 	} 
 
 	mailbox_list_add_change(box->list, MAILBOX_LOG_RECORD_DELETE_MAILBOX,
-				mailbox_guid);
+				metadata.guid);
 	return index_storage_mailbox_delete_dir(box, TRUE);
 }
 
@@ -477,16 +474,7 @@
 
 bool index_storage_is_readonly(struct mailbox *box)
 {
-	if ((box->flags & MAILBOX_FLAG_READONLY) != 0)
-		return TRUE;
-
-	if (box->backend_readonly) {
-		/* return read-only only if there are no private flags
-		   (that are stored in index files) */
-		if (box->private_flags_mask == 0)
-			return TRUE;
-	}
-	return FALSE;
+	return (box->flags & MAILBOX_FLAG_READONLY) != 0;
 }
 
 bool index_storage_allow_new_keywords(struct mailbox *box)
@@ -500,99 +488,6 @@
 	return mail_index_view_is_inconsistent(box->view);
 }
 
-bool index_keyword_is_valid(struct mailbox *box, const char *keyword,
-			    const char **error_r)
-{
-	unsigned int i, idx;
-
-	/* if it already exists, skip validity checks */
-	if (mail_index_keyword_lookup(box->index, keyword, &idx))
-		return TRUE;
-
-	if (*keyword == '\0') {
-		*error_r = "Empty keywords not allowed";
-		return FALSE;
-	}
-
-	/* these are IMAP-specific restrictions, but for now IMAP is all we
-	   care about */
-	for (i = 0; keyword[i] != '\0'; i++) {
-		if (IS_ATOM_SPECIAL((unsigned char)keyword[i])) {
-			*error_r = "Invalid characters in keyword";
-			return FALSE;
-		}
-		if ((unsigned char)keyword[i] >= 0x80) {
-			*error_r = "8bit characters in keyword";
-			return FALSE;
-		}
-	}
-	if (i > box->storage->set->mail_max_keyword_length) {
-		*error_r = "Keyword length too long";
-		return FALSE;
-	}
-	return TRUE;
-}
-
-static struct mail_keywords *
-index_keywords_create_skip(struct mailbox *box,
-			   const char *const keywords[])
-{
-	ARRAY_DEFINE(valid_keywords, const char *);
-	const char *error;
-
-	t_array_init(&valid_keywords, 32);
-	for (; *keywords != NULL; keywords++) {
-		if (mailbox_keyword_is_valid(box, *keywords, &error))
-			array_append(&valid_keywords, keywords, 1);
-	}
-	(void)array_append_space(&valid_keywords); /* NULL-terminate */
-	return mail_index_keywords_create(box->index, keywords);
-}
-
-int index_keywords_create(struct mailbox *box, const char *const keywords[],
-			  struct mail_keywords **keywords_r, bool skip_invalid)
-{
-	const char *error;
-	unsigned int i;
-
-	for (i = 0; keywords[i] != NULL; i++) {
-		if (mailbox_keyword_is_valid(box, keywords[i], &error))
-			continue;
-
-		if (!skip_invalid) {
-			mail_storage_set_error(box->storage,
-					       MAIL_ERROR_PARAMS, error);
-			return -1;
-		}
-
-		/* found invalid keywords, do this the slow way */
-		T_BEGIN {
-			*keywords_r = index_keywords_create_skip(box, keywords);
-		} T_END;
-		return 0;
-	}
-
-	*keywords_r = mail_index_keywords_create(box->index, keywords);
-	return 0;
-}
-
-struct mail_keywords *
-index_keywords_create_from_indexes(struct mailbox *_box,
-				   const ARRAY_TYPE(keyword_indexes) *idx)
-{
-	return mail_index_keywords_create_from_indexes(_box->index, idx);
-}
-
-void index_keywords_ref(struct mail_keywords *keywords)
-{
-	mail_index_keywords_ref(keywords);
-}
-
-void index_keywords_unref(struct mail_keywords *keywords)
-{
-	mail_index_keywords_unref(&keywords);
-}
-
 void index_save_context_free(struct mail_save_context *ctx)
 {
 	i_free_and_null(ctx->from_envelope);
@@ -606,10 +501,6 @@
 		      uint32_t dest_seq, const char *name, buffer_t *buf)
 {
 	struct mailbox_transaction_context *dest_trans = ctx->transaction;
-	struct index_transaction_context *dest_itrans =
-		(struct index_transaction_context *)dest_trans;
-	struct index_transaction_context *src_itrans =
-		(struct index_transaction_context *)src_mail->transaction;
 	const struct mail_cache_field *dest_field;
 	unsigned int src_field_idx, dest_field_idx;
 
@@ -630,9 +521,9 @@
 	}
 
 	buffer_set_used_size(buf, 0);
-	if (mail_cache_lookup_field(src_itrans->cache_view, buf,
+	if (mail_cache_lookup_field(src_mail->transaction->cache_view, buf,
 				    src_mail->seq, src_field_idx) > 0) {
-		mail_cache_add(dest_itrans->cache_trans, dest_seq,
+		mail_cache_add(dest_trans->cache_trans, dest_seq,
 			       dest_field_idx, buf->data, buf->used);
 	}
 }
@@ -641,15 +532,17 @@
 			     struct mail *src_mail, uint32_t dest_seq)
 {
 	T_BEGIN {
-		struct mailbox_status src_status;
+		struct mailbox_metadata src_metadata;
 		const char *const *namep;
 		buffer_t *buf;
 
-		index_storage_get_status(src_mail->box, STATUS_CACHE_FIELDS,
-					 &src_status);
+		if (mailbox_get_metadata(src_mail->box,
+					 MAILBOX_METADATA_CACHE_FIELDS,
+					 &src_metadata) < 0)
+			i_unreached();
 
 		buf = buffer_create_dynamic(pool_datastack_create(), 1024);
-		array_foreach(src_status.cache_fields, namep) {
+		array_foreach(src_metadata.cache_fields, namep) {
 			mail_copy_cache_field(ctx, src_mail, dest_seq,
 					      *namep, buf);
 		}
--- a/src/lib-storage/index/index-storage.h	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/index-storage.h	Sat Jan 01 15:59:02 2011 +0200
@@ -16,17 +16,6 @@
 	MAILBOX_LOCK_NOTIFY_MAILBOX_OVERRIDE
 };
 
-struct index_transaction_context {
-	struct mailbox_transaction_context mailbox_ctx;
-	union mail_index_transaction_module_context module_ctx;
-
-	struct mail_index_transaction_vfuncs super;
-	int mail_ref_count;
-
-	struct mail_cache_view *cache_view;
-	struct mail_cache_transaction_ctx *cache_trans;
-};
-
 struct index_vsize_header {
 	uint64_t vsize;
 	uint32_t highest_uid;
@@ -92,16 +81,6 @@
 bool index_storage_allow_new_keywords(struct mailbox *box);
 bool index_storage_is_inconsistent(struct mailbox *box);
 
-int index_keywords_create(struct mailbox *box, const char *const keywords[],
-			  struct mail_keywords **keywords_r, bool skip_invalid);
-struct mail_keywords *
-index_keywords_create_from_indexes(struct mailbox *box,
-				   const ARRAY_TYPE(keyword_indexes) *idx);
-void index_keywords_ref(struct mail_keywords *keywords);
-void index_keywords_unref(struct mail_keywords *keywords);
-bool index_keyword_is_valid(struct mailbox *box, const char *keyword,
-			    const char **error_r);
-
 void index_mailbox_set_recent_uid(struct mailbox *box, uint32_t uid);
 void index_mailbox_set_recent_seq(struct mailbox *box,
 				  struct mail_index_view *view,
@@ -126,22 +105,12 @@
 
 int index_storage_sync(struct mailbox *box, enum mailbox_sync_flags flags);
 enum mailbox_sync_type index_sync_type_convert(enum mail_index_sync_type type);
-void index_storage_get_status(struct mailbox *box,
-			      enum mailbox_status_items items,
-			      struct mailbox_status *status_r);
-void index_storage_get_seq_range(struct mailbox *box,
-				 uint32_t uid1, uint32_t uid2,
-				 uint32_t *seq1_r, uint32_t *seq2_r);
-void index_storage_get_uid_range(struct mailbox *box,
-				 const ARRAY_TYPE(seq_range) *seqs,
-				 ARRAY_TYPE(seq_range) *uids);
-bool index_storage_get_expunges(struct mailbox *box, uint64_t prev_modseq,
-				const ARRAY_TYPE(seq_range) *uids_filter,
-				ARRAY_TYPE(mailbox_expunge_rec) *expunges);
-
-struct mailbox_header_lookup_ctx *
-index_header_lookup_init(struct mailbox *box, const char *const headers[]);
-void index_header_lookup_deinit(struct mailbox_header_lookup_ctx *ctx);
+int index_storage_get_status(struct mailbox *box,
+			     enum mailbox_status_items items,
+			     struct mailbox_status *status_r);
+int index_mailbox_get_metadata(struct mailbox *box,
+			       enum mailbox_metadata_items items,
+			       struct mailbox_metadata *metadata_r);
 
 struct mail_search_context *
 index_storage_search_init(struct mailbox_transaction_context *t,
@@ -152,14 +121,10 @@
 					struct mail *mail, bool *tryagain_r);
 bool index_storage_search_next_update_seq(struct mail_search_context *ctx);
 
-void index_transaction_set_max_modseq(struct mailbox_transaction_context *_t,
-				      uint64_t max_modseq,
-				      ARRAY_TYPE(seq_range) *seqs);
-
 struct mailbox_transaction_context *
 index_transaction_begin(struct mailbox *box,
 			enum mailbox_transaction_flags flags);
-void index_transaction_init(struct index_transaction_context *it,
+void index_transaction_init(struct mailbox_transaction_context *t,
 			    struct mailbox *box,
 			    enum mailbox_transaction_flags flags);
 int index_transaction_commit(struct mailbox_transaction_context *t,
--- a/src/lib-storage/index/index-transaction.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/index-transaction.c	Sat Jan 01 15:59:02 2011 +0200
@@ -5,11 +5,11 @@
 #include "index-storage.h"
 #include "index-mail.h"
 
-static void index_transaction_free(struct index_transaction_context *t)
+static void index_transaction_free(struct mailbox_transaction_context *t)
 {
 	mail_cache_view_close(t->cache_view);
-	mail_index_view_close(&t->mailbox_ctx.view);
-	array_free(&t->mailbox_ctx.module_contexts);
+	mail_index_view_close(&t->view);
+	array_free(&t->module_contexts);
 	i_free(t);
 }
 
@@ -17,9 +17,8 @@
 index_transaction_index_commit(struct mail_index_transaction *index_trans,
 			       struct mail_index_transaction_commit_result *result_r)
 {
-	struct index_transaction_context *it =
+	struct mailbox_transaction_context *t =
 		MAIL_STORAGE_CONTEXT(index_trans);
-	struct mailbox_transaction_context *t = &it->mailbox_ctx;
 	struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(t->box);
 	int ret = 0;
 
@@ -30,11 +29,11 @@
 		}
 	}
 
-	i_assert(it->mail_ref_count == 0);
+	i_assert(t->mail_ref_count == 0);
 	if (ret < 0)
-		it->super.rollback(index_trans);
+		t->super.rollback(index_trans);
 	else {
-		if (it->super.commit(index_trans, result_r) < 0) {
+		if (t->super.commit(index_trans, result_r) < 0) {
 			mail_storage_set_index_error(t->box);
 			ret = -1;
 		}
@@ -43,29 +42,30 @@
 	if (t->save_ctx != NULL)
 		ibox->save_commit_post(t->save_ctx, result_r);
 
-	index_transaction_free(it);
+	index_transaction_free(t);
 	return ret;
 }
 
-static void index_transaction_index_rollback(struct mail_index_transaction *t)
+static void
+index_transaction_index_rollback(struct mail_index_transaction *index_trans)
 {
-	struct index_transaction_context *it = MAIL_STORAGE_CONTEXT(t);
+	struct mailbox_transaction_context *t =
+		MAIL_STORAGE_CONTEXT(index_trans);
 	struct index_mailbox_context *ibox =
-		INDEX_STORAGE_CONTEXT(it->mailbox_ctx.box);
+		INDEX_STORAGE_CONTEXT(t->box);
 
-	if (it->mailbox_ctx.save_ctx != NULL)
-		ibox->save_rollback(it->mailbox_ctx.save_ctx);
+	if (t->save_ctx != NULL)
+		ibox->save_rollback(t->save_ctx);
 
-	i_assert(it->mail_ref_count == 0);
-	it->super.rollback(t);
-	index_transaction_free(it);
+	i_assert(t->mail_ref_count == 0);
+	t->super.rollback(index_trans);
+	index_transaction_free(t);
 }
 
-void index_transaction_init(struct index_transaction_context *it,
+void index_transaction_init(struct mailbox_transaction_context *t,
 			    struct mailbox *box,
 			    enum mailbox_transaction_flags flags)
 {
-	struct mailbox_transaction_context *t = &it->mailbox_ctx;
 	enum mail_index_transaction_flags trans_flags;
 
 	i_assert(box->opened);
@@ -85,26 +85,26 @@
 	array_create(&t->module_contexts, default_pool,
 		     sizeof(void *), 5);
 
-	it->cache_view = mail_cache_view_open(box->cache, t->view);
-	it->cache_trans = mail_cache_get_transaction(it->cache_view, t->itrans);
+	t->cache_view = mail_cache_view_open(box->cache, t->view);
+	t->cache_trans = mail_cache_get_transaction(t->cache_view, t->itrans);
 
 	/* set up after mail_cache_get_transaction(), so that we'll still
 	   have the cache_trans available in _index_commit() */
-	it->super = t->itrans->v;
+	t->super = t->itrans->v;
 	t->itrans->v.commit = index_transaction_index_commit;
 	t->itrans->v.rollback = index_transaction_index_rollback;
-	MODULE_CONTEXT_SET(t->itrans, mail_storage_mail_index_module, it);
+	MODULE_CONTEXT_SET(t->itrans, mail_storage_mail_index_module, t);
 }
 
 struct mailbox_transaction_context *
 index_transaction_begin(struct mailbox *box,
 			enum mailbox_transaction_flags flags)
 {
-	struct index_transaction_context *it;
+	struct mailbox_transaction_context *t;
 
-	it = i_new(struct index_transaction_context, 1);
-	index_transaction_init(it, box, flags);
-	return &it->mailbox_ctx;
+	t = i_new(struct mailbox_transaction_context, 1);
+	index_transaction_init(t, box, flags);
+	return t;
 }
 
 int index_transaction_commit(struct mailbox_transaction_context *t,
@@ -137,10 +137,3 @@
 
 	mail_index_transaction_rollback(&itrans);
 }
-
-void index_transaction_set_max_modseq(struct mailbox_transaction_context *t,
-				      uint64_t max_modseq,
-				      ARRAY_TYPE(seq_range) *seqs)
-{
-	mail_index_transaction_set_max_modseq(t->itrans, max_modseq, seqs);
-}
--- a/src/lib-storage/index/maildir/Makefile.am	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/maildir/Makefile.am	Sat Jan 01 15:59:02 2011 +0200
@@ -12,6 +12,7 @@
 libstorage_maildir_la_SOURCES = \
 	maildir-copy.c \
 	maildir-filename.c \
+	maildir-filename-flags.c \
 	maildir-keywords.c \
 	maildir-mail.c \
 	maildir-save.c \
@@ -24,6 +25,7 @@
 
 headers = \
 	maildir-filename.h \
+	maildir-filename-flags.h \
 	maildir-keywords.h \
 	maildir-storage.h \
 	maildir-settings.h \
--- a/src/lib-storage/index/maildir/maildir-copy.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/maildir/maildir-copy.c	Sat Jan 01 15:59:02 2011 +0200
@@ -83,7 +83,8 @@
 	dest_fname = maildir_filename_generate();
 	memset(&do_ctx, 0, sizeof(do_ctx));
 	do_ctx.dest_path =
-		t_strdup_printf("%s/tmp/%s", dest_mbox->box.path, dest_fname);
+		t_strdup_printf("%s/tmp/%s", mailbox_get_path(&dest_mbox->box),
+				dest_fname);
 	if (src_mbox != NULL) {
 		/* maildir */
 		if (maildir_file_do(src_mbox, mail->uid,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/index/maildir/maildir-filename-flags.c	Sat Jan 01 15:59:02 2011 +0200
@@ -0,0 +1,172 @@
+/* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "maildir-storage.h"
+#include "maildir-keywords.h"
+#include "maildir-filename-flags.h"
+
+#include <stdlib.h>
+
+void maildir_filename_flags_get(struct maildir_keywords_sync_ctx *ctx,
+				const char *fname, enum mail_flags *flags_r,
+				ARRAY_TYPE(keyword_indexes) *keywords_r)
+{
+	const char *info;
+
+	array_clear(keywords_r);
+	*flags_r = 0;
+
+	info = strrchr(fname, MAILDIR_INFO_SEP);
+	if (info == NULL || info[1] != '2' || info[2] != MAILDIR_FLAGS_SEP)
+		return;
+
+	for (info += 3; *info != '\0' && *info != MAILDIR_FLAGS_SEP; info++) {
+		switch (*info) {
+		case 'R': /* replied */
+			*flags_r |= MAIL_ANSWERED;
+			break;
+		case 'S': /* seen */
+			*flags_r |= MAIL_SEEN;
+			break;
+		case 'T': /* trashed */
+			*flags_r |= MAIL_DELETED;
+			break;
+		case 'D': /* draft */
+			*flags_r |= MAIL_DRAFT;
+			break;
+		case 'F': /* flagged */
+			*flags_r |= MAIL_FLAGGED;
+			break;
+		default:
+			if (*info >= MAILDIR_KEYWORD_FIRST &&
+			    *info <= MAILDIR_KEYWORD_LAST) {
+				int idx;
+
+				idx = maildir_keywords_char_idx(ctx, *info);
+				if (idx < 0) {
+					/* unknown keyword. */
+					break;
+				}
+
+				array_append(keywords_r,
+					     (unsigned int *)&idx, 1);
+				break;
+			}
+
+			/* unknown flag - ignore */
+			break;
+		}
+	}
+}
+
+static int char_cmp(const void *p1, const void *p2)
+{
+	const unsigned char *c1 = p1, *c2 = p2;
+
+	return *c1 - *c2;
+}
+
+static void
+maildir_filename_append_keywords(struct maildir_keywords_sync_ctx *ctx,
+				 ARRAY_TYPE(keyword_indexes) *keywords,
+				 string_t *fname)
+{
+	const unsigned int *indexes;
+	unsigned int i, count, start = str_len(fname);
+	char chr;
+
+	indexes = array_get(keywords, &count);
+	for (i = 0; i < count; i++) {
+		chr = maildir_keywords_idx_char(ctx, indexes[i]);
+		if (chr != '\0')
+			str_append_c(fname, chr);
+	}
+
+	qsort(str_c_modifiable(fname) + start, str_len(fname) - start, 1,
+	      char_cmp);
+}
+
+const char *maildir_filename_flags_set(struct maildir_keywords_sync_ctx *ctx,
+				       const char *fname, enum mail_flags flags,
+				       ARRAY_TYPE(keyword_indexes) *keywords)
+{
+	string_t *flags_str;
+	enum mail_flags flags_left;
+	const char *info, *oldflags;
+	int nextflag;
+
+	/* remove the old :info from file name, and get the old flags */
+	info = strrchr(fname, MAILDIR_INFO_SEP);
+	if (info != NULL && strrchr(fname, '/') > info)
+		info = NULL;
+
+	oldflags = "";
+	if (info != NULL) {
+		fname = t_strdup_until(fname, info);
+		if (info[1] == '2' && info[2] == MAILDIR_FLAGS_SEP)
+			oldflags = info+3;
+	}
+
+	/* insert the new flags between old flags. flags must be sorted by
+	   their ASCII code. unknown flags are kept. */
+	flags_str = t_str_new(256);
+	str_append(flags_str, fname);
+	str_append(flags_str, MAILDIR_FLAGS_FULL_SEP);
+	flags_left = flags;
+	for (;;) {
+		/* skip all known flags */
+		while (*oldflags == 'D' || *oldflags == 'F' ||
+		       *oldflags == 'R' || *oldflags == 'S' ||
+		       *oldflags == 'T' ||
+		       (*oldflags >= MAILDIR_KEYWORD_FIRST &&
+			*oldflags <= MAILDIR_KEYWORD_LAST))
+			oldflags++;
+
+		nextflag = *oldflags == '\0' || *oldflags == MAILDIR_FLAGS_SEP ?
+			256 : (unsigned char) *oldflags;
+
+		if ((flags_left & MAIL_DRAFT) && nextflag > 'D') {
+			str_append_c(flags_str, 'D');
+			flags_left &= ~MAIL_DRAFT;
+		}
+		if ((flags_left & MAIL_FLAGGED) && nextflag > 'F') {
+			str_append_c(flags_str, 'F');
+			flags_left &= ~MAIL_FLAGGED;
+		}
+		if ((flags_left & MAIL_ANSWERED) && nextflag > 'R') {
+			str_append_c(flags_str, 'R');
+			flags_left &= ~MAIL_ANSWERED;
+		}
+		if ((flags_left & MAIL_SEEN) && nextflag > 'S') {
+			str_append_c(flags_str, 'S');
+			flags_left &= ~MAIL_SEEN;
+		}
+		if ((flags_left & MAIL_DELETED) && nextflag > 'T') {
+			str_append_c(flags_str, 'T');
+			flags_left &= ~MAIL_DELETED;
+		}
+
+		if (keywords != NULL && array_is_created(keywords) &&
+		    nextflag > MAILDIR_KEYWORD_FIRST) {
+			maildir_filename_append_keywords(ctx, keywords,
+							 flags_str);
+			keywords = NULL;
+		}
+
+		if (*oldflags == '\0' || *oldflags == MAILDIR_FLAGS_SEP)
+			break;
+
+		str_append_c(flags_str, *oldflags);
+		oldflags++;
+	}
+
+	if (*oldflags == MAILDIR_FLAGS_SEP) {
+		/* another flagset, we don't know about these, just keep them */
+		while (*oldflags != '\0')
+			str_append_c(flags_str, *oldflags++);
+	}
+
+	return str_c(flags_str);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/index/maildir/maildir-filename-flags.h	Sat Jan 01 15:59:02 2011 +0200
@@ -0,0 +1,12 @@
+#ifndef MAILDIR_FILENAME_FLAGS_H
+#define MAILDIR_FILENAME_FLAGS_H
+
+void maildir_filename_flags_get(struct maildir_keywords_sync_ctx *ctx,
+			       const char *fname, enum mail_flags *flags_r,
+                               ARRAY_TYPE(keyword_indexes) *keywords_r);
+
+const char *maildir_filename_flags_set(struct maildir_keywords_sync_ctx *ctx,
+				       const char *fname, enum mail_flags flags,
+				       ARRAY_TYPE(keyword_indexes) *keywords);
+
+#endif
--- a/src/lib-storage/index/maildir/maildir-filename.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/maildir/maildir-filename.c	Sat Jan 01 15:59:02 2011 +0200
@@ -2,16 +2,11 @@
 
 #include "lib.h"
 #include "ioloop.h"
-#include "array.h"
-#include "str.h"
 #include "time-util.h"
 #include "hostpid.h"
 #include "maildir-storage.h"
-#include "maildir-keywords.h"
 #include "maildir-filename.h"
 
-#include <stdlib.h>
-
 const char *maildir_filename_generate(void)
 {
 	static struct timeval last_tv = { 0, 0 };
@@ -34,168 +29,6 @@
 			       my_pid, my_hostname);
 }
 
-void maildir_filename_get_flags(struct maildir_keywords_sync_ctx *ctx,
-				const char *fname, enum mail_flags *flags_r,
-				ARRAY_TYPE(keyword_indexes) *keywords_r)
-{
-	const char *info;
-
-	array_clear(keywords_r);
-	*flags_r = 0;
-
-	info = strrchr(fname, MAILDIR_INFO_SEP);
-	if (info == NULL || info[1] != '2' || info[2] != MAILDIR_FLAGS_SEP)
-		return;
-
-	for (info += 3; *info != '\0' && *info != MAILDIR_FLAGS_SEP; info++) {
-		switch (*info) {
-		case 'R': /* replied */
-			*flags_r |= MAIL_ANSWERED;
-			break;
-		case 'S': /* seen */
-			*flags_r |= MAIL_SEEN;
-			break;
-		case 'T': /* trashed */
-			*flags_r |= MAIL_DELETED;
-			break;
-		case 'D': /* draft */
-			*flags_r |= MAIL_DRAFT;
-			break;
-		case 'F': /* flagged */
-			*flags_r |= MAIL_FLAGGED;
-			break;
-		default:
-			if (*info >= MAILDIR_KEYWORD_FIRST &&
-			    *info <= MAILDIR_KEYWORD_LAST) {
-				int idx;
-
-				idx = maildir_keywords_char_idx(ctx, *info);
-				if (idx < 0) {
-					/* unknown keyword. */
-					break;
-				}
-
-				array_append(keywords_r,
-					     (unsigned int *)&idx, 1);
-				break;
-			}
-
-			/* unknown flag - ignore */
-			break;
-		}
-	}
-}
-
-static int char_cmp(const void *p1, const void *p2)
-{
-	const unsigned char *c1 = p1, *c2 = p2;
-
-	return *c1 - *c2;
-}
-
-static void
-maildir_filename_append_keywords(struct maildir_keywords_sync_ctx *ctx,
-				 ARRAY_TYPE(keyword_indexes) *keywords,
-				 string_t *fname)
-{
-	const unsigned int *indexes;
-	unsigned int i, count, start = str_len(fname);
-	char chr;
-
-	indexes = array_get(keywords, &count);
-	for (i = 0; i < count; i++) {
-		chr = maildir_keywords_idx_char(ctx, indexes[i]);
-		if (chr != '\0')
-			str_append_c(fname, chr);
-	}
-
-	qsort(str_c_modifiable(fname) + start, str_len(fname) - start, 1,
-	      char_cmp);
-}
-
-const char *maildir_filename_set_flags(struct maildir_keywords_sync_ctx *ctx,
-				       const char *fname, enum mail_flags flags,
-				       ARRAY_TYPE(keyword_indexes) *keywords)
-{
-	string_t *flags_str;
-	enum mail_flags flags_left;
-	const char *info, *oldflags;
-	int nextflag;
-
-	/* remove the old :info from file name, and get the old flags */
-	info = strrchr(fname, MAILDIR_INFO_SEP);
-	if (info != NULL && strrchr(fname, '/') > info)
-		info = NULL;
-
-	oldflags = "";
-	if (info != NULL) {
-		fname = t_strdup_until(fname, info);
-		if (info[1] == '2' && info[2] == MAILDIR_FLAGS_SEP)
-			oldflags = info+3;
-	}
-
-	/* insert the new flags between old flags. flags must be sorted by
-	   their ASCII code. unknown flags are kept. */
-	flags_str = t_str_new(256);
-	str_append(flags_str, fname);
-	str_append(flags_str, MAILDIR_FLAGS_FULL_SEP);
-	flags_left = flags;
-	for (;;) {
-		/* skip all known flags */
-		while (*oldflags == 'D' || *oldflags == 'F' ||
-		       *oldflags == 'R' || *oldflags == 'S' ||
-		       *oldflags == 'T' ||
-		       (*oldflags >= MAILDIR_KEYWORD_FIRST &&
-			*oldflags <= MAILDIR_KEYWORD_LAST))
-			oldflags++;
-
-		nextflag = *oldflags == '\0' || *oldflags == MAILDIR_FLAGS_SEP ?
-			256 : (unsigned char) *oldflags;
-
-		if ((flags_left & MAIL_DRAFT) && nextflag > 'D') {
-			str_append_c(flags_str, 'D');
-			flags_left &= ~MAIL_DRAFT;
-		}
-		if ((flags_left & MAIL_FLAGGED) && nextflag > 'F') {
-			str_append_c(flags_str, 'F');
-			flags_left &= ~MAIL_FLAGGED;
-		}
-		if ((flags_left & MAIL_ANSWERED) && nextflag > 'R') {
-			str_append_c(flags_str, 'R');
-			flags_left &= ~MAIL_ANSWERED;
-		}
-		if ((flags_left & MAIL_SEEN) && nextflag > 'S') {
-			str_append_c(flags_str, 'S');
-			flags_left &= ~MAIL_SEEN;
-		}
-		if ((flags_left & MAIL_DELETED) && nextflag > 'T') {
-			str_append_c(flags_str, 'T');
-			flags_left &= ~MAIL_DELETED;
-		}
-
-		if (keywords != NULL && array_is_created(keywords) &&
-		    nextflag > MAILDIR_KEYWORD_FIRST) {
-			maildir_filename_append_keywords(ctx, keywords,
-							 flags_str);
-			keywords = NULL;
-		}
-
-		if (*oldflags == '\0' || *oldflags == MAILDIR_FLAGS_SEP)
-			break;
-
-		str_append_c(flags_str, *oldflags);
-		oldflags++;
-	}
-
-	if (*oldflags == MAILDIR_FLAGS_SEP) {
-		/* another flagset, we don't know about these, just keep them */
-		while (*oldflags != '\0')
-			str_append_c(flags_str, *oldflags++);
-	}
-
-	return str_c(flags_str);
-}
-
 bool maildir_filename_get_size(const char *fname, char type, uoff_t *size_r)
 {
 	uoff_t size = 0;
--- a/src/lib-storage/index/maildir/maildir-filename.h	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/maildir/maildir-filename.h	Sat Jan 01 15:59:02 2011 +0200
@@ -5,14 +5,6 @@
 
 const char *maildir_filename_generate(void);
 
-void maildir_filename_get_flags(struct maildir_keywords_sync_ctx *ctx,
-			       const char *fname, enum mail_flags *flags_r,
-                               ARRAY_TYPE(keyword_indexes) *keywords_r);
-
-const char *maildir_filename_set_flags(struct maildir_keywords_sync_ctx *ctx,
-				       const char *fname, enum mail_flags flags,
-				       ARRAY_TYPE(keyword_indexes) *keywords);
-
 bool maildir_filename_get_size(const char *fname, char type, uoff_t *size_r);
 
 unsigned int maildir_filename_base_hash(const void *p);
--- a/src/lib-storage/index/maildir/maildir-keywords.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/maildir/maildir-keywords.c	Sat Jan 01 15:59:02 2011 +0200
@@ -290,6 +290,7 @@
 {
 	struct maildir_mailbox *mbox = mk->mbox;
 	struct mailbox *box = &mbox->box;
+	const struct mailbox_permissions *perm = mailbox_get_permissions(box);
 	const char *const *keywords;
 	unsigned int i, count;
 	string_t *str;
@@ -313,14 +314,14 @@
 		return -1;
 	}
 
-	if (st.st_gid != box->file_create_gid &&
-	    box->file_create_gid != (gid_t)-1) {
-		if (fchown(fd, (uid_t)-1, box->file_create_gid) < 0) {
+	if (st.st_gid != perm->file_create_gid &&
+	    perm->file_create_gid != (gid_t)-1) {
+		if (fchown(fd, (uid_t)-1, perm->file_create_gid) < 0) {
 			if (errno == EPERM) {
 				mail_storage_set_critical(mk->storage, "%s",
 					eperm_error_get_chgrp("fchown", path,
-						box->file_create_gid,
-						box->file_create_gid_origin));
+						perm->file_create_gid,
+						perm->file_create_gid_origin));
 			} else {
 				mail_storage_set_critical(mk->storage,
 					"fchown(%s) failed: %m", path);
@@ -353,6 +354,7 @@
 
 static int maildir_keywords_commit(struct maildir_keywords *mk)
 {
+	const struct mailbox_permissions *perm;
 	struct dotlock *dotlock;
 	const char *lock_path;
 	mode_t old_mask;
@@ -366,11 +368,12 @@
 	lock_path = t_strconcat(mk->path, ".lock", NULL);
 	(void)unlink(lock_path);
 
+	perm = mailbox_get_permissions(&mk->mbox->box);
 	for (i = 0;; i++) {
 		/* we could just create the temp file directly, but doing it
 		   this ways avoids potential problems with overwriting
 		   contents in malicious symlinks */
-		old_mask = umask(0777 & ~mk->mbox->box.file_create_mode);
+		old_mask = umask(0777 & ~perm->file_create_mode);
 		fd = file_dotlock_open(&mk->dotlock_settings, mk->path,
 				       DOTLOCK_CREATE_FLAG_NONBLOCK, &dotlock);
 		umask(old_mask);
--- a/src/lib-storage/index/maildir/maildir-mail.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/maildir/maildir-mail.c	Sat Jan 01 15:59:02 2011 +0200
@@ -188,7 +188,7 @@
 	bool exists;
 	int ret;
 
-	ret = maildir_uidlist_lookup(mbox->uidlist, mail->uid, &flags, fname_r);
+	ret = maildir_sync_lookup(mbox, mail->uid, &flags, fname_r);
 	if (ret != 0)
 		return ret;
 
@@ -588,8 +588,7 @@
 	if (field == MAIL_FETCH_VIRTUAL_SIZE) {
 		/* make sure it gets removed from uidlist.
 		   if it's in file name, we can't really do more than log it. */
-		ret = maildir_uidlist_lookup(mbox->uidlist, _mail->uid,
-					     &flags, &fname);
+		ret = maildir_sync_lookup(mbox, _mail->uid, &flags, &fname);
 		if (ret <= 0)
 			return;
 		if (maildir_filename_get_size(fname, MAILDIR_EXTRA_VIRTUAL_SIZE,
@@ -599,7 +598,7 @@
 				"new" : "cur";
 			mail_storage_set_critical(_mail->box->storage,
 				"Maildir filename has wrong W value: %s/%s/%s",
-				mbox->box.path, subdir, fname);
+				mailbox_get_path(&mbox->box), subdir, fname);
 		} else if (maildir_uidlist_lookup_ext(mbox->uidlist, _mail->uid,
 				MAILDIR_UIDLIST_REC_EXT_VSIZE) != NULL) {
 			maildir_uidlist_set_ext(mbox->uidlist, _mail->uid,
--- a/src/lib-storage/index/maildir/maildir-save.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/maildir/maildir-save.c	Sat Jan 01 15:59:02 2011 +0200
@@ -15,6 +15,7 @@
 #include "maildir-uidlist.h"
 #include "maildir-keywords.h"
 #include "maildir-filename.h"
+#include "maildir-filename-flags.h"
 #include "maildir-sync.h"
 
 #include <stdio.h>
@@ -116,6 +117,7 @@
 {
 	struct maildir_mailbox *mbox = (struct maildir_mailbox *)t->box;
 	struct maildir_save_context *ctx;
+	const char *path;
 	pool_t pool;
 
 	pool = pool_alloconly_create("maildir_save_context", 4096);
@@ -127,9 +129,10 @@
 	ctx->files_tail = &ctx->files;
 	ctx->fd = -1;
 
-	ctx->tmpdir = p_strconcat(pool, mbox->box.path, "/tmp", NULL);
-	ctx->newdir = p_strconcat(pool, mbox->box.path, "/new", NULL);
-	ctx->curdir = p_strconcat(pool, mbox->box.path, "/cur", NULL);
+	path = mailbox_get_path(&mbox->box);
+	ctx->tmpdir = p_strconcat(pool, path, "/tmp", NULL);
+	ctx->newdir = p_strconcat(pool, path, "/new", NULL);
+	ctx->curdir = p_strconcat(pool, path, "/cur", NULL);
 
 	buffer_create_const_data(&ctx->keywords_buffer, NULL, 0);
 	array_create_from_buffer(&ctx->keywords_array, &ctx->keywords_buffer,
@@ -268,7 +271,7 @@
 			return TRUE;
 		}
 
-		*fname_r = maildir_filename_set_flags(NULL, basename,
+		*fname_r = maildir_filename_flags_set(NULL, basename,
 					mf->flags & MAIL_FLAGS_MASK, NULL);
 		return FALSE;
 	}
@@ -276,7 +279,7 @@
 	i_assert(ctx->keywords_sync_ctx != NULL || mf->keywords_count == 0);
 	buffer_create_const_data(&ctx->keywords_buffer, mf + 1,
 				 mf->keywords_count * sizeof(unsigned int));
-	*fname_r = maildir_filename_set_flags(ctx->keywords_sync_ctx, basename,
+	*fname_r = maildir_filename_flags_set(ctx->keywords_sync_ctx, basename,
 					      mf->flags & MAIL_FLAGS_MASK,
 					      &ctx->keywords_array);
 	return FALSE;
@@ -322,6 +325,7 @@
 			      const char **fname_r)
 {
 	struct mailbox *box = &mbox->box;
+	const struct mailbox_permissions *perm = mailbox_get_permissions(box);
 	unsigned int prefix_len;
 	const char *tmp_fname;
 	string_t *path;
@@ -342,7 +346,7 @@
 		   might return an existing filename is if the time moved
 		   backwards. so we'll use O_EXCL anyway, although it's mostly
 		   useless. */
-		old_mask = umask(0777 & ~box->file_create_mode);
+		old_mask = umask(0777 & ~perm->file_create_mode);
 		fd = open(str_c(path),
 			  O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0777);
 		umask(old_mask);
@@ -357,14 +361,14 @@
 			mail_storage_set_critical(box->storage,
 				"open(%s) failed: %m", str_c(path));
 		}
-	} else if (box->file_create_gid != (gid_t)-1) {
-		if (fchown(fd, (uid_t)-1, box->file_create_gid) < 0) {
+	} else if (perm->file_create_gid != (gid_t)-1) {
+		if (fchown(fd, (uid_t)-1, perm->file_create_gid) < 0) {
 			if (errno == EPERM) {
 				mail_storage_set_critical(box->storage, "%s",
 					eperm_error_get_chgrp("fchown",
 						str_c(path),
-						box->file_create_gid,
-						box->file_create_gid_origin));
+						perm->file_create_gid,
+						perm->file_create_gid_origin));
 			} else {
 				mail_storage_set_critical(box->storage,
 					"fchown(%s) failed: %m", str_c(path));
@@ -490,14 +494,12 @@
 
 static void maildir_save_remove_last_filename(struct maildir_save_context *ctx)
 {
-	struct index_transaction_context *t =
-		(struct index_transaction_context *)ctx->ctx.transaction;
 	struct maildir_filename **fm;
 
 	mail_index_expunge(ctx->trans, ctx->seq);
 	/* currently we can't just drop pending cache updates for this one
 	   specific record, so we'll reset the whole cache transaction. */
-	mail_cache_transaction_reset(t->cache_trans);
+	mail_cache_transaction_reset(ctx->ctx.transaction->cache_trans);
 	ctx->seq--;
 
 	for (fm = &ctx->files; (*fm)->next != NULL; fm = &(*fm)->next) ;
@@ -763,8 +765,6 @@
 static void
 maildir_save_rollback_index_changes(struct maildir_save_context *ctx)
 {
-	struct index_transaction_context *t =
-		(struct index_transaction_context *)ctx->ctx.transaction;
 	uint32_t seq;
 
 	if (ctx->seq == 0)
@@ -773,7 +773,7 @@
 	for (seq = ctx->seq; seq >= ctx->first_seq; seq--)
 		mail_index_expunge(ctx->trans, seq);
 
-	mail_cache_transaction_reset(t->cache_trans);
+	mail_cache_transaction_reset(ctx->ctx.transaction->cache_trans);
 }
 
 static bool maildir_filename_has_conflict(struct maildir_filename *mf,
--- a/src/lib-storage/index/maildir/maildir-storage.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/maildir/maildir-storage.c	Sat Jan 01 15:59:02 2011 +0200
@@ -233,16 +233,18 @@
 /* create or fix maildir, ignore if it already exists */
 static int create_maildir(struct mailbox *box, bool verify)
 {
+	const struct mailbox_permissions *perm = mailbox_get_permissions(box);
 	const char *path;
 	unsigned int i;
 	enum mail_error error;
 	int ret = 0;
 
 	for (i = 0; i < N_ELEMENTS(maildir_subdirs); i++) {
-		path = t_strconcat(box->path, "/", maildir_subdirs[i], NULL);
+		path = t_strconcat(mailbox_get_path(box), "/",
+				   maildir_subdirs[i], NULL);
 		if (mkdir_verify(box->storage, box->list->ns, path,
-				 box->dir_create_mode, box->file_create_gid,
-				 box->file_create_gid_origin, verify) < 0) {
+				 perm->dir_create_mode, perm->file_create_gid,
+				 perm->file_create_gid_origin, verify) < 0) {
 			(void)mail_storage_get_last_error(box->storage, &error);
 			if (error != MAIL_ERROR_EXISTS)
 				return -1;
@@ -284,25 +286,16 @@
 	ibox->save_rollback = maildir_transaction_save_rollback;
 
 	mbox->storage = (struct maildir_storage *)storage;
-	mbox->maildir_ext_id =
-		mail_index_ext_register(mbox->box.index, "maildir",
-					sizeof(mbox->maildir_hdr), 0, 0);
 	return &mbox->box;
 }
 
 static int maildir_mailbox_open_existing(struct mailbox *box)
 {
 	struct maildir_mailbox *mbox = (struct maildir_mailbox *)box;
-	struct stat st;
-	const char *shared_path;
 
 	mbox->uidlist = maildir_uidlist_init(mbox);
 	mbox->keywords = maildir_keywords_init(mbox);
 
-	shared_path = t_strconcat(box->path, "/dovecot-shared", NULL);
-	if (stat(shared_path, &st) == 0)
-		box->private_flags_mask = MAIL_SEEN;
-
 	if ((box->flags & MAILBOX_FLAG_KEEP_LOCKED) != 0) {
 		if (maildir_uidlist_lock(mbox->uidlist) <= 0)
 			return -1;
@@ -311,21 +304,41 @@
 						 mbox);
 	}
 
-	if (access(t_strconcat(box->path, "/cur", NULL), W_OK) < 0 &&
-	    errno == EACCES)
-		mbox->box.backend_readonly = TRUE;
-	return index_storage_mailbox_open(box, FALSE);
+	if (index_storage_mailbox_open(box, FALSE) < 0)
+		return -1;
+
+	mbox->maildir_ext_id =
+		mail_index_ext_register(mbox->box.index, "maildir",
+					sizeof(mbox->maildir_hdr), 0, 0);
+	return 0;
+}
+
+static bool maildir_storage_is_readonly(struct mailbox *box)
+{
+	struct maildir_mailbox *mbox = (struct maildir_mailbox *)box;
+
+	if (index_storage_is_readonly(box))
+		return TRUE;
+
+	if (maildir_is_backend_readonly(mbox)) {
+		/* return read-only only if there are no private flags
+		   (that are stored in index files) */
+		if (mailbox_get_private_flags_mask(box) == 0)
+			return TRUE;
+	}
+	return FALSE;
 }
 
 static int maildir_mailbox_open(struct mailbox *box)
 {
+	const char *box_path = mailbox_get_path(box);
 	const char *root_dir;
 	struct stat st;
 	int ret;
 
 	/* begin by checking if tmp/ directory exists and if it should be
 	   cleaned up. */
-	ret = maildir_check_tmp(box->storage, box->path);
+	ret = maildir_check_tmp(box->storage, box_path);
 	if (ret > 0) {
 		/* exists */
 		return maildir_mailbox_open_existing(box);
@@ -336,10 +349,10 @@
 	/* tmp/ directory doesn't exist. does the maildir? */
 	root_dir = mailbox_list_get_path(box->list, NULL,
 					 MAILBOX_LIST_PATH_TYPE_MAILBOX);
-	if (strcmp(box->path, root_dir) == 0) {
+	if (strcmp(box_path, root_dir) == 0) {
 		/* root directory. either INBOX or some other namespace root */
 		errno = ENOENT;
-	} else if (stat(box->path, &st) == 0) {
+	} else if (stat(box_path, &st) == 0) {
 		/* yes, we'll need to create the missing dirs */
 		if (create_maildir(box, TRUE) < 0)
 			return -1;
@@ -353,20 +366,21 @@
 		return -1;
 	} else {
 		mail_storage_set_critical(box->storage,
-					  "stat(%s) failed: %m", box->path);
+					  "stat(%s) failed: %m", box_path);
 		return -1;
 	}
 }
 
 static int maildir_create_shared(struct mailbox *box)
 {
+	const struct mailbox_permissions *perm = mailbox_get_permissions(box);
 	const char *path;
 	mode_t old_mask;
 	int fd;
 
 	old_mask = umask(0);
-	path = t_strconcat(box->path, "/dovecot-shared", NULL);
-	fd = open(path, O_WRONLY | O_CREAT, box->file_create_mode);
+	path = t_strconcat(mailbox_get_path(box), "/dovecot-shared", NULL);
+	fd = open(path, O_WRONLY | O_CREAT, perm->file_create_mode);
 	umask(old_mask);
 
 	if (fd == -1) {
@@ -375,12 +389,12 @@
 		return -1;
 	}
 
-	if (fchown(fd, (uid_t)-1, box->file_create_gid) < 0) {
+	if (fchown(fd, (uid_t)-1, perm->file_create_gid) < 0) {
 		if (errno == EPERM) {
 			mail_storage_set_critical(box->storage, "%s",
 				eperm_error_get_chgrp("fchown", path,
-					box->file_create_gid,
-					box->file_create_gid_origin));
+					perm->file_create_gid,
+					perm->file_create_gid_origin));
 		} else {
 			mail_storage_set_critical(box->storage,
 				"fchown(%s) failed: %m", path);
@@ -433,7 +447,7 @@
 	    (box->list->props & MAILBOX_LIST_PROP_NO_NOSELECT) == 0)
 		return 0;
 
-	ret = maildir_check_tmp(box->storage, box->path);
+	ret = maildir_check_tmp(box->storage, mailbox_get_path(box));
 	if (ret > 0) {
 		mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS,
 				       "Mailbox already exists");
@@ -459,11 +473,18 @@
 }
 
 static int
-maildir_mailbox_get_guid(struct mailbox *box, uint8_t guid[MAIL_GUID_128_SIZE])
+maildir_mailbox_get_metadata(struct mailbox *box,
+			     enum mailbox_metadata_items items,
+			     struct mailbox_metadata *metadata_r)
 {
 	struct maildir_mailbox *mbox = (struct maildir_mailbox *)box;
 
-	return maildir_uidlist_get_mailbox_guid(mbox->uidlist, guid);
+	if ((items & MAILBOX_METADATA_GUID) != 0) {
+		if (maildir_uidlist_get_mailbox_guid(mbox->uidlist,
+						     metadata_r->guid) < 0)
+			return -1;
+	}
+	return index_mailbox_get_metadata(box, items, metadata_r);
 }
 
 static void maildir_mailbox_close(struct mailbox *box)
@@ -486,14 +507,15 @@
 static void maildir_notify_changes(struct mailbox *box)
 {
 	struct maildir_mailbox *mbox = (struct maildir_mailbox *)box;
+	const char *box_path = mailbox_get_path(box);
 
 	if (box->notify_callback == NULL)
 		index_mailbox_check_remove_all(&mbox->box);
 	else {
 		index_mailbox_check_add(&mbox->box,
-			t_strconcat(mbox->box.path, "/new", NULL));
+			t_strconcat(box_path, "/new", NULL));
 		index_mailbox_check_add(&mbox->box,
-			t_strconcat(mbox->box.path, "/cur", NULL));
+			t_strconcat(box_path, "/cur", NULL));
 	}
 }
 
@@ -570,6 +592,35 @@
 	return mailbox_uidvalidity_next(list, path);
 }
 
+static enum mail_flags maildir_get_private_flags_mask(struct mailbox *box)
+{
+	struct maildir_mailbox *mbox = (struct maildir_mailbox *)box;
+	const char *path;
+	struct stat st;
+
+	if (!mbox->private_flags_mask_set) {
+		path = t_strconcat(mailbox_get_path(box), "/dovecot-shared", NULL);
+		if (stat(path, &st) < 0)
+			mbox->_private_flags_mask = 0;
+		else
+			mbox->_private_flags_mask = MAIL_SEEN;
+	}
+	return mbox->_private_flags_mask;
+}
+
+bool maildir_is_backend_readonly(struct maildir_mailbox *mbox)
+{
+	if (!mbox->backend_readonly_set) {
+		const char *box_path = mailbox_get_path(&mbox->box);
+
+		mbox->backend_readonly_set = TRUE;
+		if (access(t_strconcat(box_path, "/cur", NULL), W_OK) < 0 &&
+		    errno == EACCES)
+			mbox->backend_readonly = TRUE;
+	}
+	return mbox->backend_readonly;
+}
+
 struct mail_storage maildir_storage = {
 	.name = MAILDIR_STORAGE_NAME,
 	.class_flags = 0,
@@ -589,7 +640,7 @@
 
 struct mailbox maildir_mailbox = {
 	.v = {
-		index_storage_is_readonly,
+		maildir_storage_is_readonly,
 		index_storage_allow_new_keywords,
 		index_storage_mailbox_enable,
 		maildir_mailbox_open,
@@ -600,7 +651,7 @@
 		index_storage_mailbox_delete,
 		index_storage_mailbox_rename,
 		index_storage_get_status,
-		maildir_mailbox_get_guid,
+		maildir_mailbox_get_metadata,
 		maildir_list_index_has_changed,
 		maildir_list_index_update_sync,
 		maildir_storage_sync_init,
@@ -611,21 +662,8 @@
 		index_transaction_begin,
 		index_transaction_commit,
 		index_transaction_rollback,
-		index_transaction_set_max_modseq,
-		index_keywords_create,
-		index_keywords_create_from_indexes,
-		index_keywords_ref,
-		index_keywords_unref,
-		index_keyword_is_valid,
-		index_storage_get_seq_range,
-		index_storage_get_uid_range,
-		index_storage_get_expunges,
-		NULL,
-		NULL,
-		NULL,
+		maildir_get_private_flags_mask,
 		index_mail_alloc,
-		index_header_lookup_init,
-		index_header_lookup_deinit,
 		index_storage_search_init,
 		index_storage_search_deinit,
 		index_storage_search_next_nonblock,
@@ -636,7 +674,6 @@
 		maildir_save_finish,
 		maildir_save_cancel,
 		maildir_copy,
-		NULL,
 		index_storage_is_inconsistent
 	}
 };
--- a/src/lib-storage/index/maildir/maildir-storage.h	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/maildir/maildir-storage.h	Sat Jan 01 15:59:02 2011 +0200
@@ -78,6 +78,9 @@
 
 	struct timeout *keep_lock_to;
 
+	/* Filled lazily by mailbox_get_private_flags_mask() */
+	enum mail_flags _private_flags_mask;
+
 	/* maildir sync: */
 	struct maildir_uidlist *uidlist;
 	struct maildir_keywords *keywords;
@@ -87,6 +90,9 @@
 
 	unsigned int synced:1;
 	unsigned int syncing_commit:1;
+	unsigned int private_flags_mask_set:1;
+	unsigned int backend_readonly:1;
+	unsigned int backend_readonly_set:1;
 };
 
 extern struct mail_vfuncs maildir_mail_vfuncs;
@@ -111,6 +117,7 @@
 bool maildir_set_deleted(struct mailbox *box);
 uint32_t maildir_get_uidvalidity_next(struct mailbox_list *list);
 int maildir_lose_unexpected_dir(struct mail_storage *storage, const char *path);
+bool maildir_is_backend_readonly(struct maildir_mailbox *mbox);
 
 struct mail_save_context *
 maildir_save_alloc(struct mailbox_transaction_context *_t);
--- a/src/lib-storage/index/maildir/maildir-sync-index.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/maildir/maildir-sync-index.c	Sat Jan 01 15:59:02 2011 +0200
@@ -8,7 +8,7 @@
 #include "index-sync-changes.h"
 #include "maildir-uidlist.h"
 #include "maildir-keywords.h"
-#include "maildir-filename.h"
+#include "maildir-filename-flags.h"
 #include "maildir-sync.h"
 
 #include <stdio.h>
@@ -123,7 +123,7 @@
 	i_assert(*fname != '\0');
 
 	/* get the current flags and keywords */
-	maildir_filename_get_flags(ctx->keywords_sync_ctx,
+	maildir_filename_flags_get(ctx->keywords_sync_ctx,
 				   fname, &ctx->flags, &ctx->keywords);
 
 	/* apply changes */
@@ -133,7 +133,7 @@
 	ctx->flags = flags8;
 
 	/* and try renaming with the new name */
-	newfname = maildir_filename_set_flags(ctx->keywords_sync_ctx, fname,
+	newfname = maildir_filename_flags_set(ctx->keywords_sync_ctx, fname,
 					      ctx->flags, &ctx->keywords);
 	newpath = t_strconcat(dir, newfname, NULL);
 	if (strcmp(path, newpath) == 0) {
@@ -206,7 +206,7 @@
 	maildir_uidlist_sync_finish(ctx->uidlist_sync_ctx);
 
 	i_warning("Maildir %s: Expunged message reappeared, giving a new UID "
-		  "(old uid=%u, file=%s)%s", ctx->mbox->box.path,
+		  "(old uid=%u, file=%s)%s", mailbox_get_path(&ctx->mbox->box),
 		  uid, filename, strncmp(filename, "msg.", 4) != 0 ? "" :
 		  " (Your MDA is saving MH files into Maildir?)");
 	return 0;
@@ -244,7 +244,7 @@
 		maildir_keywords_sync_init(mbox->keywords, _box->index);
 	ctx->sync_changes =
 		index_sync_changes_init(ctx->sync_ctx, ctx->view, ctx->trans,
-					mbox->box.backend_readonly);
+					maildir_is_backend_readonly(mbox));
 	ctx->start_time = time(NULL);
 
 	*ctx_r = ctx;
@@ -278,12 +278,13 @@
 maildir_sync_index_update_ext_header(struct maildir_index_sync_context *ctx)
 {
 	struct maildir_mailbox *mbox = ctx->mbox;
+	const char *cur_path;
 	const void *data;
 	size_t data_size;
 	struct stat st;
 
-	if (ctx->update_maildir_hdr_cur &&
-	    stat(t_strconcat(mbox->box.path, "/cur", NULL), &st) == 0) {
+	cur_path = t_strconcat(mailbox_get_path(&mbox->box), "/cur", NULL);
+	if (ctx->update_maildir_hdr_cur && stat(cur_path, &st) == 0) {
 		if ((time_t)mbox->maildir_hdr.cur_check_time < st.st_mtime)
 			mbox->maildir_hdr.cur_check_time = st.st_mtime;
 		mbox->maildir_hdr.cur_mtime = st.st_mtime;
@@ -312,7 +313,7 @@
 		i_warning("Maildir %s: Synchronization took %u seconds "
 			  "(%u new msgs, %u flag change attempts, "
 			  "%u expunge attempts)",
-			  ctx->mbox->box.path, time_diff,
+			  mailbox_get_path(&ctx->mbox->box), time_diff,
 			  ctx->new_msgs_count, ctx->flag_change_count,
 			  ctx->expunge_count);
 	}
@@ -466,6 +467,7 @@
 	int ret = 0;
 	time_t time_before_sync;
 	uint8_t expunged_guid_128[MAIL_GUID_128_SIZE];
+	enum mail_flags private_flags_mask;
 	bool expunged, full_rescan = FALSE;
 
 	i_assert(!mbox->syncing_commit);
@@ -479,7 +481,8 @@
 		   first time, reset the index so we can add all messages as
 		   new */
 		i_warning("Maildir %s: UIDVALIDITY changed (%u -> %u)",
-			  mbox->box.path, hdr->uid_validity, uid_validity);
+			  mailbox_get_path(&ctx->mbox->box),
+			  hdr->uid_validity, uid_validity);
 		mail_index_reset(trans);
 		index_mailbox_reset_uidvalidity(&mbox->box);
 
@@ -490,6 +493,7 @@
 	}
 	hdr_next_uid = hdr->next_uid;
 
+	private_flags_mask = mailbox_get_private_flags_mask(&mbox->box);
 	time_before_sync = time(NULL);
 	mbox->syncing_commit = TRUE;
 	seq = prev_uid = 0; first_recent_uid = I_MAX(hdr->first_recent_uid, 1);
@@ -497,7 +501,7 @@
 	i_array_init(&ctx->idx_keywords, MAILDIR_MAX_KEYWORDS);
 	iter = maildir_uidlist_iter_init(mbox->uidlist);
 	while (maildir_uidlist_iter_next(iter, &uid, &uflags, &filename)) {
-		maildir_filename_get_flags(ctx->keywords_sync_ctx, filename,
+		maildir_filename_flags_get(ctx->keywords_sync_ctx, filename,
 					   &ctx->flags, &ctx->keywords);
 
 		i_assert(uid > prev_uid);
@@ -505,7 +509,7 @@
 
 		/* the private flags are kept only in indexes. don't use them
 		   at all even for newly seen mails */
-		ctx->flags &= ~mbox->box.private_flags_mask;
+		ctx->flags &= ~private_flags_mask;
 
 	again:
 		seq++;
@@ -580,7 +584,7 @@
 		}
 
 		/* the private flags are stored only in indexes, keep them */
-		ctx->flags |= rec->flags & mbox->box.private_flags_mask;
+		ctx->flags |= rec->flags & private_flags_mask;
 
 		if (index_sync_changes_have(ctx->sync_changes)) {
 			/* apply flag changes to maildir */
--- a/src/lib-storage/index/maildir/maildir-sync.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/maildir/maildir-sync.c	Sat Jan 01 15:59:02 2011 +0200
@@ -255,8 +255,8 @@
 
 	ctx = t_new(struct maildir_sync_context, 1);
 	ctx->mbox = mbox;
-	ctx->new_dir = t_strconcat(mbox->box.path, "/new", NULL);
-	ctx->cur_dir = t_strconcat(mbox->box.path, "/cur", NULL);
+	ctx->new_dir = t_strconcat(mailbox_get_path(&mbox->box), "/new", NULL);
+	ctx->cur_dir = t_strconcat(mailbox_get_path(&mbox->box), "/cur", NULL);
 	ctx->last_touch = ioloop_time;
 	ctx->last_notify = ioloop_time;
 	ctx->flags = flags;
@@ -319,7 +319,8 @@
 	}
 
 	new_fname = maildir_filename_generate();
-	new_path = t_strconcat(ctx->mbox->box.path, "/new/", new_fname, NULL);
+	new_path = t_strconcat(mailbox_get_path(&ctx->mbox->box),
+			       "/new/", new_fname, NULL);
 
 	if (rename(path2, new_path) == 0)
 		i_warning("Fixed a duplicate: %s -> %s", path2, new_fname);
@@ -886,8 +887,8 @@
 	}
 
 	if (find_uid != NULL && *find_uid != 0) {
-		ret = maildir_uidlist_lookup_nosync(ctx->mbox->uidlist,
-						    *find_uid, &flags, &fname);
+		ret = maildir_uidlist_lookup(ctx->mbox->uidlist,
+					     *find_uid, &flags, &fname);
 		if (ret < 0)
 			return -1;
 		if (ret == 0) {
@@ -902,6 +903,35 @@
 	return maildir_uidlist_sync_deinit(&ctx->uidlist_sync_ctx, TRUE);
 }
 
+int maildir_sync_lookup(struct maildir_mailbox *mbox, uint32_t uid,
+			enum maildir_uidlist_rec_flag *flags_r,
+			const char **fname_r)
+{
+	int ret;
+
+	ret = maildir_uidlist_lookup(mbox->uidlist, uid, flags_r, fname_r);
+	if (ret <= 0) {
+		if (ret < 0)
+			return -1;
+		if (maildir_uidlist_is_open(mbox->uidlist)) {
+			/* refresh uidlist and check again in case it was added
+			   after the last mailbox sync */
+			if (maildir_uidlist_refresh(mbox->uidlist) < 0)
+				return -1;
+		} else {
+			/* the uidlist doesn't exist. */
+			if (maildir_storage_sync_force(mbox, uid) < 0)
+				return -1;
+		}
+
+		/* try again */
+		ret = maildir_uidlist_lookup(mbox->uidlist, uid,
+					     flags_r, fname_r);
+	}
+
+	return ret;
+}
+
 int maildir_storage_sync_force(struct maildir_mailbox *mbox, uint32_t uid)
 {
         struct maildir_sync_context *ctx;
@@ -993,10 +1023,11 @@
 	int ret;
 
 	T_BEGIN {
+		const char *box_path = mailbox_get_path(&mbox->box);
 		const char *new_dir, *cur_dir;
 
-		new_dir = t_strconcat(mbox->box.path, "/new", NULL);
-		cur_dir = t_strconcat(mbox->box.path, "/cur", NULL);
+		new_dir = t_strconcat(box_path, "/new", NULL);
+		cur_dir = t_strconcat(box_path, "/cur", NULL);
 
 		ret = maildir_sync_quick_check(mbox, FALSE, new_dir, cur_dir,
 					       &new_changed, &cur_changed);
--- a/src/lib-storage/index/maildir/maildir-sync.h	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/maildir/maildir-sync.h	Sat Jan 01 15:59:02 2011 +0200
@@ -14,8 +14,9 @@
 /* If syncing takes longer than this, log a warning. */
 #define MAILDIR_SYNC_TIME_WARN_SECS 60
 
+enum maildir_uidlist_rec_flag;
+
 struct maildir_mailbox;
-
 struct maildir_sync_context;
 struct maildir_keywords_sync_ctx;
 struct maildir_index_sync_context;
@@ -42,6 +43,10 @@
 void maildir_sync_set_new_msgs_count(struct maildir_index_sync_context *ctx,
 				     unsigned int count);
 
+int maildir_sync_lookup(struct maildir_mailbox *mbox, uint32_t uid,
+			enum maildir_uidlist_rec_flag *flags_r,
+			const char **fname_r);
+
 int maildir_list_index_has_changed(struct mailbox *box,
 				   struct mail_index_view *list_view,
 				   uint32_t seq);
--- a/src/lib-storage/index/maildir/maildir-uidlist.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/maildir/maildir-uidlist.c	Sat Jan 01 15:59:02 2011 +0200
@@ -36,7 +36,6 @@
 #include "nfs-workarounds.h"
 #include "eacces-error.h"
 #include "maildir-storage.h"
-#include "maildir-sync.h"
 #include "maildir-filename.h"
 #include "maildir-uidlist.h"
 
@@ -53,8 +52,6 @@
 
 #define UIDLIST_IS_LOCKED(uidlist) \
 	((uidlist)->lock_count > 0)
-#define UIDLIST_ALLOW_WRITING(uidlist) \
-	(UIDLIST_IS_LOCKED(uidlist) || (uidlist)->mbox == NULL)
 
 struct maildir_uidlist_rec {
 	uint32_t uid;
@@ -65,8 +62,9 @@
 ARRAY_DEFINE_TYPE(maildir_uidlist_rec_p, struct maildir_uidlist_rec *);
 
 struct maildir_uidlist {
-	struct maildir_mailbox *mbox;
+	struct mailbox *box;
 	char *path;
+	struct maildir_index_header *mhdr;
 
 	int fd;
 	dev_t fd_dev;
@@ -137,7 +135,8 @@
 					bool nonblock, bool refresh,
 					bool refresh_when_locked)
 {
-	struct mailbox *box = &uidlist->mbox->box;
+	struct mailbox *box = uidlist->box;
+	const struct mailbox_permissions *perm = mailbox_get_permissions(box);
 	const char *control_dir, *path;
 	mode_t old_mask;
 	const enum dotlock_create_flags dotlock_flags =
@@ -153,14 +152,14 @@
 		return 1;
 	}
 
-        index_storage_lock_notify_reset(&uidlist->mbox->box);
+        index_storage_lock_notify_reset(uidlist->box);
 
 	control_dir = mailbox_list_get_path(box->list, box->name,
 					    MAILBOX_LIST_PATH_TYPE_CONTROL);
 	path = t_strconcat(control_dir, "/" MAILDIR_UIDLIST_NAME, NULL);
 
 	for (i = 0;; i++) {
-		old_mask = umask(0777 & ~box->file_create_mode);
+		old_mask = umask(0777 & ~perm->file_create_mode);
 		ret = file_dotlock_create(&uidlist->dotlock_settings, path,
 					  dotlock_flags, &uidlist->dotlock);
 		umask(old_mask);
@@ -173,8 +172,7 @@
 				MAIL_ERROR_TEMP, MAIL_ERRSTR_LOCK_TIMEOUT);
 			return 0;
 		}
-		if (errno != ENOENT || i == MAILDIR_DELETE_RETRY_COUNT ||
-		    uidlist->mbox == NULL) {
+		if (errno != ENOENT || i == MAILDIR_DELETE_RETRY_COUNT) {
 			if (errno == EACCES) {
 				mail_storage_set_critical(box->storage, "%s",
 					eacces_error_get_creating("file_dotlock_create", path));
@@ -187,7 +185,7 @@
 		}
 		/* the control dir doesn't exist. create it unless the whole
 		   mailbox was just deleted. */
-		if (!maildir_set_deleted(&uidlist->mbox->box))
+		if (!maildir_set_deleted(uidlist->box))
 			return -1;
 	}
 
@@ -232,6 +230,11 @@
 	return uidlist->initial_read;
 }
 
+bool maildir_uidlist_is_open(struct maildir_uidlist *uidlist)
+{
+	return uidlist->fd != -1;
+}
+
 void maildir_uidlist_unlock(struct maildir_uidlist *uidlist)
 {
 	i_assert(uidlist->lock_count > 0);
@@ -256,14 +259,16 @@
 
 struct maildir_uidlist *maildir_uidlist_init(struct maildir_mailbox *mbox)
 {
+	struct mailbox *box = &mbox->box;
 	struct maildir_uidlist *uidlist;
 	const char *control_dir;
 
-	control_dir = mailbox_list_get_path(mbox->box.list, mbox->box.name,
+	control_dir = mailbox_list_get_path(box->list, box->name,
 					    MAILBOX_LIST_PATH_TYPE_CONTROL);
 
 	uidlist = i_new(struct maildir_uidlist, 1);
-	uidlist->mbox = mbox;
+	uidlist->box = box;
+	uidlist->mhdr = &mbox->maildir_hdr;
 	uidlist->fd = -1;
 	uidlist->path = i_strconcat(control_dir, "/"MAILDIR_UIDLIST_NAME, NULL);
 	i_array_init(&uidlist->records, 128);
@@ -275,23 +280,23 @@
 
 	uidlist->dotlock_settings.use_io_notify = TRUE;
 	uidlist->dotlock_settings.use_excl_lock =
-		mbox->box.storage->set->dotlock_use_excl;
+		box->storage->set->dotlock_use_excl;
 	uidlist->dotlock_settings.nfs_flush =
-		mbox->box.storage->set->mail_nfs_storage;
+		box->storage->set->mail_nfs_storage;
 	uidlist->dotlock_settings.timeout =
-		mail_storage_get_lock_timeout(&mbox->storage->storage,
+		mail_storage_get_lock_timeout(box->storage,
 			MAILDIR_UIDLIST_LOCK_STALE_TIMEOUT + 2);
 	uidlist->dotlock_settings.stale_timeout =
 		MAILDIR_UIDLIST_LOCK_STALE_TIMEOUT;
 	uidlist->dotlock_settings.callback = dotlock_callback;
-	uidlist->dotlock_settings.context = &mbox->box;
+	uidlist->dotlock_settings.context = box;
 	uidlist->dotlock_settings.temp_prefix = mbox->storage->temp_prefix;
 	return uidlist;
 }
 
 static void maildir_uidlist_close(struct maildir_uidlist *uidlist)
 {
-	struct mail_storage *storage = uidlist->mbox->box.storage;
+	struct mail_storage *storage = uidlist->box->storage;
 
 	if (uidlist->fd != -1) {
 		if (close(uidlist->fd) < 0) {
@@ -347,7 +352,7 @@
 maildir_uidlist_set_corrupted(struct maildir_uidlist *uidlist,
 			      const char *fmt, ...)
 {
-	struct mail_storage *storage = uidlist->mbox->box.storage;
+	struct mail_storage *storage = uidlist->box->storage;
 	va_list args;
 
 	va_start(args, fmt);
@@ -368,14 +373,8 @@
 static void maildir_uidlist_update_hdr(struct maildir_uidlist *uidlist,
 				       const struct stat *st)
 {
-	struct maildir_index_header *mhdr;
+	struct maildir_index_header *mhdr = uidlist->mhdr;
 
-	if (uidlist->mbox == NULL) {
-		/* dbox is using this */
-		return;
-	}
-
-	mhdr = &uidlist->mbox->maildir_hdr;
 	if (mhdr->uidlist_mtime == 0 && uidlist->version != UIDLIST_VERSION) {
 		/* upgrading from older verson. don't update the
 		   uidlist times until it uses the new format */
@@ -695,7 +694,7 @@
 maildir_uidlist_update_read(struct maildir_uidlist *uidlist,
 			    bool *retry_r, bool try_retry)
 {
-	struct mail_storage *storage = uidlist->mbox->box.storage;
+	struct mail_storage *storage = uidlist->box->storage;
 	const char *line;
 	uint32_t orig_next_uid, orig_uid_validity;
 	struct istream *input;
@@ -838,7 +837,7 @@
 static int
 maildir_uidlist_stat(struct maildir_uidlist *uidlist, struct stat *st_r)
 {
-	struct mail_storage *storage = uidlist->mbox->box.storage;
+	struct mail_storage *storage = uidlist->box->storage;
 
 	if (storage->set->mail_nfs_storage) {
 		nfs_flush_file_handle_cache(uidlist->path);
@@ -858,7 +857,7 @@
 static int
 maildir_uidlist_has_changed(struct maildir_uidlist *uidlist, bool *recreated_r)
 {
-	struct mail_storage *storage = uidlist->mbox->box.storage;
+	struct mail_storage *storage = uidlist->box->storage;
         struct stat st;
 	int ret;
 
@@ -918,7 +917,7 @@
 
 	uidlist->fd = nfs_safe_open(uidlist->path, O_RDWR);
 	if (uidlist->fd == -1 && errno != ENOENT) {
-		mail_storage_set_critical(uidlist->mbox->box.storage,
+		mail_storage_set_critical(uidlist->box->storage,
 			"open(%s) failed: %m", uidlist->path);
 		return -1;
 	}
@@ -957,8 +956,8 @@
 
 int maildir_uidlist_refresh_fast_init(struct maildir_uidlist *uidlist)
 {
-	const struct maildir_index_header *mhdr = &uidlist->mbox->maildir_hdr;
-	struct mail_index *index = uidlist->mbox->box.index;
+	const struct maildir_index_header *mhdr = uidlist->mhdr;
+	struct mail_index *index = uidlist->box->index;
 	struct mail_index_view *view;
 	const struct mail_index_header *hdr;
 	struct stat st;
@@ -1028,35 +1027,6 @@
 			   enum maildir_uidlist_rec_flag *flags_r,
 			   const char **fname_r)
 {
-	int ret;
-
-	ret = maildir_uidlist_lookup_nosync(uidlist, uid, flags_r, fname_r);
-	if (ret <= 0) {
-		if (ret < 0)
-			return -1;
-		if (uidlist->fd != -1 || uidlist->mbox == NULL) {
-			/* refresh uidlist and check again in case it was added
-			   after the last mailbox sync */
-			if (maildir_uidlist_refresh(uidlist) < 0)
-				return -1;
-		} else {
-			/* the uidlist doesn't exist. */
-			if (maildir_storage_sync_force(uidlist->mbox, uid) < 0)
-				return -1;
-		}
-
-		/* try again */
-		ret = maildir_uidlist_lookup_nosync(uidlist, uid,
-						    flags_r, fname_r);
-	}
-
-	return ret;
-}
-
-int maildir_uidlist_lookup_nosync(struct maildir_uidlist *uidlist, uint32_t uid,
-				  enum maildir_uidlist_rec_flag *flags_r,
-				  const char **fname_r)
-{
 	struct maildir_uidlist_rec *rec;
 	int ret;
 
@@ -1213,22 +1183,22 @@
 {
 	const struct mail_index_header *hdr;
 
-	if (uidlist->mbox->box.opened) {
-		hdr = mail_index_get_header(uidlist->mbox->box.view);
+	if (uidlist->box->opened) {
+		hdr = mail_index_get_header(uidlist->box->view);
 		if (hdr->uid_validity != 0) {
 			uidlist->uid_validity = hdr->uid_validity;
 			return;
 		}
 	}
 	uidlist->uid_validity =
-		maildir_get_uidvalidity_next(uidlist->mbox->box.list);
+		maildir_get_uidvalidity_next(uidlist->box->list);
 }
 
 static int maildir_uidlist_write_fd(struct maildir_uidlist *uidlist, int fd,
 				    const char *path, unsigned int first_idx,
 				    uoff_t *file_size_r)
 {
-	struct mail_storage *storage = uidlist->mbox->box.storage;
+	struct mail_storage *storage = uidlist->box->storage;
 	struct maildir_uidlist_iter_ctx *iter;
 	struct ostream *output;
 	struct maildir_uidlist_rec *rec;
@@ -1332,11 +1302,11 @@
 
 	/* we could get here when opening and locking mailbox,
 	   before index files have been opened. */
-	if (!uidlist->mbox->box.opened)
+	if (!uidlist->box->opened)
 		return;
 
-	mail_index_refresh(uidlist->mbox->box.index);
-	view = mail_index_view_open(uidlist->mbox->box.index);
+	mail_index_refresh(uidlist->box->index);
+	view = mail_index_view_open(uidlist->box->index);
 	count = array_count(&uidlist->records);
 	hdr = mail_index_get_header(view);
 	if (count * UIDLIST_COMPRESS_PERCENTAGE / 100 <= hdr->messages_count) {
@@ -1381,7 +1351,8 @@
 
 static int maildir_uidlist_recreate(struct maildir_uidlist *uidlist)
 {
-	struct mailbox *box = &uidlist->mbox->box;
+	struct mailbox *box = uidlist->box;
+	const struct mailbox_permissions *perm = mailbox_get_permissions(box);
 	const char *control_dir, *temp_path;
 	struct stat st;
 	mode_t old_mask;
@@ -1398,31 +1369,30 @@
 				"/" MAILDIR_UIDLIST_NAME ".tmp", NULL);
 
 	for (i = 0;; i++) {
-		old_mask = umask(0777 & ~box->file_create_mode);
+		old_mask = umask(0777 & ~perm->file_create_mode);
 		fd = open(temp_path, O_RDWR | O_CREAT | O_TRUNC, 0777);
 		umask(old_mask);
 		if (fd != -1)
 			break;
 
-		if (errno != ENOENT || i == MAILDIR_DELETE_RETRY_COUNT ||
-		    uidlist->mbox == NULL) {
+		if (errno != ENOENT || i == MAILDIR_DELETE_RETRY_COUNT) {
 			mail_storage_set_critical(box->storage,
 				"open(%s, O_CREAT) failed: %m", temp_path);
 			return -1;
 		}
 		/* the control dir doesn't exist. create it unless the whole
 		   mailbox was just deleted. */
-		if (!maildir_set_deleted(&uidlist->mbox->box))
+		if (!maildir_set_deleted(uidlist->box))
 			return -1;
 	}
 
-	if (box->file_create_gid != (gid_t)-1 &&
-	    fchown(fd, (uid_t)-1, box->file_create_gid) < 0) {
+	if (perm->file_create_gid != (gid_t)-1 &&
+	    fchown(fd, (uid_t)-1, perm->file_create_gid) < 0) {
 		if (errno == EPERM) {
 			mail_storage_set_critical(box->storage, "%s",
 				eperm_error_get_chgrp("fchown", temp_path,
-						box->file_create_gid,
-						box->file_create_gid_origin));
+						perm->file_create_gid,
+						perm->file_create_gid_origin));
 		} else {
 			mail_storage_set_critical(box->storage,
 				"fchown(%s) failed: %m", temp_path);
@@ -1519,7 +1489,7 @@
 static int maildir_uidlist_sync_update(struct maildir_uidlist_sync_ctx *ctx)
 {
 	struct maildir_uidlist *uidlist = ctx->uidlist;
-	struct mail_storage *storage = uidlist->mbox->box.storage;
+	struct mail_storage *storage = uidlist->box->storage;
 	struct stat st;
 	uoff_t file_size;
 
@@ -1631,7 +1601,7 @@
 	*sync_ctx_r = ctx = i_new(struct maildir_uidlist_sync_ctx, 1);
 	ctx->uidlist = uidlist;
 	ctx->sync_flags = sync_flags;
-	ctx->partial = (!locked && ctx->uidlist->mbox != NULL) ||
+	ctx->partial = !locked ||
 		(sync_flags & MAILDIR_UIDLIST_SYNC_PARTIAL) != 0;
 	ctx->locked = locked;
 	ctx->first_unwritten_pos = (unsigned int)-1;
@@ -1694,7 +1664,7 @@
 	}
 	if (uid != 0) {
 		if (rec->uid != uid && rec->uid != (uint32_t)-1) {
-			mail_storage_set_critical(uidlist->mbox->box.storage,
+			mail_storage_set_critical(uidlist->box->storage,
 				"Maildir: %s changed UID %u -> %u",
 				filename, rec->uid, uid);
 			return -1;
@@ -1761,7 +1731,7 @@
 		return -1;
 	for (p = filename; *p != '\0'; p++) {
 		if (*p == 13 || *p == 10) {
-			struct mailbox *box = &uidlist->mbox->box;
+			struct mailbox *box = uidlist->box;
 
 			dir = mailbox_list_get_path(box->list, box->name,
 						MAILBOX_LIST_PATH_TYPE_MAILBOX);
@@ -1791,7 +1761,7 @@
 				MAILDIR_UIDLIST_REC_FLAG_MOVED);
 	} else {
 		old_rec = hash_table_lookup(uidlist->files, filename);
-		i_assert(old_rec != NULL || UIDLIST_ALLOW_WRITING(uidlist));
+		i_assert(old_rec != NULL || UIDLIST_IS_LOCKED(uidlist));
 
 		rec = p_new(ctx->record_pool, struct maildir_uidlist_rec, 1);
 
@@ -1913,7 +1883,7 @@
 	struct maildir_uidlist_rec **recs;
 	unsigned int dest, count;
 
-	i_assert(UIDLIST_ALLOW_WRITING(ctx->uidlist));
+	i_assert(UIDLIST_IS_LOCKED(ctx->uidlist));
 	i_assert(ctx->first_new_pos != (unsigned int)-1);
 
 	if (ctx->first_unwritten_pos == (unsigned int)-1)
@@ -1996,9 +1966,9 @@
 
 	/* mbox=NULL means we're coming from dbox rebuilding code.
 	   the dbox is already locked, so allow uidlist recreation */
-	i_assert(ctx->locked || !ctx->changed || ctx->uidlist->mbox == NULL);
+	i_assert(ctx->locked || !ctx->changed);
 	if ((ctx->changed || maildir_uidlist_want_compress(ctx)) &&
-	    !ctx->failed && (ctx->locked || ctx->uidlist->mbox == NULL)) {
+	    !ctx->failed && ctx->locked) {
 		T_BEGIN {
 			if (maildir_uidlist_sync_update(ctx) < 0) {
 				/* we couldn't write everything we wanted. make
--- a/src/lib-storage/index/maildir/maildir-uidlist.h	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/maildir/maildir-uidlist.h	Sat Jan 01 15:59:02 2011 +0200
@@ -56,6 +56,8 @@
 void maildir_uidlist_unlock(struct maildir_uidlist *uidlist);
 bool maildir_uidlist_is_locked(struct maildir_uidlist *uidlist);
 bool maildir_uidlist_is_read(struct maildir_uidlist *uidlist);
+/* Returns TRUE if uidlist file is currently open */
+bool maildir_uidlist_is_open(struct maildir_uidlist *uidlist);
 
 struct maildir_uidlist *maildir_uidlist_init(struct maildir_mailbox *mbox);
 void maildir_uidlist_deinit(struct maildir_uidlist **uidlist);
@@ -73,9 +75,6 @@
 int maildir_uidlist_lookup(struct maildir_uidlist *uidlist, uint32_t uid,
 			   enum maildir_uidlist_rec_flag *flags_r,
 			   const char **fname_r);
-int maildir_uidlist_lookup_nosync(struct maildir_uidlist *uidlist, uint32_t uid,
-				  enum maildir_uidlist_rec_flag *flags_r,
-				  const char **fname_r);
 /* Returns extension's value or NULL if it doesn't exist. */
 const char *
 maildir_uidlist_lookup_ext(struct maildir_uidlist *uidlist, uint32_t uid,
--- a/src/lib-storage/index/maildir/maildir-util.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/maildir/maildir-util.c	Sat Jan 01 15:59:02 2011 +0200
@@ -9,7 +9,7 @@
 #include "maildir-storage.h"
 #include "maildir-uidlist.h"
 #include "maildir-keywords.h"
-#include "maildir-filename.h"
+#include "maildir-filename-flags.h"
 #include "maildir-sync.h"
 
 #include <stdio.h>
@@ -41,12 +41,12 @@
 	mail_index_lookup_view_flags(view, seq, &flags, &keywords);
 	if (array_count(&keywords) == 0) {
 		*have_flags_r = (flags & MAIL_FLAGS_NONRECENT) != 0;
-		fname = maildir_filename_set_flags(NULL, fname, flags, NULL);
+		fname = maildir_filename_flags_set(NULL, fname, flags, NULL);
 	} else {
 		*have_flags_r = TRUE;
 		kw_ctx = maildir_keywords_sync_init_readonly(mbox->keywords,
 							     mbox->box.index);
-		fname = maildir_filename_set_flags(kw_ctx, fname,
+		fname = maildir_filename_flags_set(kw_ctx, fname,
 						   flags, &keywords);
 		maildir_keywords_sync_deinit(&kw_ctx);
 	}
@@ -61,7 +61,7 @@
 	bool have_flags;
 	int ret;
 
-	ret = maildir_uidlist_lookup(mbox->uidlist, uid, &flags, &fname);
+	ret = maildir_sync_lookup(mbox, uid, &flags, &fname);
 	if (ret <= 0)
 		return ret == 0 ? -2 : -1;
 
@@ -76,13 +76,14 @@
 
 	if ((flags & MAILDIR_UIDLIST_REC_FLAG_NEW_DIR) != 0) {
 		/* probably in new/ dir */
-		path = t_strconcat(mbox->box.path, "/new/", fname, NULL);
+		path = t_strconcat(mailbox_get_path(&mbox->box),
+				   "/new/", fname, NULL);
 		ret = callback(mbox, path, context);
 		if (ret != 0)
 			return ret;
 	}
 
-	path = t_strconcat(mbox->box.path, "/cur/", fname, NULL);
+	path = t_strconcat(mailbox_get_path(&mbox->box), "/cur/", fname, NULL);
 	ret = callback(mbox, path, context);
 	return ret;
 }
@@ -135,10 +136,11 @@
 static int maildir_create_path(struct mailbox *box, const char *path,
 			       enum mailbox_list_path_type type, bool retry)
 {
+	const struct mailbox_permissions *perm = mailbox_get_permissions(box);
 	const char *p, *parent;
 
-	if (mkdir_chgrp(path, box->dir_create_mode, box->file_create_gid,
-			box->file_create_gid_origin) == 0)
+	if (mkdir_chgrp(path, perm->dir_create_mode, perm->file_create_gid,
+			perm->file_create_gid_origin) == 0)
 		return 0;
 
 	switch (errno) {
@@ -179,7 +181,8 @@
 	/* @UNSAFE: get a list of directories we want to create */
 	for (i = 0; i < N_ELEMENTS(subdirs); i++) {
 		types[i] = MAILBOX_LIST_PATH_TYPE_MAILBOX;
-		dirs[i] = t_strconcat(box->path, "/", subdirs[i], NULL);
+		dirs[i] = t_strconcat(mailbox_get_path(box),
+				      "/", subdirs[i], NULL);
 	}
 	types[i] = MAILBOX_LIST_PATH_TYPE_CONTROL;
 	dirs[i++] = mailbox_list_get_path(box->list, box->name,
@@ -209,12 +212,12 @@
 	struct stat st;
 	int ret;
 
-	if (stat(box->path, &st) < 0) {
+	if (stat(mailbox_get_path(box), &st) < 0) {
 		if (errno == ENOENT)
 			mailbox_set_deleted(box);
 		else {
 			mail_storage_set_critical(box->storage,
-				"stat(%s) failed: %m", box->path);
+				"stat(%s) failed: %m", mailbox_get_path(box));
 		}
 		return FALSE;
 	}
--- a/src/lib-storage/index/mbox/mbox-file.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/mbox/mbox-file.c	Sat Jan 01 15:59:02 2011 +0200
@@ -21,15 +21,15 @@
 
 	if (mbox->mbox_file_stream != NULL) {
 		/* read-only mbox stream */
-		i_assert(mbox->box.backend_readonly);
+		i_assert(mbox_is_backend_readonly(mbox));
 		return 0;
 	}
 
-	fd = open(mbox->box.path,
-		  mbox->box.backend_readonly ? O_RDONLY : O_RDWR);
-	if (fd == -1 && errno == EACCES && !mbox->box.backend_readonly) {
-                mbox->box.backend_readonly = TRUE;
-		fd = open(mbox->box.path, O_RDONLY);
+	fd = open(mailbox_get_path(&mbox->box),
+		  mbox_is_backend_readonly(mbox) ? O_RDONLY : O_RDWR);
+	if (fd == -1 && errno == EACCES && !mbox->backend_readonly) {
+		mbox->backend_readonly = TRUE;
+		fd = open(mailbox_get_path(&mbox->box), O_RDONLY);
 	}
 
 	if (fd == -1) {
@@ -68,7 +68,7 @@
 
 	if (mbox->mbox_file_stream != NULL) {
 		/* read-only mbox stream */
-		i_assert(mbox->mbox_fd == -1 && mbox->box.backend_readonly);
+		i_assert(mbox->mbox_fd == -1 && mbox_is_backend_readonly(mbox));
 	} else {
 		if (mbox->mbox_fd == -1) {
 			if (mbox_file_open(mbox) < 0)
@@ -86,7 +86,8 @@
 			i_stream_set_init_buffer_size(mbox->mbox_file_stream,
 						      MBOX_READ_BLOCK_SIZE);
 		}
-		i_stream_set_name(mbox->mbox_file_stream, mbox->box.path);
+		i_stream_set_name(mbox->mbox_file_stream,
+				  mailbox_get_path(&mbox->box));
 	}
 
 	mbox->mbox_stream = i_stream_create_raw_mbox(mbox->mbox_file_stream);
@@ -103,7 +104,7 @@
 
 	if (ibox->recent_flags_count > 0 &&
 	    (mbox->box.flags & MAILBOX_FLAG_KEEP_RECENT) != 0 &&
-	    mbox->mbox_fd != -1 && !mbox->box.backend_readonly) {
+	    mbox->mbox_fd != -1 && !mbox_is_backend_readonly(mbox)) {
 		/* we've seen recent messages which we want to keep recent.
 		   keep file's atime lower than mtime so \Marked status
 		   gets shown while listing */
@@ -115,7 +116,8 @@
 			buf.modtime = st.st_mtime;
 			buf.actime = buf.modtime - 1;
 			/* EPERM can happen with shared mailboxes */
-			if (utime(mbox->box.path, &buf) < 0 && errno != EPERM)
+			if (utime(mailbox_get_path(&mbox->box), &buf) < 0 &&
+			    errno != EPERM)
 				mbox_set_syscall_error(mbox, "utime()");
 		}
 	}
@@ -131,7 +133,7 @@
 	if (mbox->mbox_file_stream != NULL) {
 		if (mbox->mbox_fd == -1) {
 			/* read-only mbox stream */
-			i_assert(mbox->box.backend_readonly);
+			i_assert(mbox_is_backend_readonly(mbox));
 			i_stream_seek(mbox->mbox_file_stream, 0);
 		} else {
 			i_stream_destroy(&mbox->mbox_file_stream);
@@ -153,7 +155,7 @@
 	if (data == NULL) {
 		mail_storage_set_critical(&mbox->storage->storage,
 			"Cached message offset lost for seq %u in mbox file %s",
-			seq, mbox->box.path);
+			seq, mailbox_get_path(&mbox->box));
                 mbox->mbox_hdr.dirty_flag = TRUE;
                 mbox->mbox_broken_offsets = TRUE;
 		return 0;
@@ -190,7 +192,7 @@
 
 		mail_storage_set_critical(&mbox->storage->storage,
 			"Cached message offset %s is invalid for mbox file %s",
-			dec2str(offset), mbox->box.path);
+			dec2str(offset), mailbox_get_path(&mbox->box));
 		mbox->mbox_hdr.dirty_flag = TRUE;
 		mbox->mbox_broken_offsets = TRUE;
 		return 0;
--- a/src/lib-storage/index/mbox/mbox-lock.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/mbox/mbox-lock.c	Sat Jan 01 15:59:02 2011 +0200
@@ -174,7 +174,7 @@
 		   be sure that the file is latest, but mbox files get rarely
 		   deleted and the flushing might cause errors (e.g. EBUSY for
 		   trying to flush a /var/mail mountpoint) */
-		if (nfs_safe_stat(mbox->box.path, &st) < 0) {
+		if (nfs_safe_stat(mailbox_get_path(&mbox->box), &st) < 0) {
 			if (errno == ENOENT)
 				mailbox_set_deleted(&mbox->box);
 			else
@@ -250,7 +250,7 @@
 				      struct dotlock_settings *set,
 				      enum mbox_dotlock_op op)
 {
-	const char *dir, *fname;
+	const char *box_path, *dir, *fname;
 	int ret = -1, orig_dir_fd, orig_errno;
 
 	orig_dir_fd = open(".", O_RDONLY);
@@ -268,12 +268,13 @@
 	      privileged group
 	    - DoS other users by dotlocking their mailboxes infinitely
 	*/
-	fname = strrchr(mbox->box.path, '/');
+	box_path = mailbox_get_path(&mbox->box);
+	fname = strrchr(box_path, '/');
 	if (fname == NULL) {
 		/* already relative */
-		fname = mbox->box.path;
+		fname = box_path;
 	} else {
-		dir = t_strdup_until(mbox->box.path, fname);
+		dir = t_strdup_until(box_path, fname);
 		if (chdir(dir) < 0) {
 			mail_storage_set_critical(&mbox->storage->storage,
 				"chdir(%s) failed: %m", dir);
@@ -285,7 +286,7 @@
 	if (op == MBOX_DOTLOCK_OP_LOCK) {
 		if (access(fname, R_OK) < 0) {
 			mail_storage_set_critical(&mbox->storage->storage,
-				"access(%s) failed: %m", mbox->box.path);
+				"access(%s) failed: %m", box_path);
 			return -1;
 		}
 	}
@@ -415,7 +416,7 @@
 	set.callback = dotlock_callback;
 	set.context = ctx;
 
-	ret = file_dotlock_create(&set, mbox->box.path, 0,
+	ret = file_dotlock_create(&set, mailbox_get_path(&mbox->box), 0,
 				  &mbox->mbox_dotlock);
 	if (ret >= 0) {
 		/* success / timeout */
@@ -425,7 +426,7 @@
 		ret = mbox_dotlock_privileged_op(mbox, &set,
 						 MBOX_DOTLOCK_OP_LOCK);
 	} else if (errno == EACCES)
-		mbox_dotlock_log_eacces_error(mbox, mbox->box.path);
+		mbox_dotlock_log_eacces_error(mbox, mailbox_get_path(&mbox->box));
 	else
 		mbox_set_syscall_error(mbox, "file_dotlock_create()");
 
@@ -635,7 +636,7 @@
 			mail_storage_set_critical(&ctx->mbox->storage->storage,
 				"fcntl() failed with mbox file %s: "
 				"File is locked by another process (EACCES)",
-				ctx->mbox->box.path);
+				mailbox_get_path(&ctx->mbox->box));
 			return -1;
 		}
 
@@ -707,7 +708,7 @@
 
 	if (mbox->mbox_fd == -1 && mbox->mbox_file_stream != NULL) {
 		/* read-only mbox stream. no need to lock. */
-		i_assert(mbox->box.backend_readonly);
+		i_assert(mbox_is_backend_readonly(mbox));
 		mbox->mbox_lock_type = lock_type;
 		return 1;
 	}
@@ -772,7 +773,7 @@
 int mbox_lock(struct mbox_mailbox *mbox, int lock_type,
 	      unsigned int *lock_id_r)
 {
-	const char *path = mbox->box.path;
+	const char *path = mailbox_get_path(&mbox->box);
 	int mbox_fd = mbox->mbox_fd;
 	bool fcntl_locked;
 	int ret;
--- a/src/lib-storage/index/mbox/mbox-mail.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/mbox/mbox-mail.c	Sat Jan 01 15:59:02 2011 +0200
@@ -33,7 +33,7 @@
 static int mbox_mail_seek(struct index_mail *mail)
 {
 	struct mbox_transaction_context *t =
-		(struct mbox_transaction_context *)mail->trans;
+		(struct mbox_transaction_context *)mail->mail.mail.transaction;
 	struct mail *_mail = &mail->mail.mail;
 	struct mbox_mailbox *mbox = (struct mbox_mailbox *)_mail->box;
 	enum mbox_sync_flags sync_flags = 0;
@@ -102,7 +102,7 @@
 	if (ret == 0) {
 		mail_storage_set_critical(&mbox->storage->storage,
 			"Losing sync for mail uid=%u in mbox file %s",
-			_mail->uid, mbox->box.path);
+			_mail->uid, mailbox_get_path(&mbox->box));
 	}
 	return 0;
 }
@@ -324,8 +324,8 @@
 		ret = mbox_mail_get_next_offset(mail, &next_offset);
 		if (ret < 0) {
 			i_warning("mbox %s: Can't find next message offset "
-				  "for uid=%u",
-				  mbox->box.path, mail->mail.mail.uid);
+				  "for uid=%u", mailbox_get_path(&mbox->box),
+				  mail->mail.mail.uid);
 		}
 	}
 	if (ret <= 0)
--- a/src/lib-storage/index/mbox/mbox-save.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/mbox/mbox-save.c	Sat Jan 01 15:59:02 2011 +0200
@@ -255,13 +255,13 @@
 mbox_save_init_file(struct mbox_save_context *ctx,
 		    struct mbox_transaction_context *t, bool want_mail)
 {
-	struct mailbox_transaction_context *_t = &t->ictx.mailbox_ctx;
+	struct mailbox_transaction_context *_t = &t->t;
 	struct mbox_mailbox *mbox = ctx->mbox;
 	struct mail_storage *storage = &mbox->storage->storage;
 	bool empty = FALSE;
 	int ret;
 
-	if (ctx->mbox->box.backend_readonly) {
+	if (mbox_is_backend_readonly(ctx->mbox)) {
 		mail_storage_set_error(storage, MAIL_ERROR_PERM,
 				       "Read-only mbox");
 		return -1;
@@ -770,7 +770,8 @@
 
 		buf.modtime = st.st_mtime;
 		buf.actime = ctx->orig_atime;
-		if (utime(mbox->box.path, &buf) < 0 && errno != EPERM)
+		if (utime(mailbox_get_path(&mbox->box), &buf) < 0 &&
+		    errno != EPERM)
 			mbox_set_syscall_error(mbox, "utime()");
 	}
 
--- a/src/lib-storage/index/mbox/mbox-storage.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/mbox/mbox-storage.c	Sat Jan 01 15:59:02 2011 +0200
@@ -79,9 +79,8 @@
 		const char *toobig_error = errno != EFBIG ? "" :
 			" (process was started with ulimit -f limit)";
 		mail_storage_set_critical(&mbox->storage->storage,
-					  "%s failed with mbox file %s: %m%s",
-					  function, mbox->box.path,
-					  toobig_error);
+			"%s failed with mbox file %s: %m%s", function,
+			mailbox_get_path(&mbox->box), toobig_error);
 	}
 	return -1;
 }
@@ -370,13 +369,6 @@
 	mbox->storage = (struct mbox_storage *)storage;
 	mbox->mbox_fd = -1;
 	mbox->mbox_lock_type = F_UNLCK;
-	mbox->mbox_ext_idx =
-		mail_index_ext_register(mbox->box.index, "mbox",
-					sizeof(mbox->mbox_hdr),
-					sizeof(uint64_t), sizeof(uint64_t));
-	mbox->md5hdr_ext_idx =
-		mail_index_ext_register(mbox->box.index, "header-md5",
-					0, 16, 1);
 
 	if ((storage->flags & MAIL_STORAGE_FLAG_KEEP_HEADER_MD5) != 0)
 		mbox->mbox_save_md5 = TRUE;
@@ -388,27 +380,36 @@
 	mbox_dotlock_touch(mbox);
 }
 
+static int
+mbox_mailbox_open_finish(struct mbox_mailbox *mbox, bool move_to_memory)
+{
+	if (index_storage_mailbox_open(&mbox->box, move_to_memory) < 0)
+		return -1;
+
+	mbox->mbox_ext_idx =
+		mail_index_ext_register(mbox->box.index, "mbox",
+					sizeof(mbox->mbox_hdr),
+					sizeof(uint64_t), sizeof(uint64_t));
+	mbox->md5hdr_ext_idx =
+		mail_index_ext_register(mbox->box.index, "header-md5",
+					0, 16, 1);
+	return 0;
+}
+
 static int mbox_mailbox_open_existing(struct mbox_mailbox *mbox)
 {
 	struct mailbox *box = &mbox->box;
-	const char *rootdir;
+	const char *rootdir, *box_path = mailbox_get_path(box);
 	bool move_to_memory;
 
-	if (access(box->path, R_OK|W_OK) < 0) {
-		if (errno != EACCES) {
-			mbox_set_syscall_error(mbox, "access()");
-			return -1;
-		}
-		mbox->box.backend_readonly = TRUE;
-	}
-	move_to_memory = want_memory_indexes(mbox->storage, box->path);
+	move_to_memory = want_memory_indexes(mbox->storage, box_path);
 
 	if (box->inbox_any || strcmp(box->name, "INBOX") == 0) {
 		/* if INBOX isn't under the root directory, it's probably in
 		   /var/mail and we want to allow privileged dotlocking */
 		rootdir = mailbox_list_get_path(box->list, NULL,
 						MAILBOX_LIST_PATH_TYPE_DIR);
-		if (strncmp(box->path, rootdir, strlen(rootdir)) != 0)
+		if (strncmp(box_path, rootdir, strlen(rootdir)) != 0)
 			mbox->mbox_privileged_locking = TRUE;
 	}
 	if ((box->flags & MAILBOX_FLAG_KEEP_LOCKED) != 0) {
@@ -421,7 +422,23 @@
 					    mbox_lock_touch_timeout, mbox);
 		}
 	}
-	return index_storage_mailbox_open(box, move_to_memory);
+	return mbox_mailbox_open_finish(mbox, move_to_memory);
+}
+
+static bool mbox_storage_is_readonly(struct mailbox *box)
+{
+	struct mbox_mailbox *mbox = (struct mbox_mailbox *)box;
+
+	if (index_storage_is_readonly(box))
+		return TRUE;
+
+	if (mbox_is_backend_readonly(mbox)) {
+		/* return read-only only if there are no private flags
+		   (that are stored in index files) */
+		if (mailbox_get_private_flags_mask(box) == 0)
+			return TRUE;
+	}
+	return FALSE;
 }
 
 static int mbox_mailbox_open(struct mailbox *box)
@@ -433,14 +450,16 @@
 	if (box->input != NULL) {
 		i_stream_ref(box->input);
 		mbox->mbox_file_stream = box->input;
-		mbox->box.backend_readonly = TRUE;
+		mbox->backend_readonly = TRUE;
+		mbox->backend_readonly_set = TRUE;
 		mbox->no_mbox_file = TRUE;
-		return index_storage_mailbox_open(box, FALSE);
+		return mbox_mailbox_open_finish(mbox, FALSE);
 	}
 
-	if ((ret = stat(box->path, &st)) == 0 && !S_ISDIR(st.st_mode))
-		return mbox_mailbox_open_existing(mbox);
-	else if (ret == 0) {
+	ret = stat(mailbox_get_path(box), &st);
+	if (ret == 0) {
+		if (!S_ISDIR(st.st_mode))
+			return mbox_mailbox_open_existing(mbox);
 		mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
 				       "Mailbox isn't selectable");
 		return -1;
@@ -452,7 +471,7 @@
 		return -1;
 	} else {
 		mail_storage_set_critical(box->storage,
-					  "stat(%s) failed: %m", box->path);
+			"stat(%s) failed: %m", mailbox_get_path(box));
 		return -1;
 	}
 }
@@ -523,7 +542,7 @@
 			return -1;
 	} else {
 		/* create the mbox file */
-		ret = mailbox_create_fd(box, box->path,
+		ret = mailbox_create_fd(box, mailbox_get_path(box),
 					O_RDWR | O_CREAT | O_EXCL, &fd);
 		if (ret < 0)
 			return -1;
@@ -552,7 +571,7 @@
 	if (box->view != NULL) {
 		hdr = mail_index_get_header(box->view);
 		if ((hdr->flags & MAIL_INDEX_HDR_FLAG_HAVE_DIRTY) != 0 &&
-		    !mbox->box.backend_readonly) {
+		    !mbox_is_backend_readonly(mbox)) {
 			/* we've done changes to mbox which haven't been
 			   written yet. do it now. */
 			sync_flags |= MBOX_SYNC_REWRITE;
@@ -574,12 +593,11 @@
 }
 
 static int
-mbox_mailbox_get_guid(struct mailbox *box, uint8_t guid[MAIL_GUID_128_SIZE])
+mbox_mailbox_get_guid(struct mbox_mailbox *mbox,
+		      uint8_t guid[MAIL_GUID_128_SIZE])
 {
-	struct mbox_mailbox *mbox = (struct mbox_mailbox *)box;
-
-	if (mail_index_is_in_memory(box->index)) {
-		mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE,
+	if (mail_index_is_in_memory(mbox->box.index)) {
+		mail_storage_set_error(mbox->box.storage, MAIL_ERROR_NOTPOSSIBLE,
 			"Mailbox GUIDs are not permanent without index files");
 		return -1;
 	}
@@ -591,14 +609,28 @@
 	return 0;
 }
 
+static int
+mbox_mailbox_get_metadata(struct mailbox *box,
+			  enum mailbox_metadata_items items,
+			  struct mailbox_metadata *metadata_r)
+{
+	struct mbox_mailbox *mbox = (struct mbox_mailbox *)box;
+
+	if ((items & MAILBOX_METADATA_GUID) != 0) {
+		if (mbox_mailbox_get_guid(mbox, metadata_r->guid) < 0)
+			return -1;
+	}
+	return index_mailbox_get_metadata(box, items, metadata_r);
+}
+
 static void mbox_notify_changes(struct mailbox *box)
 {
 	struct mbox_mailbox *mbox = (struct mbox_mailbox *)box;
 
 	if (box->notify_callback == NULL)
-		index_mailbox_check_remove_all(&mbox->box);
+		index_mailbox_check_remove_all(box);
 	else if (!mbox->no_mbox_file)
-		index_mailbox_check_add(&mbox->box, mbox->box.path);
+		index_mailbox_check_add(box, mailbox_get_path(box));
 }
 
 static bool
@@ -641,8 +673,8 @@
 	struct mbox_transaction_context *mt;
 
 	mt = i_new(struct mbox_transaction_context, 1);
-	index_transaction_init(&mt->ictx, box, flags);
-	return &mt->ictx.mailbox_ctx;
+	index_transaction_init(&mt->t, box, flags);
+	return &mt->t;
 }
 
 static void mbox_transaction_unlock(struct mailbox *box, unsigned int lock_id)
@@ -687,6 +719,17 @@
 	mbox_transaction_unlock(box, lock_id);
 }
 
+bool mbox_is_backend_readonly(struct mbox_mailbox *mbox)
+{
+	if (!mbox->backend_readonly_set) {
+		mbox->backend_readonly_set = TRUE;
+		if (access(mailbox_get_path(&mbox->box), R_OK|W_OK) < 0 &&
+		    errno == EACCES)
+			mbox->backend_readonly = TRUE;
+	}
+	return mbox->backend_readonly;
+}
+
 struct mail_storage mbox_storage = {
 	.name = MBOX_STORAGE_NAME,
 	.class_flags = MAIL_STORAGE_CLASS_FLAG_MAILBOX_IS_FILE |
@@ -707,7 +750,7 @@
 
 struct mailbox mbox_mailbox = {
 	.v = {
-		index_storage_is_readonly,
+		mbox_storage_is_readonly,
 		index_storage_allow_new_keywords,
 		index_storage_mailbox_enable,
 		mbox_mailbox_open,
@@ -718,7 +761,7 @@
 		index_storage_mailbox_delete,
 		index_storage_mailbox_rename,
 		index_storage_get_status,
-		mbox_mailbox_get_guid,
+		mbox_mailbox_get_metadata,
 		NULL,
 		NULL,
 		mbox_storage_sync_init,
@@ -729,21 +772,8 @@
 		mbox_transaction_begin,
 		mbox_transaction_commit,
 		mbox_transaction_rollback,
-		index_transaction_set_max_modseq,
-		index_keywords_create,
-		index_keywords_create_from_indexes,
-		index_keywords_ref,
-		index_keywords_unref,
-		index_keyword_is_valid,
-		index_storage_get_seq_range,
-		index_storage_get_uid_range,
-		index_storage_get_expunges,
-		NULL,
-		NULL,
 		NULL,
 		index_mail_alloc,
-		index_header_lookup_init,
-		index_header_lookup_deinit,
 		index_storage_search_init,
 		index_storage_search_deinit,
 		index_storage_search_next_nonblock,
@@ -754,7 +784,6 @@
 		mbox_save_finish,
 		mbox_save_cancel,
 		mail_storage_copy,
-		NULL,
 		index_storage_is_inconsistent
 	}
 };
--- a/src/lib-storage/index/mbox/mbox-storage.h	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/mbox/mbox-storage.h	Sat Jan 01 15:59:02 2011 +0200
@@ -57,10 +57,14 @@
 	unsigned int mbox_used_privileges:1;
 	unsigned int mbox_privileged_locking:1;
 	unsigned int syncing:1;
+	unsigned int backend_readonly:1;
+	unsigned int backend_readonly_set:1;
 };
 
 struct mbox_transaction_context {
-	struct index_transaction_context ictx;
+	struct mailbox_transaction_context t;
+	union mail_index_transaction_module_context module_ctx;
+
 	unsigned int mbox_lock_id;
 };
 
@@ -85,4 +89,6 @@
 				       struct mail_index_transaction_commit_result *result);
 void mbox_transaction_save_rollback(struct mail_save_context *ctx);
 
+bool mbox_is_backend_readonly(struct mbox_mailbox *mbox);
+
 #endif
--- a/src/lib-storage/index/mbox/mbox-sync.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/mbox/mbox-sync.c	Sat Jan 01 15:59:02 2011 +0200
@@ -73,13 +73,13 @@
 		mail_storage_set_critical(&sync_ctx->mbox->storage->storage,
 			"mbox file %s was modified while we were syncing, "
 			"check your locking settings",
-			sync_ctx->mbox->box.path);
+			mailbox_get_path(&sync_ctx->mbox->box));
 	}
 
 	va_start(va, fmt);
 	mail_storage_set_critical(&sync_ctx->mbox->storage->storage,
 				  "Sync failed for mbox file %s: %s",
-				  sync_ctx->mbox->box.path,
+				  mailbox_get_path(&sync_ctx->mbox->box),
 				  t_strdup_vprintf(fmt, va));
 	va_end(va);
 }
@@ -991,7 +991,7 @@
 		i_warning("UIDVALIDITY changed (%u -> %u) in mbox file %s",
 			  sync_ctx->hdr->uid_validity,
 			  sync_ctx->base_uid_validity,
-			  sync_ctx->mbox->box.path);
+			  mailbox_get_path(&sync_ctx->mbox->box));
 		sync_ctx->index_reset = TRUE;
 		return TRUE;
 	}
@@ -1114,7 +1114,7 @@
 					&sync_ctx->mbox->storage->storage,
 					"Out of UIDs, renumbering them in mbox "
 					"file %s",
-					sync_ctx->mbox->box.path);
+					mailbox_get_path(&sync_ctx->mbox->box));
 				sync_ctx->renumber_uids = TRUE;
 				return 0;
 			}
@@ -1429,7 +1429,7 @@
 		   quite minimal (an extra logged error message). */
 		while (sync_ctx->orig_mtime == st->st_mtime) {
 			usleep(500000);
-			if (utime(sync_ctx->mbox->box.path, NULL) < 0) {
+			if (utime(mailbox_get_path(&sync_ctx->mbox->box), NULL) < 0) {
 				mbox_set_syscall_error(sync_ctx->mbox,
 						       "utime()");
 				return -1;
@@ -1678,7 +1678,7 @@
 			return -1;
 		}
 	} else {
-		if (stat(mbox->box.path, &statbuf) < 0) {
+		if (stat(mailbox_get_path(&mbox->box), &statbuf) < 0) {
 			if (errno == ENOENT) {
 				mailbox_set_deleted(&mbox->box);
 				return 0;
@@ -1733,7 +1733,7 @@
 	int ret, changed;
 	bool delay_writes, readonly;
 
-	readonly = mbox->box.backend_readonly ||
+	readonly = mbox_is_backend_readonly(mbox) ||
 		(flags & MBOX_SYNC_READONLY) != 0;
 	delay_writes = readonly ||
 		((flags & MBOX_SYNC_REWRITE) == 0 &&
@@ -1791,7 +1791,8 @@
 			/* try as read-only */
 			if (mbox_lock(mbox, F_RDLCK, lock_id) <= 0)
 				return -1;
-			mbox->box.backend_readonly = readonly = TRUE;
+			mbox->backend_readonly = readonly = TRUE;
+			mbox->backend_readonly_set = TRUE;
 			delay_writes = TRUE;
 		}
 	}
@@ -1915,7 +1916,8 @@
 		else {
 			buf.modtime = st.st_mtime;
 			buf.actime = sync_ctx.orig_atime;
-			if (utime(mbox->box.path, &buf) < 0 && errno != EPERM)
+			if (utime(mailbox_get_path(&mbox->box), &buf) < 0 &&
+			    errno != EPERM)
 				mbox_set_syscall_error(mbox, "utime()");
 		}
 	}
--- a/src/lib-storage/index/raw/raw-mail.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/raw/raw-mail.c	Sat Jan 01 15:59:02 2011 +0200
@@ -22,7 +22,8 @@
 	st = i_stream_stat(mail->box->input, TRUE);
 	if (st == NULL) {
 		mail_storage_set_critical(mail->box->storage,
-			"stat(%s) failed: %m", mail->box->path);
+					  "stat(%s) failed: %m",
+					  i_stream_get_name(mail->box->input));
 		return -1;
 	}
 
@@ -103,7 +104,8 @@
 		*value_r = mbox->envelope_sender;
 		return 0;
 	case MAIL_FETCH_UIDL_FILE_NAME:
-		*value_r = mbox->have_filename ? _mail->box->path : "";
+		*value_r = mbox->have_filename ?
+			mailbox_get_path(_mail->box) : "";
 		return 0;
 	default:
 		return index_mail_get_special(_mail, field, value_r);
--- a/src/lib-storage/index/raw/raw-storage.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/index/raw/raw-storage.c	Sat Jan 01 15:59:02 2011 +0200
@@ -62,6 +62,7 @@
 static int raw_mailbox_open(struct mailbox *box)
 {
 	struct raw_mailbox *mbox = (struct raw_mailbox *)box;
+	const char *path;
 	int fd;
 
 	if (box->input != NULL) {
@@ -69,9 +70,9 @@
 		return index_storage_mailbox_open(box, FALSE);
 	}
 
-	box->path = box->name;
+	path = box->_path = box->name;
 	mbox->have_filename = TRUE;
-	fd = open(box->path, O_RDONLY);
+	fd = open(path, O_RDONLY);
 	if (fd == -1) {
 		if (ENOTFOUND(errno)) {
 			mail_storage_set_error(box->storage,
@@ -79,12 +80,12 @@
 				T_MAIL_ERR_MAILBOX_NOT_FOUND(box->name));
 		} else if (!mail_storage_set_error_from_errno(box->storage)) {
 			mail_storage_set_critical(box->storage,
-				"open(%s) failed: %m", box->path);
+				"open(%s) failed: %m", path);
 		}
 		return -1;
 	}
 	box->input = i_stream_create_fd(fd, MAIL_READ_FULL_BLOCK_SIZE, TRUE);
-	i_stream_set_name(box->input, box->path);
+	i_stream_set_name(box->input, path);
 	i_stream_set_init_buffer_size(box->input, MAIL_READ_FULL_BLOCK_SIZE);
 	return index_storage_mailbox_open(box, FALSE);
 }
@@ -154,21 +155,8 @@
 		index_transaction_begin,
 		index_transaction_commit,
 		index_transaction_rollback,
-		index_transaction_set_max_modseq,
-		index_keywords_create,
-		index_keywords_create_from_indexes,
-		index_keywords_ref,
-		index_keywords_unref,
-		index_keyword_is_valid,
-		index_storage_get_seq_range,
-		index_storage_get_uid_range,
-		index_storage_get_expunges,
-		NULL,
-		NULL,
 		NULL,
 		index_mail_alloc,
-		index_header_lookup_init,
-		index_header_lookup_deinit,
 		index_storage_search_init,
 		index_storage_search_deinit,
 		index_storage_search_next_nonblock,
@@ -179,7 +167,6 @@
 		NULL,
 		NULL,
 		mail_storage_copy,
-		NULL,
 		index_storage_is_inconsistent
 	}
 };
--- a/src/lib-storage/index/test-index-fetch.c	Sat Jan 01 14:34:14 2011 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,154 +0,0 @@
-/* Copyright (c) 2009-2010 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "array.h"
-#include "test-common.h"
-#include "mail-index-modseq.h"
-#include "index-storage.h"
-
-static uint32_t expunge_uids[] = { 25, 15, 7, 3, 11, 1, 53, 33 };
-static uint8_t mail_guids[N_ELEMENTS(expunge_uids)][MAIL_GUID_128_SIZE];
-static unsigned int expunge_idx;
-static unsigned int nonexternal_idx;
-
-void mail_index_lookup_uid(struct mail_index_view *view ATTR_UNUSED,
-			   uint32_t seq, uint32_t *uid_r)
-{
-	*uid_r = seq;
-}
-
-bool mail_index_lookup_seq_range(struct mail_index_view *view ATTR_UNUSED,
-				 uint32_t first_uid, uint32_t last_uid,
-				 uint32_t *first_seq_r, uint32_t *last_seq_r)
-{
-	*first_seq_r = first_uid;
-	*last_seq_r = last_uid;
-	return TRUE;
-}
-
-bool mail_index_modseq_get_next_log_offset(struct mail_index_view *view ATTR_UNUSED,
-					   uint64_t modseq, uint32_t *log_seq_r,
-					   uoff_t *log_offset_r)
-{
-	*log_seq_r = modseq >> 32;
-	*log_offset_r = modseq & 0xfffffff;
-	return TRUE;
-}
-
-struct mail_transaction_log_view *
-mail_transaction_log_view_open(struct mail_transaction_log *log ATTR_UNUSED) { return NULL; }
-int mail_transaction_log_view_set(struct mail_transaction_log_view *view ATTR_UNUSED,
-				  uint32_t min_file_seq ATTR_UNUSED, uoff_t min_file_offset ATTR_UNUSED,
-				  uint32_t max_file_seq ATTR_UNUSED, uoff_t max_file_offset ATTR_UNUSED,
-				  bool *reset_r ATTR_UNUSED) {
-	if (min_file_seq < 99)
-		return 0;
-	return 1;
-}
-
-void mail_transaction_log_view_close(struct mail_transaction_log_view **view ATTR_UNUSED) { }
-
-void mail_transaction_log_get_tail(struct mail_transaction_log *log ATTR_UNUSED,
-				   uint32_t *file_seq_r)
-{
-	*file_seq_r = 100;
-}
-
-int mail_transaction_log_view_next(struct mail_transaction_log_view *view ATTR_UNUSED,
-				   const struct mail_transaction_header **hdr_r,
-				   const void **data_r)
-{
-	static struct mail_transaction_header hdr;
-	static struct mail_transaction_expunge_guid exp;
-	static struct mail_transaction_expunge old_exp;
-
-	if (expunge_idx == N_ELEMENTS(expunge_uids))
-		return 0;
-
-	if (mail_guids[expunge_idx][0] == 0) {
-		old_exp.uid1 = old_exp.uid2 = expunge_uids[expunge_idx];
-		hdr.type = MAIL_TRANSACTION_EXPUNGE;
-		hdr.size = sizeof(old_exp);
-		*data_r = &old_exp;
-	} else {
-		exp.uid = expunge_uids[expunge_idx];
-		memcpy(exp.guid_128, mail_guids[expunge_idx], sizeof(exp.guid_128));
-		hdr.type = MAIL_TRANSACTION_EXPUNGE_GUID;
-		hdr.size = sizeof(exp);
-		*data_r = &exp;
-	}
-	if (expunge_idx != nonexternal_idx)
-		hdr.type |= MAIL_TRANSACTION_EXTERNAL;
-
-	*hdr_r = &hdr;
-	expunge_idx++;
-	return 1;
-}
-
-static void test_index_storage_get_expunges(void)
-{
-	struct mailbox *box;
-	ARRAY_TYPE(seq_range) uids_filter;
-	ARRAY_TYPE(mailbox_expunge_rec) expunges;
-	const struct mailbox_expunge_rec *exp;
-	unsigned int i, count;
-	uint64_t modseq;
-
-	box = t_new(struct mailbox, 1);
-	box->index = t_new(struct mail_index, 1);
-	box->view = t_new(struct mail_index_view, 1);
-
-	box->view->log_file_head_seq = 101;
-	box->view->log_file_head_offset = 1024;
-
-	test_begin("index storage get expunges");
-
-	nonexternal_idx = 1;
-	memset(mail_guids + 2, 0, MAIL_GUID_128_SIZE);
-	memset(mail_guids + 4, 0, MAIL_GUID_128_SIZE);
-
-	t_array_init(&uids_filter, 32);
-	seq_range_array_add_range(&uids_filter, 1, 20);
-	seq_range_array_add_range(&uids_filter, 53, 53);
-
-	t_array_init(&expunges, 32);
-	modseq = 98ULL << 32;
-	for (i = 0; i < 2; i++) {
-		test_assert(index_storage_get_expunges(box, modseq, &uids_filter,
-						       &expunges) == i);
-
-		exp = array_get(&expunges, &count);
-		test_assert(count == 5);
-		test_assert(exp[0].uid == 7);
-		test_assert(memcmp(exp[0].guid_128, mail_guids[2], MAIL_GUID_128_SIZE) == 0);
-		test_assert(exp[1].uid == 3);
-		test_assert(memcmp(exp[1].guid_128, mail_guids[3], MAIL_GUID_128_SIZE) == 0);
-		test_assert(exp[2].uid == 11);
-		test_assert(memcmp(exp[2].guid_128, mail_guids[4], MAIL_GUID_128_SIZE) == 0);
-		test_assert(exp[3].uid == 1);
-		test_assert(memcmp(exp[3].guid_128, mail_guids[5], MAIL_GUID_128_SIZE) == 0);
-		test_assert(exp[4].uid == 53);
-		test_assert(memcmp(exp[4].guid_128, mail_guids[6], MAIL_GUID_128_SIZE) == 0);
-
-		array_clear(&uids_filter);
-		expunge_idx = 0;
-		modseq = 100ULL << 32;
-	}
-
-	test_end();
-}
-
-int main(void)
-{
-	static void (*test_functions[])(void) = {
-		test_index_storage_get_expunges,
-		NULL
-	};
-	unsigned int i, j;
-
-	for (i = 0; i < N_ELEMENTS(mail_guids); i++) {
-		for (j = 0; j < MAIL_GUID_128_SIZE; j++)
-			mail_guids[i][j] = j + i + 1;
-	}
-	return test_run(test_functions);
-}
--- a/src/lib-storage/list/index-mailbox-list-sync.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/list/index-mailbox-list-sync.c	Sat Jan 01 15:59:02 2011 +0200
@@ -159,7 +159,7 @@
 	return ret;
 }
 
-static void
+static int
 index_list_get_status(struct mailbox *box, enum mailbox_status_items items,
 		      struct mailbox_status *status)
 {
@@ -167,11 +167,11 @@
 
 	if ((items & ~CACHED_STATUS_ITEMS) == 0) {
 		if (index_list_get_cached_status(box, status) > 0)
-			return;
+			return 0;
 		/* nonsynced / error, fallback to doing it the slow way */
 	}
 
-	ibox->module_ctx.super.get_status(box, items, status);
+	return ibox->module_ctx.super.get_status(box, items, status);
 }
 
 static int index_list_lookup_or_create(struct index_mailbox_list *ilist,
@@ -340,7 +340,7 @@
 
 	view = mail_index_view_open(ilist->mail_index);
 	if (mail_index_lookup_seq(view, uid, &seq)) {
-		mailbox_get_status(box, CACHED_STATUS_ITEMS, &status);
+		mailbox_get_open_status(box, CACHED_STATUS_ITEMS, &status);
 		(void)index_list_update(ilist, box, view, seq, &status);
 	}
 	mail_index_view_close(&view);
--- a/src/lib-storage/list/mailbox-list-fs.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/list/mailbox-list-fs.c	Sat Jan 01 15:59:02 2011 +0200
@@ -254,16 +254,8 @@
 		errno = ENOENT;
 	}
 
-	if (!mailbox_list_is_valid_create_name(_list, name)) {
-		*status = MAILBOX_NAME_INVALID;
-		return 0;
-	}
-
 	if (ENOTFOUND(errno) || errno == EACCES) {
-		*status = MAILBOX_NAME_VALID;
-		return 0;
-	} else if (errno == ENOTDIR) {
-		*status = MAILBOX_NAME_NOINFERIORS;
+		*status = MAILBOX_NAME_NONEXISTENT;
 		return 0;
 	} else {
 		mailbox_list_set_critical(_list, "stat(%s) failed: %m", path);
@@ -339,8 +331,7 @@
 		path = t_strdup_until(path, p);
 	}
 
-	mailbox_list_get_dir_permissions(list, NULL, &mode,
-					 &gid, &gid_origin);
+	mailbox_list_get_root_dir_permissions(list, &mode, &gid, &gid_origin);
 	if (mkdir_parents_chgrp(path, mode, gid, gid_origin) == 0)
 		return 0;
 	else if (errno == EEXIST) {
@@ -434,7 +425,7 @@
 	if (fs_list_rmdir(list, name, path) == 0)
 		return 0;
 
-	if (errno == ENOENT) {
+	if (errno == ENOENT || errno == ENOTDIR) {
 		mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
 			T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
 	} else if (errno == ENOTEMPTY || errno == EEXIST) {
@@ -541,8 +532,8 @@
 	/* create the hierarchy */
 	p = strrchr(newpath, '/');
 	if (p != NULL) {
-		mailbox_list_get_dir_permissions(newlist, NULL, &mode,
-						 &gid, &origin);
+		mailbox_list_get_root_dir_permissions(newlist, &mode,
+						      &gid, &origin);
 		p = t_strdup_until(newpath, p);
 		if (mkdir_parents_chgrp(p, mode, gid, origin) < 0 &&
 		    errno != EEXIST) {
--- a/src/lib-storage/list/mailbox-list-maildir.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/list/mailbox-list-maildir.c	Sat Jan 01 15:59:02 2011 +0200
@@ -240,13 +240,8 @@
 		return 0;
 	}
 
-	if (!mailbox_list_is_valid_create_name(_list, name)) {
-		*status = MAILBOX_NAME_INVALID;
-		return 0;
-	}
-
 	if (ENOTFOUND(errno) || errno == EACCES) {
-		*status = MAILBOX_NAME_VALID;
+		*status = MAILBOX_NAME_NONEXISTENT;
 		return 0;
 	} else {
 		mailbox_list_set_critical(_list, "stat(%s) failed: %m", path);
@@ -289,7 +284,7 @@
 
 	/* Maildir++ spec wants that maildirfolder named file is created for
 	   all subfolders. */
-	mailbox_list_get_permissions(list, NULL, &mode, &gid, &gid_origin);
+	mailbox_list_get_root_permissions(list, &mode, &gid, &gid_origin);
 
 	path = t_strconcat(dir, "/" MAILDIR_SUBFOLDER_FILENAME, NULL);
 	old_mask = umask(0);
@@ -352,8 +347,8 @@
 
 	root_dir = mailbox_list_get_path(list, NULL,
 					 MAILBOX_LIST_PATH_TYPE_MAILBOX);
-	mailbox_list_get_dir_permissions(list, NULL, &mode,
-					 &gid, &gid_origin);
+	mailbox_list_get_root_dir_permissions(list, &mode,
+					      &gid, &gid_origin);
 	if (mkdir_parents_chgrp(path, mode, gid, gid_origin) == 0) {
 		/* ok */
 	} else if (errno == EEXIST) {
@@ -440,7 +435,7 @@
 	if (stat(path, &st) == 0) {
 		mailbox_list_set_error(list, MAIL_ERROR_EXISTS,
 				       "Mailbox exists");
-	} else if (errno == ENOENT) {
+	} else if (errno == ENOENT || errno == ENOTDIR) {
 		mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
 			T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
 	} else {
--- a/src/lib-storage/list/mailbox-list-none.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/list/mailbox-list-none.c	Sat Jan 01 15:59:02 2011 +0200
@@ -62,7 +62,7 @@
 				  const char *name ATTR_UNUSED,
 				  enum mailbox_name_status *status)
 {
-	*status = MAILBOX_NAME_VALID;
+	*status = MAILBOX_NAME_NONEXISTENT;
 	return 0;
 }
 
--- a/src/lib-storage/list/subscription-file.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/list/subscription-file.c	Sat Jan 01 15:59:02 2011 +0200
@@ -108,7 +108,7 @@
 	dotlock_set.timeout = SUBSCRIPTION_FILE_LOCK_TIMEOUT;
 	dotlock_set.stale_timeout = SUBSCRIPTION_FILE_CHANGE_TIMEOUT;
 
-	mailbox_list_get_permissions(list, NULL, &mode, &gid, &origin);
+	mailbox_list_get_root_permissions(list, &mode, &gid, &origin);
 	fd_out = file_dotlock_open_group(&dotlock_set, path, 0,
 					 mode, gid, origin, &dotlock);
 	if (fd_out == -1 && errno == ENOENT) {
--- a/src/lib-storage/mail-copy.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/mail-copy.c	Sat Jan 01 15:59:02 2011 +0200
@@ -64,7 +64,7 @@
 		/* keywords gets unreferenced twice: first in
 		   mailbox_save_cancel()/_finish() and second time in
 		   mailbox_copy(). */
-		mailbox_keywords_ref(ctx->transaction->box, ctx->keywords);
+		mailbox_keywords_ref(ctx->keywords);
 	}
 
 	if (mail_storage_try_copy(&ctx, mail) < 0) {
@@ -78,7 +78,12 @@
 bool mail_storage_copy_can_use_hardlink(struct mailbox *src,
 					struct mailbox *dest)
 {
-	return src->file_create_mode == dest->file_create_mode &&
-		src->file_create_gid == dest->file_create_gid &&
+	const struct mailbox_permissions *src_perm =
+		mailbox_get_permissions(src);
+	const struct mailbox_permissions *dest_perm =
+		mailbox_get_permissions(dest);
+
+	return src_perm->file_create_mode == dest_perm->file_create_mode &&
+		src_perm->file_create_gid == dest_perm->file_create_gid &&
 		!dest->disable_reflink_copy_to;
 }
--- a/src/lib-storage/mail-search.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/mail-search.c	Sat Jan 01 15:59:02 2011 +0200
@@ -158,7 +158,7 @@
 		case SEARCH_KEYWORDS:
 			if (arg->value.keywords == NULL)
 				break;
-			mailbox_keywords_unref(args->box, &arg->value.keywords);
+			mailbox_keywords_unref(&arg->value.keywords);
 			break;
 		case SEARCH_MAILBOX_GLOB:
 			if (arg->value.mailbox_glob == NULL)
@@ -619,8 +619,8 @@
 		new_kw = mailbox_keywords_create_from_indexes(box,
 							      &new_indexes);
 	} T_END;
-	mailbox_keywords_unref(box, _kw1);
-	mailbox_keywords_unref(box, _kw2);
+	mailbox_keywords_unref(_kw1);
+	mailbox_keywords_unref(_kw2);
 	return new_kw;
 }
 
--- a/src/lib-storage/mail-storage-private.h	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/mail-storage-private.h	Sat Jan 01 15:59:02 2011 +0200
@@ -114,9 +114,11 @@
 	int (*rename)(struct mailbox *src, struct mailbox *dest,
 		      bool rename_children);
 
-	void (*get_status)(struct mailbox *box, enum mailbox_status_items items,
-			   struct mailbox_status *status_r);
-	int (*get_guid)(struct mailbox *box, uint8_t guid[MAIL_GUID_128_SIZE]);
+	int (*get_status)(struct mailbox *box, enum mailbox_status_items items,
+			  struct mailbox_status *status_r);
+	int (*get_metadata)(struct mailbox *box,
+			    enum mailbox_metadata_items items,
+			    struct mailbox_metadata *metadata_r);
 
 	/* Lookup sync extension record and figure out if it mailbox has
 	   changed since. Returns 1 = yes, 0 = no, -1 = error. */
@@ -150,51 +152,14 @@
 	int (*transaction_commit)(struct mailbox_transaction_context *t,
 				  struct mail_transaction_commit_changes *changes_r);
 	void (*transaction_rollback)(struct mailbox_transaction_context *t);
-	void (*transaction_set_max_modseq)(struct mailbox_transaction_context *t,
-					   uint64_t max_modseq,
-					   ARRAY_TYPE(seq_range) *seqs);
 
-	int (*keywords_create)(struct mailbox *box,
-			       const char *const keywords[],
-			       struct mail_keywords **keywords_r,
-			       bool skip_invalid);
-	struct mail_keywords *
-		(*keywords_create_from_indexes)(struct mailbox *box,
-						const ARRAY_TYPE(keyword_indexes) *idx);
-	void (*keywords_ref)(struct mail_keywords *keywords);
-	void (*keywords_unref)(struct mail_keywords *keywords);
-	bool (*keyword_is_valid)(struct mailbox *box, const char *keyword,
-				 const char **error_r);
-
-	void (*get_seq_range)(struct mailbox *box, uint32_t uid1, uint32_t uid2,
-			      uint32_t *seq1_r, uint32_t *seq2_r);
-	void (*get_uid_range)(struct mailbox *box,
-			      const ARRAY_TYPE(seq_range) *seqs,
-			      ARRAY_TYPE(seq_range) *uids);
-	bool (*get_expunges)(struct mailbox *box, uint64_t prev_modseq,
-			     const ARRAY_TYPE(seq_range) *uids_filter,
-			     ARRAY_TYPE(mailbox_expunge_rec) *expunges);
-	bool (*get_virtual_uid)(struct mailbox *box,
-				const char *backend_mailbox,
-				uint32_t backend_uidvalidity,
-				uint32_t backend_uid, uint32_t *uid_r);
-	void (*get_virtual_backend_boxes)(struct mailbox *box,
-					  ARRAY_TYPE(mailboxes) *mailboxes,
-					  bool only_with_msgs);
-	void (*get_virtual_box_patterns)(struct mailbox *box,
-				ARRAY_TYPE(mailbox_virtual_patterns) *includes,
-				ARRAY_TYPE(mailbox_virtual_patterns) *excludes);
+	enum mail_flags (*get_private_flags_mask)(struct mailbox *box);
 
 	struct mail *
 		(*mail_alloc)(struct mailbox_transaction_context *t,
 			      enum mail_fetch_field wanted_fields,
 			      struct mailbox_header_lookup_ctx *wanted_headers);
 
-	struct mailbox_header_lookup_ctx *
-		(*header_lookup_init)(struct mailbox *box,
-				      const char *const headers[]);
-	void (*header_lookup_deinit)(struct mailbox_header_lookup_ctx *ctx);
-
 	struct mail_search_context *
 	(*search_init)(struct mailbox_transaction_context *t,
 		       struct mail_search_args *args,
@@ -212,9 +177,6 @@
 	int (*save_finish)(struct mail_save_context *ctx);
 	void (*save_cancel)(struct mail_save_context *ctx);
 	int (*copy)(struct mail_save_context *ctx, struct mail *mail);
-	/* returns TRUE if message part is an attachment. */
-	bool (*save_is_attachment)(struct mail_save_context *ctx,
-				   const struct mail_attachment_part *part);
 
 	bool (*is_inconsistent)(struct mailbox *box);
 };
@@ -224,8 +186,20 @@
 	struct mail_storage_module_register *reg;
 };
 
+struct mailbox_permissions {
+	/* mode and GID to use for newly created files/dirs */
+	mode_t file_create_mode, dir_create_mode;
+	gid_t file_create_gid;
+	/* origin (e.g. path) where the file_create_gid was got from */
+	const char *file_create_gid_origin;
+
+	bool mail_index_permissions_set;
+};
+
 struct mailbox {
 	const char *name;
+	/* mailbox's virtual name (from mail_namespace_get_vname()) */
+	const char *vname;
 	struct mail_storage *storage;
 	struct mailbox_list *list;
 
@@ -233,31 +207,24 @@
 /* private: */
 	pool_t pool;
 
+	/* these won't be set until mailbox is opened: */
 	struct mail_index *index;
 	struct mail_index_view *view;
 	struct mail_cache *cache;
+	/* Filled lazily by mailbox_get_permissions() */
+	struct mailbox_permissions _perm;
+	/* Filled lazily by mailbox_get_path() */
+	const char *_path;
 
 	/* default vfuncs for new struct mails. */
 	const struct mail_vfuncs *mail_vfuncs;
 
-	/* mailbox's MAILBOX_LIST_PATH_TYPE_MAILBOX */
-	const char *path;
-	/* mailbox's virtual name (from mail_namespace_get_vname()) */
-	const char *vname;
 	struct istream *input;
+	const char *index_prefix;
 	enum mailbox_flags flags;
 	unsigned int transaction_count;
 	enum mailbox_feature enabled_features;
 
-	/* User's private flags if this is a shared mailbox */
-	enum mail_flags private_flags_mask;
-
-	/* mode and GID to use for newly created files/dirs */
-	mode_t file_create_mode, dir_create_mode;
-	gid_t file_create_gid;
-	/* origin (e.g. path) where the file_create_gid was got from */
-	const char *file_create_gid_origin;
-
 	/* Mailbox notification settings: */
 	unsigned int notify_min_interval;
 	mailbox_notify_callback_t *notify_callback;
@@ -274,8 +241,6 @@
 	unsigned int opened:1;
 	/* Mailbox was deleted while we had it open. */
 	unsigned int mailbox_deleted:1;
-	/* we've discovered there aren't enough permissions to modify mailbox */
-	unsigned int backend_readonly:1;
 	/* Mailbox is being deleted */
 	unsigned int deleting:1;
 	/* Mailbox was already marked as deleted within this allocation. */
@@ -383,10 +348,17 @@
 	struct mailbox *box;
 	enum mailbox_transaction_flags flags;
 
+	union mail_index_transaction_module_context module_ctx;
+	struct mail_index_transaction_vfuncs super;
+	int mail_ref_count;
+
 	struct mail_index_transaction *itrans;
 	/* view contains all changes done within this transaction */
 	struct mail_index_view *view;
 
+	struct mail_cache_view *cache_view;
+	struct mail_cache_transaction_ctx *cache_trans;
+
 	struct mail_transaction_commit_changes *changes;
 	ARRAY_DEFINE(module_contexts,
 		     union mailbox_transaction_module_context *);
@@ -438,6 +410,10 @@
 
 	struct mail_save_attachment *attach;
 
+	/* returns TRUE if message part is an attachment. */
+	bool (*part_is_attachment)(struct mail_save_context *ctx,
+				   const struct mail_attachment_part *part);
+
 	/* we came here from mailbox_copy() */
 	unsigned int copying:1;
 };
@@ -448,8 +424,12 @@
 
 struct mailbox_header_lookup_ctx {
 	struct mailbox *box;
-	const char *const *headers;
+	pool_t pool;
 	int refcount;
+
+	unsigned int count;
+	const char *const *name;
+	unsigned int *idx;
 };
 
 /* Modules should use do "my_id = mail_storage_module_id++" and
@@ -484,6 +464,11 @@
 void mail_set_expunged(struct mail *mail);
 void mailbox_set_deleted(struct mailbox *box);
 int mailbox_mark_index_deleted(struct mailbox *box, bool del);
+/* Easy wrapper for getting mailbox's MAILBOX_LIST_PATH_TYPE_MAILBOX */
+const char *mailbox_get_path(struct mailbox *box) ATTR_PURE;
+/* Get mailbox permissions. */
+const struct mailbox_permissions *mailbox_get_permissions(struct mailbox *box);
+/* Force permissions to be refreshed on next lookup */
 void mailbox_refresh_permissions(struct mailbox *box);
 
 /* Returns -1 if error, 0 if failed with EEXIST, 1 if ok */
--- a/src/lib-storage/mail-storage.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/mail-storage.c	Sat Jan 01 15:59:02 2011 +0200
@@ -243,7 +243,7 @@
 		return ret;
 
 	/* we need to create the root directory. */
-	mailbox_list_get_dir_permissions(list, NULL, &mode, &gid, &origin);
+	mailbox_list_get_root_dir_permissions(list, &mode, &gid, &origin);
 	if (mkdir_parents_chgrp(root_dir, mode, gid, origin) < 0 &&
 	    errno != EEXIST) {
 		*error_r = mail_error_create_eacces_msg("mkdir", root_dir);
@@ -652,7 +652,6 @@
 
 	box->opened = FALSE;
 	box->mailbox_deleted = FALSE;
-	box->backend_readonly = FALSE;
 	array_clear(&box->search_results);
 }
 
@@ -905,31 +904,45 @@
 	return ns1 == ns2;
 }
 
-void mailbox_get_status(struct mailbox *box,
-			enum mailbox_status_items items,
-			struct mailbox_status *status_r)
+int mailbox_get_status(struct mailbox *box,
+		       enum mailbox_status_items items,
+		       struct mailbox_status *status_r)
 {
-	box->v.get_status(box, items, status_r);
+	return box->v.get_status(box, items, status_r);
 }
 
-int mailbox_get_guid(struct mailbox *box, uint8_t guid[MAIL_GUID_128_SIZE])
+void mailbox_get_open_status(struct mailbox *box,
+			     enum mailbox_status_items items,
+			     struct mailbox_status *status_r)
 {
-	if (box->v.get_guid == NULL) {
-		mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE,
-				       "Storage doesn't support mailbox GUIDs");
-		return -1;
-	}
+	i_assert(box->opened);
+	if (box->v.get_status(box, items, status_r) < 0)
+		i_unreached();
+}
+
+int mailbox_get_metadata(struct mailbox *box, enum mailbox_metadata_items items,
+			 struct mailbox_metadata *metadata_r)
+{
 	if (!box->opened) {
 		if (mailbox_open(box) < 0)
 			return -1;
 	}
-	if (box->v.get_guid(box, guid) < 0)
+	if (box->v.get_metadata(box, items, metadata_r) < 0)
 		return -1;
 
-	i_assert(!mail_guid_128_is_empty(guid));
+	i_assert((items & MAILBOX_METADATA_GUID) == 0 ||
+		 !mail_guid_128_is_empty(metadata_r->guid));
 	return 0;
 }
 
+enum mail_flags mailbox_get_private_flags_mask(struct mailbox *box)
+{
+	if (box->v.get_private_flags_mask == NULL)
+		return 0;
+	else
+		return box->v.get_private_flags_mask(box);
+}
+
 struct mailbox_sync_context *
 mailbox_sync_init(struct mailbox *box, enum mailbox_sync_flags flags)
 {
@@ -1006,139 +1019,6 @@
 	mailbox_notify_changes(box, 0, NULL, NULL);
 }
 
-int mailbox_keywords_create(struct mailbox *box, const char *const keywords[],
-			    struct mail_keywords **keywords_r)
-{
-	const char *empty_keyword_list = NULL;
-
-	if (keywords == NULL)
-		keywords = &empty_keyword_list;
-	return box->v.keywords_create(box, keywords, keywords_r, FALSE);
-}
-
-struct mail_keywords *
-mailbox_keywords_create_valid(struct mailbox *box,
-			      const char *const keywords[])
-{
-	const char *empty_keyword_list = NULL;
-	struct mail_keywords *kw;
-
-	if (keywords == NULL)
-		keywords = &empty_keyword_list;
-	if (box->v.keywords_create(box, keywords, &kw, TRUE) < 0)
-		i_unreached();
-	return kw;
-}
-
-struct mail_keywords *
-mailbox_keywords_create_from_indexes(struct mailbox *box,
-				     const ARRAY_TYPE(keyword_indexes) *idx)
-{
-	return box->v.keywords_create_from_indexes(box, idx);
-}
-
-void mailbox_keywords_ref(struct mailbox *box, struct mail_keywords *keywords)
-{
-	box->v.keywords_ref(keywords);
-}
-
-void mailbox_keywords_unref(struct mailbox *box,
-			    struct mail_keywords **_keywords)
-{
-	struct mail_keywords *keywords = *_keywords;
-
-	*_keywords = NULL;
-	box->v.keywords_unref(keywords);
-}
-
-bool mailbox_keyword_is_valid(struct mailbox *box, const char *keyword,
-			      const char **error_r)
-{
-	return box->v.keyword_is_valid(box, keyword, error_r);
-}
-
-void mailbox_get_seq_range(struct mailbox *box, uint32_t uid1, uint32_t uid2,
-			   uint32_t *seq1_r, uint32_t *seq2_r)
-{
-	box->v.get_seq_range(box, uid1, uid2, seq1_r, seq2_r);
-}
-
-void mailbox_get_uid_range(struct mailbox *box,
-			   const ARRAY_TYPE(seq_range) *seqs,
-			   ARRAY_TYPE(seq_range) *uids)
-{
-	box->v.get_uid_range(box, seqs, uids);
-}
-
-bool mailbox_get_expunges(struct mailbox *box, uint64_t prev_modseq,
-			  const ARRAY_TYPE(seq_range) *uids_filter,
-			  ARRAY_TYPE(mailbox_expunge_rec) *expunges)
-{
-	return box->v.get_expunges(box, prev_modseq,
-				   uids_filter, expunges);
-}
-
-bool mailbox_get_virtual_uid(struct mailbox *box, const char *backend_mailbox,
-			     uint32_t backend_uidvalidity,
-			     uint32_t backend_uid, uint32_t *uid_r)
-{
-	if (box->v.get_virtual_uid == NULL)
-		return FALSE;
-	return box->v.get_virtual_uid(box, backend_mailbox, backend_uidvalidity,
-				      backend_uid, uid_r);
-}
-
-void mailbox_get_virtual_backend_boxes(struct mailbox *box,
-				       ARRAY_TYPE(mailboxes) *mailboxes,
-				       bool only_with_msgs)
-{
-	if (box->v.get_virtual_backend_boxes == NULL)
-		array_append(mailboxes, &box, 1);
-	else
-		box->v.get_virtual_backend_boxes(box, mailboxes, only_with_msgs);
-}
-
-void mailbox_get_virtual_box_patterns(struct mailbox *box,
-				ARRAY_TYPE(mailbox_virtual_patterns) *includes,
-				ARRAY_TYPE(mailbox_virtual_patterns) *excludes)
-{
-	if (box->v.get_virtual_box_patterns == NULL) {
-		struct mailbox_virtual_pattern pat;
-
-		memset(&pat, 0, sizeof(pat));
-		pat.ns = box->list->ns;
-		pat.pattern = box->name;
-		array_append(includes, &pat, 1);
-	} else {
-		box->v.get_virtual_box_patterns(box, includes, excludes);
-	}
-}
-
-struct mailbox_header_lookup_ctx *
-mailbox_header_lookup_init(struct mailbox *box, const char *const headers[])
-{
-	return box->v.header_lookup_init(box, headers);
-}
-
-void mailbox_header_lookup_ref(struct mailbox_header_lookup_ctx *ctx)
-{
-	i_assert(ctx->refcount > 0);
-	ctx->refcount++;
-}
-
-void mailbox_header_lookup_unref(struct mailbox_header_lookup_ctx **_ctx)
-{
-	struct mailbox_header_lookup_ctx *ctx = *_ctx;
-
-	*_ctx = NULL;
-
-	i_assert(ctx->refcount > 0);
-	if (--ctx->refcount > 0)
-		return;
-
-	ctx->box->v.header_lookup_deinit(ctx);
-}
-
 struct mail_search_context *
 mailbox_search_init(struct mailbox_transaction_context *t,
 		    struct mail_search_args *args,
@@ -1274,7 +1154,7 @@
 					uint64_t max_modseq,
 					ARRAY_TYPE(seq_range) *seqs)
 {
-	t->box->v.transaction_set_max_modseq(t, max_modseq, seqs);
+	mail_index_transaction_set_max_modseq(t->itrans, max_modseq, seqs);
 }
 
 struct mailbox *
@@ -1301,7 +1181,7 @@
 	ctx->flags = flags;
 	ctx->keywords = keywords;
 	if (keywords != NULL)
-		mailbox_keywords_ref(ctx->transaction->box, keywords);
+		mailbox_keywords_ref(keywords);
 }
 
 void mailbox_save_copy_flags(struct mail_save_context *ctx, struct mail *mail)
@@ -1409,20 +1289,19 @@
 	*_ctx = NULL;
 	ret = box->v.save_finish(ctx);
 	if (keywords != NULL)
-		mailbox_keywords_unref(box, &keywords);
+		mailbox_keywords_unref(&keywords);
 	return ret;
 }
 
 void mailbox_save_cancel(struct mail_save_context **_ctx)
 {
 	struct mail_save_context *ctx = *_ctx;
-	struct mailbox *box = ctx->transaction->box;
 	struct mail_keywords *keywords = ctx->keywords;
 
 	*_ctx = NULL;
 	ctx->transaction->box->v.save_cancel(ctx);
 	if (keywords != NULL)
-		mailbox_keywords_unref(box, &keywords);
+		mailbox_keywords_unref(&keywords);
 }
 
 int mailbox_copy(struct mail_save_context **_ctx, struct mail *mail)
@@ -1442,7 +1321,7 @@
 
 	ret = ctx->transaction->box->v.copy(ctx, mail);
 	if (keywords != NULL)
-		mailbox_keywords_unref(box, &keywords);
+		mailbox_keywords_unref(&keywords);
 	return ret;
 }
 
@@ -1458,42 +1337,76 @@
 	box->mailbox_deleted = TRUE;
 }
 
-void mailbox_refresh_permissions(struct mailbox *box)
+const char *mailbox_get_path(struct mailbox *box)
+{
+	const char *path;
+
+	if (box->_path == NULL) {
+		path = mailbox_list_get_path(box->list, box->name,
+					     MAILBOX_LIST_PATH_TYPE_MAILBOX);
+		box->_path = p_strdup(box->pool, path);
+	}
+	return box->_path;
+}
+
+static void mailbox_get_permissions_if_not_set(struct mailbox *box)
 {
 	const char *origin, *dir_origin;
 	gid_t dir_gid;
 
+	if (box->_perm.file_create_mode != 0)
+		return;
+
 	if (box->input != NULL) {
-		box->file_create_mode = 0600;
-		box->dir_create_mode = 0700;
-		box->file_create_gid = (gid_t)-1;
-		box->file_create_gid_origin = "defaults";
+		box->_perm.file_create_mode = 0600;
+		box->_perm.dir_create_mode = 0700;
+		box->_perm.file_create_gid = (gid_t)-1;
+		box->_perm.file_create_gid_origin = "defaults";
 		return;
 	}
 
 	mailbox_list_get_permissions(box->list, box->name,
-				     &box->file_create_mode,
-				     &box->file_create_gid, &origin);
+				     &box->_perm.file_create_mode,
+				     &box->_perm.file_create_gid, &origin);
+	box->_perm.file_create_gid_origin = p_strdup(box->pool, origin);
+	mailbox_list_get_dir_permissions(box->list, box->name,
+					 &box->_perm.dir_create_mode,
+					 &dir_gid, &dir_origin);
+}
+
+const struct mailbox_permissions *mailbox_get_permissions(struct mailbox *box)
+{
+	mailbox_get_permissions_if_not_set(box);
 
-	box->file_create_gid_origin = p_strdup(box->pool, origin);
-	mailbox_list_get_dir_permissions(box->list, box->name,
-					 &box->dir_create_mode,
-					 &dir_gid, &dir_origin);
+	if (!box->_perm.mail_index_permissions_set && box->index != NULL) {
+		box->_perm.mail_index_permissions_set = TRUE;
+		mail_index_set_permissions(box->index,
+					   box->_perm.file_create_mode,
+					   box->_perm.file_create_gid,
+					   box->_perm.file_create_gid_origin);
+	}
+	return &box->_perm;
+}
+
+void mailbox_refresh_permissions(struct mailbox *box)
+{
+	memset(&box->_perm, 0, sizeof(box->_perm));
+	(void)mailbox_get_permissions(box);
 }
 
 int mailbox_create_fd(struct mailbox *box, const char *path, int flags,
 		      int *fd_r)
 {
+	const struct mailbox_permissions *perm = mailbox_get_permissions(box);
 	mode_t old_mask;
 	int fd;
 
-	i_assert(box->file_create_mode != 0);
 	i_assert((flags & O_CREAT) != 0);
 
 	*fd_r = -1;
 
 	old_mask = umask(0);
-	fd = open(path, flags, box->file_create_mode);
+	fd = open(path, flags, perm->file_create_mode);
 	umask(old_mask);
 
 	if (fd != -1) {
@@ -1504,6 +1417,10 @@
 	} else if (errno == ENOENT) {
 		mailbox_set_deleted(box);
 		return -1;
+	} else if (errno == ENOTDIR) {
+		mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE,
+			"Mailbox doesn't allow inferior mailboxes");
+		return -1;
 	} else if (mail_storage_set_error_from_errno(box->storage)) {
 		return -1;
 	} else {
@@ -1512,14 +1429,14 @@
 		return -1;
 	}
 
-	if (box->file_create_gid != (gid_t)-1) {
-		if (fchown(fd, (uid_t)-1, box->file_create_gid) == 0) {
+	if (perm->file_create_gid != (gid_t)-1) {
+		if (fchown(fd, (uid_t)-1, perm->file_create_gid) == 0) {
 			/* ok */
 		} else if (errno == EPERM) {
 			mail_storage_set_critical(box->storage, "%s",
 				eperm_error_get_chgrp("fchown", path,
-					box->file_create_gid,
-					box->file_create_gid_origin));
+					perm->file_create_gid,
+					perm->file_create_gid_origin));
 		} else {
 			mail_storage_set_critical(box->storage,
 				"fchown(%s) failed: %m", path);
--- a/src/lib-storage/mail-storage.h	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/mail-storage.h	Sat Jan 01 15:59:02 2011 +0200
@@ -64,9 +64,13 @@
 	STATUS_UNSEEN		= 0x10,
 	STATUS_FIRST_UNSEEN_SEQ	= 0x20,
 	STATUS_KEYWORDS		= 0x40,
-	STATUS_HIGHESTMODSEQ	= 0x80,
-	STATUS_CACHE_FIELDS	= 0x100,
-	STATUS_VIRTUAL_SIZE	= 0x200
+	STATUS_HIGHESTMODSEQ	= 0x80
+};
+
+enum mailbox_metadata_items {
+	MAILBOX_METADATA_GUID		= 0x01,
+	MAILBOX_METADATA_VIRTUAL_SIZE	= 0x02,
+	MAILBOX_METADATA_CACHE_FIELDS	= 0x04
 };
 
 enum mailbox_search_result_flags {
@@ -185,17 +189,21 @@
 
 	uint32_t first_unseen_seq;
 	uint64_t highest_modseq;
-	/* sum of virtual size of all messages in mailbox */
-	uint64_t virtual_size;
 
 	const ARRAY_TYPE(keywords) *keywords;
-	/* Fields that have "temp" or "yes" caching decision. */
-	const ARRAY_TYPE(const_string) *cache_fields;
 
 	/* Modseqs aren't permanent (index is in memory) */
 	unsigned int nonpermanent_modseqs:1;
 };
 
+struct mailbox_metadata {
+	uint8_t guid[MAIL_GUID_128_SIZE];
+	/* sum of virtual size of all messages in mailbox */
+	uint64_t virtual_size;
+	/* Fields that have "temp" or "yes" caching decision. */
+	const ARRAY_TYPE(const_string) *cache_fields;
+};
+
 struct mailbox_update {
 	/* All non-zero fields are changed. */
 	uint8_t mailbox_guid[MAIL_GUID_128_SIZE];
@@ -393,11 +401,19 @@
    do forced CLOSE. */
 bool mailbox_is_inconsistent(struct mailbox *box);
 
-/* Gets the mailbox status information. */
-void mailbox_get_status(struct mailbox *box, enum mailbox_status_items items,
-			struct mailbox_status *status_r);
-/* Get mailbox GUID, creating it if necessary. */
-int mailbox_get_guid(struct mailbox *box, uint8_t guid[MAIL_GUID_128_SIZE]);
+/* Gets the mailbox status information, opening the mailbox if necessary. */
+int mailbox_get_status(struct mailbox *box, enum mailbox_status_items items,
+		       struct mailbox_status *status_r);
+/* Gets the mailbox status, requires that mailbox is already opened. */
+void mailbox_get_open_status(struct mailbox *box,
+			     enum mailbox_status_items items,
+			     struct mailbox_status *status_r);
+/* Gets mailbox metadata */
+int mailbox_get_metadata(struct mailbox *box, enum mailbox_metadata_items items,
+			 struct mailbox_metadata *metadata_r);
+/* Returns a mask of flags that are private to user in this mailbox
+   (as opposed to flags shared between users). */
+enum mail_flags mailbox_get_private_flags_mask(struct mailbox *box);
 
 /* Synchronize the mailbox. */
 struct mailbox_sync_context *
@@ -461,22 +477,6 @@
 bool mailbox_get_expunges(struct mailbox *box, uint64_t prev_modseq,
 			  const ARRAY_TYPE(seq_range) *uids_filter,
 			  ARRAY_TYPE(mailbox_expunge_rec) *expunges);
-/* If box is a virtual mailbox, look up UID for the given backend message.
-   Returns TRUE if found, FALSE if not. */
-bool mailbox_get_virtual_uid(struct mailbox *box, const char *backend_mailbox,
-			     uint32_t backend_uidvalidity,
-			     uint32_t backend_uid, uint32_t *uid_r);
-/* If box is a virtual mailbox, return all backend mailboxes. If
-   only_with_msgs=TRUE, return only those mailboxes that have at least one
-   message existing in the virtual mailbox. */
-void mailbox_get_virtual_backend_boxes(struct mailbox *box,
-				       ARRAY_TYPE(mailboxes) *mailboxes,
-				       bool only_with_msgs);
-/* If mailbox is a virtual mailbox, return all mailbox list patterns that
-   are used to figure out which mailboxes belong to the virtual mailbox. */
-void mailbox_get_virtual_box_patterns(struct mailbox *box,
-				ARRAY_TYPE(mailbox_virtual_patterns) *includes,
-				ARRAY_TYPE(mailbox_virtual_patterns) *excludes);
 
 /* Initialize new search request. charset specifies the character set used in
    the search argument strings. If sort_program is non-NULL, the messages are
@@ -532,9 +532,8 @@
 struct mail_keywords *
 mailbox_keywords_create_from_indexes(struct mailbox *box,
 				     const ARRAY_TYPE(keyword_indexes) *idx);
-void mailbox_keywords_ref(struct mailbox *box, struct mail_keywords *keywords);
-void mailbox_keywords_unref(struct mailbox *box,
-			    struct mail_keywords **keywords);
+void mailbox_keywords_ref(struct mail_keywords *keywords);
+void mailbox_keywords_unref(struct mail_keywords **keywords);
 /* Returns TRUE if keyword is valid, FALSE and error if not. */
 bool mailbox_keyword_is_valid(struct mailbox *box, const char *keyword,
 			      const char **error_r);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/mailbox-get.c	Sat Jan 01 15:59:02 2011 +0200
@@ -0,0 +1,138 @@
+/* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "mail-index-modseq.h"
+#include "mail-storage-private.h"
+
+void mailbox_get_seq_range(struct mailbox *box, uint32_t uid1, uint32_t uid2,
+			   uint32_t *seq1_r, uint32_t *seq2_r)
+{
+	mail_index_lookup_seq_range(box->view, uid1, uid2, seq1_r, seq2_r);
+}
+
+void mailbox_get_uid_range(struct mailbox *box,
+			   const ARRAY_TYPE(seq_range) *seqs,
+			   ARRAY_TYPE(seq_range) *uids)
+{
+	const struct seq_range *range;
+	unsigned int i, count;
+	uint32_t seq, uid;
+
+	range = array_get(seqs, &count);
+	for (i = 0; i < count; i++) {
+		if (range[i].seq2 == (uint32_t)-1) {
+			i_assert(count == i-1);
+			mail_index_lookup_uid(box->view, range[i].seq1, &uid);
+			seq_range_array_add_range(uids, uid, (uint32_t)-1);
+			break;
+		}
+		for (seq = range[i].seq1; seq <= range[i].seq2; seq++) {
+			mail_index_lookup_uid(box->view, seq, &uid);
+			seq_range_array_add(uids, 0, uid);
+		}
+	}
+}
+
+static void
+add_expunges(ARRAY_TYPE(mailbox_expunge_rec) *expunges,
+	     const struct mail_transaction_expunge *src, size_t src_size,
+	     const ARRAY_TYPE(seq_range) *uids_filter)
+{
+	const struct mail_transaction_expunge *end;
+	struct mailbox_expunge_rec *expunge;
+	uint32_t uid;
+
+	end = src + src_size / sizeof(*src);
+	for (; src != end; src++) {
+		for (uid = src->uid1; uid <= src->uid2; uid++) {
+			if (seq_range_exists(uids_filter, uid)) {
+				expunge = array_append_space(expunges);
+				expunge->uid = uid;
+			}
+		}
+	}
+}
+
+static void
+add_guid_expunges(ARRAY_TYPE(mailbox_expunge_rec) *expunges,
+		  const struct mail_transaction_expunge_guid *src,
+		  size_t src_size, const ARRAY_TYPE(seq_range) *uids_filter)
+{
+	const struct mail_transaction_expunge_guid *end;
+	struct mailbox_expunge_rec *expunge;
+
+	end = src + src_size / sizeof(*src);
+	for (; src != end; src++) {
+		if (seq_range_exists(uids_filter, src->uid)) {
+			expunge = array_append_space(expunges);
+			expunge->uid = src->uid;
+			memcpy(expunge->guid_128, src->guid_128,
+			       sizeof(expunge->guid_128));
+		}
+	}
+}
+
+bool mailbox_get_expunges(struct mailbox *box, uint64_t prev_modseq,
+			  const ARRAY_TYPE(seq_range) *uids_filter,
+			  ARRAY_TYPE(mailbox_expunge_rec) *expunges)
+{
+	struct mail_transaction_log_view *log_view;
+	const struct mail_transaction_header *thdr;
+	const void *tdata;
+	uint32_t log_seq, tail_seq = 0;
+	uoff_t log_offset;
+	bool reset;
+	int ret;
+
+	if (!mail_index_modseq_get_next_log_offset(box->view, prev_modseq,
+						   &log_seq, &log_offset)) {
+		log_seq = 1;
+		log_offset = 0;
+	}
+	if (log_seq > box->view->log_file_head_seq ||
+	    (log_seq == box->view->log_file_head_seq &&
+	     log_offset >= box->view->log_file_head_offset)) {
+		/* we haven't seen this high expunges at all */
+		return TRUE;
+	}
+
+	log_view = mail_transaction_log_view_open(box->index->log);
+	ret = mail_transaction_log_view_set(log_view, log_seq, log_offset,
+					    box->view->log_file_head_seq,
+					    box->view->log_file_head_offset,
+					    &reset);
+	if (ret == 0) {
+		mail_transaction_log_get_tail(box->index->log, &tail_seq);
+		i_assert(tail_seq > log_seq);
+		ret = mail_transaction_log_view_set(log_view, tail_seq, 0,
+					box->view->log_file_head_seq,
+					box->view->log_file_head_offset,
+					&reset);
+		i_assert(ret != 0);
+	}
+	if (ret <= 0) {
+		mail_transaction_log_view_close(&log_view);
+		return FALSE;
+	}
+
+	while ((ret = mail_transaction_log_view_next(log_view,
+						     &thdr, &tdata)) > 0) {
+		if ((thdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) {
+			/* skip expunge requests */
+			continue;
+		}
+		switch (thdr->type & MAIL_TRANSACTION_TYPE_MASK) {
+		case MAIL_TRANSACTION_EXPUNGE:
+			add_expunges(expunges, tdata, thdr->size, uids_filter);
+			break;
+		case MAIL_TRANSACTION_EXPUNGE_GUID:
+			add_guid_expunges(expunges, tdata, thdr->size,
+					  uids_filter);
+			break;
+		}
+	}
+
+	mail_transaction_log_view_close(&log_view);
+	return ret < 0 || tail_seq != 0 ? FALSE : TRUE;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/mailbox-header.c	Sat Jan 01 15:59:02 2011 +0200
@@ -0,0 +1,89 @@
+/* Copyright (c) 2003-2010 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "mail-cache.h"
+#include "mail-storage-private.h"
+
+#include <stdlib.h>
+
+static struct mailbox_header_lookup_ctx *
+mailbox_header_lookup_init_real(struct mailbox *box,
+				const char *const headers[])
+{
+	struct mail_cache_field *fields, header_field = {
+		NULL, 0, MAIL_CACHE_FIELD_HEADER, 0,
+		MAIL_CACHE_DECISION_TEMP
+	};
+	struct mailbox_header_lookup_ctx *ctx;
+	const char *const *name;
+	const char **sorted_headers, **dest_name;
+	pool_t pool;
+	unsigned int i, count;
+
+	i_assert(*headers != NULL);
+
+	for (count = 0, name = headers; *name != NULL; name++)
+		count++;
+
+	/* @UNSAFE: headers need to be sorted for filter stream. */
+	sorted_headers = t_new(const char *, count);
+	memcpy(sorted_headers, headers, count * sizeof(*sorted_headers));
+	qsort(sorted_headers, count, sizeof(*sorted_headers), i_strcasecmp_p);
+	headers = sorted_headers;
+
+	/* @UNSAFE */
+	fields = t_new(struct mail_cache_field, count);
+	for (i = 0; i < count; i++) {
+		header_field.name = t_strconcat("hdr.", headers[i], NULL);
+		fields[i] = header_field;
+	}
+	mail_cache_register_fields(box->cache, fields, count);
+
+	pool = pool_alloconly_create("mailbox_header_lookup_ctx", 1024);
+	ctx = p_new(pool, struct mailbox_header_lookup_ctx, 1);
+	ctx->box = box;
+	ctx->refcount = 1;
+	ctx->pool = pool;
+	ctx->count = count;
+
+	ctx->idx = p_new(pool, unsigned int, count);
+
+	/* @UNSAFE */
+	dest_name = p_new(pool, const char *, count + 1);
+	for (i = 0; i < count; i++) {
+		ctx->idx[i] = fields[i].idx;
+		dest_name[i] = p_strdup(pool, headers[i]);
+	}
+	ctx->name = dest_name;
+	return ctx;
+}
+
+struct mailbox_header_lookup_ctx *
+mailbox_header_lookup_init(struct mailbox *box, const char *const headers[])
+{
+	struct mailbox_header_lookup_ctx *ctx;
+
+	T_BEGIN {
+		ctx = mailbox_header_lookup_init_real(box, headers);
+	} T_END;
+	return ctx;
+}
+
+void mailbox_header_lookup_ref(struct mailbox_header_lookup_ctx *ctx)
+{
+	i_assert(ctx->refcount > 0);
+	ctx->refcount++;
+}
+
+void mailbox_header_lookup_unref(struct mailbox_header_lookup_ctx **_ctx)
+{
+	struct mailbox_header_lookup_ctx *ctx = *_ctx;
+
+	*_ctx = NULL;
+
+	i_assert(ctx->refcount > 0);
+	if (--ctx->refcount > 0)
+		return;
+
+	pool_unref(&ctx->pool);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/mailbox-keywords.c	Sat Jan 01 15:59:02 2011 +0200
@@ -0,0 +1,130 @@
+/* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "imap-arg.h"
+#include "mail-storage-private.h"
+
+static struct mail_keywords *
+mailbox_keywords_create_skip(struct mailbox *box,
+			     const char *const keywords[])
+{
+	struct mail_keywords *kw;
+
+	T_BEGIN {
+		ARRAY_DEFINE(valid_keywords, const char *);
+		const char *error;
+
+		t_array_init(&valid_keywords, 32);
+		for (; *keywords != NULL; keywords++) {
+			if (mailbox_keyword_is_valid(box, *keywords, &error))
+				array_append(&valid_keywords, keywords, 1);
+		}
+		(void)array_append_space(&valid_keywords); /* NULL-terminate */
+		kw = mail_index_keywords_create(box->index, keywords);
+	} T_END;
+	return kw;
+}
+
+static bool
+mailbox_keywords_are_valid(struct mailbox *box, const char *const keywords[],
+			   const char **error_r)
+{
+	unsigned int i;
+
+	for (i = 0; keywords[i] != NULL; i++) {
+		if (!mailbox_keyword_is_valid(box, keywords[i], error_r))
+			return FALSE;
+	}
+	return TRUE;
+}
+
+int mailbox_keywords_create(struct mailbox *box, const char *const keywords[],
+			    struct mail_keywords **keywords_r)
+{
+	const char *error, *empty_keyword_list = NULL;
+
+	i_assert(box->opened);
+
+	if (keywords == NULL)
+		keywords = &empty_keyword_list;
+	if (!mailbox_keywords_are_valid(box, keywords, &error)) {
+		mail_storage_set_error(box->storage, MAIL_ERROR_PARAMS, error);
+		return -1;
+	}
+
+	*keywords_r = mail_index_keywords_create(box->index, keywords);
+	return 0;
+}
+
+struct mail_keywords *
+mailbox_keywords_create_valid(struct mailbox *box,
+			      const char *const keywords[])
+{
+	const char *empty_keyword_list = NULL;
+	const char *error;
+
+	i_assert(box->opened);
+
+	if (keywords == NULL)
+		keywords = &empty_keyword_list;
+	if (mailbox_keywords_are_valid(box, keywords, &error))
+		return mail_index_keywords_create(box->index, keywords);
+	else {
+		/* found invalid keywords, do this the slow way */
+		return mailbox_keywords_create_skip(box, keywords);
+	}
+}
+
+struct mail_keywords *
+mailbox_keywords_create_from_indexes(struct mailbox *box,
+				     const ARRAY_TYPE(keyword_indexes) *idx)
+{
+	i_assert(box->opened);
+
+	return mail_index_keywords_create_from_indexes(box->index, idx);
+}
+
+void mailbox_keywords_ref(struct mail_keywords *keywords)
+{
+	mail_index_keywords_ref(keywords);
+}
+
+void mailbox_keywords_unref(struct mail_keywords **keywords)
+{
+	mail_index_keywords_unref(keywords);
+}
+
+bool mailbox_keyword_is_valid(struct mailbox *box, const char *keyword,
+			      const char **error_r)
+{
+	unsigned int i, idx;
+
+	i_assert(box->opened);
+
+	/* if it already exists, skip validity checks */
+	if (mail_index_keyword_lookup(box->index, keyword, &idx))
+		return TRUE;
+
+	if (*keyword == '\0') {
+		*error_r = "Empty keywords not allowed";
+		return FALSE;
+	}
+
+	/* these are IMAP-specific restrictions, but for now IMAP is all we
+	   care about */
+	for (i = 0; keyword[i] != '\0'; i++) {
+		if (IS_ATOM_SPECIAL((unsigned char)keyword[i])) {
+			*error_r = "Invalid characters in keyword";
+			return FALSE;
+		}
+		if ((unsigned char)keyword[i] >= 0x80) {
+			*error_r = "8bit characters in keyword";
+			return FALSE;
+		}
+	}
+	if (i > box->storage->set->mail_max_keyword_length) {
+		*error_r = "Keyword length too long";
+		return FALSE;
+	}
+	return TRUE;
+}
--- a/src/lib-storage/mailbox-list.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/mailbox-list.c	Sat Jan 01 15:59:02 2011 +0200
@@ -490,15 +490,26 @@
 {
 	mode_t dir_mode;
 
-	if (list->file_create_mode != (mode_t)-1 && name == NULL) {
+	i_assert(name != NULL);
+
+	mailbox_list_get_permissions_full(list, name, mode_r, &dir_mode, gid_r,
+					  gid_origin_r);
+}
+
+void mailbox_list_get_root_permissions(struct mailbox_list *list,
+				       mode_t *mode_r, gid_t *gid_r,
+				       const char **gid_origin_r)
+{
+	mode_t dir_mode;
+
+	if (list->file_create_mode != (mode_t)-1) {
 		*mode_r = list->file_create_mode;
 		*gid_r = list->file_create_gid;
 		*gid_origin_r = list->file_create_gid_origin;
-		return;
+	} else {
+		mailbox_list_get_permissions_full(list, NULL, mode_r, &dir_mode,
+						  gid_r, gid_origin_r);
 	}
-
-	mailbox_list_get_permissions_full(list, name, mode_r, &dir_mode, gid_r,
-					  gid_origin_r);
 }
 
 void mailbox_list_get_dir_permissions(struct mailbox_list *list,
@@ -508,15 +519,26 @@
 {
 	mode_t file_mode;
 
-	if (list->dir_create_mode != (mode_t)-1 && name == NULL) {
+	i_assert(name != NULL);
+
+	mailbox_list_get_permissions_full(list, name, &file_mode,
+					  mode_r, gid_r, gid_origin_r);
+}
+
+void mailbox_list_get_root_dir_permissions(struct mailbox_list *list,
+					   mode_t *mode_r, gid_t *gid_r,
+					   const char **gid_origin_r)
+{
+	mode_t file_mode;
+
+	if (list->dir_create_mode != (mode_t)-1) {
 		*mode_r = list->dir_create_mode;
 		*gid_r = list->file_create_gid;
 		*gid_origin_r = list->file_create_gid_origin;
-		return;
+	} else {
+		mailbox_list_get_permissions_full(list, NULL, &file_mode,
+						  mode_r, gid_r, gid_origin_r);
 	}
-
-	mailbox_list_get_permissions_full(list, name, &file_mode,
-					  mode_r, gid_r, gid_origin_r);
 }
 
 static int
@@ -591,7 +613,7 @@
 	mode_t mode;
 	gid_t gid;
 
-	mailbox_list_get_dir_permissions(list, NULL, &mode, &gid, &origin);
+	mailbox_list_get_root_dir_permissions(list, &mode, &gid, &origin);
 
 	/* get the directory path up to last %variable. for example
 	   unexpanded path may be "/var/mail/%d/%2n/%n/Maildir", and we want
@@ -758,8 +780,9 @@
 					 enum mailbox_name_status *status)
 {
 	if (!mailbox_list_is_valid_existing_name(list, name)) {
-		*status = MAILBOX_NAME_INVALID;
-		return 0;
+		mailbox_list_set_error(list, MAIL_ERROR_PARAMS,
+				       "Invalid mailbox name");
+		return -1;
 	}
 	return list->v.get_mailbox_name_status(list, name, status);
 }
@@ -1111,7 +1134,7 @@
 	path = t_strconcat(path, "/"MAILBOX_LOG_FILE_NAME, NULL);
 	list->changelog = mailbox_log_alloc(path);
 
-	mailbox_list_get_permissions(list, NULL, &mode, &gid, &gid_origin);
+	mailbox_list_get_root_permissions(list, &mode, &gid, &gid_origin);
 	mailbox_log_set_permissions(list->changelog, mode, gid, gid_origin);
 	return TRUE;
 }
--- a/src/lib-storage/mailbox-list.h	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/mailbox-list.h	Sat Jan 01 15:59:02 2011 +0200
@@ -49,13 +49,11 @@
 };
 
 enum mailbox_name_status {
+	MAILBOX_NAME_NONEXISTENT,
 	/* name points to a selectable mailbox */
 	MAILBOX_NAME_EXISTS_MAILBOX,
 	/* name points to non-selectable mailbox */
-	MAILBOX_NAME_EXISTS_DIR,
-	MAILBOX_NAME_VALID,
-	MAILBOX_NAME_INVALID,
-	MAILBOX_NAME_NOINFERIORS
+	MAILBOX_NAME_EXISTS_DIR
 };
 
 enum mailbox_list_iter_flags {
@@ -175,18 +173,24 @@
 				      struct mail_storage **storage);
 
 /* Returns the mode and GID that should be used when creating new files to
-   the specified mailbox, or to mailbox list root if name is NULL. (gid_t)-1 is
+   the specified mailbox or to mailbox list root. (gid_t)-1 is
    returned if it's not necessary to change the default gid. */
 void mailbox_list_get_permissions(struct mailbox_list *list,
 				  const char *name,
 				  mode_t *mode_r, gid_t *gid_r,
 				  const char **gid_origin_r);
+void mailbox_list_get_root_permissions(struct mailbox_list *list,
+				       mode_t *mode_r, gid_t *gid_r,
+				       const char **gid_origin_r);
 /* Like mailbox_list_get_permissions(), but add execute-bits for mode
    if either read or write bit is set (e.g. 0640 -> 0750). */
 void mailbox_list_get_dir_permissions(struct mailbox_list *list,
 				      const char *name,
 				      mode_t *mode_r, gid_t *gid_r,
 				      const char **gid_origin_r);
+void mailbox_list_get_root_dir_permissions(struct mailbox_list *list,
+					   mode_t *mode_r, gid_t *gid_r,
+					   const char **gid_origin_r);
 /* Create path's parent directory with proper permissions. Since most
    directories are created lazily, this function can be used to easily create
    them whenever file creation fails with ENOENT. */
--- a/src/lib-storage/mailbox-uidvalidity.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/mailbox-uidvalidity.c	Sat Jan 01 15:59:02 2011 +0200
@@ -41,7 +41,7 @@
 	gid_t gid;
 	const char *gid_origin;
 
-	mailbox_list_get_permissions(list, NULL, &mode, &gid, &gid_origin);
+	mailbox_list_get_root_permissions(list, &mode, &gid, &gid_origin);
 
 	old_mask = umask(0666 & ~mode);
 	fd = open(path, O_RDWR | O_CREAT, 0666);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/test-mailbox-get.c	Sat Jan 01 15:59:02 2011 +0200
@@ -0,0 +1,154 @@
+/* Copyright (c) 2009-2010 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "test-common.h"
+#include "mail-index-modseq.h"
+#include "mail-storage-private.h"
+
+static uint32_t expunge_uids[] = { 25, 15, 7, 3, 11, 1, 53, 33 };
+static uint8_t mail_guids[N_ELEMENTS(expunge_uids)][MAIL_GUID_128_SIZE];
+static unsigned int expunge_idx;
+static unsigned int nonexternal_idx;
+
+void mail_index_lookup_uid(struct mail_index_view *view ATTR_UNUSED,
+			   uint32_t seq, uint32_t *uid_r)
+{
+	*uid_r = seq;
+}
+
+bool mail_index_lookup_seq_range(struct mail_index_view *view ATTR_UNUSED,
+				 uint32_t first_uid, uint32_t last_uid,
+				 uint32_t *first_seq_r, uint32_t *last_seq_r)
+{
+	*first_seq_r = first_uid;
+	*last_seq_r = last_uid;
+	return TRUE;
+}
+
+bool mail_index_modseq_get_next_log_offset(struct mail_index_view *view ATTR_UNUSED,
+					   uint64_t modseq, uint32_t *log_seq_r,
+					   uoff_t *log_offset_r)
+{
+	*log_seq_r = modseq >> 32;
+	*log_offset_r = modseq & 0xfffffff;
+	return TRUE;
+}
+
+struct mail_transaction_log_view *
+mail_transaction_log_view_open(struct mail_transaction_log *log ATTR_UNUSED) { return NULL; }
+int mail_transaction_log_view_set(struct mail_transaction_log_view *view ATTR_UNUSED,
+				  uint32_t min_file_seq ATTR_UNUSED, uoff_t min_file_offset ATTR_UNUSED,
+				  uint32_t max_file_seq ATTR_UNUSED, uoff_t max_file_offset ATTR_UNUSED,
+				  bool *reset_r ATTR_UNUSED) {
+	if (min_file_seq < 99)
+		return 0;
+	return 1;
+}
+
+void mail_transaction_log_view_close(struct mail_transaction_log_view **view ATTR_UNUSED) { }
+
+void mail_transaction_log_get_tail(struct mail_transaction_log *log ATTR_UNUSED,
+				   uint32_t *file_seq_r)
+{
+	*file_seq_r = 100;
+}
+
+int mail_transaction_log_view_next(struct mail_transaction_log_view *view ATTR_UNUSED,
+				   const struct mail_transaction_header **hdr_r,
+				   const void **data_r)
+{
+	static struct mail_transaction_header hdr;
+	static struct mail_transaction_expunge_guid exp;
+	static struct mail_transaction_expunge old_exp;
+
+	if (expunge_idx == N_ELEMENTS(expunge_uids))
+		return 0;
+
+	if (mail_guids[expunge_idx][0] == 0) {
+		old_exp.uid1 = old_exp.uid2 = expunge_uids[expunge_idx];
+		hdr.type = MAIL_TRANSACTION_EXPUNGE;
+		hdr.size = sizeof(old_exp);
+		*data_r = &old_exp;
+	} else {
+		exp.uid = expunge_uids[expunge_idx];
+		memcpy(exp.guid_128, mail_guids[expunge_idx], sizeof(exp.guid_128));
+		hdr.type = MAIL_TRANSACTION_EXPUNGE_GUID;
+		hdr.size = sizeof(exp);
+		*data_r = &exp;
+	}
+	if (expunge_idx != nonexternal_idx)
+		hdr.type |= MAIL_TRANSACTION_EXTERNAL;
+
+	*hdr_r = &hdr;
+	expunge_idx++;
+	return 1;
+}
+
+static void test_index_storage_get_expunges(void)
+{
+	struct mailbox *box;
+	ARRAY_TYPE(seq_range) uids_filter;
+	ARRAY_TYPE(mailbox_expunge_rec) expunges;
+	const struct mailbox_expunge_rec *exp;
+	unsigned int i, count;
+	uint64_t modseq;
+
+	box = t_new(struct mailbox, 1);
+	box->index = t_new(struct mail_index, 1);
+	box->view = t_new(struct mail_index_view, 1);
+
+	box->view->log_file_head_seq = 101;
+	box->view->log_file_head_offset = 1024;
+
+	test_begin("index storage get expunges");
+
+	nonexternal_idx = 1;
+	memset(mail_guids + 2, 0, MAIL_GUID_128_SIZE);
+	memset(mail_guids + 4, 0, MAIL_GUID_128_SIZE);
+
+	t_array_init(&uids_filter, 32);
+	seq_range_array_add_range(&uids_filter, 1, 20);
+	seq_range_array_add_range(&uids_filter, 53, 53);
+
+	t_array_init(&expunges, 32);
+	modseq = 98ULL << 32;
+	for (i = 0; i < 2; i++) {
+		test_assert(mailbox_get_expunges(box, modseq, &uids_filter,
+						 &expunges) == i);
+
+		exp = array_get(&expunges, &count);
+		test_assert(count == 5);
+		test_assert(exp[0].uid == 7);
+		test_assert(memcmp(exp[0].guid_128, mail_guids[2], MAIL_GUID_128_SIZE) == 0);
+		test_assert(exp[1].uid == 3);
+		test_assert(memcmp(exp[1].guid_128, mail_guids[3], MAIL_GUID_128_SIZE) == 0);
+		test_assert(exp[2].uid == 11);
+		test_assert(memcmp(exp[2].guid_128, mail_guids[4], MAIL_GUID_128_SIZE) == 0);
+		test_assert(exp[3].uid == 1);
+		test_assert(memcmp(exp[3].guid_128, mail_guids[5], MAIL_GUID_128_SIZE) == 0);
+		test_assert(exp[4].uid == 53);
+		test_assert(memcmp(exp[4].guid_128, mail_guids[6], MAIL_GUID_128_SIZE) == 0);
+
+		array_clear(&uids_filter);
+		expunge_idx = 0;
+		modseq = 100ULL << 32;
+	}
+
+	test_end();
+}
+
+int main(void)
+{
+	static void (*test_functions[])(void) = {
+		test_index_storage_get_expunges,
+		NULL
+	};
+	unsigned int i, j;
+
+	for (i = 0; i < N_ELEMENTS(mail_guids); i++) {
+		for (j = 0; j < MAIL_GUID_128_SIZE; j++)
+			mail_guids[i][j] = j + i + 1;
+	}
+	return test_run(test_functions);
+}
--- a/src/lib-storage/test-mailbox.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib-storage/test-mailbox.c	Sat Jan 01 15:59:02 2011 +0200
@@ -68,13 +68,14 @@
 	return -1;
 }
 
-static void test_mailbox_get_status(struct mailbox *box ATTR_UNUSED,
-				    enum mailbox_status_items items ATTR_UNUSED,
-				    struct mailbox_status *status_r)
+static int test_mailbox_get_status(struct mailbox *box ATTR_UNUSED,
+				   enum mailbox_status_items items ATTR_UNUSED,
+				   struct mailbox_status *status_r)
 {
 	memset(status_r, 0, sizeof(*status_r));
 	status_r->uidvalidity = TEST_UID_VALIDITY;
 	status_r->uidnext = 1;
+	return 0;
 }
 
 static struct mailbox_sync_context *
@@ -138,98 +139,6 @@
 	return 0;
 }
 
-static void
-test_mailbox_transaction_set_max_modseq(struct mailbox_transaction_context *t ATTR_UNUSED,
-					uint64_t max_modseq ATTR_UNUSED,
-					ARRAY_TYPE(seq_range) *seqs ATTR_UNUSED)
-{
-}
-
-static int
-test_mailbox_keywords_create(struct mailbox *box ATTR_UNUSED,
-			     const char *const keywords[] ATTR_UNUSED,
-			     struct mail_keywords **keywords_r,
-			     bool skip_invalid ATTR_UNUSED)
-{
-	*keywords_r = i_new(struct mail_keywords, 1);
-	(*keywords_r)->refcount = 1;
-	return 0;
-}
-
-static struct mail_keywords *
-test_mailbox_keywords_create_from_indexes(struct mailbox *box ATTR_UNUSED,
-					  const ARRAY_TYPE(keyword_indexes) *idx ATTR_UNUSED)
-{
-	struct mail_keywords *keywords;
-
-	keywords = i_new(struct mail_keywords, 1);
-	keywords->refcount++;
-	return keywords;
-}
-
-static void test_mailbox_keywords_ref(struct mail_keywords *keywords)
-{
-	keywords->refcount++;
-}
-
-static void test_mailbox_keywords_unref(struct mail_keywords *keywords)
-{
-	if (--keywords->refcount == 0)
-		i_free(keywords);
-}
-
-static bool
-test_mailbox_keyword_is_valid(struct mailbox *box ATTR_UNUSED,
-			      const char *keyword ATTR_UNUSED,
-			      const char **error_r ATTR_UNUSED)
-{
-	return TRUE;
-}
-
-static void
-test_mailbox_get_seq_range(struct mailbox *box ATTR_UNUSED,
-			   uint32_t uid1, uint32_t uid2,
-			   uint32_t *seq1_r, uint32_t *seq2_r)
-{
-	*seq1_r = uid1;
-	*seq2_r = uid2;
-}
-
-static void
-test_mailbox_get_uid_range(struct mailbox *box ATTR_UNUSED,
-			   const ARRAY_TYPE(seq_range) *seqs,
-			   ARRAY_TYPE(seq_range) *uids)
-{
-	array_append_array(uids, seqs);
-}
-
-static bool
-test_mailbox_get_expunged_uids(struct mailbox *box ATTR_UNUSED,
-			       uint64_t prev_modseq ATTR_UNUSED,
-			       const ARRAY_TYPE(seq_range) *uids_filter ATTR_UNUSED,
-			       ARRAY_TYPE(mailbox_expunge_rec) *expunges ATTR_UNUSED)
-{
-	return FALSE;
-}
-
-static struct mailbox_header_lookup_ctx *
-test_mailbox_header_lookup_init(struct mailbox *box,
-				const char *const headers[])
-{
-	struct mailbox_header_lookup_ctx *ctx;
-
-	ctx = i_new(struct mailbox_header_lookup_ctx, 1);
-	ctx->box = box;
-	ctx->headers = headers; /* now exactly right, but .. */
-	return ctx;
-}
-
-static void
-test_mailbox_header_lookup_deinit(struct mailbox_header_lookup_ctx *ctx)
-{
-	i_free(ctx);
-}
-
 static struct mail_search_context *
 test_mailbox_search_init(struct mailbox_transaction_context *t,
 			 struct mail_search_args *args,
@@ -339,21 +248,8 @@
 		test_mailbox_transaction_begin,
 		test_mailbox_transaction_commit,
 		test_mailbox_transaction_rollback,
-		test_mailbox_transaction_set_max_modseq,
-		test_mailbox_keywords_create,
-		test_mailbox_keywords_create_from_indexes,
-		test_mailbox_keywords_ref,
-		test_mailbox_keywords_unref,
-		test_mailbox_keyword_is_valid,
-		test_mailbox_get_seq_range,
-		test_mailbox_get_uid_range,
-		test_mailbox_get_expunged_uids,
-		NULL,
-		NULL,
 		NULL,
 		test_mailbox_mail_alloc,
-		test_mailbox_header_lookup_init,
-		test_mailbox_header_lookup_deinit,
 		test_mailbox_search_init,
 		test_mailbox_search_deinit,
 		test_mailbox_search_next_nonblock,
@@ -364,7 +260,6 @@
 		test_mailbox_save_finish,
 		test_mailbox_save_cancel,
 		test_mailbox_copy,
-		NULL,
 		test_mailbox_is_inconsistent
 	}
 };
--- a/src/lib/lib.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib/lib.c	Sat Jan 01 15:59:02 2011 +0200
@@ -13,7 +13,7 @@
 {
 	size_t n = 1;
 
-	i_assert(num <= ((size_t)1 << (BITS_IN_SIZE_T-1)));
+	i_assert(num <= ((size_t)1 << (CHAR_BIT*sizeof(size_t) - 1)));
 
 	while (n < num) n <<= 1;
 	return n;
--- a/src/lib/macros.h	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/lib/macros.h	Sat Jan 01 15:59:02 2011 +0200
@@ -18,9 +18,6 @@
 #define N_ELEMENTS(arr) \
 	(sizeof(arr) / sizeof((arr)[0]))
 
-#define BITS_IN_UINT (CHAR_BIT * sizeof(unsigned int))
-#define BITS_IN_SIZE_T (CHAR_BIT * sizeof(size_t))
-
 #define MEM_ALIGN(size) \
 	(((size) + MEM_ALIGN_SIZE-1) & ~((unsigned int) MEM_ALIGN_SIZE-1))
 
@@ -34,13 +31,6 @@
 #define I_MIN(a, b)  (((a) < (b)) ? (a) : (b))
 #define I_MAX(a, b)  (((a) > (b)) ? (a) : (b))
 
-#undef CLAMP
-#define CLAMP(x, low, high) \
-	(((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
-
-#undef NVL
-#define NVL(str, nullstr) ((str) != NULL ? (str) : (nullstr))
-
 /* make it easier to cast from/to pointers. assumes that
    sizeof(size_t) == sizeof(void *) and they're both the largest datatypes
    that are allowed to be used. so, long long isn't safe with these. */
--- a/src/plugins/acl/acl-backend-vfile-acllist.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/plugins/acl/acl-backend-vfile-acllist.c	Sat Jan 01 15:59:02 2011 +0200
@@ -228,7 +228,7 @@
 	/* Build it into a temporary file and rename() over. There's no need
 	   to use locking, because even if multiple processes are rebuilding
 	   the file at the same time the result should be the same. */
-	mailbox_list_get_permissions(list, NULL, &mode, &gid, &origin);
+	mailbox_list_get_root_permissions(list, &mode, &gid, &origin);
 	fd = safe_mkstemp_group(path, mode, gid, origin);
 	if (fd == -1 && errno == ENOENT) {
 		if (mailbox_list_create_parent_dir(backend->backend.list, NULL,
--- a/src/plugins/acl/acl-mailbox-list.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/plugins/acl/acl-mailbox-list.c	Sat Jan 01 15:59:02 2011 +0200
@@ -407,11 +407,12 @@
 	return ret;
 }
 
-static int acl_mailbox_have_any_rights(struct acl_mailbox_list *alist,
-				       const char *name)
+static int acl_mailbox_have_visibility_rights(struct acl_mailbox_list *alist,
+					      const char *name)
 {
 	struct acl_object *aclobj;
 	const char *const *rights;
+	unsigned int i;
 	int ret;
 
 	aclobj = acl_object_init_from_name(alist->rights.backend, name);
@@ -419,8 +420,21 @@
 				       &rights);
 	acl_object_deinit(&aclobj);
 
-	return ret < 0 ? -1 :
-		(*rights == NULL ? 0 : 1);
+	if (ret < 0)
+		return -1;
+
+	/* for now this is used only by IMAP SUBSCRIBE. we'll intentionally
+	   violate RFC 4314 here, because it says SUBSCRIBE should succeed only
+	   when mailbox has 'l' right. But there's no point in not allowing
+	   a subscribe for a mailbox that can be selected anyway. Just the
+	   opposite: subscribing to such mailboxes is a very useful feature. */
+	for (i = 0; rights[i] != NULL; i++) {
+		if (strcmp(rights[i], MAIL_ACL_LOOKUP) == 0 ||
+		    strcmp(rights[i], MAIL_ACL_READ) == 0 ||
+		    strcmp(rights[i], MAIL_ACL_INSERT) == 0)
+			return 1;
+	}
+	return 0;
 }
 
 static int acl_get_mailbox_name_status(struct mailbox_list *list,
@@ -431,7 +445,7 @@
 	int ret;
 
 	T_BEGIN {
-		ret = acl_mailbox_have_any_rights(alist, name);
+		ret = acl_mailbox_have_visibility_rights(alist, name);
 	} T_END;
 	if (ret < 0)
 		return -1;
@@ -439,34 +453,9 @@
 	if (alist->module_ctx.super.get_mailbox_name_status(list, name,
 							    status) < 0)
 		return -1;
-	if (ret > 0)
-		return 0;
-
-	/* we shouldn't reveal this mailbox's existance */
-	switch (*status) {
-	case MAILBOX_NAME_EXISTS_MAILBOX:
-	case MAILBOX_NAME_EXISTS_DIR:
-		*status = MAILBOX_NAME_VALID;
-		break;
-	case MAILBOX_NAME_VALID:
-	case MAILBOX_NAME_INVALID:
-		break;
-	case MAILBOX_NAME_NOINFERIORS:
-		/* have to check if we are allowed to see the parent */
-		T_BEGIN {
-			ret = acl_mailbox_list_have_right(list, name,
-					TRUE, ACL_STORAGE_RIGHT_LOOKUP, NULL);
-		} T_END;
-
-		if (ret < 0) {
-			mailbox_list_set_internal_error(list);
-			return -1;
-		}
-		if (ret == 0) {
-			/* no permission to see the parent */
-			*status = MAILBOX_NAME_VALID;
-		}
-		break;
+	if (ret == 0) {
+		/* we shouldn't reveal this mailbox's existance */
+		*status = MAILBOX_NAME_NONEXISTENT;
 	}
 	return 0;
 }
--- a/src/plugins/acl/acl-mailbox.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/plugins/acl/acl-mailbox.c	Sat Jan 01 15:59:02 2011 +0200
@@ -64,6 +64,7 @@
 {
 	struct acl_mailbox *abox = ACL_CONTEXT(box);
 	enum acl_storage_rights save_right;
+	enum mail_flags private_flags_mask;
 
 	if (abox->module_ctx.super.is_readonly(box))
 		return TRUE;
@@ -78,10 +79,12 @@
 	/* Next up is the "shared flag rights" */
 	if (acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_WRITE) > 0)
 		return FALSE;
-	if ((box->private_flags_mask & MAIL_DELETED) == 0 &&
+
+	private_flags_mask = mailbox_get_private_flags_mask(box);
+	if ((private_flags_mask & MAIL_DELETED) == 0 &&
 	    acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_WRITE_DELETED) > 0)
 		return FALSE;
-	if ((box->private_flags_mask & MAIL_SEEN) == 0 &&
+	if ((private_flags_mask & MAIL_SEEN) == 0 &&
 	    acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_WRITE_SEEN) > 0)
 		return FALSE;
 
@@ -425,35 +428,6 @@
 	return abox->module_ctx.super.transaction_commit(ctx, changes_r);
 }
 
-static int
-acl_keywords_create(struct mailbox *box, const char *const keywords[],
-		    struct mail_keywords **keywords_r, bool skip_invalid)
-{
-	struct acl_mailbox *abox = ACL_CONTEXT(box);
-	int ret;
-
-	ret = acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_WRITE);
-	if (ret < 0) {
-		if (!skip_invalid)
-			return -1;
-		/* we can't return failure. assume we don't have permissions. */
-		ret = 0;
-	}
-
-	if (ret == 0) {
-		/* no permission to update any flags. just return empty
-		   keywords list. */
-		const char *null = NULL;
-
-		return abox->module_ctx.super.keywords_create(box, &null,
-							      keywords_r,
-							      skip_invalid);
-	}
-
-	return abox->module_ctx.super.keywords_create(box, keywords,
-						      keywords_r, skip_invalid);
-}
-
 static int acl_mailbox_open_check_acl(struct mailbox *box)
 {
 	struct acl_mailbox *abox = ACL_CONTEXT(box);
@@ -526,7 +500,6 @@
 		v->delete = acl_mailbox_delete;
 		v->rename = acl_mailbox_rename;
 		v->save_begin = acl_save_begin;
-		v->keywords_create = acl_keywords_create;
 		v->copy = acl_copy;
 		v->transaction_commit = acl_transaction_commit;
 	}
--- a/src/plugins/fts-solr/fts-backend-solr.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/plugins/fts-solr/fts-backend-solr.c	Sat Jan 01 15:59:02 2011 +0200
@@ -7,6 +7,7 @@
 #include "unichar.h"
 #include "mail-storage-private.h"
 #include "mail-namespace.h"
+#include "fts-mailbox.h"
 #include "solr-connection.h"
 #include "fts-solr-plugin.h"
 
@@ -297,7 +298,7 @@
 
 	box_name = fts_box_get_root(box, &ns);
 
-	mailbox_get_status(box, STATUS_UIDVALIDITY, &status);
+	mailbox_get_open_status(box, STATUS_UIDVALIDITY, &status);
 	str_printfa(str, "uidv:%u+box:", status.uidvalidity);
 	solr_quote_http(str, box_name);
 	solr_add_ns_query_http(str, backend, ns);
@@ -339,7 +340,7 @@
 
 	box_name = fts_box_get_root(box, &ns);
 
-	mailbox_get_status(box, STATUS_UIDVALIDITY, &status);
+	mailbox_get_open_status(box, STATUS_UIDVALIDITY, &status);
 	str_printfa(str, "uidv:%u+box:", status.uidvalidity);
 	solr_quote_http(str, box_name);
 	solr_add_ns_query_http(str, backend, ns);
@@ -452,7 +453,7 @@
 
 	t_array_init(&includes_arr, 16);
 	t_array_init(&excludes_arr, 16);
-	mailbox_get_virtual_box_patterns(box, &includes_arr, &excludes_arr);
+	fts_mailbox_get_virtual_box_patterns(box, &includes_arr, &excludes_arr);
 	includes = array_get(&includes_arr, &inc_count);
 	excludes = array_get(&excludes_arr, &exc_count);
 	i_assert(inc_count > 0);
@@ -538,7 +539,7 @@
 	ctx->ctx.backend = backend;
 	ctx->cmd = str_new(default_pool, SOLR_CMDBUF_SIZE);
 
-	mailbox_get_status(backend->box, STATUS_UIDVALIDITY, &status);
+	mailbox_get_open_status(backend->box, STATUS_UIDVALIDITY, &status);
 	ctx->uid_validity = status.uidvalidity;
 
 	*ctx_r = &ctx->ctx;
@@ -736,7 +737,7 @@
 {
 	struct mailbox_status status;
 
-	mailbox_get_status(mail->box, STATUS_UIDVALIDITY, &status);
+	mailbox_get_open_status(mail->box, STATUS_UIDVALIDITY, &status);
 
 	T_BEGIN {
 		string_t *cmd;
@@ -783,8 +784,8 @@
 	for (; ns != NULL; ns = ns->alias_chain_next) {
 		vname = convert_inbox ? ns->prefix :
 			mail_namespace_get_vname(ns, ctx->vname, mailbox);
-		if (mailbox_get_virtual_uid(ctx->box, vname, uidvalidity,
-					    *uid, uid))
+		if (fts_mailbox_get_virtual_uid(ctx->box, vname, uidvalidity,
+						*uid, uid))
 			return TRUE;
 	}
 	return FALSE;
@@ -806,7 +807,7 @@
 	bool virtual;
 
 	virtual = strcmp(box->storage->name, "virtual") == 0;
-	mailbox_get_status(box, STATUS_UIDVALIDITY, &status);
+	mailbox_get_open_status(box, STATUS_UIDVALIDITY, &status);
 
 	str = t_str_new(256);
 	if (!virtual) {
--- a/src/plugins/fts-squat/fts-backend-squat.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/plugins/fts-squat/fts-backend-squat.c	Sat Jan 01 15:59:02 2011 +0200
@@ -52,6 +52,7 @@
 
 static struct fts_backend *fts_backend_squat_init(struct mailbox *box)
 {
+	const struct mailbox_permissions *perm = mailbox_get_permissions(box);
 	struct squat_fts_backend *backend;
 	struct mail_storage *storage;
 	struct mailbox_status status;
@@ -68,7 +69,7 @@
 		return NULL;
 	}
 
-	mailbox_get_status(box, STATUS_UIDVALIDITY, &status);
+	mailbox_get_open_status(box, STATUS_UIDVALIDITY, &status);
 	if (storage->set->mmap_disable)
 		flags |= SQUAT_INDEX_FLAG_MMAP_DISABLE;
 	if (storage->set->mail_nfs_index)
@@ -82,8 +83,8 @@
 		squat_trie_init(t_strconcat(path, "/"SQUAT_FILE_PREFIX, NULL),
 				status.uidvalidity,
 				storage->set->parsed_lock_method,
-				flags, box->file_create_mode,
-				box->file_create_gid);
+				flags, perm->file_create_mode,
+				perm->file_create_gid);
 
 	env = mail_user_plugin_getenv(box->storage->user, "fts_squat");
 	if (env != NULL)
--- a/src/plugins/fts/Makefile.am	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/plugins/fts/Makefile.am	Sat Jan 01 15:59:02 2011 +0200
@@ -3,7 +3,8 @@
 	-I$(top_srcdir)/src/lib-charset \
 	-I$(top_srcdir)/src/lib-mail \
 	-I$(top_srcdir)/src/lib-index \
-	-I$(top_srcdir)/src/lib-storage
+	-I$(top_srcdir)/src/lib-storage \
+	-I$(top_srcdir)/src/lib-storage/index
 
 lib20_fts_plugin_la_LDFLAGS = -module -avoid-version
 
@@ -12,6 +13,7 @@
 
 lib20_fts_plugin_la_SOURCES = \
 	fts-api.c \
+	fts-mailbox.c \
 	fts-plugin.c \
 	fts-search.c \
 	fts-storage.c
@@ -19,5 +21,6 @@
 noinst_HEADERS = \
 	fts-api.h \
 	fts-api-private.h \
+	fts-mailbox.h \
 	fts-plugin.h \
 	fts-storage.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/plugins/fts/fts-mailbox.c	Sat Jan 01 15:59:02 2011 +0200
@@ -0,0 +1,56 @@
+/* Copyright (c) 2006-2010 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "mail-storage.h"
+#include "fts-mailbox.h"
+#include "../virtual/virtual-storage.h"
+
+bool fts_mailbox_get_virtual_uid(struct mailbox *box,
+				 const char *backend_mailbox,
+				 uint32_t backend_uidvalidity,
+				 uint32_t backend_uid, uint32_t *uid_r)
+{
+	struct virtual_mailbox *vbox;
+
+	if (strcmp(box->storage->name, VIRTUAL_STORAGE_NAME) != 0)
+		return FALSE;
+
+	vbox = (struct virtual_mailbox *)box;
+	return vbox->vfuncs.get_virtual_uid(box, backend_mailbox,
+					    backend_uidvalidity,
+					    backend_uid, uid_r);
+}
+
+void fts_mailbox_get_virtual_backend_boxes(struct mailbox *box,
+					   ARRAY_TYPE(mailboxes) *mailboxes,
+					   bool only_with_msgs)
+{
+	struct virtual_mailbox *vbox;
+
+	if (strcmp(box->storage->name, VIRTUAL_STORAGE_NAME) != 0)
+		array_append(mailboxes, &box, 1);
+	else {
+		vbox = (struct virtual_mailbox *)box;
+		vbox->vfuncs.get_virtual_backend_boxes(box, mailboxes,
+						       only_with_msgs);
+	}
+}
+
+void fts_mailbox_get_virtual_box_patterns(struct mailbox *box,
+				ARRAY_TYPE(mailbox_virtual_patterns) *includes,
+				ARRAY_TYPE(mailbox_virtual_patterns) *excludes)
+{
+	struct virtual_mailbox *vbox;
+
+	if (strcmp(box->storage->name, VIRTUAL_STORAGE_NAME) != 0) {
+		struct mailbox_virtual_pattern pat;
+
+		memset(&pat, 0, sizeof(pat));
+		pat.ns = mailbox_list_get_namespace(box->list);
+		pat.pattern = box->name;
+		array_append(includes, &pat, 1);
+	} else {
+		vbox = (struct virtual_mailbox *)box;
+		vbox->vfuncs.get_virtual_box_patterns(box, includes, excludes);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/plugins/fts/fts-mailbox.h	Sat Jan 01 15:59:02 2011 +0200
@@ -0,0 +1,22 @@
+#ifndef FTS_MAILBOX_H
+#define FTS_MAILBOX_H
+
+/* If box is a virtual mailbox, look up UID for the given backend message.
+   Returns TRUE if found, FALSE if not. */
+bool fts_mailbox_get_virtual_uid(struct mailbox *box,
+				 const char *backend_mailbox,
+				 uint32_t backend_uidvalidity,
+				 uint32_t backend_uid, uint32_t *uid_r);
+/* If box is a virtual mailbox, return all backend mailboxes. If
+   only_with_msgs=TRUE, return only those mailboxes that have at least one
+   message existing in the virtual mailbox. */
+void fts_mailbox_get_virtual_backend_boxes(struct mailbox *box,
+					   ARRAY_TYPE(mailboxes) *mailboxes,
+					   bool only_with_msgs);
+/* If mailbox is a virtual mailbox, return all mailbox list patterns that
+   are used to figure out which mailboxes belong to the virtual mailbox. */
+void fts_mailbox_get_virtual_box_patterns(struct mailbox *box,
+				ARRAY_TYPE(mailbox_virtual_patterns) *includes,
+				ARRAY_TYPE(mailbox_virtual_patterns) *excludes);
+
+#endif
--- a/src/plugins/fts/fts-storage.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/plugins/fts/fts-storage.c	Sat Jan 01 15:59:02 2011 +0200
@@ -13,6 +13,7 @@
 #include "mail-search-build.h"
 #include "mail-storage-private.h"
 #include "fts-api-private.h"
+#include "fts-mailbox.h"
 #include "fts-storage.h"
 #include "fts-plugin.h"
 
@@ -395,8 +396,8 @@
 		ret = strcmp(vname, last_uids[uidi].mailbox);
 		if (ret == 0) {
 			/* match. check also that uidvalidity matches. */
-			mailbox_get_status(boxes[boxi].box, STATUS_UIDVALIDITY,
-					   &status);
+			mailbox_get_open_status(boxes[boxi].box,
+						STATUS_UIDVALIDITY, &status);
 			if (status.uidvalidity != last_uids[uidi].uidvalidity) {
 				uidi++;
 				continue;
@@ -453,7 +454,7 @@
 	int ret;
 
 	t_array_init(&mailboxes, 64);
-	mailbox_get_virtual_backend_boxes(fctx->t->box, &mailboxes, TRUE);
+	fts_mailbox_get_virtual_backend_boxes(fctx->t->box, &mailboxes, TRUE);
 	boxes = array_get_modifiable(&mailboxes, &box_count);
 
 	vctx->pool = pool_alloconly_create("fts virtual build", 1024);
@@ -500,8 +501,8 @@
 	struct mailbox_status status;
 	int ret;
 
-	mailbox_get_status(fctx->t->box, STATUS_MESSAGES | STATUS_UIDNEXT,
-			   &status);
+	mailbox_get_open_status(fctx->t->box, STATUS_MESSAGES | STATUS_UIDNEXT,
+				&status);
 	if (status.messages == fctx->fbox->last_messages_count &&
 	    status.uidnext == fctx->fbox->last_uidnext) {
 		/* no new messages since last check */
@@ -539,8 +540,8 @@
 		ret = -1;
 
 	if (ret == 0) {
-		mailbox_get_status(box, STATUS_MESSAGES | STATUS_UIDNEXT,
-				   &status);
+		mailbox_get_open_status(box, STATUS_MESSAGES | STATUS_UIDNEXT,
+					&status);
 		fbox->last_messages_count = status.messages;
 		fbox->last_uidnext = status.uidnext;
 	}
@@ -777,7 +778,8 @@
 	struct fts_mailbox *fbox = FTS_CONTEXT(ctx->transaction->box);
 	struct mailbox_status status;
 
-	mailbox_get_status(ctx->transaction->box, STATUS_MESSAGES, &status);
+	mailbox_get_open_status(ctx->transaction->box,
+				STATUS_MESSAGES, &status);
 
 	fctx->seqs_set = FALSE;
 	ctx->seq = fctx->first_nonindexed_seq - 1;
--- a/src/plugins/imap-acl/imap-acl-plugin.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/plugins/imap-acl/imap-acl-plugin.c	Sat Jan 01 15:59:02 2011 +0200
@@ -58,7 +58,6 @@
 	struct mail_namespace *ns;
 	struct mailbox *box;
 	const char *storage_name;
-	enum mailbox_name_status status;
 	int ret;
 
 	if (ACL_USER_CONTEXT(cmd->client->user) == NULL) {
@@ -66,19 +65,10 @@
 		return NULL;
 	}
 
-	ns = client_find_namespace(cmd, name, &storage_name, &status);
+	ns = client_find_namespace(cmd, name, &storage_name);
 	if (ns == NULL)
 		return NULL;
 
-	switch (status) {
-	case MAILBOX_NAME_INVALID:
-	case MAILBOX_NAME_VALID:
-		client_fail_mailbox_name_status(cmd, name, NULL, status);
-		return NULL;
-	default:
-		break;
-	}
-
 	/* Force opening the mailbox so that we can give a nicer error message
 	   if mailbox isn't selectable but is listable. */
 	box = mailbox_alloc(ns->list, storage_name, ACL_MAILBOX_FLAGS |
@@ -330,7 +320,7 @@
 		return TRUE;
 	}
 
-	ns = client_find_namespace(cmd, mailbox, &storage_name, NULL);
+	ns = client_find_namespace(cmd, mailbox, &storage_name);
 	if (ns == NULL)
 		return TRUE;
 
--- a/src/plugins/imap-quota/imap-quota-plugin.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/plugins/imap-quota/imap-quota-plugin.c	Sat Jan 01 15:59:02 2011 +0200
@@ -81,7 +81,7 @@
 	if (!client_read_string_args(cmd, 1, &mailbox))
 		return FALSE;
 
-	ns = client_find_namespace(cmd, mailbox, &storage_name, NULL);
+	ns = client_find_namespace(cmd, mailbox, &storage_name);
 	if (ns == NULL)
 		return TRUE;
 
--- a/src/plugins/lazy-expunge/lazy-expunge-plugin.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/plugins/lazy-expunge/lazy-expunge-plugin.c	Sat Jan 01 15:59:02 2011 +0200
@@ -242,7 +242,7 @@
 	int ret;
 
 	/* make sure the destination root directory exists */
-	mailbox_list_get_dir_permissions(dest_list, NULL, &mode, &gid, &origin);
+	mailbox_list_get_root_dir_permissions(dest_list, &mode, &gid, &origin);
 	dir = mailbox_list_get_path(dest_list, NULL, MAILBOX_LIST_PATH_TYPE_DIR);
 	if (mkdir_parents_chgrp(dir, mode, gid, origin) < 0 &&
 	    errno != EEXIST) {
--- a/src/plugins/quota/quota-maildir.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/plugins/quota/quota-maildir.c	Sat Jan 01 15:59:02 2011 +0200
@@ -236,16 +236,15 @@
 	namespaces = array_get(&root->root.quota->namespaces, &count);
 	i_assert(count > 0);
 	for (i = 0; i < count; i++) {
-		if ((namespaces[i]->flags & NAMESPACE_FLAG_INBOX_USER) != 0) {
-			mailbox_list_get_permissions(namespaces[i]->list,
-						     NULL, &mode, &gid,
-						     &gid_origin);
-			mailbox_list_get_dir_permissions(namespaces[i]->list,
-							 NULL,
-							 &dir_mode, &dir_gid,
-							 &dir_gid_origin);
-			break;
-		}
+		if ((namespaces[i]->flags & NAMESPACE_FLAG_INBOX_USER) == 0)
+			continue;
+
+		mailbox_list_get_root_permissions(namespaces[i]->list,
+						  &mode, &gid, &gid_origin);
+		mailbox_list_get_root_dir_permissions(namespaces[i]->list,
+						      &dir_mode, &dir_gid,
+						      &dir_gid_origin);
+		break;
 	}
 
 	dotlock_settings.use_excl_lock = set->dotlock_use_excl;
--- a/src/plugins/virtual/virtual-config.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/plugins/virtual/virtual-config.c	Sat Jan 01 15:59:02 2011 +0200
@@ -327,23 +327,24 @@
 	struct mail_storage *storage = mbox->box.storage;
 	struct virtual_parse_context ctx;
 	struct stat st;
-	const char *path, *line, *error;
+	const char *box_path, *path, *line, *error;
 	unsigned int linenum = 0;
 	int fd, ret = 0;
 
 	i_array_init(&mbox->backend_boxes, 8);
 	mbox->search_args_crc32 = (uint32_t)-1;
 
-	path = t_strconcat(mbox->box.path, "/"VIRTUAL_CONFIG_FNAME, NULL);
+	box_path = mailbox_get_path(&mbox->box);
+	path = t_strconcat(box_path, "/"VIRTUAL_CONFIG_FNAME, NULL);
 	fd = open(path, O_RDONLY);
 	if (fd == -1) {
 		if (errno == EACCES) {
 			mail_storage_set_critical(storage, "%s",
-				mail_error_eacces_msg("stat", mbox->box.path));
+				mail_error_eacces_msg("open", path));
 		} else if (errno != ENOENT) {
 			mail_storage_set_critical(storage,
 						  "open(%s) failed: %m", path);
-		} else if (stat(mbox->box.path, &st) == 0) {
+		} else if (stat(box_path, &st) == 0) {
 			mail_storage_set_error(storage, MAIL_ERROR_NOTPOSSIBLE,
 				"Virtual mailbox missing configuration file");
 		} else if (errno == ENOENT) {
@@ -351,7 +352,7 @@
 				T_MAIL_ERR_MAILBOX_NOT_FOUND(mbox->box.name));
 		} else {
 			mail_storage_set_critical(storage,
-				"stat(%s) failed: %m", mbox->box.path);
+				"stat(%s) failed: %m", box_path);
 		}
 		return -1;
 	}
--- a/src/plugins/virtual/virtual-mail.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/plugins/virtual/virtual-mail.c	Sat Jan 01 15:59:02 2011 +0200
@@ -42,7 +42,6 @@
 	vmail->imail.data_pool =
 		pool_alloconly_create("virtual index_mail", 512);
 	vmail->imail.ibox = INDEX_STORAGE_CONTEXT(t->box);
-	vmail->imail.trans = (struct index_transaction_context *)t;
 
 	vmail->wanted_fields = wanted_fields;
 	if (wanted_headers != NULL) {
@@ -98,7 +97,7 @@
 
 	backend_headers = vmail->wanted_headers == NULL ? NULL :
 		mailbox_header_lookup_init(bbox->box,
-					   vmail->wanted_headers->headers);
+					   vmail->wanted_headers->name);
 	vmail->backend_mail = mail_alloc(backend_trans, vmail->wanted_fields,
 					 backend_headers);
 	if (backend_headers != NULL)
@@ -302,7 +301,7 @@
 		return -1;
 
 	backend_headers = mailbox_header_lookup_init(vmail->backend_mail->box,
-						     headers->headers);
+						     headers->name);
 	ret = mail_get_header_stream(vmail->backend_mail, backend_headers,
 				     stream_r);
 	mailbox_header_lookup_unref(&backend_headers);
--- a/src/plugins/virtual/virtual-save.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/plugins/virtual/virtual-save.c	Sat Jan 01 15:59:02 2011 +0200
@@ -25,7 +25,7 @@
 		return _t->save_ctx;
 
 	ctx = i_new(struct virtual_save_context, 1);
-	ctx->ctx.transaction = &t->ictx.mailbox_ctx;
+	ctx->ctx.transaction = &t->t;
 
 	if (mbox->save_bbox != NULL) {
 		backend_trans =
@@ -50,7 +50,7 @@
 		return NULL;
 
 	t_array_init(&kw_strings, src_keywords->count + 1);
-	mailbox_get_status(src_box, STATUS_KEYWORDS, &status);
+	mailbox_get_open_status(src_box, STATUS_KEYWORDS, &status);
 
 	for (i = 0; i < src_keywords->count; i++) {
 		kwp = array_idx(status.keywords, src_keywords->idx[i]);
@@ -125,7 +125,7 @@
 	struct virtual_save_context *ctx = (struct virtual_save_context *)_ctx;
 
 	if (ctx->backend_keywords != NULL)
-		mailbox_keywords_unref(ctx->backend_box, &ctx->backend_keywords);
+		mailbox_keywords_unref(&ctx->backend_keywords);
 	if (ctx->backend_save_ctx != NULL)
 		mailbox_save_cancel(&ctx->backend_save_ctx);
 	i_free(ctx);
--- a/src/plugins/virtual/virtual-storage.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/plugins/virtual/virtual-storage.c	Sat Jan 01 15:59:02 2011 +0200
@@ -29,6 +29,7 @@
 
 extern struct mail_storage virtual_storage;
 extern struct mailbox virtual_mailbox;
+extern struct virtual_mailbox_vfuncs virtual_mailbox_vfuncs;
 
 struct virtual_storage_module virtual_storage_module =
 	MODULE_CONTEXT_INIT(&mail_storage_module_register);
@@ -234,6 +235,7 @@
 	mbox->box.storage = _storage;
 	mbox->box.list = list;
 	mbox->box.mail_vfuncs = &virtual_mail_vfuncs;
+	mbox->vfuncs = virtual_mailbox_vfuncs;
 
 	index_storage_mailbox_alloc(&mbox->box, name, flags,
 				    VIRTUAL_INDEX_PREFIX);
@@ -333,12 +335,16 @@
 }
 
 static int
-virtual_mailbox_get_guid(struct mailbox *box,
-			 uint8_t guid[MAIL_GUID_128_SIZE] ATTR_UNUSED)
+virtual_mailbox_get_metadata(struct mailbox *box,
+			     enum mailbox_metadata_items items,
+			     struct mailbox_metadata *metadata_r)
 {
-	mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE,
-			       "Virtual mailboxes have no GUIDs");
-	return -1;
+	if ((items & MAILBOX_METADATA_GUID) != 0) {
+		mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE,
+				       "Virtual mailboxes have no GUIDs");
+		return -1;
+	}
+	return index_mailbox_get_metadata(box, items, metadata_r);
 }
 
 static void
@@ -435,7 +441,7 @@
 	if (bbox == NULL)
 		return FALSE;
 
-	mailbox_get_status(bbox->box, STATUS_UIDVALIDITY, &status);
+	mailbox_get_open_status(bbox->box, STATUS_UIDVALIDITY, &status);
 	if (status.uidvalidity != backend_uidvalidity)
 		return FALSE;
 
@@ -515,7 +521,7 @@
 		index_storage_mailbox_delete,
 		index_storage_mailbox_rename,
 		index_storage_get_status,
-		virtual_mailbox_get_guid,
+		virtual_mailbox_get_metadata,
 		NULL,
 		NULL,
 		virtual_storage_sync_init,
@@ -526,21 +532,8 @@
 		virtual_transaction_begin,
 		virtual_transaction_commit,
 		virtual_transaction_rollback,
-		index_transaction_set_max_modseq,
-		index_keywords_create,
-		index_keywords_create_from_indexes,
-		index_keywords_ref,
-		index_keywords_unref,
-		index_keyword_is_valid,
-		index_storage_get_seq_range,
-		index_storage_get_uid_range,
-		index_storage_get_expunges,
-		virtual_get_virtual_uid,
-		virtual_get_virtual_backend_boxes,
-		virtual_get_virtual_box_patterns,
+		NULL,
 		virtual_mail_alloc,
-		index_header_lookup_init,
-		index_header_lookup_deinit,
 		virtual_search_init,
 		virtual_search_deinit,
 		virtual_search_next_nonblock,
@@ -551,7 +544,12 @@
 		virtual_save_finish,
 		virtual_save_cancel,
 		mail_storage_copy,
-		NULL,
 		virtual_is_inconsistent
 	}
 };
+
+struct virtual_mailbox_vfuncs virtual_mailbox_vfuncs = {
+	virtual_get_virtual_uid,
+	virtual_get_virtual_backend_boxes,
+	virtual_get_virtual_box_patterns
+};
--- a/src/plugins/virtual/virtual-storage.h	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/plugins/virtual/virtual-storage.h	Sat Jan 01 15:59:02 2011 +0200
@@ -99,6 +99,19 @@
 };
 ARRAY_DEFINE_TYPE(virtual_backend_box, struct virtual_backend_box *);
 
+struct virtual_mailbox_vfuncs {
+	bool (*get_virtual_uid)(struct mailbox *box,
+				const char *backend_mailbox,
+				uint32_t backend_uidvalidity,
+				uint32_t backend_uid, uint32_t *uid_r);
+	void (*get_virtual_backend_boxes)(struct mailbox *box,
+					  ARRAY_TYPE(mailboxes) *mailboxes,
+					  bool only_with_msgs);
+	void (*get_virtual_box_patterns)(struct mailbox *box,
+				ARRAY_TYPE(mailbox_virtual_patterns) *includes,
+				ARRAY_TYPE(mailbox_virtual_patterns) *excludes);
+};
+
 struct virtual_mailbox {
 	struct mailbox box;
 	struct virtual_storage *storage;
@@ -122,6 +135,8 @@
 	ARRAY_TYPE(mailbox_virtual_patterns) list_include_patterns;
 	ARRAY_TYPE(mailbox_virtual_patterns) list_exclude_patterns;
 
+	struct virtual_mailbox_vfuncs vfuncs;
+
 	unsigned int uids_mapped:1;
 	unsigned int sync_initialized:1;
 	unsigned int inconsistent:1;
--- a/src/plugins/virtual/virtual-sync.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/plugins/virtual/virtual-sync.c	Sat Jan 01 15:59:02 2011 +0200
@@ -148,6 +148,7 @@
 
 static int virtual_sync_ext_header_read(struct virtual_sync_context *ctx)
 {
+	const char *box_path = mailbox_get_path(&ctx->mbox->box);
 	const struct virtual_mail_index_header *ext_hdr;
 	const struct mail_index_header *hdr;
 	const struct virtual_mail_index_mailbox_record *mailboxes;
@@ -184,7 +185,7 @@
 		if (ext_name_offset >= ext_size ||
 		    ext_hdr->mailbox_count > INT_MAX/sizeof(*mailboxes)) {
 			i_error("virtual index %s: Broken mailbox_count header",
-				ctx->mbox->box.path);
+				box_path);
 			ctx->index_broken = TRUE;
 			ext_mailbox_count = 0;
 			ret = 0;
@@ -199,18 +200,18 @@
 		if (mailboxes[i].id > ext_hdr->highest_mailbox_id ||
 		    mailboxes[i].id <= prev_mailbox_id) {
 			i_error("virtual index %s: Broken mailbox id",
-				ctx->mbox->box.path);
+				box_path);
 			break;
 		}
 		if (mailboxes[i].name_len == 0 ||
 		    mailboxes[i].name_len > ext_size) {
 			i_error("virtual index %s: Broken mailbox name_len",
-				ctx->mbox->box.path);
+				box_path);
 			break;
 		}
 		if (ext_name_offset + mailboxes[i].name_len > ext_size) {
 			i_error("virtual index %s: Broken mailbox list",
-				ctx->mbox->box.path);
+				box_path);
 			break;
 		}
 		T_BEGIN {
@@ -405,7 +406,7 @@
 				MODIFY_ADD : MODIFY_REMOVE;
 			mail_update_keywords(bbox->sync_mail,
 					     modify_type, keywords);
-			mailbox_keywords_unref(bbox->box, &keywords);
+			mailbox_keywords_unref(&keywords);
 			break;
 		case MAIL_INDEX_SYNC_TYPE_KEYWORD_RESET:
 			kw_names[0] = NULL;
@@ -413,7 +414,7 @@
 								 kw_names);
 			mail_update_keywords(bbox->sync_mail, MODIFY_REPLACE,
 					     keywords);
-			mailbox_keywords_unref(bbox->box, &keywords);
+			mailbox_keywords_unref(&keywords);
 			break;
 		case MAIL_INDEX_SYNC_TYPE_APPEND:
 			i_unreached();
@@ -985,8 +986,8 @@
 	unsigned int mailbox_offset;
 	uint64_t wanted_ondisk_highest_modseq;
 
-	mailbox_get_status(bbox->box, STATUS_UIDVALIDITY |
-			   STATUS_HIGHESTMODSEQ, &status);
+	mailbox_get_open_status(bbox->box, STATUS_UIDVALIDITY |
+				STATUS_HIGHESTMODSEQ, &status);
 	wanted_ondisk_highest_modseq =
 		array_count(&bbox->sync_pending_removes) > 0 ? 0 :
 		status.highest_modseq;
@@ -1052,7 +1053,7 @@
 		if (mailbox_sync(bbox->box, sync_flags) < 0)
 			return -1;
 
-		mailbox_get_status(bbox->box, STATUS_UIDVALIDITY, &status);
+		mailbox_get_open_status(bbox->box, STATUS_UIDVALIDITY, &status);
 		virtual_backend_box_sync_mail_set(bbox);
 		if (status.uidvalidity != bbox->sync_uid_validity) {
 			/* UID validity changed since last sync (or this is
@@ -1444,7 +1445,7 @@
 			if (mail_index_unlink(ctx->index) < 0) {
 				i_error("virtual index %s: Failed to unlink() "
 					"broken indexes: %m",
-					ctx->mbox->box.path);
+					mailbox_get_path(&ctx->mbox->box));
 			}
 		}
 		mail_index_sync_rollback(&ctx->index_sync_ctx);
--- a/src/plugins/virtual/virtual-transaction.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/plugins/virtual/virtual-transaction.c	Sat Jan 01 15:59:02 2011 +0200
@@ -35,8 +35,8 @@
 	vt = i_new(struct virtual_transaction_context, 1);
 	i_array_init(&vt->backend_transactions,
 		     array_count(&mbox->backend_boxes));
-	index_transaction_init(&vt->ictx, box, flags);
-	return &vt->ictx.mailbox_ctx;
+	index_transaction_init(&vt->t, box, flags);
+	return &vt->t;
 }
 
 int virtual_transaction_commit(struct mailbox_transaction_context *t,
--- a/src/plugins/virtual/virtual-transaction.h	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/plugins/virtual/virtual-transaction.h	Sat Jan 01 15:59:02 2011 +0200
@@ -4,7 +4,7 @@
 #include "index-storage.h"
 
 struct virtual_transaction_context {
-	struct index_transaction_context ictx;
+	struct mailbox_transaction_context t;
 
 	ARRAY_DEFINE(backend_transactions,
 		     struct mailbox_transaction_context *);
--- a/src/plugins/zlib/zlib-plugin.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/plugins/zlib/zlib-plugin.c	Sat Jan 01 15:59:02 2011 +0200
@@ -310,14 +310,16 @@
 	if (mail_storage_is_mailbox_file(box->storage)) {
 		/* looks like a compressed single file mailbox. we should be
 		   able to handle this. */
-		fd = open(box->path, O_RDONLY);
+		const char *box_path = mailbox_get_path(box);
+
+		fd = open(box_path, O_RDONLY);
 		if (fd == -1) {
 			mail_storage_set_critical(box->storage,
-				"open(%s) failed: %m", box->path);
+				"open(%s) failed: %m", box_path);
 			return -1;
 		}
 		input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE);
-		i_stream_set_name(input, box->path);
+		i_stream_set_name(input, box_path);
 		box->input = handler->create_istream(input, TRUE);
 		i_stream_unref(&input);
 		box->flags |= MAILBOX_FLAG_READONLY;
--- a/src/pop3/pop3-client.c	Sat Jan 01 14:34:14 2011 +0200
+++ b/src/pop3/pop3-client.c	Sat Jan 01 15:59:02 2011 +0200
@@ -116,7 +116,7 @@
 
 	*failed_uid_r = 0;
 
-	mailbox_get_status(client->mailbox, STATUS_UIDVALIDITY, &status);
+	mailbox_get_open_status(client->mailbox, STATUS_UIDVALIDITY, &status);
 	client->uid_validity = status.uidvalidity;
 	client->messages_count = status.messages;