diff -ura --new-file loudmouth.old/loudmouth/lm-bs-client.c loudmouth/loudmouth/lm-bs-client.c
--- loudmouth.old/loudmouth/lm-bs-client.c	1970-01-01 02:00:00.000000000 +0200
+++ loudmouth/loudmouth/lm-bs-client.c	2007-03-28 18:49:44.000000000 +0300
@@ -0,0 +1,598 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Free Software Foundation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * 
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <sys/stat.h> 
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h> 
+
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+
+#include "lm-sock.h"
+#include "lm-debug.h"
+#include "lm-error.h"
+#include "lm-internals.h"
+#include "lm-connection.h"
+#include "lm-utils.h"
+
+#include "lm-bs-session.h"
+#include "lm-bs-client.h"
+
+#define IO_READ_FLAGS G_IO_IN|G_IO_PRI|G_IO_HUP|G_IO_ERR
+#define IO_WRITE_FLAGS G_IO_OUT|G_IO_ERR
+#define IO_CONNECT_FLAGS G_IO_OUT|G_IO_ERR
+
+#define IN_BUFFER_SIZE 1024
+
+/* stop connect attempt after 30 sec. */
+#define CONNECT_TIMEOUT 30000
+
+struct _LmBsClient {
+	GMainContext    *context;
+	gchar           *host;
+	guint64          port;
+	ClientStatus     status;
+
+	LmCallback      *connected_cb;
+	LmCallback      *disconnected_cb;
+	LmCallback      *data_read_cb;
+	LmCallback      *data_written_cb;
+	LmSocket         fd;
+	struct addrinfo *server_addr;
+	GIOChannel      *io_channel;
+	guint            io_watch;
+	guint            timeout_id;
+
+	GString         *out_buf;
+	gint             ref_count;
+};
+
+static gboolean read_events_cb            (GIOChannel   *source, 
+					   GIOCondition  condition,
+					   gpointer      user_data);
+static gboolean write_events_cb           (GIOChannel   *source, 
+					   GIOCondition  condition,
+					   gpointer      user_data);
+static guint    client_add_watch          (LmBsClient   *client,
+					   GIOChannel   *channel,
+					   GIOCondition  condition,
+					   GIOFunc       func);
+static void     client_remove_watch       (LmBsClient   *client);
+static gboolean connect_timeout           (LmBsClient   *client);
+static gboolean connection_failed         (LmBsClient   *client);
+static gboolean connection_succeeded      (LmBsClient   *client);
+static gboolean client_get_connect_events (GIOChannel   *source, 
+					   GIOCondition  condition,
+					   gpointer      user_data);
+static gint     client_try_to_connect     (LmBsClient   *client);
+
+static gboolean fill_server_address_info  (LmBsClient   *client);
+static void     free_client               (LmBsClient   *client);
+
+
+static gboolean
+read_events_cb (GIOChannel   *source,
+		GIOCondition  condition,
+		gpointer      user_data)
+{
+	LmBsClient *client;
+	LmCallback *cb;
+	GIOStatus   io_status;
+	GString    *read_data;
+	gchar       buf[IN_BUFFER_SIZE];
+	gsize       bytes_read;
+
+	client = LM_BS_CLIENT (user_data);
+	io_status = G_IO_STATUS_AGAIN;
+	read_data = g_string_new (NULL);
+	while (io_status == G_IO_STATUS_AGAIN) {
+		io_status = g_io_channel_read_chars (client->io_channel,
+						   buf, IN_BUFFER_SIZE,
+						   &bytes_read,
+						   NULL);
+		g_string_append_len (read_data, buf, bytes_read);
+	}
+	if (io_status != G_IO_STATUS_NORMAL) {
+		g_assert (read_data->len == 0);
+		lm_bs_client_stop (client);
+		connection_failed (client);
+		g_string_free (read_data, TRUE);
+		return FALSE;
+	}
+	g_assert (read_data->len > 0);
+	cb = client->data_read_cb;
+	if (cb && cb->func) {
+		(* ((LmBsClientReadFunction) cb->func)) (client,
+							 cb->user_data,
+							 read_data);
+	}
+	g_string_free (read_data, TRUE);
+	return TRUE;
+}
+
+static gboolean
+write_events_cb (GIOChannel   *source,
+		 GIOCondition  condition,
+		 gpointer      user_data)
+{
+	LmBsClient   *client;
+	LmCallback   *cb;
+	GIOStatus     io_status;
+	gsize         bytes_written;
+	guint         total_written;
+	
+	client = LM_BS_CLIENT (user_data);
+	io_status = G_IO_STATUS_AGAIN;
+	
+	total_written = 0;
+	while (io_status == G_IO_STATUS_AGAIN) {
+		io_status = g_io_channel_write_chars (client->io_channel, 
+						      client->out_buf->str,
+						      client->out_buf->len, 
+						      &bytes_written,
+						      NULL);
+		g_string_erase (client->out_buf, 0, bytes_written);
+		total_written += bytes_written;
+		if (client->out_buf->len == 0) {
+			break;
+		}
+	}
+	if (io_status != G_IO_STATUS_NORMAL || total_written == 0) {
+		lm_bs_client_stop (client);
+		connection_failed (client);
+		return FALSE;
+	}
+	if (client->out_buf->len == 0) {
+		client_remove_watch (client);
+		cb = client->data_written_cb;
+		if (cb && cb->func) {
+			(* ((LmBsClientFunction) cb->func)) (client,
+							     cb->user_data);
+		}
+		/* no more data to write, unregister write handler */
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static guint
+client_add_watch (LmBsClient   *client,
+		  GIOChannel   *channel,
+		  GIOCondition  condition,
+		  GIOFunc       func)
+{
+	GSource *source;
+	guint    id;
+
+	g_return_val_if_fail (channel != NULL, 0);
+
+	source = g_io_create_watch (channel, condition);
+
+	g_source_set_callback (source, (GSourceFunc)func, client, NULL);
+
+	id = g_source_attach (source, client->context);
+
+	g_source_unref (source);
+  
+	return id;
+}
+
+static void
+client_remove_watch (LmBsClient *client)
+{
+	GSource *source;
+	if (client->io_watch != 0) {
+		
+		source = g_main_context_find_source_by_id (client->context,
+							   client->io_watch);
+		if (source) {
+			g_source_destroy (source);
+		}
+		client->io_watch = 0;
+	}
+	if (client->timeout_id) {
+		g_source_remove (client->timeout_id);
+		client->timeout_id = 0;
+	}
+}
+
+static gboolean
+connect_timeout (LmBsClient *client)
+{
+	g_return_val_if_fail (client != NULL, FALSE);
+
+	g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET,
+	       "Connect timeout, host: %s.\n", client->host);
+	lm_bs_client_stop (client);
+	connection_failed (client);
+	return FALSE;
+}
+
+static gboolean
+connection_failed (LmBsClient *client)
+{
+	LmCallback *cb;
+
+	g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET,
+	       "Connection to %s failed.\n", client->host);
+	client_remove_watch(client);
+	cb = client->disconnected_cb;
+	if (cb && cb->func) {
+		(* ((LmBsClientFunction) cb->func)) (client,
+						     cb->user_data);
+	}
+	return FALSE;
+}
+
+static gboolean 
+connection_succeeded (LmBsClient *client)
+{
+	
+	LmCallback *cb;
+	client_remove_watch (client);
+	if (client->status == C_STATUS_CONNECTING) {
+		client->status = C_STATUS_CONNECTED;
+	}
+	g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET,
+	       "Connection to %s succeeded.\n",
+	       client->host);
+	cb = client->connected_cb;
+	if (cb && cb->func) {
+		(* ((LmBsClientFunction) cb->func)) (client, 
+						     cb->user_data);
+	}
+	return FALSE;
+}
+
+static gboolean 
+client_get_connect_events (GIOChannel   *source,
+			   GIOCondition  condition,
+			   gpointer      user_data)
+{
+	LmBsClient *client;
+	gint        result;
+	gint        err;
+	socklen_t   len;
+
+	client = LM_BS_CLIENT (user_data);
+	if (condition == G_IO_ERR) { 
+		len = sizeof (err);
+		_lm_sock_get_error (client->fd, &err, &len);
+		if (!_lm_sock_is_blocking_error (err)) {
+			g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET,
+			       "Connection to %s %d failed.\n",
+			       client->host,
+			       (gint)client->port);
+			connection_failed (client);
+			client->io_watch = 0;
+			return FALSE;
+		}
+	}
+	if (client->status != C_STATUS_CONNECTING) {
+		return FALSE;
+	}
+	result = client_try_to_connect (client);
+	if (result < 0) {
+		err = _lm_sock_get_last_error ();
+		if (_lm_sock_is_blocking_success (err)) {
+			connection_succeeded (client);
+		}
+		if (client->status == C_STATUS_CONNECTING && 
+		    !_lm_sock_is_blocking_error (err)) {
+			g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET,
+			       "Connection to %s failed.\n",
+			       client->host);
+
+			_lm_sock_close (client->fd);
+			connection_failed (client);
+			client->io_watch = 0;
+			return FALSE;
+		}
+	} 
+	return TRUE; 
+}
+
+static gint
+client_try_to_connect (LmBsClient *client)
+{
+	gint result;
+	result = _lm_sock_connect (client->fd, 
+				   client->server_addr->ai_addr, 
+				   (int) client->server_addr->ai_addrlen);
+	return result;
+}
+
+static gboolean
+fill_server_address_info (LmBsClient *client)
+{
+	struct addrinfo  req;
+	struct addrinfo *ans;
+	int              result;
+
+	memset (&req, 0, sizeof(req));
+
+	req.ai_family   = AF_UNSPEC;
+	req.ai_socktype = SOCK_STREAM;
+	req.ai_protocol = IPPROTO_TCP;
+	result = getaddrinfo (_lm_utils_hostname_to_punycode (client->host), NULL, &req, &ans);
+	if (result != 0) {
+		g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET,
+		       "Unable to resolve hostname: %s.\n", client->host);
+		return FALSE;
+	}
+	((struct sockaddr_in *) ans->ai_addr)->sin_port = htons (client->port);
+	client->server_addr = ans;
+	return TRUE;
+}
+
+static void 
+free_client (LmBsClient *client)
+{
+	_lm_utils_free_callback (client->connected_cb);
+	_lm_utils_free_callback (client->disconnected_cb);
+	_lm_utils_free_callback (client->data_read_cb);
+	_lm_utils_free_callback (client->data_written_cb);
+
+	if (client->timeout_id != 0) {
+		g_source_remove (client->timeout_id);
+		client->timeout_id = 0;
+	}
+	lm_bs_client_stop (client);
+
+	freeaddrinfo (client->server_addr);
+	if (client->out_buf) {
+		g_string_free(client->out_buf, TRUE);
+	}
+	g_free(client->host);
+	g_free(client);
+}
+
+
+/**
+ * Creates a new instance of #LmBsClient . It is used to establish
+ * a client socket to a @host / @port .Use #lm_bs_client_connect to
+ * start the connect attempt and #lm_bs_client_stop to force a stop.
+ */
+LmBsClient * lm_bs_client_new (guint64      port,
+			       const gchar *host,
+			       LmCallback  *connected_cb,
+			       LmCallback  *disconnected_cb,
+			       LmCallback  *data_read_cb,
+			       LmCallback  *data_written_cb)
+{
+	LmBsClient *client;
+	
+	_lm_sock_library_init ();
+	lm_debug_init ();
+
+	client = g_new0 (LmBsClient, 1);
+
+	client->host              = g_strdup(host);
+	client->port              = port;
+	client->connected_cb      = connected_cb;
+	client->disconnected_cb   = disconnected_cb;
+	client->data_read_cb      = data_read_cb;
+	client->data_written_cb   = data_written_cb;
+	client->context           = NULL;
+	client->io_channel        = NULL;
+	client->fd                = -1;
+	client->timeout_id        = 0;
+	client->ref_count         = 1;
+	return client;
+}
+
+LmBsClient * lm_bs_client_new_from_fd (gint          fd,
+				       GMainContext *context)
+{
+	LmBsClient *client;
+	client = lm_bs_client_new (0, NULL, NULL, NULL, NULL, NULL);
+	client->fd = fd;
+	client->context = context;
+	client->io_channel = g_io_channel_unix_new (client->fd);
+	g_io_channel_set_encoding (client->io_channel, NULL, NULL);
+	g_io_channel_set_buffered (client->io_channel, FALSE);
+	
+	_lm_sock_set_blocking (client->fd, FALSE);
+	client->status = C_STATUS_CONNECTED;
+	return client;
+}
+
+LmBsClient *
+lm_bs_client_new_with_context (guint64       port,
+			       const gchar  *host,
+			       LmCallback   *connected_cb,
+			       LmCallback   *disconnected_cb,
+			       LmCallback   *data_read_cb,
+			       LmCallback   *data_written_cb,
+			       GMainContext *context)
+{
+	LmBsClient *client;
+
+	client = lm_bs_client_new(port, 
+				  host, 
+				  connected_cb, 
+				  disconnected_cb,
+				  data_read_cb,
+				  data_written_cb);
+	client->context = context;
+
+	return client;
+}
+
+
+guint
+lm_bs_client_connect (LmBsClient *client)
+{
+	GSource *source;
+
+	if (!fill_server_address_info(client)) {
+		g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET,
+		       "Invalid address %s.\n",
+		       client->host);
+		return -1;
+	}
+	g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_NET,
+	       "Establishing connection to %s.\n",
+	       client->host);
+	client->fd = _lm_sock_makesocket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
+	client->io_channel = g_io_channel_unix_new (client->fd);
+	g_io_channel_set_encoding (client->io_channel, NULL, NULL);
+	g_io_channel_set_buffered (client->io_channel, FALSE);
+	_lm_sock_set_blocking (client->fd, FALSE);
+	client->status = C_STATUS_CONNECTING;
+	client->io_watch = client_add_watch (client,
+					     client->io_channel,
+					     IO_CONNECT_FLAGS,
+				             (GIOFunc) client_get_connect_events);
+	source = g_timeout_source_new (CONNECT_TIMEOUT);
+	g_source_set_callback (source,
+			       (GSourceFunc) connect_timeout,
+			       client,
+			       NULL);
+	client->timeout_id = g_source_attach (source, client->context);
+	return 0;
+}
+
+guint
+lm_bs_client_stop (LmBsClient *client)
+{
+	client_remove_watch(client);
+	if (client->fd != -1) {
+		_lm_sock_shutdown (client->fd);
+		_lm_sock_close (client->fd);
+		client->fd = -1;
+	}
+	if (client->io_channel != NULL) {
+		g_io_channel_unref (client->io_channel);
+	}
+	client->io_channel = NULL;
+	client->status = C_STATUS_INTERRUPTED;
+	return 0;
+}
+
+LmBsClient*
+lm_bs_client_ref (LmBsClient *client)
+{
+	g_return_val_if_fail (client != NULL, NULL);
+
+	client->ref_count++;
+	
+	return client;
+}
+
+void
+lm_bs_client_unref (LmBsClient *client)
+{
+	g_return_if_fail (client != NULL);
+	
+	client->ref_count--;
+	
+	if (client->ref_count == 0) {
+		free_client (client);
+	}
+}
+
+
+gchar *
+lm_bs_client_get_host (LmBsClient *client)
+{
+	return client->host;
+}
+
+ClientStatus 
+lm_bs_client_get_status (LmBsClient *client)
+{
+	return client->status;
+}
+
+LmSocket 
+lm_bs_client_get_fd (LmBsClient *client)
+{
+	return client->fd;
+}
+
+
+void
+lm_bs_client_set_data_read_cb (LmBsClient *client,
+			       LmCallback *data_read_cb)
+{
+	if (client->data_read_cb) {
+		_lm_utils_free_callback(client->data_read_cb);
+	}
+	client->data_read_cb = data_read_cb;
+}
+
+void
+lm_bs_client_set_data_written_cb (LmBsClient *client,
+				  LmCallback *data_written_cb)
+{
+	if (client->data_written_cb) {
+		_lm_utils_free_callback(client->data_written_cb);
+	}
+	client->data_written_cb = data_written_cb;
+}
+
+void
+lm_bs_client_set_disconnected_cb (LmBsClient *client,
+				  LmCallback *disconnected_cb)
+{
+	if (client->disconnected_cb) {
+		_lm_utils_free_callback(client->disconnected_cb);
+	}
+	client->disconnected_cb = disconnected_cb;
+}
+
+void
+lm_bs_client_write_data (LmBsClient *client,
+			 GString    *data)
+{
+	client_remove_watch (client);
+	client->out_buf = data;
+	client->io_watch = client_add_watch (client,
+					     client->io_channel,
+					     IO_WRITE_FLAGS,
+				             (GIOFunc) write_events_cb);
+}
+
+void
+lm_bs_client_do_read (LmBsClient *client)
+{
+	client_remove_watch (client);
+	client->io_watch = client_add_watch (client,
+					     client->io_channel,
+					     IO_READ_FLAGS,
+				             (GIOFunc) read_events_cb);
+}
+
+void
+lm_bs_client_remove_watch (LmBsClient *client)
+{
+	client_remove_watch (client);
+}
+
diff -ura --new-file loudmouth.old/loudmouth/lm-bs-client.h loudmouth/loudmouth/lm-bs-client.h
--- loudmouth.old/loudmouth/lm-bs-client.h	1970-01-01 02:00:00.000000000 +0200
+++ loudmouth/loudmouth/lm-bs-client.h	2007-03-28 18:49:44.000000000 +0300
@@ -0,0 +1,87 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Free Software Foundation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * 
+ */
+
+#ifndef __LM_BS_CLIENT_H__
+#define __LM_BS_CLIENT_H__
+#include "lm-internals.h"
+
+#if !defined (LM_INSIDE_LOUDMOUTH_H) && !defined (LM_COMPILATION)
+#error "Only <loudmouth/loudmouth.h> can be included directly, this file may disappear or change contents."
+#endif
+
+
+G_BEGIN_DECLS
+
+#define LM_BS_CLIENT(obj) (LmBsClient *) obj;
+
+typedef struct _LmBsClient LmBsClient;
+
+typedef enum {
+	C_STATUS_INITIAL,
+	C_STATUS_CONNECTING,
+	C_STATUS_CONNECTED,
+	C_STATUS_AUTHENTICATED,
+	C_STATUS_INVALID,
+	C_STATUS_INTERRUPTED
+} ClientStatus;
+
+typedef void (* LmBsClientFunction)           (LmBsClient   *client,
+					      gpointer       user_data);
+typedef void (* LmBsClientReadFunction)       (LmBsClient   *client,
+					       gpointer      user_data,
+					       gpointer      user_data2);
+
+LmBsClient * lm_bs_client_new                 (guint64       port,
+					       const gchar  *host,
+					       LmCallback  *connected_cb,
+					       LmCallback   *disconnected_cb,
+					       LmCallback   *data_read_cb,
+					       LmCallback   *data_written_cb);
+LmBsClient * lm_bs_client_new_from_fd         (gint fd, 
+					       GMainContext *context);
+LmBsClient * lm_bs_client_new_with_context    (guint64       port,
+					       const gchar  *host,
+					       LmCallback   *connected_cb,
+					       LmCallback   *disconnected_cb,
+					       LmCallback   *data_read_cb,
+					       LmCallback   *data_written_cb,
+					       GMainContext *context);
+guint        lm_bs_client_connect             (LmBsClient   *client);
+guint        lm_bs_client_stop                (LmBsClient   *client);
+LmBsClient * lm_bs_client_ref                 (LmBsClient   *client);
+void         lm_bs_client_unref               (LmBsClient   *client);
+gchar *      lm_bs_client_get_host            (LmBsClient   *client);
+LmSocket     lm_bs_client_get_fd              (LmBsClient   *client);
+ClientStatus lm_bs_client_get_status          (LmBsClient   *client);
+void         lm_bs_client_set_data_read_cb    (LmBsClient   *client, 
+					       LmCallback   *data_read_cb);
+void         lm_bs_client_set_data_written_cb (LmBsClient   *client, 
+					       LmCallback   *data_written_cb);
+void         lm_bs_client_set_disconnected_cb (LmBsClient   *client, 
+					       LmCallback   *disconnected_cb);
+void         lm_bs_client_write_data          (LmBsClient   *client, 
+					       GString      *data);
+void         lm_bs_client_do_read             (LmBsClient   *client);
+void         lm_bs_client_remove_watch        (LmBsClient   *client);
+
+G_END_DECLS
+
+#endif /* __LM_BS_CLIENT_H__ */
diff -ura --new-file loudmouth.old/loudmouth/lm-bs-listener.c loudmouth/loudmouth/lm-bs-listener.c
--- loudmouth.old/loudmouth/lm-bs-listener.c	1970-01-01 02:00:00.000000000 +0200
+++ loudmouth/loudmouth/lm-bs-listener.c	2007-03-28 18:49:44.000000000 +0300
@@ -0,0 +1,294 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Free Software Foundation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * 
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+#include <glib.h>
+
+#include "lm-sock.h"
+#include "lm-debug.h"
+#include "lm-internals.h"
+#include "lm-connection.h"
+#include "lm-utils.h"
+
+#include "lm-bs-client.h"
+#include "lm-bs-listener.h"
+
+#define IO_READ_FLAGS G_IO_IN|G_IO_PRI|G_IO_HUP
+
+struct _LmBsListener {
+	GMainContext *context;
+	guint         port;
+
+	LmCallback   *new_client_cb;
+	LmCallback   *disconnected_cb;
+	LmSocket      fd;
+	GIOChannel   *io_channel;
+	guint         io_watch;
+	gint          ref_count;
+};
+
+static void     listener_free                 (LmBsListener *listener);
+static guint    listener_add_watch            (LmBsListener *listener,
+					       GIOChannel   *channel,
+					       GIOCondition  condition,
+					       GIOFunc       func);
+static gboolean listener_accept_connection_cb (GIOChannel   *source, 
+                                               GIOCondition  condition,
+                                               gpointer      user_data);
+static gboolean listener_start_listen         (LmBsListener *listener);
+static void     listener_bind                 (LmBsListener *listener,
+					       GError **error);
+static void     listener_create               (LmBsListener *listener);
+
+static void
+listener_free (LmBsListener *listener)
+{
+	_lm_utils_free_callback (listener->disconnected_cb);
+	lm_bs_listener_stop (listener);
+	if (listener->context != NULL) {
+		g_main_context_unref (listener->context);
+	}
+	g_free (listener);
+}
+
+static guint
+listener_add_watch (LmBsListener *listener,
+		    GIOChannel   *channel,
+		    GIOCondition  condition,
+		    GIOFunc       func)
+{
+	GSource *source;
+	guint    id;
+
+	g_return_val_if_fail (channel != NULL, 0);
+
+	source = g_io_create_watch (channel, condition);
+
+	g_source_set_callback (source, (GSourceFunc)func, listener, NULL);
+
+	id = g_source_attach (source, listener->context);
+
+	g_source_unref (source);
+  
+	return id;
+}
+
+static gboolean listener_accept_connection_cb(GIOChannel   *source, 
+					       GIOCondition  condition,
+					       gpointer      user_data)
+{
+	LmBsListener *listener;
+	gint          new_fd;
+
+	listener = LM_BS_LISTENER (user_data);
+
+	new_fd = accept (listener->fd, NULL, NULL);
+	if (new_fd != -1) {
+		LmCallback *cb = listener->new_client_cb;
+		if (cb) {
+			(* ((LmBsNewClientFunction) cb->func)) ((guint) new_fd,
+								cb->user_data);
+		}
+	} 
+	return TRUE;
+}
+
+
+static gboolean
+listener_start_listen (LmBsListener *listener)
+{
+	int result;
+
+	listener->io_channel = g_io_channel_unix_new (listener->fd);
+	g_io_channel_set_encoding (listener->io_channel, NULL, NULL);
+	g_io_channel_set_buffered (listener->io_channel, FALSE);
+	_lm_sock_set_blocking (listener->fd, FALSE);
+	listener->io_watch = listener_add_watch (listener,
+						 listener->io_channel,
+						 IO_READ_FLAGS,
+						 (GIOFunc) listener_accept_connection_cb);
+	result = listen (listener->fd, SOMAXCONN);
+	return (result != -1);
+}
+
+static void 
+listener_bind (LmBsListener *listener, GError **error)
+{
+
+	struct sockaddr_in  serveraddr;
+	int                 serveraddr_len;
+	int                 result;
+
+	serveraddr_len = sizeof(serveraddr);
+	memset (&serveraddr, 0, serveraddr_len);
+	serveraddr.sin_family = PF_INET;
+	serveraddr.sin_addr.s_addr =  htonl (INADDR_ANY);
+
+	result = bind (listener->fd,
+		       (struct sockaddr *)&serveraddr,
+		       serveraddr_len);
+	memset (&serveraddr, 0, serveraddr_len);
+	/* get info on the bound port */
+	if (getsockname (listener->fd,
+			 (struct sockaddr *)&serveraddr, 
+			 (socklen_t *)&serveraddr_len)) {
+		return;
+	}
+	listener->port = ntohs (serveraddr.sin_port);
+}
+
+static void 
+listener_create (LmBsListener *listener)
+{
+	int flag;
+
+	listener->fd = _lm_sock_makesocket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
+	flag = 1;
+	setsockopt (listener->fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int));
+	// remove the Nagle algorhytm, which improves the speed of sending data.
+	setsockopt (listener->fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(int));
+}
+
+LmBsListener *
+lm_bs_listener_new ()
+{
+	LmBsListener *listener;
+	
+	_lm_sock_library_init ();
+
+	listener = g_new0 (LmBsListener, 1);
+
+	listener->port              = 0;
+	listener->disconnected_cb   = NULL;
+	listener->new_client_cb     = NULL;
+	listener->fd                = -1;
+
+	listener->ref_count         = 1;
+
+	return listener;
+}
+
+LmBsListener *
+lm_bs_listener_new_with_context (GMainContext *context)
+{
+	LmBsListener *listener;
+	
+	listener = lm_bs_listener_new();
+	listener->context = context;
+	if (context != NULL) {
+		g_main_context_ref (context);
+	}
+
+	return listener;
+}
+
+void
+lm_bs_listener_set_new_client_function (LmBsListener          *listener,
+					LmBsNewClientFunction  function,
+					gpointer               user_data,
+					GDestroyNotify         notify)
+{
+	g_return_if_fail (listener != NULL);
+
+	if (listener->new_client_cb) {
+		_lm_utils_free_callback (listener->new_client_cb);
+	}
+
+	if (function) {
+		listener->new_client_cb = _lm_utils_new_callback (function,
+								  user_data,
+								  notify);
+	} else {
+		listener->new_client_cb = NULL;
+	}
+}
+
+
+guint
+lm_bs_listener_start (LmBsListener *listener)
+{
+
+	listener_create (listener);
+	listener_bind (listener, NULL);
+	if (listener_start_listen (listener)) {
+		return listener->port;
+	}
+	return -1;
+}
+
+guint
+lm_bs_listener_get_port(LmBsListener *listener)
+{
+	return listener->port;
+}
+
+
+void
+lm_bs_listener_stop (LmBsListener *listener)
+{
+	LmCallback *cb;
+	GSource    *source;
+
+	cb = listener->disconnected_cb;
+	_lm_utils_free_callback (listener->new_client_cb);
+	if (listener->io_watch != 0) {
+		source = g_main_context_find_source_by_id (listener->context,
+							   listener->io_watch);
+		if (source) {
+			g_source_destroy (source);
+		}
+		listener->io_watch = 0;
+	}
+	if (listener->fd != -1) {
+		_lm_sock_shutdown (listener->fd);
+		_lm_sock_close (listener->fd);
+		listener->fd = -1;
+	}
+	if (cb && cb->func) {
+		(* ((LmBsClientFunction) cb->func)) (NULL,
+						     cb->user_data);
+	}
+}
+
+LmBsListener*
+lm_bs_listener_ref (LmBsListener *listener)
+{
+	g_return_val_if_fail (listener != NULL, NULL);
+	
+	listener->ref_count++;
+	
+	return listener;
+}
+
+void
+lm_bs_listener_unref (LmBsListener *listener)
+{
+	g_return_if_fail (listener != NULL);
+	listener->ref_count--;
+	if (listener->ref_count == 0) {
+		listener_free (listener);
+	}
+}
diff -ura --new-file loudmouth.old/loudmouth/lm-bs-listener.h loudmouth/loudmouth/lm-bs-listener.h
--- loudmouth.old/loudmouth/lm-bs-listener.h	1970-01-01 02:00:00.000000000 +0200
+++ loudmouth/loudmouth/lm-bs-listener.h	2007-03-28 18:49:44.000000000 +0300
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Free Software Foundation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * 
+ */
+
+#ifndef __LM_BS_LISTENER_H__
+#define __LM_BS_LISTENER_H__
+
+#if !defined (LM_INSIDE_LOUDMOUTH_H) && !defined (LM_COMPILATION)
+#error "Only <loudmouth/loudmouth.h> can be included directly, this file may disappear or change contents."
+#endif
+
+G_BEGIN_DECLS
+
+#define LM_BS_LISTENER(obj) (LmBsListener *) obj;
+
+typedef struct  _LmBsListener LmBsListener;
+
+typedef void    (* LmBsNewClientFunction)              (guint           fd,
+						        gpointer        user_data);
+
+LmBsListener *  lm_bs_listener_new                     (void);
+LmBsListener *  lm_bs_listener_new_with_context        (GMainContext   *context);
+void            lm_bs_listener_set_new_client_function (LmBsListener   *listener,
+							LmBsNewClientFunction func,
+							gpointer        user_data,
+							GDestroyNotify  notify);
+guint           lm_bs_listener_start                   (LmBsListener   *listener);
+void            lm_bs_listener_stop                    (LmBsListener   *listener);
+guint           lm_bs_listener_get_port                (LmBsListener   *listener);
+LmBsListener *  lm_bs_listener_ref                     (LmBsListener   *listener);
+void            lm_bs_listener_unref                   (LmBsListener   *listener);
+
+G_END_DECLS
+
+#endif /* __LM_BS_LISTENER_H__ */
diff -ura --new-file loudmouth.old/loudmouth/lm-bs-receiver.c loudmouth/loudmouth/lm-bs-receiver.c
--- loudmouth.old/loudmouth/lm-bs-receiver.c	1970-01-01 02:00:00.000000000 +0200
+++ loudmouth/loudmouth/lm-bs-receiver.c	2007-03-28 18:49:44.000000000 +0300
@@ -0,0 +1,230 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Free Software Foundation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * 
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "lm-sock.h"
+#include "lm-debug.h"
+#include "lm-bs-transfer.h"
+#include "lm-bs-client.h"
+#include "lm-bs-receiver.h"
+
+#define IO_READ_FLAGS G_IO_IN|G_IO_PRI|G_IO_HUP
+#define IO_WRITE_FLAGS G_IO_OUT|G_IO_ERR
+
+struct _LmBsReceiver {
+	LmBsClient         *client;
+	LmBsTransfer       *transfer;
+	const gchar        *append_data;
+	CommunicationState  state;
+	gchar              *jid_used;
+	gint                ref_count;
+};
+
+static void  client_disconnected (LmBsClient *client, LmBsReceiver *receiver);
+static void  fill_request_buf    (LmBsClient *client,
+				  GString **buf,
+				  const gchar* msg);
+static void  data_read_cb        (LmBsClient *client,
+				  LmBsReceiver *receiver,
+				  GString *data);
+static void  data_written_cb     (LmBsClient *client, LmBsReceiver *receiver);
+
+static void
+client_disconnected (LmBsClient *client, LmBsReceiver *receiver)
+{
+	g_return_if_fail (client != NULL);
+	g_return_if_fail (receiver != NULL);
+	g_return_if_fail (receiver->transfer != NULL);
+
+	lm_bs_client_unref (client);
+	TransferStatus status = lm_bs_transfer_get_status (receiver->transfer);
+
+	if (status != T_STATUS_COMPLETED) {
+		lm_bs_transfer_error (receiver->transfer,
+				      _("Transfer stopped"));
+	}
+	lm_bs_transfer_close_file (receiver->transfer);
+}
+
+static void 
+fill_request_buf (LmBsClient *client, 
+		  GString **buf, 
+		  const gchar* msg)
+{
+	*buf = g_string_new_len ("\x05\x01\x00\x03", 4);
+	g_string_append_printf (*buf, "%c%s", strlen (msg), msg);
+	g_string_append_len (*buf, "\x00\x00", 2);
+}
+
+static void 
+data_read_cb (LmBsClient *client, LmBsReceiver *receiver, GString *data)
+{
+	GString      *send_data;
+	LmBsTransfer *transfer;
+	gchar        *bytes;
+	gchar        *auth_sha;
+	guint         addrlen;
+	gboolean      result;
+
+	send_data = NULL;
+	bytes = data->str;
+
+	transfer = receiver->transfer;
+
+	switch (receiver->state) {
+		case CS_AUTH_SENT:
+			if (data->len < 2) {
+				lm_bs_receiver_unref (receiver);
+				lm_bs_transfer_error (transfer,
+						      _("Protocol error"));
+				return;
+			}
+			receiver->state = CS_AUTH_REPLIED;
+			if (bytes[0] != '\x05' || bytes[1] == '\xff') {
+				lm_bs_receiver_unref (receiver);
+				lm_bs_transfer_error (transfer,
+						      _("Protocol error"));
+				return;
+			}
+			auth_sha = lm_bs_transfer_get_auth_sha (transfer);
+			fill_request_buf (client,
+					  &send_data,
+					  auth_sha);
+			g_free (auth_sha);
+			break;
+		case CS_CONNECT_REQUEST_SENT:
+			if (data->len <= 4 ) {
+				lm_bs_receiver_unref (receiver);
+				lm_bs_transfer_error (transfer,
+						      _("Protocol error"));
+				return;
+			}
+			receiver->state = CS_TRANSFER_STARTED;
+			if (bytes[3] == '\x03') {
+				addrlen = (guint) bytes[4];
+				gchar* addres = bytes + 5;
+				addres[addrlen] = '\0';
+				if (data->len - addrlen - 6 < 0) {
+					lm_bs_receiver_unref (receiver);
+					lm_bs_transfer_error (transfer,
+						      _("Protocol error"));
+					return;
+				}
+			}
+			lm_bs_transfer_send_success_reply (transfer,
+							   receiver->jid_used);
+			break;
+		case CS_TRANSFER_STARTED:
+			lm_bs_transfer_ref (transfer);
+			result = lm_bs_transfer_append_to_file (transfer,
+								data);
+			if (!result) {
+				lm_bs_receiver_unref (receiver);
+			}
+			lm_bs_transfer_unref (transfer);
+			break;
+		default:
+			g_assert_not_reached ();
+	}
+	if (send_data) {
+		lm_bs_client_write_data (receiver->client, send_data);
+	}
+}
+
+static void 
+data_written_cb (LmBsClient *client, LmBsReceiver *receiver)
+{
+	switch (receiver->state) {
+		case CS_INITIAL:
+			receiver->state = CS_AUTH_SENT;
+			break;
+		case CS_AUTH_REPLIED:
+			receiver->state = CS_CONNECT_REQUEST_SENT;
+			break;
+		default:
+			g_assert_not_reached ();
+	}
+	lm_bs_client_do_read(client);
+}
+
+LmBsReceiver *
+lm_bs_receiver_new (LmBsClient   *client,
+		    LmBsTransfer *transfer,
+		    const gchar  *jid_used)
+{
+	LmBsReceiver           *receiver;
+	LmBsClientReadFunction  read_func;
+	LmBsClientFunction      func;
+	LmCallback             *read_cb;
+	LmCallback             *written_cb;
+	LmCallback             *discon_cb;
+
+	_lm_sock_library_init ();
+	lm_debug_init ();
+	receiver = g_new0 (LmBsReceiver, 1);
+	receiver->client = client;
+	lm_bs_client_ref (client);
+	receiver->transfer = transfer;
+	receiver->jid_used = g_strdup (jid_used);
+
+	read_func = (LmBsClientReadFunction) data_read_cb;
+	read_cb = _lm_utils_new_callback (read_func, receiver, NULL);
+	lm_bs_client_set_data_read_cb (client, read_cb);
+
+
+	func = (LmBsClientFunction) data_written_cb;
+	written_cb = _lm_utils_new_callback (func, receiver, NULL);
+	lm_bs_client_set_data_written_cb (client, written_cb);
+	
+	func = (LmBsClientFunction) client_disconnected;
+	discon_cb = _lm_utils_new_callback (func, receiver, NULL);
+	lm_bs_client_set_disconnected_cb (client, discon_cb);
+
+	receiver->ref_count = 1;
+	return receiver;
+}
+
+void
+lm_bs_receiver_unref (LmBsReceiver *receiver)
+{
+
+	g_return_if_fail (receiver != NULL);
+
+	receiver->ref_count--;
+	if (receiver->ref_count == 0) {
+		lm_bs_client_unref (receiver->client);
+		g_free (receiver->jid_used);
+		g_free (receiver);
+	}
+}
+
+void
+lm_bs_receiver_start_transfer (LmBsReceiver *receiver)
+{
+	receiver->state = CS_INITIAL;
+	lm_bs_client_write_data (receiver->client,
+				 g_string_new_len ("\x05\x01\x00", 3));
+}
diff -ura --new-file loudmouth.old/loudmouth/lm-bs-receiver.h loudmouth/loudmouth/lm-bs-receiver.h
--- loudmouth.old/loudmouth/lm-bs-receiver.h	1970-01-01 02:00:00.000000000 +0200
+++ loudmouth/loudmouth/lm-bs-receiver.h	2007-03-28 18:49:44.000000000 +0300
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Free Software Foundation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * 
+ */
+
+#ifndef __LM_BS_RECEIVER_H__
+#define __LM_BS_RECEIVER_H__
+
+#if !defined (LM_INSIDE_LOUDMOUTH_H) && !defined (LM_COMPILATION)
+#error "Only <loudmouth/loudmouth.h> can be included directly, this file may disappear or change contents."
+#endif
+
+G_BEGIN_DECLS
+
+#define LM_BS_RECEIVER(obj)  (LmBsReceiver *) obj;
+
+typedef struct _LmBsReceiver LmBsReceiver;
+
+typedef enum {
+	CS_INITIAL,
+	CS_AUTH_SENT,
+	CS_AUTH_REPLIED,
+	CS_CONNECT_REQUEST_SENT,
+	CS_CONNECT_REQUEST_REPLIED,
+	CS_TRANSFER_STARTED
+} CommunicationState;
+
+LmBsReceiver *  lm_bs_receiver_new            (LmBsClient *client, 
+					       LmBsTransfer *transfer,
+					       const gchar *jid_used);
+void            lm_bs_receiver_unref          (LmBsReceiver *receiver);
+void            lm_bs_receiver_start_transfer (LmBsReceiver *receiver);
+
+G_END_DECLS
+
+#endif /* __LM_BS_RECEIVER_H__  */
+
diff -ura --new-file loudmouth.old/loudmouth/lm-bs-sender.c loudmouth/loudmouth/lm-bs-sender.c
--- loudmouth.old/loudmouth/lm-bs-sender.c	1970-01-01 02:00:00.000000000 +0200
+++ loudmouth/loudmouth/lm-bs-sender.c	2007-03-28 18:49:44.000000000 +0300
@@ -0,0 +1,314 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Free Software Foundation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * 
+ */
+
+
+#include <config.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <string.h>
+
+#include "lm-sock.h"
+#include "lm-debug.h"
+#include "lm-bs-session.h"
+#include "lm-bs-transfer.h"
+#include "lm-bs-client.h"
+#include "lm-bs-sender.h"
+
+#define IO_READ_FLAGS G_IO_IN|G_IO_PRI|G_IO_HUP
+#define IO_WRITE_FLAGS G_IO_OUT|G_IO_ERR
+
+struct _LmBsSender {
+	LmBsSession   *session;
+	LmBsClient    *client;
+	LmBsTransfer  *transfer;
+	const gchar   *append_data;
+	SenderState    state;
+	gchar         *jid_used;
+
+	gint           ref_count;
+};
+
+static void destroy_sender      (LmBsSender *sender);
+static void write_next          (LmBsSender *sender);
+static void data_written_cb     (LmBsClient *client, LmBsSender *sender);
+static void client_disconnected (LmBsClient *client, LmBsSender *sender);
+static void match_sha           (LmBsSender *sender, const gchar* sha);
+static void fill_request_buf    (LmBsClient *client,
+				 GString **buf,
+				 const gchar* msg);
+static void data_read_cb        (LmBsClient *client,
+				 LmBsSender *sender,
+				 GString *data);
+static void activate_streamhost (gpointer ptr, LmBsSender *sender);
+
+
+static void
+destroy_sender (LmBsSender *sender)
+{
+	guint fd;
+
+	fd = lm_bs_client_get_fd (sender->client);
+	_lm_bs_session_remove_sender (sender->session, fd);
+}
+
+
+static void
+write_next (LmBsSender *sender)
+{
+	GString      *data;
+	LmBsTransfer *transfer;
+	gboolean      result;
+
+	transfer = sender->transfer;
+	g_return_if_fail (transfer != NULL);
+
+	data = NULL;
+	lm_bs_transfer_ref (transfer);
+	result = lm_bs_transfer_get_file_content (sender->transfer, &data);
+	if (!result) {
+		lm_bs_sender_unref (sender);
+		data = NULL;
+	}
+	if (data != NULL && data->len) {
+		lm_bs_client_write_data (sender->client, data);
+	}
+	lm_bs_transfer_unref (transfer);
+}
+
+static void
+data_written_cb (LmBsClient *client, LmBsSender *sender)
+{
+	switch (sender->state) {
+		case SS_AUTH_READ:
+			sender->state = SS_AUTH_REPLIED;
+			break;
+		case SS_CONNECT_REQUEST_READ:
+			sender->state = SS_CONNECT_REQUEST_REPLIED;
+			lm_bs_client_remove_watch (client);
+			return;
+		case SS_TRANSFER_STARTED:
+			write_next (sender);
+			return;
+		default:
+			break;
+	}
+	lm_bs_client_do_read (client);
+}
+
+static void
+client_disconnected (LmBsClient *client, LmBsSender *sender)
+{
+	TransferStatus  status;
+	LmBsTransfer   *transfer;
+
+	transfer = sender->transfer;
+	if (transfer != NULL) {
+		lm_bs_transfer_close_file (sender->transfer);
+		status = lm_bs_transfer_get_status (sender->transfer);
+		if (status != T_STATUS_COMPLETED) {
+			destroy_sender (sender);
+			lm_bs_transfer_error (sender->transfer,
+					      _("Transfer stopped"));
+			return;
+		}
+	}
+	destroy_sender (sender);
+}
+
+static void
+match_sha (LmBsSender *sender, const gchar* sha)
+{
+	guint fd;
+
+	fd = lm_bs_client_get_fd (sender->client);
+	_lm_bs_session_match_sha (sender->session, sha, fd);
+}
+
+static void 
+fill_request_buf (LmBsClient *client,
+		  GString **buf,
+		  const gchar* msg)
+{
+	*buf = g_string_new_len ("\x05\x00\x00\x03", 4);
+	g_string_append_printf (*buf, "%c%s", strlen (msg), msg);
+	g_string_append_len (*buf, "\x00\x00", 2);
+}
+
+static void 
+data_read_cb (LmBsClient *client, LmBsSender *sender, GString *data)
+{
+	GString *send_data;
+	gchar   *bytes;
+	gchar    ver;
+	gchar    num_auth;
+	gchar    host_type;
+	gchar   *host_addr;
+	guint    addrlen;
+
+	send_data = NULL;
+	bytes = data->str;
+
+	switch (sender->state) {
+		case SS_INITIAL:
+			if (data->len < 3) {
+				/* no auth mechanisms supported */
+				return destroy_sender (sender);
+			}
+			ver = bytes[0];
+			num_auth = bytes[1];
+			if (ver != '\x05') {
+				return destroy_sender (sender);
+			}
+			if ((gint) num_auth < 1) {
+				/* zero auth mechanisms supported */
+				return destroy_sender (sender);
+			}
+			send_data = g_string_new_len ("\x05\x00", 2);
+			sender->state = SS_AUTH_READ;
+			break;
+		case SS_AUTH_REPLIED:
+			if (data->len < 5) {
+				/* wrong connect request */
+				return destroy_sender (sender);
+			}
+			if (bytes[1] != '\x01') {
+				/* request type is not 'connect' */
+				return destroy_sender (sender);
+			}
+			host_type = bytes[3];
+			if (host_type == '\x01') {
+				if (data->len < 8) {
+					return destroy_sender (sender);
+				}
+				host_addr = g_strdup_printf ("%d.%d.%d.%d", 
+							     (guint) bytes[4],
+							     (guint) bytes[5],
+							     (guint) bytes[6],
+							     (guint) bytes[7]);
+				addrlen = strlen (host_addr);
+			} else if (host_type == '\x03') {
+				addrlen = (guint) bytes[4];
+				if (data->len < addrlen + 5) {
+					return destroy_sender (sender);
+				}
+				host_addr = bytes + 5;
+				host_addr[addrlen] = '\0';
+				host_addr = g_strdup (host_addr);
+			} else {
+				/*  unknown host type */
+				return destroy_sender (sender);
+			}
+			match_sha (sender, host_addr);
+			g_free (host_addr);
+			/* lm_bs_client_remove_watch (client); */
+			sender->state = SS_CONNECT_REQUEST_READ;
+			break;
+		default:
+			g_assert_not_reached ();
+	}
+	if (send_data) {
+		lm_bs_client_write_data (sender->client, send_data);
+	}
+}
+
+static void
+activate_streamhost (gpointer ptr, LmBsSender *sender)
+{
+	sender->state = SS_TRANSFER_STARTED;
+	write_next (sender);
+}
+
+LmBsSender *
+lm_bs_sender_new (LmBsClient *client, LmBsSession *session)
+{
+	LmBsSender             *sender;
+	LmBsClientReadFunction  read_func;
+	LmBsClientFunction      func;
+	LmCallback             *read_cb;
+	LmCallback             *written_cb;
+	LmCallback             *discon_cb;
+
+	_lm_sock_library_init ();
+	lm_debug_init ();
+	sender = g_new0 (LmBsSender, 1);
+	sender->client = client;
+	sender->transfer = NULL;
+	sender->jid_used = NULL;
+	sender->state = SS_INITIAL;
+	sender->session = session;
+
+	read_func = (LmBsClientReadFunction) data_read_cb;
+	read_cb = _lm_utils_new_callback (read_func, sender, NULL);
+	lm_bs_client_set_data_read_cb (client, read_cb);
+
+
+	func = (LmBsClientFunction) data_written_cb;
+	written_cb = _lm_utils_new_callback (func, sender, NULL);
+	lm_bs_client_set_data_written_cb (client, written_cb);
+	
+	func = (LmBsClientFunction) client_disconnected;
+	discon_cb = _lm_utils_new_callback (func, sender, NULL);
+	lm_bs_client_set_disconnected_cb (client, discon_cb);
+
+	sender->ref_count = 1;
+	lm_bs_client_do_read (client);
+	return sender;
+}
+
+LmBsSender *
+lm_bs_sender_ref (LmBsSender *sender)
+{
+	g_return_val_if_fail (sender != NULL, NULL);
+	sender->ref_count++;
+	return sender;
+}
+
+void
+lm_bs_sender_unref (LmBsSender *sender)
+{
+	g_return_if_fail (sender != NULL);
+
+	sender->ref_count--;
+	if (sender->ref_count == 0) {
+		lm_bs_client_unref (sender->client);
+		g_free (sender->jid_used);
+		g_free (sender);
+	}
+}
+
+void
+lm_bs_sender_set_transfer (LmBsSender *sender, LmBsTransfer *transfer)
+{
+	GString            *send_data;
+	gchar              *auth_sha;
+	LmBsClientFunction  func;
+	LmCallback         *activate_cb;
+
+	sender->transfer = transfer;
+	func = (LmBsClientFunction) activate_streamhost;
+	activate_cb = _lm_utils_new_callback (func, sender, NULL);
+	lm_bs_transfer_set_activate_cb (transfer, activate_cb);
+	auth_sha = lm_bs_transfer_get_auth_sha (transfer);
+	fill_request_buf (sender->client, &send_data, auth_sha);
+	g_free (auth_sha);
+	lm_bs_client_write_data (sender->client, send_data);
+}
diff -ura --new-file loudmouth.old/loudmouth/lm-bs-sender.h loudmouth/loudmouth/lm-bs-sender.h
--- loudmouth.old/loudmouth/lm-bs-sender.h	1970-01-01 02:00:00.000000000 +0200
+++ loudmouth/loudmouth/lm-bs-sender.h	2007-03-28 18:49:44.000000000 +0300
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Free Software Foundation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * 
+ */
+
+#ifndef __LM_BS_SENDER_H__
+#define __LM_BS_SENDER_H__
+
+#if !defined (LM_INSIDE_LOUDMOUTH_H) && !defined (LM_COMPILATION)
+#error "Only <loudmouth/loudmouth.h> can be included directly, this file may disappear or change contents."
+#endif
+
+
+G_BEGIN_DECLS
+
+#define LM_BS_SENDER(obj) (LmBsSender *) obj;
+
+typedef struct _LmBsSender LmBsSender;
+
+typedef enum {
+	SS_INITIAL,
+	SS_AUTH_READ,
+	SS_AUTH_REPLIED,
+	SS_CONNECT_REQUEST_READ,
+	SS_CONNECT_REQUEST_REPLIED,
+	SS_TRANSFER_STARTED
+} SenderState;
+
+LmBsSender * lm_bs_sender_new          (LmBsClient *client,
+					LmBsSession *session);
+LmBsSender * lm_bs_sender_ref          (LmBsSender *sender);
+void         lm_bs_sender_unref        (LmBsSender *sender);
+void         lm_bs_sender_set_transfer (LmBsSender *sender,
+					LmBsTransfer *transfer);
+
+G_END_DECLS
+
+#endif /* __LM_BS_SENDER_H__  */
+
diff -ura --new-file loudmouth.old/loudmouth/lm-bs-session.c loudmouth/loudmouth/lm-bs-session.c
--- loudmouth.old/loudmouth/lm-bs-session.c	1970-01-01 02:00:00.000000000 +0200
+++ loudmouth/loudmouth/lm-bs-session.c	2007-03-28 18:49:44.000000000 +0300
@@ -0,0 +1,587 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Free Software Foundation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * 
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <glib.h>
+
+#include "lm-debug.h"
+#include "lm-internals.h"
+#include "lm-connection.h"
+#include "lm-parser.h"
+#include "lm-utils.h"
+
+#include "lm-bs-listener.h"
+#include "lm-bs-client.h"
+#include "lm-bs-transfer.h"
+#include "lm-bs-receiver.h"
+#include "lm-bs-sender.h"
+#include "lm-bs-session.h"
+
+#define PROFILE_FILE_TRANSFER   "http://jabber.org/protocol/si/profile/file-transfer"
+#define XMLNS_SI                "http://jabber.org/protocol/si"
+#define XMLNS_FEATURE_NEG       "http://jabber.org/protocol/feature-neg"
+#define XMLNS_X_DATA            "jabber:x:data"
+
+
+struct _LmBsSession {
+	GMainContext *context;
+	GHashTable   *float_clients;
+	GHashTable   *float_shas;
+	GHashTable   *iq_ids;
+	GHashTable   *transfers;
+	LmBsListener *listener;
+
+	LmCallback   *complete_cb;
+	LmCallback   *progress_cb;
+	LmCallback   *failure_cb;
+	
+	gint         ref_count;
+};
+
+static void session_free         (LmBsSession *session);
+static void new_client_connected (guint        fd,
+				  LmBsSession *session);
+
+
+static void
+session_free (LmBsSession *session)
+{
+	_lm_utils_free_callback (session->complete_cb);
+	_lm_utils_free_callback (session->progress_cb);
+	_lm_utils_free_callback (session->failure_cb);
+
+	g_hash_table_destroy (session->float_clients);
+	g_hash_table_destroy (session->float_shas);
+	g_hash_table_destroy (session->iq_ids);
+	g_hash_table_destroy (session->transfers);
+
+	if (session->listener) {
+		lm_bs_listener_unref (session->listener);
+		session->listener = NULL;
+	}
+	g_free (session);
+}
+
+static void 
+new_client_connected (guint fd, LmBsSession *session)
+{
+	LmBsClient *client;
+	LmBsSender *sender;
+
+	client = lm_bs_client_new_from_fd (fd, session->context);
+	sender = lm_bs_sender_new (client, session);
+	
+	g_hash_table_insert (session->float_clients,
+			     GUINT_TO_POINTER(fd),
+			     sender);
+}
+
+void 
+_lm_bs_session_remove_sender (LmBsSession *session, guint fd)
+{
+	g_hash_table_remove (session->float_clients, GUINT_TO_POINTER(fd));
+}
+
+void
+_lm_bs_session_match_sha (LmBsSession *session, const gchar *sha, guint fd)
+{
+	gpointer      id_ptr;
+	LmBsSender   *sender;
+	LmBsTransfer *transfer;
+
+	g_return_if_fail (session != NULL);
+	g_return_if_fail (session->float_clients != NULL);
+	sender = g_hash_table_lookup (session->float_clients,
+				      GUINT_TO_POINTER(fd));
+	g_return_if_fail (sender != NULL);
+
+	id_ptr = g_hash_table_lookup (session->float_shas, sha);
+	g_hash_table_remove (session->float_shas, sha);
+	if (!id_ptr) {
+		_lm_bs_session_remove_sender (session, fd);
+		return;
+	}
+	transfer = g_hash_table_lookup (session->transfers, id_ptr);
+	g_return_if_fail (transfer != NULL);
+	lm_bs_sender_set_transfer (lm_bs_sender_ref(sender), transfer);
+	_lm_bs_session_remove_sender (session, fd);
+}
+
+/**
+ * lm_bs_session_start_listener:
+ * @session: the bytestream session object
+ * 
+ * Starts listening on a server socket. When there are no more
+ * active transfers the socket will be closed automatically.
+ *
+ * Return value: the port of the server socket
+ **/
+guint
+lm_bs_session_start_listener (LmBsSession *session)
+{
+	if (session->listener) {
+		return lm_bs_listener_get_port (session->listener);
+	}
+	session->listener = lm_bs_listener_new_with_context (session->context);
+	lm_bs_listener_set_new_client_function (session->listener,
+						(LmBsNewClientFunction) new_client_connected,
+						session,
+						NULL);
+	return lm_bs_listener_start (session->listener);
+}
+
+/**
+ * lm_bs_session_new:
+ * @context: main context to be used for io events , could be NULL
+ * 
+ * Creates a new instance of #LmBsSession.
+ *
+ * Return value: the newly created instance
+ **/
+LmBsSession *
+lm_bs_session_new (GMainContext *context) {
+	LmBsSession *session = NULL;
+
+	GDestroyNotify destroy_transfer;
+	GDestroyNotify destroy_sender;
+	
+	session = g_new0 (LmBsSession, 1);
+	destroy_transfer = (GDestroyNotify) lm_bs_transfer_unref;
+	destroy_sender = (GDestroyNotify) lm_bs_sender_unref;
+	session->float_clients = g_hash_table_new_full (g_direct_hash,
+						        g_direct_equal,
+						        NULL,
+						        destroy_sender);
+	session->transfers = g_hash_table_new_full (g_direct_hash, 
+						    g_direct_equal, 
+						    NULL, 
+						    destroy_transfer);
+	session->float_shas = g_hash_table_new_full (g_str_hash, 
+						     g_str_equal,
+						     g_free,
+						     NULL);
+	session->iq_ids = g_hash_table_new_full (g_str_hash, 
+						 g_str_equal,
+						 g_free,
+						 NULL);
+
+	session->context = context;
+	if (context) {
+		g_main_context_ref (context);
+	}
+
+	session->listener = NULL;
+	session->ref_count = 1;
+
+	return session;
+}
+
+/**
+ * lm_bs_session_get_default:
+ * @context: main context to be used for io events , could be NULL
+ * 
+ * Creates a new instance of #LmBsSession, or returns a reference to
+ * the existing one.
+ *
+ * Return value: reference to default bytestream session object.
+ **/
+LmBsSession *
+lm_bs_session_get_default (GMainContext *context)
+{
+	static LmBsSession *session = NULL;
+
+	/* one instance for all accounts/transfers */
+	if (session) {
+		return lm_bs_session_ref (session);
+	}
+
+	session = lm_bs_session_new (context);
+	session->ref_count = 2;
+
+	return session;
+}
+
+/**
+ * lm_bs_session_set_failure_function:
+ * @session: the bitestream session
+ * @function: the function that will be called on transfer failure
+ * @user_data: user supplied pointer. It will be passed to function
+ * @notify: function that will be executed on callback destroy, 
+ * could be NULL
+ * 
+ * Registers a function to be called when certain file transfer
+ * has failed within a bitestream session.
+ *
+ **/
+void
+lm_bs_session_set_failure_function (LmBsSession         *session,
+				    LmBsFailureFunction  function,
+				    gpointer             user_data,
+				    GDestroyNotify       notify)
+{
+	LmCallback *failure_cb;
+
+	g_return_if_fail (session != NULL);
+	failure_cb = _lm_utils_new_callback (function, 
+					     user_data,
+					     notify);
+	if (session->failure_cb != NULL) {
+		_lm_utils_free_callback (session->failure_cb);
+	}
+	session->failure_cb = failure_cb;
+}
+
+/**
+ * lm_bs_session_set_complete_function:
+ * @session: the bitestream session
+ * @function: the function that will be called when transfer is 
+ * complete
+ * @user_data: user supplied pointer. It will be passed to function
+ * @notify: function that will be executed on callback destroy, 
+ * could be NULL
+ *
+ * Registers a function to be called when certain file transfer
+ * completes within a bitestream session.
+ *
+ **/
+void
+lm_bs_session_set_complete_function (LmBsSession          *session,
+				     LmBsCompleteFunction  function,
+				     gpointer              user_data,
+				     GDestroyNotify        notify)
+{
+	LmCallback *complete_cb;
+
+	g_return_if_fail (session != NULL);
+	complete_cb = _lm_utils_new_callback (function, 
+					     user_data,
+					     notify);
+	if (session->complete_cb != NULL) {
+		_lm_utils_free_callback (session->complete_cb);
+	}
+	session->complete_cb = complete_cb;
+}
+
+/**
+ * lm_bs_session_receive_file:
+ * @session:
+ * @connection: an active jabber connection, used for streamhost 
+ * activation reply.
+ * @id: id of the file transfer within this session.
+ * @sid: session id of the file transfer, used by the jabber session.
+ * @sender: full jid (user@server/resource) of the sender
+ * @location: place to store the file. This argument should contain a
+ * local file path, not URI
+ * @file_size: size of the file. When @file_size bytes are being 
+ * received, it is assumed that file has been fully retrieved. 
+ * 
+ * Add info on a file transfer request. In order the transfer to start
+ * you need to set up at least one streamhost. 
+ **/
+void
+lm_bs_session_receive_file (LmBsSession  *session, 
+			    LmConnection *connection,
+			    guint         id,
+			    const gchar  *sid,
+			    const gchar  *sender,
+			    const gchar  *location,
+			    guint64       file_size)
+{
+	LmBsTransfer *transfer;
+
+	g_return_if_fail (sid != NULL);
+	g_return_if_fail (session->transfers != NULL);
+	g_return_if_fail (location != NULL);
+	g_return_if_fail (sender != NULL);
+	g_return_if_fail (connection != NULL);
+
+	transfer = lm_bs_transfer_new (session,
+				       connection,
+				       TRANSFER_TYPE_RECEIVER,
+				       id,
+				       sid,
+				       sender,
+				       location,
+				       file_size);
+	
+	g_hash_table_insert (session->transfers,
+			     GUINT_TO_POINTER(id),
+			     transfer);
+}
+
+
+/**
+ * lm_bs_session_send_file:
+ * @session:
+ * @connection: an active jabber connection.
+ * @id: id of the file transfer within this session.
+ * @sid: session id of the file transfer, used by the jabber session.
+ * @receiver: full jid (user@server/resource) of the recipient
+ * @location: place to get the file from. This argument should contain
+ * a local file path, not URI
+ * @file_size: size of the file. When @file_size bytes are being 
+ * sent, it is assumed that file has been fully transfered.
+ * 
+ * Add info on a file to be sent. In order the transfer to start
+ * you need to activate a streamhost.
+ **/
+void
+lm_bs_session_send_file (LmBsSession  *session, 
+			 LmConnection *connection,
+			 guint         id,
+			 const gchar  *sid,
+			 const gchar  *receiver,
+			 const gchar  *location,
+			 guint64       file_size)
+{
+	LmBsTransfer *transfer;
+	gchar        *auth_sha;
+
+	g_return_if_fail (sid != NULL);
+	g_return_if_fail (session->transfers != NULL);
+	g_return_if_fail (location != NULL);
+	g_return_if_fail (receiver != NULL);
+	g_return_if_fail (connection != NULL);
+
+	transfer = lm_bs_transfer_new (session,
+				       connection,
+				       TRANSFER_TYPE_SENDER,
+				       id,
+				       sid,
+				       receiver,
+				       location,
+				       file_size);
+	auth_sha = lm_bs_transfer_get_auth_sha (transfer);
+	g_hash_table_insert (session->transfers,
+			     GUINT_TO_POINTER(id),
+			     transfer);
+	g_hash_table_insert (session->float_shas,
+			     auth_sha,
+			     GUINT_TO_POINTER(id));
+}
+
+/**
+ * lm_bs_session_add_streamhost:
+ * @session:
+ * @id: id of the file transfer.
+ * @host: hostname or ip of the streamhost
+ * @port: port to connect on
+ * @jid: unique id of the streamhost
+ * 
+ * Add a streamhost to be used for the transfer. File transfer is 
+ * identified by @sid . Loudmouth will try to establish connection
+ * to one of the attached streamhost and return its @jid back to the
+ * sender.
+ **/
+void
+lm_bs_session_add_streamhost (LmBsSession *session,
+			      guint        id,
+			      const gchar *host,
+			      const gchar *port,
+			      const gchar *jid)
+{
+	guint64       u_port;
+	LmBsTransfer *transfer;
+
+	g_return_if_fail (session->transfers != NULL);
+
+	transfer = g_hash_table_lookup (session->transfers,
+					GUINT_TO_POINTER (id));
+	g_return_if_fail (transfer != NULL);
+
+	if (lm_bs_transfer_has_streamhost (transfer, jid)) {
+		/* some clients send more than one streamhost with same jids */
+		return;
+	}
+	u_port = g_ascii_strtoull (port, NULL, 10);
+	lm_bs_transfer_add_streamhost (transfer, host, u_port, jid);
+}
+
+/**
+ * lm_bs_session_set_iq_id:
+ * @session:
+ * @id: id of the file transfer.
+ * @iq_id: id of the query iq stanza.
+ * 
+ * Registers the id of stanzas, used for streamhost negotiation.
+ *
+ **/
+void
+lm_bs_session_set_iq_id (LmBsSession *session, guint id, const gchar *iq_id)
+{
+	LmBsTransfer *transfer;
+
+	g_return_if_fail (iq_id != NULL);
+	g_return_if_fail (session->transfers != NULL);
+
+	transfer = g_hash_table_lookup (session->transfers,
+					GUINT_TO_POINTER (id));
+	g_return_if_fail (transfer != NULL);
+	lm_bs_transfer_set_iq_id (transfer, iq_id);
+	if (lm_bs_transfer_get_type (transfer) == TRANSFER_TYPE_SENDER) {
+		g_hash_table_insert (session->iq_ids, 
+				     g_strdup (iq_id),
+				     GUINT_TO_POINTER (id));
+	}
+}
+
+/**
+ * lm_bs_session_activate_streamhost:
+ * @session:
+ * @iq_id: id of the query iq stanza.
+ * @jid: jid of the streamhost.
+ * 
+ * Tells the bytestream session that recepient has connected on a 
+ * streamhost identified by jid.
+ *
+ **/
+void
+lm_bs_session_activate_streamhost (LmBsSession *session,
+				   const gchar *iq_id,
+				   const gchar *jid)
+{
+	gpointer      id_ptr;
+	LmBsTransfer *transfer;
+
+	g_return_if_fail (session != NULL);
+	g_return_if_fail (session->iq_ids != NULL);
+	g_return_if_fail (session->transfers != NULL);
+
+	id_ptr = g_hash_table_lookup (session->iq_ids, iq_id);
+	
+	if (!id_ptr) {
+		return;
+	}
+	transfer = g_hash_table_lookup (session->transfers, id_ptr);
+	if (!transfer) {
+		g_hash_table_remove (session->iq_ids, iq_id);
+		return;
+	}
+	lm_bs_transfer_activate (transfer, jid);
+}
+
+/**
+ * lm_bs_session_ref:
+ * @session: bytestream session to add a reference to.
+ * 
+ * Add a reference to @session.
+ * 
+ * Return value: Returns the same session.
+ **/
+LmBsSession *
+lm_bs_session_ref (LmBsSession *session)
+{
+	g_return_val_if_fail (session != NULL, NULL);
+
+	session->ref_count++;
+	return session;
+}
+
+/**
+ * lm_bs_session_unref:
+ * @session: #LmBsSession to remove reference from.
+ * 
+ * Removes a reference from @session. When there are no references to
+ * @session it will be freed.
+ **/
+void
+lm_bs_session_unref (LmBsSession *session)
+{
+	g_return_if_fail (session != NULL);
+
+	session->ref_count--;
+	if (session->ref_count == 0) {
+		session_free(session);
+	}
+}
+
+/**
+ * lm_bs_session_remove_transfer:
+ * @session: #LmBsSession to remove the transfer from.
+ * @id: id of the transfer.
+ * 
+ * Removes a previously added transfer.
+ *
+ **/
+void
+lm_bs_session_remove_transfer (LmBsSession *session, guint id)
+{
+	const gchar  *iq_id;
+	LmBsTransfer *transfer;
+
+	g_return_if_fail (session != NULL);
+	transfer = g_hash_table_lookup (session->transfers,
+					GUINT_TO_POINTER (id));
+	g_return_if_fail (transfer != NULL);
+	iq_id = lm_bs_transfer_get_iq_id (transfer);
+	if (iq_id) {
+		g_hash_table_remove (session->iq_ids, iq_id);
+	}
+	lm_bs_session_ref (session);
+	g_hash_table_remove (session->transfers, GUINT_TO_POINTER (id));
+	if (g_hash_table_size (session->transfers) == 0 &&
+	    session->listener != NULL) {
+		lm_bs_listener_unref (session->listener);
+		session->listener = NULL;
+	}
+	lm_bs_session_unref (session);
+}
+
+void
+_lm_bs_session_transfer_error (LmBsSession *session, guint id, const gchar *msg)
+{
+	LmCallback *cb;
+
+	g_return_if_fail (session != NULL);
+
+	cb = session->failure_cb;
+	if (cb && cb->func) {
+		(* ((LmBsFailureFunction) cb->func)) (cb->user_data,
+						      id,
+						      msg);
+	}
+	lm_bs_session_remove_transfer (session, id);
+}
+
+void
+_lm_bs_session_transfer_completed (LmBsSession *session, guint id)
+{
+	LmCallback *cb;
+
+	g_return_if_fail (session != NULL);
+
+	cb = session->complete_cb;
+	if (cb && cb->func) {
+		(* ((LmBsCompleteFunction) cb->func)) (cb->user_data,
+						       id);
+	}
+	lm_bs_session_remove_transfer (session, id);
+}
+
+GMainContext *
+_lm_bs_session_get_context (LmBsSession *session)
+{
+	return session->context;
+}
+
diff -ura --new-file loudmouth.old/loudmouth/lm-bs-session.h loudmouth/loudmouth/lm-bs-session.h
--- loudmouth.old/loudmouth/lm-bs-session.h	1970-01-01 02:00:00.000000000 +0200
+++ loudmouth/loudmouth/lm-bs-session.h	2007-03-28 18:49:44.000000000 +0300
@@ -0,0 +1,96 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Free Software Foundation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * 
+ */
+
+#ifndef __LM_BS_SESSION_H__
+#define __LM_BS_SESSION_H__
+
+#if !defined (LM_INSIDE_LOUDMOUTH_H) && !defined (LM_COMPILATION)
+#error "Only <loudmouth/loudmouth.h> can be included directly, this file may disappear or change contents."
+#endif
+#include "lm-connection.h"
+
+G_BEGIN_DECLS
+
+#define LM_BS_SESSION(obj) (LmBsSession *) obj;
+
+typedef struct _LmBsSession LmBsSession;
+
+typedef void    (* LmBsFailureFunction)             (gpointer       user_data,
+						     guint          id,
+						     const gchar   *failure_message);
+typedef void    (* LmBsCompleteFunction)            (gpointer       user_data,
+						     guint          id);
+
+LmBsSession *   lm_bs_session_new                   (GMainContext  *context);
+LmBsSession *   lm_bs_session_get_default           (GMainContext  *context);
+LmBsSession *   lm_bs_session_ref                   (LmBsSession   *session);
+void            lm_bs_session_unref                 (LmBsSession   *session);
+void            lm_bs_session_set_failure_function  (LmBsSession   *session,
+						     LmBsFailureFunction  function,
+						     gpointer       user_data,
+						     GDestroyNotify notify);
+void            lm_bs_session_set_complete_function (LmBsSession   *session,
+						     LmBsCompleteFunction function,
+						     gpointer       user_data,
+						     GDestroyNotify notify);
+void            lm_bs_session_add_streamhost        (LmBsSession   *session,
+						     guint          id,
+						     const gchar   *host,
+						     const gchar   *port,
+						     const gchar   *jid);
+void            lm_bs_session_receive_file          (LmBsSession   *session, 
+						     LmConnection  *connection,
+						     guint          id,
+						     const gchar   *sid,
+						     const gchar   *sender,
+						     const gchar   *location,
+						     guint64        file_size);
+void            lm_bs_session_send_file             (LmBsSession   *session, 
+						     LmConnection  *connection,
+						     guint          id,
+						     const gchar   *sid,
+						     const gchar   *receiver,
+						     const gchar   *location,
+						     guint64        file_size);
+void            lm_bs_session_remove_transfer       (LmBsSession   *session,
+						     guint          fd);
+void            lm_bs_session_activate_streamhost   (LmBsSession   *session,
+						     const gchar   *iq_id,
+						     const gchar   *jid);
+void            lm_bs_session_set_iq_id             (LmBsSession   *session,
+						     guint          id,
+						     const gchar   *iq_id);
+guint           lm_bs_session_start_listener        (LmBsSession   *session);
+GMainContext * _lm_bs_session_get_context           (LmBsSession   *session);
+void           _lm_bs_session_transfer_error        (LmBsSession   *session,
+						     guint          id,
+						     const gchar   *msg);
+void           _lm_bs_session_transfer_completed    (LmBsSession   *session,
+						     guint          id);
+void           _lm_bs_session_remove_sender         (LmBsSession   *session,
+						     guint          fd);
+void           _lm_bs_session_match_sha             (LmBsSession   *session,
+						     const gchar   *sha,
+						     guint          fd);
+
+G_END_DECLS
+
+#endif /* __LM_BS_SESSION_H__ */
diff -ura --new-file loudmouth.old/loudmouth/lm-bs-transfer.c loudmouth/loudmouth/lm-bs-transfer.c
--- loudmouth.old/loudmouth/lm-bs-transfer.c	1970-01-01 02:00:00.000000000 +0200
+++ loudmouth/loudmouth/lm-bs-transfer.c	2007-03-28 18:49:44.000000000 +0300
@@ -0,0 +1,576 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Free Software Foundation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * 
+ */
+
+#include <config.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+
+#include "lm-connection.h"
+#include "lm-sha.h"
+#include "lm-bs-session.h"
+#include "lm-bs-client.h"
+#include "lm-bs-transfer.h"
+#include "lm-bs-receiver.h"
+
+#define XMLNS_BYTESTREAMS "http://jabber.org/protocol/bytestreams"
+
+#define FILE_BUFFER_SIZE 1024
+
+struct _LmBsTransfer{
+	TransferType   type;
+	gchar         *peer_jid;
+	gchar         *location;
+	guint64        file_size;
+	guint64        bytes_transfered;
+	gint           id;
+	gchar         *sid;
+	gchar         *iq_id;
+	TransferStatus status;
+	GHashTable    *streamhosts;
+	LmConnection  *connection;
+	LmBsSession   *session;
+	GIOChannel    *file_channel;
+	LmCallback    *activate_cb;
+
+	gint           ref_count;
+};
+
+typedef struct {
+	gchar         *jid;
+	LmBsTransfer  *transfer;
+} StreamhostData;
+
+static void     free_streamhost_data          (gpointer data);
+static void     client_connnected_cb          (LmBsClient *client,
+					       StreamhostData *sreamhost_data);
+static void     client_disconnnected_cb       (LmBsClient *client,
+					       StreamhostData *sreamhost_data);
+static gboolean file_channel_open_for_writing (LmBsTransfer  *transfer,
+					       GError       **error);
+static gboolean file_channel_open_for_reading (LmBsTransfer *transfer,
+					       GError **error);
+static gchar *  io_error_to_string            (GError *error);
+static void     transfer_completed            (LmBsTransfer *transfer);
+static void     transfer_free                 (LmBsTransfer *transfer);
+
+static void
+free_streamhost_data (gpointer data)
+{
+	g_free (((StreamhostData *) data)->jid);
+	g_free (data);
+}
+
+static void
+client_connnected_cb (LmBsClient *client, StreamhostData *sreamhost_data)
+{
+	LmBsTransfer *transfer;
+	LmBsReceiver *receiver;
+
+	transfer = sreamhost_data->transfer;
+	transfer->status = T_STATUS_CONNECTED;
+	receiver = lm_bs_receiver_new (client, transfer, sreamhost_data->jid);
+	g_hash_table_remove_all (transfer->streamhosts);
+	lm_bs_receiver_start_transfer (receiver);
+}
+
+static void
+client_disconnnected_cb (LmBsClient *client, StreamhostData *sreamhost_data)
+{
+	LmBsTransfer *transfer;
+	
+	transfer = sreamhost_data->transfer;
+	g_hash_table_remove (transfer->streamhosts, sreamhost_data->jid);
+	if (!g_hash_table_size (transfer->streamhosts)) {
+		/* unable to connect to any of the supplied streamhosts */
+		_lm_bs_session_transfer_error (transfer->session,
+					       transfer->id,
+					       _("Unable to connect to any host"));
+	}
+}
+
+static gboolean 
+file_channel_open_for_writing (LmBsTransfer  *transfer,
+			       GError       **error)
+{
+	if (transfer->file_channel) {
+		return TRUE;
+	}
+	transfer->file_channel = g_io_channel_new_file (transfer->location,
+							"w",
+							error);
+	if (!transfer->file_channel) {
+		return FALSE;
+	}
+	g_io_channel_set_encoding (transfer->file_channel, NULL, NULL);
+	g_io_channel_set_buffered (transfer->file_channel, FALSE);
+	return TRUE;
+}
+
+static gboolean 
+file_channel_open_for_reading (LmBsTransfer *transfer,
+			       GError **error)
+{
+	if (transfer->file_channel) {
+		return TRUE;
+	}
+	transfer->file_channel = g_io_channel_new_file (transfer->location,
+							"r",
+							error);
+	if (!transfer->file_channel) {
+		return FALSE;
+	}
+	g_io_channel_set_encoding (transfer->file_channel, NULL, NULL);
+	g_io_channel_set_buffered (transfer->file_channel, FALSE);
+	return TRUE;
+}
+
+static gchar*
+io_error_to_string (GError *error)
+{
+	g_return_val_if_fail(error != NULL, NULL);
+	if (error->domain == G_FILE_ERROR) {
+		switch(error->code) {
+			case G_FILE_ERROR_EXIST:
+			case G_FILE_ERROR_ACCES:
+			case G_FILE_ERROR_PERM:
+				return _("Permission denied");
+			case G_FILE_ERROR_NAMETOOLONG:
+				return _("File name too long");
+			case G_FILE_ERROR_NOENT:
+				return _("File doesn't exist");
+			case G_FILE_ERROR_ISDIR:
+				return _("File is a directory");
+			case G_FILE_ERROR_ROFS:
+				return _("Read only file system");
+			case G_FILE_ERROR_TXTBSY:
+				return _("File busy");
+			case G_FILE_ERROR_FAULT:
+				return _("Bad memory");
+			case G_FILE_ERROR_LOOP:
+				return _("Too many levels of symbolic links");
+			case G_FILE_ERROR_NOSPC:
+				return _("No space left on device");
+			case G_FILE_ERROR_NOMEM:
+				return _("Virtual memory exhausted");
+			case G_FILE_ERROR_MFILE:
+				return _("Too many open files");
+			case G_FILE_ERROR_BADF:
+			case G_FILE_ERROR_IO:
+				return _("Input/output error");
+			case G_FILE_ERROR_NODEV:
+			case G_FILE_ERROR_NXIO:
+				return _("No such device");
+			default:
+				break; /* unknown error */
+		}
+
+	} else if (error->domain == G_IO_CHANNEL_ERROR) {
+		switch(error->code) {
+			case G_IO_CHANNEL_ERROR_FBIG:
+				return _("File too large");
+			case G_IO_CHANNEL_ERROR_IO:
+				return _("Input/output error");
+			case G_IO_CHANNEL_ERROR_ISDIR:
+				return _("File is a directory");
+			case G_IO_CHANNEL_ERROR_NOSPC:
+				return _("No space left on device");
+			case G_IO_CHANNEL_ERROR_NXIO:
+				return _("No such device");
+			default:
+				break; /* unknown error */
+		}
+	}
+	return _("Unknown error");
+}
+
+static void
+transfer_completed (LmBsTransfer *transfer)
+{
+	LmBsSession *session;
+	transfer->status = T_STATUS_COMPLETED;
+	session = transfer->session;
+	lm_bs_transfer_close_file (transfer);
+	_lm_bs_session_transfer_completed (session, transfer->id);
+}
+
+static void
+transfer_free (LmBsTransfer *transfer)
+{
+	LmConnection *connection;
+	LmBsSession  *session;
+
+	connection = transfer->connection;
+	session = transfer->session;
+
+	if (transfer->activate_cb) {
+		_lm_utils_free_callback (transfer->activate_cb);
+	}
+	g_hash_table_destroy (transfer->streamhosts);
+	lm_bs_transfer_close_file (transfer);
+	g_free (transfer->peer_jid);
+	g_free (transfer->location);
+	g_free (transfer->sid);
+	g_free (transfer->iq_id);
+	g_free (transfer);
+	lm_bs_session_unref (session);
+	lm_connection_unref (connection);
+}
+
+LmBsTransfer *
+lm_bs_transfer_new (LmBsSession    *session,
+		    LmConnection   *connection,
+		    TransferType    type,
+		    gint            id,
+		    const gchar    *sid,
+		    const gchar    *peer_jid,
+		    const gchar    *location,
+		    gint64          file_size)
+{
+	LmBsTransfer *transfer;
+
+	transfer = g_new0 (LmBsTransfer, 1);
+	transfer->peer_jid = g_strdup (peer_jid);
+	transfer->connection = connection;
+	lm_connection_ref (connection);
+	transfer->type = type;
+	transfer->file_size = file_size;
+	transfer->location = g_strdup (location);
+	transfer->sid = g_strdup (sid);
+	transfer->id = id;
+	transfer->iq_id = NULL;
+	transfer->status = T_STATUS_INITIAL;
+	transfer->streamhosts = g_hash_table_new_full (g_str_hash, 
+						       g_str_equal, 
+						       g_free, 
+						       (GDestroyNotify) lm_bs_client_unref);
+	transfer->session = lm_bs_session_ref (session);
+	transfer->activate_cb = NULL;
+	transfer->file_channel = NULL;
+	transfer->bytes_transfered = 0;
+
+	transfer->ref_count = 1;
+	return transfer;
+}
+
+void
+lm_bs_transfer_error(LmBsTransfer *transfer, const gchar *error_msg)
+{
+	LmBsSession *session;
+	transfer->status = T_STATUS_INTERRUPTED;
+	session = transfer->session;
+	lm_bs_transfer_close_file(transfer);
+	_lm_bs_session_transfer_error(session, transfer->id, error_msg);
+}
+
+
+gboolean
+lm_bs_transfer_append_to_file (LmBsTransfer *transfer, GString *data)
+{
+	gsize        bytes_written;
+	GIOStatus    io_status;
+	GError      *error;
+	const gchar *error_msg;
+	
+	g_return_val_if_fail (transfer->type == TRANSFER_TYPE_RECEIVER, FALSE);
+	error = NULL;
+	if (!file_channel_open_for_writing (transfer, &error)) {
+		error_msg = io_error_to_string (error);
+		g_error_free (error);
+		lm_bs_transfer_error (transfer, error_msg);
+		return FALSE;
+	}
+	io_status = g_io_channel_write_chars (transfer->file_channel, 
+					      data->str,
+					      data->len,
+					      &bytes_written,
+					      &error);
+	if (io_status != G_IO_STATUS_NORMAL) {
+		error_msg = io_error_to_string (error);
+		g_error_free (error);
+		lm_bs_transfer_error (transfer, error_msg);
+		return FALSE;
+	}
+	transfer->bytes_transfered += bytes_written;
+	if (transfer->bytes_transfered >= transfer->file_size) {
+		transfer_completed (transfer);
+		return FALSE;
+	}
+	return TRUE;
+}
+
+gboolean
+lm_bs_transfer_get_file_content (LmBsTransfer *transfer, GString **data)
+{
+	gsize        bytes_read;
+	GIOStatus    io_status;
+	GError      *error;
+	const gchar *error_msg;
+	gchar       *buffer;
+	
+	g_return_val_if_fail (transfer->type == TRANSFER_TYPE_SENDER, FALSE);
+
+	error = NULL;
+	if (transfer->bytes_transfered >= transfer->file_size) {
+		transfer_completed (transfer);
+		return FALSE;
+	}
+	if (!file_channel_open_for_reading (transfer, &error)) {
+		error_msg = io_error_to_string (error);
+		g_error_free (error);
+		lm_bs_transfer_error (transfer, error_msg);
+		return FALSE;
+	}
+	buffer = g_malloc (FILE_BUFFER_SIZE);
+	io_status = g_io_channel_read_chars (transfer->file_channel, 
+					      buffer,
+					      FILE_BUFFER_SIZE,
+					      &bytes_read,
+					      &error);
+	
+	if (io_status != G_IO_STATUS_NORMAL) {
+		error_msg = io_error_to_string (error);
+		g_error_free (error);
+		lm_bs_transfer_error (transfer, error_msg);
+		return FALSE;
+	}
+	*data =  g_string_new_len (buffer, bytes_read);
+	transfer->bytes_transfered += bytes_read;
+	return TRUE;
+}
+
+TransferStatus
+lm_bs_transfer_get_status (LmBsTransfer *transfer)
+{
+	return transfer->status;
+}
+
+void
+lm_bs_transfer_close_file (LmBsTransfer *transfer)
+{
+	if (!transfer->file_channel) {
+		return;
+	}
+	g_io_channel_unref (transfer->file_channel);
+	transfer->file_channel = NULL;
+}
+
+LmBsTransfer *
+lm_bs_transfer_ref (LmBsTransfer *transfer)
+{
+	g_return_val_if_fail (transfer != NULL, NULL);
+	transfer->ref_count++;
+	return transfer;
+}
+
+void
+lm_bs_transfer_unref (LmBsTransfer *transfer)
+{
+	g_return_if_fail (transfer != NULL);
+	transfer->ref_count--;
+	
+	if (transfer->ref_count == 0) {
+		transfer_free (transfer);
+	}
+}
+
+void 
+lm_bs_transfer_set_iq_id (LmBsTransfer *transfer, const gchar *iq_id)
+{
+	if (transfer->iq_id != NULL) {
+		/* id is already set. no need to override it, 
+		 * because it is same for all streamhosts */
+		return;
+	}
+	transfer->iq_id = g_strdup (iq_id);
+}
+
+void
+lm_bs_transfer_add_streamhost (LmBsTransfer *transfer,
+			       const gchar  *host,
+			       guint64       port,
+			       const gchar  *jid)
+{
+	GMainContext       *context;
+	LmBsClient         *streamhost;
+	LmBsClientFunction  func;
+	LmCallback         *connected_cb;
+	LmCallback         *disconnect_cb;
+	StreamhostData     *sreamhost_data;
+
+
+	sreamhost_data = g_new0 (StreamhostData, 1);
+	sreamhost_data->jid = g_strdup (jid);
+	sreamhost_data->transfer = transfer;
+	context = _lm_bs_session_get_context (transfer->session);
+	if (transfer->type == TRANSFER_TYPE_RECEIVER) {
+		func = (LmBsClientFunction) client_connnected_cb;
+		connected_cb = _lm_utils_new_callback (func,
+						       sreamhost_data,
+						       NULL);
+		func = (LmBsClientFunction) client_disconnnected_cb;
+		disconnect_cb = _lm_utils_new_callback (func, 
+							sreamhost_data,
+							(GDestroyNotify) free_streamhost_data);
+		streamhost = lm_bs_client_new_with_context (port,
+							    host,
+							    connected_cb,
+							    disconnect_cb,
+							    NULL,
+							    NULL,
+							    context);
+	} else {
+		streamhost = lm_bs_client_new_with_context (port,
+							    host,
+							    NULL,
+							    NULL,
+							    NULL,
+							    NULL,
+							    context);
+	}
+	g_hash_table_insert (transfer->streamhosts,
+			     g_strdup (jid),
+			     streamhost);
+	if (transfer->status == T_STATUS_INITIAL &&
+	    transfer->type == TRANSFER_TYPE_RECEIVER) {
+		lm_bs_client_connect (streamhost);
+	}
+}
+
+const gchar *
+lm_bs_transfer_get_sid (LmBsTransfer *transfer)
+{
+	return transfer->sid;
+}
+
+const gchar *
+lm_bs_transfer_get_initiator (LmBsTransfer *transfer)
+{
+	if (transfer->type == TRANSFER_TYPE_RECEIVER) {
+		return transfer->peer_jid;
+	}
+	return lm_connection_get_full_jid (transfer->connection);
+}
+
+const gchar *
+lm_bs_transfer_get_target (LmBsTransfer *transfer)
+{
+	if (transfer->type == TRANSFER_TYPE_RECEIVER) {
+		return lm_connection_get_full_jid (transfer->connection);
+	}
+	return transfer->peer_jid;
+}
+
+gchar * 
+lm_bs_transfer_get_auth_sha (LmBsTransfer *transfer)
+{
+	gchar       *concat;
+	const gchar *sha;
+	
+	concat = g_strconcat (transfer->sid,
+			      lm_bs_transfer_get_initiator(transfer),
+			      lm_bs_transfer_get_target(transfer),
+			      NULL);
+	sha = lm_sha_hash (concat);
+	g_free(concat);
+
+	return g_strdup (sha);
+}
+
+TransferType
+lm_bs_transfer_get_type (LmBsTransfer *transfer)
+{
+	return transfer->type;
+}
+
+const gchar *
+lm_bs_transfer_get_iq_id (LmBsTransfer *transfer)
+{
+	return transfer->iq_id;
+}
+
+gboolean
+lm_bs_transfer_has_streamhost (LmBsTransfer *transfer, const gchar *jid)
+{
+	if (g_hash_table_lookup (transfer->streamhosts, jid)) {
+		return TRUE;
+	}
+	return FALSE;
+}
+
+void 
+lm_bs_transfer_set_activate_cb (LmBsTransfer *transfer,
+				LmCallback *activate_cb)
+{
+	g_return_if_fail (transfer != NULL);
+	if (transfer->activate_cb != NULL) {
+		_lm_utils_free_callback (transfer->activate_cb);
+	}
+	transfer->activate_cb = activate_cb;
+}
+
+void
+lm_bs_transfer_send_success_reply (LmBsTransfer *transfer, const gchar *jid)
+{
+	LmMessage     *m;
+	LmMessageNode *node;
+	LmMessageNode *node1;
+	GError        *error;
+
+	g_return_if_fail (transfer->type == TRANSFER_TYPE_RECEIVER);
+
+	m = lm_message_new_with_sub_type (transfer->peer_jid, 
+					  LM_MESSAGE_TYPE_IQ, 
+					  LM_MESSAGE_SUB_TYPE_RESULT);
+	lm_message_node_set_attribute (m->node, "id", transfer->iq_id);
+	lm_message_node_set_attribute (m->node,
+				       "from",
+				       lm_bs_transfer_get_target (transfer));
+	node = lm_message_node_add_child (m->node, "query", NULL);
+	lm_message_node_set_attribute (node, "xmlns", XMLNS_BYTESTREAMS);
+	node1 = lm_message_node_add_child (node, "streamhost-used", NULL);
+	lm_message_node_set_attribute (node1, "jid", jid);
+	error = NULL;
+	if (!lm_connection_send (transfer->connection, m, &error)) {
+		g_printerr ("Failed to send message:'%s'\n", 
+			    lm_message_node_to_string (m->node));
+	} else {
+		g_print ("Sent message:'%s'\n", 
+			 lm_message_node_to_string (m->node));
+	}
+}
+
+void 
+lm_bs_transfer_activate (LmBsTransfer *transfer, const gchar *jid)
+{
+	LmCallback *cb;
+
+	g_return_if_fail (lm_bs_transfer_has_streamhost (transfer, jid));
+
+	cb = transfer->activate_cb;
+	g_hash_table_remove_all (transfer->streamhosts);
+	if (cb && cb->func) {
+		(* ((LmBsClientFunction) cb->func)) (NULL,
+						     cb->user_data);
+	}
+}
diff -ura --new-file loudmouth.old/loudmouth/lm-bs-transfer.h loudmouth/loudmouth/lm-bs-transfer.h
--- loudmouth.old/loudmouth/lm-bs-transfer.h	1970-01-01 02:00:00.000000000 +0200
+++ loudmouth/loudmouth/lm-bs-transfer.h	2007-03-28 18:49:44.000000000 +0300
@@ -0,0 +1,90 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Free Software Foundation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ * 
+ */
+
+#ifndef __LM_BS_TRANSFER_H__
+#define __LM_BS_TRANSFER_H__
+
+#if !defined (LM_INSIDE_LOUDMOUTH_H) && !defined (LM_COMPILATION)
+#error "Only <loudmouth/loudmouth.h> can be included directly, this file may disappear or change contents."
+#endif
+#include "lm-internals.h"
+#include "lm-bs-session.h"
+
+G_BEGIN_DECLS
+
+#define LM_BS_TRANSFER(obj)  (LmBsTransfer *) obj;
+typedef struct _LmBsTransfer LmBsTransfer;
+
+typedef enum {
+	T_STATUS_INITIAL,
+	T_STATUS_CONNECTED,
+	T_STATUS_TRANSFERING,
+	T_STATUS_COMPLETED,
+	T_STATUS_INTERRUPTED
+} TransferStatus;
+
+typedef enum {
+	TRANSFER_TYPE_SENDER,
+	TRANSFER_TYPE_RECEIVER
+} TransferType;
+
+LmBsTransfer * lm_bs_transfer_new                (LmBsSession   *session,
+						  LmConnection  *connection,
+						  TransferType   type,
+						  gint           id,
+						  const gchar   *sid,
+						  const gchar   *peer_jid,
+						  const gchar   *location,
+						  gint64         file_size);
+LmBsTransfer * lm_bs_transfer_ref                (LmBsTransfer  *transfer);
+void           lm_bs_transfer_unref              (LmBsTransfer  *transfer);
+void           lm_bs_transfer_set_iq_id          (LmBsTransfer  *transfer,
+						  const gchar   *iq_id);
+void           lm_bs_transfer_add_streamhost     (LmBsTransfer  *transfer,
+						  const gchar   *host,
+						  guint64        port,
+						  const gchar   *jid);
+const gchar *  lm_bs_transfer_get_sid            (LmBsTransfer  *transfer);
+const gchar *  lm_bs_transfer_get_initiator      (LmBsTransfer  *transfer);
+gboolean       lm_bs_transfer_has_streamhost     (LmBsTransfer  *transfer, 
+						  const gchar   *jid);
+const gchar *  lm_bs_transfer_get_target         (LmBsTransfer  *transfer);
+gchar *        lm_bs_transfer_get_auth_sha       (LmBsTransfer  *transfer);
+TransferType   lm_bs_transfer_get_type           (LmBsTransfer  *transfer);
+TransferStatus lm_bs_transfer_get_status         (LmBsTransfer  *transfer);
+void           lm_bs_transfer_error              (LmBsTransfer  *transfer,
+						  const gchar   *error_msg);
+const gchar *  lm_bs_transfer_get_iq_id          (LmBsTransfer  *transfer);
+void           lm_bs_transfer_set_activate_cb    (LmBsTransfer  *transfer,
+						  LmCallback    *activate_cb);
+void           lm_bs_transfer_send_success_reply (LmBsTransfer  *transfer,
+						  const gchar   *jid);
+void           lm_bs_transfer_activate           (LmBsTransfer  *transfer,
+						  const gchar   *jid);
+gboolean       lm_bs_transfer_append_to_file     (LmBsTransfer  *transfer,
+						  GString       *data);
+gboolean       lm_bs_transfer_get_file_content   (LmBsTransfer  *transfer,
+						  GString      **data);
+void           lm_bs_transfer_close_file         (LmBsTransfer  *transfer);
+
+G_END_DECLS
+
+#endif /* __LM_BS_TRANSFER_H__  */
diff -ura --new-file loudmouth.old/loudmouth/lm-connection.c loudmouth/loudmouth/lm-connection.c
--- loudmouth.old/loudmouth/lm-connection.c	2007-03-28 18:47:43.000000000 +0300
+++ loudmouth/loudmouth/lm-connection.c	2007-03-28 18:49:44.000000000 +0300
@@ -54,6 +54,7 @@
 	GMainContext *context;
 	gchar        *server;
 	gchar        *jid;
+	gchar        *resource;
 	guint         port;
 
 	LmSSL        *ssl;
@@ -1568,6 +1569,7 @@
 	connection->context           = NULL;
 	connection->port              = LM_CONNECTION_DEFAULT_PORT;
 	connection->jid               = NULL;
+	connection->resource          = NULL;
 	connection->ssl               = NULL;
 	connection->proxy             = NULL;
 	connection->disconnect_cb     = NULL;
@@ -1813,6 +1815,8 @@
 	data->password = g_strdup (password);
 	data->resource = g_strdup (resource);
 	
+	/* assume that this is our visible resource. */
+	connection->resource = g_strdup (resource);
 	handler = lm_message_handler_new (connection_auth_req_reply, 
 					  data, 
 					  (GDestroyNotify) auth_req_data_free);
@@ -2024,6 +2028,14 @@
 	return connection->jid;
 }
 
+const gchar *
+lm_connection_get_full_jid (LmConnection *connection)
+{
+	g_return_val_if_fail (connection != NULL, NULL);
+
+	return g_strconcat(connection->jid, "/", connection->resource, NULL);
+}
+
 /**
  * lm_connection_set_jid:
  * @connection: an #LmConnection
@@ -2433,6 +2445,12 @@
 
 	return connection_send (connection, str, -1, error);
 }
+
+gchar *
+lm_connection_get_client_host (LmConnection *connection)
+{
+	return _lm_sock_get_local_host(connection->fd);
+}
 /**
  * lm_connection_get_state:
  * @connection: Connection to get state on
diff -ura --new-file loudmouth.old/loudmouth/lm-connection.h loudmouth/loudmouth/lm-connection.h
--- loudmouth.old/loudmouth/lm-connection.h	2007-03-28 18:47:43.000000000 +0300
+++ loudmouth/loudmouth/lm-connection.h	2007-03-28 18:49:44.000000000 +0300
@@ -116,7 +116,7 @@
 void          lm_connection_set_jid           (LmConnection       *connection,
 					       const gchar        *jid);
 const gchar * lm_connection_get_jid           (LmConnection       *connection);
-
+const gchar * lm_connection_get_full_jid      (LmConnection       *connection);
 guint         lm_connection_get_port          (LmConnection       *connection);
 void          lm_connection_set_port          (LmConnection       *connection,
 					       guint               port);
@@ -157,6 +157,7 @@
 					       const gchar        *str,
 					       GError            **error);
 LmConnectionState lm_connection_get_state     (LmConnection       *connection);
+gchar *       lm_connection_get_client_host   (LmConnection       *connection);
 LmConnection* lm_connection_ref               (LmConnection       *connection);
 void          lm_connection_unref             (LmConnection       *connection);
 
diff -ura --new-file loudmouth.old/loudmouth/lm-internals.h loudmouth/loudmouth/lm-internals.h
--- loudmouth.old/loudmouth/lm-internals.h	2007-03-28 18:47:43.000000000 +0300
+++ loudmouth/loudmouth/lm-internals.h	2007-03-28 18:49:44.000000000 +0300
@@ -94,6 +94,10 @@
 int              _lm_sock_connect                 (LmSocket               sock,
 						   const struct sockaddr *name,
 						   int                    namelen);
+int              _lm_sock_bind                    (LmSocket               sock,
+						   const struct sockaddr *name,
+						   int                    namelen);
+gchar *          _lm_sock_get_local_host	  (LmSocket               socket);
 gboolean         _lm_sock_is_blocking_error       (int                    err);
 gboolean         _lm_sock_is_blocking_success     (int                    err);
 int              _lm_sock_get_last_error          (void);
@@ -102,5 +106,4 @@
 						   socklen_t             *len);
 const gchar *    _lm_sock_get_error_str           (int                    err);
 const gchar *    _lm_sock_addrinfo_get_error_str  (int                    err);
-
 #endif /* __LM_INTERNALS_H__ */
diff -ura --new-file loudmouth.old/loudmouth/lm-sock.c loudmouth/loudmouth/lm-sock.c
--- loudmouth.old/loudmouth/lm-sock.c	2007-03-28 18:47:43.000000000 +0300
+++ loudmouth/loudmouth/lm-sock.c	2007-03-28 18:49:44.000000000 +0300
@@ -28,6 +28,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <sys/socket.h>
+#include <arpa/inet.h>
 #include <fcntl.h>
 #define LM_SHUTDOWN SHUT_RDWR
 
@@ -161,6 +162,28 @@
 	return connect (sock, name, namelen);
 }
 
+int 
+_lm_sock_bind (LmSocket               sock, 
+	       const struct sockaddr *name, 
+	       int                    namelen)
+{
+	return bind (sock, name, namelen);
+}
+
+gchar *
+_lm_sock_get_local_host (LmSocket   sock)
+{
+	struct sockaddr_in addr_info;
+	socklen_t namelen;
+	
+	namelen = sizeof(addr_info);
+	memset (&addr_info, 0, namelen);
+	if(getsockname(sock, (struct sockaddr *)&addr_info, &namelen)) {
+		return NULL;
+	}
+	return inet_ntoa(addr_info.sin_addr);
+}
+
 gboolean
 _lm_sock_is_blocking_error (int err)
 {
diff -ura --new-file loudmouth.old/loudmouth/loudmouth.h loudmouth/loudmouth/loudmouth.h
--- loudmouth.old/loudmouth/loudmouth.h	2007-03-28 18:47:43.000000000 +0300
+++ loudmouth/loudmouth/loudmouth.h	2007-03-28 18:49:44.000000000 +0300
@@ -31,6 +31,8 @@
 #include <loudmouth/lm-proxy.h>
 #include <loudmouth/lm-utils.h>
 #include <loudmouth/lm-ssl.h>
+#include <loudmouth/lm-bs-session.h>
+
 
 #undef LM_INSIDE_LOUDMOUTH_H
 
diff -ura --new-file loudmouth.old/loudmouth/Makefile.am loudmouth/loudmouth/Makefile.am
--- loudmouth.old/loudmouth/Makefile.am	2007-03-28 18:47:43.000000000 +0300
+++ loudmouth/loudmouth/Makefile.am	2007-03-28 18:49:44.000000000 +0300
@@ -45,10 +45,22 @@
 	lm-proxy.c                      \
 	lm-sock.h			\
 	lm-sock.c			\
+	lm-bs-session.c			\
+	lm-bs-listener.c		\
+	lm-bs-listener.h		\
+	lm-bs-client.c			\
+	lm-bs-client.h			\
+	lm-bs-transfer.c		\
+	lm-bs-transfer.h		\
+	lm-bs-sender.c			\
+	lm-bs-sender.h			\
+	lm-bs-receiver.c		\
+	lm-bs-receiver.h		\
 	$(NULL)
 
 libloudmouthinclude_HEADERS =		\
 	lm-connection.h			\
+	lm-bs-session.h			\
 	lm-error.h			\
 	lm-message.h		 	\
 	lm-message-handler.h		\
