dovecot-2.2: lib-storage: Added support for "broken_char" settin...

dovecot at dovecot.org dovecot at dovecot.org
Fri Oct 12 05:46:17 EEST 2012


details:   http://hg.dovecot.org/dovecot-2.2/rev/22e7322e8601
changeset: 15210:22e7322e8601
user:      Timo Sirainen <tss at iki.fi>
date:      Fri Oct 12 05:45:42 2012 +0300
description:
lib-storage: Added support for "broken_char" setting for escaping invalid mailbox names.
This is currently enabled only for imapc backend, where '~' character is
used for it.

Most importantly this allows migrating mailboxes from other IMAP servers
that contain mailbox names that Dovecot's imapc backend couldn't otherwise
access.

diffstat:

 src/lib-storage/index/imapc/imapc-list.c    |    1 +
 src/lib-storage/index/imapc/imapc-storage.h |    1 +
 src/lib-storage/mailbox-list.c              |  121 +++++++++++++++++++++++++--
 src/lib-storage/mailbox-list.h              |    5 +
 4 files changed, 116 insertions(+), 12 deletions(-)

diffs (200 lines):

diff -r a44be96f55c1 -r 22e7322e8601 src/lib-storage/index/imapc/imapc-list.c
--- a/src/lib-storage/index/imapc/imapc-list.c	Fri Oct 12 03:47:41 2012 +0300
+++ b/src/lib-storage/index/imapc/imapc-list.c	Fri Oct 12 05:45:42 2012 +0300
@@ -216,6 +216,7 @@
 		list_set.layout = MAILBOX_LIST_NAME_MAILDIRPLUSPLUS;
 		list_set.root_dir = dir;
 		list_set.escape_char = IMAPC_LIST_ESCAPE_CHAR;
+		list_set.broken_char = IMAPC_LIST_BROKEN_CHAR;
 		list_set.mailbox_dir_name = "";
 		list_set.maildir_name = "";
 
diff -r a44be96f55c1 -r 22e7322e8601 src/lib-storage/index/imapc/imapc-storage.h
--- a/src/lib-storage/index/imapc/imapc-storage.h	Fri Oct 12 03:47:41 2012 +0300
+++ b/src/lib-storage/index/imapc/imapc-storage.h	Fri Oct 12 05:45:42 2012 +0300
@@ -7,6 +7,7 @@
 #define IMAPC_STORAGE_NAME "imapc"
 #define IMAPC_INDEX_PREFIX "dovecot.index"
 #define IMAPC_LIST_ESCAPE_CHAR '%'
+#define IMAPC_LIST_BROKEN_CHAR '~'
 
 struct imap_arg;
 struct imapc_untagged_reply;
diff -r a44be96f55c1 -r 22e7322e8601 src/lib-storage/mailbox-list.c
--- a/src/lib-storage/mailbox-list.c	Fri Oct 12 03:47:41 2012 +0300
+++ b/src/lib-storage/mailbox-list.c	Fri Oct 12 05:45:42 2012 +0300
@@ -432,6 +432,53 @@
 	return str_c(escaped_name);
 }
 
+static int
+mailbox_list_unescape_broken_chars(struct mailbox_list *list, char *name)
+{
+	char *src, *dest;
+	unsigned char chr;
+
+	if ((src = strchr(name, list->set.broken_char)) == NULL)
+		return 0;
+	dest = src;
+
+	while (*src != '\0') {
+		if (*src == list->set.broken_char) {
+			if (src[1] >= '0' && src[1] <= '9')
+				chr = (src[1]-'0') * 0x10;
+			else if (src[1] >= 'a' && src[1] <= 'f')
+				chr = (src[1]-'a' + 10) * 0x10;
+			else
+				return -1;
+
+			if (src[2] >= '0' && src[2] <= '9')
+				chr += src[2]-'0';
+			else if (src[2] >= 'a' && src[2] <= 'f')
+				chr += src[2]-'a' + 10;
+			else
+				return -1;
+			*dest++ = chr;
+			src += 3;
+		} else {
+			*dest++ = *src++;
+		}
+	}
+	*dest++ = '\0';
+	return 0;
+}
+
+static char *mailbox_list_convert_sep(const char *storage_name, char src, char dest)
+{
+	char *ret, *p;
+
+	ret = p_strdup(unsafe_data_stack_pool, storage_name);
+	for (p = ret; *p != '\0'; p++) {
+		if (*p == src)
+			*p = dest;
+	}
+	return ret;
+}
+
 const char *mailbox_list_default_get_storage_name(struct mailbox_list *list,
 						  const char *vname)
 {
@@ -439,7 +486,7 @@
 	unsigned int prefix_len = strlen(ns->prefix);
 	const char *storage_name = vname;
 	string_t *str;
-	char list_sep, ns_sep, *ret, *p;
+	char list_sep, ns_sep, *ret;
 
 	if (strcasecmp(storage_name, "INBOX") == 0 &&
 	    (ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0)
@@ -480,19 +527,29 @@
 		storage_name = "INBOX";
 	}
 
-	if (list_sep == ns_sep)
+	if (list_sep != ns_sep) {
+		if (ns->type == MAIL_NAMESPACE_TYPE_SHARED &&
+		    (ns->flags & NAMESPACE_FLAG_AUTOCREATED) == 0) {
+			/* shared namespace root. the backend storage's
+			   hierarchy separator isn't known yet, so do
+			   nothing. */
+			return storage_name;
+		}
+
+		ret = mailbox_list_convert_sep(storage_name, ns_sep, list_sep);
+	} else if (list->set.broken_char == '\0' ||
+		   strchr(storage_name, list->set.broken_char) == NULL) {
+		/* no need to convert broken chars */
 		return storage_name;
-	if (ns->type == MAIL_NAMESPACE_TYPE_SHARED &&
-	    (ns->flags & NAMESPACE_FLAG_AUTOCREATED) == 0) {
-		/* shared namespace root. the backend storage's hierarchy
-		   separator isn't known yet, so do nothing. */
-		return storage_name;
+	} else {
+		ret = p_strdup(unsafe_data_stack_pool, storage_name);
 	}
 
-	ret = p_strdup(unsafe_data_stack_pool, storage_name);
-	for (p = ret; *p != '\0'; p++) {
-		if (*p == ns_sep)
-			*p = list_sep;
+	if (list->set.broken_char != '\0') {
+		if (mailbox_list_unescape_broken_chars(list, ret) < 0) {
+			ret = mailbox_list_convert_sep(storage_name,
+						       ns_sep, list_sep);
+		}
 	}
 	return ret;
 }
@@ -539,6 +596,40 @@
 	return str_c(dest);
 }
 
+static void
+mailbox_list_escape_broken_chars(struct mailbox_list *list, string_t *str)
+{
+	unsigned int i;
+	char buf[3];
+
+	if (strchr(str_c(str), list->set.broken_char) == NULL)
+		return;
+
+	for (i = 0; i < str_len(str); i++) {
+		if (str_c(str)[i] == list->set.broken_char) {
+			i_snprintf(buf, sizeof(buf), "%02x",
+				   list->set.broken_char);
+			str_insert(str, i+1, buf);
+			i += 2;
+		}
+	}
+}
+
+static void
+mailbox_list_escape_broken_name(struct mailbox_list *list,
+				const char *vname, string_t *str)
+{
+	str_truncate(str, 0);
+	for (; *vname != '\0'; vname++) {
+		if (*vname == '&' || (unsigned char)*vname >= 0x80) {
+			str_printfa(str, "%c%02x", list->set.broken_char,
+				    *vname);
+		} else {
+			str_append_c(str, *vname);
+		}
+	}
+}
+
 const char *mailbox_list_default_get_vname(struct mailbox_list *list,
 					   const char *storage_name)
 {
@@ -573,8 +664,14 @@
 	} else if (!list->set.utf8) {
 		/* mUTF-7 -> UTF-8 conversion */
 		string_t *str = t_str_new(strlen(vname));
-		if (imap_utf7_to_utf8(vname, str) == 0)
+		if (imap_utf7_to_utf8(vname, str) == 0) {
+			if (list->set.broken_char != '\0')
+				mailbox_list_escape_broken_chars(list, str);
 			vname = str_c(str);
+		} else if (list->set.broken_char != '\0') {
+			mailbox_list_escape_broken_name(list, vname, str);
+			vname = str_c(str);
+		}
 	}
 
 	prefix_len = strlen(list->ns->prefix);
diff -r a44be96f55c1 -r 22e7322e8601 src/lib-storage/mailbox-list.h
--- a/src/lib-storage/mailbox-list.h	Fri Oct 12 03:47:41 2012 +0300
+++ b/src/lib-storage/mailbox-list.h	Fri Oct 12 05:45:42 2012 +0300
@@ -103,6 +103,11 @@
 
 	/* Encode "bad" characters in mailbox names as <escape_char><hex> */
 	char escape_char;
+	/* If mailbox name can't be changed reversibly to UTF-8 and back,
+	   encode the problematic parts using <broken_char><hex> in the
+	   user-visible UTF-8 name. The broken_char itself also has to be
+	   encoded the same way. */
+	char broken_char;
 	/* Use UTF-8 mailbox names on filesystem instead of mUTF-7 */
 	bool utf8;
 	/* Don't check/create the alt-dir symlink. */


More information about the dovecot-cvs mailing list