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

Reply via email to