changeset 21462:31fc6a0769ce

merge of 'aa714a29059f790cb003a8fe1bec9c8873d09e33' and 'd92a21a7dca2f2c4abb6371abea83d5ab4f28b10'
author Daniel Atallah <daniel.atallah@gmail.com>
date Mon, 12 Nov 2007 23:10:08 +0000
parents da75dd6c41fb (diff) a34204321321 (current diff)
children 2b6553584385
files
diffstat 10 files changed, 1103 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Nov 12 20:39:26 2007 +0000
+++ b/ChangeLog	Mon Nov 12 23:10:08 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
--- a/Makefile.mingw	Mon Nov 12 20:39:26 2007 +0000
+++ b/Makefile.mingw	Mon Nov 12 23:10:08 2007 +0000
@@ -62,8 +62,8 @@
 	saslGSSAPI.dll \
 	saslLOGIN.dll \
 	saslPLAIN.dll \
-	silc.dll \
-	silcclient.dll \
+	libsilc-1-1-2.dll \
+	libsilcclient-1-1-2.dll \
 	smime3.dll \
 	softokn3.dll \
 	ssl3.dll
--- a/libpurple/protocols/bonjour/Makefile.am	Mon Nov 12 20:39:26 2007 +0000
+++ b/libpurple/protocols/bonjour/Makefile.am	Mon Nov 12 23:10:08 2007 +0000
@@ -18,7 +18,9 @@
 	mdns_interface.h \
 	mdns_types.h \
 	parser.c \
-	parser.h
+	parser.h \
+	bonjour_ft.c \
+	bonjour_ft.h 
 
 if MDNS_AVAHI
   BONJOURSOURCES += mdns_avahi.c
--- a/libpurple/protocols/bonjour/Makefile.mingw	Mon Nov 12 20:39:26 2007 +0000
+++ b/libpurple/protocols/bonjour/Makefile.mingw	Mon Nov 12 23:10:08 2007 +0000
@@ -43,12 +43,13 @@
 ##  SOURCES, OBJECTS
 ##
 C_SRC =			bonjour.c \
+			bonjour_ft.c \
 			buddy.c \
 			dns_sd_proxy.c \
+			jabber.c \
 			mdns_common.c \
 			mdns_win32.c \
-			parser.c \
-			jabber.c
+			parser.c
 
 OBJECTS = $(C_SRC:%.c=%.o)
 
--- a/libpurple/protocols/bonjour/bonjour.c	Mon Nov 12 20:39:26 2007 +0000
+++ b/libpurple/protocols/bonjour/bonjour.c	Mon Nov 12 23:10:08 2007 +0000
@@ -41,6 +41,7 @@
 #include "mdns_common.h"
 #include "jabber.h"
 #include "buddy.h"
+#include "bonjour_ft.h"
 
 /*
  * TODO: Should implement an add_buddy callback that removes the buddy
@@ -453,8 +454,8 @@
 	NULL,                                                    /* roomlist_cancel */
 	NULL,                                                    /* roomlist_expand_category */
 	NULL,                                                    /* can_receive_file */
-	NULL,                                                    /* send_file */
-	NULL,                                                    /* new_xfer */
+	bonjour_send_file,                                       /* send_file */
+	bonjour_new_xfer,                                        /* new_xfer */
 	NULL,                                                    /* offline_message */
 	NULL,                                                    /* whiteboard_prpl_ops */
 	NULL,                                                    /* send_raw */
--- a/libpurple/protocols/bonjour/bonjour.h	Mon Nov 12 20:39:26 2007 +0000
+++ b/libpurple/protocols/bonjour/bonjour.h	Mon Nov 12 23:10:08 2007 +0000
@@ -44,6 +44,7 @@
 {
 	BonjourDnsSd *dns_sd_data;
 	BonjourJabber *jabber_data;
+	GList *xfer_lists;
 } BonjourData;
 
 #endif /* _BONJOUR_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/bonjour/bonjour_ft.c	Mon Nov 12 23:10:08 2007 +0000
@@ -0,0 +1,777 @@
+/*
+ * 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-%p.\n", 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(%p).\n", 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->sid);
+		g_free(xf);
+		xfer->data = NULL;
+	}
+	purple_debug_info("Bonjour", "Need close socket=%d.\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=%p data=%p.\n", bd, 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=%d.\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 23:10:08 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 20:39:26 2007 +0000
+++ b/libpurple/protocols/bonjour/jabber.c	Mon Nov 12 23:10:08 2007 +0000
@@ -20,6 +20,8 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 #ifndef _WIN32
+#include <net/if.h>
+#include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
@@ -45,12 +47,22 @@
 #include "parser.h"
 #include "bonjour.h"
 #include "buddy.h"
+#include "bonjour_ft.h"
+
+#ifdef _SIZEOF_ADDR_IFREQ
+#  define HX_SIZE_OF_IFREQ(a) _SIZEOF_ADDR_IFREQ(a)
+#else
+#  define HX_SIZE_OF_IFREQ(a) sizeof(a)
+#endif
 
 #define STREAM_END "</stream:stream>"
 /* TODO: specify version='1.0' and send stream features */
 #define DOCTYPE "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" \
 		"<stream:stream xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" from=\"%s\" to=\"%s\">"
 
+static void
+xep_iq_parse(xmlnode *packet, PurpleConnection *connection, PurpleBuddy *pb);
+
 #if 0 /* this isn't used anywhere... */
 static const char *
 _font_size_purple_to_ichat(int size)
@@ -316,6 +328,8 @@
 void bonjour_jabber_process_packet(PurpleBuddy *pb, xmlnode *packet) {
 	if (!strcmp(packet->name, "message"))
 		_jabber_parse_and_write_message_to_ui(packet, pb);
+	else if(!strcmp(packet->name, "iq"))
+		xep_iq_parse(packet, NULL, pb);
 	else
 		purple_debug_warning("bonjour", "Unknown packet: %s\n",
 				packet->name);
@@ -697,23 +711,21 @@
 	}
 }
 
-int
-bonjour_jabber_send_message(BonjourJabber *data, const gchar *to, const gchar *body)
+static PurpleBuddy *
+_find_or_start_conversation(BonjourJabber *data, const gchar *to)
 {
-	xmlnode *message_node, *node, *node2;
-	gchar *message;
-	PurpleBuddy *pb;
-	BonjourBuddy *bb;
-	int ret;
+	PurpleBuddy *pb = NULL;
+	BonjourBuddy *bb = NULL;
+
+	g_return_val_if_fail(data != NULL, NULL);
+	g_return_val_if_fail(to != NULL, NULL);
 
 	pb = purple_find_buddy(data->account, to);
-	if (pb == NULL) {
-		purple_debug_info("bonjour", "Can't send a message to an offline buddy (%s).\n", to);
+	if (pb == NULL || pb->proto_data == NULL)
 		/* You can not send a message to an offline buddy */
-		return -10000;
-	}
+		return NULL;
 
-	bb = pb->proto_data;
+	bb = (BonjourBuddy *) pb->proto_data;
 
 	/* Check if there is a previously open conversation */
 	if (bb->conversation == NULL)
@@ -721,6 +733,8 @@
 		PurpleProxyConnectData *connect_data;
 		PurpleProxyInfo *proxy_info;
 
+		purple_debug_info("Bonjour", "Starting conversation with %s\n", to);
+
 		/* Make sure that the account always has a proxy of "none".
 		 * This is kind of dirty, but proxy_connect_none() isn't exposed. */
 		proxy_info = purple_account_get_proxy_info(data->account);
@@ -730,13 +744,12 @@
 		}
 		purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_NONE);
 
-		connect_data =
-			purple_proxy_connect(data->account->gc, data->account, bb->ip,
-					     bb->port_p2pj, _connected_to_buddy, pb);
+		connect_data = purple_proxy_connect(data->account->gc, data->account,
+						    bb->ip, bb->port_p2pj, _connected_to_buddy, pb);
 
 		if (connect_data == NULL) {
 			purple_debug_error("bonjour", "Unable to connect to buddy (%s).\n", to);
-			return -10001;
+			return NULL;
 		}
 
 		bb->conversation = bonjour_jabber_conv_new();
@@ -745,6 +758,26 @@
 		 * that neeeds to wait until we're actually connected. */
 		bb->conversation->tx_handler = 0;
 	}
+	return pb;
+}
+
+int
+bonjour_jabber_send_message(BonjourJabber *data, const gchar *to, const gchar *body)
+{
+	xmlnode *message_node, *node, *node2;
+	gchar *message;
+	PurpleBuddy *pb;
+	BonjourBuddy *bb;
+	int ret;
+
+	pb = _find_or_start_conversation(data, to);
+	if (pb == NULL) {
+		purple_debug_info("bonjour", "Can't send a message to an offline buddy (%s).\n", to);
+		/* You can not send a message to an offline buddy */
+		return -10000;
+	}
+
+	bb = pb->proto_data;
 
 	message_node = xmlnode_new("message");
 	xmlnode_set_attrib(message_node, "to", bb->name);
@@ -839,3 +872,168 @@
 		g_slist_free(buddies);
 	}
 }
+
+XepIq *
+xep_iq_new(void *data, XepIqType type, const gchar *to, const gchar *id)
+{
+	xmlnode *iq_node = NULL;
+	XepIq *iq = NULL;
+
+	if(data == NULL || to == NULL || id == NULL)
+		return NULL;
+	iq = g_new0(XepIq, 1);
+	if(iq == NULL)
+		return NULL;
+
+	iq_node = xmlnode_new("iq");
+	if(iq_node == NULL) {
+		g_free(iq);
+		return NULL;
+	}
+
+	xmlnode_set_attrib(iq_node, "to", to);
+	xmlnode_set_attrib(iq_node, "id", id);
+	switch (type) {
+		case XEP_IQ_SET:
+			xmlnode_set_attrib(iq_node, "type", "set");
+			break;
+		case XEP_IQ_GET:
+			xmlnode_set_attrib(iq_node, "type", "get");
+			break;
+		case XEP_IQ_RESULT:
+			xmlnode_set_attrib(iq_node, "type", "result");
+			break;
+		case XEP_IQ_ERROR:
+			xmlnode_set_attrib(iq_node, "type", "error");
+			break;
+		case XEP_IQ_NONE:
+		default:
+			xmlnode_set_attrib(iq_node, "type", "none");
+			break;
+	}
+
+	iq->node = iq_node;
+	iq->type = type;
+	iq->data = ((BonjourData*)data)->jabber_data;
+	iq->to = (char*)to;
+	return iq;
+}
+
+static gboolean
+check_if_blocked(PurpleBuddy *pb)
+{
+	gboolean blocked = FALSE;
+	GSList *l = NULL;
+	PurpleAccount *acc = NULL;
+
+	if(pb == NULL)
+		return FALSE;
+
+	acc = pb->account;
+
+	for(l = acc->deny; l != NULL; l = l->next) {
+		if(!purple_utf8_strcasecmp(pb->name, (char *)l->data)) {
+			purple_debug_info("Bonjour", "%s has been blocked.\n", pb->name, acc->username);
+			blocked = TRUE;
+			break;
+		}
+	}
+	return blocked;
+}
+
+static void
+xep_iq_parse(xmlnode *packet, PurpleConnection *connection, PurpleBuddy *pb)
+{
+	xmlnode *child = NULL;
+
+	if(packet == NULL || pb == NULL)
+		return;
+
+	if(connection == NULL) {
+		if(pb->account != NULL)
+			connection = (pb->account)->gc;
+	}
+
+	if(check_if_blocked(pb))
+		return;
+
+	if ((child = xmlnode_get_child(packet, "si")) || (child = xmlnode_get_child(packet, "si")))
+		xep_si_parse(connection, packet, pb);
+	else
+		xep_bytestreams_parse(connection, packet, pb);
+}
+
+int
+xep_iq_send(XepIq *iq)
+{
+	int ret = -1;
+	PurpleBuddy *pb = NULL;
+
+	/* start the talk, reuse the message socket  */
+	pb = _find_or_start_conversation ((BonjourJabber*)iq->data, iq->to);
+	/* Send the message */
+	if (pb != NULL) {
+		/* Convert xml node into stream */
+		gchar *msg = xmlnode_to_str(iq->node, NULL);
+		ret = _send_data(pb, msg);
+		g_free(msg);
+	}
+	xmlnode_free(iq->node);
+
+	return (ret >= 0) ? 0 : -1;
+}
+
+/* This returns a ';' delimited string containing all non-localhost IPs */
+char *
+purple_network_get_my_ip_ext2(int fd)
+{
+	char buffer[1024];
+	static char ip_ext[17 * 10];
+	char *tmp;
+	char *tip;
+	struct ifconf ifc;
+	struct ifreq *ifr;
+	struct sockaddr_in *sinptr;
+	guint32 lhost = htonl(127 * 256 * 256 * 256 + 1);
+	long unsigned int add;
+	int source = fd;
+	int len;
+
+	if (fd < 0)
+		source = socket(PF_INET, SOCK_STREAM, 0);
+
+	ifc.ifc_len = sizeof(buffer);
+	ifc.ifc_req = (struct ifreq *)buffer;
+	ioctl(source, SIOCGIFCONF, &ifc);
+
+	if (fd < 0)
+		close(source);
+
+	memset(ip_ext, 0, sizeof(ip_ext));
+	memcpy(ip_ext, "0.0.0.0", 7);
+	tmp = buffer;
+	tip = ip_ext;
+	while (tmp < buffer + ifc.ifc_len)
+	{
+		ifr = (struct ifreq *)tmp;
+		tmp += HX_SIZE_OF_IFREQ(*ifr);
+
+		if (ifr->ifr_addr.sa_family == AF_INET)
+		{
+			sinptr = (struct sockaddr_in *)&ifr->ifr_addr;
+			if (sinptr->sin_addr.s_addr != lhost)
+			{
+				add = ntohl(sinptr->sin_addr.s_addr);
+				len = g_snprintf(tip, 17, "%lu.%lu.%lu.%lu;",
+					((add >> 24) & 255),
+					((add >> 16) & 255),
+					((add >> 8) & 255),
+					add & 255);
+				tip = (char*) ((int) tip + len);
+				continue;
+			}
+		}
+	}
+
+	return ip_ext;
+}
--- a/libpurple/protocols/bonjour/jabber.h	Mon Nov 12 20:39:26 2007 +0000
+++ b/libpurple/protocols/bonjour/jabber.h	Mon Nov 12 23:10:08 2007 +0000
@@ -75,4 +75,24 @@
 
 void bonjour_jabber_stop(BonjourJabber *data);
 
+typedef enum {
+	XEP_IQ_SET,
+	XEP_IQ_GET,
+	XEP_IQ_RESULT,
+	XEP_IQ_ERROR,
+	XEP_IQ_NONE
+} XepIqType;
+
+typedef struct _XepIq {
+	XepIqType type;
+	char *id;
+	xmlnode *node;
+	char *to;
+	void *data;
+} XepIq;
+
+XepIq *xep_iq_new(void *data, XepIqType type, const gchar *to, const gchar *id);
+int xep_iq_send(XepIq *iq);
+char *purple_network_get_my_ip_ext2(int fd);
+
 #endif /* _BONJOUR_JABBER_H_ */