summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin McCarthy <kevin@8t8.us>2015-11-24 15:49:27 -0800
committerKevin McCarthy <kevin@8t8.us>2015-11-24 15:49:27 -0800
commit24bab5179e031eb5f66dc3be381dabfca00f7bd0 (patch)
tree5ab5596572cda9d6951e81ee51e62578ccd4391d
parent0e0520faf7a96e2cc0efa49eced65adb4e5acfe6 (diff)
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.
-rw-r--r--mutt_idna.c326
1 files changed, 176 insertions, 150 deletions
diff --git a/mutt_idna.c b/mutt_idna.c
index 4169f788..04a7c4ca 100644
--- a/mutt_idna.c
+++ b/mutt_idna.c
@@ -24,200 +24,233 @@
#include "charset.h"
#include "mutt_idna.h"
+#ifdef HAVE_LIBIDN
+static int check_idn (char *domain)
+{
+ if (! domain)
+ return 0;
+ if (ascii_strncasecmp (domain, "xn--", 4) == 0)
+ return 1;
-/* check whether an address is an IDN */
+ while ((domain = strchr (domain, '.')) != NULL)
+ {
+ if (ascii_strncasecmp (++domain, "xn--", 4) == 0)
+ return 1;
+ }
-static int check_idn (ADDRESS *ap)
-{
- char *p = 0;
+ return 0;
+}
+#endif /* HAVE_LIBIDN */
- if (!ap || !ap->mailbox)
- return 0;
+static int mbox_to_udomain (const char *mbx, char **user, char **domain)
+{
+ char *buff = NULL;
+ char *p;
- if (!ap->intl_checked)
+ buff = safe_strdup (mbx);
+ p = strchr (buff, '@');
+ if (!p || !p[1])
{
- ap->intl_checked = 1;
- for (p = strchr (ap->mailbox, '@'); p && *p; p = strchr (p, '.'))
- if (ascii_strncasecmp (++p, "xn--", 4) == 0)
- {
- ap->is_intl = 1;
- break;
- }
+ FREE (&buff);
+ return -1;
}
-
- return ap->is_intl;
+
+ *p = '\0';
+ *user = safe_strdup (buff);
+ *domain = safe_strdup (p + 1);
+ FREE (&buff);
+ return 0;
}
-static int intl_to_local (const char *in, char **out, int flags)
+static int addr_is_local (ADDRESS *a)
{
- *out = NULL;
+ return (a->intl_checked && !a->is_intl);
+}
- if (!option (OPTUSEIDN))
- goto notrans;
+static int addr_is_intl (ADDRESS *a)
+{
+ return (a->intl_checked && a->is_intl);
+}
- if (!in)
- goto notrans;
+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_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_domain = safe_strdup (domain);
- /* Is this the right function? Interesting effects with some bad identifiers! */
#ifdef HAVE_LIBIDN
- if (idna_to_unicode_8z8z (in, out, IDNA_ALLOW_UNASSIGNED) != IDNA_SUCCESS)
- goto notrans;
+ is_idn_encoded = check_idn (domain);
+ if (is_idn_encoded && option (OPTUSEIDN))
+ {
+ 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 (out, "utf-8", Charset, 0) == -1)
- goto notrans;
+ 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
- * domain name.
+ * domain name.
*/
-
if ((flags & MI_MAY_BE_IRREVERSIBLE) == 0)
{
- int irrev = 0;
- char *t2 = NULL;
- char *tmp = safe_strdup (*out);
+ 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;
+ }
- /* 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;
#ifdef HAVE_LIBIDN
- if (!irrev && idna_to_ascii_8z (tmp, &t2, IDNA_ALLOW_UNASSIGNED) != IDNA_SUCCESS)
- irrev = 1;
-#endif /* HAVE_LIBIDN */
- if (!irrev && ascii_strcasecmp (t2, in))
+ /* 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 (OPTUSEIDN))
{
- dprint (1, (debugfile, "intl_to_local: Not reversible. in = '%s', t2 = '%s'.\n",
- in, t2));
- irrev = 1;
+ 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);
}
-
- FREE (&t2);
- FREE (&tmp);
+#endif /* HAVE_LIBIDN */
- if (irrev)
- goto notrans;
+ 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;
+ }
}
- return 0;
-
- notrans:
- FREE (out); /* __FREE_CHECKED__ */
- *out = safe_strdup (in);
- return 1;
+ 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);
+ FREE (&orig_domain);
+ FREE (&reversed_domain);
+
+ return mailbox;
}
-static int local_to_intl (const char *in, char **out)
+static char *local_to_intl (ADDRESS *a)
{
- int rv = 0;
- char *tmp = safe_strdup (in);
- *out = NULL;
+ char *user = NULL, *domain = NULL, *mailbox = NULL;
+ char *tmp = NULL;
+
+ if (mbox_to_udomain (a->mailbox, &user, &domain) == -1)
+ goto cleanup;
- if (!in)
- {
- *out = NULL;
- return -1;
- }
-
/* 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 (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 (!rv && idna_to_ascii_8z (tmp, out, IDNA_ALLOW_UNASSIGNED) != IDNA_SUCCESS)
- rv = -2;
+ if (idna_to_ascii_8z (domain, &tmp, IDNA_ALLOW_UNASSIGNED) != IDNA_SUCCESS)
+ goto cleanup;
+ mutt_str_replace (&domain, tmp);
#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);
- if (rv < 0)
- {
- FREE (out); /* __FREE_CHECKED__ */
- *out = safe_strdup (in);
- }
- return rv;
+
+ return mailbox;
}
/* higher level functions */
-static int mbox_to_udomain (const char *mbx, char **user, char **domain)
-{
- 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;
-}
-
int mutt_addrlist_to_intl (ADDRESS *a, char **err)
{
- char *user = NULL, *domain = NULL;
- char *tmp = NULL;
- int e = 0;
-
+ char *intl_mailbox = NULL;
+ int rv = 0;
+
if (err)
*err = NULL;
for (; a; a = a->next)
{
- if (!a->mailbox)
- continue;
- if (mbox_to_udomain (a->mailbox, &user, &domain) == -1)
+ if (!a->mailbox || addr_is_intl (a))
continue;
-
- if (local_to_intl (domain, &tmp) < 0)
- {
- e = 1;
- if (err)
- *err = safe_strdup (domain);
- }
- else
+
+ intl_mailbox = local_to_intl (a);
+ if (! intl_mailbox)
{
- safe_realloc (&a->mailbox, mutt_strlen (user) + mutt_strlen (tmp) + 2);
- sprintf (a->mailbox, "%s@%s", NONULL(user), NONULL(tmp)); /* __SPRINTF_CHECKED__ */
- a->intl_checked = 0;
+ rv = -1;
+ if (err && !*err)
+ *err = safe_strdup (a->mailbox);
+ continue;
}
-
- 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 (intl_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->intl_checked = 0;
- }
-
- FREE (&tmp);
+
+ local_mailbox = intl_to_local (a, 0);
+ if (local_mailbox)
+ set_local_mailbox (a, local_mailbox);
}
-
+
return 0;
}
@@ -225,27 +258,19 @@ int mutt_addrlist_to_local (ADDRESS *a)
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))
- return a->mailbox;
- if (mbox_to_udomain (a->mailbox, &user, &domain) != 0)
+ if (!a->mailbox || addr_is_local (a))
return a->mailbox;
- if (intl_to_local (domain, &tmp, MI_MAY_BE_IRREVERSIBLE) != 0)
- {
- FREE (&tmp);
+
+ local_mailbox = intl_to_local (a, MI_MAY_BE_IRREVERSIBLE);
+ if (! local_mailbox)
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;
}
@@ -265,6 +290,7 @@ void mutt_env_to_local (ENVELOPE *e)
/* 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_INTL(a) \
if (mutt_addrlist_to_intl (env->a, err) && !e) \