changeset: 6527:814ece9689cc user: Kevin McCarthy <ke...@8t8.us> date: Tue Nov 24 15:49:26 2015 -0800 link: http://dev.mutt.org/hg/mutt/rev/814ece9689cc
Rename idna functions and bits for smtputf8 changes. This is patch 1 of 4 implementing support for SMTPUTF8 (RFC 6531). Change mutt_idna.c to be always compiled. Remove the stub functions in mutt_idna.h. Instead, put #ifdefs around the idna function calls. The conversion functions will be fixed up in the next patch. Rename the conversion functions to mutt_addrlist_to_intl() and mutt_env_to_intl(). Rename the ADDRESS idna status bits to "intl" status bits. changeset: 6528:831abf39d53a user: Kevin McCarthy <ke...@8t8.us> date: Tue Nov 24 15:49:27 2015 -0800 link: http://dev.mutt.org/hg/mutt/rev/831abf39d53a Rewrite address local-to-intl conversion functions. This is patch 2 of 4 implementing support for SMTPUTF8 (RFC 6531). Perform charset conversion from local to UTF-8 for both the user and domain parts of the address. If IDN is enabled and the options (added in the next patch) are turned on, encode/decode the domain part. Use the intl_checked and is_intl status bits to record the intl/local status of the ADDRESS mailbox part. changeset: 6529:151ff413e21a user: Kevin McCarthy <ke...@8t8.us> date: Tue Nov 24 15:49:28 2015 -0800 link: http://dev.mutt.org/hg/mutt/rev/151ff413e21a Add option 'idn_encode'; rename option 'use_idn' to 'idn_decode'. This is patch 3 of 4 implementing support for SMTPUTF8 (RFC 6531). Add an option to control whether international domains are encoded with IDN or not. This defaults to set, for backward compatibility. Rename the use_idn option to idn_decode, since that more properly reflects its purpose. changeset: 6530:935c779db0ea user: Kevin McCarthy <ke...@8t8.us> date: Tue Nov 24 15:49:29 2015 -0800 link: http://dev.mutt.org/hg/mutt/rev/935c779db0ea Implement SMTPUTF8 capability support in smtp.c This is patch 4 of 4 implementing support for SMTPUTF8 (RFC 6531). RFC6532 support already worked: rfc822*.c already parsed messages in exactly the way RFC6532 wants. Thanks for Arnt Gulbrandsen for the original patch. changeset: 6531:0d7ce56bbafd user: Kevin McCarthy <ke...@8t8.us> date: Tue Nov 24 15:49:31 2015 -0800 link: http://dev.mutt.org/hg/mutt/rev/0d7ce56bbafd Add user reversibility check in intl_to_local. This ensures we don't lose information by converting to the local charset. diffs (truncated from 953 to 950 lines): diff -r e635ce43b001 -r 0d7ce56bbafd Makefile.am --- a/Makefile.am Sat Nov 21 15:28:57 2015 -0800 +++ b/Makefile.am Tue Nov 24 15:49:31 2015 -0800 @@ -33,7 +33,7 @@ rfc822.c rfc1524.c rfc2047.c rfc2231.c rfc3676.c \ score.c send.c sendlib.c signal.c sort.c \ status.c system.c thread.c charset.c history.c lib.c \ - muttlib.c editmsg.c mbyte.c \ + muttlib.c editmsg.c mbyte.c mutt_idna.c \ url.c ascii.c crypt-mod.c crypt-mod.h safe_asprintf.c nodist_mutt_SOURCES = $(BUILT_SOURCES) @@ -53,7 +53,7 @@ EXTRA_mutt_SOURCES = account.c bcache.c crypt-gpgme.c crypt-mod-pgp-classic.c \ crypt-mod-pgp-gpgme.c crypt-mod-smime-classic.c \ crypt-mod-smime-gpgme.c dotlock.c gnupgparse.c hcache.c md5.c \ - mutt_idna.c mutt_sasl.c mutt_socket.c mutt_ssl.c mutt_ssl_gnutls.c \ + mutt_sasl.c mutt_socket.c mutt_ssl.c mutt_ssl_gnutls.c \ mutt_tunnel.c pgp.c pgpinvoke.c pgpkey.c pgplib.c pgpmicalg.c \ pgppacket.c pop.c pop_auth.c pop_lib.c remailer.c resize.c sha1.c \ smime.c smtp.c utf8.c wcwidth.c \ diff -r e635ce43b001 -r 0d7ce56bbafd alias.c --- a/alias.c Sat Nov 21 15:28:57 2015 -0800 +++ b/alias.c Tue Nov 24 15:49:31 2015 -0800 @@ -290,7 +290,7 @@ else buf[0] = 0; - mutt_addrlist_to_idna (adr, NULL); + mutt_addrlist_to_intl (adr, NULL); do { @@ -302,7 +302,7 @@ if((new->addr = rfc822_parse_adrlist (new->addr, buf)) == NULL) BEEP (); - if (mutt_addrlist_to_idna (new->addr, &err)) + if (mutt_addrlist_to_intl (new->addr, &err)) { mutt_error (_("Error: '%s' is a bad IDN."), err); mutt_sleep (2); diff -r e635ce43b001 -r 0d7ce56bbafd commands.c --- a/commands.c Sat Nov 21 15:28:57 2015 -0800 +++ b/commands.c Tue Nov 24 15:49:31 2015 -0800 @@ -294,7 +294,7 @@ adr = mutt_expand_aliases (adr); - if (mutt_addrlist_to_idna (adr, &err) < 0) + if (mutt_addrlist_to_intl (adr, &err) < 0) { mutt_error (_("Bad IDN: '%s'"), err); FREE (&err); diff -r e635ce43b001 -r 0d7ce56bbafd compose.c --- a/compose.c Sat Nov 21 15:28:57 2015 -0800 +++ b/compose.c Tue Nov 24 15:49:31 2015 -0800 @@ -292,7 +292,7 @@ return (REDRAW_FULL); } - if (mutt_addrlist_to_idna (*addr, &err) != 0) + if (mutt_addrlist_to_intl (*addr, &err) != 0) { mutt_error (_("Warning: '%s' is a bad IDN."), err); mutt_refresh(); @@ -606,7 +606,7 @@ mutt_env_to_local (msg->env); mutt_edit_headers (NONULL (Editor), msg->content->filename, msg, fcc, fcclen); - if (mutt_env_to_idna (msg->env, &tag, &err)) + if (mutt_env_to_intl (msg->env, &tag, &err)) { mutt_error (_("Bad IDN in \"%s\": '%s'"), tag, err); FREE (&err); diff -r e635ce43b001 -r 0d7ce56bbafd configure.ac --- a/configure.ac Sat Nov 21 15:28:57 2015 -0800 +++ b/configure.ac Tue Nov 24 15:49:31 2015 -0800 @@ -1196,7 +1196,6 @@ AC_SEARCH_LIBS([stringprep_check_version], [idn], [ AC_DEFINE([HAVE_LIBIDN], 1, [Define to 1 if you have the GNU idn library]) - MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS mutt_idna.o" MUTTLIBS="$MUTTLIBS $LIBS" LIBS="$LIBS $LIBICONV" diff -r e635ce43b001 -r 0d7ce56bbafd edit.c --- a/edit.c Sat Nov 21 15:28:57 2015 -0800 +++ b/edit.c Tue Nov 24 15:49:31 2015 -0800 @@ -247,7 +247,7 @@ rfc822_free_address (&e->to); e->to = mutt_parse_adrlist (e->to, tmp); e->to = mutt_expand_aliases (e->to); - mutt_addrlist_to_idna (e->to, NULL); /* XXX - IDNA error reporting? */ + mutt_addrlist_to_intl (e->to, NULL); /* XXX - IDNA error reporting? */ tmp[0] = 0; rfc822_write_address (tmp, sizeof (tmp), e->to, 1); mvaddstr (LINES - 1, 4, tmp); @@ -255,7 +255,7 @@ } else { - mutt_addrlist_to_idna (e->to, NULL); /* XXX - IDNA error reporting? */ + mutt_addrlist_to_intl (e->to, NULL); /* XXX - IDNA error reporting? */ addstr (tmp); } addch ('\n'); @@ -281,12 +281,12 @@ e->cc = mutt_parse_adrlist (e->cc, tmp); e->cc = mutt_expand_aliases (e->cc); tmp[0] = 0; - mutt_addrlist_to_idna (e->cc, NULL); + mutt_addrlist_to_intl (e->cc, NULL); rfc822_write_address (tmp, sizeof (tmp), e->cc, 1); mvaddstr (LINES - 1, 4, tmp); } else - mutt_addrlist_to_idna (e->cc, NULL); + mutt_addrlist_to_intl (e->cc, NULL); addch ('\n'); } @@ -301,13 +301,13 @@ rfc822_free_address (&e->bcc); e->bcc = mutt_parse_adrlist (e->bcc, tmp); e->bcc = mutt_expand_aliases (e->bcc); - mutt_addrlist_to_idna (e->bcc, NULL); + mutt_addrlist_to_intl (e->bcc, NULL); tmp[0] = 0; rfc822_write_address (tmp, sizeof (tmp), e->bcc, 1); mvaddstr (LINES - 1, 5, tmp); } else - mutt_addrlist_to_idna (e->bcc, NULL); + mutt_addrlist_to_intl (e->bcc, NULL); addch ('\n'); } } @@ -447,7 +447,7 @@ { mutt_env_to_local (msg->env); mutt_edit_headers (NONULL(Visual), path, msg, NULL, 0); - if (mutt_env_to_idna (msg->env, &tag, &err)) + if (mutt_env_to_intl (msg->env, &tag, &err)) printw (_("Bad IDN in %s: '%s'\n"), tag, err); } else diff -r e635ce43b001 -r 0d7ce56bbafd init.c --- a/init.c Sat Nov 21 15:28:57 2015 -0800 +++ b/init.c Tue Nov 24 15:49:31 2015 -0800 @@ -874,7 +874,7 @@ case ADDR: if ((addr = mutt_parse_adrlist (NULL, buf->data)) == NULL) goto bail; - if (mutt_addrlist_to_idna (addr, &estr)) + if (mutt_addrlist_to_intl (addr, &estr)) { snprintf (err->data, err->dsize, _("%sgroup: warning: bad IDN '%s'.\n"), data == 1 ? "un" : "", estr); @@ -1339,7 +1339,7 @@ last->next = tmp; else Aliases = tmp; - if (mutt_addrlist_to_idna (tmp->addr, &estr)) + if (mutt_addrlist_to_intl (tmp->addr, &estr)) { snprintf (err->data, err->dsize, _("Warning: Bad IDN '%s' in alias '%s'.\n"), estr, tmp->name); diff -r e635ce43b001 -r 0d7ce56bbafd init.h --- a/init.h Sat Nov 21 15:28:57 2015 -0800 +++ b/init.h Tue Nov 24 15:49:31 2015 -0800 @@ -1074,6 +1074,22 @@ ** .pp ** Also see $$use_domain and $$hidden_host. */ +#ifdef HAVE_LIBIDN + { "idn_decode", DT_BOOL, R_BOTH, OPTIDNDECODE, 1}, + /* + ** .pp + ** When \fIset\fP, Mutt will show you international domain names decoded. + ** Note: You can use IDNs for addresses even if this is \fIunset\fP. + ** This variable only affects decoding. (IDN only) + */ + { "idn_encode", DT_BOOL, R_BOTH, OPTIDNENCODE, 1}, + /* + ** .pp + ** When \fIset\fP, Mutt will encode international domain names using + ** IDN. Unset this if your SMTP server can handle newer (RFC 6531) + ** UTF-8 encoded domains. (IDN only) + */ +#endif /* HAVE_LIBIDN */ { "ignore_linear_white_space", DT_BOOL, R_NONE, OPTIGNORELWS, 0 }, /* ** .pp @@ -3409,15 +3425,6 @@ ** generated unless the user explicitly sets one using the ``$my_hdr'' ** command. */ -#ifdef HAVE_LIBIDN - { "use_idn", DT_BOOL, R_BOTH, OPTUSEIDN, 1}, - /* - ** .pp - ** When \fIset\fP, Mutt will show you international domain names decoded. - ** Note: You can use IDNs for addresses even if this is \fIunset\fP. - ** This variable only affects decoding. - */ -#endif /* HAVE_LIBIDN */ #ifdef HAVE_GETADDRINFO { "use_ipv6", DT_BOOL, R_NONE, OPTUSEIPV6, 1}, /* diff -r e635ce43b001 -r 0d7ce56bbafd main.c --- a/main.c Sat Nov 21 15:28:57 2015 -0800 +++ b/main.c Tue Nov 24 15:49:31 2015 -0800 @@ -803,7 +803,7 @@ if ((a = mutt_lookup_alias (alias_queries->data))) { /* output in machine-readable form */ - mutt_addrlist_to_idna (a, NULL); + mutt_addrlist_to_intl (a, NULL); mutt_write_address_list (a, stdout, 0, 0); } else diff -r e635ce43b001 -r 0d7ce56bbafd mutt.h --- a/mutt.h Sat Nov 21 15:28:57 2015 -0800 +++ b/mutt.h Tue Nov 24 15:49:31 2015 -0800 @@ -438,7 +438,8 @@ OPTUSEFROM, OPTUSEGPGAGENT, #ifdef HAVE_LIBIDN - OPTUSEIDN, + OPTIDNDECODE, + OPTIDNENCODE, #endif #ifdef HAVE_GETADDRINFO OPTUSEIPV6, diff -r e635ce43b001 -r 0d7ce56bbafd mutt_idna.c --- a/mutt_idna.c Sat Nov 21 15:28:57 2015 -0800 +++ b/mutt_idna.c Tue Nov 24 15:49:31 2015 -0800 @@ -24,195 +24,257 @@ #include "charset.h" #include "mutt_idna.h" -/* The low-level interface we use. */ - #ifdef HAVE_LIBIDN - -/* check whether an address is an IDN */ - -static int check_idn (ADDRESS *ap) +static int check_idn (char *domain) { - char *p = 0; - - if (!ap || !ap->mailbox) + if (! domain) return 0; - if (!ap->idn_checked) + if (ascii_strncasecmp (domain, "xn--", 4) == 0) + return 1; + + while ((domain = strchr (domain, '.')) != NULL) { - ap->idn_checked = 1; - for (p = strchr (ap->mailbox, '@'); p && *p; p = strchr (p, '.')) - if (ascii_strncasecmp (++p, "xn--", 4) == 0) - { - ap->is_idn = 1; - break; - } - } - - return ap->is_idn; -} - -static int mutt_idna_to_local (const char *in, char **out, int flags) -{ - *out = NULL; - - if (!option (OPTUSEIDN)) - goto notrans; - - if (!in) - goto notrans; - - /* Is this the right function? Interesting effects with some bad identifiers! */ - if (idna_to_unicode_8z8z (in, out, IDNA_ALLOW_UNASSIGNED) != IDNA_SUCCESS) - goto notrans; - - /* we don't want charset-hook effects, so we set flags to 0 */ - if (mutt_convert_string (out, "utf-8", Charset, 0) == -1) - goto notrans; - - /* - * make sure that we can convert back and come out with the same - * domain name. - */ - - if ((flags & MI_MAY_BE_IRREVERSIBLE) == 0) - { - int irrev = 0; - char *t2 = NULL; - char *tmp = safe_strdup (*out); - - /* we don't want charset-hook effects, so we set flags to 0 */ - if (mutt_convert_string (&tmp, Charset, "utf-8", 0) == -1) - irrev = 1; - if (!irrev && idna_to_ascii_8z (tmp, &t2, IDNA_ALLOW_UNASSIGNED) != IDNA_SUCCESS) - irrev = 1; - if (!irrev && ascii_strcasecmp (t2, in)) - { - dprint (1, (debugfile, "mutt_idna_to_local: Not reversible. in = '%s', t2 = '%s'.\n", - in, t2)); - irrev = 1; - } - - FREE (&t2); - FREE (&tmp); - - if (irrev) - goto notrans; + if (ascii_strncasecmp (++domain, "xn--", 4) == 0) + return 1; } return 0; - - notrans: - FREE (out); /* __FREE_CHECKED__ */ - *out = safe_strdup (in); - return 1; +} +#endif /* HAVE_LIBIDN */ + +static int mbox_to_udomain (const char *mbx, char **user, char **domain) +{ + char *buff = NULL; + char *p; + + buff = safe_strdup (mbx); + p = strchr (buff, '@'); + if (!p || !p[1]) + { + FREE (&buff); + return -1; + } + + *p = '\0'; + *user = safe_strdup (buff); + *domain = safe_strdup (p + 1); + FREE (&buff); + return 0; } -static int mutt_local_to_idna (const char *in, char **out) +static int addr_is_local (ADDRESS *a) { - int rv = 0; - char *tmp = safe_strdup (in); - *out = NULL; + return (a->intl_checked && !a->is_intl); +} - if (!in) +static int addr_is_intl (ADDRESS *a) +{ + return (a->intl_checked && a->is_intl); +} + +static void set_local_mailbox (ADDRESS *a, char *local_mailbox) +{ + FREE (&a->mailbox); + a->mailbox = local_mailbox; + a->intl_checked = 1; + a->is_intl = 0; +} + +static void set_intl_mailbox (ADDRESS *a, char *intl_mailbox) +{ + FREE (&a->mailbox); + a->mailbox = intl_mailbox; + a->intl_checked = 1; + a->is_intl = 1; +} + +static char *intl_to_local (ADDRESS *a, int flags) +{ + char *user = NULL, *domain = NULL, *mailbox = NULL; + char *orig_user = NULL, *reversed_user = NULL; + char *orig_domain = NULL, *reversed_domain = NULL; + char *tmp = NULL; +#ifdef HAVE_LIBIDN + int is_idn_encoded = 0; +#endif /* HAVE_LIBIDN */ + + if (mbox_to_udomain (a->mailbox, &user, &domain) == -1) + goto cleanup; + orig_user = safe_strdup (user); + orig_domain = safe_strdup (domain); + +#ifdef HAVE_LIBIDN + is_idn_encoded = check_idn (domain); + if (is_idn_encoded && option (OPTIDNDECODE)) { - *out = NULL; - return -1; + if (idna_to_unicode_8z8z (domain, &tmp, IDNA_ALLOW_UNASSIGNED) != IDNA_SUCCESS) + goto cleanup; + mutt_str_replace (&domain, tmp); + FREE (&tmp); } - +#endif /* HAVE_LIBIDN */ + /* we don't want charset-hook effects, so we set flags to 0 */ - if (mutt_convert_string (&tmp, Charset, "utf-8", 0) == -1) - rv = -1; - if (!rv && idna_to_ascii_8z (tmp, out, IDNA_ALLOW_UNASSIGNED) != IDNA_SUCCESS) - rv = -2; - + if (mutt_convert_string (&user, "utf-8", Charset, 0) == -1) + goto cleanup; + + if (mutt_convert_string (&domain, "utf-8", Charset, 0) == -1) + goto cleanup; + + /* + * make sure that we can convert back and come out with the same + * user and domain name. + */ + if ((flags & MI_MAY_BE_IRREVERSIBLE) == 0) + { + reversed_user = safe_strdup (user); + + if (mutt_convert_string (&reversed_user, Charset, "utf-8", 0) == -1) + { + dprint (1, (debugfile, + "intl_to_local: Not reversible. Charset conv to utf-8 failed for user = '%s'.\n", + reversed_user)); + goto cleanup; + } + + if (ascii_strcasecmp (orig_user, reversed_user)) + { + dprint (1, (debugfile, "intl_to_local: Not reversible. orig = '%s', reversed = '%s'.\n", + orig_user, reversed_user)); + goto cleanup; + } + + reversed_domain = safe_strdup (domain); + + if (mutt_convert_string (&reversed_domain, Charset, "utf-8", 0) == -1) + { + dprint (1, (debugfile, + "intl_to_local: Not reversible. Charset conv to utf-8 failed for domain = '%s'.\n", + reversed_domain)); + goto cleanup; + } + +#ifdef HAVE_LIBIDN + /* If the original domain was UTF-8, idna encoding here could + * produce a non-matching domain! Thus we only want to do the + * idna_to_ascii_8z() if the original domain was IDNA encoded. + */ + if (is_idn_encoded && option (OPTIDNDECODE)) + { + if (idna_to_ascii_8z (reversed_domain, &tmp, IDNA_ALLOW_UNASSIGNED) != IDNA_SUCCESS) + { + dprint (1, (debugfile, + "intl_to_local: Not reversible. idna_to_ascii_8z failed for domain = '%s'.\n", + reversed_domain)); + goto cleanup; + } + mutt_str_replace (&reversed_domain, tmp); + } +#endif /* HAVE_LIBIDN */ + + if (ascii_strcasecmp (orig_domain, reversed_domain)) + { + dprint (1, (debugfile, "intl_to_local: Not reversible. orig = '%s', reversed = '%s'.\n", + orig_domain, reversed_domain)); + goto cleanup; + } + } + + mailbox = safe_malloc (mutt_strlen (user) + mutt_strlen (domain) + 2); + sprintf (mailbox, "%s@%s", NONULL(user), NONULL(domain)); /* __SPRINTF_CHECKED__ */ + +cleanup: + FREE (&user); + FREE (&domain); FREE (&tmp); - if (rv < 0) + FREE (&orig_domain); + FREE (&reversed_domain); + FREE (&orig_user); + FREE (&reversed_user); + + return mailbox; +} + +static char *local_to_intl (ADDRESS *a) +{ + char *user = NULL, *domain = NULL, *mailbox = NULL; + char *tmp = NULL; + + if (mbox_to_udomain (a->mailbox, &user, &domain) == -1) + goto cleanup; + + /* we don't want charset-hook effects, so we set flags to 0 */ + if (mutt_convert_string (&user, Charset, "utf-8", 0) == -1) + goto cleanup; + + if (mutt_convert_string (&domain, Charset, "utf-8", 0) == -1) + goto cleanup; + +#ifdef HAVE_LIBIDN + if (option (OPTIDNENCODE)) { - FREE (out); /* __FREE_CHECKED__ */ - *out = safe_strdup (in); + if (idna_to_ascii_8z (domain, &tmp, IDNA_ALLOW_UNASSIGNED) != IDNA_SUCCESS) + goto cleanup; + mutt_str_replace (&domain, tmp); } - return rv; +#endif /* HAVE_LIBIDN */ + + mailbox = safe_malloc (mutt_strlen (user) + mutt_strlen (domain) + 2); + sprintf (mailbox, "%s@%s", NONULL(user), NONULL(domain)); /* __SPRINTF_CHECKED__ */ + +cleanup: + FREE (&user); + FREE (&domain); + FREE (&tmp); + + return mailbox; } /* higher level functions */ -static int mbox_to_udomain (const char *mbx, char **user, char **domain) +int mutt_addrlist_to_intl (ADDRESS *a, char **err) { - static char *buff = NULL; - char *p; - - mutt_str_replace (&buff, mbx); - - p = strchr (buff, '@'); - if (!p || !p[1]) - return -1; - *p = '\0'; - *user = buff; - *domain = p + 1; - return 0; -} + char *intl_mailbox = NULL; + int rv = 0; -int mutt_addrlist_to_idna (ADDRESS *a, char **err) -{ - char *user = NULL, *domain = NULL; - char *tmp = NULL; - int e = 0; - if (err) *err = NULL; for (; a; a = a->next) { - if (!a->mailbox) + if (!a->mailbox || addr_is_intl (a)) continue; - if (mbox_to_udomain (a->mailbox, &user, &domain) == -1) + + intl_mailbox = local_to_intl (a); + if (! intl_mailbox) + { + rv = -1; + if (err && !*err) + *err = safe_strdup (a->mailbox); continue; - - if (mutt_local_to_idna (domain, &tmp) < 0) - { - e = 1; - if (err) - *err = safe_strdup (domain); } - else - { - safe_realloc (&a->mailbox, mutt_strlen (user) + mutt_strlen (tmp) + 2); - sprintf (a->mailbox, "%s@%s", NONULL(user), NONULL(tmp)); /* __SPRINTF_CHECKED__ */ - a->idn_checked = 0; - } - - FREE (&tmp); - - if (e) - return -1; + + set_intl_mailbox (a, intl_mailbox); } - - return 0; + + return rv; } int mutt_addrlist_to_local (ADDRESS *a) { - char *user, *domain; - char *tmp = NULL; - + char *local_mailbox = NULL; + for (; a; a = a->next) { - if (!a->mailbox) + if (!a->mailbox || addr_is_local (a)) continue; - if (!check_idn (a)) - continue; - if (mbox_to_udomain (a->mailbox, &user, &domain) == -1) - continue; - if (mutt_idna_to_local (domain, &tmp, 0) == 0) - { - safe_realloc (&a->mailbox, mutt_strlen (user) + mutt_strlen (tmp) + 2); - sprintf (a->mailbox, "%s@%s", NONULL (user), NONULL (tmp)); /* __SPRINTF_CHECKED__ */ - a->idn_checked = 0; - } - - FREE (&tmp); + + local_mailbox = intl_to_local (a, 0); + if (local_mailbox) + set_local_mailbox (a, local_mailbox); } - + return 0; } @@ -220,27 +282,19 @@ const char *mutt_addr_for_display (ADDRESS *a) { static char *buff = NULL; - char *tmp = NULL; - /* user and domain will be either allocated or reseted to the NULL in - * the mbox_to_udomain(), but for safety... */ - char *domain = NULL; - char *user = NULL; - + char *local_mailbox = NULL; + FREE (&buff); - if (!check_idn (a)) + if (!a->mailbox || addr_is_local (a)) return a->mailbox; - if (mbox_to_udomain (a->mailbox, &user, &domain) != 0) + + local_mailbox = intl_to_local (a, MI_MAY_BE_IRREVERSIBLE); + if (! local_mailbox) return a->mailbox; - if (mutt_idna_to_local (domain, &tmp, MI_MAY_BE_IRREVERSIBLE) != 0) - { - FREE (&tmp); - return a->mailbox; - } - - safe_realloc (&buff, mutt_strlen (tmp) + mutt_strlen (user) + 2); - sprintf (buff, "%s@%s", NONULL(user), NONULL(tmp)); /* __SPRINTF_CHECKED__ */ - FREE (&tmp); + + mutt_str_replace (&buff, local_mailbox); + FREE (&local_mailbox); return buff; } @@ -260,26 +314,25 @@ /* Note that `a' in the `env->a' expression is macro argument, not * "real" name of an `env' compound member. Real name will be substituted * by preprocessor at the macro-expansion time. + * Note that #a escapes and double quotes the argument. */ -#define H_TO_IDNA(a) \ - if (mutt_addrlist_to_idna (env->a, err) && !e) \ +#define H_TO_INTL(a) \ + if (mutt_addrlist_to_intl (env->a, err) && !e) \ { \ if (tag) *tag = #a; e = 1; err = NULL; \ } -int mutt_env_to_idna (ENVELOPE *env, char **tag, char **err) +int mutt_env_to_intl (ENVELOPE *env, char **tag, char **err) { int e = 0; - H_TO_IDNA(return_path); - H_TO_IDNA(from); - H_TO_IDNA(to); - H_TO_IDNA(cc); - H_TO_IDNA(bcc); - H_TO_IDNA(reply_to); - H_TO_IDNA(mail_followup_to); + H_TO_INTL(return_path); + H_TO_INTL(from); + H_TO_INTL(to); + H_TO_INTL(cc); + H_TO_INTL(bcc); + H_TO_INTL(reply_to); + H_TO_INTL(mail_followup_to); return e; } -#undef H_TO_IDNA - -#endif /* HAVE_LIBIDN */ +#undef H_TO_INTL diff -r e635ce43b001 -r 0d7ce56bbafd mutt_idna.h --- a/mutt_idna.h Sat Nov 21 15:28:57 2015 -0800 +++ b/mutt_idna.h Tue Nov 24 15:49:31 2015 -0800 @@ -33,14 +33,6 @@ /* Work around incompatibilities in the libidn API */ #ifdef HAVE_LIBIDN -int mutt_addrlist_to_idna (ADDRESS *, char **); -int mutt_addrlist_to_local (ADDRESS *); - -void mutt_env_to_local (ENVELOPE *); -int mutt_env_to_idna (ENVELOPE *, char **, char **); - -const char *mutt_addr_for_display (ADDRESS *a); - # if (!defined(HAVE_IDNA_TO_ASCII_8Z) && defined(HAVE_IDNA_TO_ASCII_FROM_UTF8)) # define idna_to_ascii_8z(a,b,c) idna_to_ascii_from_utf8(a,b,(c)&1,((c)&2)?1:0) # endif @@ -50,33 +42,16 @@ # if (!defined(HAVE_IDNA_TO_UNICODE_8Z8Z) && defined(HAVE_IDNA_TO_UNICODE_UTF8_FROM_UTF8)) # define idna_to_unicode_8z8z(a,b,c) idna_to_unicode_utf8_from_utf8(a,b,(c)&1,((c)&2)?1:0) # endif -#else - -static inline int mutt_addrlist_to_idna (ADDRESS *addr, char **err) -{ - return 0; -} - -static inline int mutt_addrlist_to_local (ADDRESS *addr) -{ - return 0; -} - -static inline void mutt_env_to_local (ENVELOPE *env) -{ - return; -} - -static inline int mutt_env_to_idna (ENVELOPE *env, char **tag, char **err) -{ - return 0; -} - -static inline const char *mutt_addr_for_display (ADDRESS *a) -{ - return a->mailbox; -} - #endif /* HAVE_LIBIDN */ + +int mutt_addrlist_to_intl (ADDRESS *, char **); +int mutt_addrlist_to_local (ADDRESS *); + +void mutt_env_to_local (ENVELOPE *); +int mutt_env_to_intl (ENVELOPE *, char **, char **); + +const char *mutt_addr_for_display (ADDRESS *a); + + #endif diff -r e635ce43b001 -r 0d7ce56bbafd query.c --- a/query.c Sat Nov 21 15:28:57 2015 -0800 +++ b/query.c Tue Nov 24 15:49:31 2015 -0800 @@ -66,7 +66,7 @@ if(!tmp->next && !tmp->personal) tmp->personal = safe_strdup (r->name); - mutt_addrlist_to_idna (tmp, NULL); + mutt_addrlist_to_intl (tmp, NULL); return tmp; } diff -r e635ce43b001 -r 0d7ce56bbafd recvcmd.c --- a/recvcmd.c Sat Nov 21 15:28:57 2015 -0800 +++ b/recvcmd.c Tue Nov 24 15:49:31 2015 -0800 @@ -185,7 +185,7 @@ adr = mutt_expand_aliases (adr); - if (mutt_addrlist_to_idna (adr, &err) < 0) + if (mutt_addrlist_to_intl (adr, &err) < 0) { mutt_error (_("Bad IDN: '%s'"), err); FREE (&err); diff -r e635ce43b001 -r 0d7ce56bbafd rfc822.c --- a/rfc822.c Sat Nov 21 15:28:57 2015 -0800 +++ b/rfc822.c Tue Nov 24 15:49:31 2015 -0800 @@ -801,8 +801,8 @@ p->personal = safe_strdup (addr->personal); p->mailbox = safe_strdup (addr->mailbox); p->group = addr->group; - p->is_idn = addr->is_idn; - p->idn_checked = addr->idn_checked; + p->is_intl = addr->is_intl; + p->intl_checked = addr->intl_checked; return p; } diff -r e635ce43b001 -r 0d7ce56bbafd rfc822.h --- a/rfc822.h Sat Nov 21 15:28:57 2015 -0800 +++ b/rfc822.h Tue Nov 24 15:49:31 2015 -0800 @@ -42,8 +42,8 @@ char *mailbox; /* mailbox and host address */ int group; /* group mailbox? */ struct address_t *next; - unsigned is_idn : 1; - unsigned idn_checked : 1; + unsigned is_intl : 1; + unsigned intl_checked : 1; } ADDRESS; diff -r e635ce43b001 -r 0d7ce56bbafd send.c --- a/send.c Sat Nov 21 15:28:57 2015 -0800 +++ b/send.c Tue Nov 24 15:49:31 2015 -0800 @@ -201,7 +201,7 @@ return (-1); rfc822_free_address (a); *a = mutt_expand_aliases (mutt_parse_adrlist (NULL, buf)); - if ((idna_ok = mutt_addrlist_to_idna (*a, &err)) != 0) + if ((idna_ok = mutt_addrlist_to_intl (*a, &err)) != 0) { mutt_error (_("Error: '%s' is a bad IDN."), err); mutt_refresh (); @@ -1423,7 +1423,7 @@ { mutt_env_to_local (msg->env); mutt_edit_headers (Editor, msg->content->filename, msg, fcc, sizeof (fcc)); - mutt_env_to_idna (msg->env, NULL, NULL); + mutt_env_to_intl (msg->env, NULL, NULL); } else { @@ -1629,7 +1629,7 @@ encode_descriptions (msg->content, 1); mutt_prepare_envelope (msg->env, 0); - mutt_env_to_idna (msg->env, NULL, NULL); /* Handle bad IDNAs the next time. */ + mutt_env_to_intl (msg->env, NULL, NULL); /* Handle bad IDNAs the next time. */ if (!Postponed || mutt_write_fcc (NONULL (Postponed), msg, (cur && (flags & SENDREPLY)) ? cur->env->message_id : NULL, 1, fcc) < 0) { @@ -1659,7 +1659,7 @@ } } - if (mutt_env_to_idna (msg->env, &tag, &err)) + if (mutt_env_to_intl (msg->env, &tag, &err)) { mutt_error (_("Bad IDN in \"%s\": '%s'"), tag, err); FREE (&err); diff -r e635ce43b001 -r 0d7ce56bbafd sendlib.c --- a/sendlib.c Sat Nov 21 15:28:57 2015 -0800 +++ b/sendlib.c Tue Nov 24 15:49:31 2015 -0800 @@ -2603,7 +2603,7 @@ rfc822_qualify (from, fqdn); rfc2047_encode_adrlist (from, "Resent-From"); - if (mutt_addrlist_to_idna (from, &err)) + if (mutt_addrlist_to_intl (from, &err)) { mutt_error (_("Bad IDN %s while preparing resent-from."), err); diff -r e635ce43b001 -r 0d7ce56bbafd smtp.c --- a/smtp.c Sat Nov 21 15:28:57 2015 -0800 +++ b/smtp.c Tue Nov 24 15:49:31 2015 -0800 @@ -61,6 +61,7 @@ AUTH, DSN, EIGHTBITMIME, + SMTPUTF8, CAPMAX }; @@ -122,6 +123,8 @@ mutt_bit_set (Capabilities, DSN); else if (!ascii_strncasecmp ("STARTTLS", buf + 4, 8)) mutt_bit_set (Capabilities, STARTTLS); + else if (!ascii_strncasecmp ("SMTPUTF8", buf + 4, 8)) + mutt_bit_set (Capabilities, SMTPUTF8); if (smtp_code (buf, n, &n) < 0) return smtp_err_code; @@ -236,6 +239,34 @@ return 0; } + +/* Returns 1 if a contains at least one 8-bit character, 0 if none do. + */ +static int +address_uses_unicode(const char * a) { + while(a && *a > 0 && *a < 128) + a++; + if(a && *a) + return 1; + return 0; +} + + +/* Returns 1 if any address in a contains at least one 8-bit + * character, 0 if none do. + */ +static int +addresses_use_unicode(const ADDRESS* a) { + while (a) + { + if(a->mailbox && !a->group && address_uses_unicode(a->mailbox)) + return 1; + a = a->next; + } + return 0; +} + + int mutt_smtp_send (const ADDRESS* from, const ADDRESS* to, const ADDRESS* cc, const ADDRESS* bcc, const char *msgfile, int eightbit) @@ -282,6 +313,12 @@ } if (DsnReturn && mutt_bit_isset (Capabilities, DSN)) ret += snprintf (buf + ret, sizeof (buf) - ret, " RET=%s", DsnReturn); + if (mutt_bit_isset (Capabilities, SMTPUTF8) && + (address_uses_unicode(envfrom) || + addresses_use_unicode(to) || + addresses_use_unicode(cc) || + addresses_use_unicode(bcc))) + ret += snprintf (buf + ret, sizeof (buf) - ret, " SMTPUTF8");