changeset 18050:2f9eabdc6011

propagate from branch 'im.pidgin.pidgin' (head dcdd8dc5d40cafef802a88f3f713ee1589bc9c41) to branch 'im.pidgin.pidgin.2.1.0' (head b13966c1f1b76ef136a61969fc8bcc68f02b138c)
author Sean Egan <seanegan@gmail.com>
date Wed, 06 Jun 2007 00:23:41 +0000
parents 82d68fedbd77 (current diff) 264d5439849b (diff)
children c57c2b245c89
files COPYRIGHT libpurple/conversation.c libpurple/idle.c libpurple/protocols/bonjour/dns_sd.c libpurple/protocols/bonjour/dns_sd.h libpurple/util.c pidgin/gtkblist.c pidgin/gtkconv.c pidgin/gtkimhtmltoolbar.c pidgin/gtkutils.c
diffstat 31 files changed, 1206 insertions(+), 633 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Tue Jun 05 12:14:38 2007 +0000
+++ b/COPYRIGHT	Wed Jun 06 00:23:41 2007 +0000
@@ -90,6 +90,7 @@
 Jeramey Crawford
 Michael Culbertson
 Steven Danna
+Chris Davies
 Martijn Dekker
 Vinicius Depizzol
 Philip Derrin
@@ -267,6 +268,7 @@
 Ted Percival
 Eduardo Pérez
 Matt Perry
+Nathan Peterson
 Celso Pinto
 Joao Luís Marques Pinto
 Aleksander Piotrowski
--- a/ChangeLog.win32	Tue Jun 05 12:14:38 2007 +0000
+++ b/ChangeLog.win32	Wed Jun 06 00:23:41 2007 +0000
@@ -1,4 +1,7 @@
-version 2.0.1 (??/??/????):
+version 2.0.2 (??/??/????):
+	* Add Bonjour protocol support thanks to Chris Davies. This requires
+	  Apple Bonjour for Windows from:
+	  http://www.apple.com/support/downloads/bonjourforwindows.html
 
 version 2.0.0 (5/3/2007):
 	* URI Handler support added via `pidgin.exe --protocolhandler=`
--- a/libpurple/conversation.c	Tue Jun 05 12:14:38 2007 +0000
+++ b/libpurple/conversation.c	Wed Jun 06 00:23:41 2007 +0000
@@ -1540,20 +1540,22 @@
 		PurpleConvChatBuddyFlags flag = GPOINTER_TO_INT(fl->data);
 		const char *extra_msg = (extra_msgs ? extra_msgs->data : NULL);
 
-		if (!strcmp(chat->nick, purple_normalize(conv->account, user))) {
-			const char *alias2 = purple_account_get_alias(conv->account);
-			if (alias2 != NULL)
-				alias = alias2;
-			else
-			{
-				const char *display_name = purple_connection_get_display_name(gc);
-				if (display_name != NULL)
-					alias = display_name;
+		if(!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
+			if (!strcmp(chat->nick, purple_normalize(conv->account, user))) {
+				const char *alias2 = purple_account_get_alias(conv->account);
+				if (alias2 != NULL)
+					alias = alias2;
+				else
+				{
+					const char *display_name = purple_connection_get_display_name(gc);
+					if (display_name != NULL)
+						alias = display_name;
+				}
+			} else {
+				PurpleBuddy *buddy;
+				if ((buddy = purple_find_buddy(gc->account, user)) != NULL)
+					alias = purple_buddy_get_contact_alias(buddy);
 			}
-		} else if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
-			PurpleBuddy *buddy;
-			if ((buddy = purple_find_buddy(gc->account, user)) != NULL)
-				alias = purple_buddy_get_contact_alias(buddy);
 		}
 
 		quiet = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_conversations_get_handle(),
@@ -1640,14 +1642,16 @@
 		/* Note this for later. */
 		is_me = TRUE;
 
-		alias = purple_account_get_alias(conv->account);
-		if (alias != NULL)
-			new_alias = alias;
-		else
-		{
-			const char *display_name = purple_connection_get_display_name(gc);
-			if (display_name != NULL)
-				alias = display_name;
+		if(!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
+			alias = purple_account_get_alias(conv->account);
+			if (alias != NULL)
+				new_alias = alias;
+			else
+			{
+				const char *display_name = purple_connection_get_display_name(gc);
+				if (display_name != NULL)
+					alias = display_name;
+			}
 		}
 	} else if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
 		PurpleBuddy *buddy;
--- a/libpurple/idle.c	Tue Jun 05 12:14:38 2007 +0000
+++ b/libpurple/idle.c	Wed Jun 06 00:23:41 2007 +0000
@@ -92,7 +92,7 @@
 }
 
 
-static int no_away = 0;
+static gboolean no_away = FALSE;
 static gint time_until_next_idle_event;
 /*
  * This function should be called when you think your idle state
@@ -118,15 +118,15 @@
 	time_t time_idle;
 	gboolean auto_away;
 	const gchar *idle_reporting;
-	gboolean report_idle;
-	GList *l;
+	gboolean report_idle = TRUE;
 	gint away_seconds = 0;
 	gint idle_recheck_interval = 0;
 
 	purple_signal_emit(purple_blist_get_handle(), "update-idle");
 
 	idle_reporting = purple_prefs_get_string("/purple/away/idle_reporting");
-	report_idle = TRUE;
+	auto_away = purple_prefs_get_bool("/purple/away/away_when_idle");
+
 	if (!strcmp(idle_reporting, "system") &&
 		(idle_ui_ops != NULL) && (idle_ui_ops->get_time_idle != NULL))
 	{
@@ -145,15 +145,9 @@
 		/* Don't report idle time */
 		time_idle = 0;
 		report_idle = FALSE;
-	}
 
-	/* Auto-away stuff */
-	auto_away = purple_prefs_get_bool("/purple/away/away_when_idle");
-
-	/* If we're not reporting idle, we can still do auto-away.
-	 * First try "system" and if that isn't possible, use "purple" */
-	if (!report_idle)
-	{
+		/* If we're not reporting idle, we can still do auto-away.
+		 * First try "system" and if that isn't possible, use "purple" */
 		if (auto_away)
 		{
 			if ((idle_ui_ops != NULL) && (idle_ui_ops->get_time_idle != NULL))
@@ -172,7 +166,7 @@
 			if (!no_away)
 			{
 				purple_savedstatus_set_idleaway(FALSE);
-				no_away = 1;
+				no_away = TRUE;
 			}
 			time_until_next_idle_event = 0;
 			return;
@@ -192,11 +186,11 @@
 	if (auto_away && time_idle > away_seconds)
 	{
 		purple_savedstatus_set_idleaway(TRUE);
-		no_away = 0;
+		no_away = FALSE;
 	}
 	else if (!no_away && time_idle < away_seconds)
 	{
-		no_away = 1;
+		no_away = TRUE;
 		purple_savedstatus_set_idleaway(FALSE);
 		if (time_until_next_idle_event == 0 || (away_seconds - time_idle) < time_until_next_idle_event)
 			time_until_next_idle_event = away_seconds - time_idle;
@@ -205,6 +199,7 @@
 	/* Idle reporting stuff */
 	if (report_idle && (time_idle >= IDLEMARK))
 	{
+		GList *l;
 		for (l = purple_connections_get_all(); l != NULL; l = l->next)
 		{
 			PurpleConnection *gc = l->data;
--- a/libpurple/protocols/Makefile.mingw	Tue Jun 05 12:14:38 2007 +0000
+++ b/libpurple/protocols/Makefile.mingw	Wed Jun 06 00:23:41 2007 +0000
@@ -8,7 +8,7 @@
 PIDGIN_TREE_TOP := ../..
 include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
 
-SUBDIRS = gg irc jabber msn novell null oscar qq sametime silc simple yahoo
+SUBDIRS = gg irc jabber msn novell null oscar qq sametime silc simple yahoo bonjour
 
 .PHONY: all install clean
 
--- a/libpurple/protocols/bonjour/Makefile.am	Tue Jun 05 12:14:38 2007 +0000
+++ b/libpurple/protocols/bonjour/Makefile.am	Wed Jun 06 00:23:41 2007 +0000
@@ -1,4 +1,6 @@
 EXTRA_DIST = \
+		mdns_win32.c \
+		mdns_win32.h \
 		Makefile.mingw
 
 pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
@@ -8,12 +10,16 @@
 	bonjour.h \
 	buddy.c \
 	buddy.h \
-	dns_sd.c \
-	dns_sd.h \
+	dns_sd_proxy.h \
 	jabber.c \
-	jabber.h
+	jabber.h \
+	mdns_common.c \
+	mdns_common.h \
+	mdns_howl.c \
+	mdns_howl.h \
+	mdns_types.h
 
-AM_CFLAGS = $(st)
+AM_CFLAGS = $(st) -DUSE_BONJOUR_HOWL
 
 libbonjour_la_LDFLAGS = -module -avoid-version
 
--- a/libpurple/protocols/bonjour/Makefile.mingw	Tue Jun 05 12:14:38 2007 +0000
+++ b/libpurple/protocols/bonjour/Makefile.mingw	Wed Jun 06 00:23:41 2007 +0000
@@ -8,7 +8,6 @@
 include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
 
 TARGET = libbonjour
-NEEDED_DLLS = $(HOWL_TOP)/bin/libhowl-1.dll
 TYPE = PLUGIN
 
 # Static or Plugin...
@@ -21,20 +20,22 @@
 endif
 endif
 
+CFLAGS += -DUSE_BONJOUR_APPLE
+
 ##
 ## INCLUDE PATHS
 ##
-INCLUDE_PATHS +=	-I$(BONJOUR_ROOT) \
+INCLUDE_PATHS +=	-I. \
 			-I$(GTK_TOP)/include \
 			-I$(GTK_TOP)/include/glib-2.0 \
 			-I$(GTK_TOP)/lib/glib-2.0/include \
-			-I$(HOWL_TOP)/include \
+			-I$(BONJOUR_TOP)/include \
 			-I$(PURPLE_TOP) \
 			-I$(PURPLE_TOP)/win32 \
 			-I$(PIDGIN_TREE_TOP)
 
 LIB_PATHS +=		-L$(GTK_TOP)/lib \
-			-L$(HOWL_TOP)/lib \
+			-L$(BONJOUR_TOP)/lib \
 			-L$(PURPLE_TOP)
 
 ##
@@ -42,7 +43,8 @@
 ##
 C_SRC =			bonjour.c \
 			buddy.c \
-			dns_sd.c \
+			mdns_common.c \
+			mdns_win32.c \
 			jabber.c
 
 OBJECTS = $(C_SRC:%.c=%.o)
@@ -54,7 +56,7 @@
 			-lglib-2.0 \
 			-lws2_32 \
 			-lintl \
-			-lhowl \
+			-ldnssd \
 			-lpurple
 
 include $(PIDGIN_COMMON_RULES)
@@ -68,7 +70,6 @@
 
 install: all $(DLL_INSTALL_DIR)
 	cp $(TARGET).dll $(DLL_INSTALL_DIR)
-	cp $(NEEDED_DLLS) $(PURPLE_INSTALL_DIR)
 
 $(OBJECTS): $(PURPLE_CONFIG_H)
 
--- a/libpurple/protocols/bonjour/bonjour.c	Tue Jun 05 12:14:38 2007 +0000
+++ b/libpurple/protocols/bonjour/bonjour.c	Wed Jun 06 00:23:41 2007 +0000
@@ -37,7 +37,7 @@
 #include "version.h"
 
 #include "bonjour.h"
-#include "dns_sd.h"
+#include "mdns_common.h"
 #include "jabber.h"
 #include "buddy.h"
 
@@ -120,17 +120,11 @@
 
 	/* Connect to the mDNS daemon looking for buddies in the LAN */
 	bd->dns_sd_data = bonjour_dns_sd_new();
-	bd->dns_sd_data->name = (sw_string)purple_account_get_username(account);
-	bd->dns_sd_data->txtvers = g_strdup("1");
-	bd->dns_sd_data->version = g_strdup("1");
 	bd->dns_sd_data->first = g_strdup(purple_account_get_string(account, "first", default_firstname));
 	bd->dns_sd_data->last = g_strdup(purple_account_get_string(account, "last", default_lastname));
 	bd->dns_sd_data->port_p2pj = bd->jabber_data->port;
-	bd->dns_sd_data->phsh = g_strdup("");
-	bd->dns_sd_data->email = g_strdup(purple_account_get_string(account, "email", ""));
-	bd->dns_sd_data->vc = g_strdup("");
-	bd->dns_sd_data->jid = g_strdup(purple_account_get_string(account, "jid", ""));
-	bd->dns_sd_data->AIM = g_strdup(purple_account_get_string(account, "AIM", ""));
+	/* Not engaged in AV conference */
+	bd->dns_sd_data->vc = g_strdup("!");
 
 	status = purple_account_get_active_status(account);
 	presence = purple_account_get_presence(account);
@@ -491,8 +485,8 @@
 		LPUSER_INFO_10 user_info = NULL;
 		LPSERVER_INFO_100 server_info = NULL;
 		wchar_t *servername = NULL;
-		wchar_t username[UNLEN + 1] = {'\0'};
-		DWORD dwLenUsername = sizeof(username);
+		wchar_t username[UNLEN + 1];
+		DWORD dwLenUsername = UNLEN + 1;
 		FARPROC myNetServerEnum = wpurple_find_and_loadproc(
 			"Netapi32.dll", "NetServerEnum");
 		FARPROC myNetApiBufferFree = wpurple_find_and_loadproc(
@@ -517,7 +511,7 @@
 			}
 		}
 
-		if (!GetUserNameW(&username, &dwLenUsername)) {
+		if (!GetUserNameW((LPWSTR) &username, &dwLenUsername)) {
 			purple_debug_warning("bonjour",
 				"Unable to look up username\n");
 		}
@@ -553,7 +547,7 @@
 		 */
 		splitpoint = strchr(tmp, ',');
 		if (splitpoint != NULL)
-			default_lastname = g_strndup(tmp, splitpoint - tmp);			
+			default_lastname = g_strndup(tmp, splitpoint - tmp);
 		else
 			default_lastname = g_strdup(tmp);
 	}
--- a/libpurple/protocols/bonjour/bonjour.h	Tue Jun 05 12:14:38 2007 +0000
+++ b/libpurple/protocols/bonjour/bonjour.h	Wed Jun 06 00:23:41 2007 +0000
@@ -26,9 +26,7 @@
 #ifndef _BONJOUR_H_
 #define _BONJOUR_H_
 
-#include <howl.h>
-
-#include "dns_sd.h"
+#include "mdns_common.h"
 #include "internal.h"
 #include "jabber.h"
 
--- a/libpurple/protocols/bonjour/buddy.c	Tue Jun 05 12:14:38 2007 +0000
+++ b/libpurple/protocols/bonjour/buddy.c	Wed Jun 06 00:23:41 2007 +0000
@@ -27,51 +27,66 @@
  * Creates a new buddy.
  */
 BonjourBuddy *
-bonjour_buddy_new(const gchar *name, const gchar *first, gint port_p2pj,
-	const gchar *phsh, const gchar *status, const gchar *email,
-	const gchar *last, const gchar *jid, const gchar *AIM,
-	const gchar *vc, const gchar *ip, const gchar *msg)
+bonjour_buddy_new(const gchar *name, PurpleAccount* account)
 {
-	BonjourBuddy *buddy = malloc(sizeof(BonjourBuddy));
+	BonjourBuddy *buddy = g_new0(BonjourBuddy, 1);
 
+	buddy->account = account;
 	buddy->name = g_strdup(name);
-	buddy->first = g_strdup(first);
-	buddy->port_p2pj = port_p2pj;
-	buddy->phsh = g_strdup(phsh);
-	buddy->status = g_strdup(status);
-	buddy->email = g_strdup(email);
-	buddy->last = g_strdup(last);
-	buddy->jid = g_strdup(jid);
-	buddy->AIM = g_strdup(AIM);
-	buddy->vc = g_strdup(vc);
-	buddy->ip = g_strdup(ip);
-	buddy->msg = g_strdup(msg);
-	buddy->conversation = NULL;
 
 	return buddy;
 }
 
+void
+set_bonjour_buddy_value(BonjourBuddy* buddy, const char *record_key, const char *value, uint32_t len){
+	gchar **fld = NULL;
+
+	if (!strcmp(record_key, "1st"))
+		fld = &buddy->first;
+	else if(!strcmp(record_key, "email"))
+		fld = &buddy->email;
+	else if(!strcmp(record_key, "ext"))
+		fld = &buddy->ext;
+	else if(!strcmp(record_key, "jid"))
+		fld = &buddy->jid;
+	else if(!strcmp(record_key, "last"))
+		fld = &buddy->last;
+	else if(!strcmp(record_key, "msg"))
+		fld = &buddy->msg;
+	else if(!strcmp(record_key, "nick"))
+		fld = &buddy->nick;
+	else if(!strcmp(record_key, "node"))
+		fld = &buddy->node;
+	else if(!strcmp(record_key, "phsh"))
+		fld = &buddy->phsh;
+	else if(!strcmp(record_key, "status"))
+		fld = &buddy->status;
+	else if(!strcmp(record_key, "vc"))
+		fld = &buddy->vc;
+	else if(!strcmp(record_key, "ver"))
+		fld = &buddy->ver;
+	else if(!strcmp(record_key, "AIM"))
+		fld = &buddy->AIM;
+
+	if(fld == NULL)
+		return;
+
+	g_free(*fld);
+	*fld = NULL;
+	*fld = g_strndup(value, len);
+}
+
 /**
  * Check if all the compulsory buddy data is present.
  */
 gboolean
 bonjour_buddy_check(BonjourBuddy *buddy)
 {
-	if (buddy->name == NULL) {
+	if (buddy->account == NULL)
 		return FALSE;
-	}
-
-	if (buddy->first == NULL) {
-		return FALSE;
-	}
 
-	if (buddy->last == NULL) {
+	if (buddy->name == NULL)
 		return FALSE;
-	}
-
-	if (buddy->status == NULL) {
-		return FALSE;
-	}
 
 	return TRUE;
 }
@@ -82,12 +97,12 @@
  * the buddy.
  */
 void
-bonjour_buddy_add_to_purple(PurpleAccount *account, BonjourBuddy *bonjour_buddy)
+bonjour_buddy_add_to_purple(BonjourBuddy *bonjour_buddy)
 {
 	PurpleBuddy *buddy;
 	PurpleGroup *group;
 	const char *status_id, *first, *last;
-	char *alias;
+	gchar *alias;
 
 	/* Translate between the Bonjour status and the Purple status */
 	if (g_ascii_strcasecmp("dnd", bonjour_buddy->status) == 0)
@@ -117,10 +132,11 @@
 	}
 
 	/* Make sure the buddy exists in our buddy list */
-	buddy = purple_find_buddy(account, bonjour_buddy->name);
+	buddy = purple_find_buddy(bonjour_buddy->account, bonjour_buddy->name);
+
 	if (buddy == NULL)
 	{
-		buddy = purple_buddy_new(account, bonjour_buddy->name, alias);
+		buddy = purple_buddy_new(bonjour_buddy->account, bonjour_buddy->name, alias);
 		buddy->proto_data = bonjour_buddy;
 		purple_blist_node_set_flags((PurpleBlistNode *)buddy, PURPLE_BLIST_NODE_FLAG_NO_SAVE);
 		purple_blist_add_buddy(buddy, NULL, group, NULL);
@@ -128,13 +144,13 @@
 
 	/* Set the user's status */
 	if (bonjour_buddy->msg != NULL)
-		purple_prpl_got_user_status(account, buddy->name, status_id,
+		purple_prpl_got_user_status(bonjour_buddy->account, buddy->name, status_id,
 								  "message", bonjour_buddy->msg,
 								  NULL);
 	else
-		purple_prpl_got_user_status(account, buddy->name, status_id,
+		purple_prpl_got_user_status(bonjour_buddy->account, buddy->name, status_id,
 								  NULL);
-	purple_prpl_got_user_idle(account, buddy->name, FALSE, 0);
+	purple_prpl_got_user_idle(bonjour_buddy->account, buddy->name, FALSE, 0);
 
 	g_free(alias);
 }
@@ -163,5 +179,13 @@
 		g_free(buddy->conversation);
 	}
 
-	free(buddy);
+#ifdef USE_BONJOUR_APPLE
+	if (buddy->txt_query != NULL)
+	{
+		purple_input_remove(buddy->txt_query_fd);
+		DNSServiceRefDeallocate(buddy->txt_query);
+	}
+#endif
+
+	g_free(buddy);
 }
--- a/libpurple/protocols/bonjour/buddy.h	Tue Jun 05 12:14:38 2007 +0000
+++ b/libpurple/protocols/bonjour/buddy.h	Wed Jun 06 00:23:41 2007 +0000
@@ -17,17 +17,27 @@
 #ifndef _BONJOUR_BUDDY
 #define _BONJOUR_BUDDY
 
-#include <howl.h>
 #include <glib.h>
 
+#include "config.h"
 #include "account.h"
 #include "jabber.h"
 
+#ifdef USE_BONJOUR_APPLE 
+#include "dns_sd_proxy.h"
+#else /* USE_BONJOUR_HOWL */
+#include <howl.h>
+#endif
+
 typedef struct _BonjourBuddy
 {
+	PurpleAccount *account;
+
 	gchar *name;
+	gchar *ip;
+	gint port_p2pj;
+
 	gchar *first;
-	gint port_p2pj;
 	gchar *phsh;
 	gchar *status;
 	gchar *email;
@@ -35,18 +45,49 @@
 	gchar *jid;
 	gchar *AIM;
 	gchar *vc;
-	gchar *ip;
 	gchar *msg;
+	gchar *ext;
+	gchar *nick;
+	gchar *node;
+	gchar *ver;
+
 	BonjourJabberConversation *conversation;
+
+#ifdef USE_BONJOUR_APPLE
+	DNSServiceRef txt_query;
+	int txt_query_fd;
+#endif
+
 } BonjourBuddy;
 
+static const char *const buddy_TXT_records[] = {
+	"1st",
+	"email",
+	"ext",
+	"jid",
+	"last",
+	"msg",
+	"nick",
+	"node",
+	"phsh",
+/*	"port.p2pj", Deprecated - MUST ignore */
+	"status",
+/*	"txtvers", Deprecated - hardcoded to 1 */
+	"vc",
+	"ver",
+	"AIM", /* non standard */
+	NULL
+};
+
 /**
  * Creates a new buddy.
  */
-BonjourBuddy *bonjour_buddy_new(const gchar *name, const gchar *first,
-	gint port_p2pj, const gchar *phsh, const gchar *status,
-	const gchar *email, const gchar *last, const gchar *jid,
-	const gchar *AIM, const gchar *vc, const gchar *ip, const gchar *msg);
+BonjourBuddy *bonjour_buddy_new(const gchar *name, PurpleAccount *account);
+
+/**
+ * Sets a value in the BonjourBuddy struct, destroying the old value
+ */
+void set_bonjour_buddy_value(BonjourBuddy* buddy, const char *record_key, const char *value, uint32_t len);
 
 /**
  * Check if all the compulsory buddy data is present.
@@ -56,7 +97,7 @@
 /**
  * If the buddy doesn't previoulsy exists, it is created. Else, its data is changed (???)
  */
-void bonjour_buddy_add_to_purple(PurpleAccount *account, BonjourBuddy *buddy);
+void bonjour_buddy_add_to_purple(BonjourBuddy *buddy);
 
 /**
  * Deletes a buddy from memory.
--- a/libpurple/protocols/bonjour/dns_sd.c	Tue Jun 05 12:14:38 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,387 +0,0 @@
-/*
- *  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 Library 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 <string.h>
-
-#include "dns_sd.h"
-#include "bonjour.h"
-#include "buddy.h"
-#include "debug.h"
-
-/* Private functions */
-
-static sw_result HOWL_API
-_publish_reply(sw_discovery discovery, sw_discovery_oid oid,
-			   sw_discovery_publish_status status, sw_opaque extra)
-{
-	purple_debug_warning("bonjour", "_publish_reply --> Start\n");
-
-	/* Check the answer from the mDNS daemon */
-	switch (status)
-	{
-		case SW_DISCOVERY_PUBLISH_STARTED :
-			purple_debug_info("bonjour", "_publish_reply --> Service started\n");
-			break;
-		case SW_DISCOVERY_PUBLISH_STOPPED :
-			purple_debug_info("bonjour", "_publish_reply --> Service stopped\n");
-			break;
-		case SW_DISCOVERY_PUBLISH_NAME_COLLISION :
-			purple_debug_info("bonjour", "_publish_reply --> Name collision\n");
-			break;
-		case SW_DISCOVERY_PUBLISH_INVALID :
-			purple_debug_info("bonjour", "_publish_reply --> Service invalid\n");
-			break;
-	}
-
-	return SW_OKAY;
-}
-
-static sw_result HOWL_API
-_resolve_reply(sw_discovery discovery, sw_discovery_oid oid,
-			   sw_uint32 interface_index, sw_const_string name,
-			   sw_const_string type, sw_const_string domain,
-			   sw_ipv4_address address, sw_port port,
-			   sw_octets text_record, sw_ulong text_record_len,
-			   sw_opaque extra)
-{
-	BonjourBuddy *buddy;
-	PurpleAccount *account = (PurpleAccount*)extra;
-	gchar *txtvers = NULL;
-	gchar *version = NULL;
-	gchar *first = NULL;
-	gchar *phsh = NULL;
-	gchar *status = NULL;
-	gchar *email = NULL;
-	gchar *last = NULL;
-	gchar *jid = NULL;
-	gchar *AIM = NULL;
-	gchar *vc = NULL;
-	gchar *msg = NULL;
-	gint address_length = 16;
-	gchar *ip = NULL;
-	sw_text_record_iterator iterator;
-	char key[SW_TEXT_RECORD_MAX_LEN];
-	char value[SW_TEXT_RECORD_MAX_LEN];
-	sw_uint32 value_length;
-
-	sw_discovery_cancel(discovery, oid);
-
-	/* Get the ip as a string */
-	ip = malloc(address_length);
-	sw_ipv4_address_name(address, ip, address_length);
-
-	/* Obtain the parameters from the text_record */
-	if ((text_record_len > 0) && (text_record) && (*text_record != '\0'))
-	{
-		sw_text_record_iterator_init(&iterator, text_record, text_record_len);
-		while (sw_text_record_iterator_next(iterator, key, (sw_octet *)value, &value_length) == SW_OKAY)
-		{
-			/* Compare the keys with the possible ones and save them on */
-			/* the appropiate place of the buddy_list */
-			if (strcmp(key, "txtvers") == 0) {
-				txtvers = g_strdup(value);
-			} else if (strcmp(key, "version") == 0) {
-				version = g_strdup(value);
-			} else if (strcmp(key, "1st") == 0) {
-				first = g_strdup(value);
-			} else if (strcmp(key, "status") == 0) {
-				status = g_strdup(value);
-			} else if (strcmp(key, "email") == 0) {
-				email = g_strdup(value);
-			} else if (strcmp(key, "last") == 0) {
-				last = g_strdup(value);
-			} else if (strcmp(key, "jid") == 0) {
-				jid = g_strdup(value);
-			} else if (strcmp(key, "AIM") == 0) {
-				AIM = g_strdup(value);
-			} else if (strcmp(key, "vc") == 0) {
-				vc = g_strdup(value);
-			} else if (strcmp(key, "phsh") == 0) {
-				phsh = g_strdup(value);
-			} else if (strcmp(key, "msg") == 0) {
-				msg = g_strdup(value);
-			}
-		}
-	}
-
-	/* Put the parameters of the text_record in a buddy and add the buddy to */
-	/* the buddy list */
-	buddy = bonjour_buddy_new(name, first, port, phsh,
-							  status, email, last, jid, AIM, vc, ip, msg);
-
-	if (bonjour_buddy_check(buddy) == FALSE)
-	{
-		bonjour_buddy_delete(buddy);
-		return SW_DISCOVERY_E_UNKNOWN;
-	}
-
-	/* Add or update the buddy in our buddy list */
-	bonjour_buddy_add_to_purple(account, buddy);
-
-	/* Free all the temporal strings */
-	g_free(txtvers);
-	g_free(version);
-	g_free(first);
-	g_free(last);
-	g_free(status);
-	g_free(email);
-	g_free(jid);
-	g_free(AIM);
-	g_free(vc);
-	g_free(phsh);
-	g_free(msg);
-
-	return SW_OKAY;
-}
-
-static sw_result HOWL_API
-_browser_reply(sw_discovery discovery, sw_discovery_oid oid,
-			   sw_discovery_browse_status status,
-			   sw_uint32 interface_index, sw_const_string name,
-			   sw_const_string type, sw_const_string domain,
-			   sw_opaque_t extra)
-{
-	sw_discovery_resolve_id rid;
-	PurpleAccount *account = (PurpleAccount*)extra;
-	PurpleBuddy *gb = NULL;
-
-	switch (status)
-	{
-		case SW_DISCOVERY_BROWSE_INVALID:
-			purple_debug_warning("bonjour", "_browser_reply --> Invalid\n");
-			break;
-		case SW_DISCOVERY_BROWSE_RELEASE:
-			purple_debug_warning("bonjour", "_browser_reply --> Release\n");
-			break;
-		case SW_DISCOVERY_BROWSE_ADD_DOMAIN:
-			purple_debug_warning("bonjour", "_browser_reply --> Add domain\n");
-			break;
-		case SW_DISCOVERY_BROWSE_ADD_DEFAULT_DOMAIN:
-			purple_debug_warning("bonjour", "_browser_reply --> Add default domain\n");
-			break;
-		case SW_DISCOVERY_BROWSE_REMOVE_DOMAIN:
-			purple_debug_warning("bonjour", "_browser_reply --> Remove domain\n");
-			break;
-		case SW_DISCOVERY_BROWSE_ADD_SERVICE:
-			/* A new peer has joined the network and uses iChat bonjour */
-			purple_debug_info("bonjour", "_browser_reply --> Add service\n");
-			if (g_ascii_strcasecmp(name, account->username) != 0)
-			{
-				if (sw_discovery_resolve(discovery, interface_index, name, type,
-						domain, _resolve_reply, extra, &rid) != SW_OKAY)
-				{
-					purple_debug_warning("bonjour", "_browser_reply --> Cannot send resolve\n");
-				}
-			}
-			break;
-		case SW_DISCOVERY_BROWSE_REMOVE_SERVICE:
-			purple_debug_info("bonjour", "_browser_reply --> Remove service\n");
-			gb = purple_find_buddy((PurpleAccount*)extra, name);
-			if (gb != NULL)
-			{
-				bonjour_buddy_delete(gb->proto_data);
-				purple_blist_remove_buddy(gb);
-			}
-			break;
-		case SW_DISCOVERY_BROWSE_RESOLVED:
-			purple_debug_info("bonjour", "_browse_reply --> Resolved\n");
-			break;
-		default:
-			break;
-	}
-
-	return SW_OKAY;
-}
-
-static int
-_dns_sd_publish(BonjourDnsSd *data, PublishType type)
-{
-	sw_text_record dns_data;
-	sw_result publish_result = SW_OKAY;
-	char portstring[6];
-
-	/* Fill the data for the service */
-	if (sw_text_record_init(&dns_data) != SW_OKAY)
-	{
-		purple_debug_error("bonjour", "Unable to initialize the data for the mDNS.\n");
-		return -1;
-	}
-
-	/* Convert the port to a string */
-	snprintf(portstring, sizeof(portstring), "%d", data->port_p2pj);
-
-	/* Publish standard records */
-	sw_text_record_add_key_and_string_value(dns_data, "txtvers", data->txtvers);
-	sw_text_record_add_key_and_string_value(dns_data, "version", data->version);
-	sw_text_record_add_key_and_string_value(dns_data, "1st", data->first);
-	sw_text_record_add_key_and_string_value(dns_data, "last", data->last);
-	sw_text_record_add_key_and_string_value(dns_data, "port.p2pj", portstring);
-	sw_text_record_add_key_and_string_value(dns_data, "phsh", data->phsh);
-	sw_text_record_add_key_and_string_value(dns_data, "status", data->status);
-	sw_text_record_add_key_and_string_value(dns_data, "vc", data->vc);
-
-	/* Publish extra records */
-	if ((data->email != NULL) && (*data->email != '\0'))
-		sw_text_record_add_key_and_string_value(dns_data, "email", data->email);
-
-	if ((data->jid != NULL) && (*data->jid != '\0'))
-		sw_text_record_add_key_and_string_value(dns_data, "jid", data->jid);
-
-	if ((data->AIM != NULL) && (*data->AIM != '\0'))
-		sw_text_record_add_key_and_string_value(dns_data, "AIM", data->AIM);
-
-	if ((data->msg != NULL) && (*data->msg != '\0'))
-		sw_text_record_add_key_and_string_value(dns_data, "msg", data->msg);
-
-	/* Publish the service */
-	switch (type)
-	{
-		case PUBLISH_START:
-			publish_result = sw_discovery_publish(data->session, 0, data->name, ICHAT_SERVICE, NULL,
-								NULL, data->port_p2pj, sw_text_record_bytes(dns_data), sw_text_record_len(dns_data),
-								_publish_reply, NULL, &data->session_id);
-			break;
-		case PUBLISH_UPDATE:
-			publish_result = sw_discovery_publish_update(data->session, data->session_id,
-								sw_text_record_bytes(dns_data), sw_text_record_len(dns_data));
-			break;
-	}
-	if (publish_result != SW_OKAY)
-	{
-		purple_debug_error("bonjour", "Unable to publish or change the status of the _presence._tcp service.\n");
-		return -1;
-	}
-
-	/* Free the memory used by temp data */
-	sw_text_record_fina(dns_data);
-
-	return 0;
-}
-
-static void
-_dns_sd_handle_packets(gpointer data, gint source, PurpleInputCondition condition)
-{
-	sw_discovery_read_socket((sw_discovery)data);
-}
-
-/* End private functions */
-
-/**
- * Allocate space for the dns-sd data.
- */
-BonjourDnsSd *
-bonjour_dns_sd_new()
-{
-	BonjourDnsSd *data = g_new0(BonjourDnsSd, 1);
-
-	return data;
-}
-
-/**
- * Deallocate the space of the dns-sd data.
- */
-void
-bonjour_dns_sd_free(BonjourDnsSd *data)
-{
-	g_free(data->first);
-	g_free(data->last);
-	g_free(data->email);
-	g_free(data);
-}
-
-/**
- * Send a new dns-sd packet updating our status.
- */
-void
-bonjour_dns_sd_send_status(BonjourDnsSd *data, const char *status, const char *status_message)
-{
-	g_free(data->status);
-	g_free(data->msg);
-
-	data->status = g_strdup(status);
-	data->msg = g_strdup(status_message);
-
-	/* Update our text record with the new status */
-	_dns_sd_publish(data, PUBLISH_UPDATE); /* <--We must control the errors */
-}
-
-/**
- * Advertise our presence within the dns-sd daemon and start browsing
- * for other bonjour peers.
- */
-gboolean
-bonjour_dns_sd_start(BonjourDnsSd *data)
-{
-	PurpleAccount *account;
-	PurpleConnection *gc;
-	gint dns_sd_socket;
-	sw_discovery_oid session_id;
-
-	account = data->account;
-	gc = purple_account_get_connection(account);
-
-	/* Initialize the dns-sd data and session */
-	if (sw_discovery_init(&data->session) != SW_OKAY)
-	{
-		purple_debug_error("bonjour", "Unable to initialize an mDNS session.\n");
-
-		/* In Avahi, sw_discovery_init frees data->session but doesn't clear it */
-		data->session = NULL;
-
-		return FALSE;
-	}
-
-	/* Publish our bonjour IM client at the mDNS daemon */
-	_dns_sd_publish(data, PUBLISH_START); /* <--We must control the errors */
-
-	/* Advise the daemon that we are waiting for connections */
-	if (sw_discovery_browse(data->session, 0, ICHAT_SERVICE, NULL, _browser_reply,
-			data->account, &session_id) != SW_OKAY)
-	{
-		purple_debug_error("bonjour", "Unable to get service.");
-		return FALSE;
-	}
-
-	/* Get the socket that communicates with the mDNS daemon and bind it to a */
-	/* callback that will handle the dns_sd packets */
-	dns_sd_socket = sw_discovery_socket(data->session);
-	gc->inpa = purple_input_add(dns_sd_socket, PURPLE_INPUT_READ,
-									_dns_sd_handle_packets, data->session);
-
-	return TRUE;
-}
-
-/**
- * Unregister the "_presence._tcp" service at the mDNS daemon.
- */
-void
-bonjour_dns_sd_stop(BonjourDnsSd *data)
-{
-	PurpleAccount *account;
-	PurpleConnection *gc;
-
-	if (data->session == NULL)
-		return;
-
-	sw_discovery_cancel(data->session, data->session_id);
-
-	account = data->account;
-	gc = purple_account_get_connection(account);
-	purple_input_remove(gc->inpa);
-
-	g_free(data->session);
-	data->session = NULL;
-}
--- a/libpurple/protocols/bonjour/dns_sd.h	Tue Jun 05 12:14:38 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-/*
- *  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 Library 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_DNS_SD
-#define _BONJOUR_DNS_SD
-
-#include <howl.h>
-#include <glib.h>
-#include "account.h"
-
-#define ICHAT_SERVICE "_presence._tcp."
-
-/**
- * Data to be used by the dns-sd connection.
- */
-typedef struct _BonjourDnsSd
-{
-	sw_discovery session;
-	sw_discovery_oid session_id;
-	PurpleAccount *account;
-	gchar *name;
-	gchar *txtvers;
-	gchar *version;
-	gchar *first;
-	gchar *last;
-	gint port_p2pj;
-	gchar *phsh;
-	gchar *status;
-	gchar *email;
-	gchar *vc;
-	gchar *jid;
-	gchar *AIM;
-	gchar *msg;
-	GHashTable *buddies;
-} BonjourDnsSd;
-
-typedef enum _PublishType {
-	PUBLISH_START,
-	PUBLISH_UPDATE
-} PublishType;
-
-/**
- * Allocate space for the dns-sd data.
- */
-BonjourDnsSd *bonjour_dns_sd_new(void);
-
-/**
- * Deallocate the space of the dns-sd data.
- */
-void bonjour_dns_sd_free(BonjourDnsSd *data);
-
-/**
- * Send a new dns-sd packet updating our status.
- */
-void bonjour_dns_sd_send_status(BonjourDnsSd *data, const char *status, const char *status_message);
-
-/**
- * Advertise our presence within the dns-sd daemon and start
- * browsing for other bonjour peers.
- */
-gboolean bonjour_dns_sd_start(BonjourDnsSd *data);
-
-/**
- * Unregister the "_presence._tcp" service at the mDNS daemon.
- */
-void bonjour_dns_sd_stop(BonjourDnsSd *data);
-
-#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/bonjour/dns_sd_proxy.h	Wed Jun 06 00:23:41 2007 +0000
@@ -0,0 +1,19 @@
+#ifndef _DNS_SD_PROXY
+#define _DNS_SD_PROXY
+
+#include <stdint.h>
+
+/* fixup to make pidgin compile against win32 bonjour */
+#ifdef _WIN32
+#define _MSL_STDINT_H
+#undef bzero
+#endif
+
+#include <dns_sd.h>
+
+/* dns_sd.h defines bzero and we also do in libc_internal.h */
+#ifdef _WIN32
+#undef bzero
+#endif
+
+#endif
--- a/libpurple/protocols/bonjour/issues.txt	Tue Jun 05 12:14:38 2007 +0000
+++ b/libpurple/protocols/bonjour/issues.txt	Wed Jun 06 00:23:41 2007 +0000
@@ -6,4 +6,3 @@
 * Avatars
 * File transfers
 * Typing notifications
-* Check if it works on win32
--- a/libpurple/protocols/bonjour/jabber.c	Tue Jun 05 12:14:38 2007 +0000
+++ b/libpurple/protocols/bonjour/jabber.c	Wed Jun 06 00:23:41 2007 +0000
@@ -49,22 +49,32 @@
 _connect_to_buddy(PurpleBuddy *gb)
 {
 	gint socket_fd;
-	gint retorno = 0;
 	struct sockaddr_in buddy_address;
+	BonjourBuddy *bb = gb->proto_data;
+
+	purple_debug_info("bonjour", "Connecting to buddy %s at %s:%d.\n",
+		purple_buddy_get_name(gb), bb->ip ? bb->ip : "(null)", bb->port_p2pj);
 
 	/* Create a socket and make it non-blocking */
 	socket_fd = socket(PF_INET, SOCK_STREAM, 0);
+	if (socket_fd < 0) {
+		purple_debug_warning("bonjour", "Error opening socket: %s\n", strerror(errno));
+		return -1;
+	}
 
 	buddy_address.sin_family = PF_INET;
-	buddy_address.sin_port = htons(((BonjourBuddy*)(gb->proto_data))->port_p2pj);
-	inet_aton(((BonjourBuddy*)(gb->proto_data))->ip, &(buddy_address.sin_addr));
+	buddy_address.sin_port = htons(bb->port_p2pj);
+	inet_aton(bb->ip, &(buddy_address.sin_addr));
 	memset(&(buddy_address.sin_zero), '\0', 8);
 
-	retorno = connect(socket_fd, (struct sockaddr*)&buddy_address, sizeof(struct sockaddr));
-	if (retorno == -1) {
-		purple_debug_warning("bonjour", "connect error: %s\n", strerror(errno));
+	/* TODO: make this nonblocking before connecting */
+	if (connect(socket_fd, (struct sockaddr*)&buddy_address, sizeof(struct sockaddr)) == 0)
+		fcntl(socket_fd, F_SETFL, O_NONBLOCK);
+	else {
+		purple_debug_warning("bonjour", "Error connecting to buddy %s at %s:%d error: %s\n", purple_buddy_get_name(gb), bb->ip ? bb->ip : "(null)", bb->port_p2pj, strerror(errno));
+		close(socket_fd);
+		socket_fd = -1;
 	}
-	fcntl(socket_fd, F_SETFL, O_NONBLOCK);
 
 	return socket_fd;
 }
@@ -258,8 +268,8 @@
 	/* Read chunks of 512 bytes till the end of the data */
 	while ((partial_message_length = recv(socket, partial_data, 512, 0)) > 0)
 	{
-			g_string_append_len(data, partial_data, partial_message_length);
-			total_message_length += partial_message_length;
+		g_string_append_len(data, partial_data, partial_message_length);
+		total_message_length += partial_message_length;
 	}
 
 	if (partial_message_length == -1)
@@ -440,7 +450,7 @@
 
 		/* Open a watcher for the client socket */
 		bb->conversation->watcher_id = purple_input_add(client_socket, PURPLE_INPUT_READ,
-													_client_socket_handler, gb);
+								_client_socket_handler, gb);
 	} else {
 		close(client_socket);
 	}
@@ -537,12 +547,29 @@
 	gint ret;
 
 	gb = purple_find_buddy(data->account, to);
-	if (gb == NULL)
+	if (gb == 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 = (BonjourBuddy *)gb->proto_data;
 
+	/* Check if there is a previously open conversation */
+	if (bb->conversation == NULL)
+	{
+		int socket = _connect_to_buddy(gb);
+		if (socket < 0)
+			return -10001;
+
+		bb->conversation = g_new(BonjourJabberConversation, 1);
+		bb->conversation->socket = socket;
+		bb->conversation->stream_started = FALSE;
+		bb->conversation->buddy_name = g_strdup(gb->name);
+		bb->conversation->watcher_id = purple_input_add(bb->conversation->socket,
+				PURPLE_INPUT_READ, _client_socket_handler, gb);
+	}
+
 	/* Enclose the message from the UI within a "font" node */
 	message_body_node = xmlnode_new("body");
 	stripped_message = purple_markup_strip_html(body);
@@ -575,17 +602,6 @@
 	message = xmlnode_to_str(message_node, &message_length);
 	xmlnode_free(message_node);
 
-	/* Check if there is a previously open conversation */
-	if (bb->conversation == NULL)
-	{
-		bb->conversation = g_new(BonjourJabberConversation, 1);
-		bb->conversation->socket = _connect_to_buddy(gb);
-		bb->conversation->stream_started = FALSE;
-		bb->conversation->buddy_name = g_strdup(gb->name);
-		bb->conversation->watcher_id = purple_input_add(bb->conversation->socket,
-				PURPLE_INPUT_READ, _client_socket_handler, gb);
-	}
-
 	/* Check if the stream for the conversation has been started */
 	if (bb->conversation->stream_started == FALSE)
 	{
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/bonjour/mdns_common.c	Wed Jun 06 00:23:41 2007 +0000
@@ -0,0 +1,175 @@
+/*
+ *  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 Library 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 <string.h>
+
+#include "config.h"
+#include "mdns_common.h"
+#include "bonjour.h"
+#include "buddy.h"
+#include "debug.h"
+
+
+/**
+ * Allocate space for the dns-sd data.
+ */
+BonjourDnsSd *
+bonjour_dns_sd_new()
+{
+	BonjourDnsSd *data = g_new0(BonjourDnsSd, 1);
+
+	return data;
+}
+
+/**
+ * Deallocate the space of the dns-sd data.
+ */
+void
+bonjour_dns_sd_free(BonjourDnsSd *data)
+{
+	g_free(data->first);
+	g_free(data->last);
+	g_free(data->phsh);
+	g_free(data->status);
+	g_free(data->vc);
+	g_free(data->msg);
+	g_free(data);
+}
+
+/**
+ * Send a new dns-sd packet updating our status.
+ */
+void
+bonjour_dns_sd_send_status(BonjourDnsSd *data, const char *status, const char *status_message)
+{
+	g_free(data->status);
+	g_free(data->msg);
+
+	data->status = g_strdup(status);
+	data->msg = g_strdup(status_message);
+
+	/* Update our text record with the new status */
+	_mdns_publish(data, PUBLISH_UPDATE); /* <--We must control the errors */
+}
+
+/**
+ * Advertise our presence within the dns-sd daemon and start browsing
+ * for other bonjour peers.
+ */
+gboolean
+bonjour_dns_sd_start(BonjourDnsSd *data)
+{
+	PurpleAccount *account;
+	PurpleConnection *gc;
+	gint dns_sd_socket;
+	gpointer opaque_data;
+
+#ifdef USE_BONJOUR_HOWL
+	sw_discovery_oid session_id;
+#endif
+
+	account = data->account;
+	gc = purple_account_get_connection(account);
+
+	/* Initialize the dns-sd data and session */
+#ifndef USE_BONJOUR_APPLE
+	if (sw_discovery_init(&data->session) != SW_OKAY)
+	{
+		purple_debug_error("bonjour", "Unable to initialize an mDNS session.\n");
+
+		/* In Avahi, sw_discovery_init frees data->session but doesn't clear it */
+		data->session = NULL;
+
+		return FALSE;
+	}
+#endif
+
+	/* Publish our bonjour IM client at the mDNS daemon */
+
+	if (0 != _mdns_publish(data, PUBLISH_START))
+	{
+		return FALSE;
+	}
+
+	/* Advise the daemon that we are waiting for connections */
+	
+#ifdef USE_BONJOUR_APPLE
+	if (DNSServiceBrowse(&data->browser, 0, 0, ICHAT_SERVICE, NULL, _mdns_service_browse_callback, account) 
+			!= kDNSServiceErr_NoError)
+#else /* USE_BONJOUR_HOWL */
+	if (sw_discovery_browse(data->session, 0, ICHAT_SERVICE, NULL, _browser_reply,
+			account, &session_id) != SW_OKAY)
+#endif
+	{
+		purple_debug_error("bonjour", "Unable to get service.");
+		return FALSE;
+	}
+
+	/* Get the socket that communicates with the mDNS daemon and bind it to a */
+	/* callback that will handle the dns_sd packets */
+
+#ifdef USE_BONJOUR_APPLE
+	dns_sd_socket = DNSServiceRefSockFD(data->browser);
+	opaque_data = data->browser;
+#else /* USE_BONJOUR_HOWL */
+	dns_sd_socket = sw_discovery_socket(data->session);
+	opaque_data = data->session;
+#endif
+
+	gc->inpa = purple_input_add(dns_sd_socket, PURPLE_INPUT_READ,
+				    _mdns_handle_event, opaque_data);
+
+	return TRUE;
+}
+
+/**
+ * Unregister the "_presence._tcp" service at the mDNS daemon.
+ */
+
+void
+bonjour_dns_sd_stop(BonjourDnsSd *data)
+{
+	PurpleAccount *account;
+	PurpleConnection *gc;
+
+#ifdef USE_BONJOUR_APPLE
+	if (data->advertisement == NULL || data->browser == NULL)
+#else /* USE_BONJOUR_HOWL */
+	if (data->session == NULL)
+#endif
+		return;
+
+#ifdef USE_BONJOUR_HOWL
+	sw_discovery_cancel(data->session, data->session_id);
+#endif
+
+	account = data->account;
+	gc = purple_account_get_connection(account);
+	purple_input_remove(gc->inpa);
+
+#ifdef USE_BONJOUR_APPLE
+	/* hack: for win32, we need to stop listening to the advertisement pipe too */
+	purple_input_remove(data->advertisement_fd);
+
+	DNSServiceRefDeallocate(data->advertisement);
+	DNSServiceRefDeallocate(data->browser);
+	data->advertisement = NULL;
+	data->browser = NULL;
+#else /* USE_BONJOUR_HOWL */
+	g_free(data->session);
+	data->session = NULL;
+#endif
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/bonjour/mdns_common.h	Wed Jun 06 00:23:41 2007 +0000
@@ -0,0 +1,54 @@
+/*
+ *  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 Library 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_MDNS_COMMON
+#define _BONJOUR_MDNS_COMMON
+
+#include "mdns_types.h"
+
+#ifdef USE_BONJOUR_APPLE
+#include "mdns_win32.h"
+#elif defined USE_BONJOUR_HOWL
+#include "mdns_howl.h"
+#endif
+
+/**
+ * Allocate space for the dns-sd data.
+ */
+BonjourDnsSd *bonjour_dns_sd_new(void);
+
+/**
+ * Deallocate the space of the dns-sd data.
+ */
+void bonjour_dns_sd_free(BonjourDnsSd *data);
+
+/**
+ * Send a new dns-sd packet updating our status.
+ */
+void bonjour_dns_sd_send_status(BonjourDnsSd *data, const char *status, const char *status_message);
+
+/**
+ * Advertise our presence within the dns-sd daemon and start
+ * browsing for other bonjour peers.
+ */
+gboolean bonjour_dns_sd_start(BonjourDnsSd *data);
+
+/**
+ * Unregister the "_presence._tcp" service at the mDNS daemon.
+ */
+void bonjour_dns_sd_stop(BonjourDnsSd *data);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/bonjour/mdns_howl.c	Wed Jun 06 00:23:41 2007 +0000
@@ -0,0 +1,238 @@
+/*
+ *  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 Library 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 "mdns_howl.h"
+
+#include "debug.h"
+#include "buddy.h"
+
+sw_result HOWL_API
+_publish_reply(sw_discovery discovery, sw_discovery_oid oid,
+			   sw_discovery_publish_status status, sw_opaque extra)
+{
+	purple_debug_warning("bonjour", "_publish_reply --> Start\n");
+
+	/* Check the answer from the mDNS daemon */
+	switch (status)
+	{
+		case SW_DISCOVERY_PUBLISH_STARTED :
+			purple_debug_info("bonjour", "_publish_reply --> Service started\n");
+			break;
+		case SW_DISCOVERY_PUBLISH_STOPPED :
+			purple_debug_info("bonjour", "_publish_reply --> Service stopped\n");
+			break;
+		case SW_DISCOVERY_PUBLISH_NAME_COLLISION :
+			purple_debug_info("bonjour", "_publish_reply --> Name collision\n");
+			break;
+		case SW_DISCOVERY_PUBLISH_INVALID :
+			purple_debug_info("bonjour", "_publish_reply --> Service invalid\n");
+			break;
+	}
+
+	return SW_OKAY;
+}
+
+sw_result HOWL_API
+_resolve_reply(sw_discovery discovery, sw_discovery_oid oid,
+			   sw_uint32 interface_index, sw_const_string name,
+			   sw_const_string type, sw_const_string domain,
+			   sw_ipv4_address address, sw_port port,
+			   sw_octets text_record, sw_ulong text_record_len,
+			   sw_opaque extra)
+{
+	BonjourBuddy *buddy;
+	PurpleAccount *account = (PurpleAccount*)extra;
+	gint address_length = 16;
+	sw_text_record_iterator iterator;
+	char key[SW_TEXT_RECORD_MAX_LEN];
+	char value[SW_TEXT_RECORD_MAX_LEN];
+	sw_uint32 value_length;
+
+	sw_discovery_cancel(discovery, oid);
+
+	/* create a buddy record */
+	buddy = bonjour_buddy_new(name, account);
+
+	/* Get the ip as a string */
+	buddy->ip = g_malloc(address_length);
+	sw_ipv4_address_name(address, buddy->ip, address_length);
+
+	buddy->port_p2pj = port;
+
+	/* Obtain the parameters from the text_record */
+	if ((text_record_len > 0) && (text_record) && (*text_record != '\0'))
+	{
+		sw_text_record_iterator_init(&iterator, text_record, text_record_len);
+		while (sw_text_record_iterator_next(iterator, key, (sw_octet *)value, &value_length) == SW_OKAY)
+			set_bonjour_buddy_value(buddy, key, value, value_length);
+
+		sw_text_record_iterator_fina(iterator);
+	}
+
+	if (!bonjour_buddy_check(buddy))
+	{
+		bonjour_buddy_delete(buddy);
+		return SW_DISCOVERY_E_UNKNOWN;
+	}
+
+	/* Add or update the buddy in our buddy list */
+	bonjour_buddy_add_to_purple(buddy);
+
+	return SW_OKAY;
+}
+
+sw_result HOWL_API
+_browser_reply(sw_discovery discovery, sw_discovery_oid oid,
+			   sw_discovery_browse_status status,
+			   sw_uint32 interface_index, sw_const_string name,
+			   sw_const_string type, sw_const_string domain,
+			   sw_opaque_t extra)
+{
+	sw_discovery_resolve_id rid;
+	PurpleAccount *account = (PurpleAccount*)extra;
+	PurpleBuddy *gb = NULL;
+
+	switch (status)
+	{
+		case SW_DISCOVERY_BROWSE_INVALID:
+			purple_debug_warning("bonjour", "_browser_reply --> Invalid\n");
+			break;
+		case SW_DISCOVERY_BROWSE_RELEASE:
+			purple_debug_warning("bonjour", "_browser_reply --> Release\n");
+			break;
+		case SW_DISCOVERY_BROWSE_ADD_DOMAIN:
+			purple_debug_warning("bonjour", "_browser_reply --> Add domain\n");
+			break;
+		case SW_DISCOVERY_BROWSE_ADD_DEFAULT_DOMAIN:
+			purple_debug_warning("bonjour", "_browser_reply --> Add default domain\n");
+			break;
+		case SW_DISCOVERY_BROWSE_REMOVE_DOMAIN:
+			purple_debug_warning("bonjour", "_browser_reply --> Remove domain\n");
+			break;
+		case SW_DISCOVERY_BROWSE_ADD_SERVICE:
+			/* A new peer has joined the network and uses iChat bonjour */
+			purple_debug_info("bonjour", "_browser_reply --> Add service\n");
+			if (g_ascii_strcasecmp(name, account->username) != 0)
+			{
+				if (sw_discovery_resolve(discovery, interface_index, name, type,
+						domain, _resolve_reply, extra, &rid) != SW_OKAY)
+				{
+					purple_debug_warning("bonjour", "_browser_reply --> Cannot send resolve\n");
+				}
+			}
+			break;
+		case SW_DISCOVERY_BROWSE_REMOVE_SERVICE:
+			purple_debug_info("bonjour", "_browser_reply --> Remove service\n");
+			gb = purple_find_buddy((PurpleAccount*)extra, name);
+			if (gb != NULL)
+			{
+				bonjour_buddy_delete(gb->proto_data);
+				purple_blist_remove_buddy(gb);
+			}
+			break;
+		case SW_DISCOVERY_BROWSE_RESOLVED:
+			purple_debug_info("bonjour", "_browse_reply --> Resolved\n");
+			break;
+		default:
+			break;
+	}
+
+	return SW_OKAY;
+}
+
+int
+_mdns_publish(BonjourDnsSd *data, PublishType type)
+{
+	sw_text_record dns_data;
+	sw_result publish_result = SW_OKAY;
+	char portstring[6];
+	const char *jid, *aim, *email;
+
+	/* Fill the data for the service */
+	if (sw_text_record_init(&dns_data) != SW_OKAY)
+	{
+		purple_debug_error("bonjour", "Unable to initialize the data for the mDNS.\n");
+		return -1;
+	}
+
+	/* Convert the port to a string */
+	snprintf(portstring, sizeof(portstring), "%d", data->port_p2pj);
+
+	jid = purple_account_get_string(data->account, "jid", NULL);
+	aim = purple_account_get_string(data->account, "AIM", NULL);
+	email = purple_account_get_string(data->account, "email", NULL);
+
+	/* We should try to follow XEP-0174, but some clients have "issues", so we humor them.
+	 * See http://telepathy.freedesktop.org/wiki/SalutInteroperability
+	 */
+
+	/* Needed by iChat */
+	sw_text_record_add_key_and_string_value(dns_data, "txtvers", "1");
+	/* Needed by Gaim/Pidgin <= 2.0.1 (remove at some point) */
+	sw_text_record_add_key_and_string_value(dns_data, "1st", data->first);
+	/* Needed by Gaim/Pidgin <= 2.0.1 (remove at some point) */
+	sw_text_record_add_key_and_string_value(dns_data, "last", data->last);
+	/* Needed by Adium */
+	sw_text_record_add_key_and_string_value(dns_data, "port.p2pj", portstring);
+	/* Needed by iChat, Gaim/Pidgin <= 2.0.1 */
+	sw_text_record_add_key_and_string_value(dns_data, "status", data->status);
+	/* Currently always set to "!" since we don't support AV and wont ever be in a conference */
+	sw_text_record_add_key_and_string_value(dns_data, "vc", data->vc);
+	sw_text_record_add_key_and_string_value(dns_data, "ver", VERSION);
+	if (email != NULL && *email != '\0')
+		sw_text_record_add_key_and_string_value(dns_data, "email", email);
+	if (jid != NULL && *jid != '\0')
+		sw_text_record_add_key_and_string_value(dns_data, "jid", jid);
+	/* Nonstandard, but used by iChat */
+	if (aim != NULL && *aim != '\0')
+		sw_text_record_add_key_and_string_value(dns_data, "AIM", aim);
+	if (data->msg != NULL && *data->msg != '\0')
+		sw_text_record_add_key_and_string_value(dns_data, "msg", data->msg);
+	if (data->phsh != NULL && *data->phsh != '\0')
+		sw_text_record_add_key_and_string_value(dns_data, "phsh", data->phsh);
+
+	/* TODO: ext, nick, node */
+
+	/* Publish the service */
+	switch (type)
+	{
+		case PUBLISH_START:
+			publish_result = sw_discovery_publish(data->session, 0, purple_account_get_username(data->account), ICHAT_SERVICE, NULL,
+								NULL, data->port_p2pj, sw_text_record_bytes(dns_data), sw_text_record_len(dns_data),
+								_publish_reply, NULL, &data->session_id);
+			break;
+		case PUBLISH_UPDATE:
+			publish_result = sw_discovery_publish_update(data->session, data->session_id,
+								sw_text_record_bytes(dns_data), sw_text_record_len(dns_data));
+			break;
+	}
+	if (publish_result != SW_OKAY)
+	{
+		purple_debug_error("bonjour", "Unable to publish or change the status of the _presence._tcp service.\n");
+		return -1;
+	}
+
+	/* Free the memory used by temp data */
+	sw_text_record_fina(dns_data);
+
+	return 0;
+}
+
+void
+_mdns_handle_event(gpointer data, gint source, PurpleInputCondition condition)
+{
+	sw_discovery_read_socket((sw_discovery)data);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/bonjour/mdns_howl.h	Wed Jun 06 00:23:41 2007 +0000
@@ -0,0 +1,47 @@
+/*
+ *  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 Library 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_MDNS_HOWL
+#define _BONJOUR_MDNS_HOWL
+
+#include "config.h"
+
+#ifdef USE_BONJOUR_HOWL
+
+#include <howl.h>
+#include <glib.h>
+#include "mdns_types.h"
+
+/* callback functions */
+
+sw_result HOWL_API _publish_reply(sw_discovery discovery, sw_discovery_oid oid, sw_discovery_publish_status status, sw_opaque extra);
+
+sw_result HOWL_API _resolve_reply(sw_discovery discovery, sw_discovery_oid oid, sw_uint32 interface_index, sw_const_string name,
+	sw_const_string type, sw_const_string domain, sw_ipv4_address address, sw_port port, sw_octets text_record, 
+	sw_ulong text_record_len, sw_opaque extra);
+
+sw_result HOWL_API _browser_reply(sw_discovery discovery, sw_discovery_oid oid, sw_discovery_browse_status status,
+	sw_uint32 interface_index, sw_const_string name, sw_const_string type, sw_const_string domain, sw_opaque_t extra);
+
+
+/* interface functions */
+
+int _mdns_publish(BonjourDnsSd *data, PublishType type);
+void _mdns_handle_event(gpointer data, gint source, PurpleInputCondition condition);
+
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/bonjour/mdns_types.h	Wed Jun 06 00:23:41 2007 +0000
@@ -0,0 +1,63 @@
+/*
+ *  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 Library 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_MDNS_TYPES
+#define _BONJOUR_MDNS_TYPES
+
+#include <glib.h>
+#include "account.h"
+#include "config.h"
+
+#ifdef USE_BONJOUR_APPLE
+#include "dns_sd_proxy.h"
+#else /* USE_BONJOUR_HOWL */
+#include <howl.h>
+#endif
+
+#define ICHAT_SERVICE "_presence._tcp."
+
+/**
+ * Data to be used by the dns-sd connection.
+ */
+typedef struct _BonjourDnsSd
+{
+#ifdef USE_BONJOUR_APPLE
+	DNSServiceRef advertisement;
+	DNSServiceRef browser;
+
+	int advertisement_fd; /* hack... windows bonjour is broken, so we have to have this */
+#else /* USE_BONJOUR_HOWL */
+	sw_discovery session;
+	sw_discovery_oid session_id;
+#endif
+
+	PurpleAccount *account;
+	gchar *first;
+	gchar *last;
+	gint port_p2pj;
+	gchar *phsh;
+	gchar *status;
+	gchar *vc;
+	gchar *msg;
+} BonjourDnsSd;
+
+typedef enum _PublishType {
+	PUBLISH_START,
+	PUBLISH_UPDATE
+} PublishType;
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/bonjour/mdns_win32.c	Wed Jun 06 00:23:41 2007 +0000
@@ -0,0 +1,296 @@
+/*
+ *  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 Library 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 "mdns_win32.h"
+
+#include "debug.h"
+
+/* data structure for the resolve callback */
+typedef struct _ResolveCallbackArgs
+{
+	DNSServiceRef resolver;
+	int resolver_fd;
+
+	PurpleDnsQueryData *query;
+	gchar *fqn;
+
+	BonjourBuddy* buddy;
+} ResolveCallbackArgs;
+
+static void
+_mdns_parse_text_record(BonjourBuddy* buddy, const char* record, uint16_t record_len)
+{
+	const char *txt_entry;
+	uint8_t txt_len;
+	int i;
+
+	for (i = 0; buddy_TXT_records[i] != NULL; i++) {
+		txt_entry = TXTRecordGetValuePtr(record_len, record, buddy_TXT_records[i], &txt_len);
+		if (txt_entry != NULL)
+			set_bonjour_buddy_value(buddy, buddy_TXT_records[i], txt_entry, txt_len);
+	}
+}
+
+static void DNSSD_API
+_mdns_text_record_query_callback(DNSServiceRef DNSServiceRef, DNSServiceFlags flags,
+	uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *fullname,
+	uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata,
+	uint32_t ttl, void *context)
+{
+	if (kDNSServiceErr_NoError != errorCode)
+		purple_debug_error("bonjour", "text record query - callback error.\n");
+	else if (flags & kDNSServiceFlagsAdd)
+	{
+		BonjourBuddy *buddy = (BonjourBuddy*)context;
+		_mdns_parse_text_record(buddy, rdata, rdlen);
+		bonjour_buddy_add_to_purple(buddy);
+	}
+}
+
+static void
+_mdns_resolve_host_callback(GSList *hosts, gpointer data, const char *error_message)
+{
+	ResolveCallbackArgs* args = (ResolveCallbackArgs*)data;
+
+	if (!hosts || !hosts->data)
+		purple_debug_error("bonjour", "host resolution - callback error.\n");
+	else
+	{
+		struct sockaddr_in *addr = (struct sockaddr_in*)g_slist_nth_data(hosts, 1);
+		BonjourBuddy* buddy = args->buddy;
+
+		buddy->ip = g_strdup(inet_ntoa(addr->sin_addr));
+
+		/* finally, set up the continuous txt record watcher, and add the buddy to purple */
+
+		if (kDNSServiceErr_NoError == DNSServiceQueryRecord(&buddy->txt_query, 0, 0, args->fqn,
+				kDNSServiceType_TXT, kDNSServiceClass_IN, _mdns_text_record_query_callback, buddy))
+		{
+			gint fd = DNSServiceRefSockFD(buddy->txt_query);
+			buddy->txt_query_fd = purple_input_add(fd, PURPLE_INPUT_READ, _mdns_handle_event, buddy->txt_query);
+
+			bonjour_buddy_add_to_purple(buddy);
+		}
+		else
+			bonjour_buddy_delete(buddy);
+
+	}
+
+	/* free the hosts list*/
+	g_slist_free(hosts);
+
+	/* free the remaining args memory */
+	purple_dnsquery_destroy(args->query);
+	g_free(args->fqn);
+	g_free(args);
+}
+
+static void DNSSD_API
+_mdns_service_resolve_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode,
+    const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const char *txtRecord, void *context)
+{
+	ResolveCallbackArgs *args = (ResolveCallbackArgs*)context;
+
+	/* remove the input fd and destroy the service ref */
+	purple_input_remove(args->resolver_fd);
+	DNSServiceRefDeallocate(args->resolver);
+
+	if (kDNSServiceErr_NoError != errorCode)
+	{
+		purple_debug_error("bonjour", "service resolver - callback error.\n");
+		bonjour_buddy_delete(args->buddy);
+		g_free(args);
+	}
+	else
+	{
+		args->buddy->port_p2pj = ntohs(port);
+
+		/* parse the text record */
+		_mdns_parse_text_record(args->buddy, txtRecord, txtLen);
+
+		/* set more arguments, and start the host resolver */
+		args->fqn = g_strdup(fullname);
+
+		if (!(args->query =
+			purple_dnsquery_a(hosttarget, port, _mdns_resolve_host_callback, args)))
+		{
+			purple_debug_error("bonjour", "service resolver - host resolution failed.\n");
+			bonjour_buddy_delete(args->buddy);
+			g_free(args->fqn);
+			g_free(args);
+		}
+	}
+
+}
+
+static void DNSSD_API
+_mdns_service_register_callback(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode,
+    const char *name, const char *regtype, const char *domain, void *context)
+{
+	/* we don't actually care about anything said in this callback - this is only here because Bonjour for windows is broken */
+	if (kDNSServiceErr_NoError != errorCode)
+		purple_debug_error("bonjour", "service advertisement - callback error.\n");
+	else
+		purple_debug_info("bonjour", "service advertisement - callback.\n");
+}
+
+void DNSSD_API
+_mdns_service_browse_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
+    DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain, void *context)
+{
+	PurpleAccount *account = (PurpleAccount*)context;
+	PurpleBuddy *gb = NULL;
+
+	if (kDNSServiceErr_NoError != errorCode)
+		purple_debug_error("bonjour", "service browser - callback error");
+	else if (flags & kDNSServiceFlagsAdd)
+	{
+		/* A presence service instance has been discovered... check it isn't us! */
+		if (g_ascii_strcasecmp(serviceName, account->username) != 0)
+		{
+			/* OK, lets go ahead and resolve it to add to the buddy list */
+			ResolveCallbackArgs *args = g_new0(ResolveCallbackArgs, 1);
+			args->buddy = bonjour_buddy_new(serviceName, account);
+
+			if (kDNSServiceErr_NoError != DNSServiceResolve(&args->resolver, 0, 0, serviceName, regtype, replyDomain, _mdns_service_resolve_callback, args))
+			{
+				bonjour_buddy_delete(args->buddy);
+				g_free(args);
+				purple_debug_error("bonjour", "service browser - failed to resolve service.\n");
+			}
+			else
+			{
+				/* get a file descriptor for this service ref, and add it to the input list */
+				gint resolver_fd = DNSServiceRefSockFD(args->resolver);
+				args->resolver_fd = purple_input_add(resolver_fd, PURPLE_INPUT_READ, _mdns_handle_event, args->resolver);
+			}
+		}
+	}
+	else
+	{
+		/* A peer has sent a goodbye packet, remove them from the buddy list */
+		purple_debug_info("bonjour", "service browser - remove notification\n");
+		gb = purple_find_buddy(account, serviceName);
+		if (gb != NULL)
+		{
+			bonjour_buddy_delete(gb->proto_data);
+			purple_blist_remove_buddy(gb);
+		}
+	}
+}
+
+int
+_mdns_publish(BonjourDnsSd *data, PublishType type)
+{
+	TXTRecordRef dns_data;
+	char portstring[6];
+	int ret = 0;
+	const char *jid, *aim, *email;
+	DNSServiceErrorType set_ret;
+
+	TXTRecordCreate(&dns_data, 256, NULL);
+
+	/* Convert the port to a string */
+	snprintf(portstring, sizeof(portstring), "%d", data->port_p2pj);
+
+	jid = purple_account_get_string(data->account, "jid", NULL);
+	aim = purple_account_get_string(data->account, "AIM", NULL);
+	email = purple_account_get_string(data->account, "email", NULL);
+
+	/* We should try to follow XEP-0174, but some clients have "issues", so we humor them.
+	 * See http://telepathy.freedesktop.org/wiki/SalutInteroperability
+	 */
+
+	/* Needed by iChat */
+	set_ret = TXTRecordSetValue(&dns_data, "txtvers", 1, "1");
+	/* Needed by Gaim/Pidgin <= 2.0.1 (remove at some point) */
+	if (set_ret == kDNSServiceErr_NoError)
+		set_ret = TXTRecordSetValue(&dns_data, "1st", strlen(data->first), data->first);
+	/* Needed by Gaim/Pidgin <= 2.0.1 (remove at some point) */
+	if (set_ret == kDNSServiceErr_NoError)
+		set_ret = TXTRecordSetValue(&dns_data, "last", strlen(data->last), data->last);
+	/* Needed by Adium */
+	if (set_ret == kDNSServiceErr_NoError)
+		set_ret = TXTRecordSetValue(&dns_data, "port.p2pj", strlen(portstring), portstring);
+	/* Needed by iChat, Gaim/Pidgin <= 2.0.1 */
+	if (set_ret == kDNSServiceErr_NoError)
+		set_ret = TXTRecordSetValue(&dns_data, "status", strlen(data->status), data->status);
+	if (set_ret == kDNSServiceErr_NoError)
+		set_ret = TXTRecordSetValue(&dns_data, "ver", strlen(VERSION), VERSION);
+	/* Currently always set to "!" since we don't support AV and wont ever be in a conference */
+	if (set_ret == kDNSServiceErr_NoError)
+		set_ret = TXTRecordSetValue(&dns_data, "vc", strlen(data->vc), data->vc);
+	if (set_ret == kDNSServiceErr_NoError && email != NULL && *email != '\0')
+		set_ret = TXTRecordSetValue(&dns_data, "email", strlen(email), email);
+	if (set_ret == kDNSServiceErr_NoError && jid != NULL && *jid != '\0')
+		set_ret = TXTRecordSetValue(&dns_data, "jid", strlen(jid), jid);
+	/* Nonstandard, but used by iChat */
+	if (set_ret == kDNSServiceErr_NoError && aim != NULL && *aim != '\0')
+		set_ret = TXTRecordSetValue(&dns_data, "AIM", strlen(aim), aim);
+	if (set_ret == kDNSServiceErr_NoError && data->msg != NULL && *data->msg != '\0')
+		set_ret = TXTRecordSetValue(&dns_data, "msg", strlen(data->msg), data->msg);
+	if (set_ret == kDNSServiceErr_NoError && data->phsh != NULL && *data->phsh != '\0')
+		set_ret = TXTRecordSetValue(&dns_data, "phsh", strlen(data->phsh), data->phsh);
+
+	/* TODO: ext, nick, node */
+
+	if (set_ret != kDNSServiceErr_NoError)
+	{
+		purple_debug_error("bonjour", "Unable to allocate memory for text record.\n");
+		ret = -1;
+	}
+	else
+	{
+		DNSServiceErrorType err = kDNSServiceErr_NoError;
+
+		/* OK, we're done constructing the text record, (re)publish the service */
+
+		switch (type)
+		{
+			case PUBLISH_START:
+				err = DNSServiceRegister(&data->advertisement, 0, 0, purple_account_get_username(data->account), ICHAT_SERVICE,
+					NULL, NULL, htons(data->port_p2pj), TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data),
+					_mdns_service_register_callback, NULL);
+				break;
+
+			case PUBLISH_UPDATE:
+				err = DNSServiceUpdateRecord(data->advertisement, NULL, 0, TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data), 0);
+				break;
+		}
+
+		if (kDNSServiceErr_NoError != err)
+		{
+			purple_debug_error("bonjour", "Failed to publish presence service.\n");
+			ret = -1;
+		}
+		else if (PUBLISH_START == type)
+		{
+			/* hack: Bonjour on windows is broken. We don't care about the callback but we have to listen anyway */
+			gint advertisement_fd = DNSServiceRefSockFD(data->advertisement);
+			data->advertisement_fd = purple_input_add(advertisement_fd, PURPLE_INPUT_READ, _mdns_handle_event, data->advertisement);
+		}
+	}
+
+	/* Free the memory used by temp data */
+	TXTRecordDeallocate(&dns_data);
+	return ret;
+}
+
+void
+_mdns_handle_event(gpointer data, gint source, PurpleInputCondition condition)
+{
+	DNSServiceProcessResult((DNSServiceRef)data);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/bonjour/mdns_win32.h	Wed Jun 06 00:23:41 2007 +0000
@@ -0,0 +1,40 @@
+/*
+ *  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 Library 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_MDNS_WIN32
+#define _BONJOUR_MDNS_WIN32
+
+#ifdef USE_BONJOUR_APPLE
+
+#include <glib.h>
+#include "mdns_types.h"
+#include "buddy.h"
+#include "dnsquery.h"
+#include "dns_sd_proxy.h"
+
+/* Bonjour async callbacks */
+
+void DNSSD_API _mdns_service_browse_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
+    DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain, void *context);
+
+/* interface functions */
+
+int _mdns_publish(BonjourDnsSd *data, PublishType type);
+void _mdns_handle_event(gpointer data, gint source, PurpleInputCondition condition);
+
+#endif
+
+#endif
--- a/libpurple/protocols/yahoo/yahoo_packet.c	Tue Jun 05 12:14:38 2007 +0000
+++ b/libpurple/protocols/yahoo/yahoo_packet.c	Wed Jun 06 00:23:41 2007 +0000
@@ -110,6 +110,29 @@
 	return len;
 }
 
+/*
+ * 'len' is the value given to us by the server that is supposed to
+ * be the length of 'data'.  But apparently there's a time when this
+ * length is incorrect.  Christopher Layne thinks it might be a bug
+ * in their server code.
+ *
+ * The following information is from Christopher:
+ *
+ * It sometimes happens when Yahoo! sends a packet continuation within
+ * chat.  Sometimes when joining a large chatroom the initial
+ * SERVICE_CHATJOIN packet will be so large that it will need to be
+ * split into multiple packets.  That's fine, except that the length
+ * of the second packet is wrong.  The packet has the same length as
+ * the first packet, and the length given in the header is the same,
+ * however the actual data in the packet is shorter than this length.
+ * So half of the packet contains good, valid data, and then the rest
+ * of the packet is junk.  Luckily there is a null terminator after
+ * the valid data and before the invalid data.
+ *
+ * What does all this mean?  It means that we parse through the data
+ * pulling out key/value pairs until we've parsed 'len' bytes, or until
+ * we run into a null terminator, whichever comes first.
+ */
 void yahoo_packet_read(struct yahoo_packet *pkt, const guchar *data, int len)
 {
 	int pos = 0;
@@ -121,18 +144,8 @@
 
 	while (pos + 1 < len)
 	{
-		/* this is weird, and in one of the chat packets, and causes us to
-		 * think all the values are keys and all the keys are values after
-		 * this point if we don't handle it */
-		if (data[pos] == '\0') {
-			while (pos + 1 < len) {
-				if (data[pos] == 0xc0 && data[pos + 1] == 0x80)
-					break;
-				pos++;
-			}
-			pos += 2;
-			continue;
-		}
+		if (data[pos] == '\0')
+			break;
 
 		pair = g_new0(struct yahoo_pair, 1);
 
--- a/libpurple/util.c	Tue Jun 05 12:14:38 2007 +0000
+++ b/libpurple/util.c	Wed Jun 06 00:23:41 2007 +0000
@@ -2711,14 +2711,17 @@
 
 	if (len >= 4)
 	{
-		if (!strncmp((char *)data, "BM", 2))
-			return "bmp";
-		else if (!strncmp((char *)data, "GIF8", 4))
+		if (!strncmp((char *)data, "GIF8", 4))
 			return "gif";
-		else if (!strncmp((char *)data, "\xff\xd8\xff\xe0", 4))
+		else if (!strncmp((char *)data, "\xff\xd8\xff", 3)) /* 4th may be e0 through ef */
 			return "jpg";
 		else if (!strncmp((char *)data, "\x89PNG", 4))
 			return "png";
+		else if (!strncmp((char *)data, "MM", 2) ||
+				 !strncmp((char *)data, "II", 2))
+			return "tif";
+		else if (!strncmp((char *)data, "BM", 2))
+			return "bmp";
 	}
 
 	return "icon";
--- a/libpurple/win32/global.mak	Tue Jun 05 12:14:38 2007 +0000
+++ b/libpurple/win32/global.mak	Wed Jun 06 00:23:41 2007 +0000
@@ -14,7 +14,7 @@
 GTKSPELL_TOP ?= $(WIN32_DEV_TOP)/gtkspell-2.0.6
 GTK_TOP ?= $(WIN32_DEV_TOP)/gtk_2_0
 GTK_BIN ?= $(GTK_TOP)/bin
-HOWL_TOP ?= $(WIN32_DEV_TOP)/howl-1.0.0
+BONJOUR_TOP ?= $(WIN32_DEV_TOP)/Bonjour_SDK
 LIBXML2_TOP ?= $(WIN32_DEV_TOP)/libxml2
 MEANWHILE_TOP ?= $(WIN32_DEV_TOP)/meanwhile-1.0.2
 NSPR_TOP ?= $(WIN32_DEV_TOP)/nspr-4.6.4
--- a/pidgin/gtkblist.c	Tue Jun 05 12:14:38 2007 +0000
+++ b/pidgin/gtkblist.c	Wed Jun 06 00:23:41 2007 +0000
@@ -266,8 +266,6 @@
 	purple_prefs_set_int(PIDGIN_PREFS_ROOT "/blist/width",  event->width);
 	purple_prefs_set_int(PIDGIN_PREFS_ROOT "/blist/height", event->height);
 
-	gtk_widget_set_size_request(gtkblist->headline_label,
-				    purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/width")-25,-1);
 	/* continue to handle event normally */
 	return FALSE;
 }
--- a/pidgin/gtkconv.c	Tue Jun 05 12:14:38 2007 +0000
+++ b/pidgin/gtkconv.c	Wed Jun 06 00:23:41 2007 +0000
@@ -2846,14 +2846,25 @@
 	GList *list;
 	PidginConversation *gtkconv;
 	PurpleConversation *conv;
-	PurpleBuddy *buddy;
+	PurpleBlistNode *node = NULL;
+	PurpleChat *chat = NULL;
+	PurpleBuddy *buddy = NULL;
 
 	gtkconv = pidgin_conv_window_get_active_gtkconv(win);
 	conv = gtkconv->active_conv;
-	buddy = purple_find_buddy(conv->account, conv->name);
 
 	menu = gtk_item_factory_get_widget(win->menu.item_factory, N_("/Conversation/More"));
 
+	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
+		chat = purple_blist_find_chat(conv->account, conv->name);
+	else
+		buddy = purple_find_buddy(conv->account, conv->name);
+
+	if (chat)
+		node = (PurpleBlistNode *)chat;
+	else if (buddy)
+		node = (PurpleBlistNode *)buddy;
+
 	/* Remove the previous entries */
 	for (list = gtk_container_get_children(GTK_CONTAINER(menu)); list; )
 	{
@@ -2863,12 +2874,11 @@
 	}
 
 	/* Now add the stuff */
-	if (buddy)
+	if (node)
 	{
 		if (purple_account_is_connected(conv->account))
-			pidgin_append_blist_node_proto_menu(menu, conv->account->gc,
-												  (PurpleBlistNode *)buddy);
-		pidgin_append_blist_node_extended_menu(menu, (PurpleBlistNode *)buddy);
+			pidgin_append_blist_node_proto_menu(menu, conv->account->gc, node);
+		pidgin_append_blist_node_extended_menu(menu, node);
 	}
 
 	if ((list = gtk_container_get_children(GTK_CONTAINER(menu))) == NULL)
--- a/pidgin/gtkimhtmltoolbar.c	Tue Jun 05 12:14:38 2007 +0000
+++ b/pidgin/gtkimhtmltoolbar.c	Wed Jun 06 00:23:41 2007 +0000
@@ -1104,6 +1104,7 @@
 	gtk_widget_show_all(font_button);
 
 	font_menu = gtk_menu_new();
+
 	
 	for (i = 0; buttons[i].label; i++) {
 		GtkWidget *old = *buttons[i].button;
@@ -1158,11 +1159,11 @@
 	gtk_widget_show_all(insert_button);
 
 	insert_menu = gtk_menu_new();
-	
+
 	button = gtk_menu_item_new_with_mnemonic(_("_Smiley"));
 	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gtk_button_clicked), toolbar->smiley);
 	gtk_menu_shell_append(GTK_MENU_SHELL(insert_menu), button);
-	
+
 	button = gtk_menu_item_new_with_mnemonic(_("_Image"));
 	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gtk_button_clicked), toolbar->image);
 	gtk_menu_shell_append(GTK_MENU_SHELL(insert_menu), button);
@@ -1170,7 +1171,7 @@
 	button = gtk_menu_item_new_with_mnemonic(_("_Link"));
 	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gtk_button_clicked), toolbar->link);
 	gtk_menu_shell_append(GTK_MENU_SHELL(insert_menu), button);
-	
+
 	g_signal_connect(G_OBJECT(insert_button), "clicked", G_CALLBACK(pidgin_menu_clicked), insert_menu);
 	g_signal_connect(G_OBJECT(insert_menu), "deactivate", G_CALLBACK(pidgin_menu_deactivate), insert_button);
 	toolbar->sml = NULL;
--- a/pidgin/gtkutils.c	Tue Jun 05 12:14:38 2007 +0000
+++ b/pidgin/gtkutils.c	Wed Jun 06 00:23:41 2007 +0000
@@ -3045,20 +3045,20 @@
 
         row = pixels;
         for (i = 3; i < rowstride; i+=4) {
-                if (row[i] != 0xff)
+                if (row[i] < 0xfe)
                         return FALSE;
         }
 
         for (i = 1; i < height - 1; i++) {
                 row = pixels + (i*rowstride);
-                if (row[3] != 0xff || row[rowstride-1] != 0xff) {
+                if (row[3] < 0xfe || row[rowstride-1] < 0xfe) {
                         return FALSE;
-                }
+            }
         }
 
         row = pixels + ((height-1) * rowstride);
         for (i = 3; i < rowstride; i+=4) {
-                if (row[i] != 0xff)
+                if (row[i] < 0xfe)
                         return FALSE;
         }
 
--- a/pidgin/win32/nsis/pidgin-installer.nsi	Tue Jun 05 12:14:38 2007 +0000
+++ b/pidgin/win32/nsis/pidgin-installer.nsi	Wed Jun 06 00:23:41 2007 +0000
@@ -690,6 +690,7 @@
     Delete "$INSTDIR\plugins\iconaway.dll"
     Delete "$INSTDIR\plugins\idle.dll"
     Delete "$INSTDIR\plugins\libaim.dll"
+    Delete "$INSTDIR\plugins\libbonjour.dll"
     Delete "$INSTDIR\plugins\libgg.dll"
     Delete "$INSTDIR\plugins\libicq.dll"
     Delete "$INSTDIR\plugins\libirc.dll"