[dovecot-cvs] dovecot/src/lib-storage/index/maildir maildir-mail.c, NONE, 1.1 maildir-sync.c, NONE, 1.1 maildir-transaction.c, NONE, 1.1 maildir-uidlist.c, NONE, 1.1 maildir-uidlist.h, NONE, 1.1 maildir-util.c, NONE, 1.1 Makefile.am, 1.1.1.1, 1.2 maildir-copy.c, 1.29, 1.30 maildir-list.c, 1.25, 1.26 maildir-save.c, 1.30, 1.31 maildir-storage.c, 1.66, 1.67 maildir-storage.h, 1.19, 1.20 maildir-expunge.c, 1.14, NONE

cras at procontrol.fi cras at procontrol.fi
Tue Apr 27 23:25:57 EEST 2004


Update of /home/cvs/dovecot/src/lib-storage/index/maildir
In directory talvi:/tmp/cvs-serv29236/src/lib-storage/index/maildir

Modified Files:
	Makefile.am maildir-copy.c maildir-list.c maildir-save.c 
	maildir-storage.c maildir-storage.h 
Added Files:
	maildir-mail.c maildir-sync.c maildir-transaction.c 
	maildir-uidlist.c maildir-uidlist.h maildir-util.c 
Removed Files:
	maildir-expunge.c 
Log Message:
importing new index code. mbox still broken.



--- NEW FILE: maildir-mail.c ---
/* Copyright (C) 2003 Timo Sirainen */

#include "lib.h"
#include "istream.h"
#include "index-mail.h"
#include "maildir-storage.h"
#include "maildir-uidlist.h"

#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>

static int do_open(struct index_mailbox *ibox, const char *path, void *context)
{
	int *fd = context;

	*fd = open(path, O_RDONLY);
	if (*fd != -1)
		return 1;
	if (errno == ENOENT)
		return 0;

	mail_storage_set_critical(ibox->box.storage,
				  "open(%s) failed: %m", path);
	return -1;
}

static int do_stat(struct index_mailbox *ibox, const char *path, void *context)
{
	struct stat *st = context;

	if (stat(path, st) == 0)
		return 1;
	if (errno == ENOENT)
		return 0;

	mail_storage_set_critical(ibox->box.storage,
				  "stat(%s) failed: %m", path);
	return -1;
}

static struct istream *
maildir_open_mail(struct index_mailbox *ibox, uint32_t uid, int *deleted)
{
	int fd;

	*deleted = FALSE;

	fd = -1;
	if (maildir_file_do(ibox, uid, do_open, &fd) < 0)
		return NULL;

	if (fd == -1) {
		*deleted = TRUE;
		return NULL;
	}

	if (ibox->mail_read_mmaped) {
		return i_stream_create_mmap(fd, default_pool,
					    MAIL_MMAP_BLOCK_SIZE, 0, 0, TRUE);
	} else {
		return i_stream_create_file(fd, default_pool,
					    MAIL_READ_BLOCK_SIZE, TRUE);
	}
}

static time_t maildir_mail_get_received_date(struct mail *_mail)
{
	struct index_mail *mail = (struct index_mail *) _mail;
	struct index_mail_data *data = &mail->data;
	struct stat st;
	int fd;

	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);

		if (fstat(fd, &st) < 0) {
			mail_storage_set_critical(mail->ibox->box.storage,
						  "fstat(maildir) failed: %m");
			return (time_t)-1;
		}
	} else {
		if (maildir_file_do(mail->ibox, mail->mail.uid,
				    do_stat, &st) <= 0)
			return (time_t)-1;
	}

	data->received_date = st.st_mtime;
	index_mail_cache_add(mail, MAIL_CACHE_RECEIVED_DATE,
			     &data->received_date, sizeof(data->received_date));
	return data->received_date;
}

static uoff_t maildir_mail_get_size(struct mail *_mail)
{
	struct index_mail *mail = (struct index_mail *) _mail;
	struct index_mail_data *data = &mail->data;
	const char *fname, *p;
	uoff_t virtual_size;
	int new_dir;

	if (data->size != (uoff_t)-1)
		return data->size;

	if ((mail->wanted_fields & MAIL_FETCH_SIZE) == 0) {
		data->size = index_mail_get_cached_virtual_size(mail);
		if (data->size != (uoff_t)-1)
			return data->size;
	}

	fname = maildir_uidlist_lookup(mail->ibox->uidlist,
				       mail->mail.uid, &new_dir);
	if (fname == NULL)
		return (uoff_t)-1;

	/* size can be included in filename */
	p = strstr(fname, ",W=");
	if (p != NULL) {
		p += 3;
		virtual_size = 0;
		while (*p >= '0' && *p <= '9') {
			virtual_size = virtual_size * 10 + (*p - '0');
			p++;
		}

		if (*p == ':' || *p == ',' || *p != '\0') {
			index_mail_cache_add(mail, MAIL_CACHE_VIRTUAL_FULL_SIZE,
					     &virtual_size,
					     sizeof(virtual_size));
			return virtual_size;
		}
	}

	return index_mail_get_size(_mail);
}

static struct istream *maildir_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;
	int deleted;

	if (data->stream == NULL) {
		data->stream = maildir_open_mail(mail->ibox, mail->mail.uid,
						 &deleted);
		if (data->stream == NULL)
			return NULL;
	}

	return index_mail_init_stream(mail, hdr_size, body_size);
}

struct mail maildir_mail = {
	0, 0, 0, 0, 0, 0,

	index_mail_get_flags,
	index_mail_get_parts,
	maildir_mail_get_received_date,
	index_mail_get_date,
	maildir_mail_get_size,
	index_mail_get_header,
	index_mail_get_headers,
	maildir_mail_get_stream,
	index_mail_get_special,
	index_mail_update_flags,
	index_mail_expunge
};

--- NEW FILE: maildir-sync.c ---
/*
   1. read files in maildir
   2. see if they're all found in uidlist read in memory
   3. if not, check if uidlist's mtime has changed and read it if so
   4. if still not, lock uidlist, sync it once more and generate UIDs for new
      files
   5. apply changes in transaction log
   6. apply changes in maildir to index
*/

/* Copyright (C) 2004 Timo Sirainen */

#include "lib.h"
#include "ioloop.h"
#include "buffer.h"
#include "hash.h"
#include "str.h"
#include "maildir-storage.h"
#include "maildir-uidlist.h"

#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>

#define MAILDIR_SYNC_SECS 1

#define MAILDIR_FILENAME_FLAG_FOUND 128

struct maildir_sync_context {
        struct index_mailbox *ibox;
	const char *new_dir, *cur_dir;

        struct maildir_uidlist_sync_ctx *uidlist_sync_ctx;
};

static int maildir_expunge(struct index_mailbox *ibox, const char *path,
			   void *context __attr_unused__)
{
	if (unlink(path) == 0)
		return 1;
	if (errno == ENOENT)
		return 0;

	mail_storage_set_critical(ibox->box.storage,
				  "unlink(%s) failed: %m", path);
	return -1;
}

static int maildir_sync_flags(struct index_mailbox *ibox, const char *path,
			      void *context)
{
	struct mail_index_sync_rec *syncrec = context;
	const char *newpath;
	enum mail_flags flags;
	uint8_t flags8;
        custom_flags_mask_t custom_flags;

	(void)maildir_filename_get_flags(path, &flags, custom_flags);

	flags8 = flags;
	mail_index_sync_flags_apply(syncrec, &flags8, custom_flags);

	newpath = maildir_filename_set_flags(path, flags8, custom_flags);
	if (rename(path, newpath) == 0)
		return 1;
	if (errno == ENOENT)
		return 0;

	mail_storage_set_critical(ibox->box.storage,
				  "rename(%s, %s) failed: %m", path, newpath);
	return -1;
}

static int maildir_sync_record(struct index_mailbox *ibox,
			       struct mail_index_view *view,
			       struct mail_index_sync_rec *syncrec)
{
        const struct mail_index_record *rec;
	uint32_t seq, uid;

	switch (syncrec->type) {
	case MAIL_INDEX_SYNC_TYPE_APPEND:
		break;
	case MAIL_INDEX_SYNC_TYPE_EXPUNGE:
		for (seq = syncrec->seq1; seq <= syncrec->seq2; seq++) {
			if (mail_index_lookup_uid(view, seq, &uid) < 0)
				return -1;
			if (maildir_file_do(ibox, uid, maildir_expunge,
					    NULL) < 0)
				return -1;
		}
		break;
	case MAIL_INDEX_SYNC_TYPE_FLAGS:
		for (seq = syncrec->seq1; seq <= syncrec->seq2; seq++) {
			if (mail_index_lookup_uid(view, seq, &uid) < 0)
				return -1;
			if (maildir_file_do(ibox, uid, maildir_sync_flags,
					    syncrec) < 0)
				return -1;
		}
		break;
	}

	return 0;
}

int maildir_sync_last_commit(struct index_mailbox *ibox)
{
        struct mail_index_view *view;
	struct mail_index_sync_ctx *sync_ctx;
	struct mail_index_sync_rec sync_rec;
	int ret;

	if (ibox->commit_log_file_seq == 0)
		return 0;

	ret = mail_index_sync_begin(ibox->index, &sync_ctx, &view,
				    ibox->commit_log_file_seq,
				    ibox->commit_log_file_offset);
	if (ret > 0) {
		while ((ret = mail_index_sync_next(sync_ctx, &sync_rec)) > 0) {
			if (maildir_sync_record(ibox, view, &sync_rec) < 0) {
				ret = -1;
				break;
			}
		}
		if (mail_index_sync_end(sync_ctx) < 0)
			ret = -1;
	}

	if (ret == 0) {
		ibox->commit_log_file_seq = 0;
		ibox->commit_log_file_offset = 0;
	} else {
		// FIXME: this is bad - we have to fix this in some way
		mail_storage_set_index_error(ibox);
	}
	return ret;
}

static struct maildir_sync_context *
maildir_sync_context_new(struct index_mailbox *ibox)
{
        struct maildir_sync_context *ctx;

	ctx = t_new(struct maildir_sync_context, 1);
	ctx->ibox = ibox;
	ctx->new_dir = t_strconcat(ibox->path, "/new", NULL);
	ctx->cur_dir = t_strconcat(ibox->path, "/cur", NULL);
	return ctx;
}

static void maildir_sync_deinit(struct maildir_sync_context *ctx)
{
	if (ctx->uidlist_sync_ctx != NULL)
		(void)maildir_uidlist_sync_deinit(ctx->uidlist_sync_ctx);
}

static int maildir_fix_duplicate(struct index_mailbox *ibox, const char *dir,
				 const char *old_fname)
{
	const char *new_fname, *old_path, *new_path;
	int ret = 0;

	t_push();

	old_path = t_strconcat(dir, "/", old_fname, NULL);
	new_fname = maildir_generate_tmp_filename(&ioloop_timeval);
	new_path = t_strconcat(ibox->path, "/new/", new_fname, NULL);

	if (rename(old_path, new_path) == 0) {
		i_warning("Fixed duplicate in %s: %s -> %s",
			  ibox->path, old_fname, new_fname);
	} else if (errno != ENOENT) {
		mail_storage_set_critical(ibox->box.storage,
			"rename(%s, %s) failed: %m", old_path, new_path);
		ret = -1;
	}
	t_pop();

	return ret;
}

static int maildir_scan_dir(struct maildir_sync_context *ctx, int new_dir)
{
	struct mail_storage *storage = ctx->ibox->box.storage;
	const char *dir;
	DIR *dirp;
	string_t *src, *dest;
	struct dirent *dp;
	int move_new, this_new, ret = 1;

	src = t_str_new(1024);
	dest = t_str_new(1024);

	dir = new_dir ? ctx->new_dir : ctx->cur_dir;
	dirp = opendir(dir);
	if (dirp == NULL) {
		mail_storage_set_critical(storage,
					  "opendir(%s) failed: %m", dir);
		return -1;
	}

	move_new = new_dir;
	while ((dp = readdir(dirp)) != NULL) {
		if (dp->d_name[0] == '.')
			continue;

		this_new = new_dir;
		if (move_new) {
			str_truncate(src, 0);
			str_truncate(dest, 0);
			str_printfa(src, "%s/%s", ctx->new_dir, dp->d_name);
			str_printfa(dest, "%s/%s", ctx->cur_dir, dp->d_name);
			if (rename(str_c(src), str_c(dest)) == 0 ||
			    ENOTFOUND(errno)) {
				/* moved - we'll look at it later in cur/ dir */
				this_new = FALSE;
				continue;
			} else if (ENOSPACE(errno)) {
				/* not enough disk space, leave here */
				move_new = FALSE;
			} else {
				mail_storage_set_critical(storage,
					"rename(%s, %s) failed: %m",
					str_c(src), str_c(dest));
			}
		}

		ret = maildir_uidlist_sync_next(ctx->uidlist_sync_ctx,
						dp->d_name, this_new);
		if (ret <= 0) {
			if (ret < 0)
				break;

			/* possibly duplicate - try fixing it */
			if (maildir_fix_duplicate(ctx->ibox,
						  dir, dp->d_name) < 0) {
				ret = -1;
				break;
			}
		}
	}

	if (closedir(dirp) < 0) {
		mail_storage_set_critical(storage,
					  "closedir(%s) failed: %m", dir);
	}
	return ret < 0 ? -1 : 0;
}

static int maildir_sync_quick_check(struct maildir_sync_context *ctx,
				    int *new_changed_r, int *cur_changed_r)
{
	struct index_mailbox *ibox = ctx->ibox;
	struct stat st;
	time_t new_mtime, cur_mtime;

	*new_changed_r = *cur_changed_r = FALSE;

	if (stat(ctx->new_dir, &st) < 0) {
		mail_storage_set_critical(ibox->box.storage,
					  "stat(%s) failed: %m", ctx->new_dir);
		return -1;
	}
	new_mtime = st.st_mtime;

	if (stat(ctx->cur_dir, &st) < 0) {
		mail_storage_set_critical(ibox->box.storage,
					  "stat(%s) failed: %m", ctx->cur_dir);
		return -1;
	}
	cur_mtime = st.st_mtime;

	if (new_mtime != ibox->last_new_mtime ||
	    new_mtime >= ibox->last_sync - MAILDIR_SYNC_SECS) {
		*new_changed_r = TRUE;
		ibox->last_new_mtime = new_mtime;
	}
	if (cur_mtime != ibox->last_cur_mtime ||
	    (cur_mtime >= ibox->last_sync - MAILDIR_SYNC_SECS &&
	     ioloop_time - ibox->last_sync > MAILDIR_SYNC_SECS)) {
		/* cur/ changed, or delayed cur/ check */
		*cur_changed_r = TRUE;
		ibox->last_cur_mtime = cur_mtime;
	}
	ibox->last_sync = ioloop_time;

	return 0;
}

static int maildir_sync_index(struct maildir_sync_context *ctx)
{
	struct index_mailbox *ibox = ctx->ibox;
	struct mail_index_sync_ctx *sync_ctx;
	struct mail_index_sync_rec sync_rec;
	struct maildir_uidlist_iter_ctx *iter;
	struct mail_index_transaction *trans;
	struct mail_index_view *view;
	const struct mail_index_header *hdr;
	const struct mail_index_record *rec;
	uint32_t seq, uid, uflags;
	const char *filename;
	enum mail_flags flags;
	custom_flags_mask_t custom_flags;
	int ret = 0;

	if (mail_index_sync_begin(ibox->index, &sync_ctx, &view,
				  (uint32_t)-1, (uoff_t)-1) <= 0) {
		// FIXME: ?
		return -1;
	}

	hdr = mail_index_get_header(view);
	trans = mail_index_transaction_begin(view, FALSE);

	seq = 0;
	iter = maildir_uidlist_iter_init(ibox->uidlist);
	while (maildir_uidlist_iter_next(iter, &uid, &uflags, &filename)) {
		maildir_filename_get_flags(filename, &flags, custom_flags);

	__again:
		seq++;
		if (seq > hdr->messages_count) {
			mail_index_append(trans, uid, &seq);
			mail_index_update_flags(trans, seq, MODIFY_REPLACE,
						flags, custom_flags);
			continue;
		}

		if (mail_index_lookup(view, seq, &rec) < 0) {
			mail_storage_set_index_error(ibox);
			ret = -1;
			break;
		}

		if (rec->uid < uid) {
			/* expunged */
			mail_index_expunge(trans, seq);
			goto __again;
		}

		if (rec->uid > uid) {
			/* new UID in the middle of the mailbox -
			   shouldn't happen */
			mail_storage_set_critical(ibox->box.storage,
				"Maildir sync: UID inserted in the middle "
				"of mailbox (%u > %u)", rec->uid, uid);
			(void)mail_index_reset(ibox->index);
			ret = -1;
			break;
		}

		maildir_filename_get_flags(filename, &flags, custom_flags);
		if (rec->flags & MAIL_RECENT)
			flags |= MAIL_RECENT;
		if ((uint8_t)flags != (rec->flags & MAIL_FLAGS_MASK) ||
		    memcmp(custom_flags, rec->custom_flags,
			   INDEX_CUSTOM_FLAGS_BYTE_COUNT) != 0) {
			mail_index_update_flags(trans, seq, MODIFY_REPLACE,
						flags, custom_flags);
		}
	}
	maildir_uidlist_iter_deinit(iter);

	if (ret < 0)
		mail_index_transaction_rollback(trans);
	else {
		uint32_t seq;
		uoff_t offset;

		if (mail_index_transaction_commit(trans, &seq, &offset) < 0)
			mail_storage_set_index_error(ibox);
		else {
			ibox->commit_log_file_seq = seq;
			ibox->commit_log_file_offset = offset;
		}
	}

	/* now, sync the index */
	while ((ret = mail_index_sync_next(sync_ctx, &sync_rec)) > 0) {
		if (maildir_sync_record(ibox, view, &sync_rec) < 0) {
			ret = -1;
			break;
		}
	}
	if (mail_index_sync_end(sync_ctx) < 0)
		ret = -1;

	if (ret == 0) {
		ibox->commit_log_file_seq = 0;
		ibox->commit_log_file_offset = 0;
	} else {
		// FIXME: this is bad - we have to fix this in some way
		mail_storage_set_index_error(ibox);
	}

	return ret;
}

static int maildir_sync_context(struct maildir_sync_context *ctx,
				int *changes_r)
{
	int ret, new_changed, cur_changed;

	if (maildir_sync_quick_check(ctx, &new_changed, &cur_changed) < 0)
		return -1;

	ctx->uidlist_sync_ctx = maildir_uidlist_sync_init(ctx->ibox->uidlist);

	if (maildir_scan_dir(ctx, TRUE) < 0)
		return -1;
	if (maildir_scan_dir(ctx, FALSE) < 0)
		return -1;

	ret = maildir_uidlist_sync_deinit(ctx->uidlist_sync_ctx);
        ctx->uidlist_sync_ctx = NULL;

	if (ret == 0)
		ret = maildir_sync_index(ctx);
	return ret;
}

static int maildir_sync_context_readonly(struct maildir_sync_context *ctx)
{
	int ret;

	ctx->uidlist_sync_ctx = maildir_uidlist_sync_init(ctx->ibox->uidlist);

	if (maildir_scan_dir(ctx, TRUE) < 0)
		return -1;
	if (maildir_scan_dir(ctx, FALSE) < 0)
		return -1;

	ret = maildir_uidlist_sync_deinit(ctx->uidlist_sync_ctx);
        ctx->uidlist_sync_ctx = NULL;

	return ret;
}

int maildir_storage_sync_readonly(struct index_mailbox *ibox)
{
        struct maildir_sync_context *ctx;
	int ret;

	ctx = maildir_sync_context_new(ibox);
	ret = maildir_sync_context_readonly(ctx);
	maildir_sync_deinit(ctx);
	return ret;
}

int maildir_storage_sync(struct mailbox *box, enum mailbox_sync_flags flags)
{
	struct index_mailbox *ibox = (struct index_mailbox *)box;
	struct maildir_sync_context *ctx;
	int changes, ret;

	if ((flags & MAILBOX_SYNC_FLAG_FAST) == 0 ||
	    ibox->sync_last_check + MAILBOX_FULL_SYNC_INTERVAL <= ioloop_time) {
		ibox->sync_last_check = ioloop_time;

		ctx = maildir_sync_context_new(ibox);
		ret = maildir_sync_context(ctx, &changes);
		maildir_sync_deinit(ctx);

		if (ret < 0)
			return -1;
	}

	return index_storage_sync(box, flags);
}

--- NEW FILE: maildir-transaction.c ---
/* Copyright (C) 2004 Timo Sirainen */

#include "lib.h"
#include "maildir-storage.h"

struct mailbox_transaction_context *
maildir_transaction_begin(struct mailbox *box, int hide)
{
	struct index_mailbox *ibox = (struct index_mailbox *)box;
	struct maildir_transaction_context *ctx;

	ctx = i_new(struct maildir_transaction_context, 1);
	ctx->ictx.mailbox_ctx.box = box;
	ctx->ictx.ibox = ibox;
	ctx->ictx.trans = mail_index_transaction_begin(ibox->view, hide);
	return &ctx->ictx.mailbox_ctx;
}

int maildir_transaction_commit(struct mailbox_transaction_context *_t)
{
	struct maildir_transaction_context *t =
		(struct maildir_transaction_context *)_t;
	struct index_mailbox *ibox = t->ictx.ibox;
	int ret = 0;

	if (t->save_ctx != NULL) {
		if (maildir_save_commit(t->save_ctx) < 0)
			ret = -1;
	}
	if (t->copy_ctx != NULL) {
		if (maildir_copy_commit(t->copy_ctx) < 0)
			ret = -1;
	}

	if (index_transaction_commit(_t) < 0)
		return -1;

	return ret < 0 ? -1 : maildir_sync_last_commit(ibox);
}

void maildir_transaction_rollback(struct mailbox_transaction_context *_t)
{
	struct maildir_transaction_context *t =
		(struct maildir_transaction_context *)_t;

	if (t->save_ctx != NULL)
		maildir_save_rollback(t->save_ctx);
	if (t->copy_ctx != NULL)
		maildir_copy_rollback(t->copy_ctx);
	index_transaction_rollback(_t);
}

--- NEW FILE: maildir-uidlist.c ---
/* Copyright (C) 2003 Timo Sirainen */

#include "lib.h"
#include "ioloop.h"
#include "buffer.h"
#include "hash.h"
#include "istream.h"
#include "str.h"
#include "file-dotlock.h"
#include "write-full.h"
#include "maildir-storage.h"
#include "maildir-uidlist.h"

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <utime.h>

/* how many seconds to wait before overriding uidlist.lock */
#define UIDLIST_LOCK_STALE_TIMEOUT (60*5)

#define UIDLIST_IS_LOCKED(uidlist) \
	((uidlist)->lock_fd != -1)

#define MAILDIR_UIDLIST_REC_FLAG_NEW_DIR 0x01

struct maildir_uidlist_rec {
	uint32_t uid;
	uint32_t flags;
	char *filename;
};

struct maildir_uidlist {
	struct index_mailbox *ibox;
	char *fname;
	int lock_fd;

	time_t last_mtime;

	pool_t filename_pool;
	buffer_t *record_buf;
	struct hash_table *files;

	unsigned int version;
	unsigned int uid_validity, next_uid, last_read_uid;
};

struct maildir_uidlist_sync_ctx {
	struct maildir_uidlist *uidlist;

	pool_t filename_pool;
	struct hash_table *files;

	struct maildir_uidlist_rec new_rec, cur_rec;
	unsigned int new_files:1;
	unsigned int synced:1;
	unsigned int failed:1;
};

struct maildir_uidlist_iter_ctx {
	const struct maildir_uidlist_rec *next, *end;
};

int maildir_uidlist_try_lock(struct maildir_uidlist *uidlist)
{
	const char *path;
	mode_t old_mask;
	int fd;

	if (UIDLIST_IS_LOCKED(uidlist))
		return 1;

	path = t_strconcat(uidlist->ibox->control_dir,
			   "/" MAILDIR_UIDLIST_NAME, NULL);
        old_mask = umask(0777 & ~uidlist->ibox->mail_create_mode);
	fd = file_dotlock_open(path, NULL, 0, 0, UIDLIST_LOCK_STALE_TIMEOUT,
			       NULL, NULL);
	umask(old_mask);
	if (fd == -1) {
		if (errno == EAGAIN)
			return 0;
		return -1;
	}

	uidlist->lock_fd = fd;
	return 1;
}

void maildir_uidlist_unlock(struct maildir_uidlist *uidlist)
{
	const char *path;

	if (!UIDLIST_IS_LOCKED(uidlist))
		return;

	path = t_strconcat(uidlist->ibox->control_dir,
			   "/" MAILDIR_UIDLIST_NAME, NULL);
	(void)file_dotlock_delete(path, uidlist->lock_fd);
	uidlist->lock_fd = -1;
}

struct maildir_uidlist *maildir_uidlist_init(struct index_mailbox *ibox)
{
	struct maildir_uidlist *uidlist;

	uidlist = i_new(struct maildir_uidlist, 1);
	uidlist->ibox = ibox;
	uidlist->fname =
		i_strconcat(ibox->control_dir, "/" MAILDIR_UIDLIST_NAME, NULL);
	uidlist->lock_fd = -1;
	uidlist->record_buf =
		buffer_create_dynamic(default_pool, 512, (size_t)-1);
	uidlist->files = hash_create(default_pool, default_pool, 4096,
				     maildir_hash, maildir_cmp);

	uidlist->uid_validity = ioloop_time;
	uidlist->next_uid = 1;

	return uidlist;
}

void maildir_uidlist_deinit(struct maildir_uidlist *uidlist)
{
	i_assert(!UIDLIST_IS_LOCKED(uidlist));

	if (uidlist->filename_pool != NULL)
		pool_unref(uidlist->filename_pool);

	hash_destroy(uidlist->files);
	buffer_free(uidlist->record_buf);
	i_free(uidlist->fname);
	i_free(uidlist);
}

static int maildir_uidlist_next(struct maildir_uidlist *uidlist,
				const char *line)
{
        struct maildir_uidlist_rec *rec;
	uint32_t uid, flags;

	uid = flags = 0;
	while (*line >= '0' && *line <= '9') {
		uid = uid*10 + (*line - '0');
		line++;
	}

	if (uid == 0 || *line != ' ') {
		/* invalid file */
                mail_storage_set_critical(uidlist->ibox->box.storage,
			"Invalid data in file %s", uidlist->fname);
		return 0;
	}
	if (uid <= uidlist->last_read_uid) {
                mail_storage_set_critical(uidlist->ibox->box.storage,
			"UIDs not ordered in file %s (%u > %u)",
			uidlist->fname, uid, uidlist->last_read_uid);
		return 0;
	}
	if (uid >= uidlist->next_uid) {
                mail_storage_set_critical(uidlist->ibox->box.storage,
			"UID larger than next_uid in file %s (%u >= %u)",
			uidlist->fname, uid, uidlist->next_uid);
		return 0;
	}

	while (*line == ' ') line++;

	flags = 0;
	if (uidlist->version > 1) {
		while (*line != ' ') {
			switch (*line) {
			case 'N':
				flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR;
				break;
			}
			line++;
		}
		while (*line == ' ') line++;
	} else {
		/* old version, have to assume it's in new dir since we
		   don't know */
		flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR;
	}

	if (hash_lookup(uidlist->files, line) != NULL) {
                mail_storage_set_critical(uidlist->ibox->box.storage,
			"Duplicate file in uidlist file %s: %s",
			uidlist->fname, line);
		return 0;
	}

	rec = buffer_append_space_unsafe(uidlist->record_buf, sizeof(*rec));
	rec->uid = uid;
	rec->flags = flags;
	rec->filename = p_strdup(uidlist->filename_pool, line);
	hash_insert(uidlist->files, rec->filename, rec);
	return 1;
}

int maildir_uidlist_update(struct maildir_uidlist *uidlist)
{
	struct mail_storage *storage = uidlist->ibox->box.storage;
	const char *line;
	struct istream *input;
	struct stat st;
	int fd, ret;

	if (uidlist->last_mtime != 0) {
		if (stat(uidlist->fname, &st) < 0) {
			if (errno != ENOENT) {
				mail_storage_set_critical(storage,
					"stat(%s) failed: %m", uidlist->fname);
				return -1;
			}
			return 0;
		}

		if (st.st_mtime == uidlist->last_mtime) {
			/* unchanged */
			return 1;
		}
	}

	fd = open(uidlist->fname, O_RDONLY);
	if (fd == -1) {
		if (errno != ENOENT) {
			mail_storage_set_critical(storage,
				"open(%s) failed: %m", uidlist->fname);
			return -1;
		}
		return 0;
	}

	if (fstat(fd, &st) < 0) {
		mail_storage_set_critical(storage,
			"fstat(%s) failed: %m", uidlist->fname);
		return -1;
	}

	if (uidlist->filename_pool != NULL)
		pool_unref(uidlist->filename_pool);
	uidlist->filename_pool =
		pool_alloconly_create("uidlist filename_pool",
				      nearest_power(st.st_size -
						    st.st_size/8));
	buffer_set_used_size(uidlist->record_buf, 0);
	uidlist->version = 0;

	input = i_stream_create_file(fd, default_pool, 4096, TRUE);

	/* get header */
	line = i_stream_read_next_line(input);
	if (line == NULL || sscanf(line, "%u %u %u", &uidlist->version,
				   &uidlist->uid_validity,
				   &uidlist->next_uid) != 3 ||
	    uidlist->version < 1 || uidlist->version > 2) {
		/* broken file */
                mail_storage_set_critical(storage,
			"Corrupted header in file %s (version = %u)",
			uidlist->fname, uidlist->version);
		ret = 0;
	} else {
		ret = 1;
		while ((line = i_stream_read_next_line(input)) != NULL) {
			if (!maildir_uidlist_next(uidlist, line)) {
				ret = 0;
				break;
			}
		}
	}

	if (ret != 0)
		uidlist->last_mtime = st.st_mtime;
	else {
		(void)unlink(uidlist->fname);
                uidlist->last_mtime = 0;
	}

	i_stream_unref(input);
	return ret;
}

const char *maildir_uidlist_lookup(struct maildir_uidlist *uidlist,
				   uint32_t uid, int *new_dir_r)
{
	const struct maildir_uidlist_rec *rec;
	unsigned int idx, left_idx, right_idx;
	size_t size;

	i_assert(uidlist->last_mtime != 0);

	rec = buffer_get_data(uidlist->record_buf, &size);
	size /= sizeof(*rec);

	idx = 0;
	left_idx = 0;
	right_idx = size;

	while (left_idx < right_idx) {
		idx = (left_idx + right_idx) / 2;

		if (rec[idx].uid < uid)
			left_idx = idx+1;
		else if (rec[idx].uid > uid)
			right_idx = idx;
		else {
			*new_dir_r = (rec[idx].flags &
				      MAILDIR_UIDLIST_REC_FLAG_NEW_DIR) != 0;
			return rec[idx].filename;
		}
	}

	return NULL;
}

static int maildir_uidlist_rewrite_fd(struct maildir_uidlist *uidlist,
				      const char *temp_path)
{
	struct mail_storage *storage = uidlist->ibox->box.storage;
	struct maildir_uidlist_iter_ctx *iter;
	struct utimbuf ut;
	string_t *str;
	uint32_t uid, flags;
	const char *filename, *flags_str;
	int ret = 0;

        uidlist->version = 2;

	str = t_str_new(4096);
	str_printfa(str, "%u %u %u\n", uidlist->version,
		    uidlist->uid_validity, uidlist->next_uid);

	iter = maildir_uidlist_iter_init(uidlist->ibox->uidlist);
	while (maildir_uidlist_iter_next(iter, &uid, &flags, &filename)) {
		if (str_len(str) + MAX_INT_STRLEN +
		    strlen(filename) + 2 >= 4096) {
			/* flush buffer */
			if (write_full(uidlist->lock_fd,
				       str_data(str), str_len(str)) < 0) {
				mail_storage_set_critical(storage,
					"write_full(%s) failed: %m", temp_path);
				ret = -1;
				break;
			}
			str_truncate(str, 0);
		}

		flags_str = (flags & MAILDIR_UIDLIST_REC_FLAG_NEW_DIR) != 0 ?
			"N" : "-";
		str_printfa(str, "%u %s %s\n", uid, flags_str, filename);
	}
	maildir_uidlist_iter_deinit(iter);

	if (ret < 0)
		return -1;

	if (write_full(uidlist->lock_fd, str_data(str), str_len(str)) < 0) {
		mail_storage_set_critical(storage,
			"write_full(%s) failed: %m", temp_path);
		return -1;
	}

	/* uidlist's mtime must grow every time */
	uidlist->last_mtime = ioloop_time <= uidlist->last_mtime ?
		uidlist->last_mtime + 1 : ioloop_time;
	ut.actime = ioloop_time;
	ut.modtime = uidlist->last_mtime;
	if (utime(temp_path, &ut) < 0) {
		mail_storage_set_critical(storage,
			"utime(%s) failed: %m", temp_path);
		return -1;
	}

	if (fsync(uidlist->lock_fd) < 0) {
		mail_storage_set_critical(storage,
			"fsync(%s) failed: %m", temp_path);
		return -1;
	}

	return 0;
}

static int maildir_uidlist_rewrite(struct maildir_uidlist *uidlist)
{
	struct index_mailbox *ibox = uidlist->ibox;
	const char *temp_path, *db_path;
	int ret;

	i_assert(UIDLIST_IS_LOCKED(uidlist));

	temp_path = t_strconcat(ibox->control_dir,
				"/" MAILDIR_UIDLIST_NAME ".lock", NULL);
	ret = maildir_uidlist_rewrite_fd(uidlist, temp_path);

	if (ret == 0) {
		db_path = t_strconcat(ibox->control_dir,
				      "/" MAILDIR_UIDLIST_NAME, NULL);

		if (file_dotlock_replace(db_path, uidlist->lock_fd,
					 FALSE) <= 0) {
			mail_storage_set_critical(ibox->box.storage,
				"file_dotlock_replace(%s) failed: %m", db_path);
			ret = -1;
		}
	} else {
		(void)close(uidlist->lock_fd);
	}
        uidlist->lock_fd = -1;

	if (ret < 0)
		(void)unlink(temp_path);
	return ret;
}

struct maildir_uidlist_sync_ctx *
maildir_uidlist_sync_init(struct maildir_uidlist *uidlist)
{
	struct maildir_uidlist_sync_ctx *ctx;

	ctx = i_new(struct maildir_uidlist_sync_ctx, 1);
	ctx->uidlist = uidlist;
	ctx->filename_pool =
		pool_alloconly_create("maildir_uidlist_sync", 16384);
	ctx->files = hash_create(default_pool, ctx->filename_pool, 4096,
				 maildir_hash, maildir_cmp);

	if (uidlist->last_mtime == 0) {
		/* uidlist not read yet, do it */
		if (maildir_uidlist_update(uidlist) < 0)
			ctx->failed = TRUE;
	}
	return ctx;
}

int maildir_uidlist_sync_next(struct maildir_uidlist_sync_ctx *ctx,
			      const char *filename, int new_dir)
{
	struct maildir_uidlist_rec *rec;
	char *fname;
	int ret;

	if (ctx->failed)
		return -1;

	rec = hash_lookup(ctx->files, filename);
	if (rec != NULL) {
		if ((rec->flags & MAILDIR_UIDLIST_REC_FLAG_NEW_DIR) == 0) {
			/* possibly duplicate */
			return 0;
		}

		rec->flags &= ~MAILDIR_UIDLIST_REC_FLAG_NEW_DIR;
	} else {
		rec = hash_lookup(ctx->uidlist->files, filename);
		if (rec == NULL && !ctx->synced) {
			/* lock and update uidlist to see if it's just
			   been added */
			ret = maildir_uidlist_try_lock(ctx->uidlist);
			if (ret <= 0) {
				ctx->failed = TRUE;
				return -1;
			}
			if (maildir_uidlist_update(ctx->uidlist) < 0) {
				ctx->failed = TRUE;
				return -1;
			}

			ctx->synced = TRUE;
			rec = hash_lookup(ctx->uidlist->files, filename);
		}

		if (rec == NULL) {
			ctx->new_files = TRUE;
			rec = new_dir ? &ctx->new_rec : &ctx->cur_rec;
		}
	}

	fname = p_strdup(ctx->filename_pool, filename);
	hash_insert(ctx->files, fname, rec);
	return 1;
}

static int maildir_time_cmp(const void *p1, const void *p2)
{
	const struct maildir_uidlist_rec *rec1 = p1, *rec2 = p2;
	const char *s1 = rec1->filename, *s2 = rec2->filename;
	time_t t1 = 0, t2 = 0;

	/* we have to do numeric comparision, strcmp() will break when
	   there's different amount of digits (mostly the 999999999 ->
	   1000000000 change in Sep 9 2001) */
	while (*s1 >= '0' && *s1 <= '9') {
		t1 = t1*10 + (*s1 - '0');
		s1++;
	}
	while (*s2 >= '0' && *s2 <= '9') {
		t2 = t2*10 + (*s2 - '0');
		s2++;
	}

	return t1 < t2 ? -1 : t1 > t2 ? 1 : 0;
}

static void maildir_uidlist_swap(struct maildir_uidlist_sync_ctx *ctx)
{
	struct maildir_uidlist *uidlist = ctx->uidlist;
	struct maildir_uidlist_rec *rec;
	struct hash_iterate_context *iter;
	void *key, *value;
	size_t size;
	unsigned int src, dest;

	rec = buffer_get_modifyable_data(uidlist->record_buf, &size);
	size /= sizeof(*rec);

	/* update filename pointers, skip deleted messages */
	for (dest = src = 0; src < size; src++) {
		if (hash_lookup_full(ctx->files, rec[src].filename,
				     &key, &value)) {
			rec[dest].uid = rec[src].uid;
			rec[dest].flags = rec[src].flags;
			rec[dest].filename = key;
			dest++;
		}
	}
	buffer_set_used_size(uidlist->record_buf, dest * sizeof(*rec));

	/* append new files */
	iter = hash_iterate_init(ctx->files);
	while (hash_iterate(iter, &key, &value)) {
		if (value == &ctx->new_rec ||
		    value == &ctx->cur_rec) {
			rec = buffer_append_space_unsafe(uidlist->record_buf,
							 sizeof(*rec));
			rec->flags = value == &ctx->cur_rec ?
				0 : MAILDIR_UIDLIST_REC_FLAG_NEW_DIR;
			rec->filename = key;
			hash_update(ctx->files, key, rec);
		}
	}
	hash_iterate_deinit(iter);

	rec = buffer_get_modifyable_data(uidlist->record_buf, &size);
	size /= sizeof(*rec);

	/* sort new files and assign UIDs for them */
	qsort(rec + dest, size - dest, sizeof(*rec), maildir_time_cmp);
	for (; dest < size; dest++)
		rec[dest].uid = uidlist->next_uid++;

	if (uidlist->filename_pool != NULL)
		pool_unref(uidlist->filename_pool);
	uidlist->filename_pool = ctx->filename_pool;
	ctx->filename_pool = NULL;

	hash_destroy(uidlist->files);
	uidlist->files = ctx->files;
	ctx->files = NULL;
}

int maildir_uidlist_sync_deinit(struct maildir_uidlist_sync_ctx *ctx)
{
	int ret;

	if (ctx->failed)
		ret = -1;
	else {
		maildir_uidlist_swap(ctx);
		if (!ctx->new_files)
			ret = 0;
		else
			ret = maildir_uidlist_rewrite(ctx->uidlist);
	}

	if (ctx->files != NULL)
		hash_destroy(ctx->files);
	if (ctx->filename_pool != NULL)
		pool_unref(ctx->filename_pool);
	i_free(ctx);
	return ret;
}

struct maildir_uidlist_iter_ctx *
maildir_uidlist_iter_init(struct maildir_uidlist *uidlist)
{
	struct maildir_uidlist_iter_ctx *ctx;
	size_t size;

	ctx = i_new(struct maildir_uidlist_iter_ctx, 1);
	ctx->next = buffer_get_data(uidlist->record_buf, &size);
	size /= sizeof(*ctx->next);
	ctx->end = ctx->next + size;
	return ctx;
}

int maildir_uidlist_iter_next(struct maildir_uidlist_iter_ctx *ctx,
			      uint32_t *uid_r, uint32_t *flags_r,
			      const char **filename_r)
{
	if (ctx->next == ctx->end)
		return 0;

	*uid_r = ctx->next->uid;
	*flags_r = ctx->next->flags;
	*filename_r = ctx->next->filename;
	ctx->next++;
	return 1;
}

void maildir_uidlist_iter_deinit(struct maildir_uidlist_iter_ctx *ctx)
{
	i_free(ctx);
}

--- NEW FILE: maildir-uidlist.h ---
#ifndef __MAILDIR_UIDLIST_H
#define __MAILDIR_UIDLIST_H

#define MAILDIR_UIDLIST_NAME "dovecot-uidlist"

int maildir_uidlist_try_lock(struct maildir_uidlist *uidlist);
void maildir_uidlist_unlock(struct maildir_uidlist *uidlist);

struct maildir_uidlist *maildir_uidlist_init(struct index_mailbox *ibox);
void maildir_uidlist_deinit(struct maildir_uidlist *uidlist);

/* Returns -1 if error, 0 if file is broken or lost, 1 if ok. */
int maildir_uidlist_update(struct maildir_uidlist *uidlist);

/* Returns uidlist record for given filename, or NULL if not found. */
const char *maildir_uidlist_lookup(struct maildir_uidlist *uidlist,
				   uint32_t uid, int *new_dir_r);

/* Sync uidlist with what's actually on maildir. */
struct maildir_uidlist_sync_ctx *
maildir_uidlist_sync_init(struct maildir_uidlist *uidlist);
int maildir_uidlist_sync_next(struct maildir_uidlist_sync_ctx *ctx,
			      const char *filename, int new_dir);
int maildir_uidlist_sync_deinit(struct maildir_uidlist_sync_ctx *ctx);

/* List all maildir files. */
struct maildir_uidlist_iter_ctx *
maildir_uidlist_iter_init(struct maildir_uidlist *uidlist);
int maildir_uidlist_iter_next(struct maildir_uidlist_iter_ctx *ctx,
			      uint32_t *uid_r, uint32_t *flags_r,
			      const char **filename_r);
void maildir_uidlist_iter_deinit(struct maildir_uidlist_iter_ctx *ctx);

#endif

--- NEW FILE: maildir-util.c ---
/* Copyright (C) 2004 Timo Sirainen */

#include "lib.h"
#include "hostpid.h"
#include "ioloop.h"
#include "str.h"
#include "maildir-storage.h"
#include "maildir-uidlist.h"

#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

static int maildir_file_do_try(struct index_mailbox *ibox, uint32_t uid,
			       maildir_file_do_func *func, void *context)
{
	const char *fname, *path;
	int ret, new_dir;

	fname = maildir_uidlist_lookup(ibox->uidlist, uid, &new_dir);
	if (fname == NULL)
		return -2; /* expunged */

	if (new_dir) {
		/* probably in new/ dir */
		path = t_strconcat(ibox->path, "/new/", fname, NULL);
		ret = func(ibox, path, context);
		if (ret != 0)
			return ret;
	}

	path = t_strconcat(ibox->path, "/cur/", fname, NULL);
	return func(ibox, path, context);
}

int maildir_file_do(struct index_mailbox *ibox, uint32_t uid,
		    maildir_file_do_func *func, void *context)
{
	int i, ret;

	ret = maildir_file_do_try(ibox, uid, func, context);
	for (i = 0; i < 10 && ret == 0; i++) {
		/* file is either renamed or deleted. sync the maildir and
		   see which one. if file appears to be renamed constantly,
		   don't try to open it more than 10 times. */
		if (maildir_storage_sync_readonly(ibox) < 0)
			return -1;

		ret = maildir_file_do_try(ibox, uid, func, context);
	}

	return ret == -2 ? 0 : ret;
}

int maildir_filename_get_flags(const char *fname, enum mail_flags *flags_r,
			       custom_flags_mask_t custom_flags_r)
{
	const char *info;
	unsigned int num;

	*flags_r = 0;
	memset(custom_flags_r, 0, INDEX_CUSTOM_FLAGS_BYTE_COUNT);

	info = strchr(fname, ':');
	if (info == NULL || info[1] != '2' || info[2] != ',')
		return 0;

	for (info += 3; *info != '\0' && *info != ','; info++) {
		switch (*info) {
		case 'R': /* replied */
			*flags_r |= MAIL_ANSWERED;
			break;
		case 'S': /* seen */
			*flags_r |= MAIL_SEEN;
			break;
		case 'T': /* trashed */
			*flags_r |= MAIL_DELETED;
			break;
		case 'D': /* draft */
			*flags_r |= MAIL_DRAFT;
			break;
		case 'F': /* flagged */
			*flags_r |= MAIL_FLAGGED;
			break;
		default:
			if (*info >= 'a' && *info <= 'z') {
				/* custom flag */
				num = (*info - 'a');
				custom_flags_r[num / CHAR_BIT] |=
					num % CHAR_BIT;
				break;
			}

			/* unknown flag - ignore */
			break;
		}
	}

	return 1;
}

const char *maildir_filename_set_flags(const char *fname, enum mail_flags flags,
				       custom_flags_mask_t custom_flags)
{
	string_t *flags_str;
	const char *info, *oldflags;
	int i, nextflag;

	if (custom_flags != NULL) {
		/* see if any custom flags are given */
		for (i = 0; i < INDEX_CUSTOM_FLAGS_BYTE_COUNT; i++) {
			if (custom_flags[i] != 0)
				break;
		}
		if (i == INDEX_CUSTOM_FLAGS_BYTE_COUNT)
			custom_flags = NULL;
	}

	/* remove the old :info from file name, and get the old flags */
	info = strrchr(fname, ':');
	if (info != NULL && strrchr(fname, '/') > info)
		info = NULL;

	oldflags = "";
	if (info != NULL) {
		fname = t_strdup_until(fname, info);
		if (info[1] == '2' && info[2] == ',')
			oldflags = info+3;
	}

	/* insert the new flags between old flags. flags must be sorted by
	   their ASCII code. unknown flags are kept. */
	flags_str = t_str_new(256);
	str_append(flags_str, fname);
	str_append(flags_str, ":2,");
	for (;;) {
		/* skip all known flags */
		while (*oldflags == 'D' || *oldflags == 'F' ||
		       *oldflags == 'R' || *oldflags == 'S' ||
		       *oldflags == 'T' ||
		       (*oldflags >= 'a' && *oldflags <= 'z'))
			oldflags++;

		nextflag = *oldflags == '\0' || *oldflags == ',' ? 256 :
			(unsigned char) *oldflags;

		if ((flags & MAIL_DRAFT) && nextflag > 'D') {
			str_append_c(flags_str, 'D');
			flags &= ~MAIL_DRAFT;
		}
		if ((flags & MAIL_FLAGGED) && nextflag > 'F') {
			str_append_c(flags_str, 'F');
			flags &= ~MAIL_FLAGGED;
		}
		if ((flags & MAIL_ANSWERED) && nextflag > 'R') {
			str_append_c(flags_str, 'R');
			flags &= ~MAIL_ANSWERED;
		}
		if ((flags & MAIL_SEEN) && nextflag > 'S') {
			str_append_c(flags_str, 'S');
			flags &= ~MAIL_SEEN;
		}
		if ((flags & MAIL_DELETED) && nextflag > 'T') {
			str_append_c(flags_str, 'T');
			flags &= ~MAIL_DELETED;
		}

		if (custom_flags != NULL && nextflag > 'a') {
			for (i = 0; i < INDEX_CUSTOM_FLAGS_COUNT; i++) {
				if ((custom_flags[i / CHAR_BIT] &
				     (1 << (i % CHAR_BIT))) != 0)
					str_append_c(flags_str, 'a' + i);
			}
			custom_flags = NULL;
		}

		if (*oldflags == '\0' || *oldflags == ',')
			break;

		str_append_c(flags_str, *oldflags);
		oldflags++;
	}

	if (*oldflags == ',') {
		/* another flagset, we don't know about these, just keep them */
		while (*oldflags != '\0')
			str_append_c(flags_str, *oldflags++);
	}

	return str_c(flags_str);
}

const char *maildir_generate_tmp_filename(const struct timeval *tv)
{
	static unsigned int create_count = 0;
	static time_t first_stamp = 0;

	if (first_stamp == 0 || first_stamp == ioloop_time) {
		/* it's possible that within last second another process had
		   the same UID as us. Use usecs to make sure we don't create
		   duplicate base name. */
		first_stamp = ioloop_time;
		return t_strdup_printf("%s.P%sQ%uM%s.%s",
				       dec2str(tv->tv_sec), my_pid,
				       create_count++,
				       dec2str(tv->tv_usec), my_hostname);
	} else {
		/* Don't bother with usecs. Saves a bit space :) */
		return t_strdup_printf("%s.P%sQ%u.%s",
				       dec2str(tv->tv_sec), my_pid,
				       create_count++, my_hostname);
	}
}

int maildir_create_tmp(struct index_mailbox *ibox, const char *dir,
		       mode_t mode, const char **fname_r)
{
	const char *path, *tmp_fname;
	struct stat st;
	struct timeval *tv, tv_now;
	pool_t pool;
	int fd;

	tv = &ioloop_timeval;
	pool = pool_alloconly_create("maildir_tmp", 4096);
	for (;;) {
		p_clear(pool);
		tmp_fname = maildir_generate_tmp_filename(tv);

		path = p_strconcat(pool, dir, "/", tmp_fname, NULL);
		if (stat(path, &st) < 0 && errno == ENOENT) {
			/* doesn't exist */
			mode_t old_mask = umask(0);
			fd = open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
			umask(old_mask);
			if (fd != -1 || errno != EEXIST)
				break;
		}

		/* wait and try again - very unlikely */
		sleep(2);
		tv = &tv_now;
		if (gettimeofday(&tv_now, NULL) < 0)
			i_fatal("gettimeofday(): %m");
	}

	*fname_r = t_strdup(path);
	if (fd == -1) {
		mail_storage_set_critical(ibox->box.storage,
					  "open(%s) failed: %m", path);
	}

	pool_unref(pool);
	return fd;
}

/* a char* hash function from ASU -- from glib */
unsigned int maildir_hash(const void *p)
{
        const unsigned char *s = p;
	unsigned int g, h = 0;

	while (*s != ':' && *s != '\0') {
		h = (h << 4) + *s;
		if ((g = h & 0xf0000000UL)) {
			h = h ^ (g >> 24);
			h = h ^ g;
		}

		s++;
	}

	return h;
}

int maildir_cmp(const void *p1, const void *p2)
{
	const char *s1 = p1, *s2 = p2;

	while (*s1 == *s2 && *s1 != ':' && *s1 != '\0') {
		s1++; s2++;
	}
	if ((*s1 == '\0' || *s1 == ':') &&
	    (*s2 == '\0' || *s2 == ':'))
		return 0;
	return *s1 - *s2;
}

Index: Makefile.am
===================================================================
RCS file: /home/cvs/dovecot/src/lib-storage/index/maildir/Makefile.am,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -d -r1.1.1.1 -r1.2
--- Makefile.am	9 Aug 2002 09:16:02 -0000	1.1.1.1
+++ Makefile.am	27 Apr 2004 20:25:54 -0000	1.2
@@ -11,10 +11,15 @@
 
 libstorage_maildir_a_SOURCES = \
 	maildir-copy.c \
-	maildir-expunge.c \
 	maildir-list.c \
+	maildir-mail.c \
 	maildir-save.c \
-	maildir-storage.c
+	maildir-storage.c \
+	maildir-sync.c \
+	maildir-transaction.c \
+	maildir-uidlist.c \
+	maildir-util.c
 
 noinst_HEADERS = \
-	maildir-storage.h
+	maildir-storage.h \
+	maildir-uidlist.h

Index: maildir-copy.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib-storage/index/maildir/maildir-copy.c,v
retrieving revision 1.29
retrieving revision 1.30
diff -u -d -r1.29 -r1.30
--- maildir-copy.c	10 Aug 2003 23:56:23 -0000	1.29
+++ maildir-copy.c	27 Apr 2004 20:25:54 -0000	1.30
@@ -1,12 +1,9 @@
-/* Copyright (C) 2002 Timo Sirainen */
+/* Copyright (C) 2002-2004 Timo Sirainen */
 
 #include "lib.h"
 #include "ioloop.h"
-#include "maildir-index.h"
 #include "maildir-storage.h"
-#include "mail-custom-flags.h"
-#include "mail-index-util.h"
-#include "index-messageset.h"
+#include "mail-save.h"
 
 #include <stdlib.h>
 #include <unistd.h>
@@ -17,8 +14,6 @@
 
 	pool_t pool;
 	struct rollback *rollbacks;
-
-	struct mail_copy_context *ctx;
 };
 
 struct hardlink_ctx {
@@ -31,7 +26,7 @@
 	const char *fname;
 };
 
-static int do_hardlink(struct mail_index *index, const char *path,
+static int do_hardlink(struct index_mailbox *ibox, const char *path,
 		       void *context)
 {
 	struct hardlink_ctx *ctx = context;
@@ -41,14 +36,16 @@
 			return 0;
 
 		if (ENOSPACE(errno)) {
-			index->nodiskspace = TRUE;
+			mail_storage_set_error(ibox->box.storage,
+					       "Not enough disk space");
 			return -1;
 		}
 		if (errno == EACCES || errno == EXDEV)
 			return 1;
 
-		index_set_error(index, "link(%s, %s) failed: %m",
-				path, ctx->dest_path);
+		mail_storage_set_critical(ibox->box.storage,
+					  "link(%s, %s) failed: %m",
+					  path, ctx->dest_path);
 		return -1;
 	}
 
@@ -62,26 +59,24 @@
 	struct index_mail *imail = (struct index_mail *) mail;
 	struct hardlink_ctx do_ctx;
 	struct rollback *rb;
+	const struct mail_full_flags *flags;
 	const char *dest_fname;
 
+        flags = mail->get_flags(mail);
 	dest_fname = maildir_generate_tmp_filename(&ioloop_timeval);
-	dest_fname = maildir_filename_set_flags(dest_fname,
-						mail->get_flags(mail)->flags);
+	dest_fname = maildir_filename_set_flags(dest_fname, flags->flags, NULL);
 
 	memset(&do_ctx, 0, sizeof(do_ctx));
-	do_ctx.dest_path = t_strconcat(ctx->ibox->index->mailbox_path, "/new/",
-				       dest_fname, NULL);
+	do_ctx.dest_path =
+		t_strconcat(ctx->ibox->path, "/new/", dest_fname, NULL);
 
-	if (!maildir_file_do(imail->ibox->index, imail->data.rec,
-			     do_hardlink, &do_ctx))
+	if (maildir_file_do(imail->ibox, imail->mail.uid,
+			    do_hardlink, &do_ctx) < 0)
 		return -1;
 
 	if (!do_ctx.found)
 		return 0;
 
-	if (ctx->pool == NULL)
-		ctx->pool = pool_alloconly_create("hard copy rollbacks", 2048);
-
 	rb = p_new(ctx->pool, struct rollback, 1);
 	rb->fname = p_strdup(ctx->pool, dest_fname);
 
@@ -90,68 +85,64 @@
 	return 1;
 }
 
-struct mail_copy_context *maildir_storage_copy_init(struct mailbox *box)
+static struct maildir_copy_context *
+maildir_copy_init(struct index_mailbox *ibox)
 {
-	struct index_mailbox *ibox = (struct index_mailbox *) box;
 	struct maildir_copy_context *ctx;
+	pool_t pool;
 
-	if (box->is_readonly(box)) {
-		mail_storage_set_error(box->storage,
-				       "Destination mailbox is read-only");
-		return NULL;
-	}
+	pool = pool_alloconly_create("maildir_copy_context", 2048);
 
-	ctx = i_new(struct maildir_copy_context, 1);
+	ctx = p_new(pool, struct maildir_copy_context, 1);
+	ctx->pool = pool;
 	ctx->hardlink = getenv("MAILDIR_COPY_WITH_HARDLINKS") != NULL;
 	ctx->ibox = ibox;
-	return (struct mail_copy_context *) ctx;
+	return ctx;
 }
 
-int maildir_storage_copy_deinit(struct mail_copy_context *_ctx, int rollback)
+int maildir_copy_commit(struct maildir_copy_context *ctx)
 {
-	struct maildir_copy_context *ctx = (struct maildir_copy_context *) _ctx;
-        struct rollback *rb;
-	int ret = TRUE;
+	pool_unref(ctx->pool);
+	return 0;
+}
 
-	if (ctx->ctx != NULL)
-		ret = index_storage_copy_deinit(ctx->ctx, rollback);
+void maildir_copy_rollback(struct maildir_copy_context *ctx)
+{
+        struct rollback *rb;
 
-	if (rollback) {
-		for (rb = ctx->rollbacks; rb != NULL; rb = rb->next) {
-			t_push();
-			(void)unlink(t_strconcat(ctx->ibox->index->mailbox_path,
-						 "/new/", rb->fname, NULL));
-			t_pop();
-		}
+	for (rb = ctx->rollbacks; rb != NULL; rb = rb->next) {
+		t_push();
+		(void)unlink(t_strconcat(ctx->ibox->path,
+					 "/new/", rb->fname, NULL));
+		t_pop();
 	}
 
-	if (ctx->pool != NULL)
-		pool_unref(ctx->pool);
-
-	i_free(ctx);
-	return ret;
+	pool_unref(ctx->pool);
 }
 
-int maildir_storage_copy(struct mail *mail, struct mail_copy_context *_ctx)
+int maildir_copy(struct mailbox_transaction_context *_t, struct mail *mail)
 {
-	struct maildir_copy_context *ctx = (struct maildir_copy_context *) _ctx;
+	struct maildir_transaction_context *t =
+		(struct maildir_transaction_context *)_t;
+	struct maildir_copy_context *ctx;
 	int ret;
 
+	if (t->copy_ctx == NULL)
+		t->copy_ctx = maildir_copy_init(t->ictx.ibox);
+	ctx = t->copy_ctx;
+
 	if (ctx->hardlink && mail->box->storage == ctx->ibox->box.storage) {
 		t_push();
 		ret = maildir_copy_hardlink(mail, ctx);
 		t_pop();
 
 		if (ret > 0)
-			return TRUE;
+			return 0;
 		if (ret < 0)
-			return FALSE;
+			return -1;
 
 		/* non-fatal hardlinking failure, try the slow way */
 	}
 
-	if (ctx->ctx == NULL)
-		ctx->ctx = index_storage_copy_init(&ctx->ibox->box);
-
-	return index_storage_copy(mail, ctx->ctx);
+	return mail_storage_copy(_t, mail);
 }

Index: maildir-list.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib-storage/index/maildir/maildir-list.c,v
retrieving revision 1.25
retrieving revision 1.26
diff -u -d -r1.25 -r1.26
--- maildir-list.c	27 Jul 2003 04:35:44 -0000	1.25
+++ maildir-list.c	27 Apr 2004 20:25:54 -0000	1.26
@@ -15,7 +15,8 @@
 
 #define MAILBOX_FLAG_MATCHED 0x40000000
 
-struct mailbox_list_context {
+struct maildir_list_context {
+	struct mailbox_list_context mailbox_ctx;
 	pool_t pool;
 
 	struct mail_storage *storage;
@@ -48,7 +49,7 @@
 	}
 }
 
-static int maildir_fill_readdir(struct mailbox_list_context *ctx,
+static int maildir_fill_readdir(struct maildir_list_context *ctx,
 				struct imap_match_glob *glob, int update_only)
 {
 	DIR *dirp;
@@ -180,15 +181,19 @@
 	return TRUE;
 }
 
-static int maildir_fill_subscribed(struct mailbox_list_context *ctx,
+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 subsfile_list_context *subsfile_ctx;
-	const char *name, *p;
+	const char *path, *name, *p;
 	struct mailbox_node *node;
 	int created;
 
-	subsfile_ctx = subsfile_list_init(ctx->storage);
+	path = t_strconcat(istorage->control_dir != NULL ?
+			   istorage->control_dir : istorage->dir,
+			   "/" SUBSCRIPTION_FILE_NAME, NULL);
+	subsfile_ctx = subsfile_list_init(ctx->storage, path);
 	if (subsfile_ctx == NULL)
 		return FALSE;
 
@@ -227,10 +232,11 @@
 }
 
 struct mailbox_list_context *
-maildir_list_mailbox_init(struct mail_storage *storage,
+maildir_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 maildir_list_context *ctx;
         struct imap_match_glob *glob;
 	const char *dir, *p;
 	pool_t pool;
@@ -238,7 +244,7 @@
 	mail_storage_clear_error(storage);
 
 	pool = pool_alloconly_create("maildir_list", 1024);
-	ctx = p_new(pool, struct mailbox_list_context, 1);
+	ctx = p_new(pool, struct maildir_list_context, 1);
 	ctx->pool = pool;
 	ctx->storage = storage;
 	ctx->flags = flags;
@@ -247,15 +253,15 @@
 	if (storage->hierarchy_sep != MAILDIR_FS_SEP &&
 	    strchr(mask, MAILDIR_FS_SEP) != NULL) {
 		/* this will never match, return nothing */
-		return ctx;
+		return &ctx->mailbox_ctx;
 	}
 
-	mask = maildir_fix_mailbox_name(storage, mask, FALSE);
+	mask = maildir_fix_mailbox_name(istorage, mask, FALSE);
 	glob = imap_match_init(pool, mask, TRUE, MAILDIR_FS_SEP);
 
-	ctx->dir = storage->dir;
+	ctx->dir = istorage->dir;
 	ctx->prefix = storage->namespace == NULL ? "" :
-		maildir_fix_mailbox_name(storage, storage->namespace, FALSE);
+		maildir_fix_mailbox_name(istorage, storage->namespace, FALSE);
 
 	if ((flags & MAILBOX_LIST_SUBSCRIBED) != 0) {
 		if (!maildir_fill_subscribed(ctx, glob)) {
@@ -269,7 +275,7 @@
 					  t_strdup_until(mask, p+1), NULL);
 
 		if (*mask != '/' && *mask != '~')
-			dir = t_strconcat(storage->dir, "/", dir, NULL);
+			dir = t_strconcat(istorage->dir, "/", dir, NULL);
 		ctx->dir = p_strdup(pool, home_expand(dir));
 	}
 
@@ -286,11 +292,14 @@
 	ctx->prefix = p_strdup(pool, ctx->prefix);
 	ctx->node_path = str_new(pool, 256);
 	ctx->root = mailbox_tree_get(ctx->tree_ctx, NULL, NULL);
-	return ctx;
+	ctx->mailbox_ctx.storage = storage;
+	return &ctx->mailbox_ctx;
 }
 
-int maildir_list_mailbox_deinit(struct mailbox_list_context *ctx)
+int maildir_mailbox_list_deinit(struct mailbox_list_context *_ctx)
 {
+	struct maildir_list_context *ctx = (struct maildir_list_context *)_ctx;
+
 	mailbox_tree_deinit(ctx->tree_ctx);
 	pool_unref(ctx->pool);
 	return TRUE;
@@ -327,8 +336,9 @@
 }
 
 struct mailbox_list *
-maildir_list_mailbox_next(struct mailbox_list_context *ctx)
+maildir_mailbox_list_next(struct mailbox_list_context *_ctx)
 {
+	struct maildir_list_context *ctx = (struct maildir_list_context *)_ctx;
 	struct mailbox_node *node;
 
 	for (node = ctx->next_node; node != NULL; node = node->next) {

Index: maildir-save.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib-storage/index/maildir/maildir-save.c,v
retrieving revision 1.30
retrieving revision 1.31
diff -u -d -r1.30 -r1.31
--- maildir-save.c	20 Oct 2003 06:18:59 -0000	1.30
+++ maildir-save.c	27 Apr 2004 20:25:54 -0000	1.31
@@ -1,9 +1,8 @@
-/* Copyright (C) 2002 Timo Sirainen */
+/* Copyright (C) 2002-2004 Timo Sirainen */
 
 #include "lib.h"
 #include "ioloop.h"
 #include "ostream.h"
-#include "maildir-index.h"
 #include "maildir-storage.h"
 #include "mail-save.h"
 
@@ -14,19 +13,18 @@
 #include <utime.h>
 #include <sys/stat.h>
 
-struct mail_filename {
-	struct mail_filename *next;
+struct maildir_filename {
+	struct maildir_filename *next;
 	const char *src, *dest;
 };
 
-struct mail_save_context {
+struct maildir_save_context {
 	pool_t pool;
 
 	struct index_mailbox *ibox;
-	int transaction;
 
 	const char *tmpdir, *newdir;
-	struct mail_filename *files;
+	struct maildir_filename *files;
 };
 
 static const char *
@@ -37,8 +35,7 @@
 	struct ostream *output;
 	int fd;
 
-	fd = maildir_create_tmp(ibox->index, dir,
-				ibox->index->mail_create_mode, &path);
+	fd = maildir_create_tmp(ibox, dir, ibox->mail_create_mode, &path);
 	if (fd == -1)
 		return NULL;
 
@@ -46,12 +43,11 @@
 	i_assert(fname != NULL);
 	fname++;
 
-	t_push();
 	output = o_stream_create_file(fd, pool_datastack_create(), 4096, FALSE);
 	o_stream_set_blocking(output, 60000, NULL, NULL);
 
-	if (!mail_storage_save(ibox->box.storage, path, input, output,
-                               getenv("MAIL_SAVE_CRLF") != NULL, NULL, NULL))
+	if (mail_storage_save(ibox->box.storage, path, input, output,
+			      getenv("MAIL_SAVE_CRLF") != NULL, NULL, NULL) < 0)
 		fname = NULL;
 
 	o_stream_unref(output);
@@ -70,15 +66,14 @@
 
 	if (fname == NULL)
 		(void)unlink(path);
-	t_pop();
 	return fname;
 }
 
-static int maildir_copy(struct mail_save_context *ctx,
-			const char *src, const char *dest)
+static int maildir_file_move(struct maildir_save_context *ctx,
+			     const char *src, const char *dest)
 {
 	const char *tmp_path, *new_path;
-	int failed;
+	int ret;
 
 	t_push();
 
@@ -86,147 +81,147 @@
 	new_path = t_strconcat(ctx->newdir, "/", dest, NULL);
 
 	if (link(tmp_path, new_path) == 0)
-		failed = FALSE;
+		ret = 0;
 	else {
-		failed = TRUE;
+		ret = -1;
 		if (ENOSPACE(errno)) {
 			mail_storage_set_error(ctx->ibox->box.storage,
 					       "Not enough disk space");
 		} else {
 			mail_storage_set_critical(ctx->ibox->box.storage,
-						  "link(%s, %s) failed: %m",
-						  tmp_path, new_path);
+				"link(%s, %s) failed: %m", tmp_path, new_path);
 		}
 	}
 
-	(void)unlink(tmp_path);
+	if (unlink(tmp_path) < 0 && errno != ENOENT) {
+		mail_storage_set_critical(ctx->ibox->box.storage,
+			"unlink(%s) failed: %m", tmp_path);
+	}
 	t_pop();
-	return !failed;
+	return ret;
 }
 
-int maildir_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)
+static struct maildir_save_context *
+mailbox_save_init(struct index_mailbox *ibox)
 {
+	struct maildir_save_context *ctx;
+	pool_t pool;
+
+	pool = pool_alloconly_create("maildir_save_context", 4096);
+	ctx = p_new(pool, struct maildir_save_context, 1);
+	ctx->pool = pool;
+	ctx->ibox = ibox;
+
+	ctx->tmpdir = p_strconcat(pool, ibox->path, "/tmp", NULL);
+	ctx->newdir = p_strconcat(pool, ibox->path, "/new", NULL);
+	return ctx;
+}
+
+int maildir_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 __attr_unused__,
+		 struct istream *data)
+{
+	struct maildir_transaction_context *t =
+		(struct maildir_transaction_context *)_t;
+	struct maildir_save_context *ctx;
+	struct index_mailbox *ibox = t->ictx.ibox;
+	struct maildir_filename *mf;
 	enum mail_flags mail_flags;
         struct utimbuf buf;
 	const char *fname, *dest_fname, *tmp_path;
-	int failed;
+
+	if (t->save_ctx == NULL)
+		t->save_ctx = mailbox_save_init(ibox);
+	ctx = t->save_ctx;
 
 	mail_flags = flags->flags;
-	if (!index_mailbox_fix_custom_flags(ctx->ibox, &mail_flags,
+	/*FIXME:if (!index_mailbox_fix_custom_flags(ibox, &mail_flags,
 					    flags->custom_flags,
 					    flags->custom_flags_count))
-		return FALSE;
+		return FALSE;*/
 
 	t_push();
 
 	/* create the file into tmp/ directory */
-	fname = maildir_read_into_tmp(ctx->ibox, ctx->tmpdir, data);
+	fname = maildir_read_into_tmp(ibox, ctx->tmpdir, data);
 	if (fname == NULL) {
 		t_pop();
-		return FALSE;
+		return -1;
 	}
 
 	tmp_path = t_strconcat(ctx->tmpdir, "/", fname, NULL);
 
-	/* set the received_date by modifying mtime */
-	buf.actime = ioloop_time;
-	buf.modtime = received_date;
-	if (utime(tmp_path, &buf) < 0) {
-		mail_storage_set_critical(ctx->ibox->box.storage,
-					  "utime() failed for %s: %m",
-					  tmp_path);
-		t_pop();
-		return FALSE;
+	if (received_date != (time_t)-1) {
+		/* set the received_date by modifying mtime */
+		buf.actime = ioloop_time;
+		buf.modtime = received_date;
+		if (utime(tmp_path, &buf) < 0) {
+			mail_storage_set_critical(ibox->box.storage,
+				"utime(%s) failed: %m", tmp_path);
+			t_pop();
+			return -1;
+		}
 	}
 
-	/* now, if we want to be able to rollback the whole append session,
-	   we'll just store the name of this temp file and move it later
+	/* now, we want to be able to rollback the whole append session,
+	   so we'll just store the name of this temp file and move it later
 	   into new/ */
 	dest_fname = mail_flags == 0 ? fname :
-		maildir_filename_set_flags(fname, mail_flags);
-	if (ctx->transaction) {
-		struct mail_filename *mf;
-
-		mf = p_new(ctx->pool, struct mail_filename, 1);
-		mf->next = ctx->files;
-		mf->src = p_strdup(ctx->pool, fname);
-		mf->dest = p_strdup(ctx->pool, dest_fname);
-		ctx->files = mf;
+		maildir_filename_set_flags(fname, mail_flags, NULL);
 
-		failed = FALSE;
-	} else {
-		failed = !maildir_copy(ctx, fname, dest_fname);
-	}
+	mf = p_new(ctx->pool, struct maildir_filename, 1);
+	mf->next = ctx->files;
+	mf->src = p_strdup(ctx->pool, fname);
+	mf->dest = p_strdup(ctx->pool, dest_fname);
+	ctx->files = mf;
 
 	t_pop();
-	return !failed;
+	return 0;
 }
 
-struct mail_save_context *
-maildir_storage_save_init(struct mailbox *box, int transaction)
+int maildir_save_commit(struct maildir_save_context *ctx)
 {
-	struct index_mailbox *ibox = (struct index_mailbox *) box;
-	struct mail_save_context *ctx;
-	pool_t pool;
+	struct maildir_filename *mf, *mf2;
+	const char *path;
+	int ret = 0;
 
-	if (box->is_readonly(box)) {
-		mail_storage_set_error(box->storage, "Mailbox is read-only");
-		return NULL;
+	/* move them into new/ */
+	for (mf = ctx->files; mf != NULL; mf = mf->next) {
+		if (maildir_file_move(ctx, mf->src, mf->dest) < 0) {
+			ret = -1;
+			break;
+		}
 	}
 
-	pool = pool_alloconly_create("mail_save_context", 4096);
-	ctx = p_new(pool, struct mail_save_context, 1);
-	ctx->pool = pool;
-	ctx->ibox = ibox;
-	ctx->transaction = transaction;
-
-	ctx->tmpdir = p_strconcat(pool, ibox->index->mailbox_path,
-				  "/tmp", NULL);
-	ctx->newdir = p_strconcat(pool, ibox->index->mailbox_path,
-				  "/new", NULL);
+	if (ret < 0) {
+		/* failed, try to unlink the mails already moved */
+		for (mf2 = ctx->files; mf2 != mf; mf2 = mf2->next) {
+			t_push();
+			path = t_strconcat(ctx->newdir, "/",
+					   mf2->dest, NULL);
+			(void)unlink(path);
+			t_pop();
+		}
+	}
 
-	return ctx;
+	pool_unref(ctx->pool);
+	return ret;
 }
 
-int maildir_storage_save_deinit(struct mail_save_context *ctx, int rollback)
+void maildir_save_rollback(struct maildir_save_context *ctx)
 {
-	struct mail_filename *mf, *mf2;
+	struct maildir_filename *mf;
 	const char *path;
-	int failed = FALSE;
-
-	if (rollback) {
-		/* clean up the temp files */
-		for (mf = ctx->files; mf != NULL; mf = mf->next) {
-			t_push();
-			path = t_strconcat(ctx->tmpdir, "/", mf->dest, NULL);
-			(void)unlink(path);
-			t_pop();
-		}
-	} else {
-		/* move them into new/ */
-		for (mf = ctx->files; mf != NULL; mf = mf->next) {
-			if (!maildir_copy(ctx, mf->src, mf->dest)) {
-				failed = TRUE;
-				break;
-			}
-		}
 
-		if (failed) {
-			/* failed, try to unlink the mails already moved */
-			for (mf2 = ctx->files; mf2 != mf; mf2 = mf2->next) {
-				t_push();
-				path = t_strconcat(ctx->newdir, "/",
-						   mf2->dest, NULL);
-				(void)unlink(path);
-				t_pop();
-			}
-		}
+	/* clean up the temp files */
+	for (mf = ctx->files; mf != NULL; mf = mf->next) {
+		t_push();
+		path = t_strconcat(ctx->tmpdir, "/", mf->dest, NULL);
+		(void)unlink(path);
+		t_pop();
 	}
 
 	pool_unref(ctx->pool);
-	return !failed;
 }

Index: maildir-storage.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib-storage/index/maildir/maildir-storage.c,v
retrieving revision 1.66
retrieving revision 1.67
diff -u -d -r1.66 -r1.67
--- maildir-storage.c	26 Oct 2003 20:13:15 -0000	1.66
+++ maildir-storage.c	27 Apr 2004 20:25:54 -0000	1.67
@@ -5,8 +5,8 @@
 #include "mkdir-parents.h"
 #include "unlink-directory.h"
 #include "subscription-file/subscription-file.h"
-#include "maildir-index.h"
 #include "maildir-storage.h"
+#include "maildir-uidlist.h"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -30,7 +30,7 @@
 maildir_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_dir, *index_dir, *control_dir;
 	const char *home, *path, *p;
 	size_t len;
@@ -85,34 +85,37 @@
 	else if (strcmp(index_dir, "MEMORY") == 0)
 		index_dir = NULL;
 
-	storage = i_new(struct mail_storage, 1);
-	memcpy(storage, &maildir_storage, sizeof(struct mail_storage));
+	storage = i_new(struct index_storage, 1);
+	storage->storage = maildir_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_dir));
+	storage->inbox_path = i_strdup(home_expand(inbox_dir));
 	storage->index_dir = i_strdup(home_expand(index_dir));
 	storage->control_dir = i_strdup(home_expand(control_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 maildir_free(struct mail_storage *storage)
+static void maildir_free(struct mail_storage *_storage)
 {
+	struct index_storage *storage = (struct index_storage *) _storage;
+
 	index_storage_deinit(storage);
 
-	i_free(storage->namespace);
+	i_free(storage->storage.namespace);
+	i_free(storage->storage.error);
+
 	i_free(storage->dir);
-	i_free(storage->inbox_file);
+	i_free(storage->inbox_path);
 	i_free(storage->index_dir);
 	i_free(storage->control_dir);
 	i_free(storage->user);
-	i_free(storage->error);
 	i_free(storage->callbacks);
 	i_free(storage);
 }
@@ -173,14 +176,14 @@
 			   MAILDIR_FS_SEP_S, p+1, NULL);
 }
 
-const char *maildir_fix_mailbox_name(struct mail_storage *storage,
+const char *maildir_fix_mailbox_name(struct index_storage *storage,
 				     const char *name, int remove_namespace)
 {
 	char *dup, *p, sep;
 	size_t len;
 
 	if (strncasecmp(name, "INBOX", 5) == 0 &&
-	    (name[5] == '\0' || name[5] == storage->hierarchy_sep)) {
+	    (name[5] == '\0' || name[5] == storage->storage.hierarchy_sep)) {
 		/* use same case with all INBOX folders or we'll get
 		   into trouble */
 		name = t_strconcat("INBOX", name+5, NULL);
@@ -190,11 +193,11 @@
 		}
 	}
 
-	if (storage->namespace != NULL && remove_namespace) {
-		len = strlen(storage->namespace);
-		if (strncmp(storage->namespace, name, len) != 0) {
+	if (storage->storage.namespace != NULL && remove_namespace) {
+		len = strlen(storage->storage.namespace);
+		if (strncmp(storage->storage.namespace, name, len) != 0) {
 			i_panic("maildir: expecting namespace '%s' in name "
-				"'%s'", storage->namespace, name);
+				"'%s'", storage->storage.namespace, name);
 		}
 		name += len;
 	}
@@ -202,7 +205,7 @@
 	if (full_filesystem_access && (*name == '/' || *name == '~'))
 		return name;
 
-	sep = storage->hierarchy_sep;
+	sep = storage->storage.hierarchy_sep;
 	if (sep == MAILDIR_FS_SEP)
 		return name;
 
@@ -215,21 +218,21 @@
 	return dup;
 }
 
-const char *maildir_get_path(struct mail_storage *storage, const char *name)
+const char *maildir_get_path(struct index_storage *storage, const char *name)
 {
 	if (full_filesystem_access && (*name == '/' || *name == '~'))
 		return maildir_get_absolute_path(name, FALSE);
 
 	if (strcmp(name, "INBOX") == 0) {
-		return storage->inbox_file != NULL ?
-			storage->inbox_file : storage->dir;
+		return storage->inbox_path != NULL ?
+			storage->inbox_path : storage->dir;
 	}
 
 	return t_strconcat(storage->dir, "/"MAILDIR_FS_SEP_S, name, NULL);
 }
 
 static const char *
-maildir_get_unlink_path(struct mail_storage *storage, const char *name)
+maildir_get_unlink_path(struct index_storage *storage, const char *name)
 {
 	if (full_filesystem_access && (*name == '/' || *name == '~'))
 		return maildir_get_absolute_path(name, TRUE);
@@ -238,14 +241,14 @@
 				t_strconcat(MAILDIR_FS_SEP_S, name, NULL));
 }
 
-static const char *maildir_get_index_path(struct mail_storage *storage,
+static const char *maildir_get_index_path(struct index_storage *storage,
 					  const char *name)
 {
 	if (storage->index_dir == NULL)
 		return NULL;
 
-	if (strcmp(name, "INBOX") == 0 && storage->inbox_file != NULL)
-		return storage->inbox_file;
+	if (strcmp(name, "INBOX") == 0 && storage->inbox_path != NULL)
+		return storage->inbox_path;
 
 	if (full_filesystem_access && (*name == '/' || *name == '~'))
 		return maildir_get_absolute_path(name, FALSE);
@@ -253,7 +256,7 @@
 	return t_strconcat(storage->index_dir, "/"MAILDIR_FS_SEP_S, name, NULL);
 }
 
-static const char *maildir_get_control_path(struct mail_storage *storage,
+static const char *maildir_get_control_path(struct index_storage *storage,
 					    const char *name)
 {
 	if (storage->control_dir == NULL)
@@ -266,132 +269,132 @@
 			   name, NULL);
 }
 
-static int mkdir_verify(struct mail_storage *storage,
+static int mkdir_verify(struct index_storage *storage,
 			const char *dir, int verify)
 {
 	struct stat st;
 
 	if (verify) {
 		if (lstat(dir, &st) == 0)
-			return TRUE;
+			return 0;
 
 		if (errno != ENOENT) {
-			mail_storage_set_critical(storage,
+			mail_storage_set_critical(&storage->storage,
 						  "lstat(%s) failed: %m", dir);
-			return FALSE;
+			return -1;
 		}
 	}
 
 	if (mkdir(dir, CREATE_MODE) < 0 && (errno != EEXIST || !verify)) {
 		if (errno != EEXIST && (!verify || errno != ENOENT)) {
-			mail_storage_set_critical(storage,
+			mail_storage_set_critical(&storage->storage,
 						  "mkdir(%s) failed: %m", dir);
 		}
-		return FALSE;
+		return -1;
 	}
 
-	return TRUE;
+	return 0;
 }
 
 /* create or fix maildir, ignore if it already exists */
-static int create_maildir(struct mail_storage *storage,
+static int create_maildir(struct index_storage *storage,
 			  const char *dir, int verify)
 {
 	const char **tmp, *path;
 
-	if (!verify && !mkdir_verify(storage, dir, verify))
-		return FALSE;
+	if (!verify && mkdir_verify(storage, dir, verify) < 0)
+		return -1;
 
 	for (tmp = maildirs; *tmp != NULL; tmp++) {
 		path = t_strconcat(dir, "/", *tmp, NULL);
 
-		if (!mkdir_verify(storage, path, verify)) {
+		if (mkdir_verify(storage, path, verify) < 0) {
 			if (!verify || errno != ENOENT)
-				return FALSE;
+				return -1;
 
 			/* small optimization. if we're verifying, we don't
 			   check that the root dir actually exists unless we
 			   fail here. */
-			if (!mkdir_verify(storage, dir, verify))
-				return FALSE;
-			if (!mkdir_verify(storage, path, verify))
-				return FALSE;
+			if (mkdir_verify(storage, dir, verify) < 0)
+				return -1;
+			if (mkdir_verify(storage, path, verify) < 0)
+				return -1;
 		}
 	}
 
-	return TRUE;
+	return 0;
 }
 
-static int create_index_dir(struct mail_storage *storage, const char *name)
+static int create_index_dir(struct index_storage *storage, const char *name)
 {
 	const char *dir;
 
 	if (storage->index_dir == NULL)
-		return TRUE;
+		return 0;
 
 	if (strcmp(storage->index_dir, storage->dir) == 0 ||
-	    (strcmp(name, "INBOX") == 0 && storage->inbox_file != NULL &&
-	     strcmp(storage->index_dir, storage->inbox_file) == 0))
-		return TRUE;
+	    (strcmp(name, "INBOX") == 0 && storage->inbox_path != NULL &&
+	     strcmp(storage->index_dir, storage->inbox_path) == 0))
+		return 0;
 
 	dir = t_strconcat(storage->index_dir, "/"MAILDIR_FS_SEP_S, name, NULL);
 	if (mkdir_parents(dir, CREATE_MODE) == -1 && errno != EEXIST) {
-		mail_storage_set_critical(storage, "mkdir(%s) failed: %m", dir);
-		return FALSE;
+		mail_storage_set_critical(&storage->storage,
+					  "mkdir(%s) failed: %m", dir);
+		return -1;
 	}
 
-	return TRUE;
+	return 0;
 }
 
-static int create_control_dir(struct mail_storage *storage, const char *name)
+static int create_control_dir(struct index_storage *storage, const char *name)
 {
 	const char *dir;
 
 	if (storage->control_dir == NULL)
-		return TRUE;
+		return 0;
 
 	dir = t_strconcat(storage->control_dir, "/"MAILDIR_FS_SEP_S,
 			  name, NULL);
 	if (mkdir_parents(dir, CREATE_MODE) < 0 && errno != EEXIST) {
-		mail_storage_set_critical(storage, "mkdir(%s) failed: %m", dir);
-		return FALSE;
+		mail_storage_set_critical(&storage->storage,
+					  "mkdir(%s) failed: %m", dir);
+		return -1;
 	}
 
-	return TRUE;
+	return 0;
 }
 
-static int verify_inbox(struct mail_storage *storage)
+static int verify_inbox(struct index_storage *storage)
 {
 	const char *inbox;
 
-	if (storage->inbox_file == NULL) {
+	if (storage->inbox_path == NULL) {
 		/* first make sure the cur/ new/ and tmp/ dirs exist
 		   in root dir */
-		if (!create_maildir(storage, storage->dir, TRUE))
-			return FALSE;
+		if (create_maildir(storage, storage->dir, TRUE) < 0)
+			return -1;
 
 		/* create the .INBOX directory */
 		inbox = t_strconcat(storage->dir,
 				    "/"MAILDIR_FS_SEP_S"INBOX", NULL);
-		if (!mkdir_verify(storage, inbox, TRUE))
-			return FALSE;
+		if (mkdir_verify(storage, inbox, TRUE) < 0)
+			return -1;
 	} else {
-		if (!create_maildir(storage, storage->inbox_file, TRUE))
-			return FALSE;
+		if (create_maildir(storage, storage->inbox_path, TRUE) < 0)
+			return -1;
 	}
 
 	/* make sure the index directories exist */
-	return create_index_dir(storage, "INBOX") &&
-		create_control_dir(storage, "INBOX");
-}
-
-static void maildir_mail_init(struct index_mail *mail)
-{
-	mail->mail.expunge = maildir_storage_expunge;
+	if (create_index_dir(storage, "INBOX") < 0)
+		return -1;
+	if (create_control_dir(storage, "INBOX") < 0)
+		return -1;
+	return 0;
 }
 
 static struct mailbox *
-maildir_open(struct mail_storage *storage, const char *name,
+maildir_open(struct index_storage *storage, const char *name,
 	     enum mailbox_open_flags flags)
 {
 	struct index_mailbox *ibox;
@@ -403,113 +406,119 @@
 	index_dir = maildir_get_index_path(storage, name);
 	control_dir = maildir_get_control_path(storage, name);
 
-	index = index_storage_lookup_ref(index_dir, path);
-	if (index == NULL) {
-		index = maildir_index_alloc(path, index_dir, control_dir);
-		index_storage_add(index);
-	}
+	index = index_storage_alloc(index_dir, path, MAILDIR_INDEX_PREFIX);
+
+	ibox = index_storage_mailbox_init(storage, &maildir_mailbox,
+					  index, name, flags);
+	if (ibox == NULL)
+		return NULL;
+
+	ibox->path = i_strdup(path);
+	ibox->control_dir = i_strdup(control_dir);
+
+	ibox->mail_interface = &maildir_mail;
+	ibox->uidlist = maildir_uidlist_init(ibox);
 
 	/* for shared mailboxes get the create mode from the
 	   permissions of dovecot-shared file */
 	if (stat(t_strconcat(path, "/dovecot-shared", NULL), &st) < 0)
-		index->mail_create_mode = 0600;
+		ibox->mail_create_mode = 0600;
 	else {
-		index->mail_create_mode = st.st_mode & 0666;
-		index->private_flags_mask = MAIL_SEEN;
+		ibox->mail_create_mode = st.st_mode & 0666;
+		ibox->private_flags_mask = MAIL_SEEN;
 	}
 
-	ibox = index_storage_mailbox_init(storage, &maildir_mailbox,
-					  index, name, flags);
-	if (ibox != NULL)
-		ibox->mail_init = maildir_mail_init;
-
-	return (struct mailbox *) ibox;
+	return &ibox->box;
 }
 
 static struct mailbox *
-maildir_open_mailbox(struct mail_storage *storage,
+maildir_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 = maildir_fix_mailbox_name(storage, name, TRUE);
 	if (strcmp(name, "INBOX") == 0) {
-		if (!verify_inbox(storage))
+		if (verify_inbox(storage) < 0)
 			return NULL;
 		return maildir_open(storage, "INBOX", flags);
 	}
 
 	if (!maildir_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 = maildir_get_path(storage, name);
 	if (stat(path, &st) == 0) {
 		/* exists - make sure the required directories are also there */
-		if (!create_maildir(storage, path, TRUE) ||
-		    !create_index_dir(storage, name) ||
-		    !create_control_dir(storage, name))
-			return FALSE;
+		if (create_maildir(storage, path, TRUE) < 0 ||
+		    create_index_dir(storage, name) < 0 ||
+		    create_control_dir(storage, name) < 0)
+			return NULL;
 
 		return maildir_open(storage, name, flags);
 	} else if (errno == ENOENT) {
-		mail_storage_set_error(storage, "Mailbox doesn't exist: %s",
+		mail_storage_set_error(_storage, "Mailbox doesn't exist: %s",
 				       name);
 		return NULL;
 	} else {
-		mail_storage_set_critical(storage, "stat(%s) failed: %m", path);
+		mail_storage_set_critical(_storage, "stat(%s) failed: %m",
+					  path);
 		return NULL;
 	}
 }
 
-static int maildir_create_mailbox(struct mail_storage *storage,
+static int maildir_mailbox_create(struct mail_storage *_storage,
 				  const char *name,
 				  int directory __attr_unused__)
 {
+	struct index_storage *storage = (struct index_storage *)_storage;
 	const char *path;
 
-	mail_storage_clear_error(storage);
+	mail_storage_clear_error(_storage);
 
 	name = maildir_fix_mailbox_name(storage, name, TRUE);
 	if (!maildir_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;
 	}
 
 	path = maildir_get_path(storage, name);
-	if (!create_maildir(storage, path, FALSE)) {
+	if (create_maildir(storage, path, FALSE) < 0) {
 		if (errno == EEXIST) {
-			mail_storage_set_error(storage,
+			mail_storage_set_error(_storage,
 					       "Mailbox already exists");
 		}
-		return FALSE;
+		return -1;
 	}
 
-	return TRUE;
+	return 0;
 }
 
-static int maildir_delete_mailbox(struct mail_storage *storage,
+static int maildir_mailbox_delete(struct mail_storage *_storage,
 				  const char *name)
 {
+	struct index_storage *storage = (struct index_storage *)_storage;
 	struct stat st;
 	const char *src, *dest, *index_dir;
 	int count;
 
-	mail_storage_clear_error(storage);
+	mail_storage_clear_error(_storage);
 
 	name = maildir_fix_mailbox_name(storage, name, TRUE);
 	if (strcmp(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 (!maildir_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;
 	}
 
 	/* rename the .maildir into ..maildir which marks it as being
@@ -518,9 +527,9 @@
 	src = maildir_get_path(storage, name);
 	dest = maildir_get_unlink_path(storage, name);
 	if (stat(src, &st) != 0 && errno == ENOENT) {
-		mail_storage_set_error(storage, "Mailbox doesn't exist: %s",
+		mail_storage_set_error(_storage, "Mailbox doesn't exist: %s",
 				       name);
-		return FALSE;
+		return -1;
 	}
 
 	if (storage->index_dir != NULL && *name != '/' && *name != '~' &&
@@ -533,48 +542,48 @@
 		   opened by another session.. can't really help it. */
 		if (unlink_directory(index_dir, TRUE) < 0 &&
 		    errno != ENOTEMPTY) {
-			mail_storage_set_critical(storage,
+			mail_storage_set_critical(_storage,
 				"unlink_directory(%s) failed: %m", index_dir);
-			return FALSE;
+			return -1;
 		}
 	}
 
 	count = 0;
 	while (rename(src, dest) < 0 && count < 2) {
 		if (errno != EEXIST && errno != ENOTEMPTY) {
-			mail_storage_set_critical(storage,
+			mail_storage_set_critical(_storage,
 				"rename(%s, %s) failed: %m", src, dest);
-			return FALSE;
+			return -1;
 		}
 
 		/* ..dir already existed? delete it and try again */
 		if (unlink_directory(dest, TRUE) < 0) {
-			mail_storage_set_critical(storage,
+			mail_storage_set_critical(_storage,
 				"unlink_directory(%s) failed: %m", dest);
-			return FALSE;
+			return -1;
 		}
 		count++;
 	}
 
 	if (unlink_directory(dest, TRUE) < 0 && errno != ENOTEMPTY) {
-		mail_storage_set_critical(storage,
+		mail_storage_set_critical(_storage,
 			"unlink_directory(%s) failed: %m", dest);
 
 		/* it's already renamed to ..dir, which means it's deleted
 		   as far as client is concerned. Report success. */
 	}
 
-	return TRUE;
+	return 0;
 }
 
-static int rename_indexes(struct mail_storage *storage,
+static int rename_indexes(struct index_storage *storage,
 			  const char *oldname, const char *newname)
 {
 	const char *oldpath, *newpath;
 
 	if (storage->index_dir == NULL ||
 	    strcmp(storage->index_dir, storage->dir) == 0)
-		return TRUE;
+		return 0;
 
 	/* Rename it's index. */
 	oldpath = t_strconcat(storage->index_dir, "/"MAILDIR_FS_SEP_S,
@@ -583,15 +592,16 @@
 			      newname, NULL);
 
 	if (rename(oldpath, newpath) < 0 && errno != ENOENT) {
-		mail_storage_set_critical(storage, "rename(%s, %s) failed: %m",
+		mail_storage_set_critical(&storage->storage,
+					  "rename(%s, %s) failed: %m",
 					  oldpath, newpath);
-		return FALSE;
+		return -1;
 	}
 
-	return TRUE;
+	return 0;
 }
 
-static int rename_subfolders(struct mail_storage *storage,
+static int rename_subfolders(struct index_storage *storage,
 			     const char *oldname, const char *newname)
 {
 	struct mailbox_list_context *ctx;
@@ -603,12 +613,12 @@
 	ret = 0;
 	oldnamelen = strlen(oldname);
 
-	mask = t_strdup_printf("%s%s%c*", storage->namespace != NULL ?
-			       storage->namespace : "", oldname,
-			       storage->hierarchy_sep);
-	ctx = storage->list_mailbox_init(storage, mask,
-					 MAILBOX_LIST_FAST_FLAGS);
-	while ((list = maildir_list_mailbox_next(ctx)) != NULL) {
+	mask = t_strdup_printf("%s%s%c*", storage->storage.namespace != NULL ?
+			       storage->storage.namespace : "", oldname,
+			       storage->storage.hierarchy_sep);
+	ctx = maildir_mailbox_list_init(&storage->storage, mask,
+					MAILBOX_LIST_FAST_FLAGS);
+	while ((list = maildir_mailbox_list_next(ctx)) != NULL) {
 		const char *list_name;
 
 		t_push();
@@ -633,7 +643,7 @@
 		    errno == EEXIST || errno == ENOTEMPTY)
 			ret = 1;
 		else {
-			mail_storage_set_critical(storage,
+			mail_storage_set_critical(&storage->storage,
 						  "rename(%s, %s) failed: %m",
 						  oldpath, newpath);
 			ret = -1;
@@ -645,32 +655,33 @@
 		t_pop();
 	}
 
-	if (!maildir_list_mailbox_deinit(ctx))
+	if (maildir_mailbox_list_deinit(ctx) < 0)
 		return -1;
 	return ret;
 }
 
-static int maildir_rename_mailbox(struct mail_storage *storage,
+static int maildir_mailbox_rename(struct mail_storage *_storage,
 				  const char *oldname, const char *newname)
 {
+	struct index_storage *storage = (struct index_storage *)_storage;
 	const char *oldpath, *newpath;
 	int ret, found;
 
-	mail_storage_clear_error(storage);
+	mail_storage_clear_error(_storage);
 
 	oldname = maildir_fix_mailbox_name(storage, oldname, TRUE);
 	newname = maildir_fix_mailbox_name(storage, newname, TRUE);
 
 	if (!maildir_is_valid_existing_name(oldname) ||
 	    !maildir_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;
 	}
 
 	if (strcmp(oldname, "INBOX") == 0) {
-		mail_storage_set_error(storage,
+		mail_storage_set_error(_storage,
 				       "Renaming INBOX isn't supported.");
-		return FALSE;
+		return -1;
 	}
 
 	/* NOTE: it's possible to rename a nonexisting folder which has
@@ -685,89 +696,98 @@
 		found = ret == 0;
 		ret = rename_subfolders(storage, oldname, newname);
 		if (ret < 0)
-			return FALSE;
+			return -1;
 		if (!found && ret == 0) {
-			mail_storage_set_error(storage,
+			mail_storage_set_error(_storage,
 					       "Mailbox doesn't exist");
-			return FALSE;
+			return -1;
 		}
 
-		return TRUE;
+		return 0;
 	}
 
 	if (errno == EEXIST) {
-		mail_storage_set_error(storage,
+		mail_storage_set_error(_storage,
 				       "Target mailbox already exists");
-		return FALSE;
+		return -1;
 	} else {
-		mail_storage_set_critical(storage, "rename(%s, %s) failed: %m",
+		mail_storage_set_critical(_storage, "rename(%s, %s) failed: %m",
 					  oldpath, newpath);
-		return FALSE;
+		return -1;
 	}
 }
 
-static int maildir_set_subscribed(struct mail_storage *storage,
+static int maildir_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->control_dir != NULL ?
+			   storage->control_dir : storage->dir,
+			   "/" SUBSCRIPTION_FILE_NAME, NULL);
+
 	name = maildir_fix_mailbox_name(storage, name, FALSE);
-	return subsfile_set_subscribed(storage, name, set);
+	return subsfile_set_subscribed(_storage, path, name, set);
 }
 
-static int maildir_get_mailbox_name_status(struct mail_storage *storage,
+static int maildir_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 = maildir_fix_mailbox_name(storage, name, TRUE);
 	if (!maildir_is_valid_existing_name(name)) {
 		*status = MAILBOX_NAME_INVALID;
-		return TRUE;
+		return 0;
 	}
 
 	path = maildir_get_path(storage, name);
 	if (stat(path, &st) == 0) {
 		*status = MAILBOX_NAME_EXISTS;
-		return TRUE;
+		return 0;
 	}
 
 	if (!maildir_is_valid_create_name(name)) {
 		*status = MAILBOX_NAME_INVALID;
-		return TRUE;
+		return 0;
 	}
 
 	if (errno == ENOENT) {
 		*status = MAILBOX_NAME_VALID;
-		return TRUE;
+		return 0;
 	} else {
-		mail_storage_set_critical(storage, "stat(%s) failed: %m", path);
-		return FALSE;
+		mail_storage_set_critical(_storage, "stat(%s) failed: %m",
+					  path);
+		return -1;
 	}
 }
 
 static int maildir_storage_close(struct mailbox *box)
 {
-	struct index_mailbox *ibox = (struct index_mailbox *) box;
-	int failed = FALSE;
+	struct index_mailbox *ibox = (struct index_mailbox *)box;
+	int ret = 0;
 
-        index_storage_init_lock_notify(ibox);
-	if (!maildir_try_flush_dirty_flags(ibox->index, TRUE)) {
+	/*FIXME:if (!maildir_try_flush_dirty_flags(ibox->index, TRUE)) {
 		mail_storage_set_index_error(ibox);
-		failed = TRUE;
-	}
-	ibox->index->set_lock_notify_callback(ibox->index, NULL, NULL);
+		ret = -1;
+	}*/
 
-	return index_storage_mailbox_free(box) && !failed;
+	maildir_uidlist_deinit(ibox->uidlist);
+        index_storage_mailbox_free(box);
+	return ret;
 }
 
 static void maildir_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;
 
@@ -784,35 +804,9 @@
 	}
 
 	index_mailbox_check_add(ibox,
-		t_strconcat(ibox->index->mailbox_path, "/new", NULL), TRUE);
+		t_strconcat(ibox->storage->dir, "/new", NULL), TRUE);
 	index_mailbox_check_add(ibox,
-		t_strconcat(ibox->index->mailbox_path, "/cur", NULL), TRUE);
-}
-
-static int maildir_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;
-	}
-
-	ibox->lock_type = lock_type;
-	return TRUE;
+		t_strconcat(ibox->storage->dir, "/cur", NULL), TRUE);
 }
 
 struct mail_storage maildir_storage = {
@@ -825,24 +819,18 @@
 	maildir_free,
 	maildir_autodetect,
 	index_storage_set_callbacks,
-	maildir_open_mailbox,
-	maildir_create_mailbox,
-	maildir_delete_mailbox,
-	maildir_rename_mailbox,
-	maildir_list_mailbox_init,
-	maildir_list_mailbox_deinit,
-	maildir_list_mailbox_next,
+	maildir_mailbox_open,
+	maildir_mailbox_create,
+	maildir_mailbox_delete,
+	maildir_mailbox_rename,
+	maildir_mailbox_list_init,
+	maildir_mailbox_list_next,
+	maildir_mailbox_list_deinit,
 	maildir_set_subscribed,
 	maildir_get_mailbox_name_status,
 	mail_storage_get_last_error,
 
 	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	NULL, NULL, NULL,
-
 	0
 };
 
@@ -853,24 +841,19 @@
 	index_storage_is_readonly,
         index_storage_allow_new_custom_flags,
 	maildir_storage_close,
-	maildir_storage_lock,
 	index_storage_get_status,
-	index_storage_sync,
+	maildir_storage_sync,
 	maildir_storage_auto_sync,
-	index_storage_fetch_uid,
-	index_storage_fetch_seq,
+	maildir_transaction_begin,
+	maildir_transaction_commit,
+	maildir_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,
-	maildir_storage_save_init,
-	maildir_storage_save_deinit,
-	maildir_storage_save_next,
-	maildir_storage_copy_init,
-	maildir_storage_copy_deinit,
-	maildir_storage_copy,
-	maildir_storage_expunge_init,
-	maildir_storage_expunge_deinit,
-	maildir_storage_expunge_fetch_next,
-	index_storage_is_inconsistency_error
+	maildir_save,
+	maildir_copy,
+	index_storage_is_inconsistent
 };

Index: maildir-storage.h
===================================================================
RCS file: /home/cvs/dovecot/src/lib-storage/index/maildir/maildir-storage.h,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -d -r1.19 -r1.20
--- maildir-storage.h	27 Jul 2003 03:12:13 -0000	1.19
+++ maildir-storage.h	27 Apr 2004 20:25:54 -0000	1.20
@@ -5,39 +5,73 @@
 #define MAILDIR_FS_SEP '.'
 #define MAILDIR_FS_SEP_S "."
 
+#define SUBSCRIPTION_FILE_NAME "subscriptions"
+#define MAILDIR_INDEX_PREFIX "dovecot.index"
+
 #include "index-storage.h"
 
-struct mail_copy_context *maildir_storage_copy_init(struct mailbox *box);
-int maildir_storage_copy_deinit(struct mail_copy_context *ctx, int rollback);
-int maildir_storage_copy(struct mail *mail, struct mail_copy_context *ctx);
+struct maildir_save_context;
+struct maildir_copy_context;
 
-struct mail_save_context *
-maildir_storage_save_init(struct mailbox *box, int transaction);
-int maildir_storage_save_deinit(struct mail_save_context *ctx, int rollback);
-int maildir_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 maildir_transaction_context {
+	struct index_transaction_context ictx;
+	struct maildir_save_context *save_ctx;
+	struct maildir_copy_context *copy_ctx;
+};
+
+extern struct mail maildir_mail;
+
+/* Return -1 = error, 0 = file not found, 1 = ok */
+typedef int maildir_file_do_func(struct index_mailbox *ibox,
+				 const char *path, void *context);
+
+int maildir_file_do(struct index_mailbox *ibox, uint32_t seq,
+		    maildir_file_do_func *func, void *context);
+const char *maildir_generate_tmp_filename(const struct timeval *tv);
+int maildir_create_tmp(struct index_mailbox *ibox, const char *dir,
+		       mode_t mode, const char **fname_r);
 
 struct mailbox_list_context *
-maildir_list_mailbox_init(struct mail_storage *storage,
+maildir_mailbox_list_init(struct mail_storage *storage,
 			  const char *mask, enum mailbox_list_flags flags);
-int maildir_list_mailbox_deinit(struct mailbox_list_context *ctx);
+int maildir_mailbox_list_deinit(struct mailbox_list_context *ctx);
 struct mailbox_list *
-maildir_list_mailbox_next(struct mailbox_list_context *ctx);
+maildir_mailbox_list_next(struct mailbox_list_context *ctx);
 
-struct mail_expunge_context *
-maildir_storage_expunge_init(struct mailbox *box,
-			     enum mail_fetch_field wanted_fields,
-			     int expunge_all);
-int maildir_storage_expunge_deinit(struct mail_expunge_context *ctx);
-struct mail *
-maildir_storage_expunge_fetch_next(struct mail_expunge_context *ctx);
-int maildir_storage_expunge(struct mail *mail, struct mail_expunge_context *ctx,
-			    unsigned int *seq_r, int notify);
+int maildir_storage_sync(struct mailbox *box, enum mailbox_sync_flags flags);
+int maildir_storage_sync_readonly(struct index_mailbox *ibox);
 
-const char *maildir_fix_mailbox_name(struct mail_storage *storage,
+struct mailbox_transaction_context *
+maildir_transaction_begin(struct mailbox *box, int hide);
+int maildir_transaction_commit(struct mailbox_transaction_context *t);
+void maildir_transaction_rollback(struct mailbox_transaction_context *t);
+
+int maildir_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 maildir_save_commit(struct maildir_save_context *ctx);
+void maildir_save_rollback(struct maildir_save_context *ctx);
+
+int maildir_copy(struct mailbox_transaction_context *t, struct mail *mail);
+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 mail_storage *storage, const char *name);
+const char *maildir_get_path(struct index_storage *storage, const char *name);
+
+int maildir_sync_last_commit(struct index_mailbox *ibox);
+
+int maildir_filename_get_flags(const char *fname, enum mail_flags *flags_r,
+			       custom_flags_mask_t custom_flags_r);
+const char *maildir_filename_set_flags(const char *fname, enum mail_flags flags,
+				       custom_flags_mask_t custom_flags);
+
+unsigned int maildir_hash(const void *p);
+int maildir_cmp(const void *p1, const void *p2);
 
 #endif

--- maildir-expunge.c DELETED ---



More information about the dovecot-cvs mailing list