changeset 1978:6303ef092c5b HEAD

mbox code compiles again, but syncing is only partially implemented so accessing mboxes fails.. Also some cleanups to index-storage and maildir code.
author Timo Sirainen <tss@iki.fi>
date Thu, 06 May 2004 04:22:25 +0300
parents ab2f615e0ce8
children a4850e1c56f7
files configure.in src/lib-storage/index/index-mail.c src/lib-storage/index/index-mail.h src/lib-storage/index/index-storage.c src/lib-storage/index/index-storage.h src/lib-storage/index/maildir/maildir-list.c src/lib-storage/index/maildir/maildir-mail.c src/lib-storage/index/maildir/maildir-storage.c src/lib-storage/index/maildir/maildir-storage.h src/lib-storage/index/maildir/maildir-sync.c src/lib-storage/index/maildir/maildir-uidlist.c src/lib-storage/index/maildir/maildir-uidlist.h src/lib-storage/index/mbox/Makefile.am src/lib-storage/index/mbox/istream-raw-mbox.c src/lib-storage/index/mbox/istream-raw-mbox.h src/lib-storage/index/mbox/mbox-expunge.c src/lib-storage/index/mbox/mbox-file.c src/lib-storage/index/mbox/mbox-file.h src/lib-storage/index/mbox/mbox-from.c src/lib-storage/index/mbox/mbox-from.h src/lib-storage/index/mbox/mbox-list.c src/lib-storage/index/mbox/mbox-lock.c src/lib-storage/index/mbox/mbox-lock.h src/lib-storage/index/mbox/mbox-mail.c src/lib-storage/index/mbox/mbox-save.c src/lib-storage/index/mbox/mbox-storage.c src/lib-storage/index/mbox/mbox-storage.h src/lib-storage/index/mbox/mbox-sync-parse.c src/lib-storage/index/mbox/mbox-sync-private.h src/lib-storage/index/mbox/mbox-sync-rewrite.c src/lib-storage/index/mbox/mbox-sync.c src/lib-storage/index/mbox/mbox-transaction.c
diffstat 32 files changed, 1350 insertions(+), 841 deletions(-) [+]
line wrap: on
line diff
--- a/configure.in	Tue May 04 21:13:10 2004 +0300
+++ b/configure.in	Thu May 06 04:22:25 2004 +0300
@@ -1,7 +1,7 @@
 AC_INIT(src)
 
 AM_CONFIG_HEADER(config.h)
-AM_INIT_AUTOMAKE(dovecot, 1.0-test3)
+AM_INIT_AUTOMAKE(dovecot, 1.0-test6)
 
 AM_MAINTAINER_MODE
 
@@ -195,7 +195,7 @@
 AC_ARG_WITH(storages,
 [  --with-storages         Build specified mail storage formats (maildir,mbox)], [
 	mail_storages=`echo "$withval"|sed 's/,/ /g'` ],
-	mail_storages="maildir")
+	mail_storages="maildir mbox")
 
 dnl * gcc specific options
 if test "x$ac_cv_prog_gcc" = "xyes"; then
--- a/src/lib-storage/index/index-mail.c	Tue May 04 21:13:10 2004 +0300
+++ b/src/lib-storage/index/index-mail.c	Thu May 06 04:22:25 2004 +0300
@@ -200,6 +200,21 @@
 	return data->parts;
 }
 
+time_t index_mail_get_received_date(struct mail *_mail)
+{
+	struct index_mail *mail = (struct index_mail *) _mail;
+	struct index_mail_data *data = &mail->data;
+
+	if (data->received_date == (time_t)-1 &&
+	    (mail->wanted_fields & MAIL_FETCH_RECEIVED_DATE) == 0) {
+		data->received_date = index_mail_get_cached_received_date(mail);
+		if (data->received_date != (time_t)-1)
+			return data->received_date;
+	}
+
+	return data->received_date;
+}
+
 time_t index_mail_get_date(struct mail *_mail, int *timezone)
 {
 	struct index_mail *mail = (struct index_mail *) _mail;
--- a/src/lib-storage/index/index-mail.h	Tue May 04 21:13:10 2004 +0300
+++ b/src/lib-storage/index/index-mail.h	Thu May 06 04:22:25 2004 +0300
@@ -94,6 +94,7 @@
 
 const struct mail_full_flags *index_mail_get_flags(struct mail *_mail);
 const struct message_part *index_mail_get_parts(struct mail *_mail);
+time_t index_mail_get_received_date(struct mail *_mail);
 time_t index_mail_get_date(struct mail *_mail, int *timezone);
 uoff_t index_mail_get_size(struct mail *_mail);
 struct istream *index_mail_init_stream(struct index_mail *mail,
--- a/src/lib-storage/index/index-storage.c	Tue May 04 21:13:10 2004 +0300
+++ b/src/lib-storage/index/index-storage.c	Thu May 06 04:22:25 2004 +0300
@@ -39,8 +39,11 @@
 	index_storage_refcount++;
 }
 
-void index_storage_deinit(struct index_storage *storage __attr_unused__)
+void index_storage_deinit(struct index_storage *storage)
 {
+	i_free(storage->storage.namespace);
+	i_free(storage->storage.error);
+
 	if (--index_storage_refcount > 0)
 		return;
 
@@ -243,10 +246,10 @@
 	return ret;
 }
 
-static void lock_notify(enum mailbox_lock_notify_type notify_type,
-			unsigned int secs_left, void *context)
+void index_storage_lock_notify(struct index_mailbox *ibox,
+			       enum mailbox_lock_notify_type notify_type,
+			       unsigned int secs_left)
 {
-	struct index_mailbox *ibox = context;
 	struct index_storage *storage = ibox->storage;
 	const char *str;
 	time_t now;
@@ -293,7 +296,7 @@
 	}
 }
 
-void index_storage_reset_lock_notify(struct index_mailbox *ibox)
+void index_storage_lock_notify_reset(struct index_mailbox *ibox)
 {
 	ibox->next_lock_notify = time(NULL) + LOCK_NOTIFY_INTERVAL;
 	ibox->last_notify_type = MAILBOX_LOCK_NOTIFY_NONE;
--- a/src/lib-storage/index/index-storage.h	Tue May 04 21:13:10 2004 +0300
+++ b/src/lib-storage/index/index-storage.h	Thu May 06 04:22:25 2004 +0300
@@ -1,6 +1,7 @@
 #ifndef __INDEX_STORAGE_H
 #define __INDEX_STORAGE_H
 
+#include "file-dotlock.h"
 #include "mail-storage-private.h"
 #include "mail-index.h"
 #include "index-mail.h"
@@ -74,7 +75,21 @@
 	uint32_t commit_log_file_seq;
 	uoff_t commit_log_file_offset;
 
-	/* sync: */
+	/* mbox: */
+	int mbox_fd;
+	struct istream *mbox_stream, *mbox_file_stream;
+	int mbox_lock_type;
+	dev_t mbox_dev;
+	ino_t mbox_ino;
+	unsigned int mbox_locks;
+	struct dotlock mbox_dotlock;
+	unsigned int mbox_lock_id;
+
+	buffer_t *mbox_data_buf;
+	const uoff_t *mbox_data;
+	uint32_t mbox_data_count;
+
+	/* maildir sync: */
 	struct maildir_uidlist *uidlist;
 	time_t last_new_mtime, last_cur_mtime, last_new_sync_time;
 	time_t dirty_cur_time;
@@ -99,7 +114,11 @@
 };
 
 int mail_storage_set_index_error(struct index_mailbox *ibox);
-void index_storage_reset_lock_notify(struct index_mailbox *ibox);
+
+void index_storage_lock_notify(struct index_mailbox *ibox,
+			       enum mailbox_lock_notify_type notify_type,
+			       unsigned int secs_left);
+void index_storage_lock_notify_reset(struct index_mailbox *ibox);
 
 struct mail_index *
 index_storage_alloc(const char *index_dir,
--- a/src/lib-storage/index/maildir/maildir-list.c	Tue May 04 21:13:10 2004 +0300
+++ b/src/lib-storage/index/maildir/maildir-list.c	Thu May 06 04:22:25 2004 +0300
@@ -19,7 +19,6 @@
 	struct mailbox_list_context mailbox_ctx;
 	pool_t pool;
 
-	struct mail_storage *storage;
 	const char *dir, *prefix;
         enum mailbox_list_flags flags;
 
@@ -63,7 +62,7 @@
 	dirp = opendir(ctx->dir);
 	if (dirp == NULL) {
 		if (errno != ENOENT) {
-			mail_storage_set_critical(ctx->storage,
+			mail_storage_set_critical(ctx->mailbox_ctx.storage,
 				"opendir(%s) failed: %m", ctx->dir);
 			return FALSE;
 		}
@@ -171,7 +170,7 @@
 	}
 
 	if (closedir(dirp) < 0) {
-		mail_storage_set_critical(ctx->storage,
+		mail_storage_set_critical(ctx->mailbox_ctx.storage,
 					  "readdir(%s) failed: %m", ctx->dir);
 		return FALSE;
 	}
@@ -184,7 +183,8 @@
 static int maildir_fill_subscribed(struct maildir_list_context *ctx,
 				   struct imap_match_glob *glob)
 {
-	struct index_storage *istorage = (struct index_storage *)ctx->storage;
+	struct index_storage *istorage =
+		(struct index_storage *)ctx->mailbox_ctx.storage;
 	struct subsfile_list_context *subsfile_ctx;
 	const char *path, *name, *p;
 	struct mailbox_node *node;
@@ -193,7 +193,7 @@
 	path = t_strconcat(istorage->control_dir != NULL ?
 			   istorage->control_dir : istorage->dir,
 			   "/" SUBSCRIPTION_FILE_NAME, NULL);
-	subsfile_ctx = subsfile_list_init(ctx->storage, path);
+	subsfile_ctx = subsfile_list_init(ctx->mailbox_ctx.storage, path);
 	if (subsfile_ctx == NULL)
 		return FALSE;
 
@@ -245,8 +245,8 @@
 
 	pool = pool_alloconly_create("maildir_list", 1024);
 	ctx = p_new(pool, struct maildir_list_context, 1);
+	ctx->mailbox_ctx.storage = storage;
 	ctx->pool = pool;
-	ctx->storage = storage;
 	ctx->flags = flags;
 	ctx->tree_ctx = mailbox_tree_init(MAILDIR_FS_SEP);
 
@@ -352,7 +352,7 @@
 
 		str_truncate(ctx->node_path, 0);
 		node = find_next(&ctx->root, ctx->node_path,
-				 ctx->storage->hierarchy_sep);
+				 ctx->mailbox_ctx.storage->hierarchy_sep);
                 ctx->parent_pos = str_len(ctx->node_path);
 
 		if (node == NULL)
@@ -364,8 +364,10 @@
 	node->flags &= ~MAILBOX_FLAG_MATCHED;
 
 	str_truncate(ctx->node_path, ctx->parent_pos);
-	if (ctx->parent_pos != 0)
-		str_append_c(ctx->node_path, ctx->storage->hierarchy_sep);
+	if (ctx->parent_pos != 0) {
+		str_append_c(ctx->node_path,
+			     ctx->mailbox_ctx.storage->hierarchy_sep);
+	}
 	str_append(ctx->node_path, node->name);
 
 	ctx->list.name = str_c(ctx->node_path);
--- a/src/lib-storage/index/maildir/maildir-mail.c	Tue May 04 21:13:10 2004 +0300
+++ b/src/lib-storage/index/maildir/maildir-mail.c	Thu May 06 04:22:25 2004 +0300
@@ -68,9 +68,8 @@
 {
 	struct index_mail *mail = (struct index_mail *)_mail;
 	struct index_mail_data *data = &mail->data;
-        const struct mail_full_flags *flags;
 
-	flags = index_mail_get_flags(_mail);
+	(void)index_mail_get_flags(_mail);
 
 	if (maildir_uidlist_is_recent(mail->ibox->uidlist, _mail->uid))
 		data->flags.flags |= MAIL_RECENT;
@@ -84,15 +83,10 @@
 	struct stat st;
 	int fd;
 
+	(void)index_mail_get_received_date(_mail);
 	if (data->received_date != (time_t)-1)
 		return data->received_date;
 
-	if ((mail->wanted_fields & MAIL_FETCH_RECEIVED_DATE) == 0) {
-		data->received_date = index_mail_get_cached_received_date(mail);
-		if (data->received_date != (time_t)-1)
-			return data->received_date;
-	}
-
 	if (data->stream != NULL) {
 		fd = i_stream_get_fd(data->stream);
 		i_assert(fd != -1);
--- a/src/lib-storage/index/maildir/maildir-storage.c	Tue May 04 21:13:10 2004 +0300
+++ b/src/lib-storage/index/maildir/maildir-storage.c	Thu May 06 04:22:25 2004 +0300
@@ -108,9 +108,6 @@
 
 	index_storage_deinit(storage);
 
-	i_free(storage->storage.namespace);
-	i_free(storage->storage.error);
-
 	i_free(storage->dir);
 	i_free(storage->inbox_path);
 	i_free(storage->index_dir);
--- a/src/lib-storage/index/maildir/maildir-storage.h	Tue May 04 21:13:10 2004 +0300
+++ b/src/lib-storage/index/maildir/maildir-storage.h	Thu May 06 04:22:25 2004 +0300
@@ -57,9 +57,6 @@
 int maildir_copy_commit(struct maildir_copy_context *ctx);
 void maildir_copy_rollback(struct maildir_copy_context *ctx);
 
-int maildir_storage_expunge(struct mail *mail,
-			    struct mailbox_transaction_context *t);
-
 const char *maildir_fix_mailbox_name(struct index_storage *storage,
 				     const char *name, int remove_namespace);
 const char *maildir_get_path(struct index_storage *storage, const char *name);
--- a/src/lib-storage/index/maildir/maildir-sync.c	Tue May 04 21:13:10 2004 +0300
+++ b/src/lib-storage/index/maildir/maildir-sync.c	Thu May 06 04:22:25 2004 +0300
@@ -390,6 +390,13 @@
 		if (dp->d_name[0] == '.')
 			continue;
 
+		ret = maildir_uidlist_sync_next_pre(ctx->uidlist_sync_ctx,
+						    dp->d_name);
+		if (ret == 0)
+			continue;
+		if (ret < 0)
+			break;
+
 		flags = 0;
 		if (move_new) {
 			str_truncate(src, 0);
--- a/src/lib-storage/index/maildir/maildir-uidlist.c	Tue May 04 21:13:10 2004 +0300
+++ b/src/lib-storage/index/maildir/maildir-uidlist.c	Thu May 06 04:22:25 2004 +0300
@@ -58,6 +58,7 @@
 
 	unsigned int partial:1;
 	unsigned int synced:1;
+	unsigned int locked:1;
 	unsigned int failed:1;
 };
 
@@ -559,8 +560,10 @@
 	/* lock and update uidlist to see if it's just been added */
 	ret = maildir_uidlist_try_lock(ctx->uidlist);
 	if (ret <= 0) {
-		if (ret == 0)
-			return 1; // FIXME: does it work right?
+		if (ret == 0) {
+			ctx->locked = TRUE;
+			return -1;
+		}
 		ctx->failed = TRUE;
 		return -1;
 	}
@@ -580,21 +583,10 @@
 {
 	struct maildir_uidlist *uidlist = ctx->uidlist;
 	struct maildir_uidlist_rec *rec;
-	int ret;
 
 	/* we'll update uidlist directly */
 	rec = hash_lookup(uidlist->files, filename);
-	if (rec == NULL && !ctx->synced) {
-		ret = maildir_uidlist_sync_uidlist(ctx);
-		if (ret < 0)
-			return -1;
-		if (ret == 0) {
-			return maildir_uidlist_sync_next_partial(ctx, filename,
-								 flags);
-		}
-
-		rec = hash_lookup(uidlist->files, filename);
-	}
+	i_assert(rec != NULL || ctx->synced);
 
 	if (rec == NULL) {
 		if (ctx->new_files_count == 0) {
@@ -616,13 +608,33 @@
 	return 1;
 }
 
+int maildir_uidlist_sync_next_pre(struct maildir_uidlist_sync_ctx *ctx,
+				  const char *filename)
+{
+	int ret;
+
+	if (!ctx->synced &&
+	    hash_lookup(ctx->uidlist->files, filename) == NULL &&
+	    (ctx->partial || hash_lookup(ctx->files, filename) == NULL)) {
+		if (ctx->locked)
+			return 0;
+
+		ret = maildir_uidlist_sync_uidlist(ctx);
+		if (ret < 0)
+			return ctx->locked ? 0 : -1;
+		if (ret == 0)
+			return maildir_uidlist_sync_next_pre(ctx, filename);
+	}
+
+	return 1;
+}
+
 int maildir_uidlist_sync_next(struct maildir_uidlist_sync_ctx *ctx,
 			      const char *filename,
 			      enum maildir_uidlist_rec_flag flags)
 {
 	struct maildir_uidlist *uidlist = ctx->uidlist;
 	struct maildir_uidlist_rec *rec, *old_rec;
-	int ret;
 
 	if (ctx->failed)
 		return -1;
@@ -642,16 +654,7 @@
 				MAILDIR_UIDLIST_REC_FLAG_MOVED);
 	} else {
 		old_rec = hash_lookup(uidlist->files, filename);
-		if (old_rec == NULL && !ctx->synced) {
-			ret = maildir_uidlist_sync_uidlist(ctx);
-			if (ret < 0)
-				return -1;
-			if (ret == 0) {
-				return maildir_uidlist_sync_next(ctx, filename,
-								 flags);
-			}
-			old_rec = hash_lookup(uidlist->files, filename);
-		}
+		i_assert(old_rec != NULL || ctx->synced);
 
 		rec = p_new(ctx->record_pool, struct maildir_uidlist_rec, 1);
 
@@ -762,10 +765,10 @@
 {
 	int ret = ctx->failed ? -1 : 0;
 
-	// FIXME: we most likely don't handle ctx->failed well enough
-	if (!ctx->partial)
-		maildir_uidlist_swap(ctx);
-	else {
+	if (!ctx->partial) {
+		if (!ctx->failed && !ctx->locked)
+			maildir_uidlist_swap(ctx);
+	} else {
 		if (ctx->new_files_count != 0) {
 			maildir_uidlist_assign_uids(ctx->uidlist,
 						    ctx->first_new_pos);
@@ -773,7 +776,7 @@
 		maildir_uidlist_mark_all(ctx->uidlist, FALSE);
 	}
 
-	if (ctx->new_files_count != 0 && ret == 0)
+	if (ctx->new_files_count != 0 && !ctx->failed && !ctx->locked)
 		ret = maildir_uidlist_rewrite(ctx->uidlist);
 
 	if (UIDLIST_IS_LOCKED(ctx->uidlist))
--- a/src/lib-storage/index/maildir/maildir-uidlist.h	Tue May 04 21:13:10 2004 +0300
+++ b/src/lib-storage/index/maildir/maildir-uidlist.h	Thu May 06 04:22:25 2004 +0300
@@ -31,6 +31,9 @@
 /* Sync uidlist with what's actually on maildir. */
 struct maildir_uidlist_sync_ctx *
 maildir_uidlist_sync_init(struct maildir_uidlist *uidlist, int partial);
+/* Returns 1 = ok, -1 = error, 0 = new file and dovecot-uidlist is locked */
+int maildir_uidlist_sync_next_pre(struct maildir_uidlist_sync_ctx *ctx,
+				  const char *filename);
 int maildir_uidlist_sync_next(struct maildir_uidlist_sync_ctx *ctx,
 			      const char *filename,
 			      enum maildir_uidlist_rec_flag flags);
--- a/src/lib-storage/index/mbox/Makefile.am	Tue May 04 21:13:10 2004 +0300
+++ b/src/lib-storage/index/mbox/Makefile.am	Thu May 06 04:22:25 2004 +0300
@@ -11,18 +11,23 @@
 
 libstorage_mbox_a_SOURCES = \
 	istream-raw-mbox.c \
-	mbox-expunge.c \
+	mbox-file.c \
 	mbox-from.c \
 	mbox-list.c \
+	mbox-lock.c \
+	mbox-mail.c \
 	mbox-save.c \
 	mbox-sync-parse.c \
 	mbox-sync-rewrite.c \
 	mbox-sync-update.c \
-	mbox-sync.c
-	mbox-storage.c
+	mbox-sync.c \
+	mbox-storage.c \
+	mbox-transaction.c
 
 noinst_HEADERS = \
 	istream-raw-mbox.h \
+	mbox-file.h \
 	mbox-from.h \
+	mbox-lock.h \
 	mbox-storage.h \
 	mbox-sync-private.h
--- a/src/lib-storage/index/mbox/istream-raw-mbox.c	Tue May 04 21:13:10 2004 +0300
+++ b/src/lib-storage/index/mbox/istream-raw-mbox.c	Thu May 06 04:22:25 2004 +0300
@@ -10,7 +10,9 @@
 	struct _istream istream;
 
 	time_t received_time, next_received_time;
-	uoff_t from_offset, body_size;
+	char *sender, *next_sender;
+
+	uoff_t from_offset, hdr_offset, next_from_offset, body_size;
 	struct istream *input;
 };
 
@@ -42,12 +44,66 @@
 			      timeout_cb, context);
 }
 
+static int mbox_read_from_line(struct raw_mbox_istream *rstream)
+{
+	const unsigned char *buf, *p;
+	char *sender;
+	time_t received_time;
+	size_t pos, line_pos;
+	int skip;
+
+	buf = i_stream_get_data(rstream->input, &pos);
+	i_assert(pos > 0);
+
+	/* from_offset points to "\nFrom ", so unless we're at the beginning
+	   of the file, skip the initial \n */
+	skip = rstream->from_offset != 0;
+
+	while ((p = memchr(buf+skip, '\n', pos-skip)) == NULL) {
+		if (i_stream_read(rstream->input) < 0) {
+			/* EOF - shouldn't happen */
+			return -1;
+		}
+		buf = i_stream_get_data(rstream->input, &pos);
+	}
+	line_pos = (size_t)(p - buf);
+
+	if (rstream->from_offset != 0) {
+		buf++;
+		pos--;
+	}
+
+	/* beginning of mbox */
+	if (memcmp(buf, "From ", 5) != 0 ||
+	    mbox_from_parse(buf+5, pos-5, &received_time, &sender) < 0) {
+		/* broken From - should happen only at beginning of
+		   file if this isn't a mbox.. */
+		return -1;
+	}
+
+	if (rstream->istream.istream.v_offset == rstream->from_offset) {
+		rstream->received_time = received_time;
+		i_free(rstream->sender);
+		rstream->sender = sender;
+	} else {
+		rstream->next_received_time = received_time;
+		i_free(rstream->next_sender);
+		rstream->next_sender = sender;
+	}
+
+	/* we'll skip over From-line */
+	rstream->istream.istream.v_offset += line_pos+1;
+	rstream->hdr_offset = rstream->istream.istream.v_offset;
+	return 0;
+}
+
 static ssize_t _read(struct _istream *stream)
 {
 	static const char *mbox_from = "\nFrom ";
 	struct raw_mbox_istream *rstream = (struct raw_mbox_istream *)stream;
-	const unsigned char *buf, *p;
+	const unsigned char *buf;
 	const char *fromp;
+	char *sender;
 	time_t received_time;
 	size_t i, pos;
 	ssize_t ret;
@@ -71,58 +127,24 @@
 	}
 
 	if (stream->istream.v_offset == rstream->from_offset) {
-		/* read the full From-line */
-		int skip = rstream->from_offset != 0;
-		size_t line_pos;
-
-		while ((p = memchr(buf+skip, '\n', pos-skip)) == NULL) {
-			if (i_stream_read(rstream->input) < 0) {
-				/* EOF - shouldn't happen */
-				stream->pos = 0;
-				stream->istream.eof = TRUE;
-				return -1;
-			}
-			buf = i_stream_get_data(rstream->input, &pos);
-		}
-		line_pos = (size_t)(p - buf);
-
-		if (rstream->from_offset != 0) {
-			buf++;
-			pos--;
-		}
-
-		/* beginning of mbox */
-		if (memcmp(buf, "From ", 5) != 0)
-			received_time = (time_t)-1;
-		else
-			received_time = mbox_from_parse_date(buf+5, pos-5);
-
-		if (received_time == (time_t)-1) {
-			/* broken From - should happen only at beginning of
-			   file if this isn't a mbox.. */
+		if (mbox_read_from_line(rstream) < 0) {
 			stream->pos = 0;
 			stream->istream.eof = TRUE;
 			return -1;
 		}
-
-		if (rstream->from_offset == 0)
-			rstream->received_time = received_time;
-		else
-			rstream->next_received_time = received_time;
-
-		/* we'll skip over From-line and try again */
-		stream->istream.v_offset += line_pos+1;
 		return _read(stream);
 	}
 
 	if (pos >= 31) {
-		if (memcmp(buf, "\nFrom ", 6) == 0) {
-			received_time = mbox_from_parse_date(buf+6, pos-6);
-			if (received_time != (time_t)-1) {
-				rstream->next_received_time = received_time;
-				i_assert(stream->pos == 0);
-				return -1;
-			}
+		if (memcmp(buf, "\nFrom ", 6) == 0 &&
+		    mbox_from_parse(buf+6, pos-6,
+				    &received_time, &sender) == 0) {
+			rstream->next_received_time = received_time;
+
+			i_free(rstream->next_sender);
+			rstream->next_sender = sender;
+			i_assert(stream->pos == 0);
+			return -1;
 		}
 	} else if (ret == -1) {
 		/* last few bytes, can't contain From-line */
@@ -194,6 +216,7 @@
 	const unsigned char *data;
 	size_t size;
 	time_t received_time;
+	char *sender;
 
 	/* minimal: "From x Thu Nov 29 22:33:52 2001" = 31 chars */
 	if (i_stream_read_data(rstream->input, &data, &size, 30) == -1)
@@ -212,11 +235,12 @@
 			break;
 	}
 
-	received_time = mbox_from_parse_date(data+6, size-6);
-	if (received_time == (time_t)-1)
+	if (mbox_from_parse(data+6, size-6, &received_time, &sender) < 0)
 		return FALSE;
 
 	rstream->next_received_time = received_time;
+	i_free(rstream->next_sender);
+	rstream->next_sender = sender;
 	return TRUE;
 }
 
@@ -250,6 +274,24 @@
 	return rstream->body_size;
 }
 
+time_t istream_raw_mbox_get_received_time(struct istream *stream)
+{
+	struct raw_mbox_istream *rstream =
+		(struct raw_mbox_istream *)stream->real_stream;
+
+	(void)_read(&rstream->istream);
+	return rstream->received_time;
+}
+
+const char *istream_raw_mbox_get_sender(struct istream *stream)
+{
+	struct raw_mbox_istream *rstream =
+		(struct raw_mbox_istream *)stream->real_stream;
+
+	(void)_read(&rstream->istream);
+	return rstream->sender == NULL ? "" : rstream->sender;
+}
+
 void istream_raw_mbox_next(struct istream *stream, uoff_t body_size)
 {
 	struct raw_mbox_istream *rstream =
@@ -261,10 +303,48 @@
 	rstream->received_time = rstream->next_received_time;
 	rstream->next_received_time = (time_t)-1;
 
+	i_free(rstream->sender);
+	rstream->sender = rstream->next_sender;
+	rstream->next_sender = NULL;
+
 	rstream->from_offset = stream->v_offset + body_size;
+	rstream->hdr_offset = rstream->from_offset;
+
+	/* don't clear stream->eof if we don't have to */
+	if (stream->v_offset != rstream->from_offset)
+		i_stream_seek(stream, rstream->from_offset);
 	i_stream_seek(rstream->input, rstream->from_offset);
 }
 
+void istream_raw_mbox_seek(struct istream *stream, uoff_t offset)
+{
+	struct raw_mbox_istream *rstream =
+		(struct raw_mbox_istream *)stream->real_stream;
+
+	if (offset == rstream->next_from_offset) {
+		istream_raw_mbox_next(stream, (uoff_t)-1);
+		return;
+	}
+
+	if (offset == rstream->from_offset) {
+		/* back to beginning of current message */
+		offset = rstream->hdr_offset;
+	} else {
+		rstream->body_size = (uoff_t)-1;
+		rstream->received_time = (time_t)-1;
+		rstream->next_received_time = (time_t)-1;
+
+		i_free(rstream->sender);
+		rstream->sender = NULL;
+		i_free(rstream->next_sender);
+		rstream->next_sender = NULL;
+	}
+
+	rstream->from_offset = rstream->hdr_offset = offset;
+	i_stream_seek(stream, offset);
+	i_stream_seek(rstream->input, offset);
+}
+
 void istream_raw_mbox_flush(struct istream *stream)
 {
 	struct raw_mbox_istream *rstream =
--- a/src/lib-storage/index/mbox/istream-raw-mbox.h	Tue May 04 21:13:10 2004 +0300
+++ b/src/lib-storage/index/mbox/istream-raw-mbox.h	Thu May 06 04:22:25 2004 +0300
@@ -10,10 +10,21 @@
    to avoid actually reading through the whole message. */
 uoff_t istream_raw_mbox_get_size(struct istream *stream, uoff_t body_size);
 
+/* Return received time of current message, or (time_t)-1 if the timestamp is
+   broken. */
+time_t istream_raw_mbox_get_received_time(struct istream *stream);
+
+/* Return sender of current message. */
+const char *istream_raw_mbox_get_sender(struct istream *stream);
+
 /* Jump to next message. If body_size isn't (uoff_t)-1, we'll use it as
    potentially valid body size. */
 void istream_raw_mbox_next(struct istream *stream, uoff_t body_size);
 
+/* Seek to message at given offset. offset must point to beginning of
+   "\nFrom ", or 0 for beginning of file. */
+void istream_raw_mbox_seek(struct istream *stream, uoff_t offset);
+
 /* Flush all buffering. Call if you modify the mbox. */
 void istream_raw_mbox_flush(struct istream *stream);
 
--- a/src/lib-storage/index/mbox/mbox-expunge.c	Tue May 04 21:13:10 2004 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,214 +0,0 @@
-/* Copyright (C) 2002-2003 Timo Sirainen */
-
-#if 0
-#include "lib.h"
-#include "istream.h"
-#include "ostream.h"
-#include "mbox-index.h"
-#include "mbox-storage.h"
-#include "mbox-lock.h"
-#include "index-expunge.h"
-
-#include <fcntl.h>
-#include <unistd.h>
-
-struct mbox_expunge_context {
-	struct mail_expunge_context *ctx;
-
-        struct index_mailbox *ibox;
-	struct istream *input;
-	struct ostream *output;
-	int failed, expunges;
-
-	uoff_t from_offset, move_offset;
-};
-
-struct mail_expunge_context *
-mbox_storage_expunge_init(struct mailbox *box,
-			  enum mail_fetch_field wanted_fields, int expunge_all)
-{
-	struct index_mailbox *ibox = (struct index_mailbox *) box;
-	struct mbox_expunge_context *ctx;
-	struct mail_expunge_context *mctx;
-	struct istream *input;
-
-	mctx = index_storage_expunge_init(box, wanted_fields, expunge_all);
-	if (mctx == NULL)
-		return NULL;
-
-	/* mbox must be already opened, synced and locked at this point.
-	   we just want the istream. */
-	input = mbox_get_stream(ibox->index, MAIL_LOCK_EXCLUSIVE);
-	if (input == NULL)
-		return NULL;
-
-	i_assert(ibox->index->mbox_sync_counter ==
-		 ibox->index->mbox_lock_counter);
-
-	ctx = i_new(struct mbox_expunge_context, 1);
-	ctx->ctx = mctx;
-	ctx->ibox = ibox;
-	ctx->input = input;
-	ctx->output = o_stream_create_file(ibox->index->mbox_fd, default_pool,
-					   4096, FALSE);
-	ctx->from_offset = (uoff_t)-1;
-	ctx->move_offset = (uoff_t)-1;
-	o_stream_set_blocking(ctx->output, 60000, NULL, NULL);
-	return (struct mail_expunge_context *) ctx;
-}
-
-static int mbox_move_data(struct mbox_expunge_context *ctx)
-{
-	struct istream *input;
-	const unsigned char *data;
-	size_t size;
-	int failed;
-
-	i_stream_seek(ctx->input, ctx->move_offset);
-
-	if (ctx->output->offset == 0) {
-		/* we're writing to beginning of mbox, so we
-		   don't want the [\r]\n there */
-		(void)i_stream_read_data(ctx->input, &data, &size, 1);
-		if (size > 0 && data[0] == '\n')
-			i_stream_skip(ctx->input, 1);
-		else if (size > 1 && data[0] == '\r' &&
-			 data[1] == '\n')
-			i_stream_skip(ctx->input, 2);
-	}
-
-	if (ctx->from_offset == 0)
-		failed = o_stream_send_istream(ctx->output, ctx->input) < 0;
-	else {
-		input = i_stream_create_limit(default_pool, ctx->input,
-					      0, ctx->from_offset);
-		failed = o_stream_send_istream(ctx->output, ctx->input) < 0;
-		i_stream_unref(input);
-	}
-
-	return !failed;
-}
-
-int mbox_storage_expunge_deinit(struct mail_expunge_context *_ctx)
-{
-	struct mbox_expunge_context *ctx = (struct mbox_expunge_context *) _ctx;
-	int failed = ctx->failed;
-
-	if (ctx->expunges) {
-		if (!failed && ctx->move_offset != (uoff_t)-1) {
-			ctx->from_offset = 0;
-			if (!mbox_move_data(ctx))
-				failed = TRUE;
-		} else if (failed && ctx->output->offset > 0) {
-			/* we moved some of the data. move the rest as well
-			   so there won't be invalid holes in mbox file */
-			(void)o_stream_send_istream(ctx->output, ctx->input);
-		}
-
-		if (ftruncate(ctx->ibox->index->mbox_fd,
-			      (off_t)ctx->output->offset) < 0) {
-			mail_storage_set_error(ctx->ibox->box.storage,
-				"ftruncate() failed for mbox file %s: %m",
-				ctx->ibox->index->mailbox_path);
-			failed = TRUE;
-		}
-	}
-
-	if (!index_storage_expunge_deinit(ctx->ctx))
-		failed = TRUE;
-
-	o_stream_unref(ctx->output);
-	i_free(ctx);
-	return !failed;
-}
-
-static int get_from_offset(struct mbox_expunge_context *ctx,
-			   struct mail_index_record *rec, uoff_t *offset_r)
-{
-	struct message_size hdr_size;
-	uoff_t offset, body_size;
-
-	if (!mbox_mail_get_location(ctx->ibox->index, rec, &offset, &body_size))
-		return FALSE;
-
-	i_stream_seek(ctx->input, offset);
-	message_get_header_size(ctx->input, &hdr_size, NULL);
-
-	*offset_r = offset + hdr_size.physical_size + body_size;
-	return TRUE;
-}
-
-struct mail *mbox_storage_expunge_fetch_next(struct mail_expunge_context *_ctx)
-{
-	struct mbox_expunge_context *ctx =
-		(struct mbox_expunge_context *) _ctx;
-	struct mail_expunge_context *mctx = ctx->ctx;
-	struct mail_index *index = ctx->ibox->index;
-
-	if (mctx->rec == NULL)
-		return NULL;
-
-	if (mctx->fetch_next) {
-                mctx->fetch_next = FALSE;
-		do {
-			if (!get_from_offset(ctx, mctx->rec,
-					     &ctx->from_offset)) {
-				ctx->failed = TRUE;
-				return NULL;
-			}
-
-			mctx->seq++;
-			mctx->rec = index->next(index, mctx->rec);
-			if (mctx->rec == NULL)
-				return NULL;
-		} while ((mctx->rec->msg_flags & MAIL_DELETED) == 0 &&
-			 !mctx->expunge_all);
-	}
-
-	return index_storage_expunge_fetch_next(ctx->ctx);
-}
-
-static int get_prev_from_offset(struct mbox_expunge_context *ctx,
-				unsigned int seq)
-{
-	struct mail_index_record *rec;
-
-	if (seq == 1)
-		ctx->from_offset = 0;
-	else {
-		rec = ctx->ibox->index->lookup(ctx->ibox->index, seq-1);
-
-		if (!get_from_offset(ctx, rec, &ctx->from_offset))
-			return FALSE;
-	}
-
-	return TRUE;
-}
-
-int mbox_storage_expunge(struct mail *mail, struct mail_expunge_context *_ctx,
-			 unsigned int *seq_r, int notify)
-{
-	struct mbox_expunge_context *ctx = (struct mbox_expunge_context *) _ctx;
-	struct index_mail *imail = (struct index_mail *) mail;
-
-	if (ctx->from_offset == (uoff_t)-1) {
-		if (!get_prev_from_offset(ctx, imail->data.idx_seq))
-			return FALSE;
-	}
-
-	if (!ctx->expunges) {
-		/* first expunged message */
-		if (o_stream_seek(ctx->output, ctx->from_offset) < 0)
-			return FALSE;
-		ctx->expunges = TRUE;
-	} else if (ctx->move_offset != ctx->from_offset) {
-		if (!mbox_move_data(ctx))
-			return FALSE;
-	}
-
-	if (!get_from_offset(ctx, imail->data.rec, &ctx->move_offset))
-		return FALSE;
-
-	return index_storage_expunge(mail, ctx->ctx, seq_r, notify);
-}
-#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/index/mbox/mbox-file.c	Thu May 06 04:22:25 2004 +0300
@@ -0,0 +1,85 @@
+/* Copyright (C) 2002-2003 Timo Sirainen */
+
+#include "lib.h"
+#include "istream.h"
+#include "mbox-storage.h"
+#include "mbox-file.h"
+#include "istream-raw-mbox.h"
+
+#include <sys/stat.h>
+
+int mbox_file_open(struct index_mailbox *ibox)
+{
+	struct stat st;
+	int fd;
+
+	i_assert(ibox->mbox_fd == -1);
+
+	fd = open(ibox->path, ibox->readonly ? O_RDONLY : O_RDWR);
+	if (fd == -1) {
+		mbox_set_syscall_error(ibox, "open()");
+		return -1;
+	}
+
+	if (fstat(fd, &st) < 0) {
+		mbox_set_syscall_error(ibox, "fstat()");
+		(void)close(fd);
+		return -1;
+	}
+
+	ibox->mbox_fd = fd;
+	ibox->mbox_dev = st.st_dev;
+	ibox->mbox_ino = st.st_ino;
+	return 0;
+}
+
+void mbox_file_close(struct index_mailbox *ibox)
+{
+	mbox_file_close_stream(ibox);
+
+	if (ibox->mbox_fd != -1) {
+		if (close(ibox->mbox_fd) < 0)
+			i_error("close(mbox) failed: %m");
+		ibox->mbox_fd = -1;
+	}
+}
+
+int mbox_file_open_stream(struct index_mailbox *ibox)
+{
+	if (ibox->mbox_stream != NULL)
+		return 0;
+
+	i_assert(ibox->mbox_file_stream == NULL);
+
+	if (ibox->mbox_fd == -1) {
+		if (mbox_file_open(ibox) < 0)
+			return -1;
+	}
+
+	if (ibox->mail_read_mmaped) {
+		ibox->mbox_file_stream =
+			i_stream_create_mmap(ibox->mbox_fd, default_pool,
+					     MAIL_MMAP_BLOCK_SIZE,
+					     0, 0, FALSE);
+	} else {
+		ibox->mbox_file_stream =
+			i_stream_create_file(ibox->mbox_fd, default_pool,
+					     MAIL_READ_BLOCK_SIZE, FALSE);
+	}
+
+	ibox->mbox_stream =
+		i_stream_create_raw_mbox(default_pool, ibox->mbox_file_stream);
+	return 0;
+}
+
+void mbox_file_close_stream(struct index_mailbox *ibox)
+{
+	if (ibox->mbox_stream != NULL) {
+		i_stream_close(ibox->mbox_file_stream);
+		i_stream_unref(ibox->mbox_file_stream);
+		ibox->mbox_file_stream = NULL;
+
+		i_stream_unref(ibox->mbox_stream);
+		ibox->mbox_stream = NULL;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/index/mbox/mbox-file.h	Thu May 06 04:22:25 2004 +0300
@@ -0,0 +1,10 @@
+#ifndef __MBOX_FILE_H
+#define __MBOX_FILE_H
+
+int mbox_file_open(struct index_mailbox *ibox);
+void mbox_file_close(struct index_mailbox *ibox);
+
+int mbox_file_open_stream(struct index_mailbox *ibox);
+void mbox_file_close_stream(struct index_mailbox *ibox);
+
+#endif
--- a/src/lib-storage/index/mbox/mbox-from.c	Tue May 04 21:13:10 2004 +0300
+++ b/src/lib-storage/index/mbox/mbox-from.c	Thu May 06 04:22:25 2004 +0300
@@ -17,22 +17,28 @@
 	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
 };
 
-time_t mbox_from_parse_date(const unsigned char *msg, size_t size)
+int mbox_from_parse(const unsigned char *msg, size_t size,
+		    time_t *time_r, char **sender_r)
 {
-	const unsigned char *msg_end;
+	const unsigned char *msg_start, *sender_end, *msg_end;
 	struct tm tm;
 	int i, timezone;
 	time_t t;
 
+	*time_r = (time_t)-1;
+	*sender_r = NULL;
+
 	/* <sender> <date> <moreinfo> */
+	msg_start = msg;
 	msg_end = msg + size;
 
-	/* skip sender */
+	/* get sender */
 	while (msg < msg_end && *msg != ' ') {
 		if (*msg == '\r' || *msg == '\n')
-			return (time_t)-1;
+			return -1;
 		msg++;
 	}
+	sender_end = msg;
 	while (msg < msg_end && *msg == ' ') msg++;
 
 	/* next 24 chars should be in the date in asctime() format, eg.
@@ -43,7 +49,7 @@
 	   "Thu Nov 29 22:33:52 EEST 2001"
 	*/
 	if (msg+24 > msg_end)
-		return (time_t)-1;
+		return -1;
 
 	memset(&tm, 0, sizeof(tm));
 
@@ -64,17 +70,17 @@
 	}
 
 	if (i == 12 || msg[3] != ' ')
-		return (time_t)-1;
+		return -1;
 	msg += 4;
 
 	/* day */
 	if (msg[0] == ' ') {
 		if (!i_isdigit(msg[1]) || msg[2] != ' ')
-			return (time_t)-1;
+			return -1;
 		tm.tm_mday = msg[1]-'0';
 	} else {
 		if (!i_isdigit(msg[0]) || !i_isdigit(msg[1]) || msg[2] != ' ')
-			return (time_t)-1;
+			return -1;
 		tm.tm_mday = (msg[0]-'0') * 10 + (msg[1]-'0');
 	}
 	if (tm.tm_mday == 0)
@@ -83,19 +89,19 @@
 
 	/* hour */
 	if (!i_isdigit(msg[0]) || !i_isdigit(msg[1]) || msg[2] != ':')
-		return (time_t)-1;
+		return -1;
 	tm.tm_hour = (msg[0]-'0') * 10 + (msg[1]-'0');
 	msg += 3;
 
 	/* minute */
 	if (!i_isdigit(msg[0]) || !i_isdigit(msg[1]) || msg[2] != ':')
-		return (time_t)-1;
+		return -1;
 	tm.tm_min = (msg[0]-'0') * 10 + (msg[1]-'0');
 	msg += 3;
 
 	/* second */
 	if (!i_isdigit(msg[0]) || !i_isdigit(msg[1]) || msg[2] != ' ')
-		return (time_t)-1;
+		return -1;
 	tm.tm_sec = (msg[0]-'0') * 10 + (msg[1]-'0');
 	msg += 3;
 
@@ -105,18 +111,18 @@
 		/* skip to next space */
 		while (msg < msg_end && *msg != ' ') {
 			if (*msg == '\r' || *msg == '\n')
-				return (time_t)-1;
+				return -1;
 			msg++;
 		}
 		if (msg+5 > msg_end)
-			return (time_t)-1;
+			return -1;
 		msg++;
 	}
 
 	/* year */
 	if (!i_isdigit(msg[0]) || !i_isdigit(msg[1]) ||
 	    !i_isdigit(msg[2]) || !i_isdigit(msg[3]))
-		return (time_t)-1;
+		return -1;
 
 	tm.tm_year = (msg[0]-'0') * 1000 + (msg[1]-'0') * 100 +
 		(msg[2]-'0') * 10 + (msg[3]-'0') - 1900;
@@ -132,14 +138,17 @@
 
 		t = utc_mktime(&tm);
 		if (t == (time_t)-1)
-			return (time_t)-1;
+			return -1;
 
 		t -= timezone * 60;
-		return t;
+		*time_r = t;
 	} else {
 		/* assume local timezone */
-		return mktime(&tm);
+		*time_r = mktime(&tm);
 	}
+
+	*sender_r = i_strdup_until(msg_start, sender_end);
+	return 0;
 }
 
 const char *mbox_from_create(const char *sender, time_t time)
--- a/src/lib-storage/index/mbox/mbox-from.h	Tue May 04 21:13:10 2004 +0300
+++ b/src/lib-storage/index/mbox/mbox-from.h	Thu May 06 04:22:25 2004 +0300
@@ -1,7 +1,8 @@
 #ifndef __MBOX_FROM_H
 #define __MBOX_FROM_H
 
-time_t mbox_from_parse_date(const unsigned char *msg, size_t size);
+int mbox_from_parse(const unsigned char *msg, size_t size,
+		    time_t *time_r, char **sender_r);
 const char *mbox_from_create(const char *sender, time_t time);
 
 #endif
--- a/src/lib-storage/index/mbox/mbox-list.c	Tue May 04 21:13:10 2004 +0300
+++ b/src/lib-storage/index/mbox/mbox-list.c	Thu May 06 04:22:25 2004 +0300
@@ -1,11 +1,9 @@
 /* Copyright (C) 2002-2003 Timo Sirainen */
 
-#if 0
 #include "lib.h"
 #include "unlink-directory.h"
 #include "imap-match.h"
 #include "subscription-file/subscription-file.h"
-#include "mbox-index.h"
 #include "mbox-storage.h"
 #include "home-expand.h"
 
@@ -27,8 +25,10 @@
 	char *real_path, *virtual_path;
 };
 
-struct mailbox_list_context {
-	struct mail_storage *storage;
+struct mbox_list_context {
+	struct mailbox_list_context mailbox_ctx;
+	struct index_storage *istorage;
+
 	enum mailbox_list_flags flags;
 
 	const char *prefix;
@@ -37,18 +37,18 @@
 
 	int failed;
 
-	struct mailbox_list *(*next)(struct mailbox_list_context *ctx);
+	struct mailbox_list *(*next)(struct mbox_list_context *ctx);
 
 	pool_t list_pool;
 	struct mailbox_list list;
         struct list_dir_context *dir;
 };
 
-static struct mailbox_list *mbox_list_subs(struct mailbox_list_context *ctx);
-static struct mailbox_list *mbox_list_inbox(struct mailbox_list_context *ctx);
-static struct mailbox_list *mbox_list_path(struct mailbox_list_context *ctx);
-static struct mailbox_list *mbox_list_next(struct mailbox_list_context *ctx);
-static struct mailbox_list *mbox_list_none(struct mailbox_list_context *ctx);
+static struct mailbox_list *mbox_list_subs(struct mbox_list_context *ctx);
+static struct mailbox_list *mbox_list_inbox(struct mbox_list_context *ctx);
+static struct mailbox_list *mbox_list_path(struct mbox_list_context *ctx);
+static struct mailbox_list *mbox_list_next(struct mbox_list_context *ctx);
+static struct mailbox_list *mbox_list_none(struct mbox_list_context *ctx);
 
 static const char *mask_get_dir(struct mail_storage *storage, const char *mask)
 {
@@ -72,7 +72,8 @@
 	return last_dir == NULL ? NULL : t_strdup_until(mask, last_dir);
 }
 
-static const char *mbox_get_path(struct mail_storage *storage, const char *name)
+static const char *
+mbox_get_path(struct index_storage *storage, const char *name)
 {
 	if (!full_filesystem_access || name == NULL ||
 	    (*name != '/' && *name != '~' && *name != '\0'))
@@ -109,10 +110,11 @@
 }
 
 struct mailbox_list_context *
-mbox_list_mailbox_init(struct mail_storage *storage, const char *mask,
+mbox_mailbox_list_init(struct mail_storage *storage, const char *mask,
 		       enum mailbox_list_flags flags)
 {
-	struct mailbox_list_context *ctx;
+	struct index_storage *istorage = (struct index_storage *)storage;
+	struct mbox_list_context *ctx;
 	const char *path, *virtual_path;
 	DIR *dirp;
 
@@ -120,13 +122,13 @@
 
 	if (storage->hierarchy_sep != '/' && strchr(mask, '/') != NULL) {
 		/* this will never match, return nothing */
-		ctx = i_new(struct mailbox_list_context, 1);
-		ctx->storage = storage;
+		ctx = i_new(struct mbox_list_context, 1);
+		ctx->mailbox_ctx.storage = storage;
                 ctx->next = mbox_list_none;
-		return ctx;
+		return &ctx->mailbox_ctx;
 	}
 
-	mask = mbox_fix_mailbox_name(storage, mask, FALSE);
+	mask = mbox_fix_mailbox_name(istorage, mask, FALSE);
 
 	/* check that we're not trying to do any "../../" lists */
 	if (!mbox_is_valid_mask(mask)) {
@@ -135,36 +137,39 @@
 	}
 
 	if ((flags & MAILBOX_LIST_SUBSCRIBED) != 0) {
-		ctx = i_new(struct mailbox_list_context, 1);
-		ctx->storage = storage;
+		ctx = i_new(struct mbox_list_context, 1);
+		ctx->mailbox_ctx.storage = storage;
+		ctx->istorage = istorage;
 		ctx->flags = flags;
 		ctx->next = mbox_list_subs;
-		ctx->subsfile_ctx = subsfile_list_init(storage);
+		ctx->subsfile_ctx =
+			subsfile_list_init(storage, SUBSCRIPTION_FILE_NAME);
 		if (ctx->subsfile_ctx == NULL) {
 			i_free(ctx);
 			return NULL;
 		}
 		ctx->glob = imap_match_init(default_pool, mask, TRUE, '/');
 		ctx->list_pool = pool_alloconly_create("mbox_list", 1024);
-		return ctx;
+		return &ctx->mailbox_ctx;
 	}
 
 	/* if we're matching only subdirectories, don't bother scanning the
 	   parent directories */
 	virtual_path = mask_get_dir(storage, mask);
 
-	path = mbox_get_path(storage, virtual_path);
+	path = mbox_get_path(istorage, virtual_path);
 	if (list_opendir(storage, path, TRUE, &dirp) < 0)
 		return NULL;
 	/* if user gave invalid directory, we just don't show any results. */
 
-	ctx = i_new(struct mailbox_list_context, 1);
-	ctx->storage = storage;
+	ctx = i_new(struct mbox_list_context, 1);
+	ctx->mailbox_ctx.storage = storage;
+	ctx->istorage = istorage;
 	ctx->flags = flags;
 	ctx->glob = imap_match_init(default_pool, mask, TRUE, '/');
 	ctx->list_pool = pool_alloconly_create("mbox_list", 1024);
 	ctx->prefix = storage->namespace == NULL ? "" :
-		mbox_fix_mailbox_name(storage, storage->namespace, FALSE);
+		mbox_fix_mailbox_name(istorage, storage->namespace, FALSE);
 
 	if (virtual_path == NULL && imap_match(ctx->glob, "INBOX") > 0)
 		ctx->next = mbox_list_inbox;
@@ -180,7 +185,7 @@
 		ctx->dir->virtual_path = virtual_path == NULL ? NULL :
 			i_strconcat(ctx->prefix, virtual_path, NULL);
 	}
-	return ctx;
+	return &ctx->mailbox_ctx;
 }
 
 static void list_dir_context_free(struct list_dir_context *dir)
@@ -191,13 +196,14 @@
 	i_free(dir);
 }
 
-int mbox_list_mailbox_deinit(struct mailbox_list_context *ctx)
+int mbox_mailbox_list_deinit(struct mailbox_list_context *_ctx)
 {
-	int failed = ctx->failed;
+	struct mbox_list_context *ctx = (struct mbox_list_context *)_ctx;
+	int ret = ctx->failed ? -1 : 0;
 
 	if (ctx->subsfile_ctx != NULL) {
-		if (!subsfile_list_deinit(ctx->subsfile_ctx))
-			failed = TRUE;
+		if (subsfile_list_deinit(ctx->subsfile_ctx) < 0)
+			ret = -1;
 	}
 
 	while (ctx->dir != NULL) {
@@ -213,15 +219,17 @@
 		imap_match_deinit(ctx->glob);
 	i_free(ctx);
 
-	return !failed;
+	return ret;
 }
 
-struct mailbox_list *mbox_list_mailbox_next(struct mailbox_list_context *ctx)
+struct mailbox_list *mbox_mailbox_list_next(struct mailbox_list_context *_ctx)
 {
+	struct mbox_list_context *ctx = (struct mbox_list_context *)_ctx;
+
 	return ctx->next(ctx);
 }
 
-static int list_file(struct mailbox_list_context *ctx, const char *fname)
+static int list_file(struct mbox_list_context *ctx, const char *fname)
 {
         struct list_dir_context *dir;
 	const char *list_path, *real_path, *path;
@@ -260,8 +268,8 @@
 	else {
 		if (ENOTFOUND(errno))
 			return 0;
-		mail_storage_set_critical(ctx->storage, "stat(%s) failed: %m",
-					  real_path);
+		mail_storage_set_critical(ctx->mailbox_ctx.storage,
+					  "stat(%s) failed: %m", real_path);
 		return -1;
 	}
 
@@ -279,7 +287,8 @@
 			ctx->list.name = NULL;
 
 		ret = match2 < 0 ? 0 :
-			list_opendir(ctx->storage, real_path, FALSE, &dirp);
+			list_opendir(ctx->mailbox_ctx.storage,
+				     real_path, FALSE, &dirp);
 		if (ret > 0) {
 			dir = i_new(struct list_dir_context, 1);
 			dir->dirp = dirp;
@@ -292,7 +301,7 @@
 			return -1;
 		return match > 0 || match2 > 0;
 	} else if (match > 0 &&
-		   strcmp(real_path, ctx->storage->inbox_file) != 0 &&
+		   strcmp(real_path, ctx->istorage->inbox_path) != 0 &&
 		   strcasecmp(list_path, "INBOX") != 0) {
 		/* don't match any INBOX here, it's added separately.
 		   we might also have ~/mail/inbox, ~/mail/Inbox etc.
@@ -306,7 +315,7 @@
 	return 0;
 }
 
-static struct mailbox_list *list_fix_name(struct mailbox_list_context *ctx)
+static struct mailbox_list *list_fix_name(struct mbox_list_context *ctx)
 {
 	char *p, *str, sep;
 
@@ -314,7 +323,7 @@
 		str = p_strdup(ctx->list_pool, ctx->list.name);
 		ctx->list.name = str;
 
-		sep = ctx->storage->hierarchy_sep;
+		sep = ctx->mailbox_ctx.storage->hierarchy_sep;
 		for (p = str; *p != '\0'; p++) {
 			if (*p == '/')
 				*p = sep;
@@ -324,7 +333,7 @@
 	return &ctx->list;
 }
 
-static struct mailbox_list *mbox_list_subs(struct mailbox_list_context *ctx)
+static struct mailbox_list *mbox_list_subs(struct mbox_list_context *ctx)
 {
 	struct stat st;
 	const char *name, *path, *p;
@@ -361,8 +370,8 @@
 		return &ctx->list;
 
 	t_push();
-	name = mbox_fix_mailbox_name(ctx->storage, ctx->list.name, TRUE);
-	path = mbox_get_path(ctx->storage, name);
+	name = mbox_fix_mailbox_name(ctx->istorage, ctx->list.name, TRUE);
+	path = mbox_get_path(ctx->istorage, name);
 	if (stat(path, &st) == 0) {
 		if (S_ISDIR(st.st_mode))
 			ctx->list.flags = MAILBOX_NOSELECT | MAILBOX_CHILDREN;
@@ -380,7 +389,7 @@
 	return &ctx->list;
 }
 
-static struct mailbox_list *mbox_list_inbox(struct mailbox_list_context *ctx)
+static struct mailbox_list *mbox_list_inbox(struct mbox_list_context *ctx)
 {
 	struct stat st;
 
@@ -393,7 +402,7 @@
 	ctx->list.flags = strncmp(ctx->prefix, "INBOX/", 6) == 0 ?
 		MAILBOX_CHILDREN : MAILBOX_NOINFERIORS;
 	if ((ctx->flags & MAILBOX_LIST_FAST_FLAGS) == 0) {
-		if (stat(ctx->storage->inbox_file, &st) < 0)
+		if (stat(ctx->istorage->inbox_path, &st) < 0)
 			ctx->list.flags |= MAILBOX_UNMARKED;
 		else
 			ctx->list.flags |= STAT_GET_MARKED(st);
@@ -403,7 +412,7 @@
 	return &ctx->list;
 }
 
-static struct mailbox_list *mbox_list_path(struct mailbox_list_context *ctx)
+static struct mailbox_list *mbox_list_path(struct mbox_list_context *ctx)
 {
 	ctx->next = mbox_list_next;
 
@@ -417,7 +426,7 @@
 		return ctx->next(ctx);
 }
 
-static struct mailbox_list *mbox_list_next(struct mailbox_list_context *ctx)
+static struct mailbox_list *mbox_list_next(struct mbox_list_context *ctx)
 {
 	struct list_dir_context *dir;
 	struct dirent *d;
@@ -450,8 +459,7 @@
 }
 
 static struct mailbox_list *
-mbox_list_none(struct mailbox_list_context *ctx __attr_unused__)
+mbox_list_none(struct mbox_list_context *ctx __attr_unused__)
 {
 	return NULL;
 }
-#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/index/mbox/mbox-lock.c	Thu May 06 04:22:25 2004 +0300
@@ -0,0 +1,318 @@
+/* Copyright (C) 2002 Timo Sirainen */
+
+#include "lib.h"
+#include "mbox-storage.h"
+#include "mbox-file.h"
+#include "mbox-lock.h"
+
+#include <time.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_FLOCK
+#  include <sys/file.h>
+#endif
+
+/* 0.1 .. 0.2msec */
+#define LOCK_RANDOM_USLEEP_TIME (100000 + (unsigned int)rand() % 100000)
+
+/* lock methods to use in wanted order */
+#define DEFAULT_LOCK_METHODS "dotlock fcntl"
+/* lock timeout */
+#define DEFAULT_LOCK_TIMEOUT 300
+/* assume stale dotlock if mbox file hasn't changed for n seconds */
+#define DEFAULT_DOTLOCK_CHANGE_TIMEOUT 30
+
+struct dotlock_context {
+        struct index_mailbox *ibox;
+        int lock_type;
+	int last_stale;
+};
+
+static int lock_settings_initialized = FALSE;
+static int use_dotlock, use_fcntl_lock, use_flock, fcntl_before_flock;
+static int use_read_dotlock, lock_timeout, dotlock_change_timeout;
+
+static int mbox_unlock_files(struct index_mailbox *ibox);
+
+static void mbox_init_lock_settings(void)
+{
+	const char *str;
+	const char *const *lock;
+
+        use_dotlock = use_fcntl_lock = use_flock = fcntl_before_flock = FALSE;
+
+	str = getenv("MBOX_LOCKS");
+	if (str == NULL) str = DEFAULT_LOCK_METHODS;
+	for (lock = t_strsplit(str, " "); *lock != NULL; lock++) {
+		if (strcasecmp(*lock, "dotlock") == 0)
+			use_dotlock = TRUE;
+		else if (strcasecmp(*lock, "fcntl") == 0) {
+			use_fcntl_lock = TRUE;
+			fcntl_before_flock = use_flock == FALSE;
+		} else if (strcasecmp(*lock, "flock") == 0)
+			use_flock = TRUE;
+		else
+			i_fatal("MBOX_LOCKS: Invalid value %s", *lock);
+	}
+
+	use_read_dotlock = getenv("MBOX_READ_DOTLOCK") != NULL;
+
+	str = getenv("MBOX_LOCK_TIMEOUT");
+	lock_timeout = str == NULL ? DEFAULT_LOCK_TIMEOUT : atoi(str);
+
+	str = getenv("MBOX_DOTLOCK_CHANGE_TIMEOUT");
+	dotlock_change_timeout = str == NULL ?
+		DEFAULT_DOTLOCK_CHANGE_TIMEOUT : atoi(str);
+
+        lock_settings_initialized = TRUE;
+}
+
+#ifdef HAVE_FLOCK
+static int mbox_lock_flock(struct index_mailbox *ibox, int lock_type,
+			   time_t max_wait_time)
+{
+	time_t now, last_notify;
+
+	if (lock_type == F_WRLCK)
+		lock_type = LOCK_EX;
+	else if (lock_type == F_RDLCK)
+		lock_type = LOCK_SH;
+	else
+		lock_type = LOCK_UN;
+
+        last_notify = 0;
+	while (flock(ibox->mbox_fd, lock_type | LOCK_NB) < 0) {
+		if (errno != EWOULDBLOCK) {
+			mbox_set_syscall_error(ibox, "flock()");
+			return -1;
+		}
+
+		if (max_wait_time == 0)
+			return 0;
+
+		now = time(NULL);
+		if (now >= max_wait_time)
+			return 0;
+
+		if (now != last_notify) {
+			index_storage_lock_notify(ibox,
+				MAILBOX_LOCK_NOTIFY_MAILBOX_ABORT,
+				max_wait_time - now);
+		}
+
+		usleep(LOCK_RANDOM_USLEEP_TIME);
+	}
+
+	return 1;
+}
+#endif
+
+static int mbox_lock_fcntl(struct index_mailbox *ibox, int lock_type,
+			   time_t max_wait_time)
+{
+	struct flock fl;
+	time_t now;
+	int wait_type;
+
+	fl.l_type = lock_type;
+	fl.l_whence = SEEK_SET;
+	fl.l_start = 0;
+	fl.l_len = 0;
+
+        wait_type = max_wait_time == 0 ? F_SETLK : F_SETLKW;
+	while (fcntl(ibox->mbox_fd, wait_type, &fl) < 0) {
+		if (errno != EINTR) {
+			if (errno != EAGAIN && errno != EACCES)
+				mbox_set_syscall_error(ibox, "fcntl()");
+			return -1;
+		}
+
+		now = time(NULL);
+		if (max_wait_time != 0 && now >= max_wait_time)
+			return 0;
+
+		index_storage_lock_notify(ibox,
+					  MAILBOX_LOCK_NOTIFY_MAILBOX_ABORT,
+					  max_wait_time - now);
+	}
+
+	return 1;
+}
+
+static int mbox_file_locks(struct index_mailbox *ibox, int lock_type,
+			   time_t max_wait_time)
+{
+	struct stat st;
+	int ret;
+
+	/* now we need to have the file itself locked. open it if needed. */
+	if (stat(ibox->path, &st) < 0) {
+		mbox_set_syscall_error(ibox, "stat()");
+		return -1;
+	}
+
+	if (st.st_dev != ibox->mbox_dev || st.st_ino != ibox->mbox_ino)
+		mbox_file_close(ibox);
+
+	if (ibox->mbox_fd == -1) {
+		if (mbox_file_open(ibox) < 0) {
+			(void)mbox_unlock_files(ibox);
+			return -1;
+		}
+	}
+
+	if (use_fcntl_lock && fcntl_before_flock) {
+		ret = mbox_lock_fcntl(ibox, lock_type, max_wait_time);
+		if (ret <= 0)
+			return ret;
+	}
+#ifdef HAVE_FLOCK
+	if (use_flock) {
+		ret = mbox_lock_flock(ibox, lock_type, max_wait_time);
+		if (ret <= 0)
+			return ret;
+	}
+#endif
+	if (use_fcntl_lock && !fcntl_before_flock) {
+		ret = mbox_lock_fcntl(ibox, lock_type, max_wait_time);
+		if (ret <= 0)
+			return ret;
+	}
+	return 1;
+}
+
+static int mbox_file_unlock(struct index_mailbox *ibox)
+{
+	int ret = 0;
+
+#ifdef HAVE_FLOCK
+	if (use_flock && mbox_lock_flock(ibox, F_UNLCK, 0) < 0)
+		ret = -1;
+#endif
+	if (use_fcntl_lock && mbox_lock_fcntl(ibox, F_UNLCK, 0) < 0)
+		ret = -1;
+
+	return ret;
+}
+
+static int dotlock_callback(unsigned int secs_left, int stale, void *context)
+{
+	struct dotlock_context *ctx = context;
+
+	if (stale && !ctx->last_stale) {
+		if (mbox_file_locks(ctx->ibox, ctx->lock_type, 0) <= 0) {
+			/* we couldn't get fcntl/flock - it's really locked */
+			ctx->last_stale = TRUE;
+			return FALSE;
+		}
+		(void)mbox_file_unlock(ctx->ibox);
+	}
+	ctx->last_stale = stale;
+
+	index_storage_lock_notify(ctx->ibox, stale ?
+				  MAILBOX_LOCK_NOTIFY_MAILBOX_OVERRIDE :
+				  MAILBOX_LOCK_NOTIFY_MAILBOX_ABORT,
+				  secs_left);
+	return TRUE;
+}
+
+int mbox_lock(struct index_mailbox *ibox, int lock_type,
+	      unsigned int *lock_id_r)
+{
+	time_t max_wait_time;
+	int ret;
+
+	/* allow only unlock -> shared/exclusive or exclusive -> shared */
+	i_assert(lock_type == F_RDLCK || lock_type == F_WRLCK);
+	i_assert(lock_type == F_RDLCK || ibox->mbox_lock_type != F_RDLCK);
+
+	if (ibox->mbox_lock_type == lock_type) {
+		ibox->mbox_locks++;
+		return 1;
+	}
+
+        index_storage_lock_notify_reset(ibox);
+
+	if (!lock_settings_initialized)
+                mbox_init_lock_settings();
+
+	max_wait_time = time(NULL) + lock_timeout;
+
+	/* make .lock file first to protect overwriting the file */
+	if (use_dotlock && ibox->mbox_dotlock.ino == 0) {
+		struct dotlock_context ctx;
+
+		ctx.ibox = ibox;
+		ctx.lock_type = lock_type;
+		ctx.last_stale = -1;
+
+		ret = file_lock_dotlock(ibox->path, NULL,
+					lock_type == F_RDLCK &&
+					!use_read_dotlock, lock_timeout,
+					dotlock_change_timeout, 0,
+					dotlock_callback, &ctx,
+					&ibox->mbox_dotlock);
+
+		if (ret < 0) {
+			mbox_set_syscall_error(ibox, "file_lock_dotlock()");
+			return -1;
+		}
+		if (ret == 0) {
+			mail_storage_set_error(ibox->box.storage,
+				"Timeout while waiting for lock");
+			return 0;
+		}
+	}
+
+	ibox->mbox_lock_type = lock_type;
+	ret = mbox_file_locks(ibox, ibox->mbox_lock_type, max_wait_time);
+	if (ret <= 0) {
+		(void)mbox_unlock_files(ibox);
+		if (ret == 0) {
+			mail_storage_set_error(ibox->box.storage,
+				"Timeout while waiting for lock");
+		}
+		return ret;
+	}
+
+	*lock_id_r = ++ibox->mbox_lock_id;
+	return 1;
+}
+
+static int mbox_unlock_files(struct index_mailbox *ibox)
+{
+	int ret = 0;
+
+	if (ibox->mbox_fd != -1) {
+		if (mbox_file_unlock(ibox) < 0)
+			ret = -1;
+	}
+
+	if (ibox->mbox_dotlock.ino != 0) {
+		if (file_unlock_dotlock(ibox->path, &ibox->mbox_dotlock) <= 0) {
+			mbox_set_syscall_error(ibox, "file_unlock_dotlock()");
+			ret = -1;
+		}
+                ibox->mbox_dotlock.ino = 0;
+	}
+
+	/* make sure we don't keep mmap() between locks */
+	mbox_file_close_stream(ibox);
+
+	ibox->mbox_lock_id++;
+	ibox->mbox_lock_type = F_UNLCK;
+	return ret;
+}
+
+int mbox_unlock(struct index_mailbox *ibox, unsigned int lock_id)
+{
+	i_assert(ibox->mbox_lock_id == lock_id);
+
+	if (--ibox->mbox_locks > 0)
+		return 0;
+
+	return mbox_unlock_files(ibox);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/index/mbox/mbox-lock.h	Thu May 06 04:22:25 2004 +0300
@@ -0,0 +1,10 @@
+#ifndef __MBOX_LOCK_H
+#define __MBOX_LOCK_H
+
+/* NOTE: if mbox file is not open, it's opened. if it is open but file has
+   been overwritten (ie. inode has changed), it's reopened. */
+int mbox_lock(struct index_mailbox *ibox, int lock_type,
+	      unsigned int *lock_id_r);
+int mbox_unlock(struct index_mailbox *ibox, unsigned int lock_id);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/index/mbox/mbox-mail.c	Thu May 06 04:22:25 2004 +0300
@@ -0,0 +1,111 @@
+/* Copyright (C) 2003 Timo Sirainen */
+
+#include "lib.h"
+#include "istream.h"
+#include "index-mail.h"
+#include "mbox-storage.h"
+#include "mbox-file.h"
+#include "istream-raw-mbox.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+static int mbox_mail_seek(struct index_mail *mail)
+{
+	i_assert(mail->mail.seq <= mail->ibox->mbox_data_count);
+
+	// FIXME: lock the file
+
+	if (mbox_file_open_stream(mail->ibox) < 0)
+		return -1;
+
+	i_stream_seek(mail->ibox->mbox_stream,
+		      mail->ibox->mbox_data[mail->mail.seq-1] >> 1);
+	return 0;
+}
+
+static const struct mail_full_flags *mbox_mail_get_flags(struct mail *_mail)
+{
+	struct index_mail *mail = (struct index_mail *)_mail;
+	struct index_mail_data *data = &mail->data;
+
+	i_assert(_mail->seq <= mail->ibox->mbox_data_count);
+
+	(void)index_mail_get_flags(_mail);
+	if ((mail->ibox->mbox_data[_mail->seq-1] & 1) != 0)
+		data->flags.flags |= MAIL_RECENT;
+
+	return &data->flags;
+}
+
+static time_t mbox_mail_get_received_date(struct mail *_mail)
+{
+	struct index_mail *mail = (struct index_mail *)_mail;
+	struct index_mail_data *data = &mail->data;
+
+	(void)index_mail_get_received_date(_mail);
+	if (data->received_date != (time_t)-1)
+		return data->received_date;
+
+	if (mbox_mail_seek(mail) < 0)
+		return (time_t)-1;
+	data->received_date =
+		istream_raw_mbox_get_received_time(mail->ibox->mbox_stream);
+
+	if (data->received_date != (time_t)-1) {
+		index_mail_cache_add(mail, MAIL_CACHE_RECEIVED_DATE,
+				     &data->received_date,
+				     sizeof(data->received_date));
+	}
+	return data->received_date;
+}
+
+static const char *
+mbox_mail_get_special(struct mail *_mail, enum mail_fetch_field field)
+{
+	struct index_mail *mail = (struct index_mail *)_mail;
+
+	if (field == MAIL_FETCH_FROM_ENVELOPE) {
+		if (mbox_mail_seek(mail) < 0)
+			return NULL;
+
+		return istream_raw_mbox_get_sender(mail->ibox->mbox_stream);
+
+	}
+
+	return index_mail_get_special(_mail, field);
+}
+
+static struct istream *mbox_mail_get_stream(struct mail *_mail,
+					    struct message_size *hdr_size,
+					    struct message_size *body_size)
+{
+	struct index_mail *mail = (struct index_mail *)_mail;
+	struct index_mail_data *data = &mail->data;
+
+	if (data->stream == NULL) {
+		if (mbox_mail_seek(mail) < 0)
+			return NULL;
+
+		data->stream = mail->ibox->mbox_stream;
+	}
+
+	return index_mail_init_stream(mail, hdr_size, body_size);
+}
+
+struct mail mbox_mail = {
+	0, 0, 0, 0, 0, 0,
+
+	mbox_mail_get_flags,
+	index_mail_get_parts,
+	mbox_mail_get_received_date,
+	index_mail_get_date,
+	index_mail_get_size,
+	index_mail_get_header,
+	index_mail_get_headers,
+	mbox_mail_get_stream,
+	mbox_mail_get_special,
+	index_mail_update_flags,
+	index_mail_expunge
+};
--- a/src/lib-storage/index/mbox/mbox-save.c	Tue May 04 21:13:10 2004 +0300
+++ b/src/lib-storage/index/mbox/mbox-save.c	Thu May 06 04:22:25 2004 +0300
@@ -1,14 +1,14 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
-#if 0
 #include "lib.h"
 #include "hostpid.h"
 #include "ostream.h"
 #include "str.h"
 #include "write-full.h"
-#include "mbox-index.h"
+#include "mbox-storage.h"
+#include "mbox-file.h"
+#include "mbox-from.h"
 #include "mbox-lock.h"
-#include "mbox-storage.h"
 #include "mail-save.h"
 
 #include <stdlib.h>
@@ -17,9 +17,9 @@
 #include <sys/stat.h>
 #include <netdb.h>
 
-struct mail_save_context {
+struct mbox_save_context {
 	struct index_mailbox *ibox;
-	int transaction;
+	uoff_t append_offset;
 
 	struct ostream *output;
 	uoff_t sync_offset, content_length_offset, eoh_offset;
@@ -29,45 +29,37 @@
 
 static char my_hostdomain[256] = "";
 
-static int syscall_error(struct mail_save_context *ctx, const char *function)
-{
-	mail_storage_set_critical(ctx->ibox->box.storage,
-				  "%s failed for mbox file %s: %m",
-				  function, ctx->ibox->index->mailbox_path);
-	return FALSE;
-}
-
-static int write_error(struct mail_save_context *ctx)
+static int write_error(struct mbox_save_context *ctx)
 {
 	if (ENOSPACE(errno)) {
 		mail_storage_set_error(ctx->ibox->box.storage,
 				       "Not enough disk space");
 	} else {
-                syscall_error(ctx, "write()");
+                mbox_set_syscall_error(ctx->ibox, "write()");
 	}
 
-	return FALSE;
+	return -1;
 }
 
-static int mbox_seek_to_end(struct mail_save_context *ctx, uoff_t *offset)
+static int mbox_seek_to_end(struct mbox_save_context *ctx, uoff_t *offset)
 {
 	struct stat st;
 	char ch;
 	int fd;
 
-	fd = ctx->ibox->index->mbox_fd;
+	fd = ctx->ibox->mbox_fd;
 	if (fstat(fd, &st) < 0)
-                return syscall_error(ctx, "fstat()");
+                return mbox_set_syscall_error(ctx->ibox, "fstat()");
 
 	*offset = (uoff_t)st.st_size;
 	if (st.st_size == 0)
-		return TRUE;
+		return 0;
 
 	if (lseek(fd, st.st_size-1, SEEK_SET) < 0)
-                return syscall_error(ctx, "lseek()");
+                return mbox_set_syscall_error(ctx->ibox, "lseek()");
 
 	if (read(fd, &ch, 1) != 1)
-		return syscall_error(ctx, "read()");
+		return mbox_set_syscall_error(ctx->ibox, "read()");
 
 	if (ch != '\n') {
 		if (write_full(fd, "\n", 1) < 0)
@@ -75,10 +67,10 @@
 		*offset += 1;
 	}
 
-	return TRUE;
+	return 0;
 }
 
-static int mbox_append_lf(struct mail_save_context *ctx)
+static int mbox_append_lf(struct mbox_save_context *ctx)
 {
 	if (o_stream_send(ctx->output, "\n", 1) < 0)
 		return write_error(ctx);
@@ -86,9 +78,10 @@
 	return TRUE;
 }
 
-static int write_from_line(struct mail_save_context *ctx, time_t received_date)
+static int write_from_line(struct mbox_save_context *ctx, time_t received_date,
+			   const char *from_envelope)
 {
-	const char *sender, *line, *name;
+	const char *line, *name;
 
 	if (*my_hostdomain == '\0') {
 		struct hostent *hent;
@@ -104,16 +97,18 @@
 		strocpy(my_hostdomain, name, sizeof(my_hostdomain));
 	}
 
-	sender = t_strconcat(ctx->ibox->box.storage->user, "@",
-			     my_hostdomain, NULL);
+	if (from_envelope == NULL) {
+		from_envelope = t_strconcat(ctx->ibox->storage->user, "@",
+					    my_hostdomain, NULL);
+	}
 
 	/* save in local timezone, no matter what it was given with */
-	line = mbox_from_create(sender, received_date);
+	line = mbox_from_create(from_envelope, received_date);
 
 	if (o_stream_send_str(ctx->output, line) < 0)
 		return write_error(ctx);
 
-	return TRUE;
+	return 0;
 }
 
 static const char *get_system_flags(enum mail_flags flags)
@@ -147,25 +142,17 @@
 static const char *get_keywords(const struct mail_full_flags *flags)
 {
 	string_t *str;
-	unsigned int field;
 	unsigned int i;
 
-	if ((flags->flags & MAIL_KEYWORDS_MASK) == 0)
+	if (flags->keywords_count == 0)
 		return "";
 
 	str = t_str_new(256);
-	field = 1 << MAIL_KEYWORD_1_BIT;
 	for (i = 0; i < flags->keywords_count; i++) {
-		const char *keyword = flags->keywords[i];
-
-		if ((flags->flags & field) && keyword != NULL) {
+		if (str_len(str) > 0)
 			str_append_c(str, ' ');
-			str_append(str, keyword);
-		}
-
-		field <<= 1;
+		str_append(str, flags->keywords[i]);
 	}
-
 	return str_c(str);
 }
 
@@ -173,7 +160,7 @@
 				void *context)
 {
 	static const char *content_length = "Content-Length: ";
-	struct mail_save_context *ctx = context;
+	struct mbox_save_context *ctx = context;
 	const char *str;
 	char *buf;
 	size_t space;
@@ -238,7 +225,7 @@
 	return 1;
 }
 
-static int mbox_fix_header(struct mail_save_context *ctx)
+static int mbox_fix_header(struct mbox_save_context *ctx)
 {
 	uoff_t old_offset;
 	const char *str;
@@ -246,7 +233,7 @@
 
 	old_offset = ctx->output->offset;
 	if (o_stream_seek(ctx->output, ctx->content_length_offset) < 0)
-                return syscall_error(ctx, "o_stream_seek()");
+                return mbox_set_syscall_error(ctx->ibox, "o_stream_seek()");
 
 	/* write value for Content-Length */
 	str = dec2str(old_offset - (ctx->eoh_offset + 1 + crlf));
@@ -264,112 +251,102 @@
 		return write_error(ctx);
 
 	if (o_stream_seek(ctx->output, old_offset) < 0)
-		return syscall_error(ctx, "o_stream_seek()");
-	return TRUE;
+		return mbox_set_syscall_error(ctx->ibox, "o_stream_seek()");
+	return 0;
 }
 
-int mbox_storage_save_next(struct mail_save_context *ctx,
-			   const struct mail_full_flags *flags,
-			   time_t received_date,
-			   int timezone_offset __attr_unused__,
-			   struct istream *data)
+int mbox_save(struct mailbox_transaction_context *_t,
+	      const struct mail_full_flags *flags,
+	      time_t received_date, int timezone_offset __attr_unused__,
+	      const char *from_envelope, struct istream *data)
 {
-	enum mail_flags real_flags;
-	int failed;
+	struct mbox_transaction_context *t =
+		(struct mbox_transaction_context *)_t;
+	struct index_mailbox *ibox = t->ictx.ibox;
+	struct mbox_save_context *ctx = t->save_ctx;
+	int ret;
+
+	ctx->flags = flags;
+
+	if (ctx == NULL) {
+		ctx = t->save_ctx = i_new(struct mbox_save_context, 1);
+		ctx->ibox = ibox;
+		ctx->append_offset = (uoff_t)-1;
+	}
 
-	/* we don't need the real flag positions, easier to keep using our own.
-	   they need to be checked/added though. */
-	ctx->flags = flags;
-	real_flags = flags->flags;
-	if (!index_mailbox_fix_keywords(ctx->ibox, &real_flags,
-					flags->keywords,
-					flags->keywords_count))
-		return FALSE;
+	if (ctx->append_offset == (uoff_t)-1) {
+		if (ibox->mbox_lock_type != F_WRLCK) {
+			if (mbox_lock(ibox, F_WRLCK, &t->mbox_lock_id) <= 0)
+				return -1;
+		}
+
+		if (ibox->mbox_fd == -1) {
+			if (mbox_file_open(ibox) < 0)
+				return -1;
+		}
+
+		if (mbox_seek_to_end(ctx, &ctx->append_offset) < 0)
+			return -1;
+
+		ctx->output = o_stream_create_file(ibox->mbox_fd, default_pool,
+						   4096, FALSE);
+		o_stream_set_blocking(ctx->output, 60000, NULL, NULL);
+	}
+
+	i_assert(ibox->mbox_lock_type == F_WRLCK);
 
 	t_push();
-	if (!write_from_line(ctx, received_date) ||
-	    !mail_storage_save(ctx->ibox->box.storage,
-			       ctx->ibox->index->mailbox_path,
-			       data, ctx->output,
-			       getenv("MAIL_SAVE_CRLF") != NULL,
-			       save_header_callback, ctx) ||
-	    !mbox_fix_header(ctx) ||
-	    !mbox_append_lf(ctx)) {
+	if (write_from_line(ctx, received_date, from_envelope) < 0 ||
+	    mail_storage_save(ibox->box.storage, ibox->path, data, ctx->output,
+			      getenv("MAIL_SAVE_CRLF") != NULL,
+			      save_header_callback, ctx) < 0 ||
+	    mbox_fix_header(ctx) < 0 ||
+	    mbox_append_lf(ctx) < 0) {
+		ret = -1;
+	} else {
+		ret = 0;
+	}
+	t_pop();
+	return ret;
+}
+
+static void mbox_save_deinit(struct mbox_save_context *ctx)
+{
+	if (ctx->output != NULL)
+		o_stream_unref(ctx->output);
+	i_free(ctx);
+}
+
+int mbox_save_commit(struct mbox_save_context *ctx)
+{
+	int ret = 0;
+
+	if (ctx->ibox->mbox_fd != -1) {
+		if (fdatasync(ctx->ibox->mbox_fd) < 0) {
+			mbox_set_syscall_error(ctx->ibox, "fsync()");
+			ret = -1;
+		}
+	}
+
+	mbox_save_deinit(ctx);
+	return ret;
+}
+
+void mbox_save_rollback(struct mbox_save_context *ctx)
+{
+	struct index_mailbox *ibox = ctx->ibox;
+
+	if (ctx->append_offset != (uoff_t)-1 && ibox->mbox_fd != -1) {
+		i_assert(ibox->mbox_lock_type == F_WRLCK);
+
 		/* failed, truncate file back to original size.
 		   output stream needs to be flushed before truncating
 		   so unref() won't write anything. */
 		o_stream_flush(ctx->output);
-		if (ctx->sync_offset != (uoff_t)-1) {
-			(void)ftruncate(ctx->ibox->index->mbox_fd,
-					ctx->sync_offset);
-			ctx->sync_offset = (uoff_t)-1;
-		}
-		failed = TRUE;
-	} else {
-		if (!ctx->transaction)
-			ctx->sync_offset = ctx->output->offset;
-		failed = FALSE;
-	}
-	t_pop();
 
-	return !failed;
-}
-
-struct mail_save_context *
-mbox_storage_save_init(struct mailbox *box, int transaction)
-{
-	struct index_mailbox *ibox = (struct index_mailbox *) box;
-	struct mail_save_context *ctx;
-
-	if (box->is_readonly(box)) {
-		mail_storage_set_error(box->storage, "Mailbox is read-only");
-		return NULL;
+		if (ftruncate(ibox->mbox_fd, (off_t)ctx->append_offset) < 0)
+			mbox_set_syscall_error(ibox, "ftruncate()");
 	}
 
-	if (!index_storage_sync_and_lock(ibox, FALSE, TRUE,
-					 MAIL_LOCK_EXCLUSIVE))
-		return NULL;
-
-	ctx = i_new(struct mail_save_context, 1);
-	ctx->ibox = ibox;
-	ctx->transaction = transaction;
-
-	if (!mbox_seek_to_end(ctx, &ctx->sync_offset)) {
-		i_free(ctx);
-		return NULL;
-	}
-
-	ctx->output = o_stream_create_file(ibox->index->mbox_fd,
-					   default_pool, 4096, FALSE);
-	o_stream_set_blocking(ctx->output, 60000, NULL, NULL);
-	return ctx;
+	mbox_save_deinit(ctx);
 }
-
-int mbox_storage_save_deinit(struct mail_save_context *ctx, int rollback)
-{
-	int failed = FALSE;
-
-	if (!index_storage_lock(ctx->ibox, MAIL_LOCK_UNLOCK))
-		failed = TRUE;
-
-	if (o_stream_flush(ctx->output) < 0)
-		failed = TRUE;
-	o_stream_unref(ctx->output);
-
-	if (rollback && ctx->sync_offset != (uoff_t)-1) {
-		if (ftruncate(ctx->ibox->index->mbox_fd,
-			      ctx->sync_offset) < 0) {
-			syscall_error(ctx, "ftruncate()");
-			failed = TRUE;
-		}
-	} else {
-		if (fdatasync(ctx->ibox->index->mbox_fd) < 0) {
-			syscall_error(ctx, "fsync()");
-			failed = TRUE;
-		}
-	}
-
-	i_free(ctx);
-	return !failed;
-}
-#endif
--- a/src/lib-storage/index/mbox/mbox-storage.c	Tue May 04 21:13:10 2004 +0300
+++ b/src/lib-storage/index/mbox/mbox-storage.c	Thu May 06 04:22:25 2004 +0300
@@ -1,14 +1,14 @@
 /* Copyright (C) 2002-2003 Timo Sirainen */
 
 #include "lib.h"
+#include "buffer.h"
 #include "home-expand.h"
 #include "mkdir-parents.h"
 #include "unlink-directory.h"
 #include "subscription-file/subscription-file.h"
-#include "mail-keywords.h"
-#include "mbox-index.h"
+#include "mbox-storage.h"
 #include "mbox-lock.h"
-#include "mbox-storage.h"
+#include "mail-save.h"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -21,8 +21,19 @@
 extern struct mail_storage mbox_storage;
 extern struct mailbox mbox_mailbox;
 
-static int mbox_handle_errors(struct mail_storage *storage)
+int mbox_set_syscall_error(struct index_mailbox *ibox, const char *function)
 {
+	i_assert(function != NULL);
+
+	mail_storage_set_critical(ibox->box.storage,
+		"%s failed with mbox file %s: %m", function, ibox->path);
+	return -1;
+}
+
+static int mbox_handle_errors(struct index_storage *istorage)
+{
+	struct mail_storage *storage = &istorage->storage;
+
 	if (ENOACCESS(errno))
 		mail_storage_set_error(storage, "Permission denied");
 	else if (ENOSPACE(errno))
@@ -130,7 +141,7 @@
 mbox_create(const char *data, const char *user,
 	    const char *namespace, char hierarchy_sep)
 {
-	struct mail_storage *storage;
+	struct index_storage *storage;
 	const char *root_dir, *inbox_file, *index_dir, *p;
 	struct stat st;
 	int autodetect;
@@ -191,39 +202,40 @@
 	else if (strcmp(index_dir, "MEMORY") == 0)
 		index_dir = NULL;
 
-	storage = i_new(struct mail_storage, 1);
-	memcpy(storage, &mbox_storage, sizeof(struct mail_storage));
+	storage = i_new(struct index_storage, 1);
+	storage->storage = mbox_storage;
 
 	if (hierarchy_sep != '\0')
-		storage->hierarchy_sep = hierarchy_sep;
-	storage->namespace = i_strdup(namespace);
+		storage->storage.hierarchy_sep = hierarchy_sep;
+	storage->storage.namespace = i_strdup(namespace);
 
 	storage->dir = i_strdup(home_expand(root_dir));
-	storage->inbox_file = i_strdup(home_expand(inbox_file));
+	storage->inbox_path = i_strdup(home_expand(inbox_file));
 	storage->index_dir = i_strdup(home_expand(index_dir));
 	storage->user = i_strdup(user);
 	storage->callbacks = i_new(struct mail_storage_callbacks, 1);
 	index_storage_init(storage);
-	return storage;
+	return &storage->storage;
 }
 
-static void mbox_free(struct mail_storage *storage)
+static void mbox_free(struct mail_storage *_storage)
 {
+	struct index_storage *storage = (struct index_storage *)_storage;
+
 	index_storage_deinit(storage);
 
-	i_free(storage->namespace);
 	i_free(storage->dir);
-	i_free(storage->inbox_file);
+	i_free(storage->inbox_path);
 	i_free(storage->index_dir);
 	i_free(storage->user);
-	i_free(storage->error);
 	i_free(storage->callbacks);
 	i_free(storage);
 }
 
-const char *mbox_fix_mailbox_name(struct mail_storage *storage,
+const char *mbox_fix_mailbox_name(struct index_storage *istorage,
 				  const char *name, int remove_namespace)
 {
+        struct mail_storage *storage = &istorage->storage;
 	char *dup, *p, sep;
 	size_t len;
 
@@ -304,7 +316,7 @@
 	return mbox_is_valid_mask(name);
 }
 
-static const char *mbox_get_index_dir(struct mail_storage *storage,
+static const char *mbox_get_index_dir(struct index_storage *storage,
 				      const char *name)
 {
 	const char *p;
@@ -329,56 +341,58 @@
 	}
 }
 
-static int create_mbox_index_dirs(struct mail_storage *storage,
+static int create_mbox_index_dirs(struct index_storage *storage,
 				  const char *name)
 {
 	const char *index_dir;
 
 	index_dir = mbox_get_index_dir(storage, name);
 	if (index_dir == NULL)
-		return TRUE;
+		return 0;
 
 	if (mkdir_parents(index_dir, CREATE_MODE) < 0) {
-		mail_storage_set_critical(storage,
+		mail_storage_set_critical(&storage->storage,
 			"mkdir_parents(%s) failed: %m", index_dir);
-		return FALSE;
+		return -1;
 	}
 
-	return TRUE;
+	return 0;
 }
 
-static int verify_inbox(struct mail_storage *storage)
+static int verify_inbox(struct index_storage *storage)
 {
 	int fd;
 
 	/* make sure inbox file itself exists */
-	fd = open(storage->inbox_file, O_RDWR | O_CREAT | O_EXCL, 0660);
+	fd = open(storage->inbox_path, O_RDWR | O_CREAT | O_EXCL, 0660);
 	if (fd != -1)
 		(void)close(fd);
 
 	/* make sure the index directories exist */
-	if (!create_mbox_index_dirs(storage, "INBOX"))
-		return FALSE;
+	if (create_mbox_index_dirs(storage, "INBOX") < 0)
+		return -1;
 
-	return TRUE;
+	return 0;
 }
 
-static const char *mbox_get_path(struct mail_storage *storage, const char *name)
+static const char *
+mbox_get_path(struct index_storage *storage, const char *name)
 {
 	if (strcasecmp(name, "INBOX") == 0)
-		return storage->inbox_file;
+		return storage->inbox_path;
 	if (full_filesystem_access && (*name == '/' || *name == '~'))
 		return home_expand(name);
 	return t_strconcat(storage->dir, "/", name, NULL);
 }
 
-static void mbox_mail_init(struct index_mail *mail)
+static uint32_t mbox_get_recent_count(struct index_mailbox *ibox)
 {
-	mail->mail.expunge = mbox_storage_expunge;
+	return 0; // FIXME
 }
 
-static struct mailbox *mbox_open(struct mail_storage *storage, const char *name,
-				 enum mailbox_open_flags flags)
+static struct mailbox *
+mbox_open(struct index_storage *storage, const char *name,
+	  enum mailbox_open_flags flags)
 {
 	struct index_mailbox *ibox;
 	struct mail_index *index;
@@ -388,7 +402,7 @@
 		/* name = "INBOX"
 		   path = "<inbox_file>/INBOX"
 		   index_dir = "/mail/.imap/INBOX" */
-		path = storage->inbox_file;
+		path = storage->inbox_path;
 		index_dir = mbox_get_index_dir(storage, "INBOX");
 	} else {
 		/* name = "foo/bar"
@@ -398,99 +412,105 @@
 		index_dir = mbox_get_index_dir(storage, name);
 	}
 
-	index = index_storage_lookup_ref(index_dir, path);
-	if (index == NULL) {
-		index = mbox_index_alloc(path, index_dir, index_dir);
-		index_storage_add(index);
-	}
+	index = index_storage_alloc(index_dir, path, MBOX_INDEX_PREFIX);
+	ibox = index_storage_mailbox_init(storage, &mbox_mailbox,
+					  index, name, flags);
+	if (ibox == NULL)
+		return NULL;
 
-	ibox = index_storage_mailbox_init(storage, &mbox_mailbox, index,
-					  name, flags);
-	if (ibox != NULL)
-		ibox->mail_init = mbox_mail_init;
-	return (struct mailbox *) ibox;
+	ibox->path = i_strdup(path);
+	ibox->mbox_fd = -1;
+
+	ibox->get_recent_count = mbox_get_recent_count;
+	ibox->mail_interface = &mbox_mail;
+
+	return &ibox->box;
 }
 
 static struct mailbox *
-mbox_open_mailbox(struct mail_storage *storage,
+mbox_mailbox_open(struct mail_storage *_storage,
 		  const char *name, enum mailbox_open_flags flags)
 {
+	struct index_storage *storage = (struct index_storage *)_storage;
 	const char *path;
 	struct stat st;
 
-	mail_storage_clear_error(storage);
+	mail_storage_clear_error(_storage);
 
 	name = mbox_fix_mailbox_name(storage, name, TRUE);
 
 	/* INBOX is always case-insensitive */
 	if (strcasecmp(name, "INBOX") == 0) {
 		/* make sure inbox exists */
-		if (!verify_inbox(storage))
-			return FALSE;
+		if (verify_inbox(storage) < 0)
+			return NULL;
 		return mbox_open(storage, "INBOX", flags);
 	}
 
 	if (!mbox_is_valid_existing_name(name)) {
-		mail_storage_set_error(storage, "Invalid mailbox name");
-		return FALSE;
+		mail_storage_set_error(_storage, "Invalid mailbox name");
+		return NULL;
 	}
 
 	path = mbox_get_path(storage, name);
 	if (stat(path, &st) == 0) {
 		if (S_ISDIR(st.st_mode)) {
-			mail_storage_set_error(storage,
+			mail_storage_set_error(_storage,
 				"Mailbox isn't selectable: %s", name);
 			return NULL;
 		}
 
 		/* exists - make sure the required directories are also there */
-		if (!create_mbox_index_dirs(storage, name))
+		if (create_mbox_index_dirs(storage, name) < 0)
 			return NULL;
 
 		return mbox_open(storage, name, flags);
 	}
 
 	if (ENOTFOUND(errno)) {
-		mail_storage_set_error(storage, "Mailbox doesn't exist: %s",
+		mail_storage_set_error(_storage, "Mailbox doesn't exist: %s",
 				       name);
-	} else if (!mbox_handle_errors(storage))
-		mail_storage_set_critical(storage, "stat(%s) failed: %m", path);
+	} else if (!mbox_handle_errors(storage)) {
+		mail_storage_set_critical(_storage, "stat(%s) failed: %m",
+					  path);
+	}
 
 	return NULL;
 }
 
-static int mbox_create_mailbox(struct mail_storage *storage, const char *name,
+static int mbox_mailbox_create(struct mail_storage *_storage, const char *name,
 			       int directory)
 {
+	struct index_storage *storage = (struct index_storage *)_storage;
 	const char *path, *p;
 	struct stat st;
 	int fd;
 
-	mail_storage_clear_error(storage);
+	mail_storage_clear_error(_storage);
 
 	name = mbox_fix_mailbox_name(storage, name, TRUE);
 
 	if (!mbox_is_valid_create_name(name)) {
-		mail_storage_set_error(storage, "Invalid mailbox name");
-		return FALSE;
+		mail_storage_set_error(_storage, "Invalid mailbox name");
+		return -1;
 	}
 
 	/* make sure it doesn't exist already */
 	path = mbox_get_path(storage, name);
 	if (stat(path, &st) == 0) {
-		mail_storage_set_error(storage, "Mailbox already exists");
-		return FALSE;
+		mail_storage_set_error(_storage, "Mailbox already exists");
+		return -1;
 	}
 
 	if (errno != ENOENT && errno != ELOOP && errno != EACCES) {
 		if (errno == ENOTDIR) {
-			mail_storage_set_error(storage,
+			mail_storage_set_error(_storage,
 				"Mailbox doesn't allow inferior mailboxes");
 		} else {
-			mail_storage_set_critical(storage,
+			mail_storage_set_critical(_storage,
 				"stat() failed for mbox file %s: %m", path);
 		}
-		return FALSE;
+		return -1;
 	}
 
 	/* create the hierarchy if needed */
@@ -499,16 +519,16 @@
 		p = t_strdup_until(path, p);
 		if (mkdir_parents(p, CREATE_MODE) < 0) {
 			if (mbox_handle_errors(storage))
-				return FALSE;
+				return -1;
 
-			mail_storage_set_critical(storage,
+			mail_storage_set_critical(_storage,
 				"mkdir_parents(%s) failed: %m", p);
-			return FALSE;
+			return -1;
 		}
 
 		if (directory) {
 			/* wanted to create only the directory */
-			return TRUE;
+			return 0;
 		}
 	}
 
@@ -516,48 +536,49 @@
 	fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0660);
 	if (fd != -1) {
 		(void)close(fd);
-		return TRUE;
+		return 0;
 	}
 
 	if (errno == EEXIST) {
 		/* mailbox was just created between stat() and open() call.. */
-		mail_storage_set_error(storage, "Mailbox already exists");
+		mail_storage_set_error(_storage, "Mailbox already exists");
 	} else if (!mbox_handle_errors(storage)) {
-		mail_storage_set_critical(storage,
+		mail_storage_set_critical(_storage,
 			"Can't create mailbox %s: %m", name);
 	}
-	return FALSE;
+	return -1;
 }
 
-static int mbox_delete_mailbox(struct mail_storage *storage, const char *name)
+static int mbox_mailbox_delete(struct mail_storage *_storage, const char *name)
 {
+	struct index_storage *storage = (struct index_storage *)_storage;
 	const char *index_dir, *path;
 	struct stat st;
 
-	mail_storage_clear_error(storage);
+	mail_storage_clear_error(_storage);
 
 	name = mbox_fix_mailbox_name(storage, name, TRUE);
 
 	if (strcasecmp(name, "INBOX") == 0) {
-		mail_storage_set_error(storage, "INBOX can't be deleted.");
-		return FALSE;
+		mail_storage_set_error(_storage, "INBOX can't be deleted.");
+		return -1;
 	}
 
 	if (!mbox_is_valid_existing_name(name)) {
-		mail_storage_set_error(storage, "Invalid mailbox name");
-		return FALSE;
+		mail_storage_set_error(_storage, "Invalid mailbox name");
+		return -1;
 	}
 
 	path = mbox_get_path(storage, name);
 	if (lstat(path, &st) < 0) {
 		if (ENOTFOUND(errno)) {
-			mail_storage_set_error(storage,
+			mail_storage_set_error(_storage,
 				"Mailbox doesn't exist: %s", name);
 		} else if (!mbox_handle_errors(storage)) {
-			mail_storage_set_critical(storage, "lstat() failed for "
-						  "%s: %m", path);
+			mail_storage_set_critical(_storage,
+				"lstat() failed for %s: %m", path);
 		}
-		return FALSE;
+		return -1;
 	}
 
 	if (S_ISDIR(st.st_mode)) {
@@ -568,40 +589,40 @@
 
 		if (index_dir != NULL && rmdir(index_dir) < 0 &&
 		    !ENOTFOUND(errno) && errno != ENOTEMPTY) {
-			if (!mbox_handle_errors(storage)) {
-				mail_storage_set_critical(storage,
+			if (!mbox_handle_errors(storage) < 0) {
+				mail_storage_set_critical(_storage,
 					"rmdir() failed for %s: %m", index_dir);
-				return FALSE;
+				return -1;
 			}
 		}
 
 		if (rmdir(path) == 0)
-			return TRUE;
+			return 0;
 
 		if (ENOTFOUND(errno)) {
-			mail_storage_set_error(storage,
+			mail_storage_set_error(_storage,
 				"Mailbox doesn't exist: %s", name);
 		} else if (errno == ENOTEMPTY) {
-			mail_storage_set_error(storage,
+			mail_storage_set_error(_storage,
 				"Folder %s isn't empty, can't delete it.",
 				name);
 		} else if (!mbox_handle_errors(storage)) {
-			mail_storage_set_critical(storage,
+			mail_storage_set_critical(_storage,
 				"rmdir() failed for %s: %m", path);
 		}
-		return FALSE;
+		return -1;
 	}
 
 	/* first unlink the mbox file */
 	if (unlink(path) < 0) {
 		if (ENOTFOUND(errno)) {
-			mail_storage_set_error(storage,
+			mail_storage_set_error(_storage,
 				"Mailbox doesn't exist: %s", name);
 		} else if (!mbox_handle_errors(storage)) {
-			mail_storage_set_critical(storage,
+			mail_storage_set_critical(_storage,
 				"unlink() failed for %s: %m", path);
 		}
-		return FALSE;
+		return -1;
 	}
 
 	/* next delete the index directory */
@@ -610,7 +631,7 @@
 		index_storage_destroy_unrefed();
 
 		if (unlink_directory(index_dir, TRUE) < 0 && errno != ENOENT) {
-			mail_storage_set_critical(storage,
+			mail_storage_set_critical(_storage,
 				"unlink_directory(%s) failed: %m", index_dir);
 
 			/* mailbox itself is deleted, so return success
@@ -618,24 +639,25 @@
 		}
 	}
 
-	return TRUE;
+	return 0;
 }
 
-static int mbox_rename_mailbox(struct mail_storage *storage,
+static int mbox_mailbox_rename(struct mail_storage *_storage,
 			       const char *oldname, const char *newname)
 {
+	struct index_storage *storage = (struct index_storage *)_storage;
 	const char *oldpath, *newpath, *old_indexdir, *new_indexdir, *p;
 	struct stat st;
 
-	mail_storage_clear_error(storage);
+	mail_storage_clear_error(_storage);
 
 	oldname = mbox_fix_mailbox_name(storage, oldname, TRUE);
 	newname = mbox_fix_mailbox_name(storage, newname, TRUE);
 
 	if (!mbox_is_valid_existing_name(oldname) ||
 	    !mbox_is_valid_create_name(newname)) {
-		mail_storage_set_error(storage, "Invalid mailbox name");
-		return FALSE;
+		mail_storage_set_error(_storage, "Invalid mailbox name");
+		return -1;
 	}
 
 	oldpath = mbox_get_path(storage, oldname);
@@ -647,11 +669,11 @@
 		p = t_strdup_until(newpath, p);
 		if (mkdir_parents(p, CREATE_MODE) < 0) {
 			if (mbox_handle_errors(storage))
-				return FALSE;
+				return -1;
 
-			mail_storage_set_critical(storage,
+			mail_storage_set_critical(_storage,
 				"mkdir_parents(%s) failed: %m", p);
-			return FALSE;
+			return -1;
 		}
 	}
 
@@ -660,26 +682,26 @@
 	   possibility that someone actually tries to rename two mailboxes
 	   to same new one */
 	if (lstat(newpath, &st) == 0) {
-		mail_storage_set_error(storage,
+		mail_storage_set_error(_storage,
 				       "Target mailbox already exists");
-		return FALSE;
+		return -1;
 	} else if (!ENOTFOUND(errno) && errno != EACCES) {
-		mail_storage_set_critical(storage, "lstat(%s) failed: %m",
+		mail_storage_set_critical(_storage, "lstat(%s) failed: %m",
 					  newpath);
-		return FALSE;
+		return -1;
 	}
 
 	/* NOTE: renaming INBOX works just fine with us, it's simply recreated
 	   the next time it's needed. */
 	if (rename(oldpath, newpath) < 0) {
 		if (ENOTFOUND(errno)) {
-			mail_storage_set_error(storage,
+			mail_storage_set_error(_storage,
 				"Mailbox doesn't exist: %s", oldname);
 		} else if (!mbox_handle_errors(storage)) {
-			mail_storage_set_critical(storage,
+			mail_storage_set_critical(_storage,
 				"rename(%s, %s) failed: %m", oldpath, newpath);
 		}
-		return FALSE;
+		return -1;
 	}
 
 	/* we need to rename the index directory as well */
@@ -688,85 +710,82 @@
 	if (old_indexdir != NULL) {
 		if (rename(old_indexdir, new_indexdir) < 0 &&
 		    errno != ENOENT) {
-			mail_storage_set_critical(storage,
+			mail_storage_set_critical(_storage,
 						  "rename(%s, %s) failed: %m",
 						  old_indexdir, new_indexdir);
 		}
 	}
 
-	return TRUE;
+	return 0;
 }
 
-static int mbox_set_subscribed(struct mail_storage *storage,
+static int mbox_set_subscribed(struct mail_storage *_storage,
 			       const char *name, int set)
 {
+	struct index_storage *storage = (struct index_storage *)_storage;
+	const char *path;
+
+	path = t_strconcat(storage->dir, "/" SUBSCRIPTION_FILE_NAME, NULL);
 	name = mbox_fix_mailbox_name(storage, name, FALSE);
-	return subsfile_set_subscribed(storage, name, set);
+	return subsfile_set_subscribed(_storage, path, name, set);
 }
 
-static int mbox_get_mailbox_name_status(struct mail_storage *storage,
+static int mbox_get_mailbox_name_status(struct mail_storage *_storage,
 					const char *name,
 					enum mailbox_name_status *status)
 {
+	struct index_storage *storage = (struct index_storage *)_storage;
 	struct stat st;
 	const char *path;
 
-	mail_storage_clear_error(storage);
+	mail_storage_clear_error(_storage);
 
 	name = mbox_fix_mailbox_name(storage, name, TRUE);
 
 	if (!mbox_is_valid_existing_name(name)) {
 		*status = MAILBOX_NAME_INVALID;
-		return TRUE;
+		return 0;
 	}
 
 	path = mbox_get_path(storage, name);
 	if (stat(path, &st) == 0) {
 		*status = MAILBOX_NAME_EXISTS;
-		return TRUE;
+		return 0;
 	}
 
 	if (!mbox_is_valid_create_name(name)) {
 		*status = MAILBOX_NAME_INVALID;
-		return TRUE;
+		return 0;
 	}
 
 	if (ENOTFOUND(errno) || errno == EACCES) {
 		*status = MAILBOX_NAME_VALID;
-		return TRUE;
+		return 0;
 	} else if (errno == ENOTDIR) {
 		*status = MAILBOX_NAME_NOINFERIORS;
-		return TRUE;
+		return 0;
 	} else {
-		mail_storage_set_critical(storage, "mailbox name status: "
+		mail_storage_set_critical(_storage, "mailbox name status: "
 					  "stat(%s) failed: %m", path);
-		return FALSE;
+		return -1;
 	}
 }
 
 static int mbox_storage_close(struct mailbox *box)
 {
-	struct index_mailbox *ibox = (struct index_mailbox *) box;
-	int failed = FALSE;
+	struct index_mailbox *ibox = (struct index_mailbox *)box;
 
-	/* update flags by rewrite mbox file */
-        index_storage_init_lock_notify(ibox);
-	if (!ibox->index->mailbox_readonly) {
-		if (!mbox_index_rewrite(ibox->index)) {
-			mail_storage_set_index_error(ibox);
-			failed = TRUE;
-		}
-	}
-	ibox->index->set_lock_notify_callback(ibox->index, NULL, NULL);
-
-	return index_storage_mailbox_free(box) && !failed;
+	if (ibox->mbox_data_buf != NULL)
+		buffer_free(ibox->mbox_data_buf);
+        index_storage_mailbox_free(box);
+	return 0;
 }
 
 static void mbox_storage_auto_sync(struct mailbox *box,
 				   enum mailbox_sync_flags flags,
 				   unsigned int min_newmail_notify_interval)
 {
-	struct index_mailbox *ibox = (struct index_mailbox *) box;
+	struct index_mailbox *ibox = (struct index_mailbox *)box;
 
 	ibox->min_newmail_notify_interval = min_newmail_notify_interval;
 
@@ -780,40 +799,7 @@
 	if (flags == 0)
 		index_mailbox_check_remove_all(ibox);
 	else
-		index_mailbox_check_add(ibox, ibox->index->mailbox_path, FALSE);
-}
-
-static int mbox_storage_lock(struct mailbox *box,
-			     enum mailbox_lock_type lock_type)
-{
-	struct index_mailbox *ibox = (struct index_mailbox *) box;
-
-	if (lock_type == MAIL_LOCK_UNLOCK) {
-		ibox->lock_type = MAIL_LOCK_UNLOCK;
-		if (!index_storage_lock(ibox, MAIL_LOCK_UNLOCK))
-			return FALSE;
-		return TRUE;
-	}
-
-	i_assert(ibox->lock_type == MAIL_LOCK_UNLOCK);
-
-	if ((lock_type & (MAILBOX_LOCK_EXPUNGE | MAILBOX_LOCK_FLAGS)) != 0) {
-		if (!index_storage_lock(ibox, MAIL_LOCK_EXCLUSIVE))
-			return FALSE;
-	} else if ((lock_type & MAILBOX_LOCK_READ) != 0) {
-		if (!index_storage_lock(ibox, MAIL_LOCK_SHARED))
-			return FALSE;
-	}
-
-	if ((lock_type & (MAILBOX_LOCK_EXPUNGE | MAILBOX_LOCK_SAVE)) != 0) {
-		/* FIXME: saving doesn't have to sync it, just lock it */
-		if (!index_storage_sync_and_lock(ibox, FALSE, TRUE,
-						 MAIL_LOCK_EXCLUSIVE))
-			return FALSE;
-	}
-
-	ibox->lock_type = lock_type;
-	return TRUE;
+		index_mailbox_check_add(ibox, ibox->path, FALSE);
 }
 
 struct mail_storage mbox_storage = {
@@ -826,24 +812,18 @@
 	mbox_free,
 	mbox_autodetect,
 	index_storage_set_callbacks,
-	mbox_open_mailbox,
-	mbox_create_mailbox,
-	mbox_delete_mailbox,
-	mbox_rename_mailbox,
-	mbox_list_mailbox_init,
-	mbox_list_mailbox_deinit,
-	mbox_list_mailbox_next,
+	mbox_mailbox_open,
+	mbox_mailbox_create,
+	mbox_mailbox_delete,
+	mbox_mailbox_rename,
+	mbox_mailbox_list_init,
+	mbox_mailbox_list_next,
+	mbox_mailbox_list_deinit,
 	mbox_set_subscribed,
 	mbox_get_mailbox_name_status,
 	mail_storage_get_last_error,
 
 	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL, NULL, NULL,
-
 	0
 };
 
@@ -854,24 +834,19 @@
 	index_storage_is_readonly,
         index_storage_allow_new_keywords,
 	mbox_storage_close,
-	mbox_storage_lock,
 	index_storage_get_status,
-	index_storage_sync,
+	mbox_storage_sync,
 	mbox_storage_auto_sync,
-	index_storage_fetch_uid,
-	index_storage_fetch_seq,
+	mbox_transaction_begin,
+	mbox_transaction_commit,
+	mbox_transaction_rollback,
+	index_storage_fetch,
+	index_storage_get_uids,
         index_storage_search_get_sorting,
 	index_storage_search_init,
 	index_storage_search_deinit,
 	index_storage_search_next,
-	mbox_storage_save_init,
-	mbox_storage_save_deinit,
-	mbox_storage_save_next,
-	index_storage_copy_init,
-	index_storage_copy_deinit,
-	index_storage_copy,
-	mbox_storage_expunge_init,
-	mbox_storage_expunge_deinit,
-	mbox_storage_expunge_fetch_next,
-	index_storage_is_inconsistency_error
+	mbox_save,
+	mail_storage_copy,
+	index_storage_is_inconsistent
 };
--- a/src/lib-storage/index/mbox/mbox-storage.h	Tue May 04 21:13:10 2004 +0300
+++ b/src/lib-storage/index/mbox/mbox-storage.h	Thu May 06 04:22:25 2004 +0300
@@ -1,34 +1,46 @@
 #ifndef __MBOX_STORAGE_H
 #define __MBOX_STORAGE_H
 
+/* Extra space to leave in X-Keywords header when rewriting mbox */
+#define MBOX_HEADER_EXTRA_SPACE 100
+
+#define SUBSCRIPTION_FILE_NAME "subscriptions"
+#define MBOX_INDEX_PREFIX "dovecot.index"
+
 #include "index-storage.h"
 
-int mbox_storage_copy(struct mailbox *box, struct mailbox *destbox,
-		      const char *messageset, int uidset);
+struct mbox_transaction_context {
+	struct index_transaction_context ictx;
 
-struct mail_save_context *
-mbox_storage_save_init(struct mailbox *box, int transaction);
-int mbox_storage_save_deinit(struct mail_save_context *ctx, int rollback);
-int mbox_storage_save_next(struct mail_save_context *ctx,
-			   const struct mail_full_flags *flags,
-			   time_t received_date, int timezone_offset,
-			   struct istream *data);
+	struct mbox_save_context *save_ctx;
+	unsigned int mbox_lock_id;
+};
+
+extern struct mail mbox_mail;
+
+int mbox_set_syscall_error(struct index_mailbox *ibox, const char *function);
 
 struct mailbox_list_context *
-mbox_list_mailbox_init(struct mail_storage *storage, const char *mask,
+mbox_mailbox_list_init(struct mail_storage *storage, const char *mask,
 		       enum mailbox_list_flags flags);
-int mbox_list_mailbox_deinit(struct mailbox_list_context *ctx);
-struct mailbox_list *mbox_list_mailbox_next(struct mailbox_list_context *ctx);
+int mbox_mailbox_list_deinit(struct mailbox_list_context *ctx);
+struct mailbox_list *mbox_mailbox_list_next(struct mailbox_list_context *ctx);
+
+struct mailbox_transaction_context *
+mbox_transaction_begin(struct mailbox *box, int hide);
+int mbox_transaction_commit(struct mailbox_transaction_context *t);
+void mbox_transaction_rollback(struct mailbox_transaction_context *t);
 
-struct mail_expunge_context *
-mbox_storage_expunge_init(struct mailbox *box,
-			  enum mail_fetch_field wanted_fields, int expunge_all);
-int mbox_storage_expunge_deinit(struct mail_expunge_context *ctx);
-struct mail *mbox_storage_expunge_fetch_next(struct mail_expunge_context *ctx);
-int mbox_storage_expunge(struct mail *mail, struct mail_expunge_context *ctx,
-			 unsigned int *seq_r, int notify);
+int mbox_storage_sync(struct mailbox *box, enum mailbox_sync_flags flags);
 
-const char *mbox_fix_mailbox_name(struct mail_storage *storage,
+int mbox_save(struct mailbox_transaction_context *t,
+	      const struct mail_full_flags *flags,
+	      time_t received_date, int timezone_offset,
+	      const char *from_envelope, struct istream *data);
+int mbox_save_commit(struct mbox_save_context *ctx);
+void mbox_save_rollback(struct mbox_save_context *ctx);
+
+const char *mbox_fix_mailbox_name(struct index_storage *istorage,
 				  const char *name, int remove_namespace);
 int mbox_is_valid_mask(const char *mask);
 
--- a/src/lib-storage/index/mbox/mbox-sync-parse.c	Tue May 04 21:13:10 2004 +0300
+++ b/src/lib-storage/index/mbox/mbox-sync-parse.c	Thu May 06 04:22:25 2004 +0300
@@ -85,27 +85,24 @@
 		return FALSE;
 	}
 
+	/* <uid validity> <last uid> */
 	t_push();
-
-	/* <uid validity> <last uid> */
 	str = t_strndup(hdr->full_value, hdr->full_value_len);
 	ctx->base_uid_validity = strtoul(str, &end, 10);
 	ctx->base_uid_last = strtoul(end, &end, 10);
 	pos = end - str;
+	t_pop();
 
 	while (pos < hdr->full_value_len && IS_LWSP_LF(hdr->full_value[pos]))
 		pos++;
 
 	if (ctx->base_uid_validity == 0) {
 		/* broken */
-		t_pop();
 		return FALSE;
 	}
 
-	if (pos == hdr->full_value_len) {
-		t_pop();
+	if (pos == hdr->full_value_len)
 		return TRUE;
-	}
 
 	// FIXME: save keywords
 
--- a/src/lib-storage/index/mbox/mbox-sync-private.h	Tue May 04 21:13:10 2004 +0300
+++ b/src/lib-storage/index/mbox/mbox-sync-private.h	Thu May 06 04:22:25 2004 +0300
@@ -25,7 +25,7 @@
 extern struct mbox_flag_type mbox_status_flags[];
 extern struct mbox_flag_type mbox_xstatus_flags[];
 
-struct mbox_mail {
+struct mbox_sync_mail {
 	uint32_t uid;
 	uint8_t flags;
 	keywords_mask_t keywords;
@@ -37,7 +37,7 @@
 
 struct mbox_sync_mail_context {
 	struct mbox_sync_context *sync_ctx;
-	struct mbox_mail *mail;
+	struct mbox_sync_mail *mail;
 
 	uint32_t seq;
 	uoff_t hdr_offset, body_offset;
@@ -55,8 +55,8 @@
 };
 
 struct mbox_sync_context {
-	struct istream *file_input;
-	struct istream *input;
+	struct index_mailbox *ibox;
+	struct istream *input, *file_input;
 	int fd;
 
 	const struct mail_index_header *hdr;
@@ -64,6 +64,7 @@
 	uint32_t prev_msg_uid, next_uid;
 };
 
+int mbox_sync(struct index_mailbox *ibox, int last_commit);
 void mbox_sync_parse_next_mail(struct istream *input,
 			       struct mbox_sync_mail_context *ctx);
 void mbox_sync_update_header(struct mbox_sync_mail_context *ctx,
--- a/src/lib-storage/index/mbox/mbox-sync-rewrite.c	Tue May 04 21:13:10 2004 +0300
+++ b/src/lib-storage/index/mbox/mbox-sync-rewrite.c	Thu May 06 04:22:25 2004 +0300
@@ -19,7 +19,7 @@
 	i_stream_seek(sync_ctx->file_input, source);
 	o_stream_seek(output, dest);
 
-	istream_raw_mbox_flush(sync_ctx->input);
+	istream_raw_mbox_flush(sync_ctx->file_input);
 
 	if (size == (uoff_t)-1) {
 		input = sync_ctx->file_input;
@@ -157,7 +157,7 @@
 int mbox_sync_rewrite(struct mbox_sync_context *sync_ctx, buffer_t *mails_buf,
 		      uint32_t first_seq, uint32_t last_seq, off_t extra_space)
 {
-	struct mbox_mail *mails;
+	struct mbox_sync_mail *mails;
 	size_t size;
 	uint32_t first_idx, last_idx, extra_per_mail;
 
--- a/src/lib-storage/index/mbox/mbox-sync.c	Tue May 04 21:13:10 2004 +0300
+++ b/src/lib-storage/index/mbox/mbox-sync.c	Thu May 06 04:22:25 2004 +0300
@@ -45,16 +45,21 @@
 */
 
 #include "lib.h"
+#include "ioloop.h"
 #include "buffer.h"
 #include "istream.h"
 #include "file-set-size.h"
 #include "str.h"
 #include "write-full.h"
 #include "istream-raw-mbox.h"
+#include "mbox-storage.h"
+#include "mbox-file.h"
 #include "mbox-sync-private.h"
 
+#include <sys/stat.h>
+
 static int mbox_sync_grow_file(struct mbox_sync_context *sync_ctx,
-			       struct mbox_mail *mail, uoff_t body_offset,
+			       struct mbox_sync_mail *mail, uoff_t body_offset,
 			       uoff_t grow_size)
 {
 	char spaces[1024];
@@ -112,41 +117,94 @@
 	return 0;
 }
 
-int mbox_sync(struct istream *input)
+int mbox_sync(struct index_mailbox *ibox, int last_commit)
 {
 	struct mbox_sync_context sync_ctx;
 	struct mbox_sync_mail_context mail_ctx;
-	struct mbox_mail mail;
+	struct mbox_sync_mail mail;
+	struct mail_index_sync_ctx *index_sync_ctx;
+	struct mail_index_view *sync_view;
+	const struct mail_index_header *hdr;
+	struct istream *input;
 	uint32_t seq, need_space_seq;
 	off_t space_diff;
+	uoff_t from_offset, offset;
 	buffer_t *mails;
-	int ret = 0;
+	string_t *header;
+	struct stat st;
+	int readonly, ret = 0;
+
+	if (last_commit) {
+		seq = ibox->commit_log_file_seq;
+		offset = ibox->commit_log_file_offset;
+	} else {
+		seq = 0;
+		offset = 0;
+	}
+
+	ret = mail_index_sync_begin(ibox->index, &index_sync_ctx, &sync_view,
+				    seq, offset);
+	if (ret <= 0)
+		return ret;
+
+	if (mbox_file_open_stream(ibox) < 0)
+		return -1;
+
+	if (mail_index_get_header(sync_view, &hdr) < 0)
+		return -1;
+
+	if (ibox->mbox_data_buf == NULL) {
+		ibox->mbox_data_buf =
+			buffer_create_dynamic(default_pool, 512, (size_t)-1);
+	} else {
+		buffer_set_used_size(ibox->mbox_data_buf, 0);
+	}
+
+	readonly = TRUE; // FIXME
+
+	// FIXME: lock the file
 
 	mails = buffer_create_dynamic(default_pool, 4096, (size_t)-1);
 
 	memset(&sync_ctx, 0, sizeof(sync_ctx));
-	sync_ctx.file_input = input;
-	sync_ctx.input = i_stream_create_raw_mbox(default_pool, input);
-	sync_ctx.fd = i_stream_get_fd(input);
-	//sync_ctx.hdr = ;
+	sync_ctx.file_input = ibox->mbox_file_stream;
+	sync_ctx.input = ibox->mbox_stream;
+	sync_ctx.fd = ibox->mbox_fd;
+	sync_ctx.hdr = hdr;
 
 	input = sync_ctx.input;
+	header = str_new(default_pool, 4096);
 
 	space_diff = 0; need_space_seq = 0; seq = 1;
 	for (seq = 1; !input->eof; seq++) {
+		from_offset = input->v_offset;
+
 		memset(&mail, 0, sizeof(mail));
 		memset(&mail_ctx, 0, sizeof(mail_ctx));
 		mail_ctx.sync_ctx = &sync_ctx;
 		mail_ctx.mail = &mail;
 		mail_ctx.seq = seq;
+		mail_ctx.header = header;
 
 		mbox_sync_parse_next_mail(input, &mail_ctx);
+		if (input->v_offset == from_offset) {
+			/* this was the last mail */
+			break;
+		}
+
 		mail.body_size =
 			istream_raw_mbox_get_size(input,
 						  mail_ctx.content_length);
 		buffer_append(mails, &mail, sizeof(mail));
 
-		if (mail_ctx.need_rewrite) {
+		/* save the offset permanently with recent flag state */
+		from_offset <<= 1;
+		if ((mail.flags & MBOX_NONRECENT) != 0)
+			from_offset |= 1;
+		buffer_append(ibox->mbox_data_buf,
+			      &from_offset, sizeof(from_offset));
+
+		if (mail_ctx.need_rewrite && !readonly) {
 			mbox_sync_update_header(&mail_ctx, NULL);
 			if ((ret = mbox_sync_try_rewrite(&mail_ctx)) < 0)
 				break;
@@ -185,83 +243,34 @@
 			ret = -1;
 	}
 
-	i_stream_unref(input);
+	if (fstat(ibox->mbox_fd, &st) < 0) {
+		mbox_set_syscall_error(ibox, "fstat()");
+		ret = -1;
+	}
+
+	if (ret < 0) {
+		st.st_mtime = 0;
+		st.st_size = 0;
+	}
+
+	if (mail_index_sync_end(index_sync_ctx, st.st_mtime, st.st_size) < 0)
+		ret = -1;
+
+	str_free(header);
 	return ret < 0 ? -1 : 0;
 }
 
-#if 0
-int mbox_sync(void)
+int mbox_storage_sync(struct mailbox *box, enum mailbox_sync_flags flags)
 {
-	struct mail_index_view *sync_view;
-	struct mail_index_sync_ctx *sync_ctx;
-	struct mail_index_sync_rec sync_rec;
-	struct mbox_sync_context ctx;
-	struct mbox_sync_mail_context mail_ctx;
-	struct mbox_mail mail;
-	string_t *header;
-	uint32_t seq;
-	unsigned int need_space_seq;
-	uoff_t missing_space;
-	buffer_t *mails;
-	int ret;
+	struct index_mailbox *ibox = (struct index_mailbox *)box;
 
-	memset(&ctx, 0, sizeof(ctx));
-	/*ctx.index = storage->index;
-	ctx.input = storage->input;*/
-	ctx.fd = i_stream_get_fd(ctx.input);
-
-	header = str_new(default_pool, 4096);
-
-	if (mail_index_sync_begin(ctx.index, &sync_ctx, &sync_view, 0, 0) < 0)
-		return -1;
+	if ((flags & MAILBOX_SYNC_FLAG_FAST) == 0 ||
+	    ibox->sync_last_check + MAILBOX_FULL_SYNC_INTERVAL <= ioloop_time) {
+		ibox->sync_last_check = ioloop_time;
 
-	ctx.hdr = mail_index_get_header(sync_view);
-	ctx.next_uid = ctx.hdr->next_uid;
-
-	seq = 1;
-	while ((ret = mail_index_sync_next(sync_ctx, &sync_rec)) > 0) {
-		while (seq < sync_rec.seq1) {
-			seq++;
-		}
-		switch (sync_rec.type) {
-		case MAIL_INDEX_SYNC_TYPE_EXPUNGE:
-			break;
-		case MAIL_INDEX_SYNC_TYPE_FLAGS:
-			break;
-		}
+		if (mbox_sync(ibox, FALSE) < 0)
+			return -1;
 	}
 
-	while (!ctx.input->eof) {
-		memset(&mail_ctx, 0, sizeof(mail_ctx));
-		mail_ctx.parent = &ctx;
-		mail_ctx.header = header;
-		mail_ctx.seq = seq;
-
-		mail_ctx.hdr_offset = ctx.input->v_offset;
-		mbox_sync_mail_parse_headers(&mail_ctx);
-		mail_ctx.body_offset = ctx.input->v_offset;
-		mail_ctx.body_size =
-			istream_raw_mbox_get_size(ctx.input,
-						  mail_ctx.content_length);
-
-                mbox_sync_mail_add_missing_headers(&mail_ctx);
-
-		ret = mbox_sync_try_rewrite_headers(&mail_ctx, &missing_space);
-		if (ret < 0)
-			break;
-		if (missing_space != 0) {
-			ctx.space_diff -= missing_space;
-		} else {
-			ctx.space_diff += mail_ctx.extra_space;
-		}
-
-		if (ctx.first_spacy_msg_offset == 0)
-                        ctx.first_spacy_msg_offset = mail_ctx.hdr_offset;
-
-		ctx.prev_msg_uid = mail_ctx.uid;
-		istream_raw_mbox_next(ctx.input, mail_ctx.content_length);
-	}
-	str_free(header);
-	return 0;
+	return index_storage_sync(box, flags);
 }
-#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/index/mbox/mbox-transaction.c	Thu May 06 04:22:25 2004 +0300
@@ -0,0 +1,63 @@
+/* Copyright (C) 2004 Timo Sirainen */
+
+#include "lib.h"
+#include "mbox-storage.h"
+#include "mbox-lock.h"
+#include "mbox-sync-private.h"
+
+struct mailbox_transaction_context *
+mbox_transaction_begin(struct mailbox *box, int hide)
+{
+	struct index_mailbox *ibox = (struct index_mailbox *)box;
+	struct mbox_transaction_context *t;
+
+	t = i_new(struct mbox_transaction_context, 1);
+	t->ictx.mailbox_ctx.box = box;
+	t->ictx.ibox = ibox;
+	t->ictx.trans = mail_index_transaction_begin(ibox->view, hide);
+	return &t->ictx.mailbox_ctx;
+}
+
+int mbox_transaction_commit(struct mailbox_transaction_context *_t)
+{
+	struct mbox_transaction_context *t =
+		(struct mbox_transaction_context *)_t;
+	struct index_mailbox *ibox = t->ictx.ibox;
+	unsigned int lock_id = t->mbox_lock_id;
+	int ret = 0;
+
+	if (t->save_ctx != NULL)
+		ret = mbox_save_commit(t->save_ctx);
+
+	if (ret == 0) {
+		if (index_transaction_commit(_t) < 0)
+			ret = -1;
+	} else {
+		index_transaction_rollback(_t);
+	}
+
+	if (ret == 0) {
+		if (mbox_sync(ibox, TRUE) < 0)
+			ret = -1;
+	}
+
+	if (lock_id != 0) {
+		if (mbox_unlock(ibox, lock_id) < 0)
+			ret = -1;
+	}
+	return ret;
+}
+
+void mbox_transaction_rollback(struct mailbox_transaction_context *_t)
+{
+	struct mbox_transaction_context *t =
+		(struct mbox_transaction_context *)_t;
+	struct index_mailbox *ibox = t->ictx.ibox;
+
+	if (t->save_ctx != NULL)
+		mbox_save_rollback(t->save_ctx);
+
+	if (t->mbox_lock_id != 0)
+		(void)mbox_unlock(ibox, t->mbox_lock_id);
+	index_transaction_rollback(_t);
+}