/pidgin/main: 53718d3c53f0: Port sslconn.[ch] to Gio

Mike Ruprecht cmaiku at gmail.com
Thu Apr 7 13:36:20 EDT 2016


Changeset: 53718d3c53f08f3f42bfd0d6fd0a174be840f6f7
Author:	 Mike Ruprecht <cmaiku at gmail.com>
Date:	 2016-03-30 01:20 -0500
Branch:	 purple-ssl-to-gio
URL: https://hg.pidgin.im/pidgin/main/rev/53718d3c53f0

Description:

Port sslconn.[ch] to Gio

This patch ports purple_ssl_* functions to use Gio's
GTlsClientConnection internally instead of the libpurple
SSL backend plugins.

This temporarily breaks manually accepting certificates until
the request API is ported and the helper functions implement
this functionality.

diffstat:

 libpurple/sslconn.c |  205 +++++++++++++++++++++++++++++++++++++--------------
 libpurple/sslconn.h |    6 +-
 2 files changed, 152 insertions(+), 59 deletions(-)

diffs (truncated from 382 to 300 lines):

diff --git a/libpurple/sslconn.c b/libpurple/sslconn.c
--- a/libpurple/sslconn.c
+++ b/libpurple/sslconn.c
@@ -22,11 +22,11 @@
 
 #include "internal.h"
 
-#include "certificate.h"
 #include "debug.h"
 #include "plugins.h"
 #include "request.h"
 #include "sslconn.h"
+#include "tls-certificate.h"
 
 static gboolean _ssl_initialized = FALSE;
 static PurpleSslOps *_ssl_ops = NULL;
@@ -34,50 +34,103 @@ static PurpleSslOps *_ssl_ops = NULL;
 static gboolean
 ssl_init(void)
 {
-	PurplePlugin *plugin;
-	PurpleSslOps *ops;
+	return g_tls_backend_supports_tls(g_tls_backend_get_default());
+}
 
-	if (_ssl_initialized)
-		return FALSE;
+static void
+emit_error(PurpleSslConnection *gsc, int error_code)
+{
+	if (gsc->error_cb != NULL)
+		gsc->error_cb(gsc, error_code, gsc->connect_cb_data);
+}
 
-	plugin = purple_plugins_find_plugin("core-ssl");
+static void
+tls_handshake_cb(GObject *source, GAsyncResult *res, gpointer user_data)
+{
+	PurpleSslConnection *gsc = user_data;
+	GError *error = NULL;
 
-	if (plugin && !purple_plugin_is_loaded(plugin))
-		purple_plugin_load(plugin, NULL);
+	if (!g_tls_connection_handshake_finish(G_TLS_CONNECTION(source), res,
+			&error)) {
+		if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+			/* Connection already closed/freed. Escape. */
+			return;
+		} else if (g_error_matches(error, G_TLS_ERROR,
+				G_TLS_ERROR_HANDSHAKE)) {
+			/* In Gio, a handshake error is because of the cert */
+			emit_error(gsc, PURPLE_SSL_CERTIFICATE_INVALID);
+		} else {
+			/* Report any other errors as handshake failing */
+			emit_error(gsc, PURPLE_SSL_HANDSHAKE_FAILED);
+		}
 
-	ops = purple_ssl_get_ops();
-	if ((ops == NULL) || (ops->init == NULL) || (ops->uninit == NULL) ||
-		(ops->connectfunc == NULL) || (ops->close == NULL) ||
-		(ops->read == NULL) || (ops->write == NULL))
-	{
-		return FALSE;
+		purple_ssl_close(gsc);
+		return;
 	}
 
-	return (_ssl_initialized = ops->init());
+	gsc->connect_cb(gsc->connect_cb_data, gsc, PURPLE_INPUT_READ);
+}
+
+static void
+tls_connect(PurpleSslConnection *gsc)
+{
+	GSocket *socket;
+	GSocketConnection *conn;
+	GSocketConnectable *identity;
+	GIOStream *tls_conn;
+	GError *error = NULL;
+
+	g_return_if_fail(gsc->conn == NULL);
+
+	socket = g_socket_new_from_fd(gsc->fd, &error);
+	if (socket == NULL) {
+		emit_error(gsc, PURPLE_SSL_CONNECT_FAILED);
+		purple_ssl_close(gsc);
+		return;
+	}
+
+	conn = g_socket_connection_factory_create_connection(socket);
+	g_object_unref(socket);
+
+	identity = g_network_address_new(gsc->host, gsc->port);
+	tls_conn = g_tls_client_connection_new(G_IO_STREAM(conn), identity,
+			&error);
+	g_object_unref(identity);
+	g_object_unref(conn);
+
+	if (tls_conn == NULL) {
+		emit_error(gsc, PURPLE_SSL_CONNECT_FAILED);
+		purple_ssl_close(gsc);
+		return;
+	}
+
+	gsc->conn = G_TLS_CONNECTION(tls_conn);
+	gsc->cancellable = g_cancellable_new();
+
+	purple_tls_certificate_attach_to_tls_connection(gsc->conn);
+
+	g_tls_connection_handshake_async(gsc->conn, G_PRIORITY_DEFAULT,
+			gsc->cancellable, tls_handshake_cb, gsc);
 }
 
 static void
 purple_ssl_connect_cb(gpointer data, gint source, const gchar *error_message)
 {
 	PurpleSslConnection *gsc;
-	PurpleSslOps *ops;
 
 	gsc = data;
 	gsc->connect_data = NULL;
 
 	if (source < 0)
 	{
-		if (gsc->error_cb != NULL)
-			gsc->error_cb(gsc, PURPLE_SSL_CONNECT_FAILED, gsc->connect_cb_data);
-
+		emit_error(gsc, PURPLE_SSL_CONNECT_FAILED);
 		purple_ssl_close(gsc);
 		return;
 	}
 
 	gsc->fd = source;
 
-	ops = purple_ssl_get_ops();
-	ops->connectfunc(gsc);
+	tls_connect(gsc);
 }
 
 PurpleSslConnection *
@@ -115,9 +168,6 @@ purple_ssl_connect_with_ssl_cn(PurpleAcc
 	gsc->connect_cb      = func;
 	gsc->error_cb        = error_func;
 
-	/* TODO: Move this elsewhere */
-	gsc->verifier = purple_certificate_find_verifier("x509","tls_cached");
-
 	gsc->connect_data = purple_proxy_connect(NULL, account, host, port, purple_ssl_connect_cb, gsc);
 
 	if (gsc->connect_data == NULL)
@@ -131,33 +181,45 @@ purple_ssl_connect_with_ssl_cn(PurpleAcc
 	return (PurpleSslConnection *)gsc;
 }
 
-static void
-recv_cb(gpointer data, gint source, PurpleInputCondition cond)
+static gboolean
+recv_cb(GObject *source, gpointer data)
 {
 	PurpleSslConnection *gsc = data;
 
-	gsc->recv_cb(gsc->recv_cb_data, gsc, cond);
+	gsc->recv_cb(gsc->recv_cb_data, gsc, PURPLE_INPUT_READ);
+
+	return TRUE;
 }
 
 void
 purple_ssl_input_add(PurpleSslConnection *gsc, PurpleSslInputFunction func,
 				   void *data)
 {
+	GInputStream *input;
+	GSource *source;
+
 	g_return_if_fail(func != NULL);
+	g_return_if_fail(gsc->conn != NULL);
 
 	purple_ssl_input_remove(gsc);
 
 	gsc->recv_cb_data = data;
 	gsc->recv_cb      = func;
 
-	gsc->inpa = purple_input_add(gsc->fd, PURPLE_INPUT_READ, recv_cb, gsc);
+	input = g_io_stream_get_input_stream(G_IO_STREAM(gsc->conn));
+	/* Pass NULL for cancellable as we don't want it notified on cancel */
+	source = g_pollable_input_stream_create_source(
+			G_POLLABLE_INPUT_STREAM(input), NULL);
+	g_source_set_callback(source, (GSourceFunc)recv_cb, gsc, NULL);
+	gsc->inpa = g_source_attach(source, NULL);
+	g_source_unref(source);
 }
 
 void
 purple_ssl_input_remove(PurpleSslConnection *gsc)
 {
 	if (gsc->inpa > 0) {
-		purple_input_remove(gsc->inpa);
+		g_source_remove(gsc->inpa);
 		gsc->inpa = 0;
 	}
 }
@@ -186,7 +248,6 @@ purple_ssl_connect_with_host_fd(PurpleAc
                       void *data)
 {
 	PurpleSslConnection *gsc;
-	PurpleSslOps *ops;
 
 	g_return_val_if_fail(fd != -1,                NULL);
 	g_return_val_if_fail(func != NULL,            NULL);
@@ -205,13 +266,9 @@ purple_ssl_connect_with_host_fd(PurpleAc
 	gsc->fd              = fd;
     if(host)
         gsc->host            = g_strdup(host);
+	gsc->cancellable     = g_cancellable_new();
 
-	/* TODO: Move this elsewhere */
-	gsc->verifier = purple_certificate_find_verifier("x509","tls_cached");
-
-
-	ops = purple_ssl_get_ops();
-	ops->connectfunc(gsc);
+	tls_connect(gsc);
 
 	return (PurpleSslConnection *)gsc;
 }
@@ -219,24 +276,30 @@ purple_ssl_connect_with_host_fd(PurpleAc
 void
 purple_ssl_close(PurpleSslConnection *gsc)
 {
-	PurpleSslOps *ops;
-
 	g_return_if_fail(gsc != NULL);
 
 	purple_request_close_with_handle(gsc);
 	purple_notify_close_with_handle(gsc);
 
-	ops = purple_ssl_get_ops();
-	(ops->close)(gsc);
-
 	if (gsc->connect_data != NULL)
 		purple_proxy_connect_cancel(gsc->connect_data);
 
 	if (gsc->inpa > 0)
 		purple_input_remove(gsc->inpa);
 
-	if (gsc->fd >= 0)
-		close(gsc->fd);
+	/* Stop any pending operations */
+	if (G_IS_CANCELLABLE(gsc->cancellable)) {
+		g_cancellable_cancel(gsc->cancellable);
+		g_clear_object(&gsc->cancellable);
+	}
+
+	if (gsc->conn != NULL) {
+		/* Close the stream. Shouldn't take long and it can't
+		 * be further cancelled so don't pass a cancellable
+		 */
+		g_io_stream_close(G_IO_STREAM(gsc->conn), NULL, NULL);
+		g_clear_object(&gsc->conn);
+	}
 
 	g_free(gsc->host);
 	g_free(gsc);
@@ -245,38 +308,71 @@ purple_ssl_close(PurpleSslConnection *gs
 size_t
 purple_ssl_read(PurpleSslConnection *gsc, void *data, size_t len)
 {
-	PurpleSslOps *ops;
+	GInputStream *input;
+	gssize outlen;
+	GError *error = NULL;
 
 	g_return_val_if_fail(gsc  != NULL, 0);
 	g_return_val_if_fail(data != NULL, 0);
 	g_return_val_if_fail(len  >  0,    0);
+	g_return_val_if_fail(gsc->conn != NULL, 0);
 
-	ops = purple_ssl_get_ops();
-	return (ops->read)(gsc, data, len);
+	input = g_io_stream_get_input_stream(G_IO_STREAM(gsc->conn));
+	outlen = g_pollable_input_stream_read_nonblocking(
+			G_POLLABLE_INPUT_STREAM(input), data, len,
+			gsc->cancellable, &error);
+
+	if (outlen < 0) {
+		if (outlen == G_IO_ERROR_WOULD_BLOCK) {
+			errno = EAGAIN;
+		}
+
+		g_clear_error(&error);
+	}
+
+	return outlen;
 }
 
 size_t
 purple_ssl_write(PurpleSslConnection *gsc, const void *data, size_t len)
 {
-	PurpleSslOps *ops;
+	GOutputStream *output;



More information about the Commits mailing list