From: Timo Sirainen Date: Tue, 1 Mar 2022 13:37:57 +0000 (-0500) Subject: dsync: Escape and unescape mailbox names as needed X-Git-Tag: 2.3.19~19 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=801aae4bd532347512393d649d476a4d3aa1562d;p=thirdparty%2Fdovecot%2Fcore.git dsync: Escape and unescape mailbox names as needed This should fix various issues with syncing local and remote mailbox names. It especially avoids changing the escape character to alt_char. --- diff --git a/src/doveadm/dsync/dsync-brain-mailbox-tree.c b/src/doveadm/dsync/dsync-brain-mailbox-tree.c index 007082cef8..0e520ae2f0 100644 --- a/src/doveadm/dsync/dsync-brain-mailbox-tree.c +++ b/src/doveadm/dsync/dsync-brain-mailbox-tree.c @@ -84,15 +84,24 @@ void dsync_brain_mailbox_trees_init(struct dsync_brain *brain) dsync_mailbox_tree_iter_init(brain->local_mailbox_tree); } +static const char *const * +dsync_brain_mailbox_to_parts(struct dsync_brain *brain, const char *name) +{ + char sep[] = { brain->hierarchy_sep, '\0' }; + char **parts = p_strsplit(unsafe_data_stack_pool, name, sep); + for (unsigned int i = 0; parts[i] != NULL; i++) { + mailbox_list_name_unescape((const char **)&parts[i], + brain->escape_char); + } + return (const char *const *)parts; +} void dsync_brain_send_mailbox_tree(struct dsync_brain *brain) { struct dsync_mailbox_node *node; enum dsync_ibc_send_ret ret; const char *full_name; - char sep[2]; - sep[0] = brain->hierarchy_sep; sep[1] = '\0'; while (dsync_mailbox_tree_iter_next(brain->local_tree_iter, &full_name, &node)) { if (node->ns == NULL) { @@ -115,19 +124,7 @@ void dsync_brain_send_mailbox_tree(struct dsync_brain *brain) dsync_mailbox_node_to_string(node)); } - /* Avoid sending out mailbox names with escape - characters. Especially when dsync is used for - migration, we don't want to end up having invalid - mUTF7 mailbox names locally. Also, remote might not - even be configured to use the same escape - character. */ - if (node->ns != NULL) { - i_assert(brain->alt_char != '\0'); - full_name = t_str_replace(full_name, - node->ns->list->set.vname_escape_char, - brain->alt_char); - } - parts = t_strsplit(full_name, sep); + parts = dsync_brain_mailbox_to_parts(brain, full_name); ret = dsync_ibc_send_mailbox_tree_node(brain->ibc, parts, node); } T_END; @@ -302,13 +299,29 @@ dsync_get_mailbox_name(struct dsync_brain *brain, const char *const *name_parts, ns_sep = mail_namespace_get_sep(ns); /* build the mailbox name */ + char escape_chars[] = { + brain->escape_char, + ns_sep, + }; + struct dsync_mailbox_list *dlist = DSYNC_LIST_CONTEXT(ns->list); + if (dlist != NULL && !dlist->have_orig_escape_char) { + /* The escape character was added only for dsync internally. + Normally there is no escape character configured. Change + the mailbox names so that it doesn't rely on it. */ + escape_chars[0] = '\0'; + } vname = t_str_new(128); for (; *name_parts != NULL; name_parts++) { - for (p = *name_parts; *p != '\0'; p++) { - if (*p != ns_sep) - str_append_c(vname, *p); - else - str_append_c(vname, brain->alt_char); + if (escape_chars[0] != '\0') { + mailbox_list_name_escape(*name_parts, escape_chars, + vname); + } else { + for (p = *name_parts; *p != '\0'; p++) { + if (*p != ns_sep) + str_append_c(vname, *p); + else + str_append_c(vname, brain->alt_char); + } } str_append_c(vname, ns_sep); } diff --git a/src/doveadm/dsync/dsync-mailbox-tree.c b/src/doveadm/dsync/dsync-mailbox-tree.c index e932378c96..e34f335e47 100644 --- a/src/doveadm/dsync/dsync-mailbox-tree.c +++ b/src/doveadm/dsync/dsync-mailbox-tree.c @@ -258,13 +258,37 @@ convert_name_to_remote_sep(struct dsync_mailbox_tree *tree, const char *name) { string_t *str = t_str_new(128); - for (; *name != '\0'; name++) { - if (*name == tree->sep) - str_append_c(str, tree->remote_sep); - else if (*name == tree->remote_sep) - str_append_c(str, tree->alt_char); - else - str_append_c(str, *name); + char remote_escape_chars[3] = { + tree->remote_escape_char, + tree->remote_sep, + '\0' + }; + + for (;;) { + const char *end = strchr(name, tree->sep); + const char *name_part = end == NULL ? name : + t_strdup_until(name, end++); + + if (tree->escape_char != '\0') + mailbox_list_name_unescape(&name_part, tree->escape_char); + if (remote_escape_chars[0] != '\0') { + /* The local name can be fully escaped to remote + name and back. */ + mailbox_list_name_escape(name_part, remote_escape_chars, + str); + } else { + /* There is no remote escape char, so for conflicting + separator use the alt_char. */ + for (; *name_part != '\0'; name_part++) { + if (*name_part == tree->remote_sep) + str_append_c(str, tree->alt_char); + else + str_append_c(str, *name_part); + } + } + if (end == NULL) + break; + str_append_c(str, tree->remote_sep); } return str_c(str); } diff --git a/src/doveadm/dsync/test-dsync-mailbox-tree-sync.c b/src/doveadm/dsync/test-dsync-mailbox-tree-sync.c index b7e88da61f..b068137dbb 100644 --- a/src/doveadm/dsync/test-dsync-mailbox-tree-sync.c +++ b/src/doveadm/dsync/test-dsync-mailbox-tree-sync.c @@ -30,6 +30,18 @@ void mailbox_name_get_sha128(const char *name, guid_128_t guid_128_r) memcpy(guid_128_r, sha, I_MIN(GUID_128_SIZE, sizeof(sha))); } +void mailbox_list_name_unescape(const char **name ATTR_UNUSED, + char escape_char ATTR_UNUSED) +{ +} + +void mailbox_list_name_escape(const char *name, + const char *escape_chars ATTR_UNUSED, + string_t *dest) +{ + str_append(dest, name); +} + static struct dsync_mailbox_node * node_create(struct dsync_mailbox_tree *tree, unsigned int counter, const char *name, unsigned int last_renamed_or_created)