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. -- Kevin J. McCarthy GPG Fingerprint: 8975 A9B3 3AA3 7910 385C 5308 ADEF 7684 8031 6BDA http://www.8t8.us/configs/gpg-key-transition-statement.txt
# HG changeset patch # User Kevin McCarthy <[email protected]> # Date 1447713362 28800 # Mon Nov 16 14:36:02 2015 -0800 # Node ID 856595362743ef7919e0bb57cce1ba50adfe1b16 # Parent 0e8ba26d119f75f1aa0d4fd590f077000d8ea1ba 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. diff --git a/mutt_idna.c b/mutt_idna.c --- a/mutt_idna.c +++ b/mutt_idna.c @@ -19,238 +19,263 @@ #if HAVE_CONFIG_H # include "config.h" #endif #include "mutt.h" #include "charset.h" #include "mutt_idna.h" - - -/* check whether an address is an IDN */ - -static int check_idn (ADDRESS *ap) +#ifdef HAVE_LIBIDN +static int check_idn (char *domain) { - char *p = 0; - - if (!ap || !ap->mailbox) + if (! domain) return 0; - if (!ap->intl_checked) + if (ascii_strncasecmp (domain, "xn--", 4) == 0) + return 1; + + while ((domain = strchr (domain, '.')) != NULL) { - 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; - } + if (ascii_strncasecmp (++domain, "xn--", 4) == 0) + return 1; } - - return ap->is_intl; + + return 0; +} +#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 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; +} - /* Is this the right function? Interesting effects with some bad identifiers! */ +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 - if (idna_to_unicode_8z8z (in, out, IDNA_ALLOW_UNASSIGNED) != IDNA_SUCCESS) - goto notrans; + int is_idn_encoded = 0; +#endif /* HAVE_LIBIDN */ + + if (mbox_to_udomain (a->mailbox, &user, &domain) == -1) + goto cleanup; + orig_domain = safe_strdup (domain); + +#ifdef HAVE_LIBIDN + 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); - /* 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 (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 (!irrev && idna_to_ascii_8z (tmp, &t2, IDNA_ALLOW_UNASSIGNED) != IDNA_SUCCESS) - irrev = 1; + /* 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)) + { + 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 (!irrev && ascii_strcasecmp (t2, in)) + + if (ascii_strcasecmp (orig_domain, reversed_domain)) { - dprint (1, (debugfile, "intl_to_local: Not reversible. in = '%s', t2 = '%s'.\n", - in, t2)); - irrev = 1; + dprint (1, (debugfile, "intl_to_local: Not reversible. orig = '%s', reversed = '%s'.\n", + orig_domain, reversed_domain)); + goto cleanup; } - - FREE (&t2); - FREE (&tmp); - - if (irrev) - goto notrans; } - 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 (!in) - { - *out = NULL; - return -1; - } - + 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 (&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) + 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 (local_to_intl (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->intl_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 (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; } /* convert just for displaying purposes */ 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 (intl_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; } /* Convert an ENVELOPE structure */ void mutt_env_to_local (ENVELOPE *e) { mutt_addrlist_to_local (e->return_path); @@ -260,16 +285,17 @@ mutt_addrlist_to_local (e->bcc); mutt_addrlist_to_local (e->reply_to); mutt_addrlist_to_local (e->mail_followup_to); } /* 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) \ { \ if (tag) *tag = #a; e = 1; err = NULL; \ } int mutt_env_to_intl (ENVELOPE *env, char **tag, char **err)
signature.asc
Description: PGP signature
