changeset 21522:dc703f13449a

Fix a couple bugs in the Bonjour XEP-0065 implementation, mainly related to error handling, but also send a <streamhost-used /> result. Also fix a XEP-0096 bug where the SI profile wasn't being specified. These bring ft with gajim closer to working, but we aren't there yet.
author Daniel Atallah <daniel.atallah@gmail.com>
date Thu, 15 Nov 2007 14:53:53 +0000
parents 287ee978db2e
children 6c6e8f25ee62
files libpurple/protocols/bonjour/bonjour.c libpurple/protocols/bonjour/bonjour_ft.c libpurple/protocols/bonjour/bonjour_ft.h libpurple/protocols/bonjour/jabber.c libpurple/protocols/bonjour/jabber.h
diffstat 5 files changed, 182 insertions(+), 116 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/bonjour/bonjour.c	Thu Nov 15 14:22:06 2007 +0000
+++ b/libpurple/protocols/bonjour/bonjour.c	Thu Nov 15 14:53:53 2007 +0000
@@ -193,6 +193,11 @@
 	if (bonjour_group != NULL)
 		purple_blist_remove_group(bonjour_group);
 
+	/* Cancel any file transfers */
+	while (bd != NULL && bd->xfer_lists) {
+		purple_xfer_cancel_local(bd->xfer_lists->data);
+	}
+
 	g_free(bd);
 	connection->proto_data = NULL;
 }
--- a/libpurple/protocols/bonjour/bonjour_ft.c	Thu Nov 15 14:22:06 2007 +0000
+++ b/libpurple/protocols/bonjour/bonjour_ft.c	Thu Nov 15 14:53:53 2007 +0000
@@ -45,34 +45,39 @@
 static unsigned int next_id = 0;
 
 static void
-xep_ft_si_reject(PurpleXfer *xfer, char *to)
+xep_ft_si_reject(BonjourData *bd, const char *id, const char *to, const char *error_code, const char *error_type)
 {
 	xmlnode *error_node = NULL;
 	xmlnode *tmp_node = NULL;
 	XepIq *iq = NULL;
-	XepXfer *xf = NULL;
 
-	if(!to || !xfer)
-		return;
-	xf = xfer->data;
-	if(!xf)
+	g_return_if_fail(error_code != NULL);
+	g_return_if_fail(error_type != NULL);
+
+	if(!to || !id)
 		return;
 
 	purple_debug_info("bonjour", "xep file transfer stream initialization error.\n");
-	iq = xep_iq_new(xf->data, XEP_IQ_ERROR, to, xf->sid);
+	iq = xep_iq_new(bd, XEP_IQ_ERROR, to, purple_account_get_username(bd->jabber_data->account), id);
 	if(iq == NULL)
 		return;
 
 	error_node = xmlnode_new_child(iq->node, "error");
-	xmlnode_set_attrib(error_node, "code", "403");
-	xmlnode_set_attrib(error_node, "type", "cancel");
+	xmlnode_set_attrib(error_node, "code", error_code);
+	xmlnode_set_attrib(error_node, "type", error_type);
+
+	/* TODO: Make this better */
+	if (!strcmp(error_code, "403")) {
+		tmp_node = xmlnode_new_child(error_node, "forbidden");
+		xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas");
 
-	tmp_node = xmlnode_new_child(error_node, "forbidden");
-	xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas");
-
-	tmp_node = xmlnode_new_child(error_node, "text");
-	xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas");
-	xmlnode_insert_data(tmp_node, "Offer Declined", -1);
+		tmp_node = xmlnode_new_child(error_node, "text");
+		xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas");
+		xmlnode_insert_data(tmp_node, "Offer Declined", -1);
+	} else if (!strcmp(error_code, "404")) {
+		tmp_node = xmlnode_new_child(error_node, "item-not-found");
+		xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas");
+	}
 
 	xep_iq_send_and_free(iq);
 }
@@ -85,8 +90,14 @@
 
 static void bonjour_xfer_request_denied(PurpleXfer *xfer)
 {
+	XepXfer *xf = NULL;
+
 	purple_debug_info("bonjour", "Bonjour-xfer-request-denied.\n");
-	xep_ft_si_reject(xfer, xfer->who);
+
+	xf = xfer->data;
+	if(xf)
+		xep_ft_si_reject(xf->data, xf->sid, xfer->who, "403", "cancel");
+
 	bonjour_free_xfer(xfer);
 }
 
@@ -193,7 +204,7 @@
 	/* Assign stream id. */
 	memset(buf, 0, 32);
 	g_snprintf(buf, sizeof(buf), "%u", next_id++);
-	iq = xep_iq_new(xf->data, XEP_IQ_SET, to, buf);
+	iq = xep_iq_new(xf->data, XEP_IQ_SET, to, purple_account_get_username(bd->jabber_data->account), buf);
 	if(iq == NULL)
 		return;
 
@@ -202,6 +213,7 @@
 	/*Construct Stream initialization offer message.*/
 	si_node = xmlnode_new_child(iq->node, "si");
 	xmlnode_set_namespace(si_node, "http://jabber.org/protocol/si");
+	xmlnode_set_attrib(si_node, "profile", "http://jabber.org/protocol/si/profile/file-transfer");
 
 	file = xmlnode_new_child(si_node, "file");
 	xmlnode_set_namespace(file, "http://jabber.org/protocol/si/profile/file-transfer");
@@ -236,37 +248,6 @@
 }
 
 static void
-xep_ft_si_reject2(BonjourData *bd, const char *to, const char *sid)
-{
-	xmlnode *error_node = NULL;
-	xmlnode *tmp_node = NULL;
-	XepIq *iq = NULL;
-
-	g_return_if_fail(bd != NULL);
-
-	if(!to || !sid)
-		return;
-
-	purple_debug_info("bonjour", "xep file transfer stream initialization error.\n");
-	iq = xep_iq_new(bd, XEP_IQ_ERROR, to, sid);
-	if(iq == NULL)
-		return;
-
-	error_node = xmlnode_new_child(iq->node, "error");
-	xmlnode_set_attrib(error_node, "code", "403");
-	xmlnode_set_attrib(error_node, "type", "cancel");
-
-	tmp_node = xmlnode_new_child(error_node, "forbidden");
-	xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas");
-
-	tmp_node = xmlnode_new_child(error_node, "text");
-	xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas");
-	xmlnode_insert_data(tmp_node, "Offer Declined", -1);
-
-	xep_iq_send_and_free(iq);
-}
-
-static void
 xep_ft_si_result(PurpleXfer *xfer, char *to)
 {
 	xmlnode *si_node = NULL;
@@ -276,6 +257,7 @@
 	xmlnode *x = NULL;
 	XepIq *iq = NULL;
 	XepXfer *xf = NULL;
+	BonjourData *bd;
 
 	if(!to || !xfer)
 		return;
@@ -283,13 +265,16 @@
 	if(!xf)
 		return;
 
+	bd = xf->data;
+
 	purple_debug_info("bonjour", "xep file transfer stream initialization result.\n");
-	iq = xep_iq_new(xf->data, XEP_IQ_RESULT, to, xf->sid);
+	iq = xep_iq_new(bd, XEP_IQ_RESULT, to, purple_account_get_username(bd->jabber_data->account), xf->sid);
 	if(iq == NULL)
 		return;
 
 	si_node = xmlnode_new_child(iq->node, "si");
 	xmlnode_set_namespace(si_node, "http://jabber.org/protocol/si");
+	/*xmlnode_set_attrib(si_node, "profile", "http://jabber.org/protocol/si/profile/file-transfer");*/
 
 	feature = xmlnode_new_child(si_node, "feature");
 	xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg");
@@ -331,6 +316,7 @@
 			purple_proxy_connect_cancel(xf->proxy_connection);
 		if (xf->listen_data != NULL)
 			purple_network_listen_cancel(xf->listen_data);
+		g_free(xf->iq_id);
 		g_free(xf->jid);
 		g_free(xf->proxy_host);
 		g_free(xf->buddy_ip);
@@ -364,7 +350,9 @@
 
 	purple_debug_info("bonjour", "Bonjour-new-xfer bd=%p data=%p.\n", bd, xep_xfer->data);
 
-	xep_xfer->mode = XEP_BYTESTREAMS | XEP_IBB;
+	/* We don't support IBB yet */
+	/*xep_xfer->mode = XEP_BYTESTREAMS | XEP_IBB;*/
+	xep_xfer->mode = XEP_BYTESTREAMS;
 	xep_xfer->sid = NULL;
 
 	purple_xfer_set_init_fnc(xfer, bonjour_xfer_init);
@@ -379,14 +367,15 @@
 void
 bonjour_send_file(PurpleConnection *gc, const char *who, const char *file)
 {
+	PurpleXfer *xfer = NULL;
 
-	PurpleXfer *xfer = NULL;
-	if(gc == NULL || who == NULL)
-		return;
+	g_return_if_fail(gc != NULL);
+	g_return_if_fail(who != NULL);
+
 	purple_debug_info("bonjour", "Bonjour-send-file to=%s.\n", who);
+
 	xfer = bonjour_new_xfer(gc, who);
-	if(xfer == NULL)
-		return;
+
 	if (file)
 		purple_xfer_request_accepted(xfer, file);
 	else
@@ -401,11 +390,10 @@
 	BonjourBuddy *bd = NULL;
 	XepXfer *xf = NULL;
 
-	if(xfer == NULL)
-		return;
 	xf = (XepXfer*)xfer->data;
 	if(xf == NULL)
 		return;
+
 	purple_debug_info("bonjour", "Bonjour-xfer-init.\n");
 
 	buddy = purple_find_buddy(xfer->account, xfer->who);
@@ -419,25 +407,19 @@
 		/* initiate file transfer, send SI offer. */
 		purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_SEND.\n");
 		xep_ft_si_offer(xfer, xfer->who);
-
 	} else {
 		/* accept file transfer request, send SI result. */
 		xep_ft_si_result(xfer, xfer->who);
 		purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_RECEIVE.\n");
 	}
-	return;
 }
 
-
 void
 xep_si_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb)
 {
-	const char *type = NULL, *from = NULL, *id = NULL;
-	xmlnode *si = NULL, *file = NULL;
+	const char *type, *id;
 	BonjourData *bd = NULL;
 	PurpleXfer *xfer = NULL;
-	const char *filename = NULL, *filesize_str = NULL;
-	int filesize = 0, option = XEP_BYTESTREAMS;
 
 	if(pc == NULL || packet == NULL || pb == NULL)
 		return;
@@ -448,51 +430,73 @@
 	purple_debug_info("bonjour", "xep-si-parse.\n");
 
 	type = xmlnode_get_attrib(packet, "type");
-	from = pb->name;
 	id = xmlnode_get_attrib(packet, "id");
 	if(type) {
-		if(!strcmp(type, "set")){
-			si = xmlnode_get_child(packet,"si");
+		if(!strcmp(type, "set")) {
+			const char *profile;
+			xmlnode *si;
+			gboolean parsed_receive = FALSE;
+
+			si = xmlnode_get_child(packet, "si");
+
 			purple_debug_info("bonjour", "si offer Message type - SET.\n");
-			file = xmlnode_get_child(si, "file");
-			/**/
-			filename = xmlnode_get_attrib(file, "name");
-			if((filesize_str = xmlnode_get_attrib(file, "size")))
-				filesize = atoi(filesize_str);
-			bonjour_xfer_receive(pc, id, from, filesize, filename, option);
-		} else if(!strcmp(type, "result")){
-			si = xmlnode_get_child(packet,"si");
+			if (si && (profile = xmlnode_get_attrib(si, "profile"))
+					&& !strcmp(profile, "http://jabber.org/protocol/si/profile/file-transfer")) {
+				const char *filename = NULL, *filesize_str = NULL;
+				int filesize = 0;
+				xmlnode *file;
+
+				if ((file = xmlnode_get_child(si, "file"))) {
+					filename = xmlnode_get_attrib(file, "name");
+					if((filesize_str = xmlnode_get_attrib(file, "size")))
+						filesize = atoi(filesize_str);
+				}
+
+				/* TODO: Make sure that it is advertising a bytestreams transfer */
+
+				bonjour_xfer_receive(pc, id, pb->name, filesize, filename, XEP_BYTESTREAMS);
+
+				parsed_receive = TRUE;
+			}
+
+			if (!parsed_receive) {
+				purple_debug_info("bonjour", "rejecting unrecognized si SET offer.\n");
+				xep_ft_si_reject((BonjourData *)pc->proto_data, id, pb->name, "403", "cancel");
+				/*TODO: Send Cancel (501) */
+			}
+		} else if(!strcmp(type, "result")) {
 			purple_debug_info("bonjour", "si offer Message type - RESULT.\n");
-			xfer = bonjour_si_xfer_find(bd, id, from);
-			if(xfer == NULL){
+
+			xfer = bonjour_si_xfer_find(bd, id, pb->name);
+
+			if(xfer == NULL) {
 				purple_debug_info("bonjour", "xfer find fail.\n");
-				xep_ft_si_reject2((BonjourData *)pc->proto_data, from, id);
-			} else {
+				xep_ft_si_reject((BonjourData *)pc->proto_data, id, pb->name, "403", "cancel");
+			} else
 				bonjour_bytestreams_init(xfer);
-			}
-		} else if(!strcmp(type, "error")){
+
+		} else if(!strcmp(type, "error")) {
 			purple_debug_info("bonjour", "si offer Message type - ERROR.\n");
-			xfer = bonjour_si_xfer_find(bd, id, from);
-			if(xfer == NULL){
+
+			xfer = bonjour_si_xfer_find(bd, id, pb->name);
+
+			if(xfer == NULL)
 				purple_debug_info("bonjour", "xfer find fail.\n");
-			} else {
+			else
 				purple_xfer_cancel_remote(xfer);
-			}
-		} else {
+		} else
 			purple_debug_info("bonjour", "si offer Message type - Unknown-%d.\n", type);
-		}
 	}
 }
 
 void
 xep_bytestreams_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb)
 {
-	const char *type = NULL, *from = NULL, *id = NULL;
+	const char *type = NULL, *from = NULL;
 	xmlnode *query = NULL, *streamhost = NULL;
 	BonjourData *bd = NULL;
 	PurpleXfer *xfer = NULL;
 	XepXfer *xf = NULL;
-	const char *jid=NULL, *host=NULL, *port=NULL;
 	int portnum;
 
 	if(pc == NULL || packet == NULL || pb == NULL)
@@ -508,13 +512,20 @@
 	from = pb->name;
 	query = xmlnode_get_child(packet,"query");
 	if(type) {
-		if(!strcmp(type, "set")){
+		if(!strcmp(type, "set")) {
+			const char *iq_id, *sid;
+			gboolean found = FALSE;
+
 			purple_debug_info("bonjour", "bytestream offer Message type - SET.\n");
 
-			id = xmlnode_get_attrib(query, "sid");
-			xfer = bonjour_si_xfer_find(bd, id, from);
+			iq_id = xmlnode_get_attrib(packet, "id");
 
-			if(xfer){
+			sid = xmlnode_get_attrib(query, "sid");
+			xfer = bonjour_si_xfer_find(bd, sid, from);
+
+			if(xfer) {
+				const char *jid, *host, *port;
+
 				xf = (XepXfer*)xfer->data;
 				for(streamhost = xmlnode_get_child(query, "streamhost");
 						streamhost;
@@ -526,15 +537,17 @@
 					   (portnum = atoi(port))) {
 
 						if(!strcmp(host, xf->buddy_ip)) {
+							g_free(xf->iq_id);
+							xf->iq_id = g_strdup(iq_id);
 							xf->jid = g_strdup(jid);
 							xf->proxy_host = g_strdup(host);
 							xf->proxy_port = portnum;
 							purple_debug_info("bonjour", "bytestream offer parse"
 									  "jid=%s host=%s port=%d.\n", jid, host, portnum);
 							bonjour_bytestreams_connect(xfer);
+							found = TRUE;
 							break;
 						}
-
 					} else {
 						purple_debug_info("bonjour", "bytestream offer Message parse error.\n");
 					}
@@ -543,8 +556,15 @@
 
 			}
 
+			if (!found) {
+				purple_debug_error("bonjour", "Didn't find an acceptable streamhost.\n");
+
+				if (iq_id)
+					xep_ft_si_reject(bd, iq_id, xfer->who, "404", "cancel");
+			}
+
 		} else {
-			purple_debug_info("bonjour", "bytestream offer Message type - Unknown-%d.\n", type);
+			purple_debug_info("bonjour", "bytestream offer Message type - Unknown-%s.\n", type);
 		}
 	}
 }
@@ -557,7 +577,7 @@
 	XepXfer *xf = NULL;
 	BonjourData *bd = NULL;
 
-	if(pc == NULL || id == NULL || from == NULL || filename == NULL)
+	if(pc == NULL || id == NULL || from == NULL)
 		return;
 
 	bd = (BonjourData*) pc->proto_data;
@@ -593,9 +613,6 @@
 	int acceptfd;
 	int len = 0;
 
-	if(xfer == NULL)
-		return;
-
 	xf = xfer->data;
 	if(xf == NULL)
 		return;
@@ -608,7 +625,7 @@
 		if(acceptfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
 
 		} else if(acceptfd == -1) {
-
+			/* TODO: This should cancel the ft */
 		} else {
 			int flags;
 
@@ -720,6 +737,7 @@
 	const char *next_ip;
 	const char *local_ip = NULL;
 	char token [] = ";";
+	BonjourData *bd;
 
 	purple_debug_info("bonjour", "Bonjour-bytestreams-listen. sock=%d.\n", sock);
 	if (sock < 0 || xfer == NULL) {
@@ -732,7 +750,9 @@
 	xf = (XepXfer*)xfer->data;
 	xf->listen_data = NULL;
 
-	iq = xep_iq_new(xf->data, XEP_IQ_SET, xfer->who, xf->sid);
+	bd = xf->data;
+
+	iq = xep_iq_new(bd, XEP_IQ_SET, xfer->who, purple_account_get_username(bd->jabber_data->account), xf->sid);
 
 	query = xmlnode_new_child(iq->node, "query");
 	xmlnode_set_namespace(query, "http://jabber.org/protocol/bytestreams");
@@ -745,15 +765,15 @@
 	/* cheat a little here - the intent of the "const" attribute is to make it clear that the string doesn't need to be freed */
 	next_ip = strtok((char *)local_ip, token);
 
+	port = g_strdup_printf("%hu", xfer->local_port);
 	while(next_ip != NULL) {
 		streamhost = xmlnode_new_child(query, "streamhost");
 		xmlnode_set_attrib(streamhost, "jid", xf->sid);
 		xmlnode_set_attrib(streamhost, "host", next_ip);
-		port = g_strdup_printf("%hu", xfer->local_port);
 		xmlnode_set_attrib(streamhost, "port", port);
-		g_free(port);
 		next_ip = strtok(NULL, token);
 	}
+	g_free(port);
 
 	xep_iq_send_and_free(iq);
 }
@@ -781,14 +801,32 @@
 {
 	PurpleXfer *xfer = data;
 	XepXfer *xf = xfer->data;
+	XepIq *iq = NULL;
+	xmlnode *q_node, *tmp_node;
+	BonjourData *bd;
 
-	if(data == NULL || source < 0)
+	if(data == NULL || source < 0) {
+		xep_ft_si_reject(xf->data, xf->iq_id, xfer->who, "404", "cancel");
+		/* Cancel the connection */
+		purple_xfer_cancel_local(xfer);
 		return;
+	}
+
+	bd = xf->data;
 
 	purple_proxy_info_destroy(xf->proxy_info);
 	xf->proxy_connection = NULL;
 	xf->proxy_info = NULL;
 	/* Here, start the file transfer.*/
+
+	/* Notify Initiator of Connection */
+	iq = xep_iq_new(bd, XEP_IQ_RESULT, xfer->who, purple_account_get_username(bd->jabber_data->account), xf->iq_id);
+	q_node = xmlnode_new_child(iq->node, "query");
+	xmlnode_set_namespace(q_node, "http://jabber.org/protocol/bytestreams");
+	tmp_node = xmlnode_new_child(q_node, "streamhost-used");
+	xmlnode_set_attrib(tmp_node, "jid", xf->jid);
+	xep_iq_send_and_free(iq);
+
 	purple_xfer_start(xfer, source, NULL, -1);
 }
 
@@ -829,8 +867,11 @@
 							   bonjour_bytestreams_connect_cb, xfer);
 
 	if(xf->proxy_connection == NULL) {
-		purple_proxy_info_destroy(xf->proxy_info);
-		xf->proxy_info = NULL;
+		xep_ft_si_reject(xf->data, xf->iq_id, xfer->who, "404", "cancel");
+		/* Cancel the connection */
+		purple_xfer_cancel_local(xfer);
+		/*purple_proxy_info_destroy(xf->proxy_info);
+		xf->proxy_info = NULL;*/
 	}
 }
 
--- a/libpurple/protocols/bonjour/bonjour_ft.h	Thu Nov 15 14:22:06 2007 +0000
+++ b/libpurple/protocols/bonjour/bonjour_ft.h	Thu Nov 15 14:53:53 2007 +0000
@@ -35,7 +35,7 @@
 	void *data;
 	char *filename;
 	int filesize;
-	int id;
+	char *iq_id;
 	char *sid;
 	char *recv_id;
 	char *buddy_ip;
--- a/libpurple/protocols/bonjour/jabber.c	Thu Nov 15 14:22:06 2007 +0000
+++ b/libpurple/protocols/bonjour/jabber.c	Thu Nov 15 14:53:53 2007 +0000
@@ -89,13 +89,14 @@
 #endif
 
 static BonjourJabberConversation *
-bonjour_jabber_conv_new() {
+bonjour_jabber_conv_new(PurpleBuddy *pb) {
 
 	BonjourJabberConversation *bconv = g_new0(BonjourJabberConversation, 1);
 	bconv->socket = -1;
 	bconv->tx_buf = purple_circ_buffer_new(512);
 	bconv->tx_handler = 0;
 	bconv->rx_handler = 0;
+	bconv->pb = pb;
 
 	return bconv;
 }
@@ -401,7 +402,6 @@
 		bonjour_jabber_close_conversation(bb->conversation);
 		bb->conversation = NULL;
 	}
-
 }
 
 void bonjour_jabber_stream_started(PurpleBuddy *pb) {
@@ -573,7 +573,7 @@
 	/* Check if the conversation has been previously started */
 	if (bb->conversation == NULL)
 	{
-		bb->conversation = bonjour_jabber_conv_new();
+		bb->conversation = bonjour_jabber_conv_new(pb);
 
 		if (!bonjour_jabber_stream_init(pb, client_socket)) {
 			close(client_socket);
@@ -752,7 +752,7 @@
 			return NULL;
 		}
 
-		bb->conversation = bonjour_jabber_conv_new();
+		bb->conversation = bonjour_jabber_conv_new(pb);
 		bb->conversation->connect_data = connect_data;
 		/* We don't want _send_data() to register the tx_handler;
 		 * that neeeds to wait until we're actually connected. */
@@ -816,8 +816,25 @@
 void
 bonjour_jabber_close_conversation(BonjourJabberConversation *bconv)
 {
-	if (bconv != NULL)
-	{
+	if (bconv != NULL) {
+		GList *xfers, *tmp_next;
+		BonjourData *bd = bconv->pb->account->gc->proto_data;
+
+		/* Cancel any file transfers that are waiting to begin */
+		xfers = bd->xfer_lists;
+		while(xfers != NULL) {
+			PurpleXfer *xfer = xfers->data;
+			tmp_next = xfers->next;
+			/* We only need to cancel this if it hasn't actually started transferring. */
+			/* This will change if we ever support IBB transfers. */
+			if (strcmp(xfer->who, bconv->pb->name) == 0
+					&& (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_NOT_STARTED
+					    || purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_UNKNOWN)) {
+				purple_xfer_cancel_remote(xfer);
+			}
+			xfers = tmp_next;
+		}
+
 		/* Close the socket and remove the watcher */
 		if (bconv->socket >= 0) {
 			/* Send the end of the stream to the other end of the conversation */
@@ -874,7 +891,7 @@
 }
 
 XepIq *
-xep_iq_new(void *data, XepIqType type, const gchar *to, const gchar *id)
+xep_iq_new(void *data, XepIqType type, const char *to, const char *from, const char *id)
 {
 	xmlnode *iq_node = NULL;
 	XepIq *iq = NULL;
@@ -886,6 +903,7 @@
 	iq_node = xmlnode_new("iq");
 
 	xmlnode_set_attrib(iq_node, "to", to);
+	xmlnode_set_attrib(iq_node, "from", from);
 	xmlnode_set_attrib(iq_node, "id", id);
 	switch (type) {
 		case XEP_IQ_SET:
@@ -911,6 +929,7 @@
 	iq->type = type;
 	iq->data = ((BonjourData*)data)->jabber_data;
 	iq->to = (char*)to;
+
 	return iq;
 }
 
--- a/libpurple/protocols/bonjour/jabber.h	Thu Nov 15 14:22:06 2007 +0000
+++ b/libpurple/protocols/bonjour/jabber.h	Thu Nov 15 14:53:53 2007 +0000
@@ -53,6 +53,7 @@
 	gpointer stream_data;
 	xmlParserCtxt *context;
 	xmlnode *current;
+	PurpleBuddy *pb;
 } BonjourJabberConversation;
 
 /**
@@ -63,7 +64,7 @@
  */
 gint bonjour_jabber_start(BonjourJabber *data);
 
-int bonjour_jabber_send_message(BonjourJabber *data, const gchar *to, const gchar *body);
+int bonjour_jabber_send_message(BonjourJabber *data, const char *to, const char *body);
 
 void bonjour_jabber_close_conversation(BonjourJabberConversation *bconv);
 
@@ -91,7 +92,7 @@
 	void *data;
 } XepIq;
 
-XepIq *xep_iq_new(void *data, XepIqType type, const gchar *to, const gchar *id);
+XepIq *xep_iq_new(void *data, XepIqType type, const char *to, const char *from, const char *id);
 int xep_iq_send_and_free(XepIq *iq);
 const char *purple_network_get_my_ip_ext2(int fd);