GPGME requires the uid to be in utf-8. Gpg (classic-mode) uses the
current locale by default; add --utf8-strings to the command to change
it to also require utf-8.
Add a one-way transition function from IDN to utf-8:
mutt_addrlist_to_utf8(). Change the crypt_get_keys() function to
invoke that on the generated list of addresses to look up.
The helper function, intl_to_utf8(), is based off the same template as
intl_to_local() and local_to_intl(). I've kept the code basically the
same, for ease of understanding, even though it may seem a bit
verbose.
Note that adding a whole new state to the IDNA code would be
significantly more complicated, requiring more state bits, and code to
connect between the three different states. Since there isn't such a
requirement right now, I've kept the change simple and commented that
the function is only meant for a one-way transition from IDN to utf-8.
---
contrib/gpg.rc | 2 +-
crypt.c | 2 +-
mutt_idna.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++
mutt_idna.h | 6 +++++
4 files changed, 76 insertions(+), 2 deletions(-)
diff --git a/contrib/gpg.rc b/contrib/gpg.rc
index d773c152..c08c47c7 100644
--- a/contrib/gpg.rc
+++ b/contrib/gpg.rc
@@ -88,7 +88,7 @@ set pgp_verify_key_command="gpg --verbose --batch
--fingerprint --check-sigs %r"
# read in the public key ring
# note: the second --with-fingerprint adds fingerprints to subkeys
-set pgp_list_pubring_command="gpg --no-verbose --batch --quiet --with-colons
--with-fingerprint --with-fingerprint --list-keys %r"
+set pgp_list_pubring_command="gpg --no-verbose --batch --quiet --with-colons
--with-fingerprint --with-fingerprint --utf8-strings --list-keys %r"
# read in the secret key ring
# note: the second --with-fingerprint adds fingerprints to subkeys
diff --git a/crypt.c b/crypt.c
index 43d7c5aa..0c1c6d90 100644
--- a/crypt.c
+++ b/crypt.c
@@ -940,7 +940,7 @@ int crypt_get_keys(HEADER *msg, char **keylist, int
oppenc_mode)
last = rfc822_append(&adrlist, msg->env->to, 0);
last = rfc822_append(last ? &last : &adrlist, msg->env->cc, 0);
rfc822_append(last ? &last : &adrlist, msg->env->bcc, 0);
- mutt_addrlist_to_local(adrlist);
+ mutt_addrlist_to_utf8(adrlist);
if (fqdn)
rfc822_qualify(adrlist, fqdn);
diff --git a/mutt_idna.c b/mutt_idna.c
index 713d9247..6bfd3b88 100644
--- a/mutt_idna.c
+++ b/mutt_idna.c
@@ -77,6 +77,16 @@ static void set_local_mailbox(ADDRESS *a, char
*local_mailbox)
a->is_intl = 0;
}
+/* NOTE: this doesn't set a state flag like the other two
+ * right now, because it's only used for a one-way transition
+ * when getting encryption keys.
+ */
+static void set_utf8_mailbox(ADDRESS *a, char *utf8_mailbox)
+{
+ FREE(&a->mailbox);
+ a->mailbox = utf8_mailbox;
+}
+
static void set_intl_mailbox(ADDRESS *a, char *intl_mailbox)
{
FREE(&a->mailbox);
@@ -85,6 +95,36 @@ static void set_intl_mailbox(ADDRESS *a, char *intl_mailbox)
a->is_intl = 1;
}
+static char *intl_to_utf8(char *orig_user, char *orig_domain)
+{
+ char *utf8_user, *utf8_domain, *mailbox = NULL, *tmp = NULL;
+
+ utf8_user = orig_user;
+ utf8_domain = safe_strdup(orig_domain);
+
+#if defined(HAVE_LIBIDN) || defined(HAVE_LIBIDN2)
+ /* NOTE: since this transform is currently used for key lookups,
+ * the check for option(OPTIDNDECODE) is not included here.
+ * Compare to the invocation in intl_to_local()
+ */
+ if (check_idn(utf8_domain))
+ {
+ if (idna_to_unicode_8z8z(utf8_domain, &tmp, IDNA_ALLOW_UNASSIGNED) !=
IDNA_SUCCESS)
+ goto cleanup;
+ mutt_str_replace(&utf8_domain, tmp);
+ }
+#endif
+
+ mailbox = safe_malloc(mutt_strlen(utf8_user) + mutt_strlen(utf8_domain) + 2);
+ sprintf(mailbox, "%s@%s", NONULL(utf8_user), NONULL(utf8_domain)); /*
__SPRINTF_CHECKED__ */
+
+cleanup:
+ FREE(&utf8_domain);
+ FREE(&tmp);
+
+ return mailbox;
+}
+
static char *intl_to_local(char *orig_user, char *orig_domain, int flags)
{
char *local_user = NULL, *local_domain = NULL, *mailbox = NULL;
@@ -255,6 +295,34 @@ int mutt_addrlist_to_intl(ADDRESS *a, char **err)
return rv;
}
+/* NOTE: this function currently only handles a one-way transition
+ * from idn to utf-8, because it's only used for looking up
+ * encryption keys in crypt_get_keys().
+ *
+ * No state bit is set. The resulting addresses should not be passed through
+ * other mutt_addrlist_to_xxx() functions. They should be used and freed.
+ */
+int mutt_addrlist_to_utf8(ADDRESS *a)
+{
+ char *user = NULL, *domain = NULL;
+ char *utf8_mailbox = NULL;
+
+ for (; a; a = a->next)
+ {
+ if (!a->mailbox || !addr_is_intl(a))
+ continue;
+
+ if (mbox_to_udomain(a->mailbox, &user, &domain) == -1)
+ continue;
+
+ utf8_mailbox = intl_to_utf8(user, domain);
+ if (utf8_mailbox)
+ set_utf8_mailbox(a, utf8_mailbox);
+ }
+
+ return 0;
+}
+
int mutt_addrlist_to_local(ADDRESS *a)
{
char *user = NULL, *domain = NULL;
diff --git a/mutt_idna.h b/mutt_idna.h
index 5de573a3..1bf9c317 100644
--- a/mutt_idna.h
+++ b/mutt_idna.h
@@ -51,6 +51,7 @@
#ifdef HAVE_ICONV
int mutt_addrlist_to_intl(ADDRESS *, char **);
+int mutt_addrlist_to_utf8(ADDRESS *);
int mutt_addrlist_to_local(ADDRESS *);
void mutt_env_to_local(ENVELOPE *);
@@ -63,6 +64,11 @@ static inline int mutt_addrlist_to_intl(ADDRESS *addr, char
**err)
return 0;
}
+static inline int mutt_addrlist_to_utf8(ADDRESS *addr)
+{
+ return 0;
+}
+
static inline int mutt_addrlist_to_local(ADDRESS *addr)
{
return 0;
--
2.54.0