[evolution-data-server] Bug 486018 - Implement IDNA (Internationalized Domain Names in Applications)



commit b068eb4c76f56e13573e4388a536df02f2beb5aa
Author: Milan Crha <mcrha redhat com>
Date:   Wed Aug 27 14:07:23 2014 +0200

    Bug 486018 - Implement IDNA (Internationalized Domain Names in Applications)

 camel/camel-internet-address.c |   66 ++++++++++++++++++++++++++++++++
 camel/camel-internet-address.h |    2 +
 camel/camel-net-utils.c        |   81 +++++++++++++++++++++++++++++++++++++++-
 camel/camel-net-utils.h        |    2 +
 camel/camel-network-service.c  |    2 +-
 camel/camel-network-settings.c |   35 +++++++++++++++++
 camel/camel-network-settings.h |    2 +
 camel/camel-transport.c        |   14 ++++++-
 configure.ac                   |    2 +-
 9 files changed, 201 insertions(+), 5 deletions(-)
---
diff --git a/camel/camel-internet-address.c b/camel/camel-internet-address.c
index 99435ee..44c8d39 100644
--- a/camel/camel-internet-address.c
+++ b/camel/camel-internet-address.c
@@ -21,6 +21,7 @@
 
 #include "camel-internet-address.h"
 #include "camel-mime-utils.h"
+#include "camel-net-utils.h"
 
 #define d(x)
 
@@ -348,6 +349,71 @@ camel_internet_address_find_name (CamelInternetAddress *addr,
        return -1;
 }
 
+static gboolean
+domain_contains_only_ascii (const gchar *address,
+                           gint *at_pos)
+{
+       gint pos;
+       gboolean all_ascii = TRUE;
+
+       g_return_val_if_fail (address != NULL, TRUE);
+       g_return_val_if_fail (at_pos != NULL, TRUE);
+
+       *at_pos = -1;
+       for (pos = 0; address[pos]; pos++) {
+               all_ascii = all_ascii && address[pos] > 0;
+               if (*at_pos == -1 && address[pos] == '@') {
+                       *at_pos = pos;
+                       all_ascii = TRUE;
+               }
+       }
+
+       /* Do not change anything when there is no domain part
+          of the email address */
+       return all_ascii || *at_pos == -1;
+}
+
+/**
+ * camel_internet_address_ensure_ascii_domains:
+ * @addr: a #CamelInternetAddress
+ *
+ * Ensures that all email address' domains will be ASCII encoded,
+ * which means that any non-ASCII letters will be properly encoded.
+ * This includes IDN (Internationalized Domain Names).
+ *
+ * Since: 3.14
+ **/
+void
+camel_internet_address_ensure_ascii_domains (CamelInternetAddress *addr)
+{
+       struct _address *a;
+       gint i, len;
+
+       g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (addr));
+
+       len = ((CamelAddress *) addr)->addresses->len;
+       for (i = 0; i < len; i++) {
+               gint at_pos = -1;
+               a = g_ptr_array_index (((CamelAddress *) addr)->addresses, i);
+               if (a->address && !domain_contains_only_ascii (a->address, &at_pos)) {
+                       gchar *address, *domain;
+
+                       domain = camel_host_idna_to_ascii (a->address + at_pos + 1);
+                       if (at_pos >= 0) {
+                               gchar *name = g_strndup (a->address, at_pos);
+                               address = g_strconcat (name, "@", domain, NULL);
+                       } else {
+                               address = domain;
+                               domain = NULL;
+                       }
+
+                       g_free (domain);
+                       g_free (a->address);
+                       a->address = address;
+               }
+       }
+}
+
 /**
  * camel_internet_address_find_address:
  * @addr: a #CamelInternetAddress object
diff --git a/camel/camel-internet-address.h b/camel/camel-internet-address.h
index ce9288b..4253abb 100644
--- a/camel/camel-internet-address.h
+++ b/camel/camel-internet-address.h
@@ -77,6 +77,8 @@ gint          camel_internet_address_find_address
                                                (CamelInternetAddress *addr,
                                                 const gchar *address,
                                                 const gchar **namep);
+void           camel_internet_address_ensure_ascii_domains
+                                               (CamelInternetAddress *addr);
 
 /* utility functions, for network/display formatting */
 gchar *                camel_internet_address_encode_address
diff --git a/camel/camel-net-utils.c b/camel/camel-net-utils.c
index d22f5b9..1af3229 100644
--- a/camel/camel-net-utils.c
+++ b/camel/camel-net-utils.c
@@ -27,6 +27,8 @@
 #include <stdio.h>
 
 #include <glib/gi18n-lib.h>
+#include <unicode/uidna.h>
+#include <unicode/ustring.h>
 
 #include "camel-msgport.h"
 #include "camel-net-utils.h"
@@ -693,6 +695,8 @@ camel_getaddrinfo (const gchar *name,
 #ifndef ENABLE_IPv6
        struct addrinfo myhints;
 #endif
+       gchar *ascii_name;
+
        g_return_val_if_fail (name != NULL, NULL);
 
        if (g_cancellable_set_error_if_cancelled (cancellable, error))
@@ -712,8 +716,10 @@ camel_getaddrinfo (const gchar *name,
        hints = &myhints;
 #endif
 
+       ascii_name = camel_host_idna_to_ascii (name);
+
        msg = g_malloc0 (sizeof (*msg));
-       msg->name = name;
+       msg->name = ascii_name;
        msg->service = service;
        msg->hints = hints;
        msg->res = &res;
@@ -739,6 +745,7 @@ camel_getaddrinfo (const gchar *name,
                res = NULL;
 
        cs_freeinfo (msg);
+       g_free (ascii_name);
 
        camel_operation_pop_message (cancellable);
 
@@ -767,3 +774,75 @@ camel_freeaddrinfo (struct addrinfo *host)
 #endif
 }
 
+/**
+ * camel_host_idna_to_ascii:
+ * @host: Host name, with or without non-ascii letters in utf8
+ *
+ * Converts IDN (Internationalized Domain Name) into ASCII representation.
+ * If there's a failure or the @host has only ASCII letters, then a copy
+ * of @host is returned.
+ *
+ * Returns: Newly allocated string with only ASCII letters describing the @host.
+ *   Free it with g_free() when done with it.
+ *
+ * Since: 3.14
+ **/
+gchar *
+camel_host_idna_to_ascii (const gchar *host)
+{
+       UErrorCode uerror = U_ZERO_ERROR;
+       int32_t uhost_len = 0;
+       const gchar *ptr;
+       gchar *ascii = NULL;
+
+       g_return_val_if_fail (host != NULL, NULL);
+
+       ptr = host;
+       while (*ptr > 0)
+               ptr++;
+
+       if (!*ptr) {
+               /* Did read whole buffer, it should be ASCII string already */
+               return g_strdup (host);
+       }
+
+       u_strFromUTF8 (NULL, 0, &uhost_len, host, -1, &uerror);
+       if (uhost_len > 0) {
+               UChar *uhost = g_new0 (UChar, uhost_len + 2);
+
+               uerror = U_ZERO_ERROR;
+               u_strFromUTF8 (uhost, uhost_len + 1, &uhost_len, host, -1, &uerror);
+               if (uerror == U_ZERO_ERROR && uhost_len > 0) {
+                       int32_t buffer_len = uhost_len * 6 + 6, nconverted;
+                       UChar *buffer = g_new0 (UChar, buffer_len);
+
+                       nconverted = uidna_IDNToASCII (uhost, uhost_len, buffer, buffer_len, 
UIDNA_ALLOW_UNASSIGNED, 0, &uerror);
+                       if (uerror == U_ZERO_ERROR && nconverted > 0) {
+                               int32_t ascii_len = 0;
+
+                               u_strToUTF8 (NULL, 0, &ascii_len, buffer, nconverted, &uerror);
+                               if (ascii_len > 0) {
+                                       uerror = U_ZERO_ERROR;
+                                       ascii = g_new0 (gchar, ascii_len + 2);
+
+                                       u_strToUTF8 (ascii, ascii_len + 1, &ascii_len, buffer, nconverted, 
&uerror);
+                                       if (uerror == U_ZERO_ERROR && ascii_len > 0) {
+                                               ascii[ascii_len] = '\0';
+                                       } else {
+                                               g_free (ascii);
+                                               ascii = NULL;
+                                       }
+                               }
+                       }
+
+                       g_free (buffer);
+               }
+
+               g_free (uhost);
+       }
+
+       if (!ascii)
+               ascii = g_strdup (host);
+
+       return ascii;
+}
diff --git a/camel/camel-net-utils.h b/camel/camel-net-utils.h
index 000f55c..8090263 100644
--- a/camel/camel-net-utils.h
+++ b/camel/camel-net-utils.h
@@ -94,6 +94,8 @@ struct addrinfo *
                                                 GError **error);
 void           camel_freeaddrinfo              (struct addrinfo *host);
 
+gchar *                camel_host_idna_to_ascii        (const gchar *host);
+
 G_END_DECLS
 
 #ifdef _WIN32
diff --git a/camel/camel-network-service.c b/camel/camel-network-service.c
index 0654ba6..4f80c3c 100644
--- a/camel/camel-network-service.c
+++ b/camel/camel-network-service.c
@@ -662,7 +662,7 @@ network_service_new_connectable (CamelNetworkService *service)
        g_return_val_if_fail (CAMEL_IS_NETWORK_SETTINGS (settings), NULL);
 
        network_settings = CAMEL_NETWORK_SETTINGS (settings);
-       host = camel_network_settings_dup_host (network_settings);
+       host = camel_network_settings_dup_host_ensure_ascii (network_settings);
        port = camel_network_settings_get_port (network_settings);
 
        if (host && *host && g_ascii_strcasecmp (host, "localhost") != 0)
diff --git a/camel/camel-network-settings.c b/camel/camel-network-settings.c
index 0e7f801..33d6ef3 100644
--- a/camel/camel-network-settings.c
+++ b/camel/camel-network-settings.c
@@ -19,6 +19,7 @@
 
 #include <camel/camel-enumtypes.h>
 #include <camel/camel-settings.h>
+#include <camel/camel-net-utils.h>
 
 #define AUTH_MECHANISM_KEY  "CamelNetworkSettings:auth-mechanism"
 #define HOST_KEY            "CamelNetworkSettings:host"
@@ -234,6 +235,40 @@ camel_network_settings_dup_host (CamelNetworkSettings *settings)
 }
 
 /**
+ * camel_network_settings_dup_host_ensure_ascii:
+ * @settings: a #CamelNetworkSettings
+ *
+ * Just like camel_network_settings_dup_host(), only makes sure that
+ * the returned host name will be converted into its ASCII form in case
+ * of IDNA value.
+ *
+ * Returns: a newly-allocated copy of #CamelNetworkSettings:host with
+ *    only ASCII letters.
+ *
+ * Since: 3.14
+ **/
+gchar *
+camel_network_settings_dup_host_ensure_ascii (CamelNetworkSettings *settings)
+{
+       const gchar *protected;
+       gchar *duplicate;
+
+       g_return_val_if_fail (CAMEL_IS_NETWORK_SETTINGS (settings), NULL);
+
+       G_LOCK (property_lock);
+
+       protected = camel_network_settings_get_host (settings);
+       if (protected && *protected)
+               duplicate = camel_host_idna_to_ascii (protected);
+       else
+               duplicate = g_strdup (protected);
+
+       G_UNLOCK (property_lock);
+
+       return duplicate;
+}
+
+/**
  * camel_network_settings_set_host:
  * @settings: a #CamelNetworkSettings
  * @host: a host name, or %NULL
diff --git a/camel/camel-network-settings.h b/camel/camel-network-settings.h
index acade27..db79485 100644
--- a/camel/camel-network-settings.h
+++ b/camel/camel-network-settings.h
@@ -71,6 +71,8 @@ const gchar * camel_network_settings_get_host
                                        (CamelNetworkSettings *settings);
 gchar *                camel_network_settings_dup_host
                                        (CamelNetworkSettings *settings);
+gchar *                camel_network_settings_dup_host_ensure_ascii
+                                       (CamelNetworkSettings *settings);
 void           camel_network_settings_set_host
                                        (CamelNetworkSettings *settings,
                                         const gchar *host);
diff --git a/camel/camel-transport.c b/camel/camel-transport.c
index 76a619d..38ad3db 100644
--- a/camel/camel-transport.c
+++ b/camel/camel-transport.c
@@ -200,8 +200,18 @@ camel_transport_send_to (CamelTransport *transport,
        service = CAMEL_SERVICE (transport);
 
        async_context = g_slice_new0 (AsyncContext);
-       async_context->from = g_object_ref (from);
-       async_context->recipients = g_object_ref (recipients);
+       if (CAMEL_IS_INTERNET_ADDRESS (from)) {
+               async_context->from = camel_address_new_clone (from);
+               camel_internet_address_ensure_ascii_domains (CAMEL_INTERNET_ADDRESS (async_context->from));
+       } else {
+               async_context->from = g_object_ref (from);
+       }
+       if (CAMEL_IS_INTERNET_ADDRESS (recipients)) {
+               async_context->recipients = camel_address_new_clone (recipients);
+               camel_internet_address_ensure_ascii_domains (CAMEL_INTERNET_ADDRESS 
(async_context->recipients));
+       } else {
+               async_context->recipients = g_object_ref (recipients);
+       }
        async_context->message = g_object_ref (message);
 
        task = g_task_new (transport, cancellable, callback, user_data);
diff --git a/configure.ac b/configure.ac
index fb78a04..93fa65a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1608,7 +1608,7 @@ fi
 
 AM_CONDITIONAL(ENABLE_LARGEFILE, [test "x$enable_largefile" = "xyes"])
 
-EVO_SET_COMPILE_FLAGS(CAMEL, gio-2.0 gmodule-2.0 $mozilla_nss $mozilla_nspr sqlite3 >= 
sqlite_minimum_version, $KRB5_CFLAGS $MANUAL_NSS_CFLAGS $MANUAL_NSPR_CFLAGS $LARGEFILE_CFLAGS, -lz $KRB5_LIBS 
$MANUAL_NSS_LIBS $MANUAL_NSPR_LIBS)
+EVO_SET_COMPILE_FLAGS(CAMEL, gio-2.0 gmodule-2.0 $mozilla_nss $mozilla_nspr sqlite3 >= 
sqlite_minimum_version, $KRB5_CFLAGS $MANUAL_NSS_CFLAGS $MANUAL_NSPR_CFLAGS $LARGEFILE_CFLAGS $ICU_CFLAGS, 
-lz $KRB5_LIBS $MANUAL_NSS_LIBS $MANUAL_NSPR_LIBS $ICU_LIBS)
 AC_SUBST(CAMEL_CFLAGS)
 AC_SUBST(CAMEL_LIBS)
 


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