--- 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-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;
}