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");

Reply via email to