changeset 12786:934c529052eb

imapc: Initial support for local index files.
author Timo Sirainen <tss@iki.fi>
date Thu, 10 Mar 2011 18:32:08 +0200
parents 395af848ec10
children 598bc0b91855
files src/lib-storage/index/imapc/imapc-list.c src/lib-storage/index/imapc/imapc-list.h src/lib-storage/index/imapc/imapc-mailbox.c src/lib-storage/index/imapc/imapc-storage.c src/lib-storage/index/imapc/imapc-storage.h src/lib-storage/index/imapc/imapc-sync.c src/lib-storage/index/imapc/imapc-sync.h
diffstat 7 files changed, 206 insertions(+), 53 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-storage/index/imapc/imapc-list.c	Thu Mar 10 18:31:28 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-list.c	Thu Mar 10 18:32:08 2011 +0200
@@ -182,22 +182,84 @@
 	return list->sep;
 }
 
-static const char *
-imapc_list_get_path(struct mailbox_list *list ATTR_UNUSED,
-		    const char *name ATTR_UNUSED,
-		    enum mailbox_list_path_type type)
+static struct mailbox_list *imapc_list_get_fs(struct imapc_mailbox_list *list)
 {
-	if (type == MAILBOX_LIST_PATH_TYPE_INDEX)
-		return "";
-	return NULL;
+	struct mailbox_list_settings list_set;
+	const char *error, *dir;
+
+	dir = list->list.set.index_dir;
+	if (dir == NULL)
+		dir = list->list.set.root_dir;
+
+	if (dir == NULL) {
+		/* indexes disabled */
+	} else if (list->index_list == NULL && !list->index_list_failed) {
+		memset(&list_set, 0, sizeof(list_set));
+		list_set.layout = MAILBOX_LIST_NAME_MAILDIRPLUSPLUS;
+		/* the root dir shouldn't actually ever be used. we just need
+		   it to be different from index_dir so the index directories
+		   get autocreated */
+		list_set.root_dir = t_strconcat(dir, "/", NULL);
+		list_set.index_dir = dir;
+		list_set.escape_char = '%';
+
+		if (mailbox_list_create(list_set.layout, list->list.ns,
+					&list_set, MAILBOX_LIST_FLAG_SECONDARY,
+					&list->index_list, &error) < 0) {
+			i_error("imapc: Couldn't create %s mailbox list: %s",
+				list_set.layout, error);
+			list->index_list_failed = TRUE;
+		}
+	}
+	return list->index_list;
 }
 
 static const char *
-imapc_list_get_temp_prefix(struct mailbox_list *list, bool global ATTR_UNUSED)
+imapc_list_get_fs_name(struct imapc_mailbox_list *list, const char *name)
+{
+	struct mailbox_list *fs_list = imapc_list_get_fs(list);
+	const char *vname;
+
+	if (name == NULL)
+		return name;
+
+	vname = mailbox_list_get_vname(&list->list, name);
+	return mailbox_list_get_storage_name(fs_list, vname);
+}
+
+static const char *
+imapc_list_get_path(struct mailbox_list *_list, const char *name,
+		    enum mailbox_list_path_type type)
 {
-	i_panic("imapc: Can't return a temp prefix for '%s'",
-		list->ns->prefix);
-	return NULL;
+	struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list;
+	struct mailbox_list *fs_list = imapc_list_get_fs(list);
+	const char *fs_name;
+
+	if (fs_list != NULL) {
+		fs_name = imapc_list_get_fs_name(list, name);
+		return mailbox_list_get_path(fs_list, fs_name, type);
+	} else {
+		if (type == MAILBOX_LIST_PATH_TYPE_INDEX)
+			return "";
+		return NULL;
+	}
+}
+
+static const char *
+imapc_list_get_temp_prefix(struct mailbox_list *_list, bool global)
+{
+	struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list;
+	struct mailbox_list *fs_list = imapc_list_get_fs(list);
+
+	if (fs_list != NULL) {
+		return global ?
+			mailbox_list_get_global_temp_prefix(fs_list) :
+			mailbox_list_get_temp_prefix(fs_list);
+	} else {
+		i_panic("imapc: Can't return a temp prefix for '%s'",
+			_list->ns->prefix);
+		return NULL;
+	}
 }
 
 static const char *
@@ -422,9 +484,15 @@
 }
 
 static int
-imapc_list_delete_dir(struct mailbox_list *list ATTR_UNUSED,
-		      const char *name ATTR_UNUSED)
+imapc_list_delete_dir(struct mailbox_list *_list, const char *name)
 {
+	struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list;
+	struct mailbox_list *fs_list = imapc_list_get_fs(list);
+
+	if (fs_list != NULL) {
+		name = imapc_list_get_fs_name(list, name);
+		(void)mailbox_list_delete_dir(fs_list, name);
+	}
 	return 0;
 }
 
@@ -434,6 +502,7 @@
 			  bool rename_children)
 {
 	struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)oldlist;
+	struct mailbox_list *fs_list = imapc_list_get_fs(list);
 	struct imapc_simple_context ctx;
 
 	if (!rename_children) {
@@ -453,6 +522,13 @@
 			  imapc_list_simple_callback, &ctx,
 			  "RENAME %s %s", oldname, newname);
 	imapc_simple_run(&ctx);
+	if (ctx.ret == 0 && fs_list != NULL && oldlist == newlist) {
+		oldname = imapc_list_get_fs_name(list, oldname);
+		newname = imapc_list_get_fs_name(list, newname);
+		(void)fs_list->v.rename_mailbox(fs_list, oldname,
+						fs_list, newname,
+						rename_children);
+	}
 	return ctx.ret;
 }
 
--- a/src/lib-storage/index/imapc/imapc-list.h	Thu Mar 10 18:31:28 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-list.h	Thu Mar 10 18:32:08 2011 +0200
@@ -10,6 +10,7 @@
 struct imapc_mailbox_list {
 	struct mailbox_list list;
 	struct imapc_storage *storage;
+	struct mailbox_list *index_list;
 
 	struct mailbox_tree_context *mailboxes, *tmp_subscriptions;
 	char sep;
@@ -21,6 +22,7 @@
 	unsigned int broken:1;
 	unsigned int refreshed_subscriptions:1;
 	unsigned int refreshed_mailboxes:1;
+	unsigned int index_list_failed:1;
 };
 
 void imapc_list_register_callbacks(struct imapc_mailbox_list *list);
--- a/src/lib-storage/index/imapc/imapc-mailbox.c	Thu Mar 10 18:31:28 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-mailbox.c	Thu Mar 10 18:32:08 2011 +0200
@@ -6,6 +6,7 @@
 #include "imap-util.h"
 #include "imapc-client.h"
 #include "imapc-seqmap.h"
+#include "imapc-sync.h"
 #include "imapc-storage.h"
 
 #define NOTIFY_DELAY_MSECS 500
@@ -37,6 +38,27 @@
 		mail_index_transaction_open_updated_view(mbox->delayed_sync_trans);
 }
 
+int imapc_mailbox_commit_delayed_trans(struct imapc_mailbox *mbox,
+				       bool *changes_r)
+{
+	int ret = 0;
+
+	*changes_r = FALSE;
+
+	if (mbox->delayed_sync_view != NULL)
+		mail_index_view_close(&mbox->delayed_sync_view);
+	if (mbox->delayed_sync_trans != NULL) {
+		if (mail_index_transaction_commit(&mbox->delayed_sync_trans) < 0) {
+			mail_storage_set_index_error(&mbox->box);
+			ret = -1;
+		}
+		*changes_r = TRUE;
+	}
+	if (mbox->sync_view != NULL)
+		mail_index_view_close(&mbox->sync_view);
+	return ret;
+}
+
 static void
 imapc_newmsgs_callback(const struct imapc_command_reply *reply,
 		       void *context)
@@ -63,7 +85,7 @@
 	uint32_t rcount = reply->num;
 	const struct mail_index_header *hdr;
 	struct imapc_seqmap *seqmap;
-	uint32_t next_lseq, next_rseq;
+	uint32_t first_uid, next_lseq, next_rseq;
 
 	if (mbox == NULL)
 		return;
@@ -74,14 +96,19 @@
 	seqmap = imapc_client_mailbox_get_seqmap(mbox->client_box);
 	next_lseq = mail_index_view_get_messages_count(view) + 1;
 	next_rseq = imapc_seqmap_lseq_to_rseq(seqmap, next_lseq);
-	if (next_rseq > rcount)
-		return;
-
-	hdr = mail_index_get_header(view);
+	if (next_rseq > rcount) {
+		if (rcount == 0 || !mbox->opening)
+			return;
+		/* initial SELECT. we don't know what the flags are. */
+		first_uid = 1;
+	} else {
+		hdr = mail_index_get_header(view);
+		first_uid = hdr->next_uid;
+	}
 
 	mbox->new_msgs = TRUE;
 	imapc_client_mailbox_cmdf(mbox->client_box, imapc_newmsgs_callback,
-				  mbox, "UID FETCH %u:* FLAGS", hdr->next_uid);
+				  mbox, "UID FETCH %u:* FLAGS", first_uid);
 }
 
 static void imapc_mailbox_idle_timeout(struct imapc_mailbox *mbox)
@@ -101,6 +128,28 @@
 	}
 }
 
+static bool keywords_are_equal(struct mail_keywords *kw,
+			       const ARRAY_TYPE(keyword_indexes) *kw_arr)
+{
+	const unsigned int *kw_idx;
+	unsigned int i, j, count;
+
+	kw_idx = array_get(kw_arr, &count);
+	if (count != kw->count)
+		return FALSE;
+
+	/* there are normally only a few keywords, so O(n^2) is fine */
+	for (i = 0; i < count; i++) {
+		for (j = 0; j < count; j++) {
+			if (kw->idx[i] == kw_idx[j])
+				break;
+		}
+		if (j == count)
+			return FALSE;
+	}
+	return TRUE;
+}
+
 static void imapc_untagged_fetch(const struct imapc_untagged_reply *reply,
 				 struct imapc_mailbox *mbox)
 {
@@ -158,32 +207,49 @@
 
 	imapc_mailbox_init_delayed_trans(mbox);
 	old_count = mail_index_view_get_messages_count(mbox->delayed_sync_view);
+	while (lseq <= old_count) {
+		rec = mail_index_lookup(mbox->delayed_sync_view, lseq);
+		if (rec->uid == uid || uid == 0)
+			break;
+
+		if (!mbox->opening || uid < rec->uid) {
+			imapc_mailbox_set_corrupted(mbox,
+				"Message UID changed %u -> %u", rec->uid, uid);
+			return;
+		}
+		/* we're opening the mailbox. this message was expunged
+		   externally, so expunge it ourself too. */
+		imapc_seqmap_expunge(seqmap, rseq);
+		mail_index_expunge(mbox->delayed_sync_trans, lseq);
+		lseq++;
+	}
 	if (lseq > old_count) {
 		if (uid == 0 || lseq != old_count + 1)
 			return;
 		i_assert(lseq == old_count + 1);
 		mail_index_append(mbox->delayed_sync_trans, uid, &lseq);
 	}
-	rec = mail_index_lookup(mbox->delayed_sync_view, lseq);
-	if (rec->uid != uid && uid != 0) {
-		imapc_mailbox_set_corrupted(mbox, "Message UID changed %u -> %u",
-					    rec->uid, uid);
-		return;
-	}
 	if (seen_flags && rec->flags != flags) {
 		mail_index_update_flags(mbox->delayed_sync_trans, lseq,
 					MODIFY_REPLACE, flags);
 	}
-	if (seen_flags) {
+	if (seen_flags) T_BEGIN {
+		ARRAY_TYPE(keyword_indexes) old_kws;
 		struct mail_keywords *kw;
 
+		t_array_init(&old_kws, 8);
+		mail_index_lookup_keywords(mbox->delayed_sync_view, lseq,
+					   &old_kws);
+
 		(void)array_append_space(&keywords);
 		kw = mail_index_keywords_create(mbox->box.index,
 						array_idx(&keywords, 0));
-		mail_index_update_keywords(mbox->delayed_sync_trans, lseq,
-					   MODIFY_REPLACE, kw);
+		if (!keywords_are_equal(kw, &old_kws)) {
+			mail_index_update_keywords(mbox->delayed_sync_trans,
+						   lseq, MODIFY_REPLACE, kw);
+		}
 		mail_index_keywords_unref(&kw);
-	}
+	} T_END;
 	imapc_mailbox_idle_notify(mbox);
 }
 
@@ -222,32 +288,47 @@
 imapc_resp_text_uidvalidity(const struct imapc_untagged_reply *reply,
 			    struct imapc_mailbox *mbox)
 {
+	const struct mail_index_header *hdr;
 	uint32_t uid_validity;
+	bool changes;
 
 	if (mbox == NULL || reply->resp_text_value == NULL ||
 	    str_to_uint32(reply->resp_text_value, &uid_validity) < 0)
 		return;
 
-	imapc_mailbox_init_delayed_trans(mbox);
-	mail_index_update_header(mbox->delayed_sync_trans,
-		offsetof(struct mail_index_header, uid_validity),
-		&uid_validity, sizeof(uid_validity), TRUE);
+	hdr = mail_index_get_header(mbox->box.view);
+	if (hdr->uid_validity != uid_validity) {
+		imapc_mailbox_init_delayed_trans(mbox);
+		if (hdr->uid_validity != 0) {
+			/* uidvalidity changed, reset the entire mailbox */
+			mail_index_reset(mbox->delayed_sync_trans);
+			(void)imapc_mailbox_commit_delayed_trans(mbox, &changes);
+			imapc_mailbox_init_delayed_trans(mbox);
+		}
+		mail_index_update_header(mbox->delayed_sync_trans,
+			offsetof(struct mail_index_header, uid_validity),
+			&uid_validity, sizeof(uid_validity), TRUE);
+	}
 }
 
 static void
 imapc_resp_text_uidnext(const struct imapc_untagged_reply *reply,
 			struct imapc_mailbox *mbox)
 {
+	const struct mail_index_header *hdr;
 	uint32_t uid_next;
 
 	if (mbox == NULL || reply->resp_text_value == NULL ||
 	    str_to_uint32(reply->resp_text_value, &uid_next) < 0)
 		return;
 
-	imapc_mailbox_init_delayed_trans(mbox);
-	mail_index_update_header(mbox->delayed_sync_trans,
-				 offsetof(struct mail_index_header, next_uid),
-				 &uid_next, sizeof(uid_next), FALSE);
+	hdr = mail_index_get_header(mbox->box.view);
+	if (hdr->next_uid != uid_next) {
+		imapc_mailbox_init_delayed_trans(mbox);
+		mail_index_update_header(mbox->delayed_sync_trans,
+			offsetof(struct mail_index_header, next_uid),
+			&uid_next, sizeof(uid_next), FALSE);
+	}
 }
 
 void imapc_mailbox_register_untagged(struct imapc_mailbox *mbox,
--- a/src/lib-storage/index/imapc/imapc-storage.c	Thu Mar 10 18:31:28 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-storage.c	Thu Mar 10 18:32:08 2011 +0200
@@ -267,8 +267,6 @@
 	struct index_mailbox_context *ibox;
 	pool_t pool;
 
-	flags |= MAILBOX_FLAG_NO_INDEX_FILES;
-
 	pool = pool_alloconly_create("imapc mailbox", 1024*3);
 	mbox = p_new(pool, struct imapc_mailbox, 1);
 	mbox->box = imapc_mailbox;
@@ -277,7 +275,8 @@
 	mbox->box.list = list;
 	mbox->box.mail_vfuncs = &imapc_mail_vfuncs;
 
-	index_storage_mailbox_alloc(&mbox->box, vname, flags, NULL);
+	index_storage_mailbox_alloc(&mbox->box, vname, flags,
+				    IMAPC_INDEX_PREFIX);
 
 	ibox = INDEX_STORAGE_CONTEXT(&mbox->box);
 	ibox->save_commit_pre = imapc_transaction_save_commit_pre;
--- a/src/lib-storage/index/imapc/imapc-storage.h	Thu Mar 10 18:31:28 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-storage.h	Thu Mar 10 18:32:08 2011 +0200
@@ -4,6 +4,7 @@
 #include "index-storage.h"
 
 #define IMAPC_STORAGE_NAME "imapc"
+#define IMAPC_INDEX_PREFIX "dovecot.index"
 
 struct imap_arg;
 struct imapc_untagged_reply;
@@ -102,6 +103,8 @@
 			       void *context);
 int imapc_mailbox_get_client_box(struct imapc_mailbox *mbox,
 				 struct imapc_client_mailbox **client_box_r);
+int imapc_mailbox_commit_delayed_trans(struct imapc_mailbox *mbox,
+				       bool *changes_r);
 
 void imapc_storage_register_untagged(struct imapc_storage *storage,
 				     const char *name,
--- a/src/lib-storage/index/imapc/imapc-sync.c	Thu Mar 10 18:31:28 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-sync.c	Thu Mar 10 18:32:08 2011 +0200
@@ -309,7 +309,7 @@
 {
 	struct imapc_mailbox *mbox = (struct imapc_mailbox *)box;
 	enum imapc_capability capabilities;
-	bool changes = FALSE;
+	bool changes;
 	int ret = 0;
 
 	if (!box->opened) {
@@ -327,18 +327,8 @@
 		imapc_client_run(mbox->storage->client);
 	}
 
-	if (mbox->delayed_sync_view != NULL)
-		mail_index_view_close(&mbox->delayed_sync_view);
-	if (mbox->delayed_sync_trans != NULL) {
-		if (mail_index_transaction_commit(&mbox->delayed_sync_trans) < 0) {
-			mail_storage_set_index_error(&mbox->box);
-			ret = -1;
-		}
-		changes = TRUE;
-	}
-	if (mbox->sync_view != NULL)
-		mail_index_view_close(&mbox->sync_view);
-
+	if (imapc_mailbox_commit_delayed_trans(mbox, &changes) < 0)
+		ret = -1;
 	if ((changes || index_mailbox_want_full_sync(&mbox->box, flags)) &&
 	    ret == 0)
 		ret = imapc_sync(mbox);
--- a/src/lib-storage/index/imapc/imapc-sync.h	Thu Mar 10 18:31:28 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-sync.h	Thu Mar 10 18:32:08 2011 +0200
@@ -1,7 +1,9 @@
 #ifndef CYDIR_SYNC_H
 #define CYDIR_SYNC_H
 
+enum mailbox_sync_flags;
 struct mailbox;
+struct mailbox_sync_status;
 
 struct imapc_sync_context {
 	struct imapc_mailbox *mbox;