[evolution-data-server] CamelNetworkService: Transition to GTcpConnection.
- From: Matthew Barnes <mbarnes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] CamelNetworkService: Transition to GTcpConnection.
- Date: Fri, 27 Sep 2013 01:14:21 +0000 (UTC)
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]