changeset 21440:d8106b63b0a4

I thought I was just fixing a leak in this commit, but it turns out I forgot to add the new files in the previous commit.
author Daniel Atallah <daniel.atallah@gmail.com>
date Mon, 12 Nov 2007 04:17:10 +0000
parents 8fbd51946259
children da75dd6c41fb
files ChangeLog libpurple/protocols/bonjour/bonjour_ft.c libpurple/protocols/bonjour/bonjour_ft.h libpurple/protocols/bonjour/jabber.c
diffstat 4 files changed, 859 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Nov 12 03:41:20 2007 +0000
+++ b/ChangeLog	Mon Nov 12 04:17:10 2007 +0000
@@ -6,6 +6,11 @@
 	libpurple:
 	* We now honor a PURPLE_DISABLE_DEPRECATED define to allow plugins to
 	  catch deprecated functions earlier rather than later.
+	* Thanks to a patch from Intel, the Bonjour prpl now supports file
+	  transfers using XEP-0096 and XEP-0065.  This should enable file
+	  transfers between libpurple clients and Gaijm clients, but will
+	  not work with iChat or Adium as they use a different file transfer
+	  implementation.
 
 	Pidgin:
 	* If a plugin says it can't be unloaded, we now display an error and
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/bonjour/bonjour_ft.c	Mon Nov 12 04:17:10 2007 +0000
@@ -0,0 +1,776 @@
+/*
+ * purple - Bonjour Protocol Plugin
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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 "internal.h"
+#include "util.h"
+#include "debug.h"
+#include "notify.h"
+#include "proxy.h"
+#include "ft.h"
+#include "buddy.h"
+#include "bonjour.h"
+#include "bonjour_ft.h"
+#include "cipher.h"
+
+static PurpleXfer*
+bonjour_si_xfer_find(BonjourData *bd, const char *sid, const char *from);
+static void
+xep_ft_si_offer(PurpleXfer *xfer, const gchar *to);
+static void
+xep_ft_si_result(PurpleXfer *xfer, char *to);
+static void
+xep_ft_si_reject(PurpleXfer *xfer, char *to);
+static void
+xep_ft_si_reject2(BonjourData *bd, const char *to, const char *sid);
+static void
+bonjour_bytestreams_init(PurpleXfer *xfer);
+static void
+bonjour_bytestreams_listen(int sock, gpointer data);
+static void
+bonjour_sock5_request_cb(gpointer data, gint source, PurpleInputCondition cond);
+static void
+bonjour_bytestreams_connect(PurpleXfer *xfer);
+static void
+bonjour_bytestreams_connect_cb(gpointer data, gint source, const gchar *error_message);
+static void
+bonjour_xfer_init(PurpleXfer *xfer);
+static void
+bonjour_xfer_receive(PurpleConnection *pc, const char *id, const char *from,
+		     const int filesize, const char *filename, int option);
+static void bonjour_xfer_cancel_send(PurpleXfer *xfer);
+static void bonjour_xfer_request_denied(PurpleXfer *xfer);
+static void bonjour_xfer_cancel_recv(PurpleXfer *xfer);
+static void bonjour_xfer_end(PurpleXfer *xfer);
+static void bonjour_free_xfer(PurpleXfer *xfer);
+
+/* Look for specific xfer handle */
+static unsigned int next_id = 0;
+
+static void bonjour_xfer_cancel_send(PurpleXfer *xfer)
+{
+	purple_debug_info("Bonjour", "Bonjour-xfer-cancel-send.\n");
+	bonjour_free_xfer(xfer);
+}
+
+static void bonjour_xfer_request_denied(PurpleXfer *xfer)
+{
+	purple_debug_info("Bonjour", "Bonjour-xfer-request-denied.\n");
+	xep_ft_si_reject(xfer, xfer->who);
+	bonjour_free_xfer(xfer);
+}
+
+
+static void bonjour_xfer_cancel_recv(PurpleXfer *xfer)
+{
+	purple_debug_info("Bonjour", "Bonjour-xfer-cancel-recv.\n");
+	bonjour_free_xfer(xfer);
+}
+
+
+static void bonjour_xfer_end(PurpleXfer *xfer)
+{
+	purple_debug_info("Bonjour", "Bonjour-xfer-end.\n");
+	bonjour_free_xfer(xfer);
+}
+
+static PurpleXfer*
+bonjour_si_xfer_find(BonjourData *bd, const char *sid, const char *from)
+{
+	GList *xfers = NULL;
+	PurpleXfer *xfer = NULL;
+	XepXfer *xf = NULL;
+
+	if(!sid || !from || !bd)
+		return NULL;
+
+	purple_debug_info("Bonjour", "Look for sid=%s from=%s xferlists.\n",
+			  sid, from);
+
+	for(xfers = bd->xfer_lists; xfers; xfers = xfers->next) {
+		xfer = xfers->data;
+		if(xfer == NULL)
+			break;
+		xf = xfer->data;
+		if(xf == NULL)
+			break;
+		if(xf->sid && xfer->who && !strcmp(xf->sid, sid) &&
+				!strcmp(xfer->who, from))
+			return xfer;
+	}
+
+	purple_debug_info("Bonjour", "Look for xfer list fail\n");
+
+	return NULL;
+}
+
+static void
+xep_ft_si_offer(PurpleXfer *xfer, const gchar *to)
+{
+	xmlnode *si_node = NULL;
+	xmlnode *feature = NULL;
+	xmlnode *field = NULL;
+	xmlnode *option = NULL;
+	xmlnode *value = NULL;
+	xmlnode *file = NULL;
+	xmlnode *x = NULL;
+	XepIq *iq = NULL;
+	XepXfer *xf = NULL;
+	BonjourData *bd = NULL;
+	char buf[32];
+
+	if(!xfer || !to)
+		return;
+	xf = xfer->data;
+	if(!xf)
+		return;
+	bd = xf->data;
+	if(!bd)
+		return;
+
+	purple_debug_info("Bonjour", "xep file transfer stream initialization offer-id=%d.\n", next_id);
+
+	/* 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);
+	if(iq == NULL)
+		return;
+
+	g_free(xf->sid);
+	xf->sid = g_strdup(buf);
+	/*Construct Stream initialization offer message.*/
+	si_node = xmlnode_new_child(iq->node, "si");
+	xmlnode_set_namespace(si_node, "http://jabber.org/protocol/si");
+
+	file = xmlnode_new_child(si_node, "file");
+	xmlnode_set_namespace(file, "http://jabber.org/protocol/si/profile/file-transfer");
+	xmlnode_set_attrib(file, "name", xfer->filename);
+	memset(buf, 0, 32);
+	g_snprintf(buf, sizeof(buf), "%" G_GSIZE_FORMAT, xfer->size);
+	xmlnode_set_attrib(file, "size", buf);
+
+	feature = xmlnode_new_child(si_node, "feature");
+	xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg");
+
+	x = xmlnode_new_child(feature, "x");
+	xmlnode_set_namespace(x, "jabber:x:data");
+	xmlnode_set_attrib(x, "type", "form");
+
+	field = xmlnode_new_child(x, "field");
+	xmlnode_set_attrib(field, "var", "stream-method");
+	xmlnode_set_attrib(field, "type", "list-single");
+
+	if (xf->mode & XEP_BYTESTREAMS) {
+		option = xmlnode_new_child(field, "option");
+		value = xmlnode_new_child(option, "value");
+		xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1);
+	}
+	if (xf->mode & XEP_IBB) {
+		option = xmlnode_new_child(field, "option");
+		value = xmlnode_new_child(option, "value");
+		xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1);
+	}
+	xep_iq_send(iq);
+}
+
+static void
+xep_ft_si_reject(PurpleXfer *xfer, char *to)
+{
+	xmlnode *error_node = NULL;
+	xmlnode *forbidden = NULL;
+	xmlnode *text = NULL;
+	XepIq *iq = NULL;
+	XepXfer *xf = NULL;
+
+	if(!to || !xfer)
+		return;
+	xf = xfer->data;
+	if(!xf)
+		return;
+
+	purple_debug_info("Bonjour", "xep file transfer stream initialization error.\n");
+	iq = xep_iq_new(xf->data, XEP_IQ_ERROR, to, xf->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");
+
+	forbidden = xmlnode_new_child(error_node, "forbidden");
+	xmlnode_set_namespace(forbidden, "urn:ietf:params:xml:ns:xmpp-stanzas");
+
+	text = xmlnode_new_child(error_node, "text");
+	xmlnode_set_namespace(text, "urn:ietf:params:xml:ns:xmpp-stanzas");
+	xmlnode_insert_data(text, "Offer Declined", -1);
+
+	xep_iq_send(iq);
+}
+static void
+xep_ft_si_reject2(BonjourData *bd, const char *to, const char *sid)
+{
+	xmlnode *error_node = NULL;
+	xmlnode *forbidden = NULL;
+	xmlnode *text = NULL;
+	XepIq *iq = NULL;
+	if(bd == NULL)
+		return;
+	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");
+
+	forbidden = xmlnode_new_child(error_node, "forbidden");
+	xmlnode_set_namespace(forbidden, "urn:ietf:params:xml:ns:xmpp-stanzas");
+
+	text = xmlnode_new_child(error_node, "text");
+	xmlnode_set_namespace(text, "urn:ietf:params:xml:ns:xmpp-stanzas");
+	xmlnode_insert_data(text, "Offer Declined", -1);
+
+	xep_iq_send(iq);
+}
+
+static void
+xep_ft_si_result(PurpleXfer *xfer, char *to)
+{
+	xmlnode *si_node = NULL;
+	xmlnode *feature = NULL;
+	xmlnode *field = NULL;
+	xmlnode *value = NULL;
+	xmlnode *x = NULL;
+	XepIq *iq = NULL;
+	XepXfer *xf = NULL;
+
+	if(!to || !xfer)
+		return;
+	xf = xfer->data;
+	if(!xf)
+		return;
+
+	purple_debug_info("Bonjour", "xep file transfer stream initialization result.\n");
+	iq = xep_iq_new(xf->data, XEP_IQ_RESULT, to, xf->sid);
+	if(iq == NULL)
+		return;
+
+	si_node = xmlnode_new_child(iq->node, "si");
+	xmlnode_set_namespace(si_node, "http://jabber.org/protocol/si");
+
+	feature = xmlnode_new_child(si_node, "feature");
+	xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg");
+
+	x = xmlnode_new_child(feature, "x");
+	xmlnode_set_namespace(x, "jabber:x:data");
+	xmlnode_set_attrib(x, "type", "submit");
+
+	field = xmlnode_new_child(x, "field");
+	xmlnode_set_attrib(field, "var", "stream-method");
+
+	value = xmlnode_new_child(field, "value");
+	xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1);
+
+	xep_iq_send(iq);
+}
+
+static void
+bonjour_free_xfer(PurpleXfer *xfer)
+{
+	XepXfer *xf = NULL;
+	BonjourData *bd = NULL;
+	if(xfer == NULL) {
+		purple_debug_info("Bonjour", "bonjour-free-xfer-null.\n");
+		return;
+	}
+	purple_debug_info("Bonjour", "bonjour-free-xfer-%x.\n", (int)xfer);
+	xf = (XepXfer*)xfer->data;
+	if(xf != NULL){
+		bd = (BonjourData*)xf->data;
+		if(bd != NULL){
+			bd->xfer_lists = g_list_remove(bd->xfer_lists, xfer);
+			purple_debug_info("Bonjour", "B free xfer from lists(0x%x).\n", (int)bd->xfer_lists);
+		}
+		if (xf->proxy_connection != NULL)
+			purple_proxy_connect_cancel(xf->proxy_connection);
+		if (xf->listen_data != NULL)
+			purple_network_listen_cancel(xf->listen_data);
+		g_free(xf->jid);
+		g_free(xf->proxy_host);
+		g_free(xf->buddy_ip);
+		g_free(xf);
+		xfer->data = NULL;
+	}
+	purple_debug_info("Bonjour", "Need close socket=0x%x.\n", xfer->fd);
+}
+
+PurpleXfer *
+bonjour_new_xfer(PurpleConnection *gc, const char *who)
+{
+	PurpleXfer *xfer = NULL;
+	XepXfer *xep_xfer = NULL;
+	BonjourData *bd = NULL;
+	if(who == NULL || gc == NULL)
+		return xfer;
+
+	purple_debug_info("Bonjour", "Bonjour-new-xfer to %s.\n", who);
+	bd = (BonjourData*) gc->proto_data;
+	if(bd == NULL)
+		return xfer;
+	/* Build the file transfer handle */
+	xfer = purple_xfer_new(gc->account, PURPLE_XFER_SEND, who);
+
+	if (xfer) {
+		xep_xfer = g_new0(XepXfer, 1);
+		if(xep_xfer == NULL){
+			bonjour_free_xfer(xfer);
+			return NULL;
+		}
+		xep_xfer->data = bd;
+		purple_debug_info("Bonjour", "Bonjour-new-xfer bd=0x%x data=0x%x.\n",(int)bd, (int)xep_xfer->data);
+		xep_xfer->mode = XEP_BYTESTREAMS | XEP_IBB;
+		xfer->data = xep_xfer;
+		xep_xfer->sid = NULL;
+		purple_xfer_set_init_fnc(xfer, bonjour_xfer_init);
+		purple_xfer_set_cancel_send_fnc(xfer, bonjour_xfer_cancel_send);
+		purple_xfer_set_end_fnc(xfer, bonjour_xfer_end);
+		bd->xfer_lists = g_list_append(bd->xfer_lists, xfer);
+	}
+	return xfer;
+}
+
+void
+bonjour_send_file(PurpleConnection *gc, const char *who, const char *file)
+{
+
+	PurpleXfer *xfer = NULL;
+	if(gc == NULL || who == NULL)
+		return;
+	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
+		purple_xfer_request(xfer);
+
+}
+
+static void
+bonjour_xfer_init(PurpleXfer *xfer)
+{
+	PurpleBuddy *buddy = NULL;
+	BonjourBuddy *bd = NULL;
+	XepXfer *xf = NULL;
+	char buffer[100];
+	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);
+	/* this buddy is offline. */
+	if (buddy == NULL)
+		return;
+
+	bd = (BonjourBuddy *)buddy->proto_data;
+	memcpy(buffer, bd->ip, 20);
+	xf->buddy_ip = g_strdup(bd->ip);
+	if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
+		/* 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;
+	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;
+	bd = (BonjourData*) pc->proto_data;
+	if(bd == NULL)
+		return;
+	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");
+			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")) != NULL )
+				filesize = atoi(filesize_str);
+			bonjour_xfer_receive(pc, id, from, filesize, filename, option);
+		} else if(!strcmp(type, "result")){
+			si = xmlnode_get_child(packet,"si");
+			purple_debug_info("Bonjour", "si offer Message type - RESULT.\n");
+			xfer = bonjour_si_xfer_find(bd, id, from);
+			if(xfer == NULL){
+				purple_debug_info("Bonjour", "xfer find fail.\n");
+				xep_ft_si_reject2((BonjourData *)pc->proto_data, from, id);
+			} else {
+				bonjour_bytestreams_init(xfer);
+			}
+		} 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){
+				purple_debug_info("Bonjour", "xfer find fail.\n");
+			} else {
+				purple_xfer_cancel_remote(xfer);
+			}
+		} 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;
+	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)
+		return;
+	bd = (BonjourData*) pc->proto_data;
+	if(bd == NULL)
+		return;
+	purple_debug_info("Bonjour", "xep-bytestreams-parse.\n");
+	type = xmlnode_get_attrib(packet, "type");
+	from = pb->name;
+	query = xmlnode_get_child(packet,"query");
+	if(type) {
+		if(!strcmp(type, "set")){
+			purple_debug_info("Bonjour", "bytestream offer Message type - SET.\n");
+			id = xmlnode_get_attrib(query, "sid");
+			xfer = bonjour_si_xfer_find(bd, id, from);
+			if(xfer){
+				xf = (XepXfer*)xfer->data;
+				for(streamhost = xmlnode_get_child(query, "streamhost");
+				    streamhost;
+				    streamhost = xmlnode_get_next_twin(streamhost)) {
+					if((jid = xmlnode_get_attrib(streamhost, "jid")) &&
+					   (host = xmlnode_get_attrib(streamhost, "host")) &&
+					   (port = xmlnode_get_attrib(streamhost, "port")) &&
+					   (portnum = atoi(port))) {
+						if(!strcmp(host, xf->buddy_ip)) {
+							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=0x%x.\n",jid, host, portnum);
+							bonjour_bytestreams_connect(xfer);
+						}
+					} else {
+						purple_debug_info("Bonjour", "bytestream offer Message parse error.\n");
+					}
+				}
+			} else {
+
+			}
+
+		} else {
+			purple_debug_info("Bonjour", "bytestream offer Message type - Unknown-%d.\n",type);
+		}
+	}
+}
+
+static void
+bonjour_xfer_receive(PurpleConnection *pc, const char *id, const char *from,
+		     const int filesize, const char *filename, int option)
+{
+	PurpleXfer *xfer = NULL;
+	XepXfer *xf = NULL;
+	BonjourData *bd = NULL;
+	if(pc == NULL || id == NULL || from == NULL || filename == NULL)
+		return;
+	bd = (BonjourData*) pc->proto_data;
+	if(bd == NULL)
+		return;
+	purple_debug_info("Bonjour", "bonjour-xfer-receive.\n");
+	/* Build the file transfer handle */
+	xfer = purple_xfer_new(pc->account, PURPLE_XFER_RECEIVE, from);
+	if (xfer) {
+		xf = g_new0(XepXfer, 1);
+		xf->data = bd;
+		xfer->data = xf;
+		purple_xfer_set_filename(xfer, filename);
+		xf->sid = g_strdup(id);
+		if(filesize > 0)
+			purple_xfer_set_size(xfer, filesize);
+		purple_xfer_set_init_fnc(xfer, bonjour_xfer_init);
+		purple_xfer_set_request_denied_fnc(xfer, bonjour_xfer_request_denied);
+		purple_xfer_set_cancel_recv_fnc(xfer, bonjour_xfer_cancel_recv);
+		purple_xfer_set_end_fnc(xfer, bonjour_xfer_end);
+		bd->xfer_lists = g_list_append(bd->xfer_lists, xfer);
+		purple_xfer_request(xfer);
+	}
+}
+
+static void
+bonjour_bytestreams_init(PurpleXfer *xfer)
+{
+	XepXfer *xf = NULL;
+	if(xfer == NULL)
+		return;
+	purple_debug_info("Bonjour", "Bonjour-bytestreams-init.\n");
+	xf = xfer->data;
+	xf->listen_data = purple_network_listen_range(0, 0, SOCK_STREAM,
+						      bonjour_bytestreams_listen, xfer);
+	if (xf->listen_data == NULL) {
+		purple_xfer_cancel_local(xfer);
+	}
+	return;
+}
+
+static void
+bonjour_bytestreams_listen(int sock, gpointer data)
+{
+	PurpleXfer *xfer = NULL;
+	XepXfer *xf;
+	XepIq *iq;
+	xmlnode *query, *streamhost;
+	char *port;
+	char *next_ip = NULL;
+	char *local_ip = NULL;
+	char token [] = ";";
+	purple_debug_info("Bonjour", "Bonjour-bytestreams-listen. sock=%d.\n", sock);
+	if (sock < 0 || data == NULL) {
+		/*purple_xfer_cancel_local(xfer);*/
+		return;
+	}
+	xfer = data;
+	xfer->watcher = purple_input_add(sock, PURPLE_INPUT_READ,
+					 bonjour_sock5_request_cb, xfer);
+	xf = (XepXfer*)xfer->data;
+	xf->listen_data = NULL;
+	/*xf->listen_data = NULL;*/
+	iq = xep_iq_new(xf->data, XEP_IQ_SET, xfer->who, xf->sid);
+	query = xmlnode_new_child(iq->node, "query");
+	xmlnode_set_namespace(query, "http://jabber.org/protocol/bytestreams");
+	xmlnode_set_attrib(query, "sid", xf->sid);
+	xmlnode_set_attrib(query, "mode", "tcp");
+	local_ip = purple_network_get_my_ip_ext2(sock);
+	next_ip = strtok(local_ip, token);
+	xfer->local_port = purple_network_get_port_from_fd(sock);
+	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);
+	}
+	xep_iq_send(iq);
+}
+
+static void
+bonjour_sock5_request_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	PurpleXfer *xfer = data;
+	XepXfer *xf = NULL;
+	int acceptfd;
+	int len = 0;
+	if(xfer == NULL )
+		return;
+	xf = xfer->data;
+	if(xf == NULL)
+		return;
+	switch(xf->sock5_req_state){
+	case 0x00:
+		acceptfd = accept (source, NULL, 0);
+		if(acceptfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK )) {
+
+		} else if(acceptfd == -1) {
+
+		} else {
+			purple_debug_info("Bonjour", "Conjour-sock5-request-cb. state= %d, accept=%d\n", xf->sock5_req_state,acceptfd);
+			purple_input_remove(xfer->watcher);
+			close(source);
+			xfer->watcher = purple_input_add(acceptfd, PURPLE_INPUT_READ,
+							 bonjour_sock5_request_cb, xfer);
+			xf->sock5_req_state++;
+			xf->rxlen = 0;
+		}
+		break;
+	case 0x01:
+		xfer->fd = source;
+		len = read(source, xf->rx_buf + xf->rxlen, 3);
+		if(len < 0 && errno == EAGAIN)
+			return;
+		else if(len <= 0){
+			purple_input_remove(xfer->watcher);
+			xfer->watcher = 0;
+			close(source);
+			purple_xfer_cancel_remote(xfer);
+			return;
+		} else {
+			purple_input_remove(xfer->watcher);
+			xfer->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
+							 bonjour_sock5_request_cb, xfer);
+			xf->sock5_req_state++;
+			xf->rxlen = 0;
+			bonjour_sock5_request_cb(xfer, source, PURPLE_INPUT_WRITE);
+		}
+		break;
+	case 0x02:
+		xf->tx_buf[0] = 0x05;
+		xf->tx_buf[1] = 0x00;
+		len = write(source, xf->tx_buf, 2);
+		if (len < 0 && errno == EAGAIN)
+			return;
+		else if (len < 0) {
+			purple_input_remove(xfer->watcher);
+			xfer->watcher = 0;
+			close(source);
+			purple_xfer_cancel_remote(xfer);
+			return;
+		} else {
+			purple_input_remove(xfer->watcher);
+			xfer->watcher = purple_input_add(source, PURPLE_INPUT_READ,
+							 bonjour_sock5_request_cb, xfer);
+			xf->sock5_req_state++;
+			xf->rxlen = 0;
+		}
+		break;
+	case 0x03:
+		len = read(source, xf->rx_buf + xf->rxlen, 20);
+		if(len<=0){
+		} else {
+			purple_input_remove(xfer->watcher);
+			xfer->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
+							 bonjour_sock5_request_cb, xfer);
+			xf->sock5_req_state++;
+			xf->rxlen = 0;
+			bonjour_sock5_request_cb(xfer, source, PURPLE_INPUT_WRITE);
+		}
+		break;
+	case 0x04:
+		xf->tx_buf[0] = 0x05;
+		xf->tx_buf[1] = 0x00;
+		xf->tx_buf[2] = 0x00;
+		xf->tx_buf[3] = 0x03;
+		xf->tx_buf[4] = strlen(xf->buddy_ip);
+		memcpy(xf->tx_buf + 5, xf->buddy_ip, strlen(xf->buddy_ip));
+		xf->tx_buf[5+strlen(xf->buddy_ip)] = 0x00;
+		xf->tx_buf[6+strlen(xf->buddy_ip)] = 0x00;
+		len = write(source, xf->tx_buf, 7 + strlen(xf->buddy_ip));
+		if (len < 0 && errno == EAGAIN) {
+			return;
+		} else if (len < 0) {
+			purple_input_remove(xfer->watcher);
+			xfer->watcher = 0;
+			close(source);
+			purple_xfer_cancel_remote(xfer);
+			return;
+		} else {
+			purple_input_remove(xfer->watcher);
+			xfer->watcher = 0;
+			xf->rxlen = 0;
+			/*close(source);*/
+			purple_xfer_start(xfer, source, NULL, -1);
+		}
+		break;
+	default:
+		break;
+	}
+	return;
+}
+static void
+bonjour_bytestreams_connect(PurpleXfer *xfer)
+{
+	XepXfer *xf = NULL;
+	char dstaddr[41];
+	unsigned char hashval[20];
+	char *p = NULL;
+	int i;
+	if(xfer == NULL)
+		return;
+	purple_debug_info("Bonjour", "bonjour-bytestreams-connect.\n");
+	xf = (XepXfer*)xfer->data;
+	if(!xf)
+		return;
+	xf->proxy_info = purple_proxy_info_new();
+	if(xf->proxy_info == NULL)
+		return;
+	p = g_strdup_printf("%s@%s", xf->sid, xfer->who);
+	purple_cipher_digest_region("sha1", (guchar *)dstaddr, strlen(dstaddr),
+				    sizeof(hashval), hashval, NULL);
+	g_free(p);
+	memset(dstaddr, 0, 41);
+	p = dstaddr;
+	for(i=0; i<20; i++) {
+		snprintf(p, 3, "%02x", hashval[i]);
+	}
+	purple_proxy_info_set_type(xf->proxy_info, PURPLE_PROXY_SOCKS5);
+	purple_proxy_info_set_host(xf->proxy_info, xf->proxy_host);
+	purple_proxy_info_set_port(xf->proxy_info, xf->proxy_port);
+	xf->proxy_connection = purple_proxy_connect_socks5(NULL, xf->proxy_info,
+							   dstaddr, 0,
+							   bonjour_bytestreams_connect_cb, xfer);
+	if(xf->proxy_connection == NULL) {
+		purple_proxy_info_destroy(xf->proxy_info);
+		xf->proxy_info = NULL;
+	}
+}
+
+static void
+bonjour_bytestreams_connect_cb(gpointer data, gint source, const gchar *error_message)
+{
+	PurpleXfer *xfer = data;
+	XepXfer *xf = xfer->data;
+	if(data == NULL || source < 0)
+		return;
+	purple_proxy_info_destroy(xf->proxy_info);
+	xf->proxy_connection = NULL;
+	xf->proxy_info = NULL;
+	/* Here, start the file transfer.*/
+	purple_xfer_start(xfer, source, NULL, -1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/bonjour/bonjour_ft.h	Mon Nov 12 04:17:10 2007 +0000
@@ -0,0 +1,75 @@
+/*
+ * purple - Bonjour Protocol Plugin
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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 _BONJOUR_FT_H_
+#define _BONJOUR_FT_H_
+#include "network.h"
+#include "proxy.h"
+typedef struct _XepXfer XepXfer;
+typedef enum {
+	XEP_BYTESTREAMS = 1,
+	XEP_IBB = 2,
+	XEP_UNKNOWN = 4,
+} XepSiMode;
+
+struct _XepXfer
+{
+	void *data;
+	char *filename;
+	int filesize;
+	int id;
+	char *sid;
+	char *recv_id;
+	char *buddy_ip;
+	int mode;
+	PurpleNetworkListenData *listen_data;
+	int sock5_req_state;
+	int rxlen;
+	char rx_buf[0x500];
+	char tx_buf[0x500];
+	PurpleProxyInfo *proxy_info;
+	PurpleProxyConnectData *proxy_connection;
+	char *jid;
+	char *proxy_host;
+	int proxy_port;
+};
+
+/**
+ * Create a new PurpleXfer
+ *
+ * @param gc The PurpleConnection handle.
+ * @param who Who will we be sending it to?
+ */
+PurpleXfer *bonjour_new_xfer(PurpleConnection *gc, const char *who);
+
+/**
+ * Send a file.
+ *
+ * @param gc The PurpleConnection handle.
+ * @param who Who are we sending it to?
+ * @param file What file? If NULL, user will choose after this call.
+ */
+void bonjour_send_file(PurpleConnection *gc, const char *who, const char *file);
+
+void xep_si_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb);
+void
+xep_bytestreams_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb);
+#endif
--- a/libpurple/protocols/bonjour/jabber.c	Mon Nov 12 03:41:20 2007 +0000
+++ b/libpurple/protocols/bonjour/jabber.c	Mon Nov 12 04:17:10 2007 +0000
@@ -1016,6 +1016,7 @@
 	return 0;
 }
 
+/* This returns a ';' delimited string containing all non-localhost IPs */
 char *
 purple_network_get_my_ip_ext2(int fd)
 {
@@ -1040,7 +1041,8 @@
 
 	if (fd < 0)
 		close(source);
-	memset(ip_ext, 0, 17 * 10);
+
+	memset(ip_ext, 0, sizeof(ip_ext));
 	memcpy(ip_ext, "0.0.0.0", 7);
 	tmp = buffer;
 	tip = ip_ext;