From 3e5ce145bb968f31ebc46001b36c936e156d6c0d Mon Sep 17 00:00:00 2001 From: nishat-06 Date: Sun, 19 Jul 2026 12:49:23 +0530 Subject: [PATCH] lib-storage: mailbox-list - Track hierarchy part start using ns_sep --- src/lib-storage/mailbox-list.c | 21 +++++++++++++++------ src/lib-storage/test-mailbox-list.c | 7 +++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/lib-storage/mailbox-list.c b/src/lib-storage/mailbox-list.c index 6d4d6fca8e..bcdf22f264 100644 --- a/src/lib-storage/mailbox-list.c +++ b/src/lib-storage/mailbox-list.c @@ -191,19 +191,28 @@ const char *mailbox_list_get_unexpanded_path(struct mailbox_list *list, return ""; } -static bool need_escape_dirstart(const char *vname, const char *maildir_name) +/* A hierarchy part ends at the namespace separator, at the end of the name, or + at '/' - which is always escaped and so can never itself become a path + separator in the result. */ +static bool dirpart_ends(char c, char ns_sep) +{ + return c == '\0' || c == ns_sep || c == '/'; +} + +static bool need_escape_dirstart(const char *vname, const char *maildir_name, + char ns_sep) { const char *suffix; if (vname[0] == '.') { - if (vname[1] == '\0' || vname[1] == '/') + if (dirpart_ends(vname[1], ns_sep)) return TRUE; /* "." */ - if (vname[1] == '.' && (vname[2] == '\0' || vname[2] == '/')) + if (vname[1] == '.' && dirpart_ends(vname[2], ns_sep)) return TRUE; /* ".." */ } if (*maildir_name != '\0') { if (str_begins(vname, maildir_name, &suffix) && - (suffix[0] == '\0' || suffix[0] == '/')) + dirpart_ends(suffix[0], ns_sep)) return TRUE; /* e.g. dbox-Mails */ } return FALSE; @@ -236,13 +245,13 @@ mailbox_list_escape_name_params_to_str(string_t *escaped_name, const char *vname *vname == escape_char || *vname == '/' || (dirstart && - need_escape_dirstart(vname, maildir_name))) { + need_escape_dirstart(vname, maildir_name, ns_sep))) { str_printfa(escaped_name, "%c%02x", escape_char, *vname); } else { str_append_c(escaped_name, *vname); } - dirstart = *vname == '/'; + dirstart = *vname == ns_sep || *vname == '/'; } } diff --git a/src/lib-storage/test-mailbox-list.c b/src/lib-storage/test-mailbox-list.c index 51af6122ae..56832f0adf 100644 --- a/src/lib-storage/test-mailbox-list.c +++ b/src/lib-storage/test-mailbox-list.c @@ -555,6 +555,13 @@ static void test_mailbox_list_escape_name_params(void) { "a+b", '/', '.', '+', "", TRUE, "a+2bb" }, /* literal '/' escaped when not the ns_sep */ { "a/b", ':', '.', '+', "", TRUE, "a+2fb" }, + /* dirstart follows ns_sep, not a hardcoded '/': "." and ".." + after a non-'/' separator must still be escaped */ + { "a:.:b", ':', '/', '+', "", TRUE, "a/+2e/b" }, + { "a:..:b", ':', '/', '+', "", TRUE, "a/+2e./b" }, + { "a:dbox-Mails:b", ':', '/', '+', "dbox-Mails", TRUE, + "a/+64box-Mails/b" }, + { "a/..:b", ':', '/', '+', "", TRUE, "a+2f+2e./b" }, }; const char *result;