From e2cfcc9e85915a7f4d7034da104cc57bbaf00ef7 Mon Sep 17 00:00:00 2001 From: Naveen Nathan Date: Wed, 24 Apr 2019 03:04:05 +1000 Subject: [PATCH] Improve hierarchy information accuracy in IMAP browser Currently the IMAP browser relies on LIST and LSUB (for listing subscribed folders) which may not provide the required hierarchy information. RFC3348 section 3 goes as far as stating that a client mustn't rely on LSUB for hierarchy information. This patch implements the LIST command extensions specified in RFC5258 requiring that a server must respond with hierarchy information for listed folders (whether or not filtering on subscribed folders). --- imap/browse.c | 27 +++++++++++++++++++++++---- imap/command.c | 3 ++- imap/imap_private.h | 5 +++-- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/imap/browse.c b/imap/browse.c index f8af6b08d..2e842faad 100644 --- a/imap/browse.c +++ b/imap/browse.c @@ -190,7 +190,8 @@ int imap_browse(const char *path, struct BrowserState *state) char buf[PATH_MAX]; char mbox[PATH_MAX]; char munged_mbox[PATH_MAX]; - char list_cmd[5]; + char list_cmd[18]; + int len; int n; char ctmp; bool showparents = false; @@ -204,7 +205,6 @@ int imap_browse(const char *path, struct BrowserState *state) save_lsub = C_ImapCheckSubscribed; C_ImapCheckSubscribed = false; - mutt_str_strfcpy(list_cmd, C_ImapListSubscribed ? "LSUB" : "LIST", sizeof(list_cmd)); // Pick first mailbox connected to the same server struct MailboxNode *np = NULL; @@ -222,6 +222,21 @@ int imap_browse(const char *path, struct BrowserState *state) if (!adata) goto fail; + if (C_ImapListSubscribed) + { + const char *lsub_cmd = "LSUB"; + + /* RFC3348 section 3 states LSUB is unreliable for hierarchy information. + * The newer LIST extensions are designed for this. */ + if (adata->capabilities & IMAP_CAP_LIST_EXTENDED) + lsub_cmd = "LIST (SUBSCRIBED)"; + mutt_str_strfcpy(list_cmd, lsub_cmd, sizeof(list_cmd)); + } + else + { + mutt_str_strfcpy(list_cmd, "LIST", sizeof(list_cmd)); + } + mutt_message(_("Getting folder list...")); /* skip check for parents when at the root */ @@ -244,7 +259,9 @@ int imap_browse(const char *path, struct BrowserState *state) /* if our target exists and has inferiors, enter it if we * aren't already going to */ imap_munge_mbox_name(adata->unicode, munged_mbox, sizeof(munged_mbox), mbox); - snprintf(buf, sizeof(buf), "%s \"\" %s", list_cmd, munged_mbox); + len = snprintf(buf, sizeof(buf), "%s \"\" %s", list_cmd, munged_mbox); + if (adata->capabilities & IMAP_CAP_LIST_EXTENDED) + snprintf(buf + len, sizeof(buf) - len, " RETURN (CHILDREN)"); imap_cmd_start(adata, buf); adata->cmdresult = &list; do @@ -332,7 +349,9 @@ int imap_browse(const char *path, struct BrowserState *state) snprintf(buf, sizeof(buf), "%s%%", mbox); imap_munge_mbox_name(adata->unicode, munged_mbox, sizeof(munged_mbox), buf); mutt_debug(LL_DEBUG3, "%s\n", munged_mbox); - snprintf(buf, sizeof(buf), "%s \"\" %s", list_cmd, munged_mbox); + len = snprintf(buf, sizeof(buf), "%s \"\" %s", list_cmd, munged_mbox); + if (adata->capabilities & IMAP_CAP_LIST_EXTENDED) + snprintf(buf + len, sizeof(buf) - len, " RETURN (CHILDREN)"); if (browse_add_list_result(adata, buf, state, false)) goto fail; diff --git a/imap/command.c b/imap/command.c index ae1ce710e..4d41200ac 100644 --- a/imap/command.c +++ b/imap/command.c @@ -68,7 +68,8 @@ static const char *const Capabilities[] = { "AUTH=GSSAPI", "AUTH=ANONYMOUS", "AUTH=OAUTHBEARER", "STARTTLS", "LOGINDISABLED", "IDLE", "SASL-IR", "ENABLE", "CONDSTORE", - "QRESYNC", "X-GM-EXT-1", NULL, + "QRESYNC", "LIST-EXTENDED", "X-GM-EXT-1", + NULL, }; /** diff --git a/imap/imap_private.h b/imap/imap_private.h index 2d0b012eb..1ec17e0e5 100644 --- a/imap/imap_private.h +++ b/imap/imap_private.h @@ -133,9 +133,10 @@ typedef uint32_t ImapCapFlags; ///< Flags, e.g. #IMAP_CAP_IMAP4 #define IMAP_CAP_ENABLE (1 << 13) ///< RFC5161 #define IMAP_CAP_CONDSTORE (1 << 14) ///< RFC7162 #define IMAP_CAP_QRESYNC (1 << 15) ///< RFC7162 -#define IMAP_CAP_X_GM_EXT_1 (1 << 16) ///< https://developers.google.com/gmail/imap/imap-extensions +#define IMAP_CAP_LIST_EXTENDED (1 << 16) ///< RFC5258: IMAP4 LIST Command Extensions +#define IMAP_CAP_X_GM_EXT_1 (1 << 17) ///< https://developers.google.com/gmail/imap/imap-extensions -#define IMAP_CAP_ALL ((1 << 17) - 1) +#define IMAP_CAP_ALL ((1 << 18) - 1) /** * struct ImapList - Items in an IMAP browser -- 2.40.0