[evolution-data-server] CamelNetworkService: Transition to GTcpConnection.



commit c16ad8de345366f78213df36520b191e203ffa43
Author: Matthew Barnes <mbarnes redhat com>
Date:   Thu Sep 26 19:37:38 2013 -0400

    CamelNetworkService: Transition to GTcpConnection.
    
    camel_network_service_connect_sync() now returns a CamelStream with a
    base GTcpConnection instead of a CamelTcpStream.  CamelNetworkService
    handles peer certificate acceptance during the TLS handshake.
    
    This also adds camel_network_service_starttls() to be called after
    issuing a STARTTLS command to a server.  This function replaces the
    GTcpConnection with a GTlsConnection in the given CamelStream.
    
    Proxy support is temporarily disabled until I can rework a few things.
    
    New functions:
    
      camel_network_service_starttls()

 camel/camel-imapx-server.c                  |   22 +-
 camel/camel-network-service.c               |  469 ++++++++++++++++++++++++---
 camel/camel-network-service.h               |    4 +
 camel/providers/pop3/camel-pop3-store.c     |    7 +-
 camel/providers/smtp/camel-smtp-transport.c |   78 +++--
 camel/providers/smtp/camel-smtp-transport.h |    3 +-
 docs/reference/camel/camel-sections.txt     |    1 +
 7 files changed, 472 insertions(+), 112 deletions(-)
---
diff --git a/camel/camel-imapx-server.c b/camel/camel-imapx-server.c
index 7a082b1..09c422e 100644
--- a/camel/camel-imapx-server.c
+++ b/camel/camel-imapx-server.c
@@ -3851,7 +3851,6 @@ imapx_connect_to_server (CamelIMAPXServer *is,
        CamelNetworkSecurityMethod method;
        CamelStream *tcp_stream = NULL;
        CamelStream *imapx_stream = NULL;
-       CamelSockOptData sockopt;
        CamelIMAPXStore *store;
        CamelSettings *settings;
        guint len;
@@ -3908,18 +3907,6 @@ imapx_connect_to_server (CamelIMAPXServer *is,
                goto exit;
        }
 
-       /* Disable Nagle
-        * We send a lot of small requests which nagle slows down. */
-       sockopt.option = CAMEL_SOCKOPT_NODELAY;
-       sockopt.value.no_delay = TRUE;
-       camel_tcp_stream_setsockopt (CAMEL_TCP_STREAM (tcp_stream), &sockopt);
-
-       /* Set Keepalive
-        * Needed for some hosts/router configurations, we're idle a lot. */
-       sockopt.option = CAMEL_SOCKOPT_KEEPALIVE;
-       sockopt.value.keep_alive = TRUE;
-       camel_tcp_stream_setsockopt (CAMEL_TCP_STREAM (tcp_stream), &sockopt);
-
        imapx_stream = camel_imapx_stream_new (tcp_stream);
 
        /* CamelIMAPXServer takes ownership of the IMAPX stream.
@@ -4039,15 +4026,16 @@ imapx_connect_to_server (CamelIMAPXServer *is,
                if (!success)
                        goto exit;
 
-               if (camel_tcp_stream_ssl_enable_ssl (
-                       CAMEL_TCP_STREAM_SSL (tcp_stream),
-                       cancellable, &local_error) == -1) {
+               success = camel_network_service_starttls (
+                       CAMEL_NETWORK_SERVICE (store), tcp_stream, error);
+               if (!success) {
                        g_prefix_error (
-                               &local_error,
+                               error,
                                _("Failed to connect to IMAP server %s in secure mode: "),
                                host);
                        goto exit;
                }
+
                /* Get new capabilities if they weren't already given */
                if (is->cinfo == NULL) {
                        ic = camel_imapx_command_new (
diff --git a/camel/camel-network-service.c b/camel/camel-network-service.c
index 438540c..0163449 100644
--- a/camel/camel-network-service.c
+++ b/camel/camel-network-service.c
@@ -19,15 +19,13 @@
 #include "camel-network-service.h"
 
 #include <config.h>
+#include <glib/gstdio.h>
 #include <glib/gi18n-lib.h>
 
 #include <camel/camel-enumtypes.h>
 #include <camel/camel-network-settings.h>
 #include <camel/camel-service.h>
 #include <camel/camel-session.h>
-#include <camel/camel-tcp-stream-raw.h>
-
-#include <camel/camel-tcp-stream-ssl.h>
 
 #define PRIVATE_KEY "CamelNetworkService:private"
 
@@ -65,6 +63,340 @@ G_DEFINE_INTERFACE (
        camel_network_service,
        CAMEL_TYPE_SERVICE)
 
+static const gchar *
+network_service_get_cert_dir (void)
+{
+       static gchar *cert_dir = NULL;
+
+       if (G_UNLIKELY (cert_dir == NULL)) {
+               const gchar *data_dir;
+               const gchar *home_dir;
+               gchar *old_dir;
+
+               home_dir = g_get_home_dir ();
+               data_dir = g_get_user_data_dir ();
+
+               cert_dir = g_build_filename (data_dir, "camel_certs", NULL);
+
+               /* Move the old certificate directory if present. */
+               old_dir = g_build_filename (home_dir, ".camel_certs", NULL);
+               if (g_file_test (old_dir, G_FILE_TEST_IS_DIR))
+                       g_rename (old_dir, cert_dir);
+               g_free (old_dir);
+
+               g_mkdir_with_parents (cert_dir, 0700);
+       }
+
+       return cert_dir;
+}
+
+static gchar *
+network_service_generate_fingerprint (GTlsCertificate *certificate)
+{
+       GChecksum *checksum;
+       GString *fingerprint;
+       GByteArray *der;
+       guint8 *digest;
+       gsize length, ii;
+       const gchar tohex[16] = "0123456789abcdef";
+
+       /* XXX No accessor function for this property. */
+       g_object_get (certificate, "certificate", &der, NULL);
+       g_return_val_if_fail (der != NULL, NULL);
+
+       length = g_checksum_type_get_length (G_CHECKSUM_MD5);
+       digest = g_alloca (length);
+
+       checksum = g_checksum_new (G_CHECKSUM_MD5);
+       g_checksum_update (checksum, der->data, der->len);
+       g_checksum_get_digest (checksum, digest, &length);
+       g_checksum_free (checksum);
+
+       g_byte_array_unref (der);
+
+       fingerprint = g_string_sized_new (50);
+
+       for (ii = 0; ii < length; ii++) {
+               guint8 byte = digest[ii];
+
+               g_string_append_c (fingerprint, tohex[(byte >> 4) & 0xf]);
+               g_string_append_c (fingerprint, tohex[byte & 0xf]);
+#ifndef G_OS_WIN32
+               g_string_append_c (fingerprint, ':');
+#else
+               /* The fingerprint is used as a filename, but can't have
+                * colons in filenames on Win32.  Use underscore instead. */
+               g_string_append_c (fingerprint, '_');
+#endif
+       }
+
+       return g_string_free (fingerprint, FALSE);
+}
+
+static GBytes *
+network_service_load_cert_file (const gchar *fingerprint,
+                                GError **error)
+{
+       GBytes *bytes = NULL;
+       gchar *contents = NULL;
+       gchar *filename;
+       gsize length;
+       const gchar *cert_dir;
+
+       cert_dir = network_service_get_cert_dir ();
+       filename = g_build_filename (cert_dir, fingerprint, NULL);
+
+       if (g_file_get_contents (filename, &contents, &length, error))
+               bytes = g_bytes_new_take (contents, length);
+
+       g_free (filename);
+
+       return bytes;
+}
+
+static GBytes *
+network_service_save_cert_file (GTlsCertificate *certificate,
+                                GError **error)
+{
+       GByteArray *der;
+       GBytes *bytes = NULL;
+       GFile *file;
+       GFileOutputStream *output_stream;
+       gchar *filename;
+       gchar *fingerprint;
+       const gchar *cert_dir;
+
+       /* XXX No accessor function for this property. */
+       g_object_get (certificate, "certificate", &der, NULL);
+       g_return_val_if_fail (der != NULL, NULL);
+
+       fingerprint = network_service_generate_fingerprint (certificate);
+       g_return_val_if_fail (fingerprint != NULL, NULL);
+
+       cert_dir = network_service_get_cert_dir ();
+       filename = g_build_filename (cert_dir, fingerprint, NULL);
+       file = g_file_new_for_path (filename);
+
+       output_stream = g_file_replace (
+               file, NULL, FALSE,
+               G_FILE_CREATE_REPLACE_DESTINATION,
+               NULL, error);
+
+       g_object_unref (file);
+       g_free (filename);
+
+       if (output_stream != NULL) {
+               gssize n_written;
+
+               /* XXX Treat GByteArray as though its data is owned by
+                *     GTlsCertificate.  That means avoiding functions
+                *     like g_byte_array_free_to_bytes() that alter or
+                *     reset the GByteArray. */
+               bytes = g_bytes_new (der->data, der->len);
+
+               /* XXX Not handling partial writes, but GIO does not make
+                *     it easy.  Need a g_output_stream_write_all_bytes().
+                *     (see: https://bugzilla.gnome.org/708838) */
+               n_written = g_output_stream_write_bytes (
+                       G_OUTPUT_STREAM (output_stream),
+                       bytes, NULL, error);
+
+               if (n_written < 0) {
+                       g_bytes_unref (bytes);
+                       bytes = NULL;
+               }
+       }
+
+       g_byte_array_unref (der);
+       g_free (fingerprint);
+
+       return bytes;
+}
+
+static CamelCert *
+network_service_certdb_lookup (CamelCertDB *certdb,
+                               GTlsCertificate *certificate,
+                               const gchar *expected_host)
+{
+       CamelCert *cert = NULL;
+       GBytes *bytes;
+       GByteArray *der;
+       gchar *fingerprint;
+
+       fingerprint = network_service_generate_fingerprint (certificate);
+       g_return_val_if_fail (fingerprint != NULL, NULL);
+
+       cert = camel_certdb_get_host (certdb, expected_host, fingerprint);
+       if (cert == NULL)
+               goto exit;
+
+       if (cert->rawcert == NULL) {
+               GError *local_error = NULL;
+
+               cert->rawcert = network_service_load_cert_file (
+                       fingerprint, &local_error);
+
+               /* Sanity check. */
+               g_warn_if_fail (
+                       ((cert->rawcert != NULL) && (local_error == NULL)) ||
+                       ((cert->rawcert == NULL) && (local_error != NULL)));
+
+               if (local_error != NULL) {
+                       g_warning ("%s: %s", G_STRFUNC, local_error->message);
+                       g_error_free (local_error);
+               }
+
+               if (cert->rawcert == NULL) {
+                       camel_certdb_remove_host (
+                               certdb, expected_host, fingerprint);
+                       camel_certdb_touch (certdb);
+                       goto exit;
+               }
+       }
+
+       /* XXX No accessor function for this property. */
+       g_object_get (certificate, "certificate", &der, NULL);
+       g_return_val_if_fail (der != NULL, cert);
+
+       bytes = g_bytes_new_static (der->data, der->len);
+
+       if (g_bytes_compare (bytes, cert->rawcert) != 0) {
+               cert->trust = CAMEL_CERT_TRUST_UNKNOWN;
+               camel_certdb_touch (certdb);
+       }
+
+       g_byte_array_unref (der);
+       g_bytes_unref (bytes);
+
+exit:
+       g_free (fingerprint);
+
+       return cert;
+}
+
+static void
+network_service_certdb_store (CamelCertDB *certdb,
+                              CamelCert *cert,
+                              GTlsCertificate *certificate)
+{
+       GError *local_error = NULL;
+
+       cert->rawcert = network_service_save_cert_file (
+               certificate, &local_error);
+
+       /* Sanity check. */
+       g_warn_if_fail (
+               ((cert->rawcert != NULL) && (local_error == NULL)) ||
+               ((cert->rawcert == NULL) && (local_error != NULL)));
+
+       if (cert->rawcert != NULL)
+               camel_certdb_put (certdb, cert);
+
+       if (local_error != NULL) {
+               g_warning ("%s: %s", G_STRFUNC, local_error->message);
+               g_error_free (local_error);
+       }
+}
+
+static gboolean
+network_service_accept_certificate_cb (GTlsConnection *connection,
+                                       GTlsCertificate *peer_certificate,
+                                       GTlsCertificateFlags errors,
+                                       CamelNetworkService *service)
+{
+       CamelCert *cert;
+       CamelCertDB *certdb;
+       CamelSession *session;
+       CamelSettings *settings;
+       CamelNetworkSettings *network_settings;
+       gboolean new_cert = FALSE;
+       gboolean accept;
+       gchar *host;
+
+       session = camel_service_ref_session (CAMEL_SERVICE (service));
+       settings = camel_service_ref_settings (CAMEL_SERVICE (service));
+
+       network_settings = CAMEL_NETWORK_SETTINGS (settings);
+       host = camel_network_settings_dup_host (network_settings);
+
+       certdb = camel_certdb_get_default ();
+       cert = network_service_certdb_lookup (certdb, peer_certificate, host);
+
+       if (cert == NULL) {
+               cert = camel_cert_new ();
+               cert->fingerprint =
+                       network_service_generate_fingerprint (
+                       peer_certificate);
+               cert->hostname = g_strdup (host);
+               cert->trust = CAMEL_CERT_TRUST_UNKNOWN;
+
+               /* Don't put() in the CamelCertDB yet.  Since we can only
+                * store one entry per hostname, we'd rather not ruin any
+                * existing entry for this hostname if the user rejects
+                * the new certificate. */
+               new_cert = TRUE;
+       }
+
+       if (cert->trust == CAMEL_CERT_TRUST_UNKNOWN) {
+               GByteArray *der;
+               gchar *base64;
+
+               /* XXX No accessor function for this property. */
+               g_object_get (peer_certificate, "certificate", &der, NULL);
+               g_return_val_if_fail (der != NULL, FALSE);
+
+               base64 = g_base64_encode (der->data, der->len);
+
+               cert->trust = camel_session_trust_prompt (
+                       session, host, base64, errors, 0, NULL);
+
+               if (new_cert)
+                       network_service_certdb_store (
+                               certdb, cert, peer_certificate);
+
+               camel_certdb_touch (certdb);
+
+               g_free (base64);
+               g_byte_array_unref (der);
+       }
+
+       switch (cert->trust) {
+               case CAMEL_CERT_TRUST_MARGINAL:
+               case CAMEL_CERT_TRUST_FULLY:
+               case CAMEL_CERT_TRUST_ULTIMATE:
+               case CAMEL_CERT_TRUST_TEMPORARY:
+                       accept = TRUE;
+                       break;
+               default:
+                       accept = FALSE;
+                       break;
+       }
+
+       camel_cert_unref (cert);
+       camel_certdb_save (certdb);
+
+       g_clear_object (&certdb);
+       g_clear_object (&session);
+       g_clear_object (&settings);
+
+       return accept;
+}
+
+static void
+network_service_client_event_cb (GSocketClient *client,
+                                 GSocketClientEvent event,
+                                 GSocketConnectable *connectable,
+                                 GIOStream *connection,
+                                 CamelNetworkService *service)
+{
+       if (event == G_SOCKET_CLIENT_TLS_HANDSHAKING) {
+               g_signal_connect (
+                       connection, "accept-certificate",
+                       G_CALLBACK (network_service_accept_certificate_cb),
+                       service);
+       }
+}
+
 static void
 network_service_set_host_reachable (CamelNetworkService *service,
                                     gboolean host_reachable)
@@ -274,63 +606,47 @@ network_service_connect_sync (CamelNetworkService *service,
                               GCancellable *cancellable,
                               GError **error)
 {
+       GSocketClient *client;
+       GSocketConnection *connection;
+       GSocketConnectable *connectable;
        CamelNetworkSecurityMethod method;
        CamelNetworkSettings *network_settings;
        CamelSettings *settings;
-       CamelSession *session;
-       CamelStream *stream;
-       const gchar *service_name;
-       guint16 default_port;
-       guint16 port;
+       CamelStream *stream = NULL;
+#if 0
        gchar *socks_host;
        gint socks_port;
-       gchar *host;
-       gint status;
+#endif
 
-       session = camel_service_ref_session (CAMEL_SERVICE (service));
        settings = camel_service_ref_settings (CAMEL_SERVICE (service));
        g_return_val_if_fail (CAMEL_IS_NETWORK_SETTINGS (settings), NULL);
 
        network_settings = CAMEL_NETWORK_SETTINGS (settings);
        method = camel_network_settings_get_security_method (network_settings);
-       host = camel_network_settings_dup_host (network_settings);
-       port = camel_network_settings_get_port (network_settings);
 
        g_object_unref (settings);
 
-       service_name = camel_network_service_get_service_name (service, method);
-       default_port = camel_network_service_get_default_port (service, method);
+       connectable = camel_network_service_ref_connectable (service);
+       g_return_val_if_fail (connectable != NULL, NULL);
 
-       /* If the URL explicitly gives a port number, make
-        * it override the service name and default port. */
-       if (port > 0) {
-               service_name = g_alloca (16);
-               sprintf ((gchar *) service_name, "%u", port);
-               default_port = 0;
-       }
+       client = g_socket_client_new ();
 
-       switch (method) {
-               case CAMEL_NETWORK_SECURITY_METHOD_NONE:
-                       stream = camel_tcp_stream_raw_new ();
-                       break;
+       g_signal_connect (
+               client, "event",
+               G_CALLBACK (network_service_client_event_cb), service);
 
-               case CAMEL_NETWORK_SECURITY_METHOD_STARTTLS_ON_STANDARD_PORT:
-                       stream = camel_tcp_stream_ssl_new_raw (
-                               session, host,
-                               CAMEL_TCP_STREAM_SSL_ENABLE_TLS);
-                       break;
+       if (method == CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT)
+               g_socket_client_set_tls (client, TRUE);
 
-               case CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT:
-                       stream = camel_tcp_stream_ssl_new (
-                               session, host,
-                               CAMEL_TCP_STREAM_SSL_ENABLE_SSL2 |
-                               CAMEL_TCP_STREAM_SSL_ENABLE_SSL3);
-                       break;
+       connection = g_socket_client_connect (
+               client, connectable, cancellable, error);
 
-               default:
-                       g_return_val_if_reached (NULL);
+       if (connection != NULL) {
+               stream = camel_stream_new (G_IO_STREAM (connection));
+               g_object_unref (connection);
        }
 
+#if 0
        camel_session_get_socks_proxy (session, host, &socks_host, &socks_port);
 
        if (socks_host != NULL) {
@@ -339,22 +655,10 @@ network_service_connect_sync (CamelNetworkService *service,
                        socks_host, socks_port);
                g_free (socks_host);
        }
+#endif
 
-       status = camel_tcp_stream_connect (
-               CAMEL_TCP_STREAM (stream), host,
-               service_name, default_port, cancellable, error);
-
-       if (status == -1) {
-               /* Translators: The first '%s' is replaced with a host name, the second '%s' with service 
name or port number */
-               g_prefix_error (
-                       error, _("Could not connect to '%s:%s': "), host, service_name ? service_name : 
"???");
-               g_object_unref (stream);
-               stream = NULL;
-       }
-
-       g_free (host);
-
-       g_object_unref (session);
+       g_object_unref (connectable);
+       g_object_unref (client);
 
        return stream;
 }
@@ -630,3 +934,60 @@ camel_network_service_connect_sync (CamelNetworkService *service,
 
        return interface->connect_sync (service, cancellable, error);
 }
+
+/**
+ * camel_network_service_starttls:
+ * @service: a #CamelNetworkService
+ * @stream: a #CamelStream
+ * @error: return location for a #GError, or %NULL
+ *
+ * Replaces @stream's #CamelStream:base-stream with a #GTlsClientConnection
+ * wrapping #CamelStream:base-stream, which is assumed to communicate with
+ * the server identified by @service's #CamelNetworkService:connectable.
+ *
+ * This should typically be called after issuing a STARTTLS command to a
+ * server to initiate a Transport Layer Security handshake.
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ *
+ * Since: 3.12
+ **/
+gboolean
+camel_network_service_starttls (CamelNetworkService *service,
+                                CamelStream *stream,
+                                GError **error)
+{
+       GSocketConnectable *connectable;
+       GIOStream *tls_client_connection;
+       GIOStream *base_stream;
+       gboolean success = FALSE;
+
+       g_return_val_if_fail (CAMEL_IS_NETWORK_SERVICE (service), FALSE);
+       g_return_val_if_fail (CAMEL_IS_STREAM (stream), FALSE);
+
+       base_stream = camel_stream_ref_base_stream (stream);
+       g_return_val_if_fail (base_stream != NULL, FALSE);
+
+       connectable = camel_network_service_ref_connectable (service);
+       g_return_val_if_fail (connectable != NULL, FALSE);
+
+       tls_client_connection = g_tls_client_connection_new (
+               base_stream, connectable, error);
+
+       if (tls_client_connection != NULL) {
+               g_signal_connect (
+                       tls_client_connection, "accept-certificate",
+                       G_CALLBACK (network_service_accept_certificate_cb),
+                       service);
+
+               camel_stream_set_base_stream (stream, tls_client_connection);
+               g_object_unref (tls_client_connection);
+               success = TRUE;
+       }
+
+       g_object_unref (base_stream);
+       g_object_unref (connectable);
+
+       return success;
+}
+
diff --git a/camel/camel-network-service.h b/camel/camel-network-service.h
index cf0a39c..7d3479d 100644
--- a/camel/camel-network-service.h
+++ b/camel/camel-network-service.h
@@ -96,6 +96,10 @@ CamelStream *        camel_network_service_connect_sync
                                        (CamelNetworkService *service,
                                         GCancellable *cancellable,
                                         GError **error);
+gboolean       camel_network_service_starttls
+                                       (CamelNetworkService *service,
+                                        CamelStream *stream,
+                                        GError **error);
 
 G_END_DECLS
 
diff --git a/camel/providers/pop3/camel-pop3-store.c b/camel/providers/pop3/camel-pop3-store.c
index 342f299..b62414c 100644
--- a/camel/providers/pop3/camel-pop3-store.c
+++ b/camel/providers/pop3/camel-pop3-store.c
@@ -202,10 +202,9 @@ connect_to_server (CamelService *service,
        }
 
        /* Okay, now toggle SSL/TLS mode */
-       ret = camel_tcp_stream_ssl_enable_ssl (
-               CAMEL_TCP_STREAM_SSL (tcp_stream), cancellable, error);
-
-       if (ret == -1) {
+       success = camel_network_service_starttls (
+               CAMEL_NETWORK_SERVICE (service), tcp_stream, error);
+       if (!success) {
                g_prefix_error (
                        error,
                        _("Failed to connect to POP server %s in secure mode: "),
diff --git a/camel/providers/smtp/camel-smtp-transport.c b/camel/providers/smtp/camel-smtp-transport.c
index 96571ac..0c0483f 100644
--- a/camel/providers/smtp/camel-smtp-transport.c
+++ b/camel/providers/smtp/camel-smtp-transport.c
@@ -109,6 +109,7 @@ connect_to_server (CamelService *service,
        CamelNetworkSecurityMethod method;
        CamelSettings *settings;
        CamelStream *tcp_stream;
+       GIOStream *base_stream;
        gchar *respbuf = NULL;
        gboolean success = TRUE;
        gchar *host;
@@ -140,8 +141,10 @@ connect_to_server (CamelService *service,
        transport->connected = TRUE;
 
        /* get the localaddr - needed later by smtp_helo */
-       transport->localaddr = camel_tcp_stream_get_local_address (
-               CAMEL_TCP_STREAM (tcp_stream), &transport->localaddrlen);
+       base_stream = camel_stream_ref_base_stream (tcp_stream);
+       transport->local_address = g_socket_connection_get_local_address (
+               G_SOCKET_CONNECTION (base_stream), NULL);
+       g_object_unref (base_stream);
 
        transport->ostream = tcp_stream;
        transport->istream = camel_stream_buffer_new (
@@ -239,13 +242,13 @@ connect_to_server (CamelService *service,
        } while (*(respbuf+3) == '-'); /* if we got "220-" then loop again */
 
        /* Okay, now toggle SSL/TLS mode */
-       if (camel_tcp_stream_ssl_enable_ssl (
-               CAMEL_TCP_STREAM_SSL (tcp_stream), cancellable, error) == -1) {
+       success = camel_network_service_starttls (
+               CAMEL_NETWORK_SERVICE (service), tcp_stream, error);
+       if (!success) {
                g_prefix_error (
                        error,
                        _("Failed to connect to SMTP server %s in secure mode: "),
                        host);
-               success = FALSE;
                goto exit;
        }
 
@@ -459,18 +462,9 @@ smtp_transport_disconnect_sync (CamelService *service,
                transport->authtypes = NULL;
        }
 
-       if (transport->istream) {
-               g_object_unref (transport->istream);
-               transport->istream = NULL;
-       }
-
-       if (transport->ostream) {
-               g_object_unref (transport->ostream);
-               transport->ostream = NULL;
-       }
-
-       g_free (transport->localaddr);
-       transport->localaddr = NULL;
+       g_clear_object (&transport->istream);
+       g_clear_object (&transport->ostream);
+       g_clear_object (&transport->local_address);
 
        transport->connected = FALSE;
 
@@ -1147,9 +1141,10 @@ smtp_helo (CamelSmtpTransport *transport,
            GError **error)
 {
        gchar *name = NULL, *cmdbuf = NULL, *respbuf = NULL;
-       const gchar *token, *numeric = NULL;
-       struct sockaddr *addr;
-       socklen_t addrlen;
+       const gchar *token;
+       GResolver *resolver;
+       GInetAddress *address;
+       GError *local_error = NULL;
 
        /* these are flags that we set, so unset them in case we
         * are being called a second time (ie, after a STARTTLS) */
@@ -1163,27 +1158,40 @@ smtp_helo (CamelSmtpTransport *transport,
                transport->authtypes = NULL;
        }
 
-       camel_operation_push_message (cancellable, _("SMTP Greeting"));
+       resolver = g_resolver_get_default ();
+       address = g_inet_socket_address_get_address (
+               G_INET_SOCKET_ADDRESS (transport->local_address));
 
-       addr = transport->localaddr;
-       addrlen = transport->localaddrlen;
+       name = g_resolver_lookup_by_address (
+               resolver, address, cancellable, &local_error);
 
-       if (camel_getnameinfo (
-               addr, addrlen, &name, NULL,
-               NI_NUMERICHOST, cancellable, NULL) != 0) {
-               name = g_strdup ("localhost.localdomain");
-       } else {
-               if (addr->sa_family == AF_INET6)
-                       numeric = "IPv6:";
+       /* Sanity check. */
+       g_return_val_if_fail (
+               ((name != NULL) && (local_error == NULL)) ||
+               ((name == NULL) && (local_error != NULL)), FALSE);
+
+       if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+               return FALSE;
+
+       g_clear_error (&local_error);
+
+       if (name == NULL) {
+               GSocketFamily family;
+               gchar *string;
+
+               string = g_inet_address_to_string (address);
+               family = g_inet_address_get_family (address);
+               if (family == G_SOCKET_FAMILY_IPV6)
+                       name = g_strdup_printf ("[IPv6:%s]", string);
                else
-                       numeric = "";
+                       name = g_strdup_printf ("[%s]", string);
+               g_free (string);
        }
 
+       camel_operation_push_message (cancellable, _("SMTP Greeting"));
+
        token = (transport->flags & CAMEL_SMTP_TRANSPORT_IS_ESMTP) ? "EHLO" : "HELO";
-       if (numeric)
-               cmdbuf = g_strdup_printf ("%s [%s%s]\r\n", token, numeric, name);
-       else
-               cmdbuf = g_strdup_printf ("%s %s\r\n", token, name);
+       cmdbuf = g_strdup_printf ("%s %s\r\n", token, name);
        g_free (name);
 
        d (fprintf (stderr, "sending : %s", cmdbuf));
diff --git a/camel/providers/smtp/camel-smtp-transport.h b/camel/providers/smtp/camel-smtp-transport.h
index 2bfccc3..82d6823 100644
--- a/camel/providers/smtp/camel-smtp-transport.h
+++ b/camel/providers/smtp/camel-smtp-transport.h
@@ -67,13 +67,12 @@ struct _CamelSmtpTransport {
        CamelTransport parent;
 
        CamelStream *istream, *ostream;
+       GSocketAddress *local_address;
 
        guint32 flags;
 
        gboolean need_rset;
        gboolean connected;
-       struct sockaddr *localaddr;
-       socklen_t localaddrlen;
 
        GHashTable *authtypes;
 };
diff --git a/docs/reference/camel/camel-sections.txt b/docs/reference/camel/camel-sections.txt
index d3f21d1..b5c3b63 100644
--- a/docs/reference/camel/camel-sections.txt
+++ b/docs/reference/camel/camel-sections.txt
@@ -1814,6 +1814,7 @@ camel_network_service_ref_connectable
 camel_network_service_set_connectable
 camel_network_service_get_host_reachable
 camel_network_service_connect_sync
+camel_network_service_starttls
 <SUBSECTION Standard>
 CAMEL_NETWORK_SERVICE
 CAMEL_IS_NETWORK_SERVICE


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