So, GMAIL has an XLIST command which names a few special folders by adding extra flags. Things like \Inbox, \Trash, \Drafts, \Sent.
You can read more about it here: http://www.ietf.org/mail-archive/web/morg/current/msg00212.html http://groups.google.com/group/Gmail-Help-POP-and-IMAP-en/browse_thread/thread/a154105c54f020fb?pli=1 https://bugzilla.mozilla.org/show_bug.cgi?id=476260#c17 It means that Thunderbird can know what you folders are even if the names don't match its default, display the right icons and copy messages into the right places. Well, I thought that might come in handy, so this. Bron.
diff --git a/imap/imapd.c b/imap/imapd.c index 002a21c..5a5fce2 100644 --- a/imap/imapd.c +++ b/imap/imapd.c @@ -1965,6 +1965,41 @@ void cmdloop() (havepartition ? arg3.s : NULL)); /* snmp_increment(XFER_COUNT, 1);*/ } + else if (!strcmp(cmd.s, "Xlist")) { + struct listargs listargs; + + memset(&listargs, 0, sizeof(struct listargs)); + listargs.opts = LIST_CHILDREN | LIST_XLIST; +#ifdef ENABLE_LISTEXT + /* Check for and parse LISTEXT options */ + c = prot_getc(imapd_in); + if (c == '(') { + c = getlistopts(tag.s, &listargs.opts); + if (c == EOF) { + eatline(imapd_in, c); + continue; + } + } + else + prot_ungetc(c, imapd_in); +#endif /* ENABLE_LISTEXT */ + if (imapd_magicplus) listargs.opts += LIST_SUBSCRIBED; + + c = getastring(imapd_in, imapd_out, &arg1); + if (c != ' ') goto missingargs; + c = getastring(imapd_in, imapd_out, &arg2); + if (c == '\r') c = prot_getc(imapd_in); + if (c != '\n') goto extraargs; + + listargs.ref = arg1.s; + listargs.pat = arg2.s; + cmd_list(tag.s, &listargs); + + snmp_increment(LIST_COUNT, 1); + } + + + else goto badcmd; break; @@ -9454,6 +9489,69 @@ static int mailboxdata(char *name, return 0; } +struct xlist_rock { + int *dospaceptr; + const char *mboxname; +}; + +static void xlist_check(const char *key, const char *val, void *rock) +{ + struct xlist_rock *r = (struct xlist_rock *)rock; + char *flag; + + if (strncmp(key, "xlist-", 6)) + return; + + if (strcmp(val, r->mboxname)) + return; + + if (*r->dospaceptr) + prot_putc(' ', imapd_out); + + flag = xstrdup(key + 6); + lcase(flag); + flag[0] = toupper((unsigned char)flag[0]); + prot_printf(imapd_out, "\\%s", flag); + free(flag); + + *r->dospaceptr = 1; +} + +static void xlist_flags(char *name, int *dospaceptr) +{ + int ulen; + const char *mbox; + + /* inbox is magic */ + if (!strncasecmp(name, "inbox", 5)) { + ulen = 5; + mbox = name; + } else { + mbox = mboxname_isusermailbox(name, 0); + if (!mbox) + return; + ulen = strlen(imapd_userid); + + /* not THIS user's mbox */ + if (strncmp(mbox, imapd_userid, ulen) || !(mbox[ulen] == '\0' || mbox[ulen] == '.')) + return; + } + + /* subdir */ + if (mbox[ulen]) { + struct xlist_rock rock; + rock.dospaceptr = dospaceptr; + rock.mboxname = mbox + ulen + 1; + config_foreachoverflowstring(xlist_check, &rock); + } + else { + if (*dospaceptr) + prot_putc(' ', imapd_out); + prot_printf(imapd_out, "\\Inbox"); + *dospaceptr = 1; + } +} + /* * Issue a LIST or LSUB untagged response */ @@ -9466,6 +9564,7 @@ static void mstringdata(char *cmd, char *name, int matchlen, int maycreate, static int nonexistent = 0; static int sawuser = 0; int lastnamehassub = 0; + int dospace; int c, mbtype; char mboxname[MAX_MAILBOX_BUFFER]; @@ -9486,25 +9585,54 @@ static void mstringdata(char *cmd, char *name, int matchlen, int maycreate, lastnamehassub = 1; } prot_printf(imapd_out, "* %s (", cmd); + dospace = 0; + if (nonexistent == IMAP_MAILBOX_RESERVED) { + if (dospace) + prot_putc(' ', imapd_out); + /* LISTEXT wants \\PlaceHolder instead of \\Noselect */ if (listopts & LIST_EXT) prot_printf(imapd_out, "\\PlaceHolder"); else prot_printf(imapd_out, "\\Noselect"); + + dospace = 1; } else if (nonexistent) { + if (dospace) + prot_putc(' ', imapd_out); + prot_printf(imapd_out, "\\NonExistent"); + + dospace = 1; } + if (lastnamenoinferiors) { - prot_printf(imapd_out, "%s\\Noinferiors", nonexistent ? " " : ""); + if (dospace) + prot_putc(' ', imapd_out); + + prot_printf(imapd_out, "\\Noinferiors"); + + dospace = 1; } else if ((listopts & LIST_CHILDREN) && /* we can't determine \HasNoChildren for subscriptions */ (lastnamehassub || !(listopts & (LIST_LSUB | LIST_SUBSCRIBED)))) { - prot_printf(imapd_out, "%s%s", nonexistent ? " " : "", - lastnamehassub ? "\\HasChildren" : "\\HasNoChildren"); + if (dospace) + prot_putc(' ', imapd_out); + + if (lastnamehassub) + prot_printf(imapd_out, "\\HasChildren"); + else + prot_printf(imapd_out, "\\HasNoChildren"); + + dospace = 1; } + + if (listopts & LIST_XLIST) + xlist_flags(lastname, &dospace); + prot_printf(imapd_out, ") \"%c\" ", imapd_namespace.hier_sep); (*imapd_namespace.mboxname_toexternal)(&imapd_namespace, lastname, @@ -9562,28 +9690,54 @@ static void mstringdata(char *cmd, char *name, int matchlen, int maycreate, return; } - c = name[matchlen]; - if (c) name[matchlen] = '\0'; prot_printf(imapd_out, "* %s (", cmd); + dospace = 0; + + c = name[matchlen]; if (c) { + name[matchlen] = '\0'; + /* Handle namespace prefix as a special case */ if (!strcmp(name, "user") || !strcmp(name, imapd_namespace.prefix[NAMESPACE_SHARED])) { + if (dospace) + prot_putc(' ', imapd_out); + prot_printf(imapd_out, "\\Noselect"); if (listopts & LIST_EXT) prot_printf(imapd_out, " \\PlaceHolder"); + + dospace = 1; } else { - if (nonexistent) + if (nonexistent) { + if (dospace) + prot_putc(' ', imapd_out); prot_printf(imapd_out, "\\NonExistent"); + dospace = 1; + } + + if (dospace) + prot_putc(' ', imapd_out); + /* LISTEXT uses \PlaceHolder instead of \Noselect */ if (listopts & LIST_EXT) - prot_printf(imapd_out, "%s\\PlaceHolder", nonexistent ? " " : ""); + prot_printf(imapd_out, "\\PlaceHolder"); else - prot_printf(imapd_out, "%s\\Noselect", nonexistent ? " " : ""); + prot_printf(imapd_out, "\\Noselect"); + + dospace = 1; } - if (listopts & LIST_CHILDREN) - prot_printf(imapd_out, " \\HasChildren"); + + if (listopts & LIST_CHILDREN) { + if (dospace) + prot_putc(' ', imapd_out); + prot_printf(imapd_out, "\\HasChildren"); + dospace = 1; + } + + if (listopts & LIST_XLIST) + xlist_flags(name, &dospace); } prot_printf(imapd_out, ") \"%c\" ", imapd_namespace.hier_sep); @@ -9682,7 +9836,8 @@ static int listdata(char *name, int matchlen, int maycreate, void *rock) } } - mstringdata(((listargs->opts & LIST_LSUB) ? "LSUB" : "LIST"), + mstringdata(((listargs->opts & LIST_XLIST) ? "XLIST" : + (listargs->opts & LIST_LSUB) ? "LSUB" : "LIST"), name, matchlen, maycreate, listargs->opts); return 0; diff --git a/imap/imapd.h b/imap/imapd.h index 6535369..b943ac1 100644 --- a/imap/imapd.h +++ b/imap/imapd.h @@ -242,7 +242,8 @@ enum { LIST_EXT = (1<<1), LIST_SUBSCRIBED = (1<<2), LIST_CHILDREN = (1<<3), - LIST_REMOTE = (1<<4) + LIST_REMOTE = (1<<4), + LIST_XLIST = (1<<5) }; extern struct protstream *imapd_out, *imapd_in; diff --git a/imap/version.h b/imap/version.h index d3b58d4..d2b5a1b 100644 --- a/imap/version.h +++ b/imap/version.h @@ -69,7 +69,7 @@ enum { "NO_ATOMIC_RENAME UNSELECT " \ "CHILDREN MULTIAPPEND BINARY " \ "SORT SORT=MODSEQ THREAD=ORDEREDSUBJECT THREAD=REFERENCES " \ - "ANNOTATEMORE CATENATE CONDSTORE SCAN" + "ANNOTATEMORE CATENATE CONDSTORE SCAN XLIST" /* Values for ID processing */