[libsoup/tls] SoupSocket: port to GSocketConnection/GTlsConnection



commit 693342ec53513b89bc8b26400bac1a3a6c90f79c
Author: Dan Winship <danw gnome org>
Date:   Thu Nov 12 19:44:27 2009 -0500

    SoupSocket: port to GSocketConnection/GTlsConnection
    
    and remove libsoup's built-in TLS support, which is no longer needed

 README                    |    2 +-
 configure.ac              |   36 ---
 libsoup-2.4.pc.in         |    2 +-
 libsoup/Makefile.am       |   10 +-
 libsoup/soup-gnutls.c     |  705 ---------------------------------------------
 libsoup/soup-message-io.c |    2 +-
 libsoup/soup-nossl.c      |   91 ------
 libsoup/soup-socket.c     |  483 +++++++++++--------------------
 libsoup/soup-ssl.c        |  134 +++++++++
 libsoup/soup-ssl.h        |   10 +-
 libsoup/soup-status.c     |    4 -
 tests/Makefile.am         |    9 +-
 tests/proxy-test.c        |    6 -
 tests/ssl-test.c          |  357 -----------------------
 tests/timeout-test.c      |    2 -
 15 files changed, 320 insertions(+), 1533 deletions(-)
---
diff --git a/README b/README
index bf3b70a..8baf635 100644
--- a/README
+++ b/README
@@ -4,7 +4,7 @@ and the glib main loop, to integrate well with GNOME applications.
 Features:
   * Both asynchronous (GMainLoop and callback-based) and synchronous APIs
   * Automatically caches connections
-  * SSL Support using GnuTLS
+  * SSL support
   * Proxy support, including authentication and SSL tunneling
   * Client support for Digest, NTLM, and Basic authentication
   * Server support for Digest and Basic authentication
diff --git a/configure.ac b/configure.ac
index 282af5b..874e203 100644
--- a/configure.ac
+++ b/configure.ac
@@ -106,39 +106,6 @@ AC_CHECK_FUNCS(gmtime_r)
 AC_CHECK_FUNCS(mmap)
 AC_CHECK_FUNC(socket, , AC_CHECK_LIB(socket, socket))
 
-dnl **********************************
-dnl *** SSL Library check (GnuTLS) ***
-dnl **********************************
-
-AC_ARG_ENABLE(ssl, 
-	      AS_HELP_STRING([--disable-ssl], [Disable SSL/TLS support (not recommended)]),,
-	      enable_ssl=auto)
-
-have_ssl=no
-if test "$enable_ssl" != "no"; then
-	PKG_CHECK_MODULES(LIBGNUTLS, gnutls >= 2.1.7,
-		[AM_PATH_LIBGCRYPT([], have_ssl=yes, have_ssl=no)], have_ssl=no)
-fi
-if test "$have_ssl" = "yes"; then
-	AC_DEFINE(HAVE_SSL, 1, [Defined if you have SSL support])
-	SSL_REQUIREMENT="gnutls"
-else
-	if test "$enable_ssl" = "no"; then
-		AC_MSG_WARN(Disabling SSL support);
-	else
-		AC_MSG_ERROR([Could not configure SSL support.
-Pass "--disable-ssl" if you really want to build without SSL support]);
-	fi
-fi
-
-AC_SUBST(LIBGNUTLS_CFLAGS)
-AC_SUBST(LIBGNUTLS_LIBS)
-AC_SUBST(SSL_REQUIREMENT)
-
-dnl This is not supposed to be conditional, but...
-AM_CONDITIONAL(HAVE_SSL, test $enable_ssl != no)
-
-
 dnl *********************
 dnl *** GNOME support ***
 dnl *********************
@@ -256,9 +223,6 @@ dnl *** Stuff for regression tests
 dnl ******************************
 AC_MSG_NOTICE([checking for programs needed for regression tests])
 MISSING_REGRESSION_TEST_PACKAGES=""
-if test $have_ssl = "no"; then
-    MISSING_REGRESSION_TEST_PACKAGES=" gnutls"
-fi
 
 AC_ARG_WITH(apache-httpd,
 	    AS_HELP_STRING([--with-apache-httpd], [Path to apache httpd (for tests)]),
diff --git a/libsoup-2.4.pc.in b/libsoup-2.4.pc.in
index 7df813c..1394939 100644
--- a/libsoup-2.4.pc.in
+++ b/libsoup-2.4.pc.in
@@ -7,6 +7,6 @@ Name: libsoup
 Description: a glib-based HTTP library
 Version: @VERSION@
 Requires: glib-2.0 gobject-2.0 gio-2.0
-Requires.private: libxml-2.0 @SSL_REQUIREMENT@
+Requires.private: libxml-2.0
 Libs: -L${libdir} -lsoup-2.4
 Cflags: -I${includedir}/libsoup-2.4
diff --git a/libsoup/Makefile.am b/libsoup/Makefile.am
index d602916..f40233f 100644
--- a/libsoup/Makefile.am
+++ b/libsoup/Makefile.am
@@ -14,9 +14,7 @@ INCLUDES = 				\
 	$(GCONF_CFLAGS)			\
 	$(LIBPROXY_CFLAGS)		\
 	$(SQLITE_CFLAGS)		\
-	$(GNOME_KEYRING_CFLAGS)		\
-	$(LIBGCRYPT_CFLAGS)		\
-	$(LIBGNUTLS_CFLAGS)
+	$(GNOME_KEYRING_CFLAGS)
 
 MARSHAL_GENERATED = soup-marshal.c soup-marshal.h
 MKENUMS_GENERATED = soup-enum-types.c soup-enum-types.h
@@ -100,9 +98,6 @@ libsoup_2_4_la_LIBADD =			\
 	$(GLIB_LIBS)			\
 	$(XML_LIBS)			\
 	-lz				\
-	$(LIBGNUTLS_LIBS_STATIC)	\
-	$(LIBGNUTLS_LIBS)		\
-	$(LIBGCRYPT_LIBS)		\
 	$(LIBWS2_32)
 
 libsoup_2_4_la_SOURCES =		\
@@ -135,7 +130,6 @@ libsoup_2_4_la_SOURCES =		\
 	soup-cookie-jar-text.c		\
 	soup-date.c			\
 	soup-form.c			\
-	soup-gnutls.c			\
 	soup-headers.c			\
 	soup-logger.c			\
 	soup-message.c			\
@@ -150,7 +144,6 @@ libsoup_2_4_la_SOURCES =		\
 	soup-method.c     		\
 	soup-misc.c     		\
 	soup-multipart.c	     	\
-	soup-nossl.c     		\
 	soup-password-manager.c		\
 	soup-path-map.h     		\
 	soup-path-map.c     		\
@@ -166,6 +159,7 @@ libsoup_2_4_la_SOURCES =		\
 	soup-session-sync.c		\
 	soup-socket.c			\
 	soup-ssl.h			\
+	soup-ssl.c	     		\
 	soup-status.c			\
 	soup-uri.c			\
 	soup-value-utils.c		\
diff --git a/libsoup/soup-message-io.c b/libsoup/soup-message-io.c
index 525ea71..e824b00 100644
--- a/libsoup/soup-message-io.c
+++ b/libsoup/soup-message-io.c
@@ -185,7 +185,7 @@ io_error (SoupSocket *sock, SoupMessage *msg, GError *error)
 	SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
 	SoupMessageIOData *io = priv->io_data;
 
-	if (error && error->domain == SOUP_SSL_ERROR) {
+	if (error && error->domain == G_TLS_ERROR) {
 		soup_message_set_status_full (msg,
 					      SOUP_STATUS_SSL_FAILED,
 					      error->message);
diff --git a/libsoup/soup-socket.c b/libsoup/soup-socket.c
index c9c2b92..cbc0a3e 100644
--- a/libsoup/soup-socket.c
+++ b/libsoup/soup-socket.c
@@ -22,9 +22,6 @@
 #include "soup-misc.h"
 #include "soup-ssl.h"
 
-#include <sys/time.h>
-#include <sys/types.h>
-
 /**
  * SECTION:soup-socket
  * @short_description: A network socket
@@ -67,12 +64,13 @@ enum {
 typedef struct {
 	int sockfd;
 	SoupAddress *local_addr, *remote_addr;
-	GIOChannel *iochannel;
-	GSocketConnection *conn;
+	GIOStream *conn;
+	GSocket *gsock;
+	GPollableInputStream *istream;
+	GPollableOutputStream *ostream;
 
 	guint non_blocking:1;
 	guint is_server:1;
-	guint timed_out:1;
 	guint ssl_strict:1;
 	guint trusted_certificate:1;
 	guint clean_dispose:1;
@@ -81,7 +79,6 @@ typedef struct {
 	GMainContext   *async_context;
 	GSource        *watch_src;
 	GSource        *read_src, *write_src;
-	GSource        *read_timeout, *write_timeout;
 	GByteArray     *read_buf;
 
 	GMutex *iolock, *addrlock;
@@ -97,11 +94,9 @@ static void get_property (GObject *object, guint prop_id,
 			  GValue *value, GParamSpec *pspec);
 
 #ifdef G_OS_WIN32
-#define SOUP_IS_SOCKET_ERROR(status) ((status) == SOCKET_ERROR)
 #define SOUP_IS_INVALID_SOCKET(socket) ((socket) == INVALID_SOCKET)
 #define SHUT_RDWR SD_BOTH
 #else
-#define SOUP_IS_SOCKET_ERROR(status) ((status) == -1)
 #define SOUP_IS_INVALID_SOCKET(socket) ((socket) < 0)
 #endif
 
@@ -120,13 +115,13 @@ soup_socket_init (SoupSocket *sock)
 static void
 disconnect_internal (SoupSocketPrivate *priv)
 {
-	if (priv->iochannel) {
-		g_io_channel_unref (priv->iochannel);
-		priv->iochannel = NULL;
-	}
 	if (priv->conn) {
 		g_object_unref (priv->conn);
 		priv->conn = NULL;
+		g_object_unref (priv->gsock);
+		priv->gsock = NULL;
+		priv->istream = NULL;
+		priv->ostream = NULL;
 	}
 	priv->sockfd = -1;
 
@@ -138,14 +133,6 @@ disconnect_internal (SoupSocketPrivate *priv)
 		g_source_destroy (priv->write_src);
 		priv->write_src = NULL;
 	}
-	if (priv->read_timeout) {
-		g_source_destroy (priv->read_timeout);
-		priv->read_timeout = NULL;
-	}
-	if (priv->write_timeout) {
-		g_source_destroy (priv->write_timeout);
-		priv->write_timeout = NULL;
-	}
 }
 
 static void
@@ -158,7 +145,7 @@ finalize (GObject *object)
 			g_warning ("Disposing socket %p during connect", object);
 		g_object_unref (priv->connect_cancel);
 	}
-	if (priv->iochannel) {
+	if (priv->conn) {
 		if (priv->clean_dispose)
 			g_warning ("Disposing socket %p while still connected", object);
 		disconnect_internal (priv);
@@ -424,93 +411,28 @@ soup_socket_class_init (SoupSocketClass *socket_class)
 
 
 static void
-set_nonblocking (SoupSocketPrivate *priv)
-{
-#ifndef G_OS_WIN32
-	int flags;
-#else
-	u_long val;
-#endif
-
-	if (priv->sockfd == -1)
-		return;
-
-#ifndef G_OS_WIN32
-	flags = fcntl (priv->sockfd, F_GETFL, 0);
-	if (flags != -1) {
-		if (priv->non_blocking)
-			flags |= O_NONBLOCK;
-		else
-			flags &= ~O_NONBLOCK;
-		fcntl (priv->sockfd, F_SETFL, flags);
-	}
-#else
-	val = priv->non_blocking ? 1 : 0;
-	ioctlsocket (priv->sockfd, FIONBIO, &val);
-#endif
-}
-
-static void
 set_fdflags (SoupSocketPrivate *priv)
 {
 	int opt;
-#ifndef G_OS_WIN32
-	struct timeval timeout;
-	int flags;
-#endif
 
 	if (priv->sockfd == -1)
 		return;
 
-	set_nonblocking (priv);
-
-#ifndef G_OS_WIN32
-	flags = fcntl (priv->sockfd, F_GETFD, 0);
-	if (flags != -1) {
-		flags |= FD_CLOEXEC;
-		fcntl (priv->sockfd, F_SETFD, flags);
-	}
-#endif
-
 	opt = 1;
 	setsockopt (priv->sockfd, IPPROTO_TCP,
 		    TCP_NODELAY, (void *) &opt, sizeof (opt));
-	setsockopt (priv->sockfd, SOL_SOCKET,
-		    SO_REUSEADDR, (void *) &opt, sizeof (opt));
-
-#ifndef G_OS_WIN32
-	timeout.tv_sec = priv->timeout;
-	timeout.tv_usec = 0;
-	setsockopt (priv->sockfd, SOL_SOCKET,
-		    SO_RCVTIMEO, (void *) &timeout, sizeof (timeout));
-
-	timeout.tv_sec = priv->timeout;
-	timeout.tv_usec = 0;
-	setsockopt (priv->sockfd, SOL_SOCKET,
-		    SO_SNDTIMEO, (void *) &timeout, sizeof (timeout));
-#else
-	if (priv->timeout < G_MAXINT / 1000)
-		opt = priv->timeout * 1000;
-	else
-		opt = 0;
 
-	setsockopt (priv->sockfd, SOL_SOCKET,
-		    SO_RCVTIMEO, (void *) &opt, sizeof (opt));
-	
-	setsockopt (priv->sockfd, SOL_SOCKET,
-		    SO_SNDTIMEO, (void *) &opt, sizeof (opt));
-#endif
+	if (!priv->conn) {
+		priv->gsock = g_socket_new_from_fd (priv->sockfd, NULL);
+		priv->conn = (GIOStream *)g_socket_connection_factory_create_connection (priv->gsock);
+	}
 
-#ifndef G_OS_WIN32
-	priv->iochannel =
-		g_io_channel_unix_new (priv->sockfd);
-#else
-	priv->iochannel =
-		g_io_channel_win32_new_socket (priv->sockfd);
-#endif
-	g_io_channel_set_close_on_unref (priv->iochannel, priv->conn == NULL);
-	g_io_channel_set_encoding (priv->iochannel, NULL, NULL);
-	g_io_channel_set_buffered (priv->iochannel, FALSE);
+	if (!priv->istream) {
+		priv->istream = G_POLLABLE_INPUT_STREAM (g_io_stream_get_input_stream (priv->conn));
+		priv->ostream = G_POLLABLE_OUTPUT_STREAM (g_io_stream_get_output_stream (priv->conn));
+	}
+
+	g_socket_set_timeout (priv->gsock, priv->timeout);
 }
 
 static void
@@ -528,7 +450,6 @@ set_property (GObject *object, guint prop_id,
 		break;
 	case PROP_NON_BLOCKING:
 		priv->non_blocking = g_value_get_boolean (value);
-		set_nonblocking (priv);
 		break;
 	case PROP_SSL_CREDENTIALS:
 		priv->ssl_creds = g_value_get_pointer (value);
@@ -546,6 +467,8 @@ set_property (GObject *object, guint prop_id,
 		break;
 	case PROP_TIMEOUT:
 		priv->timeout = g_value_get_uint (value);
+		if (priv->conn)
+			g_socket_set_timeout (priv->gsock, priv->timeout);
 		break;
 	case PROP_CLEAN_DISPOSE:
 		priv->clean_dispose = g_value_get_boolean (value);
@@ -624,7 +547,6 @@ static guint
 socket_connected (SoupSocket *sock, GSocketConnection *conn, GError *error)
 {
 	SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
-	GSocket *gsock;
 
 	g_object_unref (priv->connect_cancel);
 	priv->connect_cancel = NULL;
@@ -639,13 +561,9 @@ socket_connected (SoupSocket *sock, GSocketConnection *conn, GError *error)
 		}
 	}
 
-	/* We keep the GSocketConnection around because its GSocket
-	 * will close the fd when it's destroyed.
-	 */
-	priv->conn = conn;
-
-	gsock = g_socket_connection_get_socket (conn);
-	priv->sockfd = g_socket_get_fd (gsock);
+	priv->conn = (GIOStream *)conn;
+	priv->gsock = g_object_ref (g_socket_connection_get_socket (conn));
+	priv->sockfd = g_socket_get_fd (priv->gsock);
 	set_fdflags (priv);
 
 	return SOUP_STATUS_OK;
@@ -797,23 +715,38 @@ soup_socket_get_fd (SoupSocket *sock)
 	return SOUP_SOCKET_GET_PRIVATE (sock)->sockfd;
 }
 
+static GSource *
+soup_socket_create_watch (SoupSocketPrivate *priv, GIOCondition cond,
+			  GPollableSourceFunc callback, gpointer user_data,
+			  GCancellable *cancellable)
+{
+	GSource *watch;
+
+	if (cond == G_IO_IN)
+		watch = g_pollable_input_stream_create_source (priv->istream, cancellable);
+	else
+		watch = g_pollable_output_stream_create_source (priv->ostream, cancellable);
+	g_source_set_callback (watch, (GSourceFunc)callback, user_data, NULL);
+	g_source_attach (watch, priv->async_context);
+	g_source_unref (watch);
+
+	return watch;
+}
+
 static gboolean
-listen_watch (GIOChannel* iochannel, GIOCondition condition, gpointer data)
+listen_watch (GObject *pollable, gpointer data)
 {
 	SoupSocket *sock = data, *new;
 	SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock), *new_priv;
 	struct sockaddr_storage sa;
 	int sa_len, sockfd;
 
-	if (condition & (G_IO_HUP | G_IO_ERR)) {
-		priv->watch_src = NULL;
-		return FALSE;
-	}
+	/* Using g_socket_accept() here would require more rewriting... */
 
 	sa_len = sizeof (sa);
 	sockfd = accept (priv->sockfd, (struct sockaddr *)&sa, (void *)&sa_len);
 	if (SOUP_IS_INVALID_SOCKET (sockfd))
-		return TRUE;
+		return FALSE;
 
 	new = g_object_new (SOUP_TYPE_SOCKET, NULL);
 	new_priv = SOUP_SOCKET_GET_PRIVATE (new);
@@ -822,7 +755,8 @@ listen_watch (GIOChannel* iochannel, GIOCondition condition, gpointer data)
 		new_priv->async_context = g_main_context_ref (priv->async_context);
 	new_priv->non_blocking = priv->non_blocking;
 	new_priv->is_server = TRUE;
-	new_priv->ssl_creds = priv->ssl_creds;
+	if (priv->ssl_creds)
+		new_priv->ssl_creds = priv->ssl_creds;
 	set_fdflags (new_priv);
 
 	new_priv->remote_addr = soup_address_new_from_sockaddr ((struct sockaddr *)&sa, sa_len);
@@ -855,8 +789,6 @@ soup_socket_listen (SoupSocket *sock)
 
 {
 	SoupSocketPrivate *priv;
-	struct sockaddr_storage sa;
-	int sa_len;
 	GSocketAddress *addr;
 
 	g_return_val_if_fail (SOUP_IS_SOCKET (sock), FALSE);
@@ -875,39 +807,49 @@ soup_socket_listen (SoupSocket *sock)
 	addr = soup_address_get_gsockaddr (priv->local_addr);
 	g_return_val_if_fail (addr != NULL, FALSE);
 
-	sa_len = g_socket_address_get_native_size (addr);
-	g_socket_address_to_native (addr, &sa, sizeof (sa), NULL);
-	g_object_unref (addr);
-
-	priv->sockfd = socket (sa.ss_family, SOCK_STREAM, 0);
+	priv->sockfd = socket (g_socket_address_get_family (addr),
+			       SOCK_STREAM, 0);
 	if (SOUP_IS_INVALID_SOCKET (priv->sockfd))
 		goto cant_listen;
 	set_fdflags (priv);
 
 	/* Bind */
-	if (bind (priv->sockfd, (struct sockaddr *)&sa, sa_len) != 0)
+	if (!g_socket_bind (priv->gsock, addr, TRUE, NULL))
 		goto cant_listen;
 	/* Force local_addr to be re-resolved now */
 	g_object_unref (priv->local_addr);
 	priv->local_addr = NULL;
 
 	/* Listen */
-	if (listen (priv->sockfd, 10) != 0)
+	if (!g_socket_listen (priv->gsock, NULL))
 		goto cant_listen;
 
-	priv->watch_src = soup_add_io_watch (priv->async_context,
-					     priv->iochannel,
-					     G_IO_IN | G_IO_ERR | G_IO_HUP,
-					     listen_watch, sock);
+	priv->watch_src = soup_socket_create_watch (priv, G_IO_IN,
+						    listen_watch, sock,
+						    NULL);
 	return TRUE;
 
  cant_listen:
-	if (priv->iochannel)
+	if (priv->conn)
 		disconnect_internal (priv);
 
 	return FALSE;
 }
 
+static gboolean
+soup_socket_accept_certificate (GTlsConnection *conn, GTlsCertificate *cert,
+				GTlsValidationFlags errors, gpointer sock)
+{
+	SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+
+	if (!priv->ssl_strict) {
+		priv->trusted_certificate = FALSE;
+		return TRUE;
+	}
+
+	return (errors & soup_ssl_credentials_get_validation_flags (priv->ssl_creds)) == 0;
+}
+
 /**
  * soup_socket_start_ssl:
  * @sock: the socket
@@ -941,27 +883,55 @@ soup_socket_start_proxy_ssl (SoupSocket *sock, const char *ssl_host,
 			     GCancellable *cancellable)
 {
 	SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
-	GIOChannel *ssl_chan;
-	GIOChannel *real_chan;
 
-	real_chan = priv->iochannel;
-	ssl_chan = soup_ssl_wrap_iochannel (
-		real_chan, priv->non_blocking, priv->is_server ?
-		SOUP_SSL_TYPE_SERVER : SOUP_SSL_TYPE_CLIENT,
-		ssl_host, priv->ssl_creds);
-
-	if (!ssl_chan)
+	if (G_IS_TLS_CONNECTION (priv->conn))
+		return TRUE;
+	if (!priv->ssl_creds)
 		return FALSE;
 
-	/* This is optimistic, we will set this to false if we get a
-	 * cert error from one of the I/O calls
-	 */
-	if (priv->ssl_creds)
+	if (!priv->is_server) {
+		GTlsClientConnection *conn;
+		GSocketConnectable *identity;
+
+		identity = g_network_address_new (ssl_host, 0);
+		conn = g_tls_client_connection_new (NULL,
+						    "base-io-stream", priv->conn,
+						    "server-identity", identity,
+						    "ca-list", soup_ssl_credentials_get_ca_list (priv->ssl_creds),
+						    NULL);
+		g_object_unref (identity);
+
+		if (!conn)
+			return FALSE;
+
+		g_object_unref (priv->conn);
+		priv->conn = G_IO_STREAM (conn);
+
+		/* FIXME: need a signal to let us know a successful
+		 * handshake happened.
+		 */
 		priv->trusted_certificate = TRUE;
 
-	priv->iochannel = ssl_chan;
-	g_io_channel_unref (real_chan);
+		g_signal_connect (conn, "accept-certificate",
+				  G_CALLBACK (soup_socket_accept_certificate),
+				  sock);
+	} else {
+		GTlsServerConnection *conn;
+
+		conn = g_tls_server_connection_new (NULL,
+						    "base-io-stream", priv->conn,
+						    "certificate", soup_ssl_credentials_get_certificate (priv->ssl_creds),
+						    NULL);
 
+		if (!conn)
+			return FALSE;
+
+		g_object_unref (priv->conn);
+		priv->conn = G_IO_STREAM (conn);
+	}
+
+	priv->istream = G_POLLABLE_INPUT_STREAM (g_io_stream_get_input_stream (priv->conn));
+	priv->ostream = G_POLLABLE_OUTPUT_STREAM (g_io_stream_get_output_stream (priv->conn));
 	return TRUE;
 }
 	
@@ -1003,7 +973,7 @@ soup_socket_disconnect (SoupSocket *sock)
 		g_cancellable_cancel (priv->connect_cancel);
 		return;
 	} else if (g_mutex_trylock (priv->iolock)) {
-		if (priv->iochannel)
+		if (priv->conn)
 			disconnect_internal (priv);
 		else
 			already_disconnected = TRUE;
@@ -1012,7 +982,7 @@ soup_socket_disconnect (SoupSocket *sock)
 		int sockfd;
 
 		/* Another thread is currently doing IO, so
-		 * we can't close the iochannel. So just shutdown
+		 * we can't close the socket. So just shutdown
 		 * the file descriptor to force the I/O to fail.
 		 * (It will actually be closed when the socket is
 		 * destroyed.)
@@ -1061,7 +1031,7 @@ soup_socket_is_connected (SoupSocket *sock)
 	g_return_val_if_fail (SOUP_IS_SOCKET (sock), FALSE);
 	priv = SOUP_SOCKET_GET_PRIVATE (sock);
 
-	return priv->iochannel != NULL;
+	return priv->conn != NULL;
 }
 
 /**
@@ -1126,128 +1096,63 @@ soup_socket_get_remote_address (SoupSocket *sock)
 
 
 static gboolean
-socket_timeout (gpointer sock)
-{
-	SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
-	gboolean readable = FALSE, writable = FALSE;
-
-	priv->timed_out = TRUE;
-	if (priv->read_timeout) {
-		priv->read_timeout = NULL;
-		readable = TRUE;
-	}
-	if (priv->write_timeout) {
-		priv->write_timeout = NULL;
-		writable = TRUE;
-	}
-
-	if (readable)
-		g_signal_emit (sock, signals[READABLE], 0);
-	if (writable)
-		g_signal_emit (sock, signals[WRITABLE], 0);
-
-	return FALSE;
-}
-
-static gboolean
-socket_read_watch (GIOChannel *chan, GIOCondition cond, gpointer user_data)
+socket_read_watch (GObject *pollable, gpointer user_data)
 {
 	SoupSocket *sock = user_data;
 	SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
 
 	priv->read_src = NULL;
-	if (priv->read_timeout) {
-		g_source_destroy (priv->read_timeout);
-		priv->read_timeout = NULL;
-	}
-
-	if (cond & (G_IO_ERR | G_IO_HUP))
-		soup_socket_disconnect (sock);
-	else
-		g_signal_emit (sock, signals[READABLE], 0);
-
+	g_signal_emit (sock, signals[READABLE], 0);
 	return FALSE;
 }
 
 static SoupSocketIOStatus
 read_from_network (SoupSocket *sock, gpointer buffer, gsize len,
-		   gsize *nread, GError **error)
+		   gsize *nread, GCancellable *cancellable, GError **error)
 {
 	SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
-	GIOStatus status;
-	GIOCondition cond = G_IO_IN;
 	GError *my_err = NULL;
+	gssize my_nread;
 
 	*nread = 0;
 
-	if (!priv->iochannel)
+	if (!priv->conn)
 		return SOUP_SOCKET_EOF;
 
-	if (priv->timed_out) {
-		g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
-				     "Timed out");
-		return SOUP_SOCKET_ERROR;
-	}
-
-again:
-	status = g_io_channel_read_chars (priv->iochannel,
-					  buffer, len, nread, &my_err);
-	if (my_err) {
-		if (g_error_matches (my_err, SOUP_SSL_ERROR,
-				     SOUP_SSL_ERROR_CERTIFICATE) &&
-		    !priv->ssl_strict) {
-			priv->trusted_certificate = FALSE;
-			g_clear_error (&my_err);
-			goto again;
-		}
-
-		if (g_error_matches (my_err, SOUP_SSL_ERROR,
-				     SOUP_SSL_ERROR_HANDSHAKE_NEEDS_WRITE))
-			cond = G_IO_OUT;
-		g_propagate_error (error, my_err);
+	if (!priv->non_blocking) {
+		my_nread = g_input_stream_read (G_INPUT_STREAM (priv->istream),
+						buffer, len,
+						cancellable, &my_err);
+	} else {
+		my_nread = g_pollable_input_stream_read_nonblocking (
+			priv->istream, buffer, len,
+			cancellable, &my_err);
 	}
 
-	switch (status) {
-	case G_IO_STATUS_NORMAL:
-	case G_IO_STATUS_AGAIN:
-		if (*nread > 0) {
-			g_clear_error (error);
-			return SOUP_SOCKET_OK;
-		}
-
-		/* If the socket is sync and we get EAGAIN, then it is
-		 * a socket timeout and should be treated as an error
-		 * condition.
-		 */
-		if (!priv->non_blocking) {
-			g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
-					     "Timed out");
-			return SOUP_SOCKET_ERROR;
-		}
-
+	if (my_nread > 0) {
+		g_clear_error (&my_err);
+		*nread = my_nread;
+		return SOUP_SOCKET_OK;
+	} else if (my_nread == 0) {
+		g_clear_error (&my_err);
+		*nread = my_nread;
+		return SOUP_SOCKET_EOF;
+	} else if (g_error_matches (my_err, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
+		g_clear_error (&my_err);
 		if (!priv->read_src) {
 			priv->read_src =
-				soup_add_io_watch (priv->async_context,
-						   priv->iochannel,
-						   cond | G_IO_HUP | G_IO_ERR,
-						   socket_read_watch, sock);
-			if (priv->timeout) {
-				priv->read_timeout =
-					soup_add_timeout (priv->async_context,
-							  priv->timeout * 1000,
-							  socket_timeout, sock);
-			}
+				soup_socket_create_watch (priv, G_IO_IN,
+							  socket_read_watch, sock,
+							  cancellable);
 		}
-		g_clear_error (error);
 		return SOUP_SOCKET_WOULD_BLOCK;
-
-	case G_IO_STATUS_EOF:
-		g_clear_error (error);
-		return SOUP_SOCKET_EOF;
-
-	default:
-		return SOUP_SOCKET_ERROR;
+	} else if (g_error_matches (my_err, G_TLS_ERROR, G_TLS_ERROR_HANDSHAKE)) {
+		my_err->domain = SOUP_SSL_ERROR;
+		my_err->code = SOUP_SSL_ERROR_CERTIFICATE;
 	}
+
+	g_propagate_error (error, my_err);
+	return SOUP_SOCKET_ERROR;
 }
 
 static SoupSocketIOStatus
@@ -1324,7 +1229,7 @@ soup_socket_read (SoupSocket *sock, gpointer buffer, gsize len,
 	if (priv->read_buf)
 		status = read_from_buf (sock, buffer, len, nread);
 	else
-		status = read_from_network (sock, buffer, len, nread, error);
+		status = read_from_network (sock, buffer, len, nread, cancellable, error);
 	g_mutex_unlock (priv->iolock);
 
 	return status;
@@ -1389,7 +1294,7 @@ soup_socket_read_until (SoupSocket *sock, gpointer buffer, gsize len,
 		g_byte_array_set_size (read_buf, len);
 		status = read_from_network (sock,
 					    read_buf->data + prev_len,
-					    len - prev_len, nread, error);
+					    len - prev_len, nread, cancellable, error);
 		read_buf->len = prev_len + *nread;
 
 		if (status != SOUP_SOCKET_OK) {
@@ -1420,22 +1325,13 @@ soup_socket_read_until (SoupSocket *sock, gpointer buffer, gsize len,
 }
 
 static gboolean
-socket_write_watch (GIOChannel *chan, GIOCondition cond, gpointer user_data)
+socket_write_watch (GObject *pollable, gpointer user_data)
 {
 	SoupSocket *sock = user_data;
 	SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
 
 	priv->write_src = NULL;
-	if (priv->write_timeout) {
-		g_source_destroy (priv->write_timeout);
-		priv->write_timeout = NULL;
-	}
-
-	if (cond & (G_IO_ERR | G_IO_HUP))
-		soup_socket_disconnect (sock);
-	else
-		g_signal_emit (sock, signals[WRITABLE], 0);
-
+	g_signal_emit (sock, signals[WRITABLE], 0);
 	return FALSE;
 }
 
@@ -1471,9 +1367,8 @@ soup_socket_write (SoupSocket *sock, gconstpointer buffer,
 		   GCancellable *cancellable, GError **error)
 {
 	SoupSocketPrivate *priv;
-	GIOStatus status;
-	GIOCondition cond = G_IO_OUT;
 	GError *my_err = NULL;
+	gssize my_nwrote;
 
 	g_return_val_if_fail (SOUP_IS_SOCKET (sock), SOUP_SOCKET_ERROR);
 	g_return_val_if_fail (nwrote != NULL, SOUP_SOCKET_ERROR);
@@ -1482,72 +1377,46 @@ soup_socket_write (SoupSocket *sock, gconstpointer buffer,
 
 	g_mutex_lock (priv->iolock);
 
-	if (!priv->iochannel) {
+	if (!priv->conn) {
 		g_mutex_unlock (priv->iolock);
 		return SOUP_SOCKET_EOF;
 	}
-	if (priv->timed_out) {
-		g_mutex_unlock (priv->iolock);
-		g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
-				     "Timed out");
-		return SOUP_SOCKET_ERROR;
-	}
 	if (priv->write_src) {
 		g_mutex_unlock (priv->iolock);
 		return SOUP_SOCKET_WOULD_BLOCK;
 	}
 
-again:
-	status = g_io_channel_write_chars (priv->iochannel,
-					   buffer, len, nwrote, &my_err);
-	if (my_err) {
-		if (g_error_matches (my_err, SOUP_SSL_ERROR,
-				     SOUP_SSL_ERROR_CERTIFICATE) &&
-		    !priv->ssl_strict) {
-			priv->trusted_certificate = FALSE;
-			g_clear_error (&my_err);
-			goto again;
-		}
-
-		if (g_error_matches (my_err, SOUP_SSL_ERROR,
-				     SOUP_SSL_ERROR_HANDSHAKE_NEEDS_READ))
-			cond = G_IO_IN;
-		g_propagate_error (error, my_err);
-	}
-
-	/* If the socket is sync and we get EAGAIN, then it is a
-	 * socket timeout and should be treated as an error condition.
-	 */
-	if (!priv->non_blocking && status == G_IO_STATUS_AGAIN) {
-		g_mutex_unlock (priv->iolock);
-		g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
-				     "Timed out");
-		return SOUP_SOCKET_ERROR;
+	if (!priv->non_blocking) {
+		my_nwrote = g_output_stream_write (G_OUTPUT_STREAM (priv->ostream),
+						   buffer, len,
+						   cancellable, &my_err);
+	} else {
+		my_nwrote = g_pollable_output_stream_write_nonblocking (
+			priv->ostream, buffer, len,
+			cancellable, &my_err);
 	}
 
-	if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN) {
+	if (my_nwrote > 0) {
 		g_mutex_unlock (priv->iolock);
-		return SOUP_SOCKET_ERROR;
+		g_clear_error (&my_err);
+		*nwrote = my_nwrote;
+		return SOUP_SOCKET_OK;
 	}
 
-	g_clear_error (error);
-
-	if (*nwrote) {
+	if (g_error_matches (my_err, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
 		g_mutex_unlock (priv->iolock);
-		return SOUP_SOCKET_OK;
-	}
 
-	priv->write_src =
-		soup_add_io_watch (priv->async_context,
-				   priv->iochannel,
-				   cond | G_IO_HUP | G_IO_ERR, 
-				   socket_write_watch, sock);
-	if (priv->timeout) {
-		priv->write_timeout = soup_add_timeout (priv->async_context,
-							priv->timeout * 1000,
-							socket_timeout, sock);
+		priv->write_src =
+			soup_socket_create_watch (priv,
+						  G_IO_OUT,
+						  socket_write_watch, sock, cancellable);
+		return SOUP_SOCKET_WOULD_BLOCK;
+	} else if (g_error_matches (my_err, G_TLS_ERROR, G_TLS_ERROR_HANDSHAKE)) {
+		my_err->domain = SOUP_SSL_ERROR;
+		my_err->code = SOUP_SSL_ERROR_CERTIFICATE;
 	}
 
 	g_mutex_unlock (priv->iolock);
-	return SOUP_SOCKET_WOULD_BLOCK;
+	g_propagate_error (error, my_err);
+	return SOUP_SOCKET_ERROR;
 }
diff --git a/libsoup/soup-ssl.c b/libsoup/soup-ssl.c
new file mode 100644
index 0000000..96a1d97
--- /dev/null
+++ b/libsoup/soup-ssl.c
@@ -0,0 +1,134 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-ssl.c: temporary ssl integration
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gio/gio.h>
+
+#include "soup-ssl.h"
+#include "soup-misc.h"
+
+const gboolean soup_ssl_supported = TRUE;
+
+struct SoupSSLCredentials {
+	GList *ca_list;
+	GTlsValidationFlags validation_flags;
+	GTlsCertificate *certificate;
+};
+
+SoupSSLCredentials *
+soup_ssl_get_client_credentials (const char *ca_file)
+{
+	SoupSSLCredentials *creds;
+
+	creds = g_slice_new0 (SoupSSLCredentials);
+
+	if (ca_file) {
+		GError *error = NULL;
+
+		creds->ca_list = g_tls_certificate_list_new_from_file (ca_file, &error);
+		if (error) {
+			g_warning ("Could not set SSL credentials from '%s': %s",
+				   ca_file, error->message);
+			g_error_free (error);
+		}
+		creds->validation_flags = G_TLS_VALIDATE_ALL;
+	}
+
+	return creds;
+}
+
+GList *
+soup_ssl_credentials_get_ca_list (SoupSSLCredentials *creds)
+{
+	return creds->ca_list;
+}
+
+GTlsValidationFlags
+soup_ssl_credentials_get_validation_flags (SoupSSLCredentials *creds)
+{
+	return creds->validation_flags;
+}
+
+void
+soup_ssl_free_client_credentials (SoupSSLCredentials *client_creds)
+{
+	GList *c;
+
+	for (c = client_creds->ca_list; c; c = c->next)
+		g_object_unref (c->data);
+	g_list_free (client_creds->ca_list);
+	g_slice_free (SoupSSLCredentials, client_creds);
+}
+
+SoupSSLCredentials *
+soup_ssl_get_server_credentials (const char *cert_file, const char *key_file)
+{
+	SoupSSLCredentials *creds;
+	GError *error = NULL;
+
+	creds = g_slice_new0 (SoupSSLCredentials);
+
+	creds->certificate = g_tls_certificate_new_from_files (cert_file, key_file, &error);
+	if (!creds->certificate) {
+		g_warning ("Could not read SSL certificate from '%s': %s",
+			   cert_file, error->message);
+		g_error_free (error);
+		g_slice_free (SoupSSLCredentials, creds);
+		return NULL;
+	}
+
+	return creds;
+}
+
+GTlsCertificate *
+soup_ssl_credentials_get_certificate (SoupSSLCredentials *creds)
+{
+	return creds->certificate;
+}
+
+void
+soup_ssl_free_server_credentials (SoupSSLCredentials *server_creds)
+{
+	g_object_unref (server_creds->certificate);
+	g_slice_free (SoupSSLCredentials, server_creds);
+}
+
+/**
+ * SOUP_SSL_ERROR:
+ *
+ * A #GError domain representing an SSL error. Used with #SoupSSLError.
+ **/
+/**
+ * soup_ssl_error_quark:
+ *
+ * The quark used as %SOUP_SSL_ERROR
+ *
+ * Return value: The quark used as %SOUP_SSL_ERROR
+ **/
+GQuark
+soup_ssl_error_quark (void)
+{
+	static GQuark error;
+	if (!error)
+		error = g_quark_from_static_string ("soup_ssl_error_quark");
+	return error;
+}
+
+/**
+ * SoupSSLError:
+ * @SOUP_SSL_ERROR_HANDSHAKE_NEEDS_READ: Internal error. Never exposed
+ * outside of libsoup.
+ * @SOUP_SSL_ERROR_HANDSHAKE_NEEDS_WRITE: Internal error. Never exposed
+ * outside of libsoup.
+ * @SOUP_SSL_ERROR_CERTIFICATE: Indicates an error validating an SSL
+ * certificate
+ *
+ * SSL-related I/O errors.
+ **/
diff --git a/libsoup/soup-ssl.h b/libsoup/soup-ssl.h
index f4e3eab..fd45108 100644
--- a/libsoup/soup-ssl.h
+++ b/libsoup/soup-ssl.h
@@ -6,7 +6,7 @@
 #ifndef SOUP_SSL_H
 #define SOUP_SSL_H 1
 
-#include <glib.h>
+#include <gio/gio.h>
 
 typedef enum {
 	SOUP_SSL_TYPE_CLIENT = 0,
@@ -22,10 +22,8 @@ SoupSSLCredentials *soup_ssl_get_server_credentials  (const char         *cert_f
 						      const char         *key_file);
 void                soup_ssl_free_server_credentials (SoupSSLCredentials *creds);
 
-GIOChannel         *soup_ssl_wrap_iochannel          (GIOChannel         *sock,
-						      gboolean            non_blocking,
-						      SoupSSLType         type,
-						      const char         *remote_host,
-						      SoupSSLCredentials *creds);
+GList              *soup_ssl_credentials_get_ca_list          (SoupSSLCredentials *creds);
+GTlsValidationFlags soup_ssl_credentials_get_validation_flags (SoupSSLCredentials *creds);
+GTlsCertificate    *soup_ssl_credentials_get_certificate      (SoupSSLCredentials *creds);
 
 #endif /* SOUP_SSL_H */
diff --git a/libsoup/soup-status.c b/libsoup/soup-status.c
index 1581d7a..8a2653c 100644
--- a/libsoup/soup-status.c
+++ b/libsoup/soup-status.c
@@ -181,11 +181,7 @@ static const struct {
 	{ SOUP_STATUS_CANT_RESOLVE_PROXY,         "Cannot resolve proxy hostname" },
 	{ SOUP_STATUS_CANT_CONNECT,               "Cannot connect to destination" },
 	{ SOUP_STATUS_CANT_CONNECT_PROXY,         "Cannot connect to proxy" },
-#ifdef HAVE_SSL
 	{ SOUP_STATUS_SSL_FAILED,                 "SSL handshake failed" },
-#else
-	{ SOUP_STATUS_SSL_FAILED,                 "SSL support not available" },
-#endif
 	{ SOUP_STATUS_IO_ERROR,                   "Connection terminated unexpectedly" },
 	{ SOUP_STATUS_MALFORMED,                  "Message Corrupt" },
 	{ SOUP_STATUS_TOO_MANY_REDIRECTS,         "Too many redirects" },
diff --git a/tests/Makefile.am b/tests/Makefile.am
index b69d27a..8316f77 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -4,8 +4,7 @@ INCLUDES =		\
 	-DLIBSOUP_DISABLE_DEPRECATED \
 	$(SOUP_MAINTAINER_FLAGS) \
 	$(XML_CFLAGS)	\
-	$(GLIB_CFLAGS)	\
-	$(LIBGNUTLS_CFLAGS)
+	$(GLIB_CFLAGS)
 
 LIBS =			\
 	$(top_builddir)/libsoup/libsoup-2.4.la \
@@ -35,7 +34,6 @@ noinst_PROGRAMS =	\
 	uri-parsing	\
 	$(CURL_TESTS)	\
 	$(APACHE_TESTS) \
-	$(SSL_TESTS)	\
 	$(XMLRPC_TESTS)
 
 TEST_SRCS = test-utils.c test-utils.h
@@ -65,7 +63,6 @@ server_auth_test_SOURCES = server-auth-test.c $(TEST_SRCS)
 simple_httpd_SOURCES = simple-httpd.c
 simple_proxy_SOURCES = simple-proxy.c
 sniffing_test_SOURCES = sniffing-test.c  $(TEST_SRCS)
-ssl_test_SOURCES = ssl-test.c $(TEST_SRCS)
 streaming_test_SOURCES = streaming-test.c $(TEST_SRCS)
 timeout_test_SOURCES = timeout-test.c $(TEST_SRCS)
 uri_parsing_SOURCES = uri-parsing.c $(TEST_SRCS)
@@ -78,9 +75,6 @@ endif
 if HAVE_CURL
 CURL_TESTS = forms-test server-auth-test
 endif
-if HAVE_SSL
-SSL_TESTS = ssl-test
-endif
 if HAVE_XMLRPC_EPI_PHP
 XMLRPC_TESTS = xmlrpc-test xmlrpc-server-test
 endif
@@ -102,7 +96,6 @@ TESTS =			\
 	uri-parsing	\
 	$(APACHE_TESTS) \
 	$(CURL_TESTS)	\
-	$(SSL_TESTS)	\
 	$(XMLRPC_TESTS)
 
 SNIFFING_FILES =		\
diff --git a/tests/proxy-test.c b/tests/proxy-test.c
index 68c1de8..4fb74ed 100644
--- a/tests/proxy-test.c
+++ b/tests/proxy-test.c
@@ -145,18 +145,12 @@ run_test (int i, gboolean sync)
 		https_url = g_strconcat (HTTPS_SERVER, tests[i].url, NULL);
 	}
 	test_url (http_url, SIMPLE_PROXY, tests[i].final_status, sync, FALSE);
-#ifdef HAVE_SSL
 	test_url (https_url, SIMPLE_PROXY, tests[i].final_status, sync, FALSE);
-#endif
 	test_url (http_url, AUTH_PROXY, tests[i].final_status, sync, FALSE);
-#ifdef HAVE_SSL
 	test_url (https_url, AUTH_PROXY, tests[i].final_status, sync, FALSE);
 	test_url (https_url, AUTH_PROXY, tests[i].final_status, sync, TRUE);
-#endif
 	test_url (http_url, UNAUTH_PROXY, tests[i].final_status, sync, FALSE);
-#ifdef HAVE_SSL
 	test_url (https_url, UNAUTH_PROXY, tests[i].final_status, sync, FALSE);
-#endif
 
 	g_free (http_url);
 	g_free (https_url);
diff --git a/tests/timeout-test.c b/tests/timeout-test.c
index 53d54d9..db9dd8d 100644
--- a/tests/timeout-test.c
+++ b/tests/timeout-test.c
@@ -176,7 +176,6 @@ main (int argc, char **argv)
 	g_free (slow_uri);
 	soup_test_server_quit_unref (server);
 
-#ifdef HAVE_SSL
 	debug_printf (1, "https\n");
 	server = soup_test_server_new_ssl (TRUE);
 	soup_server_add_handler (server, NULL, server_handler, NULL, NULL);
@@ -188,7 +187,6 @@ main (int argc, char **argv)
 	g_free (fast_uri);
 	g_free (slow_uri);
 	soup_test_server_quit_unref (server);
-#endif
 
 	test_cleanup ();
 	return errors != 0;



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