From fc83cb64135c949383881bcb9600f4cc9d9e58fe Mon Sep 17 00:00:00 2001 From: Kevin McCarthy Date: Thu, 17 Jul 2014 16:05:47 +0200 Subject: [PATCH] Add support for utf-8 mailboxes in imap. This adds support for RFC6855 to imap/*.c. Thanks to Arnt Gulbrandsen for the original patch. --- imap/browse.c | 7 +++++-- imap/command.c | 21 +++++++++++++++++++-- imap/imap.c | 25 ++++++++++++++----------- imap/imap_private.h | 15 ++++++++++----- imap/message.c | 4 ++-- imap/utf7.c | 22 ++++++++++++++++------ imap/util.c | 9 +++++---- 7 files changed, 71 insertions(+), 32 deletions(-) diff --git a/imap/browse.c b/imap/browse.c index 8b60bc78..2cd091dd 100644 --- a/imap/browse.c +++ b/imap/browse.c @@ -75,7 +75,7 @@ int imap_browse (char* path, struct browser_state* state) char *ptr; imap_fix_path (idata, mx.mbox, mbox, sizeof (mbox)); ptr = safe_strdup (mbox); - imap_utf7_encode (&ptr); + imap_utf_encode (idata, &ptr); mbox[sizeof (mbox) - 1] = '\0'; strncpy (mbox, ptr, sizeof (mbox) - 1); FREE (&ptr); @@ -400,11 +400,14 @@ static void imap_add_folder (char delim, char *folder, int noselect, char tmp[LONG_STRING]; char relpath[LONG_STRING]; IMAP_MBOX mx; + IMAP_DATA* idata; if (imap_parse_path (state->folder, &mx)) return; + if (!(idata = imap_conn_find (&(mx.account), 0))) + return; - imap_unmunge_mbox_name (folder); + imap_unmunge_mbox_name (idata, folder); if (state->entrylen + 1 == state->entrymax) { diff --git a/imap/command.c b/imap/command.c index 32f84178..5fd4e690 100644 --- a/imap/command.c +++ b/imap/command.c @@ -51,6 +51,7 @@ static void cmd_parse_fetch (IMAP_DATA* idata, char* s); static void cmd_parse_myrights (IMAP_DATA* idata, const char* s); static void cmd_parse_search (IMAP_DATA* idata, const char* s); static void cmd_parse_status (IMAP_DATA* idata, char* s); +static void cmd_parse_enabled (IMAP_DATA* idata, const char* s); static const char * const Capabilities[] = { "IMAP4", @@ -65,6 +66,7 @@ static const char * const Capabilities[] = { "LOGINDISABLED", "IDLE", "SASL-IR", + "ENABLE", NULL }; @@ -522,6 +524,8 @@ static int cmd_handle_untagged (IMAP_DATA* idata) cmd_parse_search (idata, s); else if (ascii_strncasecmp ("STATUS", s, 6) == 0) cmd_parse_status (idata, s); + else if (ascii_strncasecmp ("ENABLED", s, 7) == 0) + cmd_parse_enabled (idata, s); else if (ascii_strncasecmp ("BYE", s, 3) == 0) { dprint (2, (debugfile, "Handling BYE\n")); @@ -728,7 +732,7 @@ static void cmd_parse_list (IMAP_DATA* idata, char* s) } else { - imap_unmunge_mbox_name (s); + imap_unmunge_mbox_name (idata, s); list->name = s; } @@ -917,7 +921,7 @@ static void cmd_parse_status (IMAP_DATA* idata, char* s) { s = imap_next_word (mailbox); *(s - 1) = '\0'; - imap_unmunge_mbox_name (mailbox); + imap_unmunge_mbox_name (idata, mailbox); } status = imap_mboxcache_get (idata, mailbox, 1); @@ -1022,3 +1026,16 @@ static void cmd_parse_status (IMAP_DATA* idata, char* s) FREE (&mx.mbox); } } + +/* cmd_parse_enabled: record what the server has enabled */ +static void cmd_parse_enabled (IMAP_DATA* idata, const char* s) +{ + dprint (2, (debugfile, "Handling ENABLED\n")); + + while ((s = imap_next_word ((char*)s)) && *s != '\0') + { + if (ascii_strncasecmp(s, "UTF8=ACCEPT", 11) == 0 || + ascii_strncasecmp(s, "UTF8=ONLY", 9) == 0) + idata->unicode = 1; + } +} diff --git a/imap/imap.c b/imap/imap.c index ad15f5e1..b2fb2cba 100644 --- a/imap/imap.c +++ b/imap/imap.c @@ -92,7 +92,7 @@ int imap_access (const char* path, int flags) return 0; } - imap_munge_mbox_name (mbox, sizeof (mbox), mailbox); + imap_munge_mbox_name (idata, mbox, sizeof (mbox), mailbox); if (mutt_bit_isset (idata->capabilities, IMAP4REV1)) snprintf (buf, sizeof (buf), "STATUS %s (UIDVALIDITY)", mbox); @@ -117,7 +117,7 @@ int imap_create_mailbox (IMAP_DATA* idata, char* mailbox) { char buf[LONG_STRING], mbox[LONG_STRING]; - imap_munge_mbox_name (mbox, sizeof (mbox), mailbox); + imap_munge_mbox_name (idata, mbox, sizeof (mbox), mailbox); snprintf (buf, sizeof (buf), "CREATE %s", mbox); if (imap_exec (idata, buf, 0) != 0) @@ -135,8 +135,8 @@ int imap_rename_mailbox (IMAP_DATA* idata, IMAP_MBOX* mx, const char* newname) char newmbox[LONG_STRING]; char buf[LONG_STRING]; - imap_munge_mbox_name (oldmbox, sizeof (oldmbox), mx->mbox); - imap_munge_mbox_name (newmbox, sizeof (newmbox), newname); + imap_munge_mbox_name (idata, oldmbox, sizeof (oldmbox), mx->mbox); + imap_munge_mbox_name (idata, newmbox, sizeof (newmbox), newname); snprintf (buf, sizeof (buf), "RENAME %s %s", oldmbox, newmbox); @@ -162,7 +162,7 @@ int imap_delete_mailbox (CONTEXT* ctx, IMAP_MBOX mx) idata = ctx->data; } - imap_munge_mbox_name (mbox, sizeof (mbox), mx.mbox); + imap_munge_mbox_name (idata, mbox, sizeof (mbox), mx.mbox); snprintf (buf, sizeof (buf), "DELETE %s", mbox); if (imap_exec ((IMAP_DATA*) idata, buf, 0) != 0) @@ -386,6 +386,9 @@ IMAP_DATA* imap_conn_find (const ACCOUNT* account, int flags) { /* capabilities may have changed */ imap_exec (idata, "CAPABILITY", IMAP_CMD_QUEUE); + /* enable RFC6855, if the server supports that */ + if (mutt_bit_isset (idata->capabilities, ENABLE)) + imap_exec (idata, "ENABLE UTF8=ACCEPT", IMAP_CMD_QUEUE); /* get root delimiter, '/' as default */ idata->delim = '/'; imap_exec (idata, "LIST \"\" \"\"", IMAP_CMD_QUEUE); @@ -596,7 +599,7 @@ int imap_open_mailbox (CONTEXT* ctx) idata->newMailCount = 0; mutt_message (_("Selecting %s..."), idata->mailbox); - imap_munge_mbox_name (buf, sizeof(buf), idata->mailbox); + imap_munge_mbox_name (idata, buf, sizeof(buf), idata->mailbox); /* pipeline ACL test */ if (mutt_bit_isset (idata->capabilities, ACL)) @@ -1521,7 +1524,7 @@ int imap_buffy_check (int force) if (!lastdata) lastdata = idata; - imap_munge_mbox_name (munged, sizeof (munged), name); + imap_munge_mbox_name (idata, munged, sizeof (munged), name); snprintf (command, sizeof (command), "STATUS %s (UIDNEXT UIDVALIDITY UNSEEN RECENT)", munged); @@ -1569,9 +1572,9 @@ int imap_status (char* path, int queue) else if (mutt_bit_isset(idata->capabilities,IMAP4REV1) || mutt_bit_isset(idata->capabilities,STATUS)) { - imap_munge_mbox_name (mbox, sizeof(mbox), buf); + imap_munge_mbox_name (idata, mbox, sizeof(mbox), buf); snprintf (buf, sizeof (buf), "STATUS %s (%s)", mbox, "MESSAGES"); - imap_unmunge_mbox_name (mbox); + imap_unmunge_mbox_name (idata, mbox); } else /* Server does not support STATUS, and this is not the current mailbox. @@ -1851,14 +1854,14 @@ int imap_subscribe (char *path, int subscribe) mutt_message (_("Subscribing to %s..."), buf); else mutt_message (_("Unsubscribing from %s..."), buf); - imap_munge_mbox_name (mbox, sizeof(mbox), buf); + imap_munge_mbox_name (idata, mbox, sizeof(mbox), buf); snprintf (buf, sizeof (buf), "%sSUBSCRIBE %s", subscribe ? "" : "UN", mbox); if (imap_exec (idata, buf, 0) < 0) goto fail; - imap_unmunge_mbox_name(mx.mbox); + imap_unmunge_mbox_name(idata, mx.mbox); if (subscribe) mutt_message (_("Subscribed to %s"), mx.mbox); else diff --git a/imap/imap_private.h b/imap/imap_private.h index ab7ba8a9..ef1d2d21 100644 --- a/imap/imap_private.h +++ b/imap/imap_private.h @@ -115,6 +115,7 @@ enum LOGINDISABLED, /* LOGINDISABLED */ IDLE, /* RFC 2177: IDLE */ SASL_IR, /* SASL initial response draft */ + ENABLE, /* RFC 5161 */ CAPMAX }; @@ -144,7 +145,7 @@ typedef struct typedef struct { char* name; - + char delim; /* if we end up storing a lot of these we could turn this into a bitfield */ unsigned char noselect; @@ -186,6 +187,10 @@ typedef struct char* buf; unsigned int blen; + /* If nonzero, we can send UTF-8, and the server will use UTF8 rather + * than mUTF7 */ + int unicode; + /* if set, the response parser will store results for complicated commands * here. */ IMAP_COMMAND_TYPE cmdtype; @@ -289,13 +294,13 @@ void imap_make_date (char* buf, time_t timestamp); void imap_qualify_path (char *dest, size_t len, IMAP_MBOX *mx, char* path); void imap_quote_string (char* dest, size_t slen, const char* src); void imap_unquote_string (char* s); -void imap_munge_mbox_name (char *dest, size_t dlen, const char *src); -void imap_unmunge_mbox_name (char *s); +void imap_munge_mbox_name (IMAP_DATA *idata, char *dest, size_t dlen, const char *src); +void imap_unmunge_mbox_name (IMAP_DATA *idata, char *s); int imap_wordcasecmp(const char *a, const char *b); /* utf7.c */ -void imap_utf7_encode (char **s); -void imap_utf7_decode (char **s); +void imap_utf_encode (IMAP_DATA *idata, char **s); +void imap_utf_decode (IMAP_DATA *idata, char **s); #if USE_HCACHE /* typedef size_t (*hcache_keylen_t)(const char* fn); */ diff --git a/imap/message.c b/imap/message.c index 38773818..3972f60f 100644 --- a/imap/message.c +++ b/imap/message.c @@ -642,7 +642,7 @@ int imap_append_message (CONTEXT *ctx, MESSAGE *msg) mutt_progress_init (&progressbar, _("Uploading message..."), M_PROGRESS_SIZE, NetInc, len); - imap_munge_mbox_name (mbox, sizeof (mbox), mailbox); + imap_munge_mbox_name (idata, mbox, sizeof (mbox), mailbox); imap_make_date (internaldate, msg->received); imap_flags[0] = imap_flags[1] = 0; @@ -773,7 +773,7 @@ int imap_copy_messages (CONTEXT* ctx, HEADER* h, char* dest, int delete) imap_fix_path (idata, mx.mbox, mbox, sizeof (mbox)); if (!*mbox) strfcpy (mbox, "INBOX", sizeof (mbox)); - imap_munge_mbox_name (mmbox, sizeof (mmbox), mbox); + imap_munge_mbox_name (idata, mmbox, sizeof (mmbox), mbox); /* loop in case of TRYCREATE */ do diff --git a/imap/utf7.c b/imap/utf7.c index a433971c..ed8723c8 100644 --- a/imap/utf7.c +++ b/imap/utf7.c @@ -252,30 +252,40 @@ static char *utf8_to_utf7 (const char *u8, size_t u8len, char **u7, return 0; } -void imap_utf7_encode (char **s) +void imap_utf_encode (IMAP_DATA *idata, char **s) { if (Charset) { char *t = safe_strdup (*s); - if (!mutt_convert_string (&t, Charset, "utf-8", 0)) + if (t && !mutt_convert_string (&t, Charset, "utf-8", 0)) { - char *u7 = utf8_to_utf7 (t, strlen (t), NULL, 0); FREE (s); /* __FREE_CHECKED__ */ - *s = u7; + if (idata->unicode) + *s = safe_strdup (t); + else + *s = utf8_to_utf7 (t, strlen (t), NULL, 0); } FREE (&t); } } -void imap_utf7_decode (char **s) +void imap_utf_decode (IMAP_DATA *idata, char **s) { + char *t; + if (Charset) { - char *t = utf7_to_utf8 (*s, strlen (*s), 0, 0); + if (idata->unicode) + t = safe_strdup (*s); + else + t = utf7_to_utf8 (*s, strlen (*s), 0, 0); + if (t && !mutt_convert_string (&t, "utf-8", Charset, 0)) { FREE (s); /* __FREE_CHECKED__ */ *s = t; } + else + FREE (&t); } } diff --git a/imap/util.c b/imap/util.c index c05465a3..9b9f07f6 100644 --- a/imap/util.c +++ b/imap/util.c @@ -673,23 +673,24 @@ void imap_unquote_string (char *s) *d = '\0'; } + /* * Quoting and UTF-7 conversion */ -void imap_munge_mbox_name (char *dest, size_t dlen, const char *src) +void imap_munge_mbox_name (IMAP_DATA *idata, char *dest, size_t dlen, const char *src) { char *buf; buf = safe_strdup (src); - imap_utf7_encode (&buf); + imap_utf_encode (idata, &buf); imap_quote_string (dest, dlen, buf); FREE (&buf); } -void imap_unmunge_mbox_name (char *s) +void imap_unmunge_mbox_name (IMAP_DATA *idata, char *s) { char *buf; @@ -698,7 +699,7 @@ void imap_unmunge_mbox_name (char *s) buf = safe_strdup (s); if (buf) { - imap_utf7_decode (&buf); + imap_utf_decode (idata, &buf); strncpy (s, buf, strlen (s)); } -- 2.40.0