Index: protocols/jabber/gossip-jabber.c
===================================================================
--- protocols/jabber/gossip-jabber.c	(revision 2282)
+++ protocols/jabber/gossip-jabber.c	(working copy)
@@ -50,6 +50,7 @@
 #include "gossip-jabber-chatrooms.h"
 #include "gossip-jabber-ft.h"
 #include "gossip-jabber-vcard.h"
+#include "gossip-jabber-disco.h"
 #include "gossip-jabber-services.h"
 #include "gossip-jabber-utils.h"
 
@@ -635,6 +636,7 @@
 	/* initiate extended modules */
 	priv->chatrooms = gossip_jabber_chatrooms_init (jabber);
 	priv->fts = gossip_jabber_ft_init (jabber);
+	gossip_jabber_disco_init (jabber);
 
 #ifdef USE_TRANSPORTS
 	/* initialise the jabber accounts module which is necessary to
Index: protocols/jabber/gossip-jabber-ft.c
===================================================================
--- protocols/jabber/gossip-jabber-ft.c	(revision 2282)
+++ protocols/jabber/gossip-jabber-ft.c	(working copy)
@@ -58,12 +58,18 @@
 struct _GossipJabberFTs {
 	GossipJabber   *jabber;
 	LmConnection   *connection;
+	LmBsSession    *bs_session;
 
 	GHashTable     *str_ids;
 	GHashTable     *ft_ids;
 	GHashTable     *remote_ids;
+	GHashTable     *jid_sids;
 };
-
+static void            jabber_ft_transfer_complete    (GossipJabber      *jabber,
+						       guint              id);
+static void            jabber_ft_transfer_failure     (GossipJabber      *jabber,
+						       guint              id,
+						       gchar             *reason);
 static LmHandlerResult jabber_ft_iq_si_handler        (LmMessageHandler  *handler,
 						       LmConnection      *conn,
 						       LmMessage         *message,
@@ -83,6 +89,8 @@
 						       gchar            **mime_type);
 static gchar *         jabber_ft_get_contact_last_jid (GossipContact     *contact);
 
+static gchar *         jabber_ft_get_unique_sid       (void);
+
 GossipJabberFTs *
 gossip_jabber_ft_init (GossipJabber *jabber)
 {
@@ -95,10 +103,19 @@
 	connection = gossip_jabber_get_connection (jabber);
 
 	fts = g_new0 (GossipJabberFTs, 1);
-
 	fts->jabber     = g_object_ref (jabber);
 	fts->connection = lm_connection_ref (connection);
+	fts->bs_session = lm_bs_session_new (NULL);
+	lm_bs_session_set_failure_function (fts->bs_session, 
+					    (LmBsFailureFunction) jabber_ft_transfer_failure,
+					    jabber,
+					    NULL);
 
+	lm_bs_session_set_complete_function (fts->bs_session, 
+					    (LmBsCompleteFunction) jabber_ft_transfer_complete,
+					    jabber,
+					    NULL);
+
 	fts->str_ids = g_hash_table_new_full (g_direct_hash,
 					      g_direct_equal,
 					      NULL,
@@ -114,6 +131,11 @@
 						 NULL,
 						 g_free);
 
+	fts->jid_sids = g_hash_table_new_full (g_str_hash,
+					       g_str_equal,
+					       NULL,
+					       (GDestroyNotify) g_hash_table_destroy);
+
 	handler = lm_message_handler_new ((LmHandleMessageFunction) jabber_ft_iq_si_handler,
 					  jabber, NULL);
 	lm_connection_register_message_handler (connection,
@@ -122,6 +144,7 @@
 						LM_HANDLER_PRIORITY_NORMAL);
 	lm_message_handler_unref (handler);
 
+
 	return fts;
 }
 
@@ -138,6 +161,7 @@
 	g_hash_table_destroy (fts->str_ids);
 	g_hash_table_destroy (fts->ft_ids);
 	g_hash_table_destroy (fts->remote_ids);
+	g_hash_table_destroy (fts->jid_sids);
 
 	lm_connection_unref (fts->connection);
 	g_object_unref (fts->jabber);
@@ -145,7 +169,294 @@
 	g_free (fts);
 }
 
+static guint
+guess_id_by_sid_and_sender (GossipJabber *jabber,
+			    const gchar  *sid,
+			    const gchar  *jid)
+{
+	GossipJabberFTs *fts;
+	GHashTable      *jids;
+	gpointer         id_ptr;
+	
+	fts = gossip_jabber_get_fts (jabber);
+	g_return_val_if_fail (fts, 0);
+	jids = g_hash_table_lookup (fts->jid_sids, jid);
+	if (!jids) {
+		return 0;
+	}
+	id_ptr = g_hash_table_lookup (jids, sid);
+	if (!id_ptr) {
+		return 0;
+	}
+	return GPOINTER_TO_UINT(id_ptr);
+}
+
+static void
+jabber_ft_transfer_complete (GossipJabber *jabber, guint id)
+{
+	gossip_debug (DEBUG_DOMAIN, "transfer is complete");
+}
+
+static void
+jabber_ft_transfer_failure (GossipJabber *jabber, guint id, gchar *reason)
+{
+	gossip_debug (DEBUG_DOMAIN, "transfer failed");
+}
+
+static void
+add_jid_sid_to_table (GossipJabberFTs *fts,
+		      const gchar     *jid,
+		      const gchar     *sid,
+		      guint            id)
+{
+	GHashTable *jid_sids_table;
+
+	jid_sids_table = g_hash_table_lookup(fts->jid_sids, jid);
+	if (!jid_sids_table) {
+		jid_sids_table = g_hash_table_new_full (g_str_hash,
+							g_str_equal,
+							g_free,
+							NULL);
+		g_hash_table_insert (fts->jid_sids,
+				     g_strdup (jid),
+				     jid_sids_table);
+	} 
+	g_hash_table_insert (jid_sids_table,
+			     g_strdup (sid),
+			     GUINT_TO_POINTER (id));
+}
+
 static LmHandlerResult
+jabber_ft_iq_streamhost (LmConnection *conn,
+			 LmMessage    *m,
+			 GossipJabber *jabber)
+{
+	GossipJabberFTs *fts;
+	LmMessageNode   *node;
+
+	const gchar   *attr;
+	const gchar   *iq_id;
+
+	node = lm_message_node_get_child (m->node, "query");
+	if(!node) {
+		/* child node is not a query */
+		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	}
+	attr = lm_message_node_get_attribute (node, "xmlns");
+	if(!attr || strcmp (attr, XMPP_BYTESTREAMS_PROTOCOL) != 0) {
+		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	}
+	node = lm_message_node_get_child (node, "streamhost-used");
+	if(!node) {
+		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	}
+	attr = lm_message_node_get_attribute (node, "jid");
+	if(!attr) {
+		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	}
+	iq_id = lm_message_node_get_attribute (m->node, "id");
+	fts = gossip_jabber_get_fts (jabber);
+	lm_bs_session_activate_streamhost (fts->bs_session, iq_id, attr);
+	return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+static LmHandlerResult
+jabber_ft_iq_query (LmConnection   *conn,
+		    LmMessage      *m,
+		    GossipJabber   *jabber)
+{
+	LmMessageNode   *child;
+	LmMessageNode   *node;
+	GossipJabberFTs *fts;
+	const gchar     *xmlns;
+	const gchar     *sid;
+	const gchar     *iq_id;
+	const gchar     *host;
+	const gchar     *port;
+	const gchar     *jid;
+	guint            id;
+
+	node = lm_message_node_get_child (m->node, "query");
+	if(!node) {
+		/* child node is not a query */
+		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	}
+	xmlns = lm_message_node_get_attribute (node, "xmlns");
+	if(!xmlns || strcmp (xmlns, XMPP_BYTESTREAMS_PROTOCOL) != 0) {
+		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	}
+	sid = lm_message_node_get_attribute (node, "sid");
+	if(!sid) {
+		/* cannot identify transfer */
+		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	}
+	jid = lm_message_node_get_attribute (m->node, "from");
+	if (!jid) {
+		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	}
+	id = guess_id_by_sid_and_sender (jabber, sid, jid);
+	if (id == 0) {
+		/* previos records for transfer doesn't exist */
+		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	}
+	iq_id = lm_message_node_get_attribute (m->node, "id");
+	fts = gossip_jabber_get_fts (jabber);
+	lm_bs_session_set_iq_id (fts->bs_session, id, iq_id);
+
+	/* get all children named "streamhost" */
+	for (child = node->children; child; child = child->next) {
+		if (strcmp (child->name, "streamhost") == 0) {
+		/* this id is needed for the reply stanza 'streamhost-used' */
+			host = lm_message_node_get_attribute (child, 
+							      "host");
+			port = lm_message_node_get_attribute (child, 
+							      "port");
+			jid = lm_message_node_get_attribute (child,
+							     "jid");
+			if(!host || !port || !jid) {
+				continue;
+			}
+			lm_bs_session_add_streamhost (fts->bs_session,
+						      id,
+						      host,
+						      port,
+						      jid);
+		}
+	}
+	lm_message_unref (m);
+	return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+static void 
+jabber_ft_send_streamhosts (LmConnection *conn,
+			    GossipJabber *jabber,
+			    GossipFT     *ft)
+{
+	GossipJabberFTs *fts;
+	LmMessage       *m;
+	LmMessageNode   *node;
+	gint             file_id;
+	const gchar     *to_str;
+	const gchar     *jid_str;
+	const gchar     *iq_id;
+	const gchar     *sid;
+
+	gchar           *local_host;
+	guint            port;
+	gchar           *port_str;
+	guint            id;
+
+	
+	fts = gossip_jabber_get_fts (jabber);
+	g_return_if_fail (fts != NULL);
+	file_id = gossip_ft_get_id (ft);
+	sid = gossip_ft_get_sid (ft);
+	to_str = g_hash_table_lookup (fts->remote_ids,
+				      GUINT_TO_POINTER(file_id));
+	m = lm_message_new_with_sub_type (to_str,
+					  LM_MESSAGE_TYPE_IQ,
+					  LM_MESSAGE_SUB_TYPE_SET);
+	jid_str = lm_connection_get_full_jid (conn);
+	id = guess_id_by_sid_and_sender (jabber, sid, to_str);
+	if (id == 0) {
+		/* previos records for transfer doesn't exist */
+		return;
+	}
+	port = lm_bs_session_start_listener (fts->bs_session);
+	port_str = g_strdup_printf ("%d", port);
+	node = lm_message_node_add_child (m->node, "query", NULL);
+	lm_message_node_set_attributes (node,
+					"xmlns",
+					XMPP_BYTESTREAMS_PROTOCOL,
+					"mode",
+					"tcp",
+					"sid",
+					sid,
+					NULL);
+	local_host = lm_connection_get_client_host (conn);
+	node = lm_message_node_add_child (node, "streamhost", NULL);
+	lm_message_node_set_attributes (node,
+					"host",
+					local_host,
+					"jid",
+					jid_str,
+					"port",
+					port_str);
+	
+	lm_bs_session_add_streamhost (fts->bs_session,
+				      id,
+				      local_host,
+				      port_str,
+				      jid_str);
+	lm_connection_send (conn, m, NULL);
+	iq_id = lm_message_node_get_attribute (m->node, "id");
+	lm_bs_session_set_iq_id (fts->bs_session, id, iq_id);
+	g_free (port_str);
+	lm_message_unref (m);
+}
+
+
+static LmHandlerResult
+jabber_ft_feature_result (LmConnection   *conn,
+			  LmMessage      *m,
+			  GossipJabber   *jabber)
+{
+	GossipJabberFTs *fts;
+	GossipFT        *ft;
+	const gchar     *id_str;
+	LmMessageNode   *node;
+	const gchar     *attr;
+
+	fts = gossip_jabber_get_fts (jabber);
+	g_return_val_if_fail (fts != NULL,
+			      LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS);
+
+	node = lm_message_node_get_child (m->node, "si");
+	/* already tested in jabber_ft_iq_si_handler */
+	g_return_val_if_fail (node != NULL, 
+			      LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS);
+	node = lm_message_node_get_child (node, "feature");
+	if (!node) {
+		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	}
+	attr = lm_message_node_get_attribute (node, "xmlns");
+	if (!attr || strcmp (attr, XMPP_FEATURE_XMLNS) != 0) {
+		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	}
+	node = lm_message_node_get_child (node, "x");
+	if (!node) {
+		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	}
+	attr = lm_message_node_get_attribute (node, "type");
+	if (!attr || strcmp (attr, "submit") != 0) {
+		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	}
+	node = lm_message_node_get_child (node, "field");
+	if (!node) {
+		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	}
+	attr = lm_message_node_get_attribute (node, "var");
+	if (!attr || strcmp (attr, "stream-method") != 0) {
+		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	}
+	node = lm_message_node_get_child (node, "value");
+	if (!node) {
+		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	}
+	attr = lm_message_node_get_value (node);
+	if (!attr || strcmp (attr, XMPP_BYTESTREAMS_PROTOCOL) != 0) {
+		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	}
+	id_str = lm_message_node_get_attribute (m->node, "id");
+	ft = g_hash_table_lookup (fts->ft_ids, id_str);
+	if (!ft) {
+		return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+	}
+	jabber_ft_send_streamhosts (conn, jabber, ft);
+	return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+static LmHandlerResult
 jabber_ft_iq_si_handler (LmMessageHandler *handler,
 			 LmConnection     *conn,
 			 LmMessage        *m,
@@ -172,7 +483,11 @@
 
 	node = lm_message_node_get_child (m->node, "si");
 	if (!node) {
-		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+		if (subtype == LM_MESSAGE_SUB_TYPE_SET) {
+			return jabber_ft_iq_query (conn, m, jabber);
+		} else if (subtype == LM_MESSAGE_SUB_TYPE_RESULT) {
+			return jabber_ft_iq_streamhost (conn, m, jabber);
+		}
 	}
 
 	xmlns = lm_message_node_get_attribute (node, "xmlns");
@@ -185,6 +500,9 @@
 			jabber_ft_handle_request (jabber, m);
 			return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
 		}
+		if (subtype == LM_MESSAGE_SUB_TYPE_RESULT) {
+			return jabber_ft_feature_result (conn, m, jabber);
+		}
 	}
 
 	/* if a get, return error for unsupported IQ */
@@ -215,28 +533,30 @@
 	const gchar     *file_name;
 	const gchar     *file_size;
 	const gchar     *file_mime;
+	const gchar     *sid;
 
 	fts = gossip_jabber_get_fts (jabber);
 	g_return_if_fail (fts != NULL);
 
+	from_str = lm_message_node_get_attribute (m->node, "from");
 	id_str = lm_message_node_get_attribute (m->node, "id");
-
-	from_str = lm_message_node_get_attribute (m->node, "from");
 	from = gossip_jabber_get_contact_from_jid (jabber, from_str, NULL, TRUE);
 
 	node = lm_message_node_find_child (m->node, "si");
+	sid = lm_message_node_get_attribute (node, "id");
 	file_mime = lm_message_node_get_attribute (node, "mime-type");
 
 	node = lm_message_node_find_child (m->node, "file");
 	file_name = lm_message_node_get_attribute (node, "name");
 	file_size = lm_message_node_get_attribute (node, "size");
-
+	
 	ft = g_object_new (GOSSIP_TYPE_FT,
 			   "type", GOSSIP_FT_TYPE_RECEIVING,
 			   "contact", from,
 			   "file-name", file_name,
 			   "file-size", g_ascii_strtoull (file_size, NULL, 10),
 			   "file-mime-type", file_mime,
+			   "sid", sid,
 			   NULL);
 
 	g_object_get (ft, "id", &id, NULL);
@@ -251,6 +571,7 @@
 	g_hash_table_insert (fts->str_ids, GUINT_TO_POINTER (id), g_strdup (id_str));
 	g_hash_table_insert (fts->ft_ids, g_strdup (id_str), ft);
 	g_hash_table_insert (fts->remote_ids, GUINT_TO_POINTER (id), g_strdup (from_str));
+	add_jid_sid_to_table (fts, from_str, sid, id);
 
 	/* signal */
 	g_signal_emit_by_name (fts->jabber,
@@ -411,6 +732,13 @@
 	return to;
 }
 
+static gchar *         jabber_ft_get_unique_sid       () {
+	static guint id = 0;
+	id++;
+	return g_strdup_printf("GOSSIPSID%d", id);
+}
+
+
 /*
  * sending
  */
@@ -432,6 +760,8 @@
 	gchar         *file_name;
 	gchar         *mime_type;
 	gboolean       ok;
+	const gchar   *sid;
+	gchar         *file_path;
 
 	g_return_val_if_fail (fts != NULL, 0);
 	g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), 0);
@@ -457,14 +787,16 @@
 
 	id_str = lm_message_node_get_attribute (m->node, "id");
 
-	lm_message_node_set_attribute (node, "from",
-				       gossip_contact_get_id (own_contact));
+	/* bare jids are not allowed in FT */
+	/* lm_message_node_set_attribute (node, "from",
+				       gossip_contact_get_id (own_contact)); */
 
 	node = lm_message_node_add_child (node, "si", NULL);
+	sid = jabber_ft_get_unique_sid();
 	lm_message_node_set_attributes (node,
 					"profile", XMPP_FILE_TRANSFER_PROFILE,
 					"mime-type", mime_type,
-					"id", "ft1",
+					"id", sid,
 					"xmlns", XMPP_FILE_TRANSFER_XMLNS,
 					NULL);
 
@@ -496,12 +828,8 @@
 
 	field_node = node;
 
-	/* we do not support this yet */
-/*  	node = lm_message_node_add_child (field_node, "option", NULL);  */
-/*  	lm_message_node_add_child (node, "value", XMPP_BYTESTREAMS_PROTOCOL);  */
-
 	node = lm_message_node_add_child (field_node, "option", NULL);
-	lm_message_node_add_child (node, "value", XMPP_IBB_PROTOCOL);
+	lm_message_node_add_child (node, "value", XMPP_BYTESTREAMS_PROTOCOL);
 
 	/* create object */
 	ft = g_object_new (GOSSIP_TYPE_FT,
@@ -510,6 +838,7 @@
 			   "file-name", file_name,
 			   "file-size", g_ascii_strtoull (file_size, NULL, 10),
 			   "file-mime-type", mime_type,
+			   "sid", sid,
 			   NULL);
 
 	g_object_get (ft, "id", &id, NULL);
@@ -521,7 +850,18 @@
 	g_hash_table_insert (fts->str_ids, GUINT_TO_POINTER (id), g_strdup (id_str));
 	g_hash_table_insert (fts->ft_ids, g_strdup (id_str), ft);
 	g_hash_table_insert (fts->remote_ids, GUINT_TO_POINTER (id), to);
+	add_jid_sid_to_table (fts, to, sid, id);
 
+	file_path = g_filename_from_uri (file, NULL, NULL);
+	lm_bs_session_send_file (fts->bs_session,
+				 connection,
+				 id,
+				 sid,
+				 to,
+				 file_path,
+				 gossip_ft_get_file_size(ft));
+	g_free (file_path);
+
 	/* send */
 	lm_connection_send (connection, m, NULL);
 	lm_message_unref (m);
@@ -541,20 +881,21 @@
 	GossipContact *own_contact;
 	const gchar   *id_str;
 	const gchar   *to_str;
+	const gchar   *file_uri;
+	gchar         *file_path;
 	LmConnection  *connection;
 	LmMessage     *m;
 	LmMessageNode *node;
-
+	GossipFT      *ft;
 	g_return_if_fail (fts != NULL);
 
 	gossip_debug (DEBUG_DOMAIN, "ID[%d] Accepting file transfer", id);
 
 	connection = gossip_jabber_get_connection (fts->jabber);
 	own_contact = gossip_jabber_get_own_contact (fts->jabber);
-
 	to_str = g_hash_table_lookup (fts->remote_ids, GUINT_TO_POINTER (id));
 	id_str = g_hash_table_lookup (fts->str_ids, GUINT_TO_POINTER (id));
-
+	ft = g_hash_table_lookup (fts->ft_ids, id_str);
 	if (!to_str) {
 		g_warning ("ProtocolFT: ID[%d] Could not get remote JID", id);
 		return;
@@ -564,14 +905,24 @@
 		g_warning ("ProtocolFT: ID[%d] Could not get original message id", id);
 		return;
 	}
-
+	file_uri = gossip_ft_get_location (ft);
+	file_path = g_filename_from_uri (file_uri, NULL, NULL);
+	lm_bs_session_receive_file (fts->bs_session,
+				    connection,
+				    id,
+				    gossip_ft_get_sid (ft),
+				    to_str,
+				    file_path,
+				    gossip_ft_get_file_size (ft));
+	g_free (file_path);
 	m = lm_message_new_with_sub_type (to_str,
 					  LM_MESSAGE_TYPE_IQ,
 					  LM_MESSAGE_SUB_TYPE_RESULT);
 	node = m->node;
 	lm_message_node_set_attributes (node,
 					"id", id_str,
-					"from", gossip_contact_get_id (own_contact),
+					/* don't set a bare JID  */
+					/* "from", gossip_contact_get_id (own_contact), */
 					NULL);
 
 	node = lm_message_node_add_child (node, "si", NULL);
@@ -596,7 +947,7 @@
 					NULL);
 
 	/* choose method here, currently we only support one */
-	lm_message_node_add_child (node, "value", XMPP_IBB_PROTOCOL);
+	lm_message_node_add_child (node, "value", XMPP_BYTESTREAMS_PROTOCOL);
 
 	/* send */
 	lm_connection_send (connection, m, NULL);
@@ -657,6 +1008,9 @@
 	g_hash_table_remove (fts->str_ids, &id);
 	g_hash_table_remove (fts->ft_ids, id_str);
 	g_hash_table_remove (fts->remote_ids, &id);
+	
+	/* remove it from bt session */
+	lm_bs_session_remove_transfer (fts->bs_session, id);
 }
 
 void
@@ -664,7 +1018,7 @@
 			 GossipFTId       id)
 {
 	g_return_if_fail (fts != NULL);
-
+	lm_bs_session_remove_transfer (fts->bs_session, id);
 /* 	room = (JabberChatroom*) g_hash_table_lookup (chatrooms->room_id_hash,  */
 /* 						       GINT_TO_POINTER (id)); */
 }
@@ -887,3 +1241,5 @@
 	lm_message_unref (m);
 }
 
+
+
Index: protocols/jabber/gossip-jabber-disco.c
===================================================================
--- protocols/jabber/gossip-jabber-disco.c	(revision 2282)
+++ protocols/jabber/gossip-jabber-disco.c	(working copy)
@@ -38,9 +38,14 @@
 #define DEBUG_DOMAIN "JabberDisco"
 
 /* Common XMPP XML namespaces we use. */
-#define XMPP_DISCO_ITEMS_XMLNS "http://jabber.org/protocol/disco#items"
-#define XMPP_DISCO_INFO_XMLNS  "http://jabber.org/protocol/disco#info"
+#define XMPP_DISCO_ITEMS_XMLNS          "http://jabber.org/protocol/disco#items"
+#define XMPP_DISCO_INFO_XMLNS           "http://jabber.org/protocol/disco#info"
 
+#define XMPP_SI_FEATURE                 "http://jabber.org/protocol/si"
+#define XMPP_FILE_TRANSFER_FEATURE      "http://jabber.org/protocol/si/profile/file-transfer"
+#define XMPP_BYTESTREAMS_FEATURE        "http://jabber.org/protocol/bytestreams"
+#define XMPP_MUC_FEATURE                "http://jabber.org/protocol/muc"
+
 /* In seconds */
 #define DISCO_TIMEOUT      20
 #define DISCO_INFO_TIMEOUT 15
@@ -126,7 +131,12 @@
 static void               jabber_disco_handle_info              (GossipJabberDisco     *disco,
 								 LmMessage             *m,
 								 gpointer               user_data);
+static LmHandlerResult    jabber_disco_info_handler             (LmMessageHandler      *handler,
+								 LmConnection          *conn,
+								 LmMessage             *m,
+								 GossipJabber          *jabber);
 
+
 static GHashTable *discos = NULL;
 
 static void
@@ -705,6 +715,60 @@
 	}
 }
 
+static LmHandlerResult
+jabber_disco_info_handler (LmMessageHandler *handler,
+			   LmConnection     *connection,
+			   LmMessage        *request_message,
+			   GossipJabber     *jabber)
+{
+	LmMessageSubType  subtype;
+	LmMessage        *m;
+	LmMessageNode    *node;
+	LmMessageNode    *q_node;
+	const gchar      *xmlns;
+	const gchar      *to_str;
+	const gchar      *id_str;
+
+	subtype = lm_message_get_sub_type (request_message);
+	if (subtype != LM_MESSAGE_SUB_TYPE_GET) {
+		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	}
+	node = lm_message_node_get_child (request_message->node, "query");
+	if(!node) {
+		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	}
+	xmlns = lm_message_node_get_attribute(node, "xmlns");
+	if(!xmlns || strcmp(xmlns, XMPP_DISCO_INFO_XMLNS) != 0) {
+		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+	}
+	to_str = lm_message_node_get_attribute (request_message->node, "from");
+	id_str = lm_message_node_get_attribute (request_message->node, "id");
+	m = lm_message_new_with_sub_type (to_str,
+					  LM_MESSAGE_TYPE_IQ,
+					  LM_MESSAGE_SUB_TYPE_RESULT);
+	lm_message_node_set_attribute (m->node, "id", id_str);
+	q_node = lm_message_node_add_child (m->node, "query", NULL);
+	lm_message_node_set_attribute (q_node, "xmlns", XMPP_DISCO_INFO_XMLNS);
+	node = lm_message_node_add_child (q_node, "identity", NULL);
+	lm_message_node_set_attribute (node, "category", "client");
+	lm_message_node_set_attribute (node, "type", "pc");
+	lm_message_node_set_attribute (node, "name", PACKAGE_STRING);
+	node = lm_message_node_add_child (q_node, "feature", NULL);
+	lm_message_node_set_attribute (node, "var", XMPP_DISCO_INFO_XMLNS);
+	node = lm_message_node_add_child (q_node, "feature", NULL);
+	lm_message_node_set_attribute (node, "var", XMPP_SI_FEATURE);
+	node = lm_message_node_add_child (q_node, "feature", NULL);
+	lm_message_node_set_attribute (node, "var", XMPP_FILE_TRANSFER_FEATURE);
+	node = lm_message_node_add_child (q_node, "feature", NULL);
+	lm_message_node_set_attribute (node, "var", XMPP_BYTESTREAMS_FEATURE);
+	node = lm_message_node_add_child (q_node, "feature", NULL);
+	lm_message_node_set_attribute (node, "var", XMPP_MUC_FEATURE);
+	lm_connection_send (connection, m, NULL);
+	lm_message_unref (m);
+
+	return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
 GossipJabberDisco *
 gossip_jabber_disco_request (GossipJabber              *jabber,
 			     const char                *to,
@@ -1152,3 +1216,23 @@
 	return FALSE;
 }
 
+
+
+
+void
+gossip_jabber_disco_init(GossipJabber *jabber) 
+{
+	LmConnection     *connection;
+	LmMessageHandler *handler;
+
+	g_return_if_fail (GOSSIP_IS_JABBER (jabber));
+
+	connection = gossip_jabber_get_connection(jabber);
+	handler = lm_message_handler_new ((LmHandleMessageFunction) jabber_disco_info_handler,
+					  jabber, NULL);
+	lm_connection_register_message_handler (connection,
+						handler,
+						LM_MESSAGE_TYPE_IQ,
+						LM_HANDLER_PRIORITY_NORMAL);
+	lm_message_handler_unref (handler);
+}
Index: protocols/jabber/gossip-jabber-disco.h
===================================================================
--- protocols/jabber/gossip-jabber-disco.h	(revision 2282)
+++ protocols/jabber/gossip-jabber-disco.h	(working copy)
@@ -70,6 +70,7 @@
 gboolean               gossip_jabber_disco_item_has_type         (GossipJabberDiscoItem     *item,
 								  const gchar               *type);
 gboolean               gossip_jabber_disco_servers               (void);
+void                   gossip_jabber_disco_init                  (GossipJabber              *jabber);
 
 G_END_DECLS
 
Index: src/gossip-contact-list.c
===================================================================
--- src/gossip-contact-list.c	(revision 2282)
+++ src/gossip-contact-list.c	(working copy)
@@ -2112,7 +2112,7 @@
 	g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
 
 	can_show_log = gossip_log_exists_for_contact (contact);
-	can_send_file = FALSE;
+	can_send_file = TRUE;
 
 	menu = contact_list_get_contact_menu (list,
 					      can_send_file,
Index: src/gossip-ft-window.c
===================================================================
--- src/gossip-ft-window.c	(revision 2282)
+++ src/gossip-ft-window.c	(working copy)
@@ -83,7 +83,13 @@
 static void ft_window_filechooser_response_cb  (GtkDialog          *dialog,
 						gint                response_id,
 						GossipContact      *contact);
+static void ft_window_save_filechooser_create  (FTData             *data);
 
+static void 
+ft_window_save_filechooser_response_cb 	       (GtkDialog          *dialog,
+						gint                response_id,
+						FTData             *data);
+
 void
 gossip_ft_window_init (GossipSession *session)
 {
@@ -348,7 +354,76 @@
 	gtk_widget_show (dialog);
 }
 
+
+
 static void
+ft_window_save_filechooser_response_cb (GtkDialog  *dialog,
+				        gint        response_id,
+				        FTData     *data)
+{
+	const gchar *file_location;
+	
+	if (response_id == GTK_RESPONSE_OK) {
+		file_location = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
+		/* TODO: some checks for existing files */
+		gossip_ft_set_location (data->ft, file_location);
+		
+		gossip_ft_provider_accept (GOSSIP_FT_PROVIDER (data->protocol),
+					   gossip_ft_get_id (data->ft));
+	} else {
+		gossip_ft_provider_decline (GOSSIP_FT_PROVIDER (data->protocol),
+					    gossip_ft_get_id (data->ft));
+	}
+	gtk_widget_destroy (GTK_WIDGET(dialog));
+	g_object_unref (data->protocol);
+	g_object_unref (data->ft);
+	if (data->vcard) {
+		g_object_unref (data->vcard);
+	}
+	g_free (data);
+}
+
+static void
+ft_window_save_filechooser_create (FTData *data)
+{
+	GtkWidget     *dialog;
+	GtkFileFilter *filter;
+	const gchar   *file_name;
+
+	gossip_debug (DEBUG_DOMAIN, "Creating filechooser...");
+
+	dialog = g_object_new (GTK_TYPE_FILE_CHOOSER_DIALOG,
+			       "action", GTK_FILE_CHOOSER_ACTION_SAVE,
+			       "select-multiple", FALSE,
+			       NULL);
+
+	gtk_window_set_title (GTK_WINDOW (dialog), _("Save file as..."));
+	file_name = gossip_ft_get_file_name (data->ft);
+	gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(dialog),
+					   file_name);
+	gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+				GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+				GTK_STOCK_SAVE, GTK_RESPONSE_OK,
+				NULL);
+
+	gtk_dialog_set_default_response (GTK_DIALOG (dialog),
+					 GTK_RESPONSE_OK);
+
+	g_signal_connect (dialog, "response",
+			  G_CALLBACK (ft_window_save_filechooser_response_cb),
+			  data);
+
+	/* filters */
+	filter = gtk_file_filter_new ();
+	gtk_file_filter_set_name (filter, "All Files");
+	gtk_file_filter_add_pattern (filter, "*");
+	gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+	gtk_widget_show (dialog);
+}
+
+
+static void
 ft_window_request_dialog_cb (GtkWidget *dialog,
 			     gint       response,
 			     FTData    *data)
@@ -364,8 +439,8 @@
 /* 	g_return_if_fail (!G_STR_EMPTY (file)); */
 
 	if (response == GTK_RESPONSE_YES) {
-		gossip_ft_provider_accept (GOSSIP_FT_PROVIDER (data->protocol),
-					   gossip_ft_get_id (data->ft));
+		ft_window_save_filechooser_create (data);
+		return;
 	} else {
 		gossip_ft_provider_decline (GOSSIP_FT_PROVIDER (data->protocol),
 					    gossip_ft_get_id (data->ft));
Index: libgossip/gossip-ft.c
===================================================================
--- libgossip/gossip-ft.c	(revision 2282)
+++ libgossip/gossip-ft.c	(working copy)
@@ -38,6 +38,8 @@
 	gchar         *file_name;
 	guint          file_size;
 	gchar         *file_mime_type;
+	gchar         *sid;
+	gchar         *location;
 };
 
 static void ft_class_init   (GossipFTClass *class);
@@ -60,6 +62,8 @@
 	PROP_FILE_NAME,
 	PROP_FILE_SIZE,
 	PROP_FILE_MIME_TYPE,
+	PROP_SID,
+	PROP_LOCATION
 };
 
 static gpointer parent_class = NULL;
@@ -157,6 +161,20 @@
 							      "The mime type (for example text/plain)",
 							      NULL,
 							      G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_SID,
+					 g_param_spec_string ("sid",
+							      "Session ID",
+							      "ID to match stanzas assosiated to this file",
+							      NULL,
+							      G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_SID,
+					 g_param_spec_string ("location",
+							      "Location",
+							      "Where to store the file on the file system (URI)",
+							      NULL,
+							      G_PARAM_READWRITE));
 
 	g_type_class_add_private (object_class, sizeof (GossipFTPriv));
 }
@@ -178,6 +196,8 @@
 	priv->file_name      = NULL;
 	priv->file_size      = 0;
 	priv->file_mime_type = NULL;
+	priv->sid            = NULL;
+	priv->location       = NULL;
 }
 
 static void
@@ -193,6 +213,8 @@
 
 	g_free (priv->file_name);
 	g_free (priv->file_mime_type);
+	g_free (priv->sid);
+	g_free (priv->location);
 
 	(G_OBJECT_CLASS (parent_class)->finalize) (object);
 }
@@ -226,6 +248,12 @@
 	case PROP_FILE_MIME_TYPE:
 		g_value_set_string (value, priv->file_mime_type);
 		break;
+	case PROP_SID:
+		g_value_set_string (value, priv->sid);
+		break;
+	case PROP_LOCATION:
+		g_value_set_string (value, priv->location);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
 		break;
@@ -263,6 +291,14 @@
 		gossip_ft_set_file_mime_type (GOSSIP_FT (object),
 					      g_value_get_string (value));
 		break;
+	case PROP_SID:
+		gossip_ft_set_sid (GOSSIP_FT (object),
+				   g_value_get_string (value));
+		break;
+	case PROP_LOCATION:
+		gossip_ft_set_location (GOSSIP_FT (object),
+					g_value_get_string (value));
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
 		break;
@@ -421,6 +457,58 @@
 	priv->file_mime_type = g_strdup (file_mime_type);
 }
 
+const gchar *
+gossip_ft_get_sid (GossipFT *ft)
+{
+	GossipFTPriv *priv;
+
+	g_return_val_if_fail (GOSSIP_IS_FT (ft), NULL);
+
+	priv = GET_PRIV (ft);
+
+	return priv->sid;
+}
+
+void
+gossip_ft_set_sid (GossipFT    *ft,
+		   const gchar *sid)
+{
+	GossipFTPriv *priv;
+
+	g_return_if_fail (GOSSIP_IS_FT (ft));
+
+	priv = GET_PRIV (ft);
+
+	g_free (priv->sid);
+	priv->sid = g_strdup (sid);
+}
+
+const gchar *
+gossip_ft_get_location (GossipFT *ft)
+{
+	GossipFTPriv *priv;
+
+	g_return_val_if_fail (GOSSIP_IS_FT (ft), NULL);
+
+	priv = GET_PRIV (ft);
+
+	return priv->location;
+}
+
+void
+gossip_ft_set_location (GossipFT    *ft,
+			const gchar *location)
+{
+	GossipFTPriv *priv;
+
+	g_return_if_fail (GOSSIP_IS_FT (ft));
+
+	priv = GET_PRIV (ft);
+
+	g_free (priv->location);
+	priv->location = g_strdup (location);
+}
+
 gboolean
 gossip_ft_equal (gconstpointer a,
 		 gconstpointer b)
Index: libgossip/gossip-ft.h
===================================================================
--- libgossip/gossip-ft.h	(revision 2282)
+++ libgossip/gossip-ft.h	(working copy)
@@ -77,6 +77,12 @@
 const gchar *  gossip_ft_get_file_mime_type (GossipFT      *ft);
 void           gossip_ft_set_file_mime_type (GossipFT      *ft,
 					     const gchar   *file_mime_type);
+const gchar *  gossip_ft_get_sid            (GossipFT      *ft);
+void           gossip_ft_set_sid            (GossipFT      *ft,
+					     const gchar   *sid);
+const gchar *  gossip_ft_get_location       (GossipFT      *ft);
+void           gossip_ft_set_location       (GossipFT      *ft,
+					     const gchar   *location);
 
 gboolean       gossip_ft_equal              (gconstpointer  a,
 					     gconstpointer  b);
