[gmime: 2/4] Added libidn support



commit 826a84ac397b8be7d6a0ca1abe5385228814f765
Author: Jeffrey Stedfast <jestedfa microsoft com>
Date:   Sat Apr 8 10:39:22 2017 -0400

    Added libidn support

 configure.ac             |   20 ++++++
 gmime/Makefile.am        |    5 +-
 gmime/gmime-utils.c      |   15 +++++
 gmime/internet-address.c |  152 +++++++++++++++++++++++++++++++++++++++------
 gmime/internet-address.h |    6 ++-
 5 files changed, 174 insertions(+), 24 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 1eac75e..b042e14 100644
--- a/configure.ac
+++ b/configure.ac
@@ -509,6 +509,25 @@ AS_IF([test "x$enable_crypto" = "xyes"], [
 ])
 AM_CONDITIONAL(ENABLE_CRYPTO, test "x$enable_crypto" != "xno")
 
+dnl *****************************
+dnl *** Checks for GNU Libidn ***
+dnl *****************************
+AC_ARG_WITH(libidn, AC_HELP_STRING([--with-libidn=[DIR]],
+                                [Support IDN (needs GNU Libidn)]),
+  libidn=$withval, libidn=yes)
+if test "$libidn" != "no" ; then
+  PKG_CHECK_MODULES(LIBIDN, libidn >= 0.0.0, [libidn=yes], [libidn=no])
+  if test "$libidn" != "yes" ; then
+    libidn=no
+    AC_MSG_WARN([Libidn not found])
+  else
+    libidn=yes
+    AC_DEFINE(LIBIDN, 1, [Define to 1 if GNU Libidn should be used.])
+  fi
+fi
+AC_MSG_CHECKING([if Libidn should be used])
+AC_MSG_RESULT($libidn)
+
 dnl Check for GObject introspection and Vala binding generator
 GOBJECT_INTROSPECTION_CHECK([1.30.0])
 VAPIGEN_CHECK
@@ -649,6 +668,7 @@ Configuration:
   Console warnings:     ${enable_warnings}
   PGP/MIME support:     ${enable_crypto}
   S/MIME support:       ${enable_crypto}
+  libidn support:       ${libidn}
 
   Vala bindings:        ${enable_vala}
 "
diff --git a/gmime/Makefile.am b/gmime/Makefile.am
index bf25ad3..6fc6daf 100644
--- a/gmime/Makefile.am
+++ b/gmime/Makefile.am
@@ -11,7 +11,8 @@ AM_CPPFLAGS =                                 \
        -I$(top_builddir)/util          \
        -DG_LOG_DOMAIN=\"gmime\"        \
        $(GMIME_CFLAGS)                 \
-       $(GLIB_CFLAGS)
+       $(GLIB_CFLAGS)                  \
+       $(LIBIDN_CFLAGS)
 
 noinst_PROGRAMS = gen-table charset-map
 
@@ -173,7 +174,7 @@ install-libtool-import-lib:
 uninstall-libtool-import-lib:
 endif
 
-libgmime_3_0_la_LIBADD = $(top_builddir)/util/libutil.la $(GLIB_LIBS)
+libgmime_3_0_la_LIBADD = $(top_builddir)/util/libutil.la $(GLIB_LIBS) $(LIBIDN_LIBS)
 libgmime_3_0_la_LDFLAGS = \
        -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \
        -export-dynamic $(no_undefined)
diff --git a/gmime/gmime-utils.c b/gmime/gmime-utils.c
index 39ebc1b..6dd2484 100644
--- a/gmime/gmime-utils.c
+++ b/gmime/gmime-utils.c
@@ -50,6 +50,10 @@
 #include <ctype.h>
 #include <errno.h>
 
+#ifdef LIBIDN
+#include <idna.h>
+#endif
+
 #include "gmime-utils.h"
 #include "gmime-common.h"
 #include "gmime-internal.h"
@@ -774,6 +778,7 @@ g_mime_utils_generate_message_id (const char *fqdn)
        unsigned long value;
        char *name = NULL;
        GString *msgid;
+       char *ascii;
        int i;
        
        if (!fqdn) {
@@ -859,7 +864,17 @@ g_mime_utils_generate_message_id (const char *fqdn)
        } while (value != 0);
        
        g_string_append_c (msgid, '@');
+       
+#ifdef LIBIDN
+       if (idna_to_ascii_8z (fqdn, &ascii, 0) == IDNA_SUCCESS) {
+               g_string_append (msgid, ascii);
+               free (ascii);
+       } else {
+               g_string_append (msgid, fqdn);
+       }
+#else
        g_string_append (msgid, fqdn);
+#endif
        
        g_free (name);
        
diff --git a/gmime/internet-address.c b/gmime/internet-address.c
index 1dcfa7a..bbd91b6 100644
--- a/gmime/internet-address.c
+++ b/gmime/internet-address.c
@@ -24,10 +24,15 @@
 #endif
 
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
 #include <errno.h>
 
+#ifdef LIBIDN
+#include <idna.h>
+#endif
+
 #include "internet-address.h"
 #include "gmime-table-private.h"
 #include "gmime-parse-utils.h"
@@ -91,6 +96,7 @@ enum {
        INTERNET_ADDRESS_FOLD   = 1 << 1,
 };
 
+static gboolean addrspec_parse (const char **in, const char *sentinels, char **addrspec, int *at);
 
 static void internet_address_class_init (InternetAddressClass *klass);
 static void internet_address_init (InternetAddress *ia, InternetAddressClass *klass);
@@ -331,7 +337,9 @@ internet_address_mailbox_class_init (InternetAddressMailboxClass *klass)
 static void
 internet_address_mailbox_init (InternetAddressMailbox *mailbox, InternetAddressMailboxClass *klass)
 {
+       mailbox->idn_addr = NULL;
        mailbox->addr = NULL;
+       mailbox->at = -1;
 }
 
 static void
@@ -339,11 +347,26 @@ internet_address_mailbox_finalize (GObject *object)
 {
        InternetAddressMailbox *mailbox = (InternetAddressMailbox *) object;
        
+       g_free (mailbox->idn_addr);
        g_free (mailbox->addr);
        
        G_OBJECT_CLASS (mailbox_parent_class)->finalize (object);
 }
 
+static InternetAddress *
+_internet_address_mailbox_new (const char *name, const char *addr, int at)
+{
+       InternetAddressMailbox *mailbox;
+       
+       mailbox = g_object_new (INTERNET_ADDRESS_TYPE_MAILBOX, NULL);
+       mailbox->addr = g_strdup (addr);
+       mailbox->at = at;
+       
+       _internet_address_set_name ((InternetAddress *) mailbox, name);
+       
+       return (InternetAddress *) mailbox;
+}
+
 
 /**
  * internet_address_mailbox_new:
@@ -361,11 +384,14 @@ InternetAddress *
 internet_address_mailbox_new (const char *name, const char *addr)
 {
        InternetAddressMailbox *mailbox;
+       const char *inptr = addr;
        
        g_return_val_if_fail (addr != NULL, NULL);
        
        mailbox = g_object_new (INTERNET_ADDRESS_TYPE_MAILBOX, NULL);
-       mailbox->addr = g_strdup (addr);
+
+       if (!addrspec_parse (&inptr, "", &mailbox->addr, &mailbox->at))
+               mailbox->addr = g_strdup (addr);
        
        _internet_address_set_name ((InternetAddress *) mailbox, name);
        
@@ -383,13 +409,20 @@ internet_address_mailbox_new (const char *name, const char *addr)
 void
 internet_address_mailbox_set_addr (InternetAddressMailbox *mailbox, const char *addr)
 {
+       const char *inptr = addr;
+       
        g_return_if_fail (INTERNET_ADDRESS_IS_MAILBOX (mailbox));
        
        if (mailbox->addr == addr)
                return;
        
+       g_free (mailbox->idn_addr);
+       mailbox->idn_addr = NULL;
+       
        g_free (mailbox->addr);
-       mailbox->addr = g_strdup (addr);
+       
+       if (!addrspec_parse (&inptr, "", &mailbox->addr, &mailbox->at))
+               mailbox->addr = g_strdup (addr);
        
        g_mime_event_emit (((InternetAddress *) mailbox)->changed, NULL);
 }
@@ -401,7 +434,7 @@ internet_address_mailbox_set_addr (InternetAddressMailbox *mailbox, const char *
  *
  * Gets the addr-spec of the internet address mailbox.
  *
- * Returns: the address of the mailbox.
+ * Returns: the addr-spec string.
  **/
 const char *
 internet_address_mailbox_get_addr (InternetAddressMailbox *mailbox)
@@ -412,6 +445,43 @@ internet_address_mailbox_get_addr (InternetAddressMailbox *mailbox)
 }
 
 
+/**
+ * internet_address_mailbox_get_idn_addr:
+ * @mailbox: a #InternetAddressMailbox
+ *
+ * Gets the IDN ascii-encoded addr-spec.
+ *
+ * Returns: the encoded addr-spec string.
+ **/
+const char *
+internet_address_mailbox_get_idn_addr (InternetAddressMailbox *mailbox)
+{
+       GString *encoded;
+       char *ascii;
+       
+       g_return_val_if_fail (INTERNET_ADDRESS_IS_MAILBOX (mailbox), NULL);
+       
+#ifdef LIBIDN
+       if (!mailbox->idn_addr && mailbox->at > 0) {
+               encoded = g_string_new ("");
+               g_string_append_len (encoded, mailbox->addr, mailbox->at + 1);
+               if (idna_to_ascii_8z (mailbox->addr + mailbox->at + 1, &ascii, 0) == IDNA_SUCCESS) {
+                       g_string_append (encoded, ascii);
+                       free (ascii);
+               } else {
+                       g_string_append (encoded, mailbox->addr + mailbox->at + 1);
+               }
+               
+               mailbox->idn_addr = g_string_free (encoded, FALSE);
+       }
+       
+       return mailbox->idn_addr ? mailbox->idn_addr : mailbox->addr;
+#else
+       return mailbox->addr;
+#endif
+}
+
+
 static void internet_address_group_class_init (InternetAddressGroupClass *klass);
 static void internet_address_group_init (InternetAddressGroup *group, InternetAddressGroupClass *klass);
 static void internet_address_group_finalize (GObject *object);
@@ -1124,12 +1194,14 @@ mailbox_to_string (InternetAddress *ia, GMimeFormatOptions *options, guint32 fla
        InternetAddressMailbox *mailbox = (InternetAddressMailbox *) ia;
        gboolean encode = flags & INTERNET_ADDRESS_ENCODE;
        gboolean fold = flags & INTERNET_ADDRESS_FOLD;
-       const char *newline;
+       const char *newline, *addr;
        char *name;
        size_t len;
        
        newline = g_mime_format_options_get_newline (options);
        
+       addr = internet_address_mailbox_get_idn_addr (mailbox);
+       
        if (ia->name && *ia->name) {
                name = encoded_name (options, ia->name, encode, ia->charset);
                len = strlen (name);
@@ -1157,7 +1229,7 @@ mailbox_to_string (InternetAddress *ia, GMimeFormatOptions *options, guint32 fla
                
                g_free (name);
                
-               len = strlen (mailbox->addr);
+               len = strlen (addr);
                
                if (fold && (*linelen + len + 3) >= GMIME_FOLD_LEN) {
                        g_string_append (str, newline);
@@ -1168,18 +1240,18 @@ mailbox_to_string (InternetAddress *ia, GMimeFormatOptions *options, guint32 fla
                        *linelen += 2;
                }
                
-               g_string_append_len (str, mailbox->addr, len);
+               g_string_append_len (str, addr, len);
                g_string_append_c (str, '>');
                *linelen += len + 1;
        } else {
-               len = strlen (mailbox->addr);
+               len = strlen (addr);
                
                if (fold && (*linelen + len) > GMIME_FOLD_LEN) {
                        linewrap (str, newline);
                        *linelen = 1;
                }
                
-               g_string_append_len (str, mailbox->addr, len);
+               g_string_append_len (str, addr, len);
                *linelen += len;
        }
 }
@@ -1429,6 +1501,7 @@ dotatom_parse (GString *str, const char **in, const char *sentinels)
        const char *atom, *comment;
        const char *inptr = *in;
        const char *start = *in;
+       GString *domain = str;
        
        do {
                if (!is_atom (*inptr))
@@ -1441,7 +1514,15 @@ dotatom_parse (GString *str, const char **in, const char *sentinels)
                if (!g_utf8_validate (atom, (size_t) (inptr - atom), NULL))
                        goto error;
                
-               g_string_append_len (str, atom, (size_t) (inptr - atom));
+#if LIBIDN
+               if (domain == str && !strncmp (atom, "xn--", 4)) {
+                       /* from here on out, we'll use a temp domain buffer so that
+                        * we can decode it once we're done parsing it */
+                       domain = g_string_new ("");
+               }
+#endif
+               
+               g_string_append_len (domain, atom, (size_t) (inptr - atom));
                
                comment = inptr;
                if (!skip_cfws (&inptr))
@@ -1462,14 +1543,36 @@ dotatom_parse (GString *str, const char **in, const char *sentinels)
                if (*inptr == '\0' || strchr (sentinels, *inptr))
                        break;
                
-               g_string_append_c (str, '.');
+               g_string_append_c (domain, '.');
        } while (TRUE);
        
+#ifdef LIBIDN
+       if (domain != str) {
+               char *unicode;
+               
+               if (idna_to_unicode_8z8z (domain->str, &unicode, 0) == IDNA_SUCCESS) {
+                       g_string_append (str, unicode);
+                       free (unicode);
+               } else {
+                       g_string_append_len (str, domain->str, domain->len);
+               }
+               
+               g_string_free (domain, TRUE);
+       }
+#endif
+       
        *in = inptr;
        
        return TRUE;
        
  error:
+#ifdef LIBIDN
+       if (domain != str) {
+               g_string_append_len (str, domain->str, domain->len);
+               g_string_free (domain, TRUE);
+       }
+#endif
+       
        *in = inptr;
        
        return FALSE;
@@ -1522,11 +1625,12 @@ domain_parse (GString *str, const char **in, const char *sentinels)
 }
 
 static gboolean
-addrspec_parse (const char **in, const char *sentinels, char **addrspec)
+addrspec_parse (const char **in, const char *sentinels, char **addrspec, int *at)
 {
        const char *inptr = *in;
        const char *start = *in;
        GString *str;
+       guint domain;
        
        str = g_string_new ("");
        
@@ -1536,12 +1640,14 @@ addrspec_parse (const char **in, const char *sentinels, char **addrspec)
        if (*inptr == '\0' || strchr (sentinels, *inptr)) {
                *addrspec = g_string_free (str, FALSE);
                *in = inptr;
+               *at = -1;
                return TRUE;
        }
        
        if (*inptr != '@')
                goto error;
        
+       *at = str->len;
        g_string_append_c (str, *inptr++);
        
        if (*inptr == '\0')
@@ -1565,6 +1671,7 @@ addrspec_parse (const char **in, const char *sentinels, char **addrspec)
        g_string_free (str, TRUE);
        *addrspec = NULL;
        *in = inptr;
+       *at = -1;
        
        return FALSE;
 }
@@ -1576,6 +1683,7 @@ mailbox_parse (GMimeParserOptions *options, const char **in, const char *name, I
        GMimeRfcComplianceMode mode = g_mime_parser_options_get_address_compliance_mode (options);
        const char *inptr = *in;
        char *addrspec = NULL;
+       int at;
        
        /* skip over the '<' */
        inptr++;
@@ -1609,12 +1717,12 @@ mailbox_parse (GMimeParserOptions *options, const char **in, const char *name, I
                        goto error;
        }
        
-       // Note: The only syntactically correct sentinel token here is the '>', but alas... to deal with the 
first example
-       // in section 7.1.5 of rfc7103, we need to at least handle ',' as a sentinel and might as well handle 
';' as well
-       // in case the mailbox is within a group address.
+       // Note: The only syntactically correct sentinel token here is the '>', but alas... to deal with the 
first
+       // example in section 7.1.5 of rfc7103, we need to at least handle ',' as a sentinel and might as 
well handle
+       // ';' as well in case the mailbox is within a group address.
        //
        // Example: <third example net, fourth example net>
-       if (!addrspec_parse (&inptr, COMMA_GREATER_THAN_OR_SEMICOLON, &addrspec))
+       if (!addrspec_parse (&inptr, COMMA_GREATER_THAN_OR_SEMICOLON, &addrspec, &at))
                goto error;
        
        if (!skip_cfws (&inptr))
@@ -1638,7 +1746,7 @@ mailbox_parse (GMimeParserOptions *options, const char **in, const char *name, I
                }
        }
        
-       *address = internet_address_mailbox_new (name, addrspec);
+       *address = _internet_address_mailbox_new (name, addrspec, at);
        g_free (addrspec);
        *in = inptr;
        
@@ -1758,6 +1866,7 @@ address_parse (GMimeParserOptions *options, AddressParserFlags flags, const char
                char sentinel = *inptr != '\0' ? *inptr : ',';
                char sentinels[2] = { sentinel, 0 };
                char *name, *addrspec;
+               int at;
                
                /* rewind back to the beginning of the local-part */
                inptr = start;
@@ -1765,7 +1874,7 @@ address_parse (GMimeParserOptions *options, AddressParserFlags flags, const char
                if (!(flags & ALLOW_MAILBOX))
                        goto error;
                
-               if (!addrspec_parse (&inptr, sentinels, &addrspec))
+               if (!addrspec_parse (&inptr, sentinels, &addrspec, &at))
                        goto error;
                
                skip_lwsp (&inptr);
@@ -1791,7 +1900,7 @@ address_parse (GMimeParserOptions *options, AddressParserFlags flags, const char
                        inptr++;
                }
                
-               *address = internet_address_mailbox_new (name, addrspec);
+               *address = _internet_address_mailbox_new (name, addrspec, at);
                g_free (addrspec);
                g_free (name);
                *in = inptr;
@@ -1837,11 +1946,12 @@ address_parse (GMimeParserOptions *options, AddressParserFlags flags, const char
                /* we're either in the middle of an addr-spec token or we completely gobbled up
                 * an addr-spec w/o a domain */
                char *name, *addrspec;
+               int at;
                
                /* rewind back to the beginning of the local-part */
                inptr = start;
                
-               if (!addrspec_parse (&inptr, COMMA_GREATER_THAN_OR_SEMICOLON, &addrspec))
+               if (!addrspec_parse (&inptr, COMMA_GREATER_THAN_OR_SEMICOLON, &addrspec, &at))
                        goto error;
                
                skip_lwsp (&inptr);
@@ -1867,7 +1977,7 @@ address_parse (GMimeParserOptions *options, AddressParserFlags flags, const char
                }
                
                if (*inptr == '\0') {
-                       *address = internet_address_mailbox_new (name, addrspec);
+                       *address = _internet_address_mailbox_new (name, addrspec, at);
                        g_free (addrspec);
                        g_free (name);
                        *in = inptr;
@@ -1903,7 +2013,7 @@ address_parse (GMimeParserOptions *options, AddressParserFlags flags, const char
                                inptr++;
                        }
                        
-                       *address = internet_address_mailbox_new (name, addrspec);
+                       *address = _internet_address_mailbox_new (name, addrspec, at);
                        g_free (addrspec);
                        g_free (name);
                        *in = inptr;
diff --git a/gmime/internet-address.h b/gmime/internet-address.h
index f2bebef..44ad049 100644
--- a/gmime/internet-address.h
+++ b/gmime/internet-address.h
@@ -110,14 +110,18 @@ char *internet_address_to_string (InternetAddress *ia, GMimeFormatOptions *optio
 /**
  * InternetAddressMailbox:
  * @parent_object: parent #InternetAddress
- * @addr: address string
+ * @idn_addr: the ascii-encoded version of @addr
+ * @addr: the address string
+ * @at: the index of the '@' character
  *
  * An RFC 2822 Mailbox address.
  **/
 struct _InternetAddressMailbox {
        InternetAddress parent_object;
        
+       char *idn_addr;
        char *addr;
+       int at;
 };
 
 struct _InternetAddressMailboxClass {


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]