diff plugins/ssl/ssl-gnutls.c @ 13200:33bef17125c2

[gaim-migrate @ 15563] This is the soon-to-be-infamous nonblocking network activity patch that I've been working on. Feel free to yell at me if this makes you unhappy. committer: Tailor Script <tailor@pidgin.im>
author Daniel Atallah <daniel.atallah@gmail.com>
date Thu, 09 Feb 2006 04:17:56 +0000
parents c18bd02be106
children 7d513e44201b
line wrap: on
line diff
--- a/plugins/ssl/ssl-gnutls.c	Thu Feb 09 04:14:54 2006 +0000
+++ b/plugins/ssl/ssl-gnutls.c	Thu Feb 09 04:17:56 2006 +0000
@@ -34,7 +34,7 @@
 typedef struct
 {
 	gnutls_session session;
-
+	guint handshake_handler;
 } GaimSslGnutlsData;
 
 #define GAIM_SSL_GNUTLS_DATA(gsc) ((GaimSslGnutlsData *)gsc->private_data)
@@ -48,7 +48,7 @@
 
 	gnutls_certificate_allocate_credentials(&xcred);
 	gnutls_certificate_set_x509_trust_file(xcred, "ca.pem",
-										   GNUTLS_X509_FMT_PEM);
+		GNUTLS_X509_FMT_PEM);
 }
 
 static gboolean
@@ -65,15 +65,48 @@
 	gnutls_certificate_free_credentials(xcred);
 }
 
+
+static void ssl_gnutls_handshake_cb(gpointer data, gint source,
+		GaimInputCondition cond)
+{
+	GaimSslConnection *gsc = data;
+	GaimSslGnutlsData *gnutls_data = GAIM_SSL_GNUTLS_DATA(gsc);
+	ssize_t ret;
+
+	gaim_debug_info("gnutls", "Handshaking\n");
+	ret = gnutls_handshake(gnutls_data->session);
+
+	if(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
+		return;
+
+	gaim_input_remove(gnutls_data->handshake_handler);
+	gnutls_data->handshake_handler = 0;
+
+	if(ret != 0) {
+		gaim_debug_error("gnutls", "Handshake failed. Error %d\n", ret);
+
+		if(gsc->error_cb != NULL)
+			gsc->error_cb(gsc, GAIM_SSL_HANDSHAKE_FAILED,
+				gsc->connect_cb_data);
+
+		gaim_ssl_close(gsc);
+	} else {
+		gaim_debug_info("gnutls", "Handshake complete\n");
+
+		gsc->connect_cb(gsc->connect_cb_data, gsc, cond);
+	}
+
+}
+
+
 static void
 ssl_gnutls_connect_cb(gpointer data, gint source, GaimInputCondition cond)
 {
 	GaimSslConnection *gsc = (GaimSslConnection *)data;
 	GaimSslGnutlsData *gnutls_data;
 	static const int cert_type_priority[2] = { GNUTLS_CRT_X509, 0 };
-	int ret;
 
-	if (source < 0) {
+	if(source < 0) {
 		if(gsc->error_cb != NULL)
 			gsc->error_cb(gsc, GAIM_SSL_CONNECT_FAILED, gsc->connect_cb_data);
 
@@ -90,37 +123,17 @@
 	gnutls_set_default_priority(gnutls_data->session);
 
 	gnutls_certificate_type_set_priority(gnutls_data->session,
-										 cert_type_priority);
+		cert_type_priority);
 
 	gnutls_credentials_set(gnutls_data->session, GNUTLS_CRD_CERTIFICATE,
-						   xcred);
+		xcred);
 
 	gnutls_transport_set_ptr(gnutls_data->session, GINT_TO_POINTER(source));
 
-
-	do
-	{
-		gaim_debug_info("gnutls", "Handshaking\n");
-		ret = gnutls_handshake(gnutls_data->session);
-	}
-	while ((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED));
-
-	if (ret < 0)
-	{
-		gaim_debug_error("gnutls", "Handshake failed. Error %d\n", ret);
+	gnutls_data->handshake_handler = gaim_input_add(gsc->fd,
+		GAIM_INPUT_READ, ssl_gnutls_handshake_cb, gsc);
 
-		if (gsc->error_cb != NULL)
-			gsc->error_cb(gsc, GAIM_SSL_HANDSHAKE_FAILED,
-						  gsc->connect_cb_data);
-
-		gaim_ssl_close(gsc);
-	}
-	else
-	{
-		gaim_debug_info("gnutls", "Handshake complete\n");
-
-		gsc->connect_cb(gsc->connect_cb_data, gsc, cond);
-	}
+	ssl_gnutls_handshake_cb(gsc, gsc->fd, GAIM_INPUT_READ);
 }
 
 static void
@@ -131,27 +144,29 @@
 	if(!gnutls_data)
 		return;
 
+	if(gnutls_data->handshake_handler)
+		gaim_input_remove(gnutls_data->handshake_handler);
+
 	gnutls_bye(gnutls_data->session, GNUTLS_SHUT_RDWR);
 
 	gnutls_deinit(gnutls_data->session);
 
 	g_free(gnutls_data);
+	gsc->private_data = NULL;
 }
 
 static size_t
 ssl_gnutls_read(GaimSslConnection *gsc, void *data, size_t len)
 {
 	GaimSslGnutlsData *gnutls_data = GAIM_SSL_GNUTLS_DATA(gsc);
-	int s;
+	ssize_t s;
+
+	s = gnutls_record_recv(gnutls_data->session, data, len);
 
-	do
-	{
-		s = gnutls_record_recv(gnutls_data->session, data, len);
-	}
-	while ((s == GNUTLS_E_AGAIN) || (s == GNUTLS_E_INTERRUPTED));
-
-	if (s < 0)
-	{
+	if(s == GNUTLS_E_AGAIN || s == GNUTLS_E_INTERRUPTED) {
+		s = -1;
+		errno = EAGAIN;
+	} else if(s < 0) {
 		gaim_debug_error("gnutls", "receive failed: %d\n", s);
 		s = 0;
 	}
@@ -163,11 +178,20 @@
 ssl_gnutls_write(GaimSslConnection *gsc, const void *data, size_t len)
 {
 	GaimSslGnutlsData *gnutls_data = GAIM_SSL_GNUTLS_DATA(gsc);
-	size_t s = 0;
+	ssize_t s = 0;
 
+	/* XXX: when will gnutls_data be NULL? */
 	if(gnutls_data)
 		s = gnutls_record_send(gnutls_data->session, data, len);
 
+	if(s == GNUTLS_E_AGAIN || s == GNUTLS_E_INTERRUPTED) {
+		s = -1;
+		errno = EAGAIN;
+	} else if(s < 0) {
+		gaim_debug_error("gnutls", "send failed: %d\n", s);
+		s = 0;
+	}
+
 	return s;
 }
 
@@ -187,7 +211,7 @@
 plugin_load(GaimPlugin *plugin)
 {
 #ifdef HAVE_GNUTLS
-	if (!gaim_ssl_get_ops()) {
+	if(!gaim_ssl_get_ops()) {
 		gaim_ssl_set_ops(&ssl_ops);
 	}
 
@@ -204,7 +228,7 @@
 plugin_unload(GaimPlugin *plugin)
 {
 #ifdef HAVE_GNUTLS
-	if (gaim_ssl_get_ops() == &ssl_ops) {
+	if(gaim_ssl_get_ops() == &ssl_ops) {
 		gaim_ssl_set_ops(NULL);
 	}
 #endif