changeset 13219:7098642a0374

lib-storage: Added mailbox_alloc_guid() for opening mailbox by its GUID.
author Timo Sirainen <tss@iki.fi>
date Thu, 11 Aug 2011 18:03:20 +0300
parents bd0e7cdbb6b2
children 19d8ec0db2e3
files src/lib-storage/Makefile.am src/lib-storage/mail-storage-private.h src/lib-storage/mail-storage.c src/lib-storage/mail-storage.h src/lib-storage/mailbox-guid-cache.c src/lib-storage/mailbox-guid-cache.h src/lib-storage/mailbox-list-private.h src/lib-storage/mailbox-list.c
diffstat 8 files changed, 205 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-storage/Makefile.am	Thu Aug 11 18:02:25 2011 +0300
+++ b/src/lib-storage/Makefile.am	Thu Aug 11 18:03:20 2011 +0300
@@ -36,6 +36,7 @@
 	mail-thread.c \
 	mail-user.c \
 	mailbox-get.c \
+	mailbox-guid-cache.c \
 	mailbox-header.c \
 	mailbox-keywords.c \
 	mailbox-list.c \
@@ -62,6 +63,7 @@
 	mail-storage-service.h \
 	mail-storage-settings.h \
 	mail-user.h \
+	mailbox-guid-cache.h \
 	mailbox-list.h \
 	mailbox-list-private.h \
 	mailbox-search-result-private.h \
--- a/src/lib-storage/mail-storage-private.h	Thu Aug 11 18:02:25 2011 +0300
+++ b/src/lib-storage/mail-storage-private.h	Thu Aug 11 18:03:20 2011 +0300
@@ -234,6 +234,10 @@
 	/* default vfuncs for new struct mails. */
 	const struct mail_vfuncs *mail_vfuncs;
 
+	/* If non-zero, fail mailbox_open() with this error. mailbox_alloc()
+	   can set this to force open to fail. */
+	enum mail_error open_error;
+
 	struct istream *input;
 	const char *index_prefix;
 	enum mailbox_flags flags;
--- a/src/lib-storage/mail-storage.c	Thu Aug 11 18:02:25 2011 +0300
+++ b/src/lib-storage/mail-storage.c	Thu Aug 11 18:03:20 2011 +0300
@@ -18,6 +18,7 @@
 #include "mail-search.h"
 #include "mail-search-register.h"
 #include "mailbox-search-result-private.h"
+#include "mailbox-guid-cache.h"
 
 #include <stdlib.h>
 #include <ctype.h>
@@ -600,6 +601,52 @@
 	return box;
 }
 
+struct mailbox *mailbox_alloc_guid(struct mailbox_list *list,
+				   uint8_t guid[MAIL_GUID_128_SIZE],
+				   enum mailbox_flags flags)
+{
+	struct mailbox *box = NULL;
+	struct mailbox_metadata metadata;
+	enum mail_error open_error = MAIL_ERROR_TEMP;
+	const char *vname;
+
+	if (mailbox_guid_cache_find(list, guid, &vname) < 0) {
+		vname = NULL;
+	} else if (vname != NULL) {
+		box = mailbox_alloc(list, vname, flags);
+		if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID,
+					 &metadata) < 0) {
+		} else if (memcmp(metadata.guid, guid,
+				  sizeof(metadata.guid)) != 0) {
+			/* GUID mismatch, refresh cache and try again */
+			mailbox_free(&box);
+			mailbox_guid_cache_refresh(list);
+			return mailbox_alloc_guid(list, guid, flags);
+		} else {
+			/* successfully opened the correct mailbox */
+			return box;
+		}
+		i_error("mailbox_alloc_guid(%s): "
+			"Couldn't verify mailbox GUID: %s",
+			mail_guid_128_to_string(guid),
+			mailbox_get_last_error(box, NULL));
+		vname = NULL;
+		mailbox_free(&box);
+	} else {
+		vname = t_strdup_printf("(nonexistent mailbox with GUID=%s)",
+					mail_guid_128_to_string(guid));
+		open_error = MAIL_ERROR_NOTFOUND;
+	}
+
+	if (vname == NULL) {
+		vname = t_strdup_printf("(error in mailbox with GUID=%s)",
+					mail_guid_128_to_string(guid));
+	}
+	box = mailbox_alloc(list, vname, flags);
+	box->open_error = open_error;
+	return box;
+}
+
 static bool have_listable_namespace_prefix(struct mail_namespace *ns,
 					   const char *name)
 {
@@ -625,6 +672,16 @@
 int mailbox_exists(struct mailbox *box, bool auto_boxes,
 		   enum mailbox_existence *existence_r)
 {
+	switch (box->open_error) {
+	case 0:
+		break;
+	case MAIL_ERROR_NOTFOUND:
+		*existence_r = MAILBOX_EXISTENCE_NONE;
+		return 0;
+	default:
+		/* unsure if this exists or not */
+		return -1;
+	}
 	if (!mailbox_list_is_valid_existing_name(box->list, box->name)) {
 		/* report it as not selectable, since it exists but we won't
 		   let it be opened. */
@@ -691,6 +748,18 @@
 
 	if (box->opened)
 		return 0;
+	switch (box->open_error) {
+	case 0:
+		break;
+	case MAIL_ERROR_NOTFOUND:
+		mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
+			T_MAIL_ERR_MAILBOX_NOT_FOUND(box->vname));
+		return -1;
+	default:
+		mail_storage_set_internal_error(box->storage);
+		box->storage->error = box->open_error;
+		return -1;
+	}
 
 	if (mailbox_check_mismatching_separators(box) < 0)
 		return -1;
--- a/src/lib-storage/mail-storage.h	Thu Aug 11 18:02:25 2011 +0300
+++ b/src/lib-storage/mail-storage.h	Thu Aug 11 18:03:20 2011 +0300
@@ -365,6 +365,10 @@
    with possibly different readonly-state. */
 struct mailbox *mailbox_alloc(struct mailbox_list *list, const char *vname,
 			      enum mailbox_flags flags);
+/* Like mailbox_alloc(), but use mailbox GUID. */
+struct mailbox *mailbox_alloc_guid(struct mailbox_list *list,
+				   uint8_t guid[MAIL_GUID_128_SIZE],
+				   enum mailbox_flags flags);
 /* Get mailbox existence state. If auto_boxes=FALSE, return
    MAILBOX_EXISTENCE_NONE for autocreated mailboxes that haven't been
    physically created yet */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/mailbox-guid-cache.c	Thu Aug 11 18:03:20 2011 +0300
@@ -0,0 +1,107 @@
+/* Copyright (c) 2005-2011 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "hash.h"
+#include "mail-storage.h"
+#include "mailbox-list-private.h"
+#include "mailbox-guid-cache.h"
+
+struct mailbox_guid_cache_rec {
+	uint8_t guid[MAIL_GUID_128_SIZE];
+	const char *vname;
+};
+
+static unsigned int guid_cache_rec_hash(const void *_rec)
+{
+	const struct mailbox_guid_cache_rec *rec = _rec;
+	unsigned int i, g, h = 0;
+
+	for (i = 0; i < sizeof(rec->guid); i++) {
+		h = (h << 4) + rec->guid[i];
+		if ((g = h & 0xf0000000UL)) {
+			h = h ^ (g >> 24);
+			h = h ^ g;
+		}
+	}
+	return h;
+}
+
+static int guid_cache_rec_cmp(const void *_r1, const void *_r2)
+{
+	const struct mailbox_guid_cache_rec *r1 = _r1, *r2 = _r2;
+
+	return memcmp(r1->guid, r2->guid, sizeof(r1->guid));
+}
+
+int mailbox_guid_cache_find(struct mailbox_list *list,
+			    uint8_t guid[MAIL_GUID_128_SIZE],
+			    const char **vname_r)
+{
+	const struct mailbox_guid_cache_rec *rec;
+	struct mailbox_guid_cache_rec lookup_rec;
+
+	memcpy(lookup_rec.guid, guid, sizeof(lookup_rec.guid));
+	if (list->guid_cache == NULL) {
+		mailbox_guid_cache_refresh(list);
+		rec = hash_table_lookup(list->guid_cache, &lookup_rec);
+	} else {
+		rec = hash_table_lookup(list->guid_cache, &lookup_rec);
+		if (rec == NULL) {
+			mailbox_guid_cache_refresh(list);
+			rec = hash_table_lookup(list->guid_cache, &lookup_rec);
+		}
+	}
+	if (rec == NULL) {
+		*vname_r = NULL;
+		return list->guid_cache_errors ? -1 : 0;
+	}
+	*vname_r = rec->vname;
+	return 0;
+}
+
+void mailbox_guid_cache_refresh(struct mailbox_list *list)
+{
+	struct mailbox_list_iterate_context *ctx;
+	const struct mailbox_info *info;
+	struct mailbox *box;
+	struct mailbox_metadata metadata;
+	struct mailbox_guid_cache_rec *rec;
+
+	if (list->guid_cache == NULL) {
+		list->guid_cache_pool =
+			pool_alloconly_create("guid cache", 1024*16);
+		list->guid_cache = hash_table_create(default_pool,
+						     list->guid_cache_pool, 0,
+						     guid_cache_rec_hash,
+						     guid_cache_rec_cmp);
+	} else {
+		hash_table_clear(list->guid_cache, TRUE);
+		p_clear(list->guid_cache_pool);
+	}
+	list->guid_cache_errors = FALSE;
+
+	ctx = mailbox_list_iter_init(list, "*",
+				     MAILBOX_LIST_ITER_NO_AUTO_BOXES);
+	while ((info = mailbox_list_iter_next(ctx)) != NULL) {
+		if ((info->flags &
+		     (MAILBOX_NOSELECT | MAILBOX_NONEXISTENT)) != 0)
+			continue;
+
+		box = mailbox_alloc(list, info->name, MAILBOX_FLAG_KEEP_RECENT);
+		if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID,
+					 &metadata) < 0) {
+			i_error("Couldn't get mailbox %s GUID: %s",
+				info->name, mailbox_get_last_error(box, NULL));
+			list->guid_cache_errors = TRUE;
+		} else {
+			rec = p_new(list->guid_cache_pool,
+				    struct mailbox_guid_cache_rec, 1);
+			memcpy(rec->guid, metadata.guid, sizeof(rec->guid));
+			rec->vname = p_strdup(list->guid_cache_pool, info->name);
+			hash_table_insert(list->guid_cache, rec, rec);
+		}
+		mailbox_free(&box);
+	}
+	if (mailbox_list_iter_deinit(&ctx) < 0)
+		list->guid_cache_errors = TRUE;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/mailbox-guid-cache.h	Thu Aug 11 18:03:20 2011 +0300
@@ -0,0 +1,9 @@
+#ifndef MAILBOX_GUID_CACHE_H
+#define MAILBOX_GUID_CACHE_H
+
+int mailbox_guid_cache_find(struct mailbox_list *list,
+			    uint8_t guid[MAIL_GUID_128_SIZE],
+			    const char **vname_r);
+void mailbox_guid_cache_refresh(struct mailbox_list *list);
+
+#endif
--- a/src/lib-storage/mailbox-list-private.h	Thu Aug 11 18:02:25 2011 +0300
+++ b/src/lib-storage/mailbox-list-private.h	Thu Aug 11 18:03:20 2011 +0300
@@ -123,6 +123,10 @@
 	struct mailbox_log *changelog;
 	time_t changelog_timestamp;
 
+	pool_t guid_cache_pool;
+	struct hash_table *guid_cache;
+	bool guid_cache_errors;
+
 	char *error_string;
 	enum mail_error error;
 	bool temporary_error;
--- a/src/lib-storage/mailbox-list.c	Thu Aug 11 18:02:25 2011 +0300
+++ b/src/lib-storage/mailbox-list.c	Thu Aug 11 18:03:20 2011 +0300
@@ -6,6 +6,7 @@
 #include "mkdir-parents.h"
 #include "str.h"
 #include "sha1.h"
+#include "hash.h"
 #include "home-expand.h"
 #include "close-keep-errno.h"
 #include "eacces-error.h"
@@ -543,6 +544,11 @@
 	*_list = NULL;
 	i_free_and_null(list->error_string);
 
+	if (list->guid_cache != NULL) {
+		hash_table_destroy(&list->guid_cache);
+		pool_unref(&list->guid_cache_pool);
+	}
+
 	if (list->subscriptions != NULL)
 		mailbox_tree_deinit(&list->subscriptions);
 	if (list->changelog != NULL)