2006-04-03 Mikael Hallendal <micke@imendio.com>
authorhallski <hallski>
Mon, 03 Apr 2006 16:14:31 +0000
changeset 133 f7522d7ed6dc
parent 132 8c9c1629f691
child 134 3df25ef89435
2006-04-03 Mikael Hallendal <micke@imendio.com> * loudmouth/lm-connection.c: (connection_free), (_lm_connection_succeeded), (connection_buffered_write_cb), (connection_output_is_buffered), (connection_setup_output_buffer), (connection_do_close), (connection_do_write), (connection_send), (lm_connection_new), (lm_connection_authenticate): - Added an output buffer that will be enabled if the nonblocking write cannot be performed in one go (for large messages). - Fixes LM-17. * loudmouth/lm-internals.h: * loudmouth/lm-ssl.c: (ssl_func_always_continue), (_lm_ssl_send), (lm_ssl_new): - Added a default SSL function that will always continue, this will be used if NULL is passed in as SSL function to lm_ssl_new.
ChangeLog
loudmouth/lm-connection.c
loudmouth/lm-internals.h
loudmouth/lm-ssl.c
--- a/ChangeLog	Mon Mar 20 14:54:06 2006 +0000
+++ b/ChangeLog	Mon Apr 03 16:14:31 2006 +0000
@@ -1,3 +1,19 @@
+2006-04-03  Mikael Hallendal  <micke@imendio.com>
+
+	* loudmouth/lm-connection.c: (connection_free),
+	(_lm_connection_succeeded), (connection_buffered_write_cb),
+	(connection_output_is_buffered), (connection_setup_output_buffer),
+	(connection_do_close), (connection_do_write), (connection_send),
+	(lm_connection_new), (lm_connection_authenticate):
+	- Added an output buffer that will be enabled if the nonblocking write
+	  cannot be performed in one go (for large messages).
+	- Fixes LM-17.
+	* loudmouth/lm-internals.h:
+	* loudmouth/lm-ssl.c: (ssl_func_always_continue), (_lm_ssl_send),
+	(lm_ssl_new):
+	- Added a default SSL function that will always continue, this will 
+	  be used if NULL is passed in as SSL function to lm_ssl_new.
+
 2006-03-20  Mikael Hallendal  <micke@imendio.com>
 
 	* loudmouth/lm-connection.c: 
--- a/loudmouth/lm-connection.c	Mon Mar 20 14:54:06 2006 +0000
+++ b/loudmouth/lm-connection.c	Mon Apr 03 16:14:31 2006 +0000
@@ -96,6 +96,9 @@
 	guint         keep_alive_rate;
 	guint         keep_alive_id;
 
+	guint         io_watch_out;
+	GString      *out_buf;
+
 	gint          ref_count;
 };
 
@@ -118,6 +121,10 @@
 					      GError              **error);
 
 static void     connection_do_close           (LmConnection        *connection);
+static gint     connection_do_write          (LmConnection         *connection,
+					      const gchar          *buf,
+					      gint                  len);
+
 static gboolean connection_in_event          (GIOChannel   *source,
 					      GIOCondition  condition,
 					      LmConnection *connection);
@@ -127,7 +134,7 @@
 static gboolean connection_hup_event         (GIOChannel   *source,
 					      GIOCondition  condition,
 					      LmConnection *connection);
-static gboolean connection_send              (LmConnection             *connection,
+static gboolean connection_send              (LmConnection         *connection,
 					      const gchar          *str,
 					      gint                  len,
 					      GError               **error);
@@ -148,8 +155,8 @@
 					      LmMessage           *m,
 					      gpointer             user_data);
 
-static void     connection_stream_received   (LmConnection             *connection, 
-					      LmMessage                *m);
+static void     connection_stream_received   (LmConnection        *connection, 
+					      LmMessage           *m);
 
 static gint     connection_handler_compare_func (HandlerData  *a,
 						 HandlerData  *b);
@@ -172,6 +179,15 @@
 static gboolean connection_send_keep_alive      (LmConnection  *connection);
 static void     connection_start_keep_alive     (LmConnection  *connection);
 static void     connection_stop_keep_alive      (LmConnection  *connection);
+static gboolean connection_buffered_write_cb    (GIOChannel    *source, 
+						 GIOCondition   condition,
+						 LmConnection  *connection);
+static gboolean connection_output_is_buffered   (LmConnection  *connection,
+						 const gchar   *buffer,
+						 gint           len);
+static void     connection_setup_output_buffer  (LmConnection  *connection,
+						 const gchar   *buffer,
+						 gint           len);
 
 static GSourceFuncs incoming_funcs = {
 	connection_incoming_prepare,
@@ -221,6 +237,10 @@
         if (connection->context) {
                 g_main_context_unref (connection->context);
         }
+
+	if (connection->out_buf) {
+		g_string_free (connection->out_buf, TRUE);
+	}
         
         g_free (connection);
 }
@@ -365,7 +385,9 @@
 				      G_IO_HUP,
 				      (GIOFunc) connection_hup_event,
 				      connection);
-	
+
+	/* FIXME: Set up according to XMPP 1.0 specification */
+	/*        StartTLS and the like */
 	if (!connection_send (connection, 
 			      "<?xml version='1.0' encoding='UTF-8'?>", -1,
 			      NULL)) {
@@ -603,6 +625,75 @@
 	connection->keep_alive_id = 0;
 }
 
+static gboolean
+connection_buffered_write_cb (GIOChannel   *source, 
+			      GIOCondition  condition,
+			      LmConnection *connection)
+{
+	gint     b_written;
+	GString *out_buf;
+	/* FIXME: Do the writing */
+
+	out_buf = connection->out_buf;
+	if (!out_buf) {
+		/* Should not be possible */
+		return FALSE;
+	}
+
+	b_written = connection_do_write (connection, out_buf->str, out_buf->len);
+
+	if (b_written < 0) {
+		connection_error_event (connection->io_channel, 
+					G_IO_HUP,
+					connection);
+		return FALSE;
+	}
+
+	g_string_erase (out_buf, 0, (gsize) b_written);
+	if (out_buf->len == 0) {
+		lm_verbose ("Output buffer is empty, going back to normal output\n");
+		g_source_destroy (g_main_context_find_source_by_id (
+			connection->context, connection->io_watch_out));
+		connection->io_watch_out = 0;
+		g_string_free (out_buf, TRUE);
+		connection->out_buf = NULL;
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+connection_output_is_buffered (LmConnection *connection,
+			       const gchar  *buffer,
+			       gint          len)
+{
+	if (connection->out_buf) {
+		lm_verbose ("Appending %d bytes to output buffer\n", len);
+		g_string_append_len (connection->out_buf, buffer, len);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static void
+connection_setup_output_buffer (LmConnection *connection,
+				const gchar  *buffer,
+				gint          len)
+{
+	lm_verbose ("OUTPUT BUFFER ENABLED\n");
+
+	connection->out_buf = g_string_new_len (buffer, len);
+
+	connection->io_watch_out =
+		connection_add_watch (connection,
+				      connection->io_channel,
+				      G_IO_OUT,
+				      (GIOFunc) connection_buffered_write_cb,
+				      connection);
+}
+
 /* Returns directly */
 /* Setups all data needed to start the connection attempts */
 static gboolean
@@ -704,6 +795,12 @@
 		g_source_destroy (g_main_context_find_source_by_id (
 			connection->context, connection->io_watch_hup));
 
+		if (connection->io_watch_out != 0) {
+			g_source_destroy (g_main_context_find_source_by_id (
+				connection->context, connection->io_watch_out));
+			connection->io_watch_out = 0;
+		}
+
 		if (connection->io_watch_connect != 0) {
 			g_source_destroy (g_main_context_find_source_by_id(connection->context,
 									   connection->io_watch_connect));
@@ -731,6 +828,35 @@
 	}
 }
 
+static gint
+connection_do_write (LmConnection *connection,
+		     const gchar  *buf,
+		     gint          len)
+{
+	gint b_written;
+
+	if (connection->ssl) {
+		b_written = _lm_ssl_send (connection->ssl, buf, len);
+	} else {
+		GIOStatus io_status = G_IO_STATUS_AGAIN;
+		gsize     bytes_written;
+
+		while (io_status == G_IO_STATUS_AGAIN) {
+			io_status = g_io_channel_write_chars (connection->io_channel, 
+							      buf, len, 
+							      &bytes_written,
+							      NULL);
+		}
+
+		b_written = bytes_written;
+
+		if (io_status != G_IO_STATUS_NORMAL) {
+			b_written = -1;
+		}
+	}
+
+	return b_written;
+}
 
 static gboolean
 connection_in_event (GIOChannel   *source,
@@ -834,7 +960,7 @@
 		 gint           len, 
 		 GError       **error)
 {
-	gsize             bytes_written;
+	gint b_written;
 	
 	if (connection->state < LM_CONNECTION_STATE_OPENING) {
 		g_set_error (error,
@@ -855,16 +981,27 @@
 	g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET, 
 	       "-----------------------------------\n");
 
-	if (connection->ssl) {
-		if (!_lm_ssl_send (connection->ssl, str, len)) {
-			
-			connection_error_event (connection->io_channel, 
-						G_IO_HUP,
-						connection);
-		}
-	} else {
-		g_io_channel_write_chars (connection->io_channel, str, len, 
-					  &bytes_written, NULL);
+	/* Check to see if there already is an output buffer, if so, add to the
+	   buffer and return */
+
+	if (connection_output_is_buffered (connection, str, len)) {
+		return TRUE;
+	}
+
+	b_written = connection_do_write (connection, str, len);
+
+
+	if (b_written < 0) {
+		connection_error_event (connection->io_channel, 
+					G_IO_HUP,
+					connection);
+		return FALSE;
+	}
+
+	if (b_written < len) {
+		connection_setup_output_buffer (connection, 
+						str + b_written, 
+						len - b_written);
 	}
 
 	return TRUE;
@@ -1179,6 +1316,7 @@
 	connection->state             = LM_CONNECTION_STATE_CLOSED;
 	connection->keep_alive_id     = 0;
 	connection->keep_alive_rate   = 0;
+	connection->out_buf           = NULL;
 	
 	connection->id_handlers = g_hash_table_new_full (g_str_hash, 
 							 g_str_equal,
@@ -1389,6 +1527,8 @@
 		return FALSE;
 	}
 
+	/* FIXME: Do SASL authentication here (if XMPP 1.0 is used) */
+
 	connection->state = LM_CONNECTION_STATE_AUTHENTICATING;
 	
 	connection->auth_cb = _lm_utils_new_callback (function, 
--- a/loudmouth/lm-internals.h	Mon Mar 20 14:54:06 2006 +0000
+++ b/loudmouth/lm-internals.h	Mon Apr 03 16:14:31 2006 +0000
@@ -75,7 +75,7 @@
 						     gchar            *buf,
 						     gint              len,
 						     gsize             *bytes_read);
-gboolean         _lm_ssl_send                       (LmSSL            *ssl,
+gint             _lm_ssl_send                       (LmSSL            *ssl,
 						     const gchar      *str,
 						     gint              len);
 void             _lm_ssl_close                      (LmSSL            *ssl);
--- a/loudmouth/lm-ssl.c	Mon Mar 20 14:54:06 2006 +0000
+++ b/loudmouth/lm-ssl.c	Mon Apr 03 16:14:31 2006 +0000
@@ -45,11 +45,14 @@
 #endif
 };
 
-static void      ssl_free                (LmSSL       *ssl);
+static void           ssl_free                  (LmSSL       *ssl);
 
 #ifdef HAVE_GNUTLS
-static gboolean  ssl_verify_certificate  (LmSSL       *ssl,
-					  const gchar *server);
+static gboolean       ssl_verify_certificate    (LmSSL       *ssl,
+						 const gchar *server);
+static LmSSLResponse  ssl_func_always_continue  (LmSSL       *ssl,
+						 LmSSLStatus  status,
+						 gpointer     user_data);
 
 static gboolean
 ssl_verify_certificate (LmSSL *ssl, const gchar *server)
@@ -145,6 +148,14 @@
 	return TRUE;
 }
 
+static LmSSLResponse  
+ssl_func_always_continue (LmSSL       *ssl,
+			  LmSSLStatus  status,
+			  gpointer     user_data)
+{
+	return LM_SSL_RESPONSE_CONTINUE;;
+}
+
 void
 _lm_ssl_initialize (LmSSL *ssl) 
 {
@@ -223,7 +234,7 @@
 	return status;
 }
 
-gboolean
+gint
 _lm_ssl_send (LmSSL *ssl, const gchar *str, gint len)
 {
 	gint bytes_written;
@@ -233,14 +244,14 @@
 	while (bytes_written < 0) {
 		if (bytes_written != GNUTLS_E_INTERRUPTED &&
 		    bytes_written != GNUTLS_E_AGAIN) {
-			return FALSE;
+			return -1;
 		}
 	
 		bytes_written = gnutls_record_send (ssl->gnutls_session, 
 						    str, len);
 	}
 
-	return TRUE;
+	return bytes_written;
 }
 
 void 
@@ -280,7 +291,7 @@
 /**
  * lm_ssl_new:
  * @expected_fingerprint: The expected fingerprint. @ssl_function will be called if there is a mismatch. %NULL if you are not interested in this check.
- * @ssl_function: Callback called to inform the user of a problem during setting up the SSL connection and how to proceed.
+ * @ssl_function: Callback called to inform the user of a problem during setting up the SSL connection and how to proceed. If %NULL is passed the default function that always continues will be used.
  * @user_data: Data sent with the callback.
  * @notify: Function to free @user_dataa when the connection is finished. %NULL if @user_data should not be freed.
  *
@@ -310,6 +321,13 @@
 		ssl->expected_fingerprint = NULL;
 	}
 
+	if (!ssl->func) {
+		/* If user didn't provide an SSL func the default will be used
+		 * this function will always tell the connection to continue.
+		 */
+		ssl->func = ssl_func_always_continue;
+	}
+
 	return ssl;
 }