[PATCH] Improve accuracy of hierarchy view when browsing (subscribed) IMAP folders

Naveen Nathan mutt at t.lastninja.net
Tue Apr 23 17:04:05 UTC 2019


Hi,

This is to fix an annoyance where the imap browser may inaccurately
display hierarchical visual cues ('+') to folders that have no
children. This happens when the \HasNoChildren flag is absent in
IMAP LIST/LSUB responses (thanks Kevin for figuring this out).

RFC3348 [1] section 3 specifically states that LSUB mustn't be relied on
for hierarchical information and suggests using LIST command extensions
instead.

RFC5258 [2] specifies the LIST command extensions which supports both
listing subscribed folders and an option to ensure responses return
hierarchy information.

This patch implements the LIST command extension and uses it for both
listing folders and subscribed folders. If the LIST-EXTENDED capability
is not offered, then mutt will use the regular LIST/LSUB commands.

[1]: https://tools.ietf.org/html/rfc3348
[2]: https://tools.ietf.org/html/rfc5258
-------------- next part --------------
>From 32d3398c640dd9c93f45a5d5e8ef02df3ca8b11a Mon Sep 17 00:00:00 2001
From: Naveen Nathan <naveen at lastninja.net>
Date: Wed, 24 Apr 2019 01:34:02 +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       | 25 +++++++++++++++++++++----
 imap/command.c      |  1 +
 imap/imap_private.h |  1 +
 3 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/imap/browse.c b/imap/browse.c
index 1aa4d357..ec97683f 100644
--- a/imap/browse.c
+++ b/imap/browse.c
@@ -46,7 +46,8 @@ int imap_browse (const char* path, struct browser_state* state)
   char buf[LONG_STRING*2];
   char mbox[LONG_STRING];
   char munged_mbox[LONG_STRING];
-  char list_cmd[5];
+  char list_cmd[18];
+  int len;
   int n;
   int nsup;
   char ctmp;
@@ -62,11 +63,23 @@ int imap_browse (const char* path, struct browser_state* state)
 
   save_lsub = option (OPTIMAPCHECKSUBSCRIBED);
   unset_option (OPTIMAPCHECKSUBSCRIBED);
-  strfcpy (list_cmd, option (OPTIMAPLSUB) ? "LSUB" : "LIST", sizeof (list_cmd));
 
   if (!(idata = imap_conn_find (&(mx.account), 0)))
     goto fail;
 
+  if (option (OPTIMAPLSUB)) {
+    const char *lsub_cmd = "LSUB";
+
+    /* RFC3348 section 3 states LSUB is unreliable for hierarchy information.
+     * The newer LIST extensions are designed for this.
+     */
+    if (mutt_bit_isset (idata->capabilities, LIST_EXTENDED))
+      lsub_cmd = "LIST (SUBSCRIBED)";
+    strfcpy (list_cmd, lsub_cmd, sizeof (list_cmd));
+  } else {
+    strfcpy (list_cmd, "LIST", sizeof (list_cmd));
+  }
+
   mutt_message _("Getting folder list...");
 
   /* skip check for parents when at the root */
@@ -89,7 +102,9 @@ int imap_browse (const char* path, struct browser_state* state)
     /* if our target exists and has inferiors, enter it if we
      * aren't already going to */
     imap_munge_mbox_name (idata, 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 (mutt_bit_isset (idata->capabilities, LIST_EXTENDED))
+      snprintf (buf + len, sizeof(buf) - len, " RETURN (CHILDREN)");
     imap_cmd_start (idata, buf);
     idata->cmdtype = IMAP_CT_LIST;
     idata->cmddata = &list;
@@ -181,7 +196,9 @@ int imap_browse (const char* path, struct browser_state* state)
   snprintf (buf, sizeof (buf), "%s%%", mbox);
   imap_munge_mbox_name (idata, munged_mbox, sizeof (munged_mbox), buf);
   dprint (3, (debugfile, "%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 (mutt_bit_isset (idata->capabilities, LIST_EXTENDED))
+    snprintf (buf + len, sizeof(buf) - len, " RETURN (CHILDREN)");
   if (browse_add_list_result (idata, buf, state, 0))
     goto fail;
 
diff --git a/imap/command.c b/imap/command.c
index 21f6a40b..91457a11 100644
--- a/imap/command.c
+++ b/imap/command.c
@@ -73,6 +73,7 @@ static const char * const Capabilities[] = {
   "ENABLE",
   "CONDSTORE",
   "QRESYNC",
+  "LIST-EXTENDED",
 
   NULL
 };
diff --git a/imap/imap_private.h b/imap/imap_private.h
index 07523672..7d12ec25 100644
--- a/imap/imap_private.h
+++ b/imap/imap_private.h
@@ -120,6 +120,7 @@ enum
   ENABLE,                       /* RFC 5161 */
   CONDSTORE,                    /* RFC 7162 */
   QRESYNC,                      /* RFC 7162 */
+  LIST_EXTENDED,                /* RFC 5258: IMAP4 - LIST Command Extensions */
 
   CAPMAX
 };
-- 
2.16.2



More information about the Mutt-dev mailing list